From 6bb37e5b11d55bfa3462ef1cfb0579a42632d456 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 2 Sep 2026 14:27:18 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat(link):=20=E9=80=A3=E7=B5=90=E4=B8=80?= =?UTF-8?q?=E5=BE=8B=E5=AF=AB=E6=88=90=20[=E6=96=87=E5=AD=97](=E7=B5=95?= =?UTF-8?q?=E5=B0=8D=E7=B6=B2=E5=9D=80)=EF=BC=8C=E5=AF=AB=E5=85=A5?= =?UTF-8?q?=E5=89=8D=E5=85=88=E9=A9=97=E8=AD=89=E9=80=A3=E5=BE=97=E5=88=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 取消 [[頁名]] 與 [[顯示文字|頁名]] 兩種同 wiki 寫法,不再分「同存取庫」與 「跨存取庫」兩條規則。那種寫法只在自己那個 wiki 內解析,寫錯不報錯,畫面上 看起來像普通文字或死連結,巡不到也修不了。 連結寫進頁面前先過 jsc-gitea 的 link-check.sh,結束碼 0 才寫。驗證一律走 API, 不看網頁狀態碼:私有存取庫的網頁網址對未登入請求一律回 404,拿狀態碼判會把 好連結判成壞的。認證失敗回 7,與死連結的 1 分開,免得金鑰一過期就把還在的頁 整批判死。 --- references/behaviors.md | 8 ++++---- skills/ask/SKILL.md | 8 +++++--- templates/question-contents.md | 4 +++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/references/behaviors.md b/references/behaviors.md index 36e5091..9ac3dba 100644 --- a/references/behaviors.md +++ b/references/behaviors.md @@ -7,7 +7,7 @@ | 項目 | 內容 | | --- | --- | | 觸發時機 | 任何 jsc 技能需要使用者做決定時叫用。答案已經寫在 `QUESTION_{HASH}` 且意圖相符時不叫用,直接採用舊答案。答案查得到程式碼、設定檔或環境變數時不叫用,自己去查。 | -| 關鍵步驟 | 確認目前工作的 `{owner}/{repo}`、執行 `jsc-gitea/tools/hash-id {owner}/{repo}` 取得完整 40 碼大寫 `HASH`、每個工作階段各解析一次 `QUESTION` 與 `CONTENTS` 兩個 wiki 存取庫、讀入 `QUESTION_CONTENTS`(`CONTENTS` 庫)與 `QUESTION_{HASH}`(`QUESTION` 庫)並留成階段副本、把這一輪每一題標成「照紀錄回答」或「要問」、以決策樹選單一次問一個決策點且每個選項標明影響範圍、依答案決定下一題直到沒有疑慮、交一個 sub agent 先用 `jsc-gitea:wiki` 寫 `QUESTION_{HASH}`、再用 `jsc-gitea/tools/gitea.sh wiki-url` 取紀錄頁的絕對網址並依結束碼分流(4 是紀錄頁還沒寫上去要重寫、5 是沒有 `html_url`、7 金鑰失效、8 重試一次,其餘非 0 一律停下並註明那一列沒有被索引,網址取不到就不寫那一列)、再用 `jsc-gitea/tools/wiki-contents.sh upsert` 把單列寫進 `QUESTION_CONTENTS` 並依結束碼分流、寫入後同步更新階段副本、把答案交回叫用的技能 | -| 外部呼叫 | `jsc-gitea/tools/hash-id`、`jsc-gitea:wiki`(`wiki-repo QUESTION`、`wiki-repo CONTENTS`、`wiki-get`、寫入)、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-gitea/tools/gitea.sh wiki-url`(目錄頁連到紀錄頁的絕對網址)、AskUserQuestion 或等效選單、一個負責兩次 wiki 寫入的 sub agent;範本 `templates/question-record.md`、`templates/question-contents.md` | -| 完成條件 | 這一輪每一題都有答案,來源是紀錄或使用者;答案交回叫用的技能;有存取庫名稱時,sub agent 回報兩頁都寫成功(`wiki-contents.sh` 退 0),主代理接受那一次回報。沒有存取庫名稱時,問完直接交回答案,不寫任何頁。目錄頁寫不成的另一種完成條件:`wiki-contents.sh` 退 2、退 3、退 7,或 `wiki-url` 取不到網址,四種都算這一步走完——紀錄頁記成「已寫、未被索引」,回報講明結束碼與沒寫成的那一頁,答案照樣交回叫用的技能。退 3 特別要交回答案:`JSC_WIKI_REPO_CONTENTS` 在 `jsc-cli/tools/config-spec.tsv` 是 `fix=ask`,`/jsc-cli:setup` 只能靠這一支問到值,答案在這裡被吞掉,變數就永遠設不起來。退 4 代表範本參數被漏掉了,本技能的呼叫一律帶第五個參數,所以不會出現;範本路徑不存在回的是 2。停止執行並回報的情況:`hash-id` 找不到 SHA-1 工具、wiki 讀取非 exit 4 的失敗、重試後仍寫不進去(`wiki-contents.sh` 退 1 或 8)。寫入失敗一律把答案交回並註明沒有記錄。 | -| 可驗證跡象 | `QUESTION` 存取庫的 wiki `QUESTION_{HASH}` 頁尾多一節,頁名是完整 40 碼大寫十六進位,開頭是使用者意圖,底下每題一張選項與影響範圍的表,附答案與時間;`CONTENTS` 存取庫的 wiki `QUESTION_CONTENTS` 該存取庫那一列的「最後更新」變成這次執行的時間戳,沒有該列就新增一列,該列的問詢紀錄欄是指向紀錄頁的絕對網址,不是 `[[...]]`,別的存取庫那幾列一字不動。沒有 `{owner}/{repo}` 時無寫入跡象,只有回報內容。 | +| 關鍵步驟 | 確認目前工作的 `{owner}/{repo}`、執行 `jsc-gitea/tools/hash-id {owner}/{repo}` 取得完整 40 碼大寫 `HASH`、每個工作階段各解析一次 `QUESTION` 與 `CONTENTS` 兩個 wiki 存取庫、讀入 `QUESTION_CONTENTS`(`CONTENTS` 庫)與 `QUESTION_{HASH}`(`QUESTION` 庫)並留成階段副本、把這一輪每一題標成「照紀錄回答」或「要問」、以決策樹選單一次問一個決策點且每個選項標明影響範圍、依答案決定下一題直到沒有疑慮、交一個 sub agent 做兩次寫入:先把新一節要放進 `QUESTION_{HASH}` 的每個連結交給 `jsc-gitea/tools/link-check.sh` 驗證,退 0 才用 `jsc-gitea:wiki` 寫那一節;再用 `jsc-gitea/tools/gitea.sh wiki-url` 取紀錄頁的絕對網址並依結束碼分流(4 是紀錄頁還沒寫上去要重寫、5 是沒有 `html_url`、7 金鑰失效、8 重試一次,其餘非 0 一律停下並註明那一列沒有被索引,網址取不到就不寫那一列)、把該列的連結再交給 `link-check.sh` 驗證,退 0 才用 `jsc-gitea/tools/wiki-contents.sh upsert` 把單列寫進 `QUESTION_CONTENTS` 並依結束碼分流、寫入後同步更新階段副本、把答案交回叫用的技能。頁面裡的連結一律寫成 `[{文字}]({連結})`,wiki 連結取自 `wiki-url` 印出的絕對網址,不自行組路徑。 | +| 外部呼叫 | `jsc-gitea/tools/hash-id`、`jsc-gitea:wiki`(`wiki-repo QUESTION`、`wiki-repo CONTENTS`、`wiki-get`、寫入)、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-gitea/tools/gitea.sh wiki-url`(目錄頁連到紀錄頁的絕對網址)、`jsc-gitea/tools/link-check.sh`(兩頁寫入前各驗證一次連結)、AskUserQuestion 或等效選單、一個負責兩次 wiki 寫入的 sub agent;範本 `templates/question-record.md`、`templates/question-contents.md` | +| 完成條件 | 這一輪每一題都有答案,來源是紀錄或使用者;答案交回叫用的技能;有存取庫名稱時,sub agent 回報兩頁都寫成功(`wiki-contents.sh` 退 0),主代理接受那一次回報。沒有存取庫名稱時,問完直接交回答案,不寫任何頁。目錄頁寫不成的另一種完成條件:`wiki-contents.sh` 退 2、退 3、退 7,或 `wiki-url` 取不到網址,四種都算這一步走完——紀錄頁記成「已寫、未被索引」,回報講明結束碼與沒寫成的那一頁,答案照樣交回叫用的技能。退 3 特別要交回答案:`JSC_WIKI_REPO_CONTENTS` 在 `jsc-cli/tools/config-spec.tsv` 是 `fix=ask`,`/jsc-cli:setup` 只能靠這一支問到值,答案在這裡被吞掉,變數就永遠設不起來。退 4 代表範本參數被漏掉了,本技能的呼叫一律帶第五個參數,所以不會出現;範本路徑不存在回的是 2。`link-check.sh` 非 0 也算這一步走完:退 1 就那一頁不寫並回報 DEAD 清單,退 2 補參數重跑,退 3 先設定 `GITEA_HOST` 再驗證,退 7 停下來回報金鑰,四種都不得跳過驗證直接寫入。停止執行並回報的情況:`hash-id` 找不到 SHA-1 工具、wiki 讀取非 exit 4 的失敗、重試後仍寫不進去(`wiki-contents.sh` 退 1 或 8)。寫入失敗一律把答案交回並註明沒有記錄。 | +| 可驗證跡象 | `QUESTION` 存取庫的 wiki `QUESTION_{HASH}` 頁尾多一節,頁名是完整 40 碼大寫十六進位,開頭是使用者意圖,底下每題一張選項與影響範圍的表,附答案與時間;`CONTENTS` 存取庫的 wiki `QUESTION_CONTENTS` 該存取庫那一列的「最後更新」變成這次執行的時間戳,沒有該列就新增一列,該列的問詢紀錄欄是 `[QUESTION_{HASH}]({絕對網址})`,網址與 `wiki-url` 印出的一字不差,別的存取庫那幾列一字不動;兩頁寫進去的每個連結都通得過 `link-check.sh`,驗不過那一輪頁面停在舊內容,回報裡有 DEAD 清單或結束碼。沒有 `{owner}/{repo}` 時無寫入跡象,只有回報內容。 | diff --git a/skills/ask/SKILL.md b/skills/ask/SKILL.md index 23da5af..b40d1e0 100644 --- a/skills/ask/SKILL.md +++ b/skills/ask/SKILL.md @@ -35,9 +35,11 @@ Inside one work session this skill is the only writer of `QUESTION_{HASH}` and ` 1. Without a repository name, do not record; stop here. Done when the answers are handed back to the calling skill and no wiki page was touched. 2. After each Q&A round, hand both wiki writes to one sub agent, content page first and directory page second, so the directory never links a page that failed to write. - - `QUESTION_{HASH}` is written through `jsc-gitea:wiki` — it owns the `JSC_WIKI_REPO_QUESTION` → `JSC_WIKI_REPO` resolution and the tea-token fallback, which a hand-rolled Gitea API call loses. Apply `templates/question-record.md`: append this round as a new section, keep every earlier section, and open the section with the user's original intent (why this questioning round happened), which lets a later reader judge why the user chose as they did and whether the answer still applies. `QUESTION_{HASH}` belongs to this one repository, which is why appending a section is the right shape there. - - `QUESTION_CONTENTS` is written by one call to `jsc-gitea/tools/wiki-contents.sh upsert QUESTION 1 {owner}/{repo} {row-file} templates/question-contents.md`. `key-col` is the 1-based column index, not a column name, and column 1 of `QUESTION_CONTENTS` is 存取庫名稱 — that cell holds the bare `{owner}/{repo}` text. The script compares the whole cell, so pass the key exactly as the row file writes it. The row file holds this repository's single row, and its 問詢紀錄 cell is the absolute URL printed by `jsc-gitea/tools/gitea.sh wiki-url {question-repo} QUESTION_{HASH}` — never `[[...]]`, which resolves only inside one wiki and now points at nothing, because the directory page lives in the `CONTENTS` repo while the record page lives in the `QUESTION` repo. Always pass the template argument. The script owns the read-merge-write of that shared directory: it upserts this one row and touches no other repository's row, so do not read the page and rebuild it by hand. + - `QUESTION_{HASH}` is written through `jsc-gitea:wiki` — it owns the `JSC_WIKI_REPO_QUESTION` → `JSC_WIKI_REPO` resolution and the tea-token fallback, which a hand-rolled Gitea API call loses. Apply `templates/question-record.md`: append this round as a new section, keep every earlier section, and open the section with the user's original intent (why this questioning round happened), which lets a later reader judge why the user chose as they did and whether the answer still applies. `QUESTION_{HASH}` belongs to this one repository, which is why appending a section is the right shape there. Write every link in the section as `[{text}]({url})`, and take each wiki `{url}` from `jsc-gitea/tools/gitea.sh wiki-url` — never assemble a path by hand. + - Verify before writing: hand every link of the new section to `jsc-gitea/tools/link-check.sh` and branch on its exit code. 0: every link answers, so write the section. 1: at least one link is DEAD, so write nothing and report the DEAD lines to the calling skill — a dead link on a record page reads as a real page and nobody finds it again. 2: no URL reached the script, so pass the links again. 3: `GITEA_HOST` is unset, so report that it must be set and run the check again; never write a link that skipped the check. 7: the Gitea key failed, so stop and report the key problem — a failed key makes live pages look dead. A section carrying no link goes straight to the write. Done when the check exited 0 and the section is written, or nothing was written and the report names the failing links or the exit code. + - `QUESTION_CONTENTS` is written by one call to `jsc-gitea/tools/wiki-contents.sh upsert QUESTION 1 {owner}/{repo} {row-file} templates/question-contents.md`. `key-col` is the 1-based column index, not a column name, and column 1 of `QUESTION_CONTENTS` is 存取庫名稱 — that cell holds the bare `{owner}/{repo}` text. The script compares the whole cell, so pass the key exactly as the row file writes it. The row file holds this repository's single row, and its 問詢紀錄 cell is written as `[QUESTION_{HASH}]({url})`, where `{url}` is the absolute URL printed by `jsc-gitea/tools/gitea.sh wiki-url {question-repo} QUESTION_{HASH}`. Take that URL from the command only; never assemble a path by hand. The two pages sit in different repositories — the directory page in the `CONTENTS` repo, the record page in the `QUESTION` repo — so only an absolute URL crosses from one to the other. Always pass the template argument. The script owns the read-merge-write of that shared directory: it upserts this one row and touches no other repository's row, so do not read the page and rebuild it by hand. - Branch on `wiki-url`'s exit code before the row is built, because a URL that never arrived leaves that cell silently empty and the row still looks written. 0: put the printed URL in the cell, verbatim. 4: `QUESTION_{HASH}` is not on the wiki yet, so the record-page write has not landed — write that page again, then ask for the URL once more. 5: the page carries no `html_url`, so report it and never assemble a URL by hand. 7: the key is invalid or has no permission, so stop without retrying. 8: any other API failure, so retry once, then stop. Any other non-zero exit stops the same way. Whenever no URL comes back, write no row at all — an empty or hand-made cell is worse than a missing one — stop, report the exit code, and state that this repository's row was not indexed this round. + - Verify before writing: hand that URL, and every other link the row carries, to `jsc-gitea/tools/link-check.sh`, and branch on its exit code. 0: the links answer, so run the upsert. 1: at least one link is DEAD, so write no row and report the DEAD lines; `QUESTION_{HASH}` counts as written and unindexed, and this round's answers still go back to the calling skill. 2: no URL reached the script, so pass the links again. 3: `GITEA_HOST` is unset, so report that it must be set and run the check again; never upsert a row whose link skipped the check. 7: the Gitea key failed, so stop and report the key problem — a failed key makes live pages look dead, and an unchecked row would carry the blame instead. Done when the check exited 0 and the upsert ran, or no row was written and the report names the failing links or the exit code. - Branch on `wiki-contents.sh`'s exit code, every code its own branch. 0: the row was added or updated, so this round is recorded. 1: the write failed, so retry once per step 3. 2: usage error, so stop and report the arguments that were passed — the same call retried fails the same way. A template path that does not exist lands here too, and means the plugin installation is incomplete. 3: no `CONTENTS` repo is configured, so report that `JSC_WIKI_REPO_CONTENTS` (or `JSC_WIKI_REPO`) must be set, note `QUESTION_{HASH}` as written but unindexed, and hand this round's answers back to the calling skill all the same. **Never withhold the answers over this code.** `JSC_WIKI_REPO_CONTENTS` is a `fix=ask` item in `jsc-cli/tools/config-spec.tsv`, so `/jsc-cli:setup` can only learn its value by asking through this skill; an answer dropped here leaves the variable unset, and the unset variable makes the next round drop the answer again. 4: the page does not exist and no template reached the script. The call form above always passes the template as the fifth argument, so this code cannot come out of it — getting it means that argument was dropped, so restore it and run the same call once more. 7: the key is invalid or has no permission, so stop and report without retrying. 8: any other API failure, so retry once per step 3, then stop and report. - - Done when the sub agent has reported both writes as succeeded and the main agent has accepted that one report — this single confirmation covers both pages and both templates; do not re-read the pages to prove it. Exit 2, 3 or 7 from `wiki-contents.sh`, and any `wiki-url` failure, close this step the second way: `QUESTION_{HASH}` counts as written and unindexed, the report names the exit code and the page that stayed unwritten, and this round's answers go back to the calling skill regardless. **The answers of a finished questioning round are never lost because the directory page could not be written.** + - Done when the sub agent has reported both writes as succeeded and the main agent has accepted that one report — this single confirmation covers both pages and both templates; do not re-read the pages to prove it. Exit 2, 3 or 7 from `wiki-contents.sh`, any `wiki-url` failure, and any non-zero `link-check.sh` exit on the directory row, close this step the second way: `QUESTION_{HASH}` counts as written and unindexed, the report names the exit code and the page that stayed unwritten, and this round's answers go back to the calling skill regardless. **The answers of a finished questioning round are never lost because the directory page could not be written.** 3. On a reported write failure, retry that one page once — a `QUESTION_{HASH}` failure, or `wiki-contents.sh` exit 1 or 8. Exit 2, 3 and 7 are not retried, because the same call fails identically until someone fixes the arguments, the environment or the key; they end the recording, not the round. Every one of these paths ends the same way: the answers go back to the calling skill, and the report names the page that stayed unwritten, the exit code behind it, and what was recorded and what was not. Done when the retry succeeded and step 3 of "Session cache" refreshed that page's copy, or — when the retry also failed, or the code was 2, 3 or 7 — the report above has been printed and the answers have been handed back. diff --git a/templates/question-contents.md b/templates/question-contents.md index 1784bf6..a00916d 100644 --- a/templates/question-contents.md +++ b/templates/question-contents.md @@ -4,7 +4,9 @@ > > 寫入語意:一列代表一個存取庫。一律用 `jsc-gitea/tools/wiki-contents.sh upsert` 寫入:它讀回整頁,該存取庫已經有列就更新那一列,沒有才在文末附加一列,最後整頁寫回。只動自己那一列,禁止整頁覆蓋,也不得改動別人的列。 > -> 連結寫法:問詢紀錄那一欄放 `jsc-gitea/tools/gitea.sh wiki-url` 印出的絕對網址,不用 `[[...]]`。兩頁分屬不同存取庫,`[[...]]` 連不過去,畫面上還看不出壞掉。 +> 連結寫法:問詢紀錄那一欄一律寫成 `[{文字}]({連結})`,連結取自 `jsc-gitea/tools/gitea.sh wiki-url` 印出的絕對網址,不自行組路徑。兩頁分屬不同存取庫,只有絕對網址連得過去。 +> +> 連結驗證:這一列寫進頁面前,先把該列的每一個連結交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入。有任一筆 DEAD 就整列不寫,把連不到的清單回報給呼叫端。結束碼 3 代表 `GITEA_HOST` 沒設定,先設定再驗證,不得跳過;結束碼 7 代表金鑰失效,停下來回報,別把還在的頁當成死連結。 | 存取庫名稱 | 問詢紀錄 | 最後更新 | | --- | --- | --- | -- 2.53.0 From 12e79ee3af68ec33b9851ed3279ab337efcc99d5 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 2 Sep 2026 14:27:18 +0800 Subject: [PATCH 2/6] =?UTF-8?q?chore(plugin=20=E7=89=88=E6=9C=AC):=20?= =?UTF-8?q?=E4=B8=89=E4=BB=BD=20manifest=20=E5=8D=87=E7=89=88=E8=87=B3=200?= =?UTF-8?q?.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 880bb34..d5ae74d 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-ask", - "version": "0.1.0", + "version": "0.1.1", "description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 33d40bd..7b2e74e 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-ask", - "version": "0.1.0", + "version": "0.1.1", "description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)", "skills": "./skills", "jsc": { diff --git a/plugin.json b/plugin.json index d9fe380..fba80ed 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-ask", - "version": "0.1.0", + "version": "0.1.1", "description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)", "skills": "./skills/", "jsc": { -- 2.53.0 From df317adad8ad7bcf4541754a8e66161d09e515b8 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 2 Sep 2026 16:01:12 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feat(=E7=8B=80=E6=85=8B=E5=9B=9E=E5=A0=B1):?= =?UTF-8?q?=20=E6=94=B6=E5=B0=BE=E5=AF=AB=E4=B8=80=E7=AD=86=20skill-end=20?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 現行紀錄只記「被叫用」,沒有成敗也沒有結束碼。跑完整輪的技能與開場就 中止的技能,在紀錄裡長得一模一樣。 start 由技能用量 hook 順手發,不必改技能文件。end 只能由技能自己在收尾 步驟寫——hook 接在技能工具呼叫上,而實際工作發生在之後的模型輪次,它在 原理上看不到成敗。有 start 沒有配對的 end,就是那一輪中止了。 status 五選一,每支技能各自寫明什麼情況選哪一個。找不到回報腳本就安靜 跳過,回報失敗一律不改變技能自己的結論。 --- references/behaviors.md | 6 +++--- skills/ask/SKILL.md | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/references/behaviors.md b/references/behaviors.md index 9ac3dba..4e51f70 100644 --- a/references/behaviors.md +++ b/references/behaviors.md @@ -7,7 +7,7 @@ | 項目 | 內容 | | --- | --- | | 觸發時機 | 任何 jsc 技能需要使用者做決定時叫用。答案已經寫在 `QUESTION_{HASH}` 且意圖相符時不叫用,直接採用舊答案。答案查得到程式碼、設定檔或環境變數時不叫用,自己去查。 | -| 關鍵步驟 | 確認目前工作的 `{owner}/{repo}`、執行 `jsc-gitea/tools/hash-id {owner}/{repo}` 取得完整 40 碼大寫 `HASH`、每個工作階段各解析一次 `QUESTION` 與 `CONTENTS` 兩個 wiki 存取庫、讀入 `QUESTION_CONTENTS`(`CONTENTS` 庫)與 `QUESTION_{HASH}`(`QUESTION` 庫)並留成階段副本、把這一輪每一題標成「照紀錄回答」或「要問」、以決策樹選單一次問一個決策點且每個選項標明影響範圍、依答案決定下一題直到沒有疑慮、交一個 sub agent 做兩次寫入:先把新一節要放進 `QUESTION_{HASH}` 的每個連結交給 `jsc-gitea/tools/link-check.sh` 驗證,退 0 才用 `jsc-gitea:wiki` 寫那一節;再用 `jsc-gitea/tools/gitea.sh wiki-url` 取紀錄頁的絕對網址並依結束碼分流(4 是紀錄頁還沒寫上去要重寫、5 是沒有 `html_url`、7 金鑰失效、8 重試一次,其餘非 0 一律停下並註明那一列沒有被索引,網址取不到就不寫那一列)、把該列的連結再交給 `link-check.sh` 驗證,退 0 才用 `jsc-gitea/tools/wiki-contents.sh upsert` 把單列寫進 `QUESTION_CONTENTS` 並依結束碼分流、寫入後同步更新階段副本、把答案交回叫用的技能。頁面裡的連結一律寫成 `[{文字}]({連結})`,wiki 連結取自 `wiki-url` 印出的絕對網址,不自行組路徑。 | +| 關鍵步驟 | 確認目前工作的 `{owner}/{repo}`、執行 `jsc-gitea/tools/hash-id {owner}/{repo}` 取得完整 40 碼大寫 `HASH`、每個工作階段各解析一次 `QUESTION` 與 `CONTENTS` 兩個 wiki 存取庫、讀入 `QUESTION_CONTENTS`(`CONTENTS` 庫)與 `QUESTION_{HASH}`(`QUESTION` 庫)並留成階段副本、把這一輪每一題標成「照紀錄回答」或「要問」、以決策樹選單一次問一個決策點且每個選項標明影響範圍、依答案決定下一題直到沒有疑慮、交一個 sub agent 做兩次寫入:先把新一節要放進 `QUESTION_{HASH}` 的每個連結交給 `jsc-gitea/tools/link-check.sh` 驗證,退 0 才用 `jsc-gitea:wiki` 寫那一節;再用 `jsc-gitea/tools/gitea.sh wiki-url` 取紀錄頁的絕對網址並依結束碼分流(4 是紀錄頁還沒寫上去要重寫、5 是沒有 `html_url`、7 金鑰失效、8 重試一次,其餘非 0 一律停下並註明那一列沒有被索引,網址取不到就不寫那一列)、把該列的連結再交給 `link-check.sh` 驗證,退 0 才用 `jsc-gitea/tools/wiki-contents.sh upsert` 把單列寫進 `QUESTION_CONTENTS` 並依結束碼分流、寫入後同步更新階段副本、把答案交回叫用的技能、收尾呼叫 `jsc-hooks/tools/report-status.sh skill-end jsc-ask:ask {status} {結束碼} {detail}` 記下這一輪怎麼結束(腳本路徑比照 `jsc-gitea/tools/hash-id` 的解析方式,檔案不在就安靜跳過,不得讓回報失敗變成問詢失敗)。頁面裡的連結一律寫成 `[{文字}]({連結})`,wiki 連結取自 `wiki-url` 印出的絕對網址,不自行組路徑。 | | 外部呼叫 | `jsc-gitea/tools/hash-id`、`jsc-gitea:wiki`(`wiki-repo QUESTION`、`wiki-repo CONTENTS`、`wiki-get`、寫入)、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-gitea/tools/gitea.sh wiki-url`(目錄頁連到紀錄頁的絕對網址)、`jsc-gitea/tools/link-check.sh`(兩頁寫入前各驗證一次連結)、AskUserQuestion 或等效選單、一個負責兩次 wiki 寫入的 sub agent;範本 `templates/question-record.md`、`templates/question-contents.md` | -| 完成條件 | 這一輪每一題都有答案,來源是紀錄或使用者;答案交回叫用的技能;有存取庫名稱時,sub agent 回報兩頁都寫成功(`wiki-contents.sh` 退 0),主代理接受那一次回報。沒有存取庫名稱時,問完直接交回答案,不寫任何頁。目錄頁寫不成的另一種完成條件:`wiki-contents.sh` 退 2、退 3、退 7,或 `wiki-url` 取不到網址,四種都算這一步走完——紀錄頁記成「已寫、未被索引」,回報講明結束碼與沒寫成的那一頁,答案照樣交回叫用的技能。退 3 特別要交回答案:`JSC_WIKI_REPO_CONTENTS` 在 `jsc-cli/tools/config-spec.tsv` 是 `fix=ask`,`/jsc-cli:setup` 只能靠這一支問到值,答案在這裡被吞掉,變數就永遠設不起來。退 4 代表範本參數被漏掉了,本技能的呼叫一律帶第五個參數,所以不會出現;範本路徑不存在回的是 2。`link-check.sh` 非 0 也算這一步走完:退 1 就那一頁不寫並回報 DEAD 清單,退 2 補參數重跑,退 3 先設定 `GITEA_HOST` 再驗證,退 7 停下來回報金鑰,四種都不得跳過驗證直接寫入。停止執行並回報的情況:`hash-id` 找不到 SHA-1 工具、wiki 讀取非 exit 4 的失敗、重試後仍寫不進去(`wiki-contents.sh` 退 1 或 8)。寫入失敗一律把答案交回並註明沒有記錄。 | -| 可驗證跡象 | `QUESTION` 存取庫的 wiki `QUESTION_{HASH}` 頁尾多一節,頁名是完整 40 碼大寫十六進位,開頭是使用者意圖,底下每題一張選項與影響範圍的表,附答案與時間;`CONTENTS` 存取庫的 wiki `QUESTION_CONTENTS` 該存取庫那一列的「最後更新」變成這次執行的時間戳,沒有該列就新增一列,該列的問詢紀錄欄是 `[QUESTION_{HASH}]({絕對網址})`,網址與 `wiki-url` 印出的一字不差,別的存取庫那幾列一字不動;兩頁寫進去的每個連結都通得過 `link-check.sh`,驗不過那一輪頁面停在舊內容,回報裡有 DEAD 清單或結束碼。沒有 `{owner}/{repo}` 時無寫入跡象,只有回報內容。 | +| 完成條件 | 這一輪每一題都有答案,來源是紀錄或使用者;答案交回叫用的技能;有存取庫名稱時,sub agent 回報兩頁都寫成功(`wiki-contents.sh` 退 0),主代理接受那一次回報。沒有存取庫名稱時,問完直接交回答案,不寫任何頁。目錄頁寫不成的另一種完成條件:`wiki-contents.sh` 退 2、退 3、退 7,或 `wiki-url` 取不到網址,四種都算這一步走完——紀錄頁記成「已寫、未被索引」,回報講明結束碼與沒寫成的那一頁,答案照樣交回叫用的技能。退 3 特別要交回答案:`JSC_WIKI_REPO_CONTENTS` 在 `jsc-cli/tools/config-spec.tsv` 是 `fix=ask`,`/jsc-cli:setup` 只能靠這一支問到值,答案在這裡被吞掉,變數就永遠設不起來。退 4 代表範本參數被漏掉了,本技能的呼叫一律帶第五個參數,所以不會出現;範本路徑不存在回的是 2。`link-check.sh` 非 0 也算這一步走完:退 1 就那一頁不寫並回報 DEAD 清單,退 2 補參數重跑,退 3 先設定 `GITEA_HOST` 再驗證,退 7 停下來回報金鑰,四種都不得跳過驗證直接寫入。停止執行並回報的情況:`hash-id` 找不到 SHA-1 工具、wiki 讀取非 exit 4 的失敗、重試後仍寫不進去(`wiki-contents.sh` 退 1 或 8)。寫入失敗一律把答案交回並註明沒有記錄。以上每一條路線都要走完最後一步:呼叫 `report-status.sh skill-end`,狀態五選一——答案齊全且該寫的兩頁都寫成是 `ok`;問詢做完但記錄少了一塊是 `degraded`,涵蓋沒有 `{owner}/{repo}` 因而完全不記錄,以及紀錄頁寫成、目錄頁沒寫成;使用者中止那一輪是 `aborted`;停手而且答案沒交回去是 `failed`。閘門擋下的情形不在這裡出現,被擋的技能根本走不到這一步,所以不用 `blocked`。腳本不在磁碟上就跳過,這一步照樣算走完。 | +| 可驗證跡象 | `QUESTION` 存取庫的 wiki `QUESTION_{HASH}` 頁尾多一節,頁名是完整 40 碼大寫十六進位,開頭是使用者意圖,底下每題一張選項與影響範圍的表,附答案與時間;`CONTENTS` 存取庫的 wiki `QUESTION_CONTENTS` 該存取庫那一列的「最後更新」變成這次執行的時間戳,沒有該列就新增一列,該列的問詢紀錄欄是 `[QUESTION_{HASH}]({絕對網址})`,網址與 `wiki-url` 印出的一字不差,別的存取庫那幾列一字不動;兩頁寫進去的每個連結都通得過 `link-check.sh`,驗不過那一輪頁面停在舊內容,回報裡有 DEAD 清單或結束碼。沒有 `{owner}/{repo}` 時無 wiki 寫入跡象,只有回報內容。不論走哪一條路線,`$JSC_HOME/usage/events.jsonl` 尾端都會多一筆 `{kind:skill,phase:end}` 事件,`name` 是 `jsc-ask:ask`,`status` 與這一輪的結局相符,`exit` 是決定結局的那支工具的結束碼;`report-status.sh` 不在那台機器上就沒有這一筆,而問詢結果一字不變。 | diff --git a/skills/ask/SKILL.md b/skills/ask/SKILL.md index b40d1e0..58743db 100644 --- a/skills/ask/SKILL.md +++ b/skills/ask/SKILL.md @@ -43,3 +43,7 @@ Inside one work session this skill is the only writer of `QUESTION_{HASH}` and ` - Branch on `wiki-contents.sh`'s exit code, every code its own branch. 0: the row was added or updated, so this round is recorded. 1: the write failed, so retry once per step 3. 2: usage error, so stop and report the arguments that were passed — the same call retried fails the same way. A template path that does not exist lands here too, and means the plugin installation is incomplete. 3: no `CONTENTS` repo is configured, so report that `JSC_WIKI_REPO_CONTENTS` (or `JSC_WIKI_REPO`) must be set, note `QUESTION_{HASH}` as written but unindexed, and hand this round's answers back to the calling skill all the same. **Never withhold the answers over this code.** `JSC_WIKI_REPO_CONTENTS` is a `fix=ask` item in `jsc-cli/tools/config-spec.tsv`, so `/jsc-cli:setup` can only learn its value by asking through this skill; an answer dropped here leaves the variable unset, and the unset variable makes the next round drop the answer again. 4: the page does not exist and no template reached the script. The call form above always passes the template as the fifth argument, so this code cannot come out of it — getting it means that argument was dropped, so restore it and run the same call once more. 7: the key is invalid or has no permission, so stop and report without retrying. 8: any other API failure, so retry once per step 3, then stop and report. - Done when the sub agent has reported both writes as succeeded and the main agent has accepted that one report — this single confirmation covers both pages and both templates; do not re-read the pages to prove it. Exit 2, 3 or 7 from `wiki-contents.sh`, any `wiki-url` failure, and any non-zero `link-check.sh` exit on the directory row, close this step the second way: `QUESTION_{HASH}` counts as written and unindexed, the report names the exit code and the page that stayed unwritten, and this round's answers go back to the calling skill regardless. **The answers of a finished questioning round are never lost because the directory page could not be written.** 3. On a reported write failure, retry that one page once — a `QUESTION_{HASH}` failure, or `wiki-contents.sh` exit 1 or 8. Exit 2, 3 and 7 are not retried, because the same call fails identically until someone fixes the arguments, the environment or the key; they end the recording, not the round. Every one of these paths ends the same way: the answers go back to the calling skill, and the report names the page that stayed unwritten, the exit code behind it, and what was recorded and what was not. Done when the retry succeeded and step 3 of "Session cache" refreshed that page's copy, or — when the retry also failed, or the code was 2, 3 or 7 — the report above has been printed and the answers have been handed back. +4. Record how this run ended. This is the closing step and it runs on every route above, the ones that stopped early included. Run `jsc-hooks/tools/report-status.sh skill-end jsc-ask:ask {status} {exit} "{detail}"`, resolving the script the same sibling-plugin way this skill already resolves `jsc-gitea/tools/hash-id`. The hook that records a skill's start cannot see how it ended — it fires when the skill is loaded, and the work happens in later turns — so a start with no matching end is exactly what an abandoned run looks like, and writing this line is what keeps a finished run from reading as one. + - `{status}` is one of five. `ok`: every question of this round carries an answer, the answers went back to the calling skill, and where `{owner}/{repo}` was known both wiki pages were written. `degraded`: the round finished and part of the recording did not — no `{owner}/{repo}` was known so nothing was recorded at all, or `QUESTION_{HASH}` landed while `QUESTION_CONTENTS` did not (`wiki-contents.sh` exit 2, 3 or 7, any `wiki-url` failure, or a non-zero `link-check.sh` on the directory row). Those stay `degraded` rather than `failed` because the answers still reached the caller, which is this skill's own completion condition. `aborted`: the user stopped the questioning round, so the undecided points were never put to them. `failed`: the run stopped and no answers came back — `hash-id` found no SHA-1 helper, a wiki read failed with anything other than exit 4, or the one retry of a page write failed as well. `blocked` does not arise here, because anything that gates this skill stops it before it ever reaches this step. + - `{exit}` is the exit code of the tool that decided the outcome where one did, and otherwise 0 for `ok` and 1 for every other status. `{detail}` is optional, one line, at most 200 characters: name the page that stayed unwritten, or the tool and the code that stopped the run. Anything longer belongs in the report to the caller, not on this line. + - Reporting never changes the outcome of the round. `report-status.sh` is not on every machine, so a missing file is skipped in silence and nothing else about the run changes; the script swallows its own write failures and always exits 0, so its result is never worth branching on. Done when the call was made, or the script was absent and this step was skipped without a word. -- 2.53.0 From 891e64a2ac7a6a4ecbeb001fa6661511e58e5c09 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 2 Sep 2026 16:01:12 +0800 Subject: [PATCH 4/6] =?UTF-8?q?chore(plugin=20=E7=89=88=E6=9C=AC):=20?= =?UTF-8?q?=E4=B8=89=E4=BB=BD=20manifest=20=E5=8D=87=E7=89=88=E8=87=B3=200?= =?UTF-8?q?.1.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index d5ae74d..19b8502 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-ask", - "version": "0.1.1", + "version": "0.1.2", "description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 7b2e74e..a129e88 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-ask", - "version": "0.1.1", + "version": "0.1.2", "description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)", "skills": "./skills", "jsc": { diff --git a/plugin.json b/plugin.json index fba80ed..5b123bc 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-ask", - "version": "0.1.1", + "version": "0.1.2", "description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)", "skills": "./skills/", "jsc": { -- 2.53.0 From e8294f389104c595d84c1cb8593b7738dd06fc27 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 2 Sep 2026 17:24:49 +0800 Subject: [PATCH 5/6] =?UTF-8?q?feat(ask):=20=E5=95=8F=E8=A9=A2=E7=9B=AE?= =?UTF-8?q?=E9=8C=84=E9=A0=81=E6=94=B9=E6=A2=9D=E5=88=97=E5=BC=8F=E4=B8=A6?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20key-col=20=E5=8F=83=E6=95=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What: 把問詢目錄頁 `QUESTION_CONTENTS` 的呈現格式從 markdown 表格改成條列式——每個存取庫一個 H2 區塊,H2 標題就是該存取庫問詢紀錄頁的頁名 `QUESTION_{HASH}`,欄位改成標題底下一層條列 `- {欄位名}:{值}`,順序維持存取庫名稱、問詢紀錄、最後更新,整頁不再留任何 markdown 表格。同一輪把 `wiki-contents.sh upsert QUESTION` 的呼叫參數從 `1 {owner}/{repo}` 改成 `2 QUESTION_{HASH}`。改動落在 `templates/question-contents.md` 的版面與參數語意、`skills/ask/SKILL.md` 與 `references/behaviors.md` 的步驟敘述與可驗證跡象,以及 `README.md` 的技能描述。 Why: 表格版面一列塞滿所有欄位,欄位一長就難讀,每一筆的鍵也埋在儲存格裡;改成一筆一個 H2 區塊之後,鍵就是標題文字,人與工具都直接看得出哪一筆對應哪一個內容頁。`key-col` 原本填 `1`,指到的是純文字的存取庫名稱欄:舊表格自動轉條列時,工具會拿那一欄的連結產生 H2 標題,填 `1` 只會做出 `## plugins/meta` 這種標題,比不到鍵 `QUESTION_{HASH}`,既有那一筆會被當成新的附加上去,同一個存取庫在頁面上出現兩個區塊,舊區塊從此再也更新不到。 How: `templates/question-contents.md` 的範本主體從表頭加資料列換成 `## QUESTION_{HASH}` 加三條 bullet,引言補上版面段與參數語意段,寫明 `{key}` 是 H2 標題文字、第四個參數帶的是整個區塊的 markdown 而不是單列。`skills/ask/SKILL.md` 把目錄頁那幾條的 row 敘述全部改成 block,呼叫式改為 `upsert QUESTION 2 QUESTION_{HASH} {block-file} templates/question-contents.md`,並說明 `key-col` 為什麼固定是 `2`、填 `1` 會壞成什麼樣,另外在 `wiki-contents.sh` 退 1 的分支補上「頁面上還沒有自己那個區塊不算失敗,那是附加的情形」。`references/behaviors.md` 的關鍵步驟補上區塊檔的組法與參數語意,外部呼叫改成單一 H2 區塊的讀取、合併、寫回,可驗證跡象改成檢查該區塊三條 bullet 的齊全度、順序、全形冒號與整頁不留表格,並補上舊表格轉條列後 H1 與 `>` 引言原樣保留、原有資料一筆不少。`README.md` 的技能描述同步改成條列式的說法。實際的轉檔與 upsert 邏輯在另一個存取庫的 `gitea/tools/wiki-contents.sh`,本存取庫只調整敘述與範本。 Who: 屬於 wiki 目錄頁一律改條列式呈現的需求,`ask` 這一支負責 `QUESTION_CONTENTS` 的範本與敘述。`key-col` 修正是同一個需求能正確落地的必要條件,且與版面敘述改在同一段文字上,無法拆成獨立提交。 --- README.md | 2 +- references/behaviors.md | 8 ++++---- skills/ask/SKILL.md | 12 ++++++------ templates/question-contents.md | 20 +++++++++++++------- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 31eeafe..b22506c 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安 ### `ask` -決策樹問詢:一題一個決策點、選項標明影響範圍、問到沒有疑慮為止。問前查 `QUESTION_CONTENTS` 與 `QUESTION_{HASH}`,其中 `{HASH}` 執行 `jsc-gitea/tools/hash-id` 產生,原樣採用它印出的完整 40 碼大寫十六進位,不截短、不加前綴;已有答案不再問;AI 取用舊答案時依意圖判斷適用性,意圖不符就重新問。兩頁分屬兩個存取庫:目錄頁 `QUESTION_CONTENTS` 走 `JSC_WIKI_REPO_CONTENTS`,紀錄頁 `QUESTION_{HASH}` 走 `JSC_WIKI_REPO_QUESTION`,兩者都退回 `JSC_WIKI_REPO`。兩頁在同一個工作階段只讀一次,之後沿用手上那份,自己寫入後就更新它;問出來的 wiki 存取庫也留在工作階段內重複使用。問後套範本寫回 wiki,紀錄頁每節開頭記錄使用者意圖,目錄頁用 `jsc-gitea/tools/wiki-contents.sh upsert` 只改自己那一列、以絕對網址連到紀錄頁(沒有存取庫名稱就不記錄)。不適用於答案已有紀錄的情況。 +決策樹問詢:一題一個決策點、選項標明影響範圍、問到沒有疑慮為止。問前查 `QUESTION_CONTENTS` 與 `QUESTION_{HASH}`,其中 `{HASH}` 執行 `jsc-gitea/tools/hash-id` 產生,原樣採用它印出的完整 40 碼大寫十六進位,不截短、不加前綴;已有答案不再問;AI 取用舊答案時依意圖判斷適用性,意圖不符就重新問。兩頁分屬兩個存取庫:目錄頁 `QUESTION_CONTENTS` 走 `JSC_WIKI_REPO_CONTENTS`,紀錄頁 `QUESTION_{HASH}` 走 `JSC_WIKI_REPO_QUESTION`,兩者都退回 `JSC_WIKI_REPO`。兩頁在同一個工作階段只讀一次,之後沿用手上那份,自己寫入後就更新它;問出來的 wiki 存取庫也留在工作階段內重複使用。問後套範本寫回 wiki,紀錄頁每節開頭記錄使用者意圖,目錄頁是條列式:每個存取庫一個 H2 區塊,標題就是內容頁頁名 `QUESTION_{HASH}`,欄位一條一行,用 `jsc-gitea/tools/wiki-contents.sh upsert` 只改自己那個區塊、以絕對網址連到紀錄頁(沒有存取庫名稱就不記錄)。不適用於答案已有紀錄的情況。 diff --git a/references/behaviors.md b/references/behaviors.md index 4e51f70..b4aa161 100644 --- a/references/behaviors.md +++ b/references/behaviors.md @@ -7,7 +7,7 @@ | 項目 | 內容 | | --- | --- | | 觸發時機 | 任何 jsc 技能需要使用者做決定時叫用。答案已經寫在 `QUESTION_{HASH}` 且意圖相符時不叫用,直接採用舊答案。答案查得到程式碼、設定檔或環境變數時不叫用,自己去查。 | -| 關鍵步驟 | 確認目前工作的 `{owner}/{repo}`、執行 `jsc-gitea/tools/hash-id {owner}/{repo}` 取得完整 40 碼大寫 `HASH`、每個工作階段各解析一次 `QUESTION` 與 `CONTENTS` 兩個 wiki 存取庫、讀入 `QUESTION_CONTENTS`(`CONTENTS` 庫)與 `QUESTION_{HASH}`(`QUESTION` 庫)並留成階段副本、把這一輪每一題標成「照紀錄回答」或「要問」、以決策樹選單一次問一個決策點且每個選項標明影響範圍、依答案決定下一題直到沒有疑慮、交一個 sub agent 做兩次寫入:先把新一節要放進 `QUESTION_{HASH}` 的每個連結交給 `jsc-gitea/tools/link-check.sh` 驗證,退 0 才用 `jsc-gitea:wiki` 寫那一節;再用 `jsc-gitea/tools/gitea.sh wiki-url` 取紀錄頁的絕對網址並依結束碼分流(4 是紀錄頁還沒寫上去要重寫、5 是沒有 `html_url`、7 金鑰失效、8 重試一次,其餘非 0 一律停下並註明那一列沒有被索引,網址取不到就不寫那一列)、把該列的連結再交給 `link-check.sh` 驗證,退 0 才用 `jsc-gitea/tools/wiki-contents.sh upsert` 把單列寫進 `QUESTION_CONTENTS` 並依結束碼分流、寫入後同步更新階段副本、把答案交回叫用的技能、收尾呼叫 `jsc-hooks/tools/report-status.sh skill-end jsc-ask:ask {status} {結束碼} {detail}` 記下這一輪怎麼結束(腳本路徑比照 `jsc-gitea/tools/hash-id` 的解析方式,檔案不在就安靜跳過,不得讓回報失敗變成問詢失敗)。頁面裡的連結一律寫成 `[{文字}]({連結})`,wiki 連結取自 `wiki-url` 印出的絕對網址,不自行組路徑。 | -| 外部呼叫 | `jsc-gitea/tools/hash-id`、`jsc-gitea:wiki`(`wiki-repo QUESTION`、`wiki-repo CONTENTS`、`wiki-get`、寫入)、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-gitea/tools/gitea.sh wiki-url`(目錄頁連到紀錄頁的絕對網址)、`jsc-gitea/tools/link-check.sh`(兩頁寫入前各驗證一次連結)、AskUserQuestion 或等效選單、一個負責兩次 wiki 寫入的 sub agent;範本 `templates/question-record.md`、`templates/question-contents.md` | -| 完成條件 | 這一輪每一題都有答案,來源是紀錄或使用者;答案交回叫用的技能;有存取庫名稱時,sub agent 回報兩頁都寫成功(`wiki-contents.sh` 退 0),主代理接受那一次回報。沒有存取庫名稱時,問完直接交回答案,不寫任何頁。目錄頁寫不成的另一種完成條件:`wiki-contents.sh` 退 2、退 3、退 7,或 `wiki-url` 取不到網址,四種都算這一步走完——紀錄頁記成「已寫、未被索引」,回報講明結束碼與沒寫成的那一頁,答案照樣交回叫用的技能。退 3 特別要交回答案:`JSC_WIKI_REPO_CONTENTS` 在 `jsc-cli/tools/config-spec.tsv` 是 `fix=ask`,`/jsc-cli:setup` 只能靠這一支問到值,答案在這裡被吞掉,變數就永遠設不起來。退 4 代表範本參數被漏掉了,本技能的呼叫一律帶第五個參數,所以不會出現;範本路徑不存在回的是 2。`link-check.sh` 非 0 也算這一步走完:退 1 就那一頁不寫並回報 DEAD 清單,退 2 補參數重跑,退 3 先設定 `GITEA_HOST` 再驗證,退 7 停下來回報金鑰,四種都不得跳過驗證直接寫入。停止執行並回報的情況:`hash-id` 找不到 SHA-1 工具、wiki 讀取非 exit 4 的失敗、重試後仍寫不進去(`wiki-contents.sh` 退 1 或 8)。寫入失敗一律把答案交回並註明沒有記錄。以上每一條路線都要走完最後一步:呼叫 `report-status.sh skill-end`,狀態五選一——答案齊全且該寫的兩頁都寫成是 `ok`;問詢做完但記錄少了一塊是 `degraded`,涵蓋沒有 `{owner}/{repo}` 因而完全不記錄,以及紀錄頁寫成、目錄頁沒寫成;使用者中止那一輪是 `aborted`;停手而且答案沒交回去是 `failed`。閘門擋下的情形不在這裡出現,被擋的技能根本走不到這一步,所以不用 `blocked`。腳本不在磁碟上就跳過,這一步照樣算走完。 | -| 可驗證跡象 | `QUESTION` 存取庫的 wiki `QUESTION_{HASH}` 頁尾多一節,頁名是完整 40 碼大寫十六進位,開頭是使用者意圖,底下每題一張選項與影響範圍的表,附答案與時間;`CONTENTS` 存取庫的 wiki `QUESTION_CONTENTS` 該存取庫那一列的「最後更新」變成這次執行的時間戳,沒有該列就新增一列,該列的問詢紀錄欄是 `[QUESTION_{HASH}]({絕對網址})`,網址與 `wiki-url` 印出的一字不差,別的存取庫那幾列一字不動;兩頁寫進去的每個連結都通得過 `link-check.sh`,驗不過那一輪頁面停在舊內容,回報裡有 DEAD 清單或結束碼。沒有 `{owner}/{repo}` 時無 wiki 寫入跡象,只有回報內容。不論走哪一條路線,`$JSC_HOME/usage/events.jsonl` 尾端都會多一筆 `{kind:skill,phase:end}` 事件,`name` 是 `jsc-ask:ask`,`status` 與這一輪的結局相符,`exit` 是決定結局的那支工具的結束碼;`report-status.sh` 不在那台機器上就沒有這一筆,而問詢結果一字不變。 | +| 關鍵步驟 | 確認目前工作的 `{owner}/{repo}`、執行 `jsc-gitea/tools/hash-id {owner}/{repo}` 取得完整 40 碼大寫 `HASH`、每個工作階段各解析一次 `QUESTION` 與 `CONTENTS` 兩個 wiki 存取庫、讀入 `QUESTION_CONTENTS`(`CONTENTS` 庫)與 `QUESTION_{HASH}`(`QUESTION` 庫)並留成階段副本、把這一輪每一題標成「照紀錄回答」或「要問」、以決策樹選單一次問一個決策點且每個選項標明影響範圍、依答案決定下一題直到沒有疑慮、交一個 sub agent 做兩次寫入:先把新一節要放進 `QUESTION_{HASH}` 的每個連結交給 `jsc-gitea/tools/link-check.sh` 驗證,退 0 才用 `jsc-gitea:wiki` 寫那一節;再用 `jsc-gitea/tools/gitea.sh wiki-url` 取紀錄頁的絕對網址並依結束碼分流(4 是紀錄頁還沒寫上去要重寫、5 是沒有 `html_url`、7 金鑰失效、8 重試一次,其餘非 0 一律停下並註明那個區塊沒有被索引,網址取不到就不寫那個區塊)、備妥自己那個 H2 區塊的區塊檔(`## QUESTION_{HASH}` 那一行、空行,接存取庫名稱、問詢紀錄、最後更新三條 bullet,格式 `- {欄位名}:{值}`)、把區塊裡的連結再交給 `link-check.sh` 驗證,退 0 才執行 `jsc-gitea/tools/wiki-contents.sh upsert QUESTION 2 QUESTION_{HASH} {區塊檔} templates/question-contents.md` 把單一區塊寫進 `QUESTION_CONTENTS` 並依結束碼分流、寫入後同步更新階段副本、把答案交回叫用的技能、收尾呼叫 `jsc-hooks/tools/report-status.sh skill-end jsc-ask:ask {status} {結束碼} {detail}` 記下這一輪怎麼結束(腳本路徑比照 `jsc-gitea/tools/hash-id` 的解析方式,檔案不在就安靜跳過,不得讓回報失敗變成問詢失敗)。目錄頁的鍵是 H2 標題文字,也就是內容頁頁名 `QUESTION_{HASH}`,標題不放連結、不放網址、不加前後綴;第三個參數帶的就是這個頁名,第二個參數是 `2`,指舊表格裡持有內容頁連結那一欄的欄位序號(1 起算)——`QUESTION_CONTENTS` 的舊表格欄序是存取庫名稱、問詢紀錄、最後更新,帶連結的是第 2 欄「問詢紀錄」。這個參數只在舊頁還是 markdown 表格、要自動轉成條列時才有作用,但不是死參數也不能隨便填:轉檔時工具會拿那一欄的連結產生 H2 標題,填成 `1` 就取到純文字的 `{owner}/{repo}`,標題變成 `## plugins/meta`,比不到鍵 `QUESTION_{HASH}`,既有那一筆會被當成新的附加上去,同一個存取庫在頁面上出現兩次,舊區塊從此再也更新不到。頁面裡的連結一律寫成 `[{文字}]({連結})`,wiki 連結取自 `wiki-url` 印出的絕對網址,不自行組路徑。 | +| 外部呼叫 | `jsc-gitea/tools/hash-id`、`jsc-gitea:wiki`(`wiki-repo QUESTION`、`wiki-repo CONTENTS`、`wiki-get`、寫入)、`jsc-gitea/tools/wiki-contents.sh upsert`(單一 H2 區塊的讀取、合併、寫回)、`jsc-gitea/tools/gitea.sh wiki-url`(目錄頁連到紀錄頁的絕對網址)、`jsc-gitea/tools/link-check.sh`(兩頁寫入前各驗證一次連結)、AskUserQuestion 或等效選單、一個負責兩次 wiki 寫入的 sub agent;範本 `templates/question-record.md`、`templates/question-contents.md` | +| 完成條件 | 這一輪每一題都有答案,來源是紀錄或使用者;答案交回叫用的技能;有存取庫名稱時,sub agent 回報兩頁都寫成功(`wiki-contents.sh` 退 0,代表該區塊被換掉或附加上去),主代理接受那一次回報,不再重讀頁面確認。沒有存取庫名稱時,問完直接交回答案,不寫任何頁。目錄頁寫不成的另一種完成條件:`wiki-contents.sh` 退 2、退 3、退 7,或 `wiki-url` 取不到網址,四種都算這一步走完——紀錄頁記成「已寫、未被索引」,回報講明結束碼與沒寫成的那一頁,答案照樣交回叫用的技能。退 3 特別要交回答案:`JSC_WIKI_REPO_CONTENTS` 在 `jsc-cli/tools/config-spec.tsv` 是 `fix=ask`,`/jsc-cli:setup` 只能靠這一支問到值,答案在這裡被吞掉,變數就永遠設不起來。退 1 是組不出頁面內容或寫入失敗,重試一次;頁面上沒有自己那個區塊不算失敗,那是附加的情形。退 4 代表範本參數被漏掉了,本技能的呼叫一律帶第五個參數,所以不會出現;範本路徑不存在回的是 2。`link-check.sh` 非 0 也算這一步走完:退 1 就那一頁不寫並回報 DEAD 清單,退 2 補參數重跑,退 3 先設定 `GITEA_HOST` 再驗證,退 7 停下來回報金鑰,四種都不得跳過驗證直接寫入。停止執行並回報的情況:`hash-id` 找不到 SHA-1 工具、wiki 讀取非 exit 4 的失敗、重試後仍寫不進去(`wiki-contents.sh` 退 1 或 8)。寫入失敗一律把答案交回並註明沒有記錄。以上每一條路線都要走完最後一步:呼叫 `report-status.sh skill-end`,狀態五選一——答案齊全且該寫的兩頁都寫成是 `ok`;問詢做完但記錄少了一塊是 `degraded`,涵蓋沒有 `{owner}/{repo}` 因而完全不記錄,以及紀錄頁寫成、目錄頁沒寫成;使用者中止那一輪是 `aborted`;停手而且答案沒交回去是 `failed`。閘門擋下的情形不在這裡出現,被擋的技能根本走不到這一步,所以不用 `blocked`。腳本不在磁碟上就跳過,這一步照樣算走完。 | +| 可驗證跡象 | `QUESTION` 存取庫的 wiki `QUESTION_{HASH}` 頁尾多一節,頁名是完整 40 碼大寫十六進位,開頭是使用者意圖,底下每題一張選項與影響範圍的表,附答案與時間;`CONTENTS` 存取庫的 wiki `QUESTION_CONTENTS` 上,標題為 `QUESTION_{HASH}` 的那個 H2 區塊的「最後更新」那一條變成這次執行的時間戳,沒有該區塊就在頁尾新增一個,區塊裡三條 bullet 齊全且順序是存取庫名稱、問詢紀錄、最後更新,格式 `- {欄位名}:{值}` 用全形冒號,問詢紀錄那一條是 `[QUESTION_{HASH}]({絕對網址})`,網址與 `wiki-url` 印出的一字不差,別的存取庫那幾個區塊一字不動,整頁不留 markdown 表格;舊頁本來是表格時,這一次寫入後整頁已轉成條列,H1 與 `>` 引言原樣保留,原有資料一筆不少、順序不變。兩頁寫進去的每個連結都通得過 `link-check.sh`,驗不過那一輪頁面停在舊內容,回報裡有 DEAD 清單或結束碼。沒有 `{owner}/{repo}` 時無 wiki 寫入跡象,只有回報內容。不論走哪一條路線,`$JSC_HOME/usage/events.jsonl` 尾端都會多一筆 `{kind:skill,phase:end}` 事件,`name` 是 `jsc-ask:ask`,`status` 與這一輪的結局相符,`exit` 是決定結局的那支工具的結束碼;`report-status.sh` 不在那台機器上就沒有這一筆,而問詢結果一字不變。 | diff --git a/skills/ask/SKILL.md b/skills/ask/SKILL.md index 58743db..db8d13b 100644 --- a/skills/ask/SKILL.md +++ b/skills/ask/SKILL.md @@ -37,13 +37,13 @@ Inside one work session this skill is the only writer of `QUESTION_{HASH}` and ` 2. After each Q&A round, hand both wiki writes to one sub agent, content page first and directory page second, so the directory never links a page that failed to write. - `QUESTION_{HASH}` is written through `jsc-gitea:wiki` — it owns the `JSC_WIKI_REPO_QUESTION` → `JSC_WIKI_REPO` resolution and the tea-token fallback, which a hand-rolled Gitea API call loses. Apply `templates/question-record.md`: append this round as a new section, keep every earlier section, and open the section with the user's original intent (why this questioning round happened), which lets a later reader judge why the user chose as they did and whether the answer still applies. `QUESTION_{HASH}` belongs to this one repository, which is why appending a section is the right shape there. Write every link in the section as `[{text}]({url})`, and take each wiki `{url}` from `jsc-gitea/tools/gitea.sh wiki-url` — never assemble a path by hand. - Verify before writing: hand every link of the new section to `jsc-gitea/tools/link-check.sh` and branch on its exit code. 0: every link answers, so write the section. 1: at least one link is DEAD, so write nothing and report the DEAD lines to the calling skill — a dead link on a record page reads as a real page and nobody finds it again. 2: no URL reached the script, so pass the links again. 3: `GITEA_HOST` is unset, so report that it must be set and run the check again; never write a link that skipped the check. 7: the Gitea key failed, so stop and report the key problem — a failed key makes live pages look dead. A section carrying no link goes straight to the write. Done when the check exited 0 and the section is written, or nothing was written and the report names the failing links or the exit code. - - `QUESTION_CONTENTS` is written by one call to `jsc-gitea/tools/wiki-contents.sh upsert QUESTION 1 {owner}/{repo} {row-file} templates/question-contents.md`. `key-col` is the 1-based column index, not a column name, and column 1 of `QUESTION_CONTENTS` is 存取庫名稱 — that cell holds the bare `{owner}/{repo}` text. The script compares the whole cell, so pass the key exactly as the row file writes it. The row file holds this repository's single row, and its 問詢紀錄 cell is written as `[QUESTION_{HASH}]({url})`, where `{url}` is the absolute URL printed by `jsc-gitea/tools/gitea.sh wiki-url {question-repo} QUESTION_{HASH}`. Take that URL from the command only; never assemble a path by hand. The two pages sit in different repositories — the directory page in the `CONTENTS` repo, the record page in the `QUESTION` repo — so only an absolute URL crosses from one to the other. Always pass the template argument. The script owns the read-merge-write of that shared directory: it upserts this one row and touches no other repository's row, so do not read the page and rebuild it by hand. - - Branch on `wiki-url`'s exit code before the row is built, because a URL that never arrived leaves that cell silently empty and the row still looks written. 0: put the printed URL in the cell, verbatim. 4: `QUESTION_{HASH}` is not on the wiki yet, so the record-page write has not landed — write that page again, then ask for the URL once more. 5: the page carries no `html_url`, so report it and never assemble a URL by hand. 7: the key is invalid or has no permission, so stop without retrying. 8: any other API failure, so retry once, then stop. Any other non-zero exit stops the same way. Whenever no URL comes back, write no row at all — an empty or hand-made cell is worse than a missing one — stop, report the exit code, and state that this repository's row was not indexed this round. - - Verify before writing: hand that URL, and every other link the row carries, to `jsc-gitea/tools/link-check.sh`, and branch on its exit code. 0: the links answer, so run the upsert. 1: at least one link is DEAD, so write no row and report the DEAD lines; `QUESTION_{HASH}` counts as written and unindexed, and this round's answers still go back to the calling skill. 2: no URL reached the script, so pass the links again. 3: `GITEA_HOST` is unset, so report that it must be set and run the check again; never upsert a row whose link skipped the check. 7: the Gitea key failed, so stop and report the key problem — a failed key makes live pages look dead, and an unchecked row would carry the blame instead. Done when the check exited 0 and the upsert ran, or no row was written and the report names the failing links or the exit code. - - Branch on `wiki-contents.sh`'s exit code, every code its own branch. 0: the row was added or updated, so this round is recorded. 1: the write failed, so retry once per step 3. 2: usage error, so stop and report the arguments that were passed — the same call retried fails the same way. A template path that does not exist lands here too, and means the plugin installation is incomplete. 3: no `CONTENTS` repo is configured, so report that `JSC_WIKI_REPO_CONTENTS` (or `JSC_WIKI_REPO`) must be set, note `QUESTION_{HASH}` as written but unindexed, and hand this round's answers back to the calling skill all the same. **Never withhold the answers over this code.** `JSC_WIKI_REPO_CONTENTS` is a `fix=ask` item in `jsc-cli/tools/config-spec.tsv`, so `/jsc-cli:setup` can only learn its value by asking through this skill; an answer dropped here leaves the variable unset, and the unset variable makes the next round drop the answer again. 4: the page does not exist and no template reached the script. The call form above always passes the template as the fifth argument, so this code cannot come out of it — getting it means that argument was dropped, so restore it and run the same call once more. 7: the key is invalid or has no permission, so stop and report without retrying. 8: any other API failure, so retry once per step 3, then stop and report. - - Done when the sub agent has reported both writes as succeeded and the main agent has accepted that one report — this single confirmation covers both pages and both templates; do not re-read the pages to prove it. Exit 2, 3 or 7 from `wiki-contents.sh`, any `wiki-url` failure, and any non-zero `link-check.sh` exit on the directory row, close this step the second way: `QUESTION_{HASH}` counts as written and unindexed, the report names the exit code and the page that stayed unwritten, and this round's answers go back to the calling skill regardless. **The answers of a finished questioning round are never lost because the directory page could not be written.** + - `QUESTION_CONTENTS` is a list page: one H2 block per repository, the block's fields written one per line as `- {欄位名}:{值}` in the order 存取庫名稱、問詢紀錄、最後更新. It is written by one call to `jsc-gitea/tools/wiki-contents.sh upsert QUESTION 2 QUESTION_{HASH} {block-file} templates/question-contents.md`. `key` is the H2 heading text, which is this repository's record page name `QUESTION_{HASH}` — the script matches the heading text after trimming, so pass it exactly as the block file writes it, with no link, no URL and no affix. The key is the page name and nothing else, because the page name follows from `{owner}/{repo}` alone: renaming the host, pointing `JSC_WIKI_REPO_QUESTION` at another repository, or a change in Gitea's page-name encoding all leave it untouched, so the key still finds the existing block. `key-col` is `2`, and it is not a free choice: it is the 1-based index of the column that held the link to the content page in the legacy markdown table, which for `QUESTION_CONTENTS` is the second column, 問詢紀錄. The script reads it only while converting such a legacy table page, and it takes the H2 heading of each converted block from that column's link. Passing `1` there names the 存取庫名稱 column, whose plain `{owner}/{repo}` text becomes a heading like `## plugins/meta`, which never matches the key `QUESTION_{HASH}`; the existing record is then appended as a new block, the page carries the same repository twice, and the old block can never be updated again. The block file holds this repository's whole H2 block: the `## QUESTION_{HASH}` line, a blank line, then the three field lines. The 存取庫名稱 field keeps the bare `{owner}/{repo}` text, and the 問詢紀錄 field is written as `[QUESTION_{HASH}]({url})`, where `{url}` is the absolute URL printed by `jsc-gitea/tools/gitea.sh wiki-url {question-repo} QUESTION_{HASH}`. Take that URL from the command only; never assemble a path by hand. The two pages sit in different repositories — the directory page in the `CONTENTS` repo, the record page in the `QUESTION` repo — so only an absolute URL crosses from one to the other. Always pass the template argument. The script owns the read-merge-write of that shared directory: it upserts this one block and touches no other repository's block, so do not read the page and rebuild it by hand. + - Branch on `wiki-url`'s exit code before the block is built, because a URL that never arrived leaves that field silently empty and the block still looks written. 0: put the printed URL in the 問詢紀錄 field, verbatim. 4: `QUESTION_{HASH}` is not on the wiki yet, so the record-page write has not landed — write that page again, then ask for the URL once more. 5: the page carries no `html_url`, so report it and never assemble a URL by hand. 7: the key is invalid or has no permission, so stop without retrying. 8: any other API failure, so retry once, then stop. Any other non-zero exit stops the same way. Whenever no URL comes back, write no block at all — an empty or hand-made field is worse than a missing one — stop, report the exit code, and state that this repository's block was not indexed this round. + - Verify before writing: hand that URL, and every other link the block carries, to `jsc-gitea/tools/link-check.sh`, and branch on its exit code. 0: the links answer, so run the upsert. 1: at least one link is DEAD, so write no block and report the DEAD lines; `QUESTION_{HASH}` counts as written and unindexed, and this round's answers still go back to the calling skill. 2: no URL reached the script, so pass the links again. 3: `GITEA_HOST` is unset, so report that it must be set and run the check again; never upsert a block whose link skipped the check. 7: the Gitea key failed, so stop and report the key problem — a failed key makes live pages look dead, and an unchecked block would carry the blame instead. Done when the check exited 0 and the upsert ran, or no block was written and the report names the failing links or the exit code. + - Branch on `wiki-contents.sh`'s exit code, every code its own branch. 0: the block was added or updated, so this round is recorded. 1: the page content could not be built or the write failed, so retry once per step 3 — a page with no block for this repository is not an error, it just means the block is appended. 2: usage error, so stop and report the arguments that were passed — the same call retried fails the same way. A template path that does not exist lands here too, and means the plugin installation is incomplete. 3: no `CONTENTS` repo is configured, so report that `JSC_WIKI_REPO_CONTENTS` (or `JSC_WIKI_REPO`) must be set, note `QUESTION_{HASH}` as written but unindexed, and hand this round's answers back to the calling skill all the same. **Never withhold the answers over this code.** `JSC_WIKI_REPO_CONTENTS` is a `fix=ask` item in `jsc-cli/tools/config-spec.tsv`, so `/jsc-cli:setup` can only learn its value by asking through this skill; an answer dropped here leaves the variable unset, and the unset variable makes the next round drop the answer again. 4: the page does not exist and no template reached the script. The call form above always passes the template as the fifth argument, so this code cannot come out of it — getting it means that argument was dropped, so restore it and run the same call once more. 7: the key is invalid or has no permission, so stop and report without retrying. 8: any other API failure, so retry once per step 3, then stop and report. + - Done when the sub agent has reported both writes as succeeded and the main agent has accepted that one report — this single confirmation covers both pages and both templates; do not re-read the pages to prove it. Exit 2, 3 or 7 from `wiki-contents.sh`, any `wiki-url` failure, and any non-zero `link-check.sh` exit on the directory block, close this step the second way: `QUESTION_{HASH}` counts as written and unindexed, the report names the exit code and the page that stayed unwritten, and this round's answers go back to the calling skill regardless. **The answers of a finished questioning round are never lost because the directory page could not be written.** 3. On a reported write failure, retry that one page once — a `QUESTION_{HASH}` failure, or `wiki-contents.sh` exit 1 or 8. Exit 2, 3 and 7 are not retried, because the same call fails identically until someone fixes the arguments, the environment or the key; they end the recording, not the round. Every one of these paths ends the same way: the answers go back to the calling skill, and the report names the page that stayed unwritten, the exit code behind it, and what was recorded and what was not. Done when the retry succeeded and step 3 of "Session cache" refreshed that page's copy, or — when the retry also failed, or the code was 2, 3 or 7 — the report above has been printed and the answers have been handed back. 4. Record how this run ended. This is the closing step and it runs on every route above, the ones that stopped early included. Run `jsc-hooks/tools/report-status.sh skill-end jsc-ask:ask {status} {exit} "{detail}"`, resolving the script the same sibling-plugin way this skill already resolves `jsc-gitea/tools/hash-id`. The hook that records a skill's start cannot see how it ended — it fires when the skill is loaded, and the work happens in later turns — so a start with no matching end is exactly what an abandoned run looks like, and writing this line is what keeps a finished run from reading as one. - - `{status}` is one of five. `ok`: every question of this round carries an answer, the answers went back to the calling skill, and where `{owner}/{repo}` was known both wiki pages were written. `degraded`: the round finished and part of the recording did not — no `{owner}/{repo}` was known so nothing was recorded at all, or `QUESTION_{HASH}` landed while `QUESTION_CONTENTS` did not (`wiki-contents.sh` exit 2, 3 or 7, any `wiki-url` failure, or a non-zero `link-check.sh` on the directory row). Those stay `degraded` rather than `failed` because the answers still reached the caller, which is this skill's own completion condition. `aborted`: the user stopped the questioning round, so the undecided points were never put to them. `failed`: the run stopped and no answers came back — `hash-id` found no SHA-1 helper, a wiki read failed with anything other than exit 4, or the one retry of a page write failed as well. `blocked` does not arise here, because anything that gates this skill stops it before it ever reaches this step. + - `{status}` is one of five. `ok`: every question of this round carries an answer, the answers went back to the calling skill, and where `{owner}/{repo}` was known both wiki pages were written. `degraded`: the round finished and part of the recording did not — no `{owner}/{repo}` was known so nothing was recorded at all, or `QUESTION_{HASH}` landed while `QUESTION_CONTENTS` did not (`wiki-contents.sh` exit 2, 3 or 7, any `wiki-url` failure, or a non-zero `link-check.sh` on the directory block). Those stay `degraded` rather than `failed` because the answers still reached the caller, which is this skill's own completion condition. `aborted`: the user stopped the questioning round, so the undecided points were never put to them. `failed`: the run stopped and no answers came back — `hash-id` found no SHA-1 helper, a wiki read failed with anything other than exit 4, or the one retry of a page write failed as well. `blocked` does not arise here, because anything that gates this skill stops it before it ever reaches this step. - `{exit}` is the exit code of the tool that decided the outcome where one did, and otherwise 0 for `ok` and 1 for every other status. `{detail}` is optional, one line, at most 200 characters: name the page that stayed unwritten, or the tool and the code that stopped the run. Anything longer belongs in the report to the caller, not on this line. - Reporting never changes the outcome of the round. `report-status.sh` is not on every machine, so a missing file is skipped in silence and nothing else about the run changes; the script swallows its own write failures and always exits 0, so its result is never worth branching on. Done when the call was made, or the script was absent and this step was skipped without a word. diff --git a/templates/question-contents.md b/templates/question-contents.md index a00916d..7ad0ada 100644 --- a/templates/question-contents.md +++ b/templates/question-contents.md @@ -1,13 +1,19 @@ # 問詢目錄 -> 由 `jsc-ask:ask` 維護。這是問詢目錄頁 `QUESTION_CONTENTS`,落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,和問詢紀錄頁不同庫。每個存取庫一列;`QUESTION_{HASH}` 的 `{HASH}` 執行 `jsc-gitea/tools/hash-id {owner}/{repo}` 取得,原樣採用它印出的完整 40 碼大寫十六進位,不截短、不加前綴(共用 wiki hash 規則,演算法見 `jsc-meta` 的 `references/guidelines.md`)。 +> 由 `jsc-ask:ask` 維護。這是問詢目錄頁 `QUESTION_CONTENTS`,落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,和問詢紀錄頁不同庫。每個存取庫一個區塊;`QUESTION_{HASH}` 的 `{HASH}` 執行 `jsc-gitea/tools/hash-id {owner}/{repo}` 取得,原樣採用它印出的完整 40 碼大寫十六進位,不截短、不加前綴(共用 wiki hash 規則,演算法見 `jsc-meta` 的 `references/guidelines.md`)。 > -> 寫入語意:一列代表一個存取庫。一律用 `jsc-gitea/tools/wiki-contents.sh upsert` 寫入:它讀回整頁,該存取庫已經有列就更新那一列,沒有才在文末附加一列,最後整頁寫回。只動自己那一列,禁止整頁覆蓋,也不得改動別人的列。 +> 版面:H1 頁名、這段 `>` 引言,然後每個存取庫一個 H2 區塊。H2 標題就是該存取庫的問詢紀錄頁頁名 `QUESTION_{HASH}`,標題不放連結、不放網址、不加前後綴。欄位一條一行,格式 `- {欄位名}:{值}`,全形冒號,順序是存取庫名稱、問詢紀錄、最後更新。標題與第一條之間空一行,區塊之間空一行。頁面上不留 markdown 表格。 > -> 連結寫法:問詢紀錄那一欄一律寫成 `[{文字}]({連結})`,連結取自 `jsc-gitea/tools/gitea.sh wiki-url` 印出的絕對網址,不自行組路徑。兩頁分屬不同存取庫,只有絕對網址連得過去。 +> 寫入語意:一個區塊代表一個存取庫。一律用 `jsc-gitea/tools/wiki-contents.sh upsert` 寫入:它讀回整頁,該存取庫已經有區塊就整塊換掉,沒有才在文末附加一個區塊,最後整頁寫回。只動自己那個區塊,禁止整頁覆蓋,也不得改動別人的區塊。 > -> 連結驗證:這一列寫進頁面前,先把該列的每一個連結交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入。有任一筆 DEAD 就整列不寫,把連不到的清單回報給呼叫端。結束碼 3 代表 `GITEA_HOST` 沒設定,先設定再驗證,不得跳過;結束碼 7 代表金鑰失效,停下來回報,別把還在的頁當成死連結。 +> 參數語意:`upsert QUESTION 2 {key} {區塊檔} {範本}`。第二個參數是 `{key-col}`,指舊表格裡持有內容頁連結那一欄的欄位序號(1 起算);本頁舊表格的欄序是存取庫名稱、問詢紀錄、最後更新,帶連結的是第 2 欄「問詢紀錄」,所以這裡固定帶 `2`。它只在舊頁還是 markdown 表格、要自動轉成條列時才用得到,頁面已經是條列格式就完全忽略它;但它不是死參數,也不能隨便填:轉檔時工具會拿那一欄的連結產生 H2 標題,填成 `1` 就取到純文字的 `{owner}/{repo}`,標題變成 `## plugins/meta`,比不到鍵 `QUESTION_{HASH}`,既有那一筆會被當成新的附加上去,同一個存取庫在頁面上出現兩次,舊區塊從此再也更新不到。`{key}` 是 H2 標題文字,也就是內容頁頁名 `QUESTION_{HASH}`,用來找既有區塊。第四個參數是區塊檔,內容是整個 H2 區塊的 markdown,不是單列。 +> +> 連結寫法:問詢紀錄那一條一律寫成 `[{文字}]({連結})`,連結取自 `jsc-gitea/tools/gitea.sh wiki-url` 印出的絕對網址,不自行組路徑。兩頁分屬不同存取庫,只有絕對網址連得過去。 +> +> 連結驗證:這個區塊寫進頁面前,先把區塊裡的每一個連結交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入。有任一筆 DEAD 就整個區塊不寫,把連不到的清單回報給呼叫端。結束碼 3 代表 `GITEA_HOST` 沒設定,先設定再驗證,不得跳過;結束碼 7 代表金鑰失效,停下來回報,別把還在的頁當成死連結。 -| 存取庫名稱 | 問詢紀錄 | 最後更新 | -| --- | --- | --- | -| {owner}/{repo} | [QUESTION_{HASH}]({wiki-url 印出的絕對網址}) | {yyyy-MM-dd HH:mm} | +## QUESTION_{HASH} + +- 存取庫名稱:{owner}/{repo} +- 問詢紀錄:[QUESTION_{HASH}]({wiki-url 印出的絕對網址}) +- 最後更新:{yyyy-MM-dd HH:mm} -- 2.53.0 From 9b4100060e4396d89e4ac8c707fa32c70a55c894 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 2 Sep 2026 17:24:49 +0800 Subject: [PATCH 6/6] =?UTF-8?q?chore(ask):=20=E6=8F=92=E4=BB=B6=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E8=99=9F=E6=9B=B4=E6=96=B0=E7=82=BA=200.1.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What: 把 `plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` 三份 manifest 的 `version` 從 `0.1.2` 改成 `0.1.3`,三份檔案的其餘欄位一字不動。 Why: 本輪技能敘述與範本有行為變更,安裝端是靠 manifest 的 `version` 判斷要不要更新,版本號不動的話已經安裝的機器不會拉到新版。三份 manifest 分別給不同的 CLI 讀取,必須一起前進,只改其中一份會讓各 CLI 認到的版本不一致。 How: 只改三份 manifest 的 `version` 欄位,內容與敘述、範本無關,因此不與需求本身的變更混在一起,獨立成一個提交,讓發版動作在歷史上單獨可追、需要時也能單獨回退。 Who: 屬於插件發版維護,隨本輪目錄頁條列式需求一起放行,本身不改任何技能行為。 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 19b8502..37a6062 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-ask", - "version": "0.1.2", + "version": "0.1.3", "description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index a129e88..a9967f7 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-ask", - "version": "0.1.2", + "version": "0.1.3", "description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)", "skills": "./skills", "jsc": { diff --git a/plugin.json b/plugin.json index 5b123bc..20c7c05 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-ask", - "version": "0.1.2", + "version": "0.1.3", "description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)", "skills": "./skills/", "jsc": { -- 2.53.0