From 2e237b76742847684b30c20b03a7f36b20a8ea7d Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 31 Aug 2026 13:37:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(behaviors):=20=E6=96=B0=E5=A2=9E=E6=8A=80?= =?UTF-8?q?=E8=83=BD=E8=A1=8C=E7=82=BA=E6=B8=85=E5=96=AE=E8=88=87=E6=AA=A2?= =?UTF-8?q?=E6=9F=A5=E8=85=B3=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:新增 references/behaviors.md,一支技能一節,共七支技能。每節五列,記下觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象。新增 tools/check-behaviors.sh,比對 skills/ 與這份清單。skill-new、skill-update、skill-delete、skillset-update 加上同步更新清單的步驟。skill-check 把這支腳本併進第一組稽核。 Why:技能驗證原本沒有基準,稽核只能靠眼睛比對 SKILL.md。十個 domain 每輪都要重做一遍,還會漏掉。行為清單當基準,技能改了、清單沒跟著改,就是漂移。漂移交給程式判定才穩。 How:腳本檢查節數、節名、節序、每節一張表、五個欄位齊全、內容欄非空。退出碼 0 代表相符,1 代表不符,2 代表用法錯誤,3 代表找不到清單或找不到 skills 目錄。domain 名以 plugin.json 的 name 為準,checkout 目錄名只是退路。四支異動技能在同一個 PR 內改清單,並照退出碼分流。 Who:屬於「技能行為清單」這件需求,提供技能驗證的參考基準。 --- references/behaviors.md | 73 ++++++++++++++ skills/skill-check/SKILL.md | 21 ++-- skills/skill-delete/SKILL.md | 6 +- skills/skill-new/SKILL.md | 5 +- skills/skill-update/SKILL.md | 4 +- skills/skillset-update/SKILL.md | 4 +- tools/check-behaviors.sh | 171 ++++++++++++++++++++++++++++++++ 7 files changed, 267 insertions(+), 17 deletions(-) create mode 100644 references/behaviors.md create mode 100755 tools/check-behaviors.sh diff --git a/references/behaviors.md b/references/behaviors.md new file mode 100644 index 0000000..de4bb7f --- /dev/null +++ b/references/behaviors.md @@ -0,0 +1,73 @@ +# jsc-meta 技能行為清單 + +本頁記錄 jsc-meta 每支技能的行為基準,供技能驗證比對。技能異動時,在同一個 PR 內一起更新這一頁。 + +## skill-check + +| 項目 | 內容 | +| --- | --- | +| 觸發時機 | 手上沒有異動需求,要對整組技能做例行或臨時稽核時用。帶著異動需求要改多支技能走 skillset-update、只改一支走 skill-update | +| 關鍵步驟 | 先跑 sync-domains.sh 同步全部 domain 存取庫、再平行跑三組審查(第一組跑腳本檢查、行為清單檢查與 hook smoke、第二組以 sub agent 逐 domain 對 guidelines 檢查清單稽核、第三組以 sub agent 分六個面向審查流程與成本)、合併三組結果並用決策樹逐項確認、以平行 sub agent 套用確認過的修正並跑 sync-skill-manifest.sh、跑 sync-marketplace.sh 同步兩份正本 marketplace、重跑三組驗證直到接受的修正全通過、每個受影響存取庫各開一條 PR | +| 外部呼叫 | tools/sync-domains.sh、tools/lint-scripts.sh、tools/check-behaviors.sh、tools/sync-skill-manifest.sh、tools/sync-marketplace.sh、jsc-cli/tools/detect-clis.sh、jsc-hooks/tools/wire-cli.sh smoke、jsc-ask:ask、jsc-git:pr | +| 完成條件 | 每個 domain 都有腳本檢查與行為清單檢查的結論、每個 domain 的檢查清單在合併後補齊、每項不合規與每項優化建議都有決策紀錄、接受的修正重驗通過、每個受影響存取庫都拿到 PR 網址 | +| 可驗證跡象 | 受影響存取庫留下檔案改動、改到行為的技能連帶改寫該存取庫的 references/behaviors.md、README 的「Skills 目錄」重寫、三份 manifest 版本號提升、兩份 marketplace 檔逐位元一致、每個受影響存取庫一條 PR | + +## skill-delete + +| 項目 | 內容 | +| --- | --- | +| 觸發時機 | 要把一支技能從技能組移除時用。改名不走這支,走 skill-update | +| 關鍵步驟 | 跑 sync-domains.sh 同步、跑 list-skills.sh 列出全部技能、讓使用者挑一支確認刪除、跑 find-skill-refs.sh 盤點所有引用檔案、以平行 sub agent 逐檔修正到檢查清單全過、刪掉 skills/{name}/ 目錄、移除 references/behaviors.md 對應那一節、跑 sync-skill-manifest.sh、開 PR、依 deploy-verify.md 部署、用新的 CLI 行程驗證、跑 verify-skill-removed.sh 查磁碟殘留、把異動報告附加到 wiki | +| 外部呼叫 | tools/sync-domains.sh、tools/list-skills.sh、tools/find-skill-refs.sh、tools/check-behaviors.sh、tools/sync-skill-manifest.sh、tools/deploy-route.sh、tools/verify-skill-removed.sh、jsc-ask:ask、jsc-git:pr、jsc-gitea:wiki | +| 完成條件 | 盤點清單每一檔都有「已修正」或「無需修正」的結論、技能目錄與行為清單那一節都不存在、list-skills.sh 查不到那一列、殘留檢查退出 0 或據實記成「無處可查」並帶進報告、PR 網址與 wiki 頁都到手 | +| 可驗證跡象 | skills/{name}/ 目錄消失、references/behaviors.md 少一節、README 與三份 manifest 更新、一條 PR、wiki SKILLSET_{HASH} 附加一節並登記在 SKILLSET_CONTENTS | + +## skill-new + +| 項目 | 內容 | +| --- | --- | +| 觸發時機 | 要在技能組新增一支技能時用。改既有技能走 skill-update | +| 關鍵步驟 | 平行跑 list-skills.sh 與 sync-domains.sh 預取技能清單與 domain 清單、用決策樹問出目標、觸發時機、輸入輸出與所屬 domain、domain 未註冊就先確認存取庫在不在、依 template 結構補齊內容再跑 sync-marketplace.sh 註冊、以 sub agent 產生 skills/{name}/SKILL.md、在 references/behaviors.md 依字典序插入該技能一節、跑 sync-skill-manifest.sh、自查 guidelines 檢查清單並跑 check-behaviors.sh、開 PR、依 deploy-verify.md 部署並用新的 CLI 行程驗證、把異動報告附加到 wiki | +| 外部呼叫 | tools/list-skills.sh、tools/sync-domains.sh、tools/sync-marketplace.sh、tools/check-behaviors.sh、tools/sync-skill-manifest.sh、tools/deploy-route.sh、jsc-gitea/tools/gitea.sh、jsc-ask:ask、jsc-git:pr、jsc-gitea:wiki | +| 完成條件 | 四項提問都有紀錄、SKILL.md 與行為清單那一節都在、README 與三份 manifest 同步、檢查清單全過且 check-behaviors.sh 退出 0、PR 網址到手、deploy-verify.md 第 1 到第 5 節的完成條件全數成立、wiki 頁寫成功 | +| 可驗證跡象 | 新增 skills/{name}/SKILL.md、references/behaviors.md 多一節、README 與三份 manifest 更新、新 domain 時兩份 marketplace 檔多一筆 plugin 條目並同步到每個 domain 存取庫、一條 PR、wiki SKILLSET_{HASH} 附加一節 | + +## skill-update + +| 項目 | 內容 | +| --- | --- | +| 觸發時機 | 要改一支既有技能時用。新增走 skill-new、刪除走 skill-delete、一次改多支或跨 domain 走 skillset-update | +| 關鍵步驟 | 跑 sync-domains.sh 同步、跑 list-skills.sh 列出全部技能、讓使用者挑一支、用決策樹問出改動細節、以 sub agent 改 SKILL.md 與相關檔案、同步更新 references/behaviors.md 該技能那一節、跑 sync-skill-manifest.sh、對 guidelines 檢查清單逐項自查並跑 check-behaviors.sh、開 PR、依 deploy-verify.md 部署並用新的 CLI 行程驗證、把異動報告附加到 wiki | +| 外部呼叫 | tools/sync-domains.sh、tools/list-skills.sh、tools/check-behaviors.sh、tools/sync-skill-manifest.sh、tools/deploy-route.sh、jsc-ask:ask、jsc-git:pr、jsc-gitea:wiki | +| 完成條件 | 每個提問都有紀錄、技能檔案帶著改動、行為清單那一節與新行為一致且 check-behaviors.sh 退出 0、三份 manifest 同版、檢查清單全過、PR 網址到手、deploy-verify.md 第 1 到第 5 節的完成條件全數成立、wiki 頁寫成功 | +| 可驗證跡象 | 該技能的 SKILL.md 與相關檔案改動、references/behaviors.md 對應節改寫、README 與三份 manifest 更新、一條 PR、wiki SKILLSET_{HASH} 附加一節 | + +## skillset-update + +| 項目 | 內容 | +| --- | --- | +| 觸發時機 | 一個異動需求橫跨多支技能或多個 domain,要一次做完時用。只改一支走 skill-update、手上沒有異動需求的例行稽核走 skill-check | +| 關鍵步驟 | 平行啟動 sync-domains.sh 與異動細節決策樹、問清楚改哪一條規則、影響哪些技能與 domain,並補問工具化、sub agent、環境變數三項塑形檢查、以每個 domain 一個 sub agent 平行套用改動、同步更新每個受影響 domain 的 references/behaviors.md、逐存取庫跑 sync-skill-manifest.sh、以平行 sub agent 重跑 guidelines 檢查清單與 check-behaviors.sh 直到全過、每個受影響存取庫各開一條 PR、依 deploy-verify.md 部署並用新的 CLI 行程驗證、逐存取庫把異動報告附加到 wiki | +| 外部呼叫 | tools/sync-domains.sh、tools/check-behaviors.sh、tools/sync-skill-manifest.sh、tools/deploy-route.sh、tools/list-skills.sh、jsc-ask:ask、jsc-git:pr、jsc-gitea:wiki | +| 完成條件 | 受影響技能清單與三項塑形檢查都跟使用者談定、每個受影響存取庫都帶著改動、README 同步與 manifest 提升、每支動過的技能檢查清單全過且該 domain 的 check-behaviors.sh 退出 0、每個受影響存取庫都有 PR 網址、deploy-verify.md 第 1 到第 5 節對每個存取庫都成立、每個存取庫的 wiki 頁都寫成功 | +| 可驗證跡象 | 每個受影響存取庫的技能檔案改動、各自的 references/behaviors.md 更新、README 與三份 manifest 更新、每個存取庫一條 PR、每個存取庫的 wiki SKILLSET_{HASH} 各附加一節並登記在 SKILLSET_CONTENTS | + +## ste100-sync + +| 項目 | 內容 | +| --- | --- | +| 觸發時機 | 定期維護,或上游 speak-human-tw 發佈新版時用。只改本地自訂規則不走這支 | +| 關鍵步驟 | 先讀 references/ste100.md 釘住的上游版本、再用 HTTPS 讀上游 SKILL.md frontmatter 的版本比對、同版就回報「上游沒有新版」並停在這裡、有新版才淺層 clone 取 changelog、以 sub agent 蒸餾適用於技術文件與對話的變更、用決策樹逐項確認採用、改寫 references/ste100.md 與「上游版本」行、必要時更新 ste100-lint.sh 的樣式與 jsc-hooks/hooks/simplified.txt、平行對每個 jsc 存取庫重跑 lint、跑 sync-skill-manifest.sh、開 PR | +| 外部呼叫 | 上游 speak-human-tw 的 raw SKILL.md 與 git clone、tools/ste100-lint.sh、tools/sync-domains.sh、tools/sync-skill-manifest.sh、jsc-ask:ask、jsc-git:pr | +| 完成條件 | 上游同版時停在版本比對並回報;有新版時每項蒸餾出來的變更都有決策、`sh -n tools/ste100-lint.sh` 通過且新採用的詞彙在測試字串上命中、本存取庫 lint 退出 0、其他存取庫的命中附 file:line 交給擁有者、三份 manifest 同版、PR 網址到手 | +| 可驗證跡象 | references/ste100.md 的「上游版本」行換值、tools/ste100-lint.sh 的樣式更新、jsc-hooks/hooks/simplified.txt 更新、三份 manifest 版本號提升、一條 PR;上游同版時無寫入跡象,只有回報內容 | + +## tooling-guide + +| 項目 | 內容 | +| --- | --- | +| 觸發時機 | 使用者要技能組導覽、工具地圖、支援的 plugin 清單、支援的技能清單、hook 管理概觀或新人上手參考時用。安裝、更新、刪除、稽核、修復都不走這支 | +| 關鍵步驟 | 跑 plugins-root.sh 確認工作根目錄、跑 sync-domains.sh 取得 domain 與本機路徑、跑 inventory-tooling.sh 產生基準盤點並同時蒐集管理流程事實、需要說明或分組時以 sub agent 綜整導覽草稿、主 agent 逐項核對每個說法的來源、依記錄下來的目標交付、目標是 wiki 頁時才以 sub agent 逐 CLI 算出頁名、先寫內容頁再登記目錄頁 | +| 外部呼叫 | tools/plugins-root.sh、tools/sync-domains.sh、tools/inventory-tooling.sh、jsc-gitea/tools/hash-id、jsc-gitea:wiki、jsc-ask:ask | +| 完成條件 | 每項事實都指得到來源檔案或工具輸出、必填章節都不是空的、收尾回報寫明交付目標、來源新鮮度、過期輸入與未知的 hook 判定;目標是 wiki 時每一頁都確認寫成功,或列為未寫入並附完整內容 | +| 可驗證跡象 | 目標是聊天回應時無寫入跡象,只有回報內容;目標是檔案時只產生使用者指定的那一個檔;目標是 wiki 時每支偵測到的 CLI 各一頁 TOOLING_{HASH},並在 TOOLING_CONTENTS 更新自己那一列 | diff --git a/skills/skill-check/SKILL.md b/skills/skill-check/SKILL.md index aa1f115..501b527 100644 --- a/skills/skill-check/SKILL.md +++ b/skills/skill-check/SKILL.md @@ -1,6 +1,6 @@ --- name: skill-check -description: Routine compliance, script, hook, flow-efficiency, and cost-efficiency audit of the whole jsc skill set with no change request in hand. Sync every domain repo from the Gitea canonical marketplace, then run three parallel groups - lint-scripts.sh plus hook smoke, the guidelines.md checklist audit, and a review of parallelism, tool extraction, repeated interaction, redundant checks, misplaced gates, and avoidable token, sub-agent, API, scan, or interaction cost. Confirm compliance fixes and optimization suggestions before applying them, re-check until accepted fixes pass, then open a PR per affected repo via jsc-git pr. Use for periodic or on-demand skill-set checks; not for applying a change request (use skillset-update) or editing one skill (use skill-update). +description: Routine compliance, script, hook, flow-efficiency, and cost-efficiency audit of the whole jsc skill set with no change request in hand. Sync every domain repo from the Gitea canonical marketplace, then run three parallel groups - lint-scripts.sh plus check-behaviors.sh plus hook smoke, the guidelines.md checklist audit, and a review of parallelism, tool extraction, repeated interaction, redundant checks, misplaced gates, and avoidable token, sub-agent, API, scan, or interaction cost. Confirm compliance fixes and optimization suggestions before applying them, re-check until accepted fixes pass, then open a PR per affected repo via jsc-git pr. Use for periodic or on-demand skill-set checks; not for applying a change request (use skillset-update) or editing one skill (use skill-update). --- # skill-check — audit compliance, flow efficiency, and cost efficiency @@ -14,11 +14,12 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references The three review groups of step 2 all read this synced tree, so the sync finishes first. 2. Run the three review groups over the synced repos. They are independent — every one only reads, none writes a file — so **launch all three in parallel** and merge their results in step 3. - **Group 1 — validate scripts and hooks.** + **Group 1 — validate scripts, behavior lists, and hooks.** 1. For every synced domain repo, run `tools/lint-scripts.sh {domain-path}`. One run per domain, and the runs go **in parallel** — no domain's verdict depends on another's. The tool covers three checks in one pass: `sh -n` syntax, executable bit, and an exit-code declaration in the file header. Route each exit code: 0 — the domain's scripts pass all three; 1 — the failing items are printed as `{file}:{check}:{detail}`, so report each one; 2 — usage error, the tool takes exactly one argument; 3 — nothing was scanned, because the path is missing or the domain has neither `tools/` nor `hooks/`. Record exit 3 as 「無腳本可掃」; a domain with no script directory is not a failure, but exit 3 is **never** a pass. - 2. For every shell script directly named by a SKILL.md, confirm the skill routes every exit code the script's header declares. `lint-scripts.sh` proves the script exists and declares its codes; this check is the other half — that the caller branches on each of them. Report evidence as `skill file:line -> script path`. - 3. When the `jsc-hooks` domain is present, run `jsc-hooks/tools/wire-cli.sh smoke {cli}` for every CLI reported by `jsc-cli/tools/detect-clis.sh`; the per-CLI smokes run **in parallel**. When no CLI is detected, run `jsc-hooks/tools/wire-cli.sh smoke codex` as the minimum hook behavior check and label it 「預設 hook smoke」 in the report. Use `smoke`, not `purge` or rewiring actions, and set `JSC_READONLY=1` for the whole audit so a mistyped sub-command is refused in code (exit 6) instead of rewiring the machine; `status` and `smoke` are unaffected by that variable. Route each `smoke` exit code: 0 — the run passed its own assertions; 2 — usage error, so fix the CLI code and rerun; 4 — the smoke failed, which includes the script's own result-line count not matching what it expected. **Read the count from the script's `lines{數量}` output line; never write the number into this skill.** The script counts its own result lines and asserts them, so a hardcoded number here goes stale the moment a hook or a decision path is added — an out-of-date count in a SKILL.md is exactly what misled the previous audit. - 4. When a hook or script smoke fails, route it as a compliance failure with script name, exit code, output summary, and proposed fix. Do not continue to report the affected hook as compliant. + 2. For every synced domain repo, run `tools/check-behaviors.sh {domain-path}`. One run per domain, and the runs go **in parallel** alongside the `lint-scripts.sh` runs — no domain's verdict depends on another's. It compares `references/behaviors.md` against `skills/`: section per skill, dictionary order, one table per section, five rows, no empty content cell. Route each exit code: 0 — that domain's behavior list matches; 1 — the mismatches are printed on stderr as `{檔案}:{技能名}:{說明}`, so report every one as a compliance failure with the skill it belongs to; 2 — usage error, the tool takes exactly one argument; 3 — nothing was checked, because `references/behaviors.md` is missing, `skills/` is missing, or no `SKILL.md` was found. Record exit 3 as 「無清單可查」with the cause from stderr and carry it into the step 3 merge; a domain with no behavior list is a compliance failure, and exit 3 is **never** a pass. + 3. For every shell script directly named by a SKILL.md, confirm the skill routes every exit code the script's header declares. `lint-scripts.sh` proves the script exists and declares its codes; this check is the other half — that the caller branches on each of them. Report evidence as `skill file:line -> script path`. + 4. When the `jsc-hooks` domain is present, run `jsc-hooks/tools/wire-cli.sh smoke {cli}` for every CLI reported by `jsc-cli/tools/detect-clis.sh`; the per-CLI smokes run **in parallel**. When no CLI is detected, run `jsc-hooks/tools/wire-cli.sh smoke codex` as the minimum hook behavior check and label it 「預設 hook smoke」 in the report. Use `smoke`, not `purge` or rewiring actions, and set `JSC_READONLY=1` for the whole audit so a mistyped sub-command is refused in code (exit 6) instead of rewiring the machine; `status` and `smoke` are unaffected by that variable. Route each `smoke` exit code: 0 — the run passed its own assertions; 2 — usage error, so fix the CLI code and rerun; 4 — the smoke failed, which includes the script's own result-line count not matching what it expected. **Read the count from the script's `lines{數量}` output line; never write the number into this skill.** The script counts its own result lines and asserts them, so a hardcoded number here goes stale the moment a hook or a decision path is added — an out-of-date count in a SKILL.md is exactly what misled the previous audit. + 5. When a hook or script smoke fails, route it as a compliance failure with script name, exit code, output summary, and proposed fix. Do not continue to report the affected hook as compliant. **Group 2 — audit every skill of every domain against the guidelines.md audit checklist.** This group MUST run as a sub agent, one sub agent per domain repo, and those sub agents run **in parallel**. Each sub agent reports its findings: skill, failed checklist item, evidence (file:line), proposed fix. Cover the checklist's four flow checks by name, not only the naming and language items: - Every step number, file path and section title the skill references — inside itself and in other files — really exists (the pointer points at something). @@ -26,7 +27,7 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references - Every external call (script, API, other skill) states what to do on failure and routes every exit code. - No gate the skill installs blocks the only path that lifts that gate. - Three checklist items are **already decided by group 1** and must not be re-run here: `sh -n` on every `tools/` and `hooks/` script, script existence with the executable bit, and the hook smoke. Tell each sub agent to skip those three and leave them blank; the main agent fills them in from the group 1 verdicts when merging in step 3. Re-scanning the same files in every domain sub agent buys nothing — group 1 already scanned them all, with the same tool, on the same synced tree. + Four checklist items are **already decided by group 1** and must not be re-run here: `sh -n` on every `tools/` and `hooks/` script, script existence with the executable bit, the hook smoke, and the `references/behaviors.md` match. Tell each sub agent to skip those four and leave them blank; the main agent fills them in from the group 1 verdicts when merging in step 3. Re-scanning the same files in every domain sub agent buys nothing — group 1 already scanned them all, with the same tools, on the same synced tree. **Group 3 — a flow and cost optimization review**, kept separate from the compliance audit. Each aspect **MUST run as a sub agent**, and the six aspects run in parallel with each other and with groups 1 and 2: @@ -41,13 +42,13 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references Each optimization finding reports skill, aspect, evidence (file:line), current flow step count, proposed flow step count, what time or interaction it saves, what cost it saves, current cost driver, proposed cost driver, whether correctness decreases, and which protection would be weakened if any. Cost savings may be token volume, sub-agent count, API calls, file scans, full-repo audits, or user prompts. Keep optimization findings separate from compliance failures. - Completion condition for all three groups: every domain has a `lint-scripts.sh` verdict, every script named by a SKILL.md has an exit-code-routing verdict, and every smoked CLI has a `smoke` exit code plus the `lines` value the script printed for it; every domain has a group 2 audit result that names a verdict for all checklist items — the four flow checks included, and the three group 1 items left blank for the step 3 merge rather than re-scanned; and every one of the six aspects has returned a verdict for every domain, 「無發現」 where an aspect found nothing. -3. Merge the three groups, then present compliance failures and optimization findings separately via the `jsc-ask:ask` decision tree. Merging means one thing in code: fill the three skipped checklist items of every group 2 sub agent report from the matching group 1 verdicts, so each domain ends with one complete checklist and no item counted twice. + Completion condition for all three groups: every domain has a `lint-scripts.sh` verdict and a `check-behaviors.sh` verdict, every script named by a SKILL.md has an exit-code-routing verdict, and every smoked CLI has a `smoke` exit code plus the `lines` value the script printed for it; every domain has a group 2 audit result that names a verdict for all checklist items — the four flow checks included, and the four group 1 items left blank for the step 3 merge rather than re-scanned; and every one of the six aspects has returned a verdict for every domain, 「無發現」 where an aspect found nothing. +3. Merge the three groups, then present compliance failures and optimization findings separately via the `jsc-ask:ask` decision tree. Merging means one thing in code: fill the four skipped checklist items of every group 2 sub agent report from the matching group 1 verdicts, so each domain ends with one complete checklist and no item counted twice. - Compliance failure options: apply the proposed fix / skip / custom fix. Every option states its impact scope, for example skipping leaves the skill non-compliant until the next audit. - Optimization options: apply / defer / custom. Any suggestion that weakens a protection must name the protection it removes and must not be applied unless the user explicitly accepts that tradeoff. Cost optimization may move, merge, cache, or narrow checks; it must not delete a compliance check only because it is expensive. Completion condition: every domain's checklist is complete after the merge, and every compliance failure and every optimization finding has a recorded decision. -4. Apply the confirmed fixes and accepted optimizations — the file-change part MUST run as a sub agent, one sub agent per affected domain repo, and those sub agents run **in parallel**: each repo's files are independent. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) for each affected domain repo to refresh that domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: every affected repo carries the changes and the manifest bump. +4. Apply the confirmed fixes and accepted optimizations — the file-change part MUST run as a sub agent, one sub agent per affected domain repo, and those sub agents run **in parallel**: each repo's files are independent. A fix that changes a skill's behavior also updates that skill's `## {name}` section in the same repo's `references/behaviors.md`, in the same pass, so the fix and the behavior list land in one PR. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) for each affected domain repo to refresh that domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: every affected repo carries the changes, the matching `references/behaviors.md` update for every fix that changed a skill's behavior, and the manifest bump. 5. Sync the canonical marketplace — a **required** step, never optional. The canonical pair lives in `plugins/meta` and every domain repo carries a byte-identical copy, so a fix that leaves the copies apart makes some repos register a stale plugin set. Run `tools/sync-marketplace.sh {domain} {repo-url} {description}` once with an existing entry's own current values (rewriting the same entry is idempotent); the script rewrites both canonical files and copies them into every domain repo. Route each exit code: - Exit 3 — written, but some domain repo is not present locally. Run `tools/sync-domains.sh`, then rerun this step. - Exit 2 — usage error: the script takes exactly three arguments. Fix them and rerun. @@ -55,5 +56,5 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references - Exit 0 — every copy holds identical bytes; the script verifies that itself. Completion condition: the script exits 0 and prints the touched paths. -6. Re-run the group 1 script and hook validation, re-check the guidelines.md audit checklist for every touched skill, then re-run the optimization aspect that produced each accepted optimization. These three re-runs are as independent as the first pass, so run them **in parallel** and merge them the same way step 3 did. On any compliance failure, **return to step 3**: confirm and fix again, until all accepted compliance fixes pass. On an accepted optimization that does not produce the promised step reduction or cost reduction, or still weakens correctness beyond the recorded decision, return to step 3 for a new decision. Completion condition: `tools/lint-scripts.sh` exits 0 or 3 for every domain, every hook smoke exits 0 with the `lines` count the script itself asserted, all checklist items pass, and every accepted optimization has a matching verification result. +6. Re-run the group 1 script, behavior-list, and hook validation, re-check the guidelines.md audit checklist for every touched skill, then re-run the optimization aspect that produced each accepted optimization. These three re-runs are as independent as the first pass, so run them **in parallel** and merge them the same way step 3 did. On any compliance failure, **return to step 3**: confirm and fix again, until all accepted compliance fixes pass. On an accepted optimization that does not produce the promised step reduction or cost reduction, or still weakens correctness beyond the recorded decision, return to step 3 for a new decision. Completion condition: `tools/lint-scripts.sh` exits 0 or 3 for every domain, `tools/check-behaviors.sh` exits 0 for every domain, every hook smoke exits 0 with the `lines` count the script itself asserted, all checklist items pass, and every accepted optimization has a matching verification result. 7. Call `jsc-git:pr` once per affected domain repo to open a Push Request. Completion condition: every affected repo has a PR URL, and all URLs are reported in one table with the format in [`../../references/pr-report.md`](../../references/pr-report.md). diff --git a/skills/skill-delete/SKILL.md b/skills/skill-delete/SKILL.md index d7d0196..80e6f1c 100644 --- a/skills/skill-delete/SKILL.md +++ b/skills/skill-delete/SKILL.md @@ -19,7 +19,11 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references 3. Apply the confirmed fix, then check the guidelines.md audit checklist for the file — the per-file fix work MUST run as a sub agent, one sub agent per affected domain repo, and those sub agents **run in parallel**: each repo's files are independent, so serialising them only adds waiting. Each sub agent reports one line per file: the path and either the applied fix or「無需修正」with the reason. On any checklist failure, return to step 5.2. Completion condition: the fix is in the file and every checklist item passes for it. Completion condition: every file in the step 4 inventory is marked either fixed-with-a-clean-checklist or explicitly no-fix-needed with a reason — no file is left without a verdict. -6. Delete the skill directory `skills/{name}/`, then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a remaining `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: the directory is gone, the README's 「Skills 目錄」 no longer lists the skill, and all three manifests show the same new version. +6. Delete the skill directory `skills/{name}/` and remove that skill's `## {name}` section from `references/behaviors.md` — the whole section, its table included, leaving every other section untouched. Both deletions ship in this same PR: a behavior list still carrying a deleted skill fails the domain's next audit, and the extra section is exactly what `check-behaviors.sh` reports. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a remaining `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. + + Then run `tools/check-behaviors.sh {domain-path}` and route each exit code: 0 — the remaining sections match the remaining skills; 1 — every mismatch is printed on stderr as `{檔案}:{技能名}:{說明}`, so fix each one and rerun, the deleted skill's leftover section included; 2 — usage error, the tool takes exactly one argument; 3 — nothing was checked, because `references/behaviors.md` is missing, `skills/` is missing, or no `SKILL.md` was found, so fix the named cause and rerun. **Exit 3 is never a pass.** + + Completion condition: the directory is gone, `references/behaviors.md` holds no `## {name}` section for the deleted skill, `tools/check-behaviors.sh {domain-path}` exits 0, the README's 「Skills 目錄」 no longer lists the skill, and all three manifests show the same new version. 7. Call `jsc-git:pr` to open a Push Request. Completion condition: a PR URL comes back and is reported with the table format in [`../../references/pr-report.md`](../../references/pr-report.md). 8. Deploy the deletion, verify it took, then report: 1. Follow [`../../references/deploy-verify.md`](../../references/deploy-verify.md) from section 1 to section 5: `tools/deploy-route.sh {domain-path}` picks the route, the deploy route or the worktree route runs, and the verification then runs in a **fresh CLI process**, never in the session that ran the deploy. That session raised the restart gate itself and still holds the old skill set, so verifying inside it either gets blocked or passes on stale behavior. On the worktree route, add that the skill stays installed and stays callable until the outstanding release PR merges. Completion condition: every completion condition in `deploy-verify.md` sections 1 to 5 holds for this domain repo. diff --git a/skills/skill-new/SKILL.md b/skills/skill-new/SKILL.md index 447d818..da93efb 100644 --- a/skills/skill-new/SKILL.md +++ b/skills/skill-new/SKILL.md @@ -36,9 +36,10 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references 3. Generate the skill per guidelines.md — this step MUST run as a sub agent: - `skills/{name}/SKILL.md`: entirely in English (description within either cap — ≤ 5 sentences or ≤ 5 steps — and stating when to use and when not to; body in STE100-style English) - Rules enforceable by hooks go to `jsc-hooks` (never scattered in this domain); standard input/output flows go to `tools/` + - `references/behaviors.md`: add one `## {name}` section for the new skill, placed in dictionary order among the existing sections, carrying the five rows the guidelines' 「技能行為清單」 section defines — 觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象. Write what the skill really does; do not copy the `description`. A read-only skill still fills 可驗證跡象 with 「無寫入跡象,只有回報內容」. The behavior list ships in this same PR — a skill added without its section leaves the domain's list out of sync the moment this PR merges. When the domain has no `references/behaviors.md` yet, create it with the header line `# jsc-{domain} 技能行為清單`. - Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: `skills/{name}/SKILL.md` exists, the README lists the skill, and all three manifests show the same new version. -4. Self-check every item of the guidelines.md audit checklist; fix anything that fails. Completion condition: every checklist item passes. + Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: `skills/{name}/SKILL.md` exists, `references/behaviors.md` holds a `## {name}` section with all five rows filled, the README lists the skill, and all three manifests show the same new version. +4. Self-check every item of the guidelines.md audit checklist; fix anything that fails. Run `tools/check-behaviors.sh {domain-path}` for the behavior-list item instead of comparing by eye, and route each exit code: 0 — the list matches `skills/` and all five rows are filled; 1 — every mismatch is printed on stderr as `{檔案}:{技能名}:{說明}`, so fix each one and rerun; 2 — usage error, the tool takes exactly one argument; 3 — nothing was checked, because `references/behaviors.md` is missing, `skills/` is missing, or no `SKILL.md` was found, so create the missing file and rerun. **Exit 3 is never a pass.** Completion condition: every checklist item passes and `tools/check-behaviors.sh {domain-path}` exits 0. 5. Call `jsc-git:pr` to open a Push Request. Completion condition: a PR URL comes back and is reported with the table format in [`../../references/pr-report.md`](../../references/pr-report.md). 6. Deploy the new skill, verify it runs, then report: 1. Follow [`../../references/deploy-verify.md`](../../references/deploy-verify.md) from section 1 to section 5: `tools/deploy-route.sh {domain-path}` picks the route, the deploy route or the worktree route runs, and the verification then runs in a **fresh CLI process**, never in the session that ran the deploy. That session raised the restart gate itself and still holds the old skill body, so verifying inside it either gets blocked or passes on stale behavior. Verify the added skill's row in `tools/list-skills.sh`, every tool the skill added, and one minimal prompt per checkable CLI — the per-CLI prompts run in parallel. Completion condition: every completion condition in `deploy-verify.md` sections 1 to 5 holds for this domain repo. diff --git a/skills/skill-update/SKILL.md b/skills/skill-update/SKILL.md index 7d5c2fc..fe79107 100644 --- a/skills/skill-update/SKILL.md +++ b/skills/skill-update/SKILL.md @@ -13,8 +13,8 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references 2. Run `tools/list-skills.sh` and present its `domain / name / description` rows to the user. The tool prints skills, not domains, so read the domain column to prove coverage. Exit 1 means the root could not be derived, the domain list was unreadable, or no skill was found — read stderr, fix the named cause (`JSC_PLUGINS_ROOT` for the root case, as in step 1) and rerun; never read it as an empty skill set. Completion condition: the script exits 0 and every domain printed by step 1 appears in at least one row; a domain with no row means its repo is missing or holds no skill — return to step 1 for that domain. 3. Let the user pick the skill to update. Completion condition: one `{domain}/{name}` pair is confirmed. 4. Ask for update details via the `jsc-ask:ask` decision tree (change the goal? the trigger? the flow? move rules down to a hook or a tool?). Every option states its impact scope (example: renaming breaks the existing invocation command). Completion condition: every question has a recorded answer. -5. Update the skill — the modification part MUST run as a sub agent: modify SKILL.md and related files. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: the skill files carry the change and all three manifests show the same new version. -6. Check every item of the guidelines.md audit checklist. On any failure, **return to step 4**: ask again and fix, until all items pass. Completion condition: every checklist item passes. +5. Update the skill — the modification part MUST run as a sub agent: modify SKILL.md and related files. In the same pass, update this skill's `## {name}` section in `references/behaviors.md` so its five rows — 觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象 — describe the new behavior. A renamed skill gets its section renamed and moved back into dictionary order. The behavior list ships in this same PR: a behavior change that lands without its section makes the domain's list wrong from the merge onward, and the next audit reports drift this step created. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: the skill files carry the change, the skill's `references/behaviors.md` section states the new behavior with all five rows filled, and all three manifests show the same new version. +6. Check every item of the guidelines.md audit checklist. Run `tools/check-behaviors.sh {domain-path}` for the behavior-list item instead of comparing by eye, and route each exit code: 0 — the list matches `skills/` and all five rows are filled; 1 — every mismatch is printed on stderr as `{檔案}:{技能名}:{說明}`, so fix each one and rerun; 2 — usage error, the tool takes exactly one argument; 3 — nothing was checked, because `references/behaviors.md` is missing, `skills/` is missing, or no `SKILL.md` was found, so create the missing file and rerun. **Exit 3 is never a pass.** On any failure, **return to step 4**: ask again and fix, until all items pass. Completion condition: every checklist item passes and `tools/check-behaviors.sh {domain-path}` exits 0. 7. Call `jsc-git:pr` to open a Push Request. Completion condition: a PR URL comes back and is reported with the table format in [`../../references/pr-report.md`](../../references/pr-report.md). 8. Deploy the update, verify it runs, then report: 1. Follow [`../../references/deploy-verify.md`](../../references/deploy-verify.md) from section 1 to section 5: `tools/deploy-route.sh {domain-path}` picks the route, the deploy route or the worktree route runs, and the verification then runs in a **fresh CLI process**, never in the session that ran the deploy. That session raised the restart gate itself and still holds the old skill body, so verifying inside it either gets blocked or passes on stale behavior. Verify the updated `description` in the skill's `tools/list-skills.sh` row, every tool this change touched, and one minimal prompt per checkable CLI — the per-CLI prompts run in parallel. Completion condition: every completion condition in `deploy-verify.md` sections 1 to 5 holds for this domain repo. diff --git a/skills/skillset-update/SKILL.md b/skills/skillset-update/SKILL.md index 6b49cd8..865efa0 100644 --- a/skills/skillset-update/SKILL.md +++ b/skills/skillset-update/SKILL.md @@ -14,8 +14,8 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references 2. Ask for the change details via the `jsc-ask:ask` decision tree: what rule or behavior changes, which skills and which domains are affected. Include three required checks before the affected-skill list is final: whether any deterministic input/output flow must move to `tools/`, whether any detailed flow must run as a sub agent, and whether any wiki or Gitea flow must read inherited environment variables before asking the user. Every option states its impact scope (example: changing a shared flow step touches every skill that calls it). These three are a shaping guardrail asked before any file is touched; keep asking them even when a later step would catch the same problem. Completion condition: the `domainpath` rows are in hand, and the affected-skill list plus the three checks are agreed with the user. -2. Apply the change to every affected skill — the modification part MUST run as a sub agent, one sub agent per affected domain repo, and those sub agents **run in parallel**: each repo's files are independent. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) for each affected domain repo to sync that domain README's 「Skills 目錄」 section and bump the version in all three manifests; these runs are independent per repo and may also go in parallel. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: every affected domain repo carries the change, the README sync, and the manifest bump. -3. Check every item of the guidelines.md audit checklist for each touched skill — one sub agent per affected domain repo, run in parallel. On any failure, **return to step 1.2**: ask again and fix, until all items pass. Completion condition: every checklist item passes for every touched skill. +2. Apply the change to every affected skill — the modification part MUST run as a sub agent, one sub agent per affected domain repo, and those sub agents **run in parallel**: each repo's files are independent. Every sub agent also updates its own repo's `references/behaviors.md` in the same pass: a changed behavior rewrites that skill's `## {name}` section, a new skill gets a section inserted in dictionary order, a removed skill loses its section. Keep all five rows filled — 觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象. Each repo's behavior list ships in that repo's own PR, so no cross-repo PR pair has to be merged in order. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) for each affected domain repo to sync that domain README's 「Skills 目錄」 section and bump the version in all three manifests; these runs are independent per repo and may also go in parallel. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: every affected domain repo carries the change, its behavior-list update, the README sync, and the manifest bump. +3. Check every item of the guidelines.md audit checklist for each touched skill — one sub agent per affected domain repo, run in parallel. Each sub agent runs `tools/check-behaviors.sh {domain-path}` for the behavior-list item of its own repo instead of comparing by eye, and routes each exit code: 0 — that repo's list matches its `skills/` and all five rows are filled; 1 — every mismatch is printed on stderr as `{檔案}:{技能名}:{說明}`, so fix each one and rerun; 2 — usage error, the tool takes exactly one argument; 3 — nothing was checked, because `references/behaviors.md` is missing, `skills/` is missing, or no `SKILL.md` was found, so create the missing file and rerun. **Exit 3 is never a pass.** On any failure, **return to step 1.2**: ask again and fix, until all items pass. Completion condition: every checklist item passes for every touched skill, and `tools/check-behaviors.sh` exits 0 for every affected domain repo. 4. Call `jsc-git:pr` once per affected domain repo to open a Push Request. Completion condition: every affected repo has a PR URL, and all URLs are reported in one table with the format in [`../../references/pr-report.md`](../../references/pr-report.md). 5. Deploy the batch change, verify it runs, then report: 1. Follow [`../../references/deploy-verify.md`](../../references/deploy-verify.md) from section 1 to section 5, once per affected domain repo — the route judgements run in parallel. The batch takes the deploy route only when **every** affected repo's `tools/deploy-route.sh` exits 0; a single exit 3 puts the whole batch on the worktree route, because the change reaches the CLIs only when the last repo merges, so name every outstanding release PR. The verification then runs in a **fresh CLI process**, never in the session that ran the deploy: that session raised the restart gate itself and still holds the old skill bodies. Verify every touched skill's row in `tools/list-skills.sh`, every tool this change touched, and one minimal prompt per affected domain per checkable CLI — the per-CLI and per-domain prompts run in parallel. Completion condition: every completion condition in `deploy-verify.md` sections 1 to 5 holds for every affected domain repo. diff --git a/tools/check-behaviors.sh b/tools/check-behaviors.sh new file mode 100755 index 0000000..be3bcbf --- /dev/null +++ b/tools/check-behaviors.sh @@ -0,0 +1,171 @@ +#!/usr/bin/env sh +# check-behaviors.sh — 檢查一個 domain 的技能行為清單 references/behaviors.md 有沒有跟 skills/ 對齊。 +# +# 用法: check-behaviors.sh +# +# 檢查六項(格式合約見 jsc-meta references/guidelines.md 的「技能行為清單」一節): +# 1. 標題 — 第一行是「# jsc-{domain} 技能行為清單」,檔案不得有 UTF-8 BOM。 +# 2. 節對技能 — 每支 skills/*/SKILL.md 一個「## {技能名}」節,名稱與目錄名逐字相同,不多不少。 +# 3. 節順序 — 節的排列照技能目錄名的字典序(LC_ALL=C)。 +# 4. 表格 — 每節恰好一張表,表頭是「| 項目 | 內容 |」。 +# 5. 五個欄位 — 依序為 觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象,不多不少。 +# 6. 內容 — 每一列的「內容」欄不得空白。 +# +# 為什麼要這支: 技能改了行為、清單沒跟著改,兩邊就漂移。漂移靠眼睛比對,10 個 domain 每次稽核 +# 都要重做一遍,還會漏。這六項的輸入輸出固定,交給程式判定才穩。 +# +# 輸出: 一行一個不合格項目,格式 {behaviors.md 路徑}:{技能名或 -}:{說明}(stderr); +# 通過時在 stderr 印一行摘要。stdout 不印東西。 +# 結束碼: 0=行為清單與 skills/ 相符,五個欄位齊全且內容欄非空 +# 1=不符:缺節、多節、順序不對、表格不對、缺欄位或欄位空白,清單在 stderr +# 2=用法錯誤(本腳本只吃一個參數) +# 3=找不到 {domain-path}/references/behaviors.md,或找不到 {domain-path}/skills/, +# 或 skills/ 底下一支 SKILL.md 都沒有——**什麼都沒查**,不等於通過 +set -u + +usage() { + echo 'usage: check-behaviors.sh ' >&2 + exit 2 +} + +[ "$#" -eq 1 ] || usage +DOMAIN=${1%/} +[ -n "$DOMAIN" ] || usage + +SKILLS="$DOMAIN/skills" +DOC="$DOMAIN/references/behaviors.md" +[ -d "$DOMAIN" ] || { echo "找不到 domain 路徑:$DOMAIN" >&2; exit 3; } +[ -d "$SKILLS" ] || { echo "找不到 skills/:$SKILLS" >&2; exit 3; } +[ -f "$DOC" ] || { echo "找不到行為清單:$DOC" >&2; exit 3; } + +TMPD=$(mktemp -d) || { echo "無法建立暫存目錄" >&2; exit 3; } +trap 'rm -rf "$TMPD"' EXIT + +TAB=$(printf '\t') +BOM=$(printf '\357\273\277') +FIELDS='觸發時機 關鍵步驟 外部呼叫 完成條件 可驗證跡象' + +fail=0 +report() { # $1=技能名或 -,$2=說明 + printf '%s:%s:%s\n' "$DOC" "$1" "$2" >&2 + fail=1 +} + +# 技能清單: skills/ 底下帶 SKILL.md 的目錄名,字典序。 +for d in "$SKILLS"/*/; do + [ -f "${d}SKILL.md" ] || continue + n=${d%/} + echo "${n##*/}" +done | LC_ALL=C sort > "$TMPD/skills.txt" +[ -s "$TMPD/skills.txt" ] || { echo "skills/ 底下沒有任何 SKILL.md:$SKILLS" >&2; exit 3; } + +# 解析 behaviors.md,攤平成三種記錄: +# SEC{節名} 一個「## 」標題 +# HDR{節名} 一列表頭「| 項目 | 內容 |」 +# ROW{節名}{項目}{內容} 一列資料(分隔列不算) +awk ' +function trim(s) { gsub(/^[ \t]+/, "", s); gsub(/[ \t]+$/, "", s); return s } +BEGIN { FS = "|"; sec = "-" } +{ sub(/\r$/, "") } +/^## / { + sec = trim(substr($0, 4)) + printf "SEC\t%s\n", sec + next +} +/^[ \t]*\|/ { + item = trim($2) + body = "" + for (i = 3; i < NF; i++) body = (body == "" ? $i : body "|" $i) + body = trim(body) + if (item ~ /^[-: ]+$/ && body ~ /^[-: ]*$/) next + if (item == "項目" && body == "內容") { printf "HDR\t%s\n", sec; next } + printf "ROW\t%s\t%s\t%s\n", sec, item, body +} +' "$DOC" > "$TMPD/parsed.txt" + +# --- 1. 標題 --- +first=$(head -1 "$DOC" | tr -d '\r') +case $first in + "$BOM"*) report - '檔頭帶 UTF-8 BOM,請改存無 BOM'; first=${first#"$BOM"} ;; +esac +# domain 名以 plugin.json 的 name 為準,checkout 目錄名只是退路。 +# 目錄名是誰 clone 誰決定的,同一個 repo 換一台機器就可能叫別的名字, +# 拿它當唯一來源會在別人的工作區誤報一次「標題不符」。 +dom=$(sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$DOMAIN/plugin.json" 2>/dev/null | head -n1) +if [ -z "$dom" ]; then + dom=$(cd "$DOMAIN" 2>/dev/null && pwd) || dom=$DOMAIN + dom=${dom##*/} +fi +dom=${dom#jsc-} +want_title="# jsc-$dom 技能行為清單" +[ "$first" = "$want_title" ] || report - "第一行要是「$want_title」,實際是「$first」" + +# --- 2. 節對技能 --- +awk -F"$TAB" '$1 == "SEC" { print $2 }' "$TMPD/parsed.txt" > "$TMPD/sections.txt" +LC_ALL=C sort "$TMPD/sections.txt" > "$TMPD/sections-sorted.txt" + +LC_ALL=C uniq -d "$TMPD/sections-sorted.txt" | while IFS= read -r s; do + [ -n "$s" ] && printf '%s:%s:%s\n' "$DOC" "$s" '同一支技能出現多個節,只留一節' >&2 +done +if [ -n "$(LC_ALL=C uniq -d "$TMPD/sections-sorted.txt")" ]; then fail=1; fi + +LC_ALL=C uniq "$TMPD/sections-sorted.txt" > "$TMPD/sections-uniq.txt" +LC_ALL=C comm -23 "$TMPD/skills.txt" "$TMPD/sections-uniq.txt" > "$TMPD/missing.txt" +LC_ALL=C comm -13 "$TMPD/skills.txt" "$TMPD/sections-uniq.txt" > "$TMPD/extra.txt" + +while IFS= read -r s; do + [ -n "$s" ] || continue + report "$s" "skills/$s/SKILL.md 存在,行為清單缺這一節,請補「## $s」" +done < "$TMPD/missing.txt" + +while IFS= read -r s; do + [ -n "$s" ] || continue + report "$s" "行為清單多這一節,skills/ 底下沒有這支技能,請移除或改名" +done < "$TMPD/extra.txt" + +# --- 3. 節順序 --- +if ! cmp -s "$TMPD/sections.txt" "$TMPD/sections-sorted.txt"; then + report - '節的排列不是技能目錄名的字典序,請重排' +fi + +# --- 4~6. 逐節查表格與五個欄位 --- +while IFS= read -r name; do + [ -n "$name" ] || continue + grep -q "^$name\$" "$TMPD/missing.txt" && continue + + hdr=$(awk -F"$TAB" -v s="$name" '$1 == "HDR" && $2 == s' "$TMPD/parsed.txt" | wc -l) + hdr=$((hdr + 0)) + if [ "$hdr" -eq 0 ]; then + report "$name" '這一節沒有表頭「| 項目 | 內容 |」,五個欄位無從判讀' + continue + fi + [ "$hdr" -eq 1 ] || report "$name" "這一節有 $hdr 張表,合約規定恰好一張" + + awk -F"$TAB" -v s="$name" '$1 == "ROW" && $2 == s { print $3 "\t" $4 }' \ + "$TMPD/parsed.txt" > "$TMPD/rows.txt" + + rows=$(wc -l < "$TMPD/rows.txt") + rows=$((rows + 0)) + [ "$rows" -eq 5 ] || report "$name" "表格有 $rows 列,合約規定 5 列:$FIELDS" + + i=0 + while IFS="$TAB" read -r item body; do + i=$((i + 1)) + [ "$i" -le 5 ] || { report "$name" "第 $i 列「$item」是多的,合約只收 5 列"; continue; } + want=$(echo "$FIELDS" | cut -d' ' -f"$i") + [ "$item" = "$want" ] || report "$name" "第 $i 列的項目要是「$want」,實際是「$item」" + [ -n "$body" ] || report "$name" "「$item」的內容欄空白,請補實際行為" + done < "$TMPD/rows.txt" +done < "$TMPD/skills.txt" + +# --- 節裡以外的表格 --- +if awk -F"$TAB" '$2 == "-" { found = 1 } END { exit found ? 0 : 1 }' "$TMPD/parsed.txt"; then + report - '有表格落在任何「## 」節之外,請搬進所屬技能的節裡' +fi + +if [ "$fail" -eq 0 ]; then + echo "行為清單檢查通過:$DOC 對上 $(wc -l < "$TMPD/skills.txt" | tr -d ' ') 支技能,五個欄位齊全" >&2 +else + echo "行為清單檢查不符:$DOC 與 $SKILLS 對不起來,逐項見上方" >&2 +fi +exit $fail