@@ -24,16 +24,17 @@ Marketplace 統一為 `jsc`( https://gitea.jsc.idv.tw/plugins/meta.git),安
| --- | --- | --- |
| --- | --- | --- |
| `hooks/ste100-guard.sh` | UserPromptSubmit | 注入 STE100 繁體中文輸出規則(hook > prompt 強制層) |
| `hooks/ste100-guard.sh` | UserPromptSubmit | 注入 STE100 繁體中文輸出規則(hook > prompt 強制層) |
| `hooks/session-timer.sh` | SessionStart / Stop / SessionEnd | 記錄工作階段起訖。子指令:`start` 記起始時間(已有紀錄就不動,給 claude 這種每階段有自己 session id 的 CLI)、`restart` 一律覆寫起始時間(給接不到 session id 的 kiro,不覆寫會把上一階段算進來)、`mark` 更新最後活動時間、`report` 供 `jsc-log:worklog` 取花費時間。`start` 與 `restart` 判定為新工作階段時,另外呼叫 `restart-gate.sh clear` 放下部署後的重啟閘門——新工作階段代表 CLI 行程是新起的,新版一定已經載入。清除的範圍只有跑到這支腳本的那一支 CLI 自己那一份狀態檔,別支沒重啟就繼續被擋 |
| `hooks/session-timer.sh` | SessionStart / Stop / SessionEnd | 記錄工作階段起訖。子指令:`start` 記起始時間(已有紀錄就不動,給 claude 這種每階段有自己 session id 的 CLI)、`restart` 一律覆寫起始時間(給接不到 session id 的 kiro,不覆寫會把上一階段算進來)、`mark` 更新最後活動時間、`report` 供 `jsc-log:worklog` 取花費時間。`start` 與 `restart` 判定為新工作階段時,另外呼叫 `restart-gate.sh clear` 放下部署後的重啟閘門——新工作階段代表 CLI 行程是新起的,新版一定已經載入。清除的範圍只有跑到這支腳本的那一支 CLI 自己那一份狀態檔,別支沒重啟就繼續被擋 |
| `hooks/version-guard.sh` | PreToolUse(Skill) | 技能使用前的版本前置檢查:本機**實際載入**版本落後遠端發佈版本就以 exit 2 擋下該次呼叫並提示更新指令(更新指令依當前 CLI 給)。只擋落後這一種情況:超前放行(開發技能組時本機本來就會超前),讀不到本機版本、推導不出站台、查不到遠端版本也一律放行。遠端版本快取在 `$JSC_HOME/version-cache/{CLI 代號}/{domain}` ,一支 CLI 一份;舊路徑 `$JSC_HOME/version-cache/{domain}` 會在第一次讀取時複製到新路徑。逃生門 `JSC_VERSION_GUARD=off` 。豁免 `jsc-cli:deploy` 、`jsc-hooks:hooks-install` 、`jsc-cli:models` 、`jsc-meta:*` |
| `hooks/version-guard.sh` | PreToolUse(Skill) | 技能使用前的版本前置檢查:本機**實際載入**版本落後遠端發佈版本就以 exit 2 擋下該次呼叫並提示更新指令(更新指令依當前 CLI 給)。只擋落後這一種情況:超前放行(開發技能組時本機本來就會超前),讀不到本機版本、推導不出站台、查不到遠端版本也一律放行。遠端版本快取在 `$JSC_HOME/version-cache/{CLI 代號}/{domain}` ,一支 CLI 一份;舊路徑 `$JSC_HOME/version-cache/{domain}` 會在第一次讀取時複製到新路徑。逃生門 `JSC_VERSION_GUARD=off` 。豁免 `jsc-cli:deploy` 、`jsc-hooks:hooks-install` 、`jsc-hooks:repair` 、 `jsc- cli:models` 、`jsc-meta:*` 、 `jsc-ask:ask` 、 `jsc-gitea:wiki` ——清單的唯一來源是 `hooks/version-guard.sh` 的檔頭,那裡一項一個理由 |
| `hooks/restart-gate.sh` | PreToolUse(Skill) | 部署後強制重啟閘門:`$JSC_HOME/restart-required.d/{CLI 代號}` 一支 CLI 一份,當前 CLI 那份存在時以 exit 2 擋下 jsc 技能呼叫,並印出要重新啟動哪一支 CLI;別支 CLI 那幾份不影響這一支。狀態檔由 `jsc-cli:deploy` 在 install 或 update 收尾時經 `restart-gate.sh require {install| update} [{domain}...]` 寫入當前 CLI 那一份,在下一個工作階段開始時由 `session-timer.sh` 呼叫 `restart-gate.sh clear` 只清除那一份。判定看檔案在不在:狀態檔讀不到、CLI 代號取不到、技能名取不到都放行(理由與 `version-guard.sh` 一致,只擋確定違規)。舊格式的單一檔案 `$JSC_HOME/restart-required` 存在時一律擋,`clear` 會一併刪掉它(過渡相容,詳見下面「部署後重啟狀態檔」)。豁免 `jsc-cli:deploy` 、`jsc-hooks:hooks-install` 、`jsc-gitea:wiki` 、`jsc-log:worklog` 、`jsc-log:learn` 、`jsc-meta:*` 、`jsc-ask:ask` 、`jsc-git:pr` 、`jsc-git:commit` ——部署後還要寫得完技能組異動報告與工作日誌,整批擋下去兩條 規則會互相打死。清單認技能名不認呼叫鏈,後三支是為了讓前六 支走得完才補進來的:`deploy` 要問模式、報告寫完要開 PR。另有唯讀子指令 `report` ,一支 CLI 一行印出每一份狀態檔的內容,看得出還有哪幾支沒重啟。逃生門 `JSC_RESTART_GATE=off` |
| `hooks/restart-gate.sh` | PreToolUse(Skill) | 部署後強制重啟閘門:`$JSC_HOME/restart-required.d/{CLI 代號}` 一支 CLI 一份,當前 CLI 那份存在時以 exit 2 擋下 jsc 技能呼叫,並印出要重新啟動哪一支 CLI;別支 CLI 那幾份不影響這一支。狀態檔由 `jsc-cli:deploy` 在 install 或 update 收尾時經 `restart-gate.sh require {install| update} [{domain}...]` 寫入當前 CLI 那一份,在下一個工作階段開始時由 `session-timer.sh` 呼叫 `restart-gate.sh clear` 只清除那一份。判定看檔案在不在:狀態檔讀不到、CLI 代號取不到、技能名取不到都放行(理由與 `version-guard.sh` 一致,只擋確定違規)。舊格式的單一檔案 `$JSC_HOME/restart-required` 存在時一律擋,`clear` 會一併刪掉它(過渡相容,詳見下面「部署後重啟狀態檔」)。豁免 `jsc-cli:deploy` 、`jsc-hooks:hooks-install` 、`jsc-hooks:repair` 、 `jsc- gitea:wiki` 、`jsc-log:worklog` 、`jsc-log:learn` 、`jsc-meta:*` 、`jsc-ask:ask` 、`jsc-git:pr` 、`jsc-git:commit` ——部署後還要寫得完技能組異動報告與工作日誌,hook 壞掉也要修得回來, 整批擋下去這些 規則會互相打死。清單認技能名不認呼叫鏈,後三支是為了讓前七 支走得完才補進來的:`deploy` 要問模式、報告寫完要開 PR。另有唯讀子指令 `report` ,一支 CLI 一行印出每一份狀態檔的內容,看得出還有哪幾支沒重啟。逃生門 `JSC_RESTART_GATE=off` |
| `hooks/skill-usage.sh` | PostToolUse(Skill) | 記錄技能使用與呼叫鏈到 `$JSC_HOME/usage/*.jsonl` ,供 `jsc-log:stats` 統計 |
| `hooks/skill-usage.sh` | PostToolUse(Skill) | 記錄技能使用與呼叫鏈到 `$JSC_HOME/usage/*.jsonl` ,供 `jsc-log:stats` 統計 |
| `hooks/comment-scope.sh` | UserPromptSubmit、PostToolUse(Write、Edit、MultiEdit)、codex `notify` 、kiro `userPromptSubmit` 、`tools/jsc-wrap.sh` 收尾 | 程式碼註解不得夾帶文件相關資訊與審查流程痕跡,共三種模式。`prompt` :在每次提示注入規則摘要(禁止項與白名單各一行),五個 CLI 都接得到。無參數:寫檔後的逐檔掃描,從 stdin JSON 取 `file_path` (或環境變數 `JSC_CHANGED_FILE` ),只有 claude 的 PostToolUse 接得上。`sweep [dir]` :掃整個 git 工作區這次改過的所有檔案,給沒有 post-tool hook 的四個 CLI 用,找不到 git 就安靜 exit 0。掃描時機每個 CLI 不同——claude 逐檔即時(PostToolUse)、codex 每輪結束(`notify` )、kiro 每輪提示送出時(`userPromptSubmit` ,掃的是上一輪寫的檔)、copilot 與 antigravity 只有工作階段結束時由 `tools/jsc-wrap.sh` 收尾掃一次。兩種掃描模式都只看 `git diff HEAD` 的新增行、不翻舊帳,命中就把警告與最多三行證據送到 stderr 並以 exit 2 交回模型就地修正(不擋寫入,檔案已經寫好了)。markdown、純文字、資料檔與二進位檔一律跳過。只實作可用樣式判定的項目,專案代號、客戶名稱這類判不出來的交給 `/jsc-review:code-review` 。規則正文的唯一來源在 `jsc-review` 的 `references/comment-scope.md` ,本存取庫不留副本。逃生門 `JSC_COMMENT_SCOPE=off` |
| `hooks/comment-scope.sh` | UserPromptSubmit、PostToolUse(Write、Edit、MultiEdit)、codex `notify` 、kiro `userPromptSubmit` 、`tools/jsc-wrap.sh` 收尾 | 程式碼註解不得夾帶文件相關資訊與審查流程痕跡,共三種模式。`prompt` :在每次提示注入規則摘要(禁止項與白名單各一行),五個 CLI 都接得到。無參數:寫檔後的逐檔掃描,從 stdin JSON 取 `file_path` (或環境變數 `JSC_CHANGED_FILE` ),只有 claude 的 PostToolUse 接得上。`sweep [dir]` :掃整個 git 工作區這次改過的所有檔案,給沒有 post-tool hook 的四個 CLI 用,找不到 git 就安靜 exit 0。掃描時機每個 CLI 不同——claude 逐檔即時(PostToolUse)、codex 每輪結束(`notify` )、kiro 每輪提示送出時(`userPromptSubmit` ,掃的是上一輪寫的檔)、copilot 與 antigravity 只有工作階段結束時由 `tools/jsc-wrap.sh` 收尾掃一次。兩種掃描模式都只看 `git diff HEAD` 的新增行、不翻舊帳,命中就把警告與最多三行證據送到 stderr 並以 exit 2 交回模型就地修正(不擋寫入,檔案已經寫好了)。markdown、純文字、資料檔與二進位檔一律跳過。只實作可用樣式判定的項目,專案代號、客戶名稱這類判不出來的交給 `/jsc-review:code-review` 。規則正文的唯一來源在 `jsc-review` 的 `references/comment-scope.md` ,本存取庫不留副本。逃生門 `JSC_COMMENT_SCOPE=off` |
| `hooks/lang-guard.sh` | UserPromptSubmit、PostToolUse(Write、Edit、MultiEdit)、codex `notify` 、kiro `userPromptSubmit` 、`tools/jsc-wrap.sh` 收尾 | 所有非程式碼輸出一律繁體中文、UTF-8、無亂碼、無簡體字,共三種模式。`prompt` :在每次提示注入規則摘要(適用範圍與自我檢查各一行),五個 CLI 都接得到。無參數:寫檔後的逐檔掃描,從 stdin JSON 取 `file_path` (或環境變數 `JSC_CHANGED_FILE` ),只有 claude 的 PostToolUse 接得上。`sweep [dir]` :掃整個 git 工作區這次改過的所有檔案,給沒有 post-tool hook 的四個 CLI 用,找不到 git 就安靜 exit 0。接線位置與掃描時機跟 `comment-scope.sh` 完全一樣,見下面那張表。偵測三項:簡體字(字表在 `hooks/simplified.txt` ,讀不到就安靜跳過這一項)、亂碼(U+FFFD 替代字元與雙重編碼殘骸)、非 UTF-8 編碼(用 `iconv` 判定,沒有 `iconv` 就跳過)。三項都掃整個檔案、不只掃註解行,`.md` 與純文字檔照掃——那些正是「非程式碼輸出」的主場,這兩點跟 `comment-scope.sh` 刻意不同。掃描深度仍只看 `git diff HEAD` 的新增行、不翻舊帳,命中就把警告與最多三行證據送到 stderr 並以 exit 2 交回模型就地修正(不擋寫入)。二進位檔(只認 NUL 位元組)與 `*.lock` 、`*.min.js` 、`*.map` 這類產生檔跳過;`hooks/simplified.txt` 、`hooks/ste100-guard.sh` 、`hooks/lang-guard.sh` 也跳過,那三份檔案裡的簡體字與亂碼樣本是被討論的對象,不是被使用。規則正文的唯一來源在 `jsc-meta` 的 `references/ste100.md` 。逃生門 `JSC_LANG_GUARD=off` |
| `hooks/lang-guard.sh` | UserPromptSubmit、PostToolUse(Write、Edit、MultiEdit)、codex `notify` 、kiro `userPromptSubmit` 、`tools/jsc-wrap.sh` 收尾 | 所有非程式碼輸出一律繁體中文、UTF-8、無亂碼、無簡體字,共三種模式。`prompt` :在每次提示注入規則摘要(適用範圍與自我檢查各一行),五個 CLI 都接得到。無參數:寫檔後的逐檔掃描,從 stdin JSON 取 `file_path` (或環境變數 `JSC_CHANGED_FILE` ),只有 claude 的 PostToolUse 接得上。`sweep [dir]` :掃整個 git 工作區這次改過的所有檔案,給沒有 post-tool hook 的四個 CLI 用,找不到 git 就安靜 exit 0。接線位置與掃描時機跟 `comment-scope.sh` 完全一樣,見下面那張表。偵測三項:簡體字(字表在 `hooks/simplified.txt` ,讀不到就安靜跳過這一項)、亂碼(U+FFFD 替代字元與雙重編碼殘骸)、非 UTF-8 編碼(用 `iconv` 判定,沒有 `iconv` 就跳過)。三項都掃整個檔案、不只掃註解行,`.md` 與純文字檔照掃——那些正是「非程式碼輸出」的主場,這兩點跟 `comment-scope.sh` 刻意不同。掃描深度仍只看 `git diff HEAD` 的新增行、不翻舊帳,命中就把警告與最多三行證據送到 stderr 並以 exit 2 交回模型就地修正(不擋寫入)。二進位檔(只認 NUL 位元組)與 `*.lock` 、`*.min.js` 、`*.map` 這類產生檔跳過;`hooks/simplified.txt` 、`hooks/ste100-guard.sh` 、`hooks/lang-guard.sh` 也跳過,那三份檔案裡的簡體字與亂碼樣本是被討論的對象,不是被使用。規則正文的唯一來源在 `jsc-meta` 的 `references/ste100.md` 。逃生門 `JSC_LANG_GUARD=off` |
| `hooks/sdlc-gate.sh` | UserPromptSubmit、PreToolUse(Skill) | SDLC 階段能力標籤閘門與模型鎖:`lock {stage}` 由 jsc-sdlc 階段技能呼叫,從可驗證來源讀出模型 id,比對該階段必要標籤(`$JSC_HOME/model-tags.tsv` ),不符就拒絕上鎖;來源優先序為 transcript、hook stdin JSON、Codex 本機 session 記錄,最後才接受 `JSC_MODEL` 人工覆寫,且回報會標明人工覆寫;`check` 在模型不符時以 exit 2 擋下該輪提示(其他 hook 一律 exit 0,此處是刻意例外);`report` 印出階段、必要標籤、模型 id、模型來源與判定結果;`unlock` 為逃生門。另含工作包 PR 閘門:`wp-lock {owner}/{repo} {index} [{工作包代號}]` 記下一筆未結清的工作包 PR、`wp-unlock {owner}/{repo} {index}` 結清那一筆(檔案不存在也算成功)、`wp-claim {owner}/{repo} {工作包代號} [{PR 編號}] [{分析頁頁名}]` 記下這個存取庫目前領取哪一包、`wp-unclaim {owner}/{repo}` 交回、`wp-report` 印出所有未結清、`wp-check {prompt| skill}` 為 hook 模式。狀態檔一個工作包一支,在 `$JSC_HOME/wp/{owner}-{repo}-{index}.pr` , **刻意不綁 session**——PR 沒合併時換一個工作階段照樣要擋;一個工作包一支鎖檔是為了讓好幾個互不相依的工作包能同時記在案,不會互相覆蓋掉對方的鎖。`wp-check prompt` 只注入提醒、絕不擋提示(擋了連「去修那支 PR」的對話都送不出去);`wp-check skill` 在有未結清 PR 時以 exit 2 擋下 `plan` 、 ` analyze`、 `maintain` ,但一律放行 `implement` (結清 PR 正是 implement 的步驟,擋它會鎖死流程)——這一層是整個存取庫共用的粗粒度提醒,「某個候選工作包能不能挑」的細粒度判斷在 `jsc-sdlc/tools/wp-gate.sh check-deps` ,不是這裡。另外會比對歸屬:未結清的 PR 不屬於目前領取的工作包時,`prompt` 多注入一行「那幾支交給領取它的工作階段」,`skill` 在擋下 `plan` 、 ` analyze` 、`maintain` 時一併點名,`implement` 仍放行但收到同一則提醒。逃生門 `JSC_WP_GATE=off` 。這道閘門只讀檔案、不打網路,PR 的真實合併狀態由 `jsc-sdlc/tools/wp-gate.sh` 查證 |
| `hooks/sdlc-gate.sh` | UserPromptSubmit、PreToolUse(Skill) | SDLC 階段能力標籤閘門與模型鎖:`lock {stage}` 由 jsc-sdlc 階段技能呼叫,從可驗證來源讀出模型 id,比對該階段必要標籤(`$JSC_HOME/model-tags.tsv` ),不符就拒絕上鎖;來源優先序為 transcript、hook stdin JSON、Codex 本機 session 記錄,最後才接受 `JSC_MODEL` 人工覆寫,且回報會標明人工覆寫;`check` 在模型不符時以 exit 2 擋下該輪提示(其他 hook 一律 exit 0,此處是刻意例外);`report` 印出階段、必要標籤、模型 id、模型來源與判定結果;`unlock` 為逃生門。另含工作包 PR 閘門:`wp-lock {owner}/{repo} {index} [{工作包代號}]` 記下一筆未結清的工作包 PR、`wp-unlock {owner}/{repo} {index}` 結清那一筆(檔案不存在也算成功)、`wp-claim {owner}/{repo} {工作包代號} [{PR 編號}] [{分析頁頁名}]` 記下這個存取庫目前領取哪一包、`wp-unclaim {owner}/{repo}` 交回、`wp-report` 印出所有未結清、`wp-check {prompt| skill}` 為 hook 模式。狀態檔一個工作包一支,在 `$JSC_HOME/wp/{owner}-{repo}-{index}.pr` , **刻意不綁 session**——PR 沒合併時換一個工作階段照樣要擋;一個工作包一支鎖檔是為了讓好幾個互不相依的工作包能同時記在案,不會互相覆蓋掉對方的鎖。`wp-check prompt` 只注入提醒、絕不擋提示(擋了連「去修那支 PR」的對話都送不出去);`wp-check skill` 在有未結清 PR 時以 exit 2 擋下 `analyze` 與 `maintain` ,但一律放行 `implement` (結清 PR 正是 implement 的步驟,擋它會鎖死流程),也放行 `plan` ,只注入提醒(plan 是純邏輯階段、不碰程式碼,而這道閘門只知道「有 PR 未合併」、判不出跟新計畫有沒有關聯;放棄的是在製品上限, `analyze` 與 `maintain` 兩道仍在,上限晚一個階段才生效) ——這一層是整個存取庫共用的粗粒度提醒,「某個候選工作包能不能挑」的細粒度判斷在 `jsc-sdlc/tools/wp-gate.sh check-deps` ,不是這裡。另外會比對歸屬:未結清的 PR 不屬於目前領取的工作包時,`prompt` 多注入一行「那幾支交給領取它的工作階段」,`skill` 在擋下 `analyze` 、`maintain` 時一併點名,`plan` 與 `implement` 仍放行但收到同一則提醒。逃生門 `JSC_WP_GATE=off` 。這道閘門只讀檔案、不打網路,PR 的真實合併狀態由 `jsc-sdlc/tools/wp-gate.sh` 查證 |
| `hooks/write-guard.sh` | PreToolUse(Write、Edit、MultiEdit)、PreToolUse(Bash) | 寫入與提交閘門,共三種擋人模式,只有 claude 有 PreToolUse,其餘四支 CLI 一條都接不上;另有一個不接 hook 的 `release` 解除模式。`stage` : `sdlc-gate.sh` 的階段鎖鎖在 `plan` 或 `analyze` 時,以 exit 2 擋下 `Write` 、`Edit` 、`MultiEdit` ——那兩個階段的產出是計畫頁與分析頁,不是檔案。階段鎖狀態檔沿用 `sdlc-gate.sh` 那一份,這裡只讀不寫。`review` :目前技能是 `jsc-review:code-review` 或 `jsc-review:api-doc` 時擋下寫入,那兩支只回報發現、不改程式碼。技能名先讀環境變數,取不到才讀 `skill-usage.sh` 記下的那一份;沒有「技能結束」事件可讀,所以紀錄超過 `JSC_WRITE_GUARD_TTL` 秒就當那支技能早已跑完。`jsc-review:comment-cleanup` **刻意不擋 ** :它本來就要改檔,只是限定僅註解行,而精確判定要解析工具參數裡整份新內容再逐語言判斷哪幾行是註解,判錯會擋掉合法的清理,代價比漏擋大,所以那條界線留給技能內文與後續審查。`commit` :擋下「同一道指令把全部變更一次加進索引再提交」,也擋下含簡體字、亂碼或非 UTF-8 編碼的提交訊息(判定整段轉呼叫 `lang-guard.sh` ,字表仍是 `hooks/simplified.txt` ,這裡不留第二份樣式)。跨兩次工具呼叫的 `git add -A` 不擋:那要記跨呼叫狀態,而被擋下的人沒有辦法讓那個狀態自己消失,閘門會把解除自己的路徑一起鎖掉。`release` :刪掉 `review` 模式認人用的那份紀錄,一律 exit 0,由 `jsc-review:code-review` 與 `jsc-review:api-doc` 在收尾時各呼叫一次。有這個模式是因為那份紀錄記的是「最近一次載入的技能」不是「還在跑的技能」——稽核收尾後呼叫端本來就要動手改,那時紀錄仍寫著稽核技能,TTL 內每一次寫入都被擋,解除路徑只剩逃生門或空等;閘門不得把解除自己的路徑一起鎖掉。逃生門 `JSC_WRITE_GUARD=off` ( `release` 不受它影響,清紀錄擋不到任何人) |
Claude 由 `hooks/hooks.json` 自動接線八 支 hook;其他 CLI 用 `hooks-install` 技能接線、改裝包裝啟動器,或降級為規則檔。寫進使用者設定的長期命令一律指向 `$JSC_HOME/current/jsc-hooks` ,不指向帶版號的 plugin 快取目錄,也不指向開發存取庫。
Claude 由 `hooks/hooks.json` 自動接線九 支 hook;其他 CLI 用 `hooks-install` 技能接線、改裝包裝啟動器,或降級為規則檔。寫進使用者設定的長期命令一律指向 `$JSC_HOME/current/jsc-hooks` ,不指向帶版號的 plugin 快取目錄,也不指向開發存取庫。
> 覆蓋範圍要據實看待:只有 claude 同時有 PreToolUse、PostToolUse 與 UserPromptSubmit, 八 支 hook 全接得上,回報 `wired`。codex、copilot、antigravity、kiro 都沒有 pre-tool hook,接不上 `version-guard.sh` 的版本前置檢查,也 接不上 `restart-gate.sh` 的部署後重啟閘門,SDLC 模型鎖也只剩技能步驟檢查,這四個 CLI 一律回報 `degraded`,靠 `/jsc-cli:deploy` 定期更新。重啟閘門在這四個 CLI 上一次技能呼叫都擋不下來:那一支自己那份狀態檔照樣寫、下一個工作階段開始照樣清,只是中間沒有判定點,重啟得靠 `/jsc-cli:deploy` 收尾的提示自己動手。codex 另外沒有工作階段開始事件,計時改由 `tools/jsc-wrap.sh` 的 `codex` 別名在啟動當下開始;沒走別名啟動時,時間從第一輪回應算起。
> 覆蓋範圍要據實看待:只有 claude 同時有 PreToolUse、PostToolUse 與 UserPromptSubmit, 九 支 hook 全接得上,回報 `wired`。codex、copilot、antigravity、kiro 都沒有 pre-tool hook,接不上 `version-guard.sh` 的版本前置檢查,接不上 `restart-gate.sh` 的部署後重啟閘門,也接不上 `write-guard.sh` 的三種模式, SDLC 模型鎖也只剩技能步驟檢查,這四個 CLI 一律回報 `degraded`,靠 `/jsc-cli:deploy` 定期更新。重啟閘門在這四個 CLI 上一次技能呼叫都擋不下來:那一支自己那份狀態檔照樣寫、下一個工作階段開始照樣清,只是中間沒有判定點,重啟得靠 `/jsc-cli:deploy` 收尾的提示自己動手。codex 另外沒有工作階段開始事件,計時改由 `tools/jsc-wrap.sh` 的 `codex` 別名在啟動當下開始;沒走別名啟動時,時間從第一輪回應算起。
> `comment-scope.sh` 與 `lang-guard.sh` 五個 CLI 都掃得到,接的是同一批位置,但時機不同,不能當成五支一樣:
> `comment-scope.sh` 與 `lang-guard.sh` 五個 CLI 都掃得到,接的是同一批位置,但時機不同,不能當成五支一樣:
@@ -92,6 +93,12 @@ Claude 由 `hooks/hooks.json` 自動接線八支 hook;其他 CLI 用 `hooks-in
> 代表這台機器無法做版本檢查,跟「全部最新」是兩件事。查遠端版本走與 hook 同一份快取
> 代表這台機器無法做版本檢查,跟「全部最新」是兩件事。查遠端版本走與 hook 同一份快取
> 與同一個 `JSC_VERSION_TTL`,但快取依 CLI 分開,一次部署不會為同一支 CLI 的每個 domain 重複打一輪網路。
> 與同一個 `JSC_VERSION_TTL`,但快取依 CLI 分開,一次部署不會為同一支 CLI 的每個 domain 重複打一輪網路。
> `jsc-cli:deploy` 用它決定要不要把「更新」設成推薦選項。
> `jsc-cli:deploy` 用它決定要不要把「更新」設成推薦選項。
>
> `version-guard.sh recommend` 是同一份比對的結論版,只印一行 `recommend<TAB>update| none| unverifiable`,
> 永遠 exit 0。判定規則寫在腳本檔頭:任一 plugin 落後就 `update`;查不到本機註冊檔、
> 一列 domain 都沒有、或每一列都查詢失敗都是 `unverifiable`;其餘是 `none`。查詢失敗那幾列
> 不計入——查不到不等於最新。證據表要另外看就再呼叫一次 `report`,兩者刻意不混印,
> 呼叫端取第二欄的解析才不會被表格內容打亂。
### 狀態檔盤點
### 狀態檔盤點
@@ -114,8 +121,8 @@ Claude 由 `hooks/hooks.json` 自動接線八支 hook;其他 CLI 用 `hooks-in
| --- | --- |
| --- | --- |
| `tools/jsc-wrap.sh` | 沒有完整 hook 系統的 CLI 的包裝啟動器:匯出 `JSC_CLI` 、`JSC_SESSION_ID` ,前後接 `session-timer.sh` ,結束時自動跑 `scan-logs.sh` 回填,再依序跑一次 `comment-scope.sh sweep` 與 `lang-guard.sh sweep` 掃整個 git 工作區的註解範圍與繁中編碼(copilot 與 antigravity 沒有任何逐輪事件,整個工作階段只有這裡掃得到)。兩次收尾掃描一律不影響結束碼:包裝器原樣回傳 CLI 自己的結束碼,`sweep` 命中只把警告印到 stderr。`JSC_CLI` 存 CLI 代號,實際執行的是對應的執行檔(antigravity 是 agy、kiro 是 kiro-cli) |
| `tools/jsc-wrap.sh` | 沒有完整 hook 系統的 CLI 的包裝啟動器:匯出 `JSC_CLI` 、`JSC_SESSION_ID` ,前後接 `session-timer.sh` ,結束時自動跑 `scan-logs.sh` 回填,再依序跑一次 `comment-scope.sh sweep` 與 `lang-guard.sh sweep` 掃整個 git 工作區的註解範圍與繁中編碼(copilot 與 antigravity 沒有任何逐輪事件,整個工作階段只有這裡掃得到)。兩次收尾掃描一律不影響結束碼:包裝器原樣回傳 CLI 自己的結束碼,`sweep` 命中只把警告印到 stderr。`JSC_CLI` 存 CLI 代號,實際執行的是對應的執行檔(antigravity 是 agy、kiro 是 kiro-cli) |
| `tools/scan-logs.sh` | 離線回填:解析 copilot、antigravity、codex 的原生日誌,把技能用量與階段界線補進 `$JSC_HOME` ,重掃不重複 |
| `tools/scan-logs.sh` | 離線回填:解析 copilot、antigravity、codex 的原生日誌,把技能用量與階段界線補進 `$JSC_HOME` ,重掃不重複 |
| `tools/report-error.sh` | 失敗回報流程:把一筆 hook 或工具異常寫成 wiki 的 `ERROR_{HASH}` ,並在 `ERROR_CONTENTS` 附上一列索引。wiki 位置由 `jsc-gitea` 的 `gitea.sh wiki-repo ERROR` 解析,解析不出來就安靜降級。由操作者手動執行,或由 `hooks-install` 在 `wire-cli.sh` 回報 `status=failed` 時執行;**不接在失敗的 hook 上自動觸發**(hook 一律安靜 exit 0,自我回報會疊出迴圈) |
| `tools/report-error.sh` | 失敗回報流程:把一筆 hook 或工具異常寫成 wiki 的 `ERROR_{HASH}` ,並在 `ERROR_CONTENTS` 附上一列索引。目錄頁一律先讀回舊頁再附加新列、整頁寫回,不整頁覆蓋:只有 `wiki-get` 回 4(頁面真的不存在)才用範本建新頁,回 7(金鑰失效)或 8(其他 API 失敗)代表舊內容未知,放棄目錄頁寫入並以 exit 4 回報,免得拿範本蓋掉所有既有列。 wiki 位置由 `jsc-gitea` 的 `gitea.sh wiki-repo ERROR` 解析,解析不出來就安靜降級。由操作者手動執行,或由 `hooks-install` 在 `wire-cli.sh` 回報 `status=failed` 時執行;**不接在失敗的 hook 上自動觸發**(hook 一律安靜 exit 0,自我回報會疊出迴圈) |
| `tools/wire-cli.sh` | 單一 CLI 的 hook 生命週期,共三 個用法。`{cli}` 是接線:先建立或更新 `$JSC_HOME/current/jsc-hooks` 指向目前這版 plugin,接著把對應的設定編輯、包裝別名安裝、hook 檔建立成穩定路徑,皆以 `<!-- jsc-hooks -->` (或 `# jsc-hooks` )標記整段重寫,重跑等同先移除再重裝;寫完每個檔案會重讀驗證位置正確才回報成功(codex 的 `notify` 必須是根層鍵、kiro 的 JSON 必須成對且 `on` 、`run` 在最上層),也會確認寫入路徑能解到既有腳本。檔案系統不能建立 symlink 時,會明確回報並退回目前根目錄,不會靜默寫出壞路徑。`status=wired\|degraded\|skipped\|failed` 回報接線結果。`purge {cli}` 是移除:把該 CLI 的**所有** hook 清掉,含非 jsc 的第三方項目,動到的檔案先原樣備份到 `$JSC_HOME/backup/hooks/{cli}/{yyyyMMdd_HHmmss}/` ,備份失敗就不移除;移除標記段落時會先去掉標記行前後空白,所以縮排或尾端補空白的 jsc 區塊一樣會移除;移除後重讀驗證,驗不過自動還原備份,以 `status=purged\|skipped\|failed` 回報。`smoke {cli}` 是執行期冒煙測試:八 支 hook 的每個接線模式各跑一次,非零退出即為錯誤,另外用一份暫時的 `$JSC_HOME` 狀態檔把工作包歸屬的五條判定路徑與重啟閘門的十二條判定與清除 路徑各跑一次並比對結束碼,驗的是判定結果本身,不只是腳本跑得完(例外有三 個:`sdlc-gate.sh check` 的 exit 2 是階段鎖的設計行為,`comment-scope.sh` 與 `lang-guard.sh` 掃描模式的 exit 2 是掃到 違規的設計行為——`sweep` 在髒工作區本來就會回 2, 不算 hook 壞掉),以 `status=ok\|failed` 回報 。`status {cli}` 是唯讀盤點:只讀設定檔判斷標記段落在不在,不寫檔也不執行 hook,每個接線點印一行 `item<TAB>{項目}<TAB>{路徑}<TAB>{present\|missing}` ,也會把帶版號快取路徑、開發存取庫路徑與不存在的腳本列為缺項;`status claude` 讀 Claude Code 實際載入的 `installed_plugins.json` ,不再檢查目前腳本旁邊那份 `hooks.json` 。體檢類技能(`/jsc-cli:doctor` )只能用這個子命令,另外三個都會動到環境 |
| `tools/wire-cli.sh` | 單一 CLI 的 hook 生命週期,共四 個用法。`{cli}` 是接線:先建立或更新 `$JSC_HOME/current/jsc-hooks` 指向目前這版 plugin,接著把對應的設定編輯、包裝別名安裝、hook 檔建立成穩定路徑,皆以 `<!-- jsc-hooks -->` (或 `# jsc-hooks` )標記整段重寫,重跑等同先移除再重裝;寫完每個檔案會重讀驗證位置正確才回報成功(codex 的 `notify` 必須是根層鍵、kiro 的 JSON 必須成對且 `on` 、`run` 在最上層),也會確認寫入路徑能解到既有腳本。檔案系統不能建立 symlink 時,會明確回報並退回目前根目錄,不會靜默寫出壞路徑。`status=wired\|degraded\|skipped\|failed` 回報接線結果。`purge {cli}` 是移除:把該 CLI 的**所有** hook 清掉,含非 jsc 的第三方項目,動到的檔案先原樣備份到 `$JSC_HOME/backup/hooks/{cli}/{yyyyMMdd_HHmmss}/` ,備份失敗就不移除;移除標記段落時會先去掉標記行前後空白,所以縮排或尾端補空白的 jsc 區塊一樣會移除;移除後重讀驗證,驗不過自動還原備份,以 `status=purged\|skipped\|failed` 回報。`smoke {cli}` 是執行期冒煙測試:九 支 hook 的每個接線模式各跑一次,非零退出即為錯誤,另外用一份暫時的 `$JSC_HOME` 狀態檔把工作包歸屬、部署後重啟閘門與寫入提交閘門的每條判定 路徑各跑一次並比對結束碼,驗的是判定結果本身,不只是腳本跑得完(例外有四 個:`sdlc-gate.sh check` 的 exit 2 是階段鎖的設計行為,`comment-scope.sh` 、 `lang-guard.sh` 掃描模式與 `write-guard.sh` 三種模式 的 exit 2 是命中 違規的設計行為——`sweep` 在髒工作區本來就會回 2, `write-guard.sh` 在機器剛好鎖在 `plan` 階段時也會回 2,都不算 hook 壞掉),以 `status=ok\|failed` 回報。**結果行數由腳本自己數、自己斷言**: `status=` 之後緊接一行 `lines<TAB>{數量}` ,那是其後 `[jsc]` 結果行的實際條數,與腳本內逐類宣告的預期條數比對,不符就回非零。判定路徑增減時只改腳本裡的預期值,散文一律引用這一行,不另外抄一份數字 。`status {cli}` 是唯讀盤點:只讀設定檔判斷標記段落在不在,不寫檔也不執行 hook,每個接線點印一行 `item<TAB>{項目}<TAB>{路徑}<TAB>{present\|missing}` ,也會把帶版號快取路徑、開發存取庫路徑與不存在的腳本列為缺項;`status claude` 讀 Claude Code 實際載入的 `installed_plugins.json` ,不再檢查目前腳本旁邊那份 `hooks.json` 。體檢類技能(`/jsc-cli:doctor` )只能用這個子命令,另外三個都會動到環境;那道限制另有程式層把關, `JSC_READONLY=1` 之下只准 `status` 與 `smoke` , `purge` 與接線一律以 exit 6 拒絕並回報 `status=readonly` ,環境不會被動到 |
| `tools/scan-hook-errors.sh` | 掃 CLI 原生紀錄找 hook 的執行期錯誤(接線寫對、跑起來出錯)。只有 claude 有 hook 結果紀錄,掃 `~/.claude/projects/**/*.jsonl` 的 `hook_non_blocking_error` 與非空 `hookErrors` ; codex、copilot、antigravity、kiro 沒有等價紀錄,一律回報 `unavailable` 並指向 `wire-cli.sh smoke {cli}` 。每筆錯誤附加一行 JSON 到 `$JSC_HOME/errors/hooks.jsonl` , `jsc` 欄位標明是不是 jsc 自己的 hook(第三方 hook 的錯誤只回報,不由 jsc 修正);去重與 `scan-logs.sh` 同法,重掃只讀新增段落,以 `status=clean\|errors\|unavailable` 回報 |
| `tools/scan-hook-errors.sh` | 掃 CLI 原生紀錄找 hook 的執行期錯誤(接線寫對、跑起來出錯)。只有 claude 有 hook 結果紀錄,掃 `~/.claude/projects/**/*.jsonl` 的 `hook_non_blocking_error` 與非空 `hookErrors` ; codex、copilot、antigravity、kiro 沒有等價紀錄,一律回報 `unavailable` 並指向 `wire-cli.sh smoke {cli}` 。每筆錯誤附加一行 JSON 到 `$JSC_HOME/errors/hooks.jsonl` , `jsc` 欄位標明是不是 jsc 自己的 hook(第三方 hook 的錯誤只回報,不由 jsc 修正);去重與 `scan-logs.sh` 同法,重掃只讀新增段落,以 `status=clean\|errors\|unavailable` 回報 |
## 失敗回報範本
## 失敗回報範本
@@ -136,7 +143,7 @@ Claude 由 `hooks/hooks.json` 自動接線八支 hook;其他 CLI 用 `hooks-in
### `hooks-install`
### `hooks-install`
把八 支 hook 接線到所有已安裝的 CLI,每個 CLI 走四 道關卡:先 `tools/wire-cli.sh purge {cli}` 備份後移除所有 hook(含非 jsc 的第三方項目,乾淨起跑才分得清後續失敗是誰的),再 `tools/wire-cli.sh {cli}` 接線(claude 由 `hooks.json` 自動接線,無需寫入;其他 CLI 的持久命令會寫成 `$JSC_HOME/current/jsc-hooks` 穩定路徑),接著 `tools/wire-cli.sh smoke {cli}` 驗執行期,最後 `tools/scan-hook-errors.sh --cli {cli}` 掃原生紀錄。codex、copilot、antigravity 由接線腳本裝上 `tools/jsc-wrap.sh` 包裝別名補上計時與用量回填(結束時自動跑 `tools/scan-logs.sh` ),語言規則仍重寫到各自的規則檔(以 `<!-- jsc-hooks -->` 標記整段取代,等同先移除再重裝,不重複追加)。codex、copilot、antigravity、kiro 的 SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 SDLC 技能直接呼叫 `sdlc-gate.sh lock` 寫入;這四個 CLI 沒有 pre-tool hook,版本前置檢查與 部署後重啟閘門都接不上(重啟閘門在那四支上一次技能呼叫都擋不下來,狀態檔照樣寫、下個工作階段照樣清);也沒有 post-tool hook, `comment-scope.sh` 接不到逐檔即時掃描,改用 `sweep` 掃整個 git 工作區——codex 每輪結束、kiro 每輪提示送出時、copilot 與 antigravity 只有工作階段結束時掃一次,腳本會在 `reason` 裡講明各自的時機,只有 claude 回報 `wired` ,也只有 claude 掃得到執行期錯誤紀錄。任一關卡出錯(purge、接線、冒煙失敗,或掃到 `jsc=true` 的執行期錯誤)就先寫 `ERROR_{HASH}` ,再交給 `repair` 技能接手並以 `develop` PR 收尾;此時允許中止剩下的安裝,但修正一定要開始。掃到 `jsc=false` 的第三方 hook 錯誤只回報,不轉修正。
把九 支 hook 接線到所有已安裝的 CLI,每個 CLI 走五 道關卡:先 `tools/wire-cli.sh purge {cli}` 備份後移除所有 hook(含非 jsc 的第三方項目,乾淨起跑才分得清後續失敗是誰的),再 `tools/wire-cli.sh {cli}` 接線(claude 由 `hooks.json` 自動接線,無需寫入;其他 CLI 的持久命令會寫成 `$JSC_HOME/current/jsc-hooks` 穩定路徑),接著 `tools/wire-cli.sh status {cli}` 唯讀盤點接線結果,再 `tools/wire-cli.sh smoke {cli}` 驗執行期,最後 `tools/scan-hook-errors.sh --cli {cli}` 掃原生紀錄。第一支 CLI 的管線單獨跑完( `$JSC_HOME/current/jsc-hooks` 連結由它統一更新),其餘各 CLI 的管線才並行。 codex、copilot、antigravity 由接線腳本裝上 `tools/jsc-wrap.sh` 包裝別名補上計時與用量回填(結束時自動跑 `tools/scan-logs.sh` ),語言規則仍重寫到各自的規則檔(以 `<!-- jsc-hooks -->` 標記整段取代,等同先移除再重裝,不重複追加)。codex、copilot、antigravity、kiro 的 SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 SDLC 技能直接呼叫 `sdlc-gate.sh lock` 寫入;這四個 CLI 沒有 pre-tool hook,版本前置檢查、 部署後重啟閘門與 `write-guard.sh` 的三種模式 都接不上(重啟閘門在那四支上一次技能呼叫都擋不下來,狀態檔照樣寫、下個工作階段照樣清);也沒有 post-tool hook, `comment-scope.sh` 接不到逐檔即時掃描,改用 `sweep` 掃整個 git 工作區——codex 每輪結束、kiro 每輪提示送出時、copilot 與 antigravity 只有工作階段結束時掃一次,腳本會在 `reason` 裡講明各自的時機,只有 claude 回報 `wired` ,也只有 claude 掃得到執行期錯誤紀錄。任一關卡出錯(purge、接線、冒煙失敗,或掃到 `jsc=true` 的執行期錯誤)就先寫 `ERROR_{HASH}` ,再交給 `repair` 技能接手並以 `develop` PR 收尾;此時允許中止剩下的安裝,但修正一定要開始。掃到 `jsc=false` 的第三方 hook 錯誤只回報,不轉修正。
### `repair`
### `repair`
@@ -158,7 +165,11 @@ Claude 由 `hooks/hooks.json` 自動接線八支 hook;其他 CLI 用 `hooks-in
| `JSC_RESTART_GATE` | 設 `off` 完全略過部署後重啟閘門(`restart-gate.sh` 一律放行) | 啟用閘門 |
| `JSC_RESTART_GATE` | 設 `off` 完全略過部署後重啟閘門(`restart-gate.sh` 一律放行) | 啟用閘門 |
| `JSC_COMMENT_SCOPE` | 設 `off` 完全略過註解範圍檢查(`comment-scope.sh` 三種模式都直接結束) | 啟用檢查 |
| `JSC_COMMENT_SCOPE` | 設 `off` 完全略過註解範圍檢查(`comment-scope.sh` 三種模式都直接結束) | 啟用檢查 |
| `JSC_LANG_GUARD` | 設 `off` 完全略過繁中與編碼檢查(`lang-guard.sh` 三種模式都直接結束) | 啟用檢查 |
| `JSC_LANG_GUARD` | 設 `off` 完全略過繁中與編碼檢查(`lang-guard.sh` 三種模式都直接結束) | 啟用檢查 |
| `JSC_WRITE_GUARD` | 設 `off` 完全略過寫入與提交閘門(`write-guard.sh` 三種模式都直接結束) | 啟用閘門 |
| `JSC_WRITE_GUARD_TTL` | `write-guard.sh review` 判定「稽核技能還在跑」的時效秒數 | 預設 900 |
| `JSC_READONLY` | 設 `1` 時 `tools/wire-cli.sh` 只准 `status` 與 `smoke` , `purge` 與接線一律拒絕並回 exit 6 | 四個用法都可執行 |
| `JSC_CHANGED_FILE` | 非 Claude CLI 要掃描的檔案路徑,代替 stdin JSON 的 `file_path` ,供 `comment-scope.sh` 與 `lang-guard.sh` 使用 | 安靜降級,不掃描 |
| `JSC_CHANGED_FILE` | 非 Claude CLI 要掃描的檔案路徑,代替 stdin JSON 的 `file_path` ,供 `comment-scope.sh` 與 `lang-guard.sh` 使用 | 安靜降級,不掃描 |
| `JSC_TOOL_COMMAND` | 非 Claude CLI 要判定的 Bash 指令字串,代替 stdin JSON 的 `command` ,供 `write-guard.sh commit` 使用 | 安靜降級,不判定 |
| `JSC_CLI` / `JSC_SESSION_ID` / `JSC_SKILL` / `JSC_TOOL_NAME` | 非 Claude CLI 接線時由 `tools/jsc-wrap.sh` 或接線設定提供,代替 stdin JSON 的 `session_id` 、`skill` 、`tool_name` ( `version-guard.sh` 也收沒有前綴的 `SKILL` 、`TOOL_NAME` ) | 安靜降級 |
| `JSC_CLI` / `JSC_SESSION_ID` / `JSC_SKILL` / `JSC_TOOL_NAME` | 非 Claude CLI 接線時由 `tools/jsc-wrap.sh` 或接線設定提供,代替 stdin JSON 的 `session_id` 、`skill` 、`tool_name` ( `version-guard.sh` 也收沒有前綴的 `SKILL` 、`TOOL_NAME` ) | 安靜降級 |
| `JSC_MODEL` | `sdlc-gate.sh` 找不到 transcript、hook stdin JSON 與 Codex 本機 session 記錄時的人工覆寫模型 id;回報會標明 `人工覆寫:JSC_MODEL` | 找不到可驗證模型來源時拒絕 `lock` ,並列出已檢查來源與修復建議 |
| `JSC_MODEL` | `sdlc-gate.sh` 找不到 transcript、hook stdin JSON 與 Codex 本機 session 記錄時的人工覆寫模型 id;回報會標明 `人工覆寫:JSC_MODEL` | 找不到可驗證模型來源時拒絕 `lock` ,並列出已檢查來源與修復建議 |