jiantw83 f586a6b69f docs(hooks): 同步四支 CLI 的接線位置與驗證等級
What:README.md、references/behaviors.md 與 skills/hooks-install/SKILL.md 同步這一批的接線事實。

Why:
- 文件寫著「這些 CLI 沒有 pre-tool hook」。那句話是錯的,也正是三支 CLI 長期沒有守門的原因。照著它報告,會讓使用者以為只有 claude 有保護、其餘四支本來就接不上。
- kiro 的 degraded 原本被寫成接線缺東西,實際上是那支 CLI 擋不下技能叫用。兩件事的處理方式完全不同。

How:
- 一支 CLI 一列,列出接線位置、事件與 matcher、阻擋形態與判定,並寫明 claude、codex、copilot、antigravity 回 wired,kiro 回 degraded。
- 新增驗證等級表,把「形狀」與「觸發」分開記:形狀是那支 CLI 真的讀得懂設定,觸發是 hook 真的被叫用過。copilot 的形狀未證、antigravity 與 codex 的觸發未驗證,一律照實列出,不含糊帶過。
- README 補上 hooks/skill-name.sh 與 hooks/deny.sh 兩列,並補 COPILOT_HOME、KIRO_HOME、JSC_ANTIGRAVITY_HOOKS、JSC_COPILOT_INSTRUCTIONS、JSC_ANTIGRAVITY_RULES 幾個環境變數。
- kiro 的接線位置從 .kiro/hooks/jsc-hooks.json 全面改寫成 ~/.kiro/agents/jsc.json。
- 講明 write-guard.sh 三個模式與 SDLC 模型鎖在另外三支上是「還沒接」,不是「沒有 hook 可接」。
- behaviors.md 的完成條件與可驗證跡象跟著改,repair 那一列加上「技能名解析改 skill-name.sh、阻擋形態改 deny.sh,兩支是唯一真實來源」。

Who:codex、copilot、antigravity、kiro 四支 CLI 的 pre-tool hook 接線修正。
2026-08-31 19:05:48 +08:00

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/skill-name.sh 不直接接線,由 version-guard.sh 與 restart-gate.sh 呼叫 從各 CLI 的 hook 負載解析出這一次要用哪一支 jsc 技能,一支 CLI 一個子命令,印一行「{domain}{技能名}」,解析不出來就印空字串。取值來源:claude 讀 stdin JSON 的 skill 欄位、codex 讀 tool_input.command 裡那條 SKILL.md 路徑(Codex 沒有 Skill 工具,技能是模型自己用 Bash 讀 SKILL.md 載入的)、copilot 讀 toolArgs(字串化的 JSON,要先剝一層跳脫)、antigravity 讀 toolCall.args.AbsolutePath 另收提示字串(斜線指令不產生工具呼叫)、kiro 讀 prompt 開頭那個斜線指令;五支都先看環境變數 JSC_SKILL、SKILL。永遠 exit 0:閘門那一端一律 fail-open,而且 copilot 的 command hook 是 fail-closed 的,回非零等於拒絕。規則只有這一份,兩支閘門都不重寫第二套
hooks/deny.sh 不直接接線,由 version-guard.sh 與 restart-gate.sh 呼叫 產出各 CLI 認得的阻擋輸出,訊息從參數或標準輸入進。claude、codex、copilot 訊息寫 stderr 並回 exit 2;antigravity 印 stdout 的 {"decision":"deny","reason":"..."} 並固定回 0——那支 CLI 的結束碼語意兩邊文件都沒寫,靠結束碼會變成「判定擋下、CLI 照樣放行」的無聲失效,所以 stdout 只准有那一行;kiro 擋不下技能叫用,改印警告後回 0;認不得的代號走 stderr 加 2 這個保守預設
hooks/version-guard.sh PreToolUse:claude matcher Skill、codex matcher Bash、copilot matcher skill、antigravity matcher ^view_file$ 加 PreInvocation;kiro userPromptSubmit(只注入警告) 技能使用前的版本前置檢查,擋兩種情況,兩種都擋下該次呼叫並提示更新指令(更新指令依當前 CLI 給;技能名解析交給 hooks/skill-name.sh、阻擋輸出形態交給 hooks/deny.sh,兩支的規則見上面兩列,這裡不重寫第二套):一是本機實際載入版本落後遠端發佈版本,二是技能所屬 plugin 的 manifest 在 jsc.requires 宣告的相依 plugin 版本落後——相依那一項讀 installPath 底下那份 plugin.json,逐項比對相依 plugin 的本機實際載入版本,訊息講明哪一個 plugin、需要哪一版、目前哪一版、怎麼補。相依檢查排在遠端比對之前,全部讀本機檔案,離線也判得動;判定邏輯自己實作,不呼叫 jsc-cli/tools/check-requires.sh,免得 hook 散落到別的 domain,也免得跟已宣告相依 jsc-hooks 的 jsc-cli 做出循環相依。部署那端照樣更新、只回報,阻擋落在這支 hook。兩種都只擋確定落後:超前放行(開發技能組時本機本來就會超前),讀不到本機版本、推導不出站台、查不到遠端版本、解不出安裝路徑、讀不到 manifest、manifest 沒有 jsc.requires、讀不到相依 plugin 的本機載入版本也一律放行。遠端版本快取在 $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——共 7 項,兩種擋人情況共用同一份,相依落後不另立短清單;清單的唯一來源是 hooks/version-guard.sh 的檔頭,那裡一項一個理由
hooks/restart-gate.sh PreToolUse:claude matcher Skill、codex matcher Bash、copilot matcher skill、antigravity matcher ^view_file$ 加 PreInvocation;kiro userPromptSubmit(只注入警告) 部署後強制重啟閘門:$JSC_HOME/restart-required.d/{CLI 代號} 一支 CLI 一份,當前 CLI 那份存在時擋下 jsc 技能呼叫,並印出要重新啟動哪一支 CLI(技能名解析交給 hooks/skill-name.sh、阻擋輸出形態交給 hooks/deny.sh,兩支的規則見上面兩列);別支 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/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 階段技能呼叫,從可驗證來源讀出模型 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 上;codex、copilot、antigravity 三支已經有可用的 pre-tool hook(見上面「各 CLI 的 pre-tool 接線位置」),只是這三種模式還沒接過去,kiro 則是本來就擋不下來。另有一個不接 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 快取目錄,也不指向開發存取庫。

各 CLI 的 pre-tool 接線位置

五支裡有四支都有能阻擋的 pre-tool hook。先前版本前置檢查與部署後重啟閘門在 codex、copilot、antigravity 上從未生效,原因是接錯位置——不是沒有位置可接。

CLI 接線位置 事件與 matcher 阻擋形態 verdict
claude hooks/hooks.json PreToolUse matcher Skill stderr 加 exit 2 wired
codex hooks/codex-hooks.json,由 .codex-plugin/plugin.json 的 hooks 鍵以路徑字串指過去 PreToolUse matcher Bash stderr 加 exit 2 wired
copilot ~/.copilot/settings.json 的頂層 hooks 鍵(合併,不覆寫) PreToolUse matcher skill stderr 加 exit 2 wired
antigravity ~/.gemini/config/hooks.json 的 jsc 段落 PreToolUse matcher ^view_file$(Grouped:matcher 加 hooks 包一層),加 PreInvocation(Flat) stdout 的 {"decision":"deny",...} wired
kiro ~/.kiro/agents/jsc.json 的 hooks 鍵,加上 settings/cli.json 的 chat.defaultAgent=jsc agentSpawn、userPromptSubmit、stop stdout 注入警告,擋不下來 degraded

四支非 claude 的 CLI,接線命令一律以 JSC_CLI={代號} 前綴自帶 CLI 代號。兩道閘門要先認出自己跑在哪一支上,才取得到 hooks/skill-name.sh 的技能名與 hooks/deny.sh 的阻擋形態;代號取不到時技能名解不出來,兩道閘門一律安靜放行,設定寫得完全正確、matcher 也對,卻一次都擋不下來。antigravity 更嚴重:代號不明時阻擋會退回結束碼形態,而它只認 stdout 的 deny JSON,等於判定擋下了、CLI 卻收不到拒絕。tools/jsc-wrap.sh 的別名雖然也會 export JSC_CLI,那只在使用者從互動 shell 走別名啟動時才成立,接線不靠它。

各列的原因,逐支講白:

  • codex 沒有 Skill 這個工具,技能是模型自己用 Bash 讀 SKILL.md 載入的,所以 matcher 是 Bash。manifest 的 hooks 鍵跟 skills 一樣是路徑字串(Codex 的 plugin manifest 規格:"hooks": "./hooks.json"),寫成內嵌物件解不出來。那個鍵是覆寫,codex 只讀它指到的那一份,所以 hooks/codex-hooks.json 是從 hooks/hooks.json 推導出來的——整份複製,只把 "matcher": "Skill" 換成 "matcher": "Bash"。手寫第二份會漏掉 SessionStart、UserPromptSubmit、Stop 那幾組,而且從此兩份各自漂移;推導的話 hooks/hooks.json 仍是唯一真實來源,那邊加一支 hook,這邊重跑接線就跟著有。Claude 讀的還是原本那份,一個位元組都沒動。

  • copilot 有專用的 skill 工具,matcher 就是小寫的 skill。事件名只寫 PascalCase 一種:兩種大小寫都吃,兩種同時存在會把同一支 hook 跑兩次。它的 command hook 是 fail-closed 的(崩潰或任何非零結束碼都算拒絕,只有逾時 fail-open),所以那條路徑上的腳本錯誤處理要收乾淨。設定位置是 settings.json 的頂層 hooks 鍵,內嵌定義、以事件名當鍵(copilot help config 原文:「In global config.json these act as user-level hooks」,而 config.json 第一行自己就寫著 // User settings belong in settings.json.)。$COPILOT_HOME/hooks/ 底下放的是 hook 要跑的腳本,不是設定——把設定寫進那裡,檔案好端端在、內容也對,copilot 一次都不會讀。指引檔同理要在 $COPILOT_HOME 底下,舊接線寫在 ~/.config/copilot/,那個位置從來不會被載入。

    那份 settings.json 同時裝著 enabledPlugins(十個 jsc plugin 的啟用狀態)與 extraKnownMarketplaces,弄壞會讓外掛整批失效,所以只合併不覆寫:寫前備份到 $JSC_HOME/backup/,只動 hooks 底下 jsc 自己那幾筆條目,寫後回讀核對最上層鍵與別人的 hook 條目,任何一項對不上就還原備份。purge 也只挑掉 jsc 那幾筆,第三方的 SessionStart 原樣留著。

  • antigravity 沒有技能專用工具,系統提示要求模型用 view_file 讀 SKILL.md,所以 matcher 是 ^view_file$;錨點不能省,省了會連 view_file_outline 一起命中。兩個事件的結構不一樣,不能寫成同一種形狀:PreToolUse 與 PostToolUse 是 Grouped(handler 要用 matcher 加 hooks 包一層),PreInvocation、PostInvocation、Stop 是 Flat(handler 物件直接排在陣列裡)。這是執行檔內嵌文件的「Supported Event Types」表寫死的,也是實測踩出來的——PreToolUse 寫成 Flat 時 antigravity 靜默丟棄整個事件,hook 名稱照樣登記,連 actions 鍵都不生成,不報任何錯,設定檔看起來也完全正常。matcher 要留在 group 那一層,不是 handler 那一層。plugin.json 不能宣告 hook,只有 hooks.json 這一個位置,而那個檔案的最上層是一個安裝來源一個命名空間鍵,寫 jsc 那一個不會動到別人的段落。斜線指令與預載技能會把 SKILL.md 全文直接注入訊息、不產生工具呼叫,那條路由 PreInvocation 接住。結束碼絕對不可靠:語意兩邊文件都沒寫,擋人一律靠 stdout 的 deny JSON。

  • kiro 的 hook 宣告只認 agent 設定檔的 hooks 鍵,合法事件只有 agentSpawn、userPromptSubmit、preToolUse、postToolUse、stop 五個,欄位是 command(必填)、matcher、timeout_ms 等,沒有 on、run、env。舊版把 on/run/env 寫在最上層,kiro-cli agent validate 一個錯都不報——未知的頂層鍵被靜默忽略——那份檔案卻什麼都沒做。檔案合法不等於接線生效,所以形狀要另外驗。

    kiro-cli agent validate 一律回結束碼 0,合法、事件名非法、hooks 裡放 on/run、缺 command,四種情況的結束碼全是 0,錯誤只印在輸出(stderr)。拿結束碼當判準會做出一支永遠通過的檢查,跟這一輪在修的錯是同一類。判準是輸出:空的才算通過。輸出在講別的事(沒登入、憑證過期)算「驗不了」不是「驗不過」,照 fail-open 放行並據實說明——報成接線失敗的話,沒登入的機器會整批接不了線。

  • kiro 擋不下技能叫用,這是 CLI 的限制,不是我們接錯。技能走 ResolveSkill 這個 agent 內部請求,不經工具管線,preToolUse 攔不到;userPromptSubmit 的非零結束碼也不會擋下那一輪。唯一可用的介入是 userPromptSubmit 的 stdout 注入,所以兩道閘門只印警告。hook 宣告只認 agent 設定檔的 hooks 鍵,.kiro/hooks/ 目錄不在它的設定目錄常數裡,一份都不會被讀。同一份 agent 檔還要寫 resources 的兩層 skill:// glob(預設只掃一層,jsc 的技能在 jsc-{domain}/{name}/SKILL.md 第二層,少了那一條一支都載不到)與明列的 tools(自訂 agent 沒宣告時可用工具會受限),並把 chat.defaultAgent 設成 jsc,那個 agent 才會被選用。

覆蓋範圍其餘部分仍要據實看待:只有 claude 同時有 PreToolUse、PostToolUse 與 UserPromptSubmit,九支 hook 全接得上。codex、copilot、antigravity 三支目前接上的是版本前置檢查與部署後重啟閘門兩道;write-guard.sh 的三種模式還沒接線,SDLC 模型鎖仍只剩技能步驟檢查,註解範圍與繁中編碼仍是 sweep。codex 另外沒有工作階段開始事件,計時改由 tools/jsc-wrap.sh 的 codex 別名在啟動當下開始;沒走別名啟動時,時間從第一輪回應算起。

驗證等級

「形狀」是那支 CLI 真的讀得懂這份設定;「觸發」是 hook 真的被叫用過。兩件事分開記,不得混為一談,回報也照這張表寫:

CLI 形狀 觸發
claude 實證(hooks.json 長期在用) 實證
codex 實證:~/.codex/config.toml 的 [hooks.state] 以 {事件}:{群組}:{條目} 兩層索引登記,證明它解析的是 Grouped 結構;manifest 的 hooks 是路徑字串,出自執行檔內嵌的 plugin-json-spec.md 未驗證
antigravity 實證:接線後 agy -p "/hooks" 四條全載入,matcher=^view_file$,第三方段落完好 未驗證(本機對話 quota 用盡)
copilot 未證:settings.json 沒有唯讀的列出管道,位置與條目形態出自 copilot help config 的說明與機器上既有的第三方實例 未驗證
kiro 實證:kiro-cli agent validate 通過(輸出為空),並以反證確認它真的在判別——sessionStart、hooks 裡放 on/run、缺 command 三種都會報錯 部分實證:agentSpawn 與 userPromptSubmit 實跑觸發過;preToolUse 與 stop 未驗證(模型額度用盡,09/01 重置)

還有兩項未驗證,一併記著:kiro 的 resources 兩層 glob 能不能真的修好技能可見性沒有驗過(要模型跑得動才列得出技能);四支非 claude 的 CLI 上,write-guard.sh 三種模式與 SDLC 模型鎖仍未接線,那是還沒做,不是驗不過。

接線內容與腳本邏輯有 wire-cli.sh smoke 逐條斷言(技能名解析、四種阻擋形態、各 CLI 的接線形狀、fail-open、豁免放行、kiro 的注入路徑),觸發不在斷言範圍內。antigravity 的唯讀確認可跑 agy -p "/hooks" 與 agy -p "/skills",兩個指令都不吃 quota;kiro 用 kiro-cli agent validate --path {檔案},看輸出不看結束碼。

comment-scope.sh 與 lang-guard.sh 五個 CLI 都掃得到,接的是同一批位置,但時機不同,不能當成五支一樣:

CLI 掃描時機 接在哪裡
claude 逐檔即時,寫完哪個檔就掃哪個 PostToolUse
codex 每輪結束,掃整個 git 工作區 config.toml 的根層 notify
kiro 每輪提示送出時,掃整個 git 工作區(掃到的是上一輪寫的檔) ~/.kiro/agents/jsc.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,但快取依 CLI 分開,一次部署不會為同一支 CLI 的每個 domain 重複打一輪網路。 jsc-cli:deploy 用它決定要不要把「更新」設成推薦選項。

version-guard.sh recommend 是同一份比對的結論版,只印一行 recommend<TAB>update|none|unverifiable, 永遠 exit 0。判定規則寫在腳本檔頭:任一 plugin 落後就 update;查不到本機註冊檔、 一列 domain 都沒有、或每一列都查詢失敗都是 unverifiable;其餘是 none。查詢失敗那幾列 不計入——查不到不等於最新。證據表要另外看就再呼叫一次 report,兩者刻意不混印, 呼叫端取第二欄的解析才不會被表格內容打亂。

狀態檔盤點

同一台主機上的不同 CLI 不共用檢查紀錄。真正與 CLI 無關的資料才共用。

路徑 CLI 維度 設計判定 理由
$JSC_HOME/version-cache/{CLI 代號}/{domain} 有 正確 版本檢查由當前 CLI 觸發;不同 CLI 的安裝來源與載入版本可能不同,所以快取分開。舊路徑 $JSC_HOME/version-cache/{domain} 只作第一次相容讀取。
$JSC_HOME/errors/scan-state/{CLI 代號}-*.offset 有 正確 原生日誌位置與格式依 CLI 不同,掃描位移不能共用。
$JSC_HOME/usage/scan-state/{CLI 代號}-*.offset 有 正確 離線回填逐 CLI 掃不同日誌,位移檔以 CLI 前綴隔離。
$JSC_HOME/usage/skills.jsonl、$JSC_HOME/usage/chains.jsonl 每筆有 cli 欄位 正確 統計要能跨 CLI 彙整,也要能用欄位篩選。
$JSC_HOME/sessions/ 以 session id 分 正確 工作階段 id 由 CLI 或包裝器提供,實質上分離;同 id 才代表同一工作階段。
$JSC_HOME/restart-required.d/{CLI 代號} 有 正確 重啟只清當前 CLI,那一支沒有重啟就不能被另一支解除。
$JSC_HOME/wp/ 無 正確 工作包 PR 狀態屬於存取庫與 PR,不屬於 CLI;換 CLI 也要看到同一支未結清 PR。
$JSC_HOME/model-tags.tsv、$JSC_HOME/models.conf、$JSC_HOME/html-styles.conf 無 正確 這些是全機共用設定,不是檢查紀錄。

工具

腳本 用途
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-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 必須是根層鍵、.codex-plugin/plugin.json 的 matcher 必須是 Bash、copilot 必須是小寫 skill 且沒有第二種大小寫的事件名、antigravity 的 matcher 必須帶錨點 ^view_file$ 且有 PreInvocation、kiro 的 agent JSON 必須成對且 hooks、resources、tools 在最上層並含兩層 skill:// glob),也會確認寫入路徑能解到既有腳本。matcher 本身要單獨驗:鍵在、matcher 卻錯的形態最難查,回報會說接好了,實際一次都不會被叫用。檔案系統不能建立 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 的每個接線模式各跑一次,非零退出即為錯誤,另外把五支 CLI 的真實負載各餵進 skill-name.sh 一次驗技能名解析、四種阻擋形態各驗一次 deny.sh,再把那些負載直接餵進 restart-gate.sh 驗「解析→判定→輸出形態」整條串得起來(含 fail-open、豁免放行與 kiro 的注入路徑)——前兩組分開看都會顯示正常,中間接不上照樣是全程放行,那正是先前三支 CLI 失效的樣子;另外用一份暫時的 $JSC_HOME 狀態檔把工作包歸屬、部署後重啟閘門與寫入提交閘門的每條判定路徑各跑一次並比對結束碼,再用一份暫時的 HOME(假的 installed_plugins.json 與各 plugin 的 manifest)把 version-guard.sh 相依版本檢查的每條路徑跑一次——相依落後的擋人與訊息內容、相等與超前的放行、豁免技能在相依落後時照樣放行、四種 fail-open、逃生門,另加一條回歸:多行縮排的 manifest,jsc.requires 的最後一個鍵也要解得到。驗的是判定結果本身,不只是腳本跑得完(例外有四個: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} 是唯讀盤點:只讀設定檔判斷段落與 matcher 對不對,不寫檔也不執行 hook,claude、codex、copilot、antigravity 回 wired,kiro 回 degraded 並在 reason 講明那是 CLI 限制;每個接線點印一行 item<TAB>{項目}<TAB>{路徑}<TAB>{present|missing|unverified},也會把帶版號快取路徑、開發存取庫路徑與不存在的腳本列為缺項。狀態有三格不是兩格:unverified 是「這一項驗不了」,只有 missing 才算缺項——kiro-cli agent validate 在沒登入時印的是環境問題,不是這個檔案的問題,報 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/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 自動接線,無需寫入;其他 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 寫入。版本前置檢查與部署後重啟閘門在 codex、copilot、antigravity 三支都擋得下來,各自接在自己的 pre-tool 位置(見上面「各 CLI 的 pre-tool 接線位置」),三支回報 wired;kiro 擋不下技能叫用,只注入警告,回報 degraded,那是 CLI 的限制。write-guard.sh 的三種模式目前仍只接在 claude。這四個 CLI 都沒有 post-tool hook,comment-scope.sh 接不到逐檔即時掃描,改用 sweep 掃整個 git 工作區——codex 每輪結束、kiro 每輪提示送出時、copilot 與 antigravity 只有工作階段結束時掃一次,腳本會在 reason 裡講明各自的時機,也只有 claude 掃得到執行期錯誤紀錄。antigravity 與 kiro 的 hook 觸發都沒有實跑驗證(前者 quota 用盡、後者未登入),回報時要把「接線已驗」與「觸發未驗」分開講。任一關卡出錯(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
COPILOT_HOME copilot 的設定根目錄,hooks/jsc-hooks.json 與指引檔都寫在它底下 預設 ~/.copilot
KIRO_HOME kiro 的設定根目錄,agents/jsc.json、settings/cli.json 與 resources 的 skill:// glob 都由它推導 預設 ~/.kiro
JSC_ANTIGRAVITY_HOOKS antigravity 的 hook 設定檔,tools/wire-cli.sh 只寫它的 jsc 段落 預設 ~/.gemini/config/hooks.json
JSC_COPILOT_INSTRUCTIONS copilot 指引檔位置。舊值 ~/.config/copilot/copilot-instructions.md 從來不會被載入,不要再指回去 預設 $COPILOT_HOME/copilot-instructions.md
JSC_ANTIGRAVITY_RULES antigravity 全域規則檔位置 預設 ~/.antigravity/AGENTS.md
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_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_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、技能名與 tool_name(hooks/skill-name.sh 也收沒有前綴的 SKILL,而且環境變數蓋過負載解析;write-guard.sh 也收 TOOL_NAME)。version-guard.sh 與 restart-gate.sh 已經不篩工具名——五支 CLI 的工具名各不相同(Skill、Bash、skill、view_file),拿 Claude 那一個當通用條件會把另外四支整批擋在判定之外 安靜降級:JSC_CLI 取不到就當查不到 CLI,技能名取不到就由負載解析,兩邊都空就放行
JSC_MODEL sdlc-gate.sh 找不到 transcript、hook stdin JSON 與 Codex 本機 session 記錄時的人工覆寫模型 id;回報會標明 人工覆寫:JSC_MODEL 找不到可驗證模型來源時拒絕 lock,並列出已檢查來源與修復建議

相關 domain

  • jsc-cli:CLI 偵測(tools/detect-clis.sh)
  • jsc-log:讀取本 domain 產出的工時與用量資料
S
Description
No description provided
Readme
2.4 MiB
Languages
Shell 100%