Reviewed-on: #37
jsc-hooks — 跨 CLI Hooks
jsc 技能組的 hooks domain:所有 hook 只放在這個 repo(技能準則)。腳本為 POSIX shell,同時支援 stdin JSON(Claude 格式)與環境變數輸入,適用 claude / codex / copilot / antigravity / kiro。
安裝、更新、移除
Marketplace 統一為 jsc(https://gitea.jsc.idv.tw/plugins/meta.git),安裝 token 為 jsc-hooks@jsc。每個指令一行:
| CLI | 安裝 | 更新 | 移除 |
|---|---|---|---|
| claude | claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && claude plugin install jsc-hooks@jsc |
claude plugin marketplace update jsc && claude plugin update jsc-hooks@jsc |
claude plugin uninstall jsc-hooks@jsc |
| codex | codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && codex plugin add jsc-hooks@jsc |
codex plugin marketplace upgrade jsc |
codex plugin remove jsc-hooks@jsc |
| copilot | copilot plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && copilot plugin install jsc-hooks@jsc |
copilot plugin marketplace update jsc && copilot plugin update jsc-hooks@jsc |
copilot plugin uninstall jsc-hooks@jsc |
| antigravity | git clone https://gitea.jsc.idv.tw/plugins/hooks.git ~/plugins/hooks && agy plugin install ~/plugins/hooks |
git -C ~/plugins/hooks pull && agy plugin uninstall jsc-hooks && agy plugin install ~/plugins/hooks |
agy plugin uninstall jsc-hooks |
| kiro | kiro-cli plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && kiro-cli plugin install jsc-hooks@jsc |
kiro-cli plugin marketplace update jsc && kiro-cli plugin update jsc-hooks@jsc |
kiro-cli plugin uninstall jsc-hooks@jsc |
antigravity 不支援 gitea URL 安裝,改用本地 clone 路徑。批次操作五個 CLI:使用
/jsc-cli:deploy。
舊入口
plugins/jsc已移除,marketplace 正本移到plugins/meta。marketplace 名稱仍是jsc(取自 marketplace.json 的name欄位,與存取庫名無關),安裝 token 不變;已從舊入口安裝過的人先執行claude plugin marketplace remove jsc,再依上表重新 add。
Hooks
| 腳本 | 事件 | 作用 |
|---|---|---|
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/version-guard.sh |
PreToolUse(Skill) | 技能使用前的版本前置檢查:本機實際載入版本落後遠端發佈版本就以 exit 2 擋下該次呼叫並提示更新指令(更新指令依當前 CLI 給)。只擋落後這一種情況:超前放行(開發技能組時本機本來就會超前),讀不到本機版本、推導不出站台、查不到遠端版本也一律放行。逃生門 JSC_VERSION_GUARD=off。豁免 jsc-cli:deploy、jsc-hooks:hooks-install、jsc-cli:models、jsc-meta:* |
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/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/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 階段技能呼叫,從 transcript 讀出實際模型 id 比對該階段必要標籤($JSC_HOME/model-tags.tsv),不符就拒絕上鎖;check 在模型不符時以 exit 2 擋下該輪提示(其他 hook 一律 exit 0,此處是刻意例外);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 查證 |
Claude 由 hooks/hooks.json 自動接線八支 hook;其他 CLI 用 hooks-install 技能接線、改裝包裝啟動器,或降級為規則檔。
覆蓋範圍要據實看待:只有 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別名在啟動當下開始;沒走別名啟動時,時間從第一輪回應算起。
comment-scope.sh與lang-guard.sh五個 CLI 都掃得到,接的是同一批位置,但時機不同,不能當成五支一樣:
| CLI | 掃描時機 | 接在哪裡 |
|---|---|---|
| claude | 逐檔即時,寫完哪個檔就掃哪個 | PostToolUse |
| codex | 每輪結束,掃整個 git 工作區 | config.toml 的根層 notify |
| kiro | 每輪提示送出時,掃整個 git 工作區(掃到的是上一輪寫的檔) | .kiro/hooks/jsc-hooks.json 的 userPromptSubmit |
| copilot、antigravity | 工作階段結束時掃一次 | tools/jsc-wrap.sh 收尾 |
上表對
comment-scope.sh與lang-guard.sh同時成立,兩支接在同一批位置。sweep看的是git diff HEAD,涵蓋範圍與 claude 一樣,差的是回饋速度:claude 當下就叫,其他四個要等到該輪或該階段結束。不在 git 工作區內時sweep安靜 exit 0,等於沒掃。規則提示(prompt模式)在五個 CLI 都照樣寫進規則檔,三段(STE100、註解範圍、繁中編碼)共用同一個標記段落——晚一輪的警告,價值仍低於一開始就不要寫。判不出來的項目(專案代號、客戶名稱)一律交給/jsc-review:code-review第 2 組。
工作包歸屬狀態檔
$JSC_HOME/wp/ 底下兩種檔案,都是純文字 key=value,一行一欄位,順序不拘,不認得的鍵一律忽略。格式壓到最簡,jsc-hooks 與 jsc-sdlc 兩邊各自實作也對得上。
| 檔案 | 欄位 | 範例(一行一欄位) |
|---|---|---|
鎖檔 {owner}-{repo}-{index}.pr |
repo、index、wp、locked |
repo=jsc/demo、index=12、wp=WP-03、locked=2026-08-27T02:00:00Z |
領取檔 {owner}-{repo}.claim |
repo、wp、pr、analyze、claimed |
repo=jsc/demo、wp=WP-03、pr=12、analyze=ANALYZE_1A2B3C4D、claimed=2026-08-27T02:00:00Z |
寫檔的一律是 jsc-sdlc(領工作包時呼叫 wp-claim,開完 PR 呼叫 wp-lock),hook 只讀檔比對:把鎖檔的 wp 和同一個存取庫領取檔的 wp 取數字比一次,兩邊都有值且不相等,那支 PR 就不是這裡該處理的。
歸屬查不到就放行(exit 0,只注入提醒):沒有分析頁、wp 沒寫、領取檔不存在,三種都算這一類。理由與 version-guard.sh 一致——只擋確定違規,否則會把技能組維護自己鎖死。舊版鎖檔是單行 TSV({repo}<TAB>{index}<TAB>{上鎖時間},沒有工作包欄位),照樣讀得動,讀出來是查無歸屬。
部署後重啟狀態檔
$JSC_HOME/restart-required.d/{CLI 代號}(JSC_HOME 未設定時為 ~/.jsc)一支 CLI 一份,檔名就是 CLI 代號(claude、codex、copilot、antigravity、kiro)。格式與工作包狀態檔同一套:純文字 key=value,一行一欄位,順序不拘,不認得的鍵一律忽略。jsc-hooks 與 jsc-cli 兩邊各自實作也對得上。
| 欄位 | 內容 | 範例 |
|---|---|---|
at |
部署收尾時間,UTC | at=2026-08-27T02:00:00Z |
mode |
這次部署的模式,install 或 update |
mode=update |
domains |
這次更新到的 domain,空白分隔 | domains=hooks cli meta |
cli |
執行部署的 CLI 代號,與檔名相同 | cli=claude |
一支 CLI 一份是為了修兩個實測抓到的洞:一台機器上五支 CLI 各自是獨立行程,各自載入自己記憶體裡的那一版。早先的單一檔案設計裡,並行部署會互相覆寫(後寫的把 domains 與 cli 蓋掉,欄位不再代表先寫的那一支),而且任一支 CLI 重啟就把五支的閘門一起解除,其餘四支沒重啟卻不再被擋,閘門在多 CLI 環境等於半失效。拆成一支一份之後,寫入、判定、清除三件事都只碰自己那一份。
寫檔的一律是 jsc-cli:deploy,經 restart-gate.sh require {install|update} [{domain}...] 落地,寫的是當前 CLI 那一份;取不到 CLI 代號或寫不進去都會 exit 2 並講明「這次部署沒有掛上重啟閘門」——沒寫成就沒有閘門,不能讓部署以為掛上了。清除的一律是 session-timer.sh:start 判定起始檔不存在(這個 session id 第一次開始)、或 restart(接不到 session id 的 CLI,每次工作階段開始都算新的)時,呼叫 restart-gate.sh clear,只刪呼叫端那一支自己那一份。判準留在 session-timer.sh、狀態檔留在 restart-gate.sh,兩邊都不抄對方那一半。
欄位只用在擋人訊息上。判定看的是「當前 CLI 那份檔案在不在」——檔案存在就是這一支還沒重啟過的證據,欄位缺了只讓訊息少幾個字。別支 CLI 那幾份一律不看。狀態檔讀不到、CLI 代號取不到、技能名取不到一律放行,理由與 version-guard.sh 相同。
restart-gate.sh report 一份狀態檔印一行,欄位以空白分隔,domains 可能含空白所以擺最後:
{CLI 代號} at={ISO 時間} mode={install|update} domains={domain 清單}
有幾行就代表有幾支 CLI 還沒重啟;一份都沒有就不印。欄位缺值時只留鍵名(例如 domains=)。第一欄印 legacy 的那一行代表下面說的舊格式單一檔案,它不屬於任何一支 CLI。
舊檔相容(過渡用)。 舊版把狀態寫進 $JSC_HOME/restart-required 單一檔案。改用狀態目錄的第一輪部署,機器上可能還留著那份舊檔,所以判定與清除都認它:舊檔存在就一律擋,視為「每一支 CLI 都有未重啟的部署」,擋人訊息會標明這是舊格式紀錄;clear 除了刪當前 CLI 那一份,也一併刪掉舊檔。取捨講白:clear 只在新工作階段被呼叫,呼叫到就代表確實有一支 CLI 重新啟動過了;舊檔沒有 per-CLI 資訊,留著會讓五支 CLI 一路被擋到有人手動刪,刪掉是唯一收斂的做法,代價是同一輪部署的其他 CLI 少擋一次,只影響改用狀態目錄的那一輪。這一段相容邏輯在所有機器都跑過一次寫狀態目錄的部署與重啟之後就可以整段移除,屆時舊檔不會再被寫出來。
version-guard.sh report是非 hook 的子指令:印出每個已安裝 jsc plugin 的 「{domain} {本機} {遠端} {落後|最新|超前|查詢失敗}」,最後一行behind {落後個數}。 本機沒有 Claude 的 plugin 註冊檔時改印noregistry {路徑}再接behind 0, 代表這台機器無法做版本檢查,跟「全部最新」是兩件事。查遠端版本走與 hook 同一份快取 與同一個JSC_VERSION_TTL,一次部署不會為每個 domain 各打一輪網路。jsc-cli:deploy用它決定要不要把「更新」設成推薦選項。
工具
| 腳本 | 用途 |
|---|---|
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/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/wire-cli.sh |
單一 CLI 的 hook 生命週期,共三個用法。{cli} 是接線:對應的設定編輯、包裝別名安裝、hook 檔建立,皆以 <!-- jsc-hooks -->(或 # jsc-hooks)標記整段重寫,重跑等同先移除再重裝;寫完每個檔案會重讀驗證位置正確才回報成功(codex 的 notify 必須是根層鍵、kiro 的 JSON 必須成對且 on、run 在最上層),以 status=wired|degraded|skipped|failed 回報。purge {cli} 是移除:把該 CLI 的所有 hook 清掉,含非 jsc 的第三方項目,動到的檔案先原樣備份到 $JSC_HOME/backup/hooks/{cli}/{yyyyMMdd_HHmmss}/,備份失敗就不移除,移除後重讀驗證,驗不過自動還原備份,以 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=wired|degraded|unwired|skipped 回報(結束碼 0、1、5、3)。體檢類技能(/jsc-cli:doctor)只能用這個子命令,另外三個都會動到環境 |
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/report-error.sh 填欄位後寫進 wiki。
用法:tools/report-error.sh --hook {名稱} --exit {碼} --summary {摘要},錯誤輸出摘要走標準輸入。
| 範本 | 用途 |
|---|---|
templates/error-page.md |
單筆 hook 異常頁 ERROR_{HASH},記錄當次失敗的觸發條件、錯誤摘要與處理結果。 |
templates/error-contents.md |
異常目錄 ERROR_CONTENTS,彙整所有異常頁,方便先看最新問題再往下追。 |
Skills 目錄
呼叫方式:Claude / Antigravity /jsc-hooks:{name};Codex ${name};Copilot / Kiro 描述需求自動觸發。
hooks-install
把八支 hook 接線到所有已安裝的 CLI,每個 CLI 走四道關卡:先 tools/wire-cli.sh purge {cli} 備份後移除所有 hook(含非 jsc 的第三方項目,乾淨起跑才分得清後續失敗是誰的),再 tools/wire-cli.sh {cli} 接線(claude 由 hooks.json 自動接線,無需寫入),接著 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 錯誤只回報,不轉修正。
repair
接手 hooks-install 或 report-error.sh 留下的失敗:讀 ERROR_{HASH} 與相關檔案後,把診斷拆給安裝中的其他 AI agent CLI 當 sub agent,彙整建議修正、實作 hooks repo 的修補、同步 manifest,最後以 develop 為基底開 PR。只有在真的沒有可用 CLI 時,才退回主 agent 自己判讀。
環境變數
| 變數 | 用途 | 未設定時 |
|---|---|---|
JSC_HOME |
Hook 資料目錄 | 預設 ~/.jsc |
JSC_WIKI_REPO_ERROR |
ERROR_CONTENTS、ERROR_{HASH} 所在的 {owner}/{repo} |
退回 JSC_WIKI_REPO |
JSC_WIKI_REPO |
未逐類設定時的共用 wiki {owner}/{repo} |
tools/report-error.sh 安靜降級,不寫 wiki |
JSC_CLAUDE_SETTINGS_DIR |
tools/wire-cli.sh purge claude 要清 hooks 鍵的設定檔目錄。指向一份複製品就能完整測過刪鍵邏輯,不必拿使用者本人的設定檔當測試場 |
預設 ~/.claude |
JSC_VERSION_GUARD |
設 off 完全略過版本前置檢查(離線工作用) |
啟用檢查 |
JSC_VERSION_TTL |
遠端版本查詢的快取秒數 | 預設 600 |
JSC_WP_GATE |
設 off 完全略過工作包 PR 閘門(wp-check 一律放行) |
啟用閘門 |
JSC_RESTART_GATE |
設 off 完全略過部署後重啟閘門(restart-gate.sh 一律放行) |
啟用閘門 |
JSC_COMMENT_SCOPE |
設 off 完全略過註解範圍檢查(comment-scope.sh 三種模式都直接結束) |
啟用檢查 |
JSC_LANG_GUARD |
設 off 完全略過繁中與編碼檢查(lang-guard.sh 三種模式都直接結束) |
啟用檢查 |
JSC_CHANGED_FILE |
非 Claude CLI 要掃描的檔案路徑,代替 stdin JSON 的 file_path,供 comment-scope.sh 與 lang-guard.sh 使用 |
安靜降級,不掃描 |
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 |
非 Claude CLI 的目前模型,供 sdlc-gate.sh 比對;優先序在 transcript 實際值與 stdin model 之後 |
改讀 ~/.claude/settings.json,再不行就安靜降級 |