From 0bebdef46f211ca6123c47d77ec88d73b28303f8 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 27 Aug 2026 09:16:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(wire-cli):=20=E6=8A=8A=20comment-scope=20s?= =?UTF-8?q?weep=20=E6=8E=A5=E5=88=B0=E5=85=B6=E9=A4=98=E5=9B=9B=E5=80=8B?= =?UTF-8?q?=20CLI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:`tools/wire-cli.sh` 把 `comment-scope.sh sweep` 接進 codex 的 `config.toml` 根層 `notify` 與 kiro 的 `.kiro/hooks/jsc-hooks.json` 的 `userPromptSubmit`;`tools/jsc-wrap.sh` 在 CLI 結束後、回傳結束碼之前跑一次 sweep,涵蓋 copilot、antigravity 與同樣走包裝器的 codex。`smoke` 多跑一輪 sweep,`status` 多盤點 codex 的 `notify-sweep` 與 kiro 的 `comment-scope-sweep` 兩項。 Why:這四個 CLI 沒有 post-tool hook,接不到逐檔即時掃描,先前只寫得進規則提示,違規註解寫進去沒有人叫。接上 sweep 之後五個 CLI 都掃得到,差別只剩回饋速度。`smoke` 與 `status` 不同步補上,就驗不出來接線少了哪一支。 How:codex 的 notify 串成 `start; mark; sweep`,位置仍由 `replace_block_toml` 保證落在第一個表頭之前,寫入後除了既有的根層鍵檢查,再 grep 一次確認 sweep 真的串進那一行。kiro 的 `run` 尾端接上 sweep,`prompt` 與 `sweep` 各驗一個 grep,只驗腳本名會漏掉少接的那一個。`jsc-wrap.sh` 的 sweep 加 `|| true` 接住 exit 2,包裝器一律原樣回傳 CLI 自己的結束碼——包裝器改掉結束碼,呼叫端的 `cmd && next` 就會誤判。`smoke` 的 exit 2 白名單由「無參數模式」放寬到「所有掃描模式」,因為 sweep 在髒工作區本來就會回 2,那是 hook 正常工作,不是 hook 壞掉。四個 CLI 的 `reason` 與 `[jsc]` 說明改寫成各自真正的掃描時機。 Who:`jsc-hooks` 的接線工具層,供 `jsc-hooks:hooks-install` 與 `jsc-hooks:repair` 呼叫,最終服務 codex、copilot、antigravity、kiro 的使用者。 --- tools/jsc-wrap.sh | 9 +++- tools/wire-cli.sh | 116 ++++++++++++++++++++++++++++++---------------- 2 files changed, 85 insertions(+), 40 deletions(-) diff --git a/tools/jsc-wrap.sh b/tools/jsc-wrap.sh index cc3adf6..74290a9 100755 --- a/tools/jsc-wrap.sh +++ b/tools/jsc-wrap.sh @@ -2,7 +2,8 @@ # jsc-wrap.sh — 無 hook 系統 CLI 的包裝啟動器(例:copilot、antigravity)。 # 用法: jsc-wrap.sh {cli} [args...] # 行為: 匯出 JSC_CLI 與 JSC_SESSION_ID → session-timer start → 執行 CLI → -# 結束後 session-timer mark 並以 scan-logs.sh 回填用量,最後回傳 CLI 的結束碼。 +# 結束後 session-timer mark、以 scan-logs.sh 回填用量、以 comment-scope.sh sweep +# 掃一次整個工作區的註解範圍,最後回傳 CLI 的結束碼。 # 注意: JSC_CLI 存的是 CLI 代號(antigravity、kiro),實際執行的是 cli_bin 對應的 # 執行檔(agy、kiro-cli)。直接拿代號當指令跑會 127,因為沒有這兩個執行檔。 HERE=$(cd "$(dirname "$0")" && pwd) @@ -25,4 +26,10 @@ rc=$? # 收尾:補記結束時間,並從原生日誌回填技能用量 sh "$HOOKS/session-timer.sh" mark > "$smoke_out" - elif [ "$_h" = comment-scope.sh ] && [ -z "$_s" ] && [ "$_rc" -eq 2 ]; then - # 同一類放行:comment-scope.sh 無參數模式的 exit 2 是「掃到違規註解」的設計行為。 - # 冒煙時取不到檔名,正常會走 exit 0;真的走到 exit 2 也代表腳本判定跑完了,不是出錯。 - printf '[jsc] %s:exit 2,掃到違規註解並發出警告,屬設計行為,不算錯誤。\n' "$_h" >> "$smoke_out" + elif [ "$_h" = comment-scope.sh ] && [ "$_rc" -eq 2 ]; then + # 同一類放行:comment-scope.sh 掃描模式的 exit 2 是「掃到違規註解」的設計行為。 + # 無參數模式冒煙時取不到檔名,正常會走 exit 0;sweep 則看工作區乾不乾淨——工作區剛好 + # 有違規註解就回 2。那是 hook 正常工作,不是 hook 壞掉,不能因此判定接線失敗。 + printf '[jsc] %s%s:exit 2,掃到違規註解並發出警告,屬設計行為,不算錯誤。\n' "$_h" "${_s:+ $_s}" >> "$smoke_out" else smoke_fails=$((smoke_fails + 1)) printf '[jsc] %s%s:exit %s,執行期出錯:%s\n' "$_h" "${_s:+ $_s}" "$_rc" \ @@ -639,10 +648,13 @@ if [ "$action" = smoke ]; then # wp-check skill 在取不到技能名時放行(上面已清空技能名),所以兩者都不需要白名單例外。 smoke_one sdlc-gate.sh "wp-check prompt" smoke_one sdlc-gate.sh "wp-check skill" - # comment-scope.sh 也有兩個接在不同事件的模式,兩個都要驗:prompt 一律 exit 0, - # 無參數模式在取不到檔名時安靜 exit 0(上面已清空 JSC_CHANGED_FILE,stdin 也只有 {})。 + # comment-scope.sh 有三個接在不同事件的模式,三個都要驗:prompt 一律 exit 0, + # 無參數模式在取不到檔名時安靜 exit 0(上面已清空 JSC_CHANGED_FILE,stdin 也只有 {}), + # sweep 掃目前工作目錄所在的 git 工作區——乾淨或非 git 目錄回 0,有違規註解回 2, + # 後者由上面的白名單放行(見 smoke_one)。 smoke_one comment-scope.sh prompt smoke_one comment-scope.sh + smoke_one comment-scope.sh sweep if [ "$smoke_fails" -eq 0 ]; then printf 'status=ok reason=%s\n' "六支 hook 的每個接線模式都跑得完,沒有執行期錯誤" @@ -710,22 +722,29 @@ if [ "$action" = status ]; then else st_item notify-root "$config" missing fi + # notify 接上了,不代表 sweep 也串進那一行:舊版接線只有計時,掃描是後來才加的 + if [ -f "$config" ] && grep -qF 'comment-scope.sh' "$config" 2>/dev/null && + grep -qF 'sweep' "$config" 2>/dev/null; then + st_item notify-sweep "$config" present + else + st_item notify-sweep "$config" missing + fi st_rc_alias alias jsc-hooks:codex st_block ste100 "${CODEX_HOME:-$HOME/.codex}/AGENTS.md" "" st_comment_scope comment-scope "${CODEX_HOME:-$HOME/.codex}/AGENTS.md" - st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" ;; + st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍改為每輪結束掃整個工作區,不是逐檔即時" ;; copilot) st_rc_alias alias jsc-hooks:copilot st_block ste100 "${JSC_COPILOT_INSTRUCTIONS:-$HOME/.config/copilot/copilot-instructions.md}" "" st_comment_scope comment-scope "${JSC_COPILOT_INSTRUCTIONS:-$HOME/.config/copilot/copilot-instructions.md}" - st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" ;; + st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只在工作階段結束時掃一次整個工作區" ;; antigravity) st_rc_alias alias jsc-hooks:antigravity st_block ste100 "${JSC_ANTIGRAVITY_RULES:-$HOME/.antigravity/AGENTS.md}" "" st_comment_scope comment-scope "${JSC_ANTIGRAVITY_RULES:-$HOME/.antigravity/AGENTS.md}" - st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" ;; + st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只在工作階段結束時掃一次整個工作區" ;; kiro) # kiro 的 hook 檔綁在工作區,這裡看的一律是目前工作目錄底下那一份 @@ -733,12 +752,17 @@ if [ "$action" = status ]; then if [ -f "$_f" ] && json_top_key "$_f" run; then st_item "$(basename "$_f" .json)" "$_f" present else st_item "$(basename "$_f" .json)" "$_f" missing; fi done - # userPromptSubmit 那一筆的 run 串了兩支腳本,只驗 JSON 讀得懂會漏掉少接的那一支 + # userPromptSubmit 那一筆的 run 串了好幾支腳本,只驗 JSON 讀得懂會漏掉少接的那一支。 + # comment-scope.sh 的 prompt 與 sweep 是兩件事,各算一項,才看得出舊版接線少了哪一個。 if [ -f ./.kiro/hooks/jsc-hooks.json ] && - grep -qF 'comment-scope.sh' ./.kiro/hooks/jsc-hooks.json 2>/dev/null + grep -qF 'comment-scope.sh\" prompt' ./.kiro/hooks/jsc-hooks.json 2>/dev/null then st_item comment-scope ./.kiro/hooks/jsc-hooks.json present else st_item comment-scope ./.kiro/hooks/jsc-hooks.json missing; fi - st_degrade="SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" ;; + if [ -f ./.kiro/hooks/jsc-hooks.json ] && + grep -qF 'comment-scope.sh\" sweep' ./.kiro/hooks/jsc-hooks.json 2>/dev/null + then st_item comment-scope-sweep ./.kiro/hooks/jsc-hooks.json present + else st_item comment-scope-sweep ./.kiro/hooks/jsc-hooks.json missing; fi + st_degrade="SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍改為每輪提示送出時掃整個工作區,不是逐檔即時" ;; esac if [ "$st_missing" -gt 0 ]; then @@ -762,7 +786,7 @@ case "$cli" in || fail "$HOOKS/hooks.json 沒有接上 comment-scope.sh,註解範圍檢查不會生效" printf 'status=wired reason=%s\n' "hooks.json 自動接線" echo "[jsc] claude:由 hooks/hooks.json 自動接線全部六支 hook,無需寫入設定。" - echo "[jsc] claude:只有 claude 有 post-tool hook,comment-scope.sh 的寫檔後自動掃描只在這裡接得上。" + echo "[jsc] claude:只有 claude 有 post-tool hook,comment-scope.sh 的逐檔即時掃描只在這裡接得上;其他四個 CLI 改用 sweep 掃整個工作區,時機晚一輪或晚到工作階段結束。" exit 0 ;; codex) @@ -776,27 +800,33 @@ case "$cli" in # 一個 JSC_SESSION_ID,codex 內觸發的 notify 會沿用同一個 id。 # 2. notify 每輪先補 start 再 mark。start 已有紀錄就不動,所以沒走別名啟動時 # 仍拿得到起始時間(從第一輪算起)。少了這一段,worklog 只會拿到 0 秒。 + # notify 最後再掛 comment-scope.sh sweep:codex 沒有 post-tool hook,拿不到「剛剛寫了 + # 哪個檔」,只能改掃整個 git 工作區的 diff。每輪結束掃一次,時機比 claude 晚,那一輪 + # 寫過的檔一個都不會漏。 timer="sh '$HOOKS/session-timer.sh'" - notify_line="notify = [\"env\", \"JSC_CLI=codex\", \"sh\", \"-c\", \"$timer start /dev/null \ + || fail "$config 的 notify 沒有接到 comment-scope.sh sweep,codex 每輪結束不會掃註解範圍" write_alias_rc "jsc-hooks:codex" "$alias_line" || fail "無法把 $bin 別名寫進 shell rc 檔" replace_block "$agents" "" "" "$(rules_text)" \ || fail "無法寫入 $agents" has_block "$agents" "" || fail "$agents 寫入後讀不到 jsc-hooks 標記段落" has_comment_scope "$agents" || fail "$agents 寫入後讀不到註解範圍規則" - printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" + printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍改為每輪結束掃整個工作區,不是逐檔即時" echo "[jsc] codex:已在 shell rc 加上 $bin 別名,轉呼叫 tools/jsc-wrap.sh codex,啟動當下開始計時。" echo "[jsc] codex:別名要開新的 shell 或重新 source rc 檔才生效。" - echo "[jsc] codex:已設定 $config 的 notify(根層鍵,已驗證),每輪補 session-timer.sh start 再 mark。" + echo "[jsc] codex:已設定 $config 的 notify(根層鍵,已驗證),每輪補 session-timer.sh start 再 mark,最後跑 comment-scope.sh sweep。" echo "[jsc] codex:已在 $agents 寫入 STE100 與註解範圍規則段落(prompt 降級)。" echo "[jsc] codex:SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 sdlc-gate.sh lock 寫入。" echo "[jsc] codex:版本前置檢查接不上(codex 沒有 pre-tool hook),改由 /jsc-cli:deploy 定期更新。" - echo "[jsc] codex:註解範圍檢查只接得到規則提示(codex 沒有 post-tool hook),寫檔後的自動掃描接不上,違規註解要靠 /jsc-review:code-review 補。" + echo "[jsc] codex:註解範圍除了規則提示,每輪結束會由 notify 掃一次整個 git 工作區(codex 沒有 post-tool hook,接不到逐檔即時掃描),回饋比 claude 晚一輪。" exit 1 ;; copilot) @@ -809,13 +839,13 @@ case "$cli" in || fail "無法寫入 $instr" has_block "$instr" "" || fail "$instr 寫入後讀不到 jsc-hooks 標記段落" has_comment_scope "$instr" || fail "$instr 寫入後讀不到註解範圍規則" - printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" + printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只在工作階段結束時掃一次整個工作區" echo "[jsc] copilot:已在 shell rc 加上 $bin 別名,轉呼叫 tools/jsc-wrap.sh copilot。" echo "[jsc] copilot:已在 $instr 寫入 STE100 與註解範圍規則段落(prompt 降級)。" echo "[jsc] copilot:別名要開新的 shell 或重新 source rc 檔才生效。" echo "[jsc] copilot:SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 sdlc-gate.sh lock 寫入。" echo "[jsc] copilot:版本前置檢查接不上(copilot 沒有 pre-tool hook),改由 /jsc-cli:deploy 定期更新。" - echo "[jsc] copilot:註解範圍檢查只接得到規則提示(copilot 沒有 post-tool hook),寫檔後的自動掃描接不上,違規註解要靠 /jsc-review:code-review 補。" + echo "[jsc] copilot:註解範圍除了規則提示,工作階段結束時由 jsc-wrap.sh 收尾掃一次整個 git 工作區(copilot 連逐輪事件都沒有),回饋要等到離開 CLI 才看得到。" exit 1 ;; antigravity) @@ -828,13 +858,13 @@ case "$cli" in || fail "無法寫入 $rules" has_block "$rules" "" || fail "$rules 寫入後讀不到 jsc-hooks 標記段落" has_comment_scope "$rules" || fail "$rules 寫入後讀不到註解範圍規則" - printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" + printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只在工作階段結束時掃一次整個工作區" echo "[jsc] antigravity:已在 shell rc 加上 $bin 別名,轉呼叫 tools/jsc-wrap.sh antigravity。" echo "[jsc] antigravity:已在 $rules 寫入 STE100 與註解範圍規則段落(prompt 降級)。" echo "[jsc] antigravity:別名要開新的 shell 或重新 source rc 檔才生效。" echo "[jsc] antigravity:SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 sdlc-gate.sh lock 寫入。" echo "[jsc] antigravity:版本前置檢查接不上(antigravity 沒有 pre-tool hook),改由 /jsc-cli:deploy 定期更新。" - echo "[jsc] antigravity:註解範圍檢查只接得到規則提示(antigravity 沒有 post-tool hook),寫檔後的自動掃描接不上,違規註解要靠 /jsc-review:code-review 補。" + echo "[jsc] antigravity:註解範圍除了規則提示,工作階段結束時由 jsc-wrap.sh 收尾掃一次整個 git 工作區(antigravity 連逐輪事件都沒有),回饋要等到離開 CLI 才看得到。" exit 1 ;; kiro) @@ -855,13 +885,16 @@ case "$cli" in "run": "sh \\"$HOOKS/session-timer.sh\\" restart "$hookfile" 2>/dev/null </dev/null || fail "$startfile 沒有接在 sessionStart" grep -qF '"userPromptSubmit"' "$hookfile" 2>/dev/null || fail "$hookfile 沒有接在 userPromptSubmit" grep -qF 'comment-scope.sh' "$hookfile" 2>/dev/null || fail "$hookfile 的 run 沒有接到 comment-scope.sh" - printf 'status=degraded reason=%s\n' "SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" + # 兩個模式接的是兩件事,只驗腳本名會漏掉少接的那一個,所以各驗一次 + grep -qF 'comment-scope.sh\" prompt' "$hookfile" 2>/dev/null \ + || fail "$hookfile 的 run 沒有接到 comment-scope.sh prompt,每輪不會注入註解範圍規則" + grep -qF 'comment-scope.sh\" sweep' "$hookfile" 2>/dev/null \ + || fail "$hookfile 的 run 沒有接到 comment-scope.sh sweep,kiro 每輪不會掃註解範圍" + printf 'status=degraded reason=%s\n' "SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍改為每輪提示送出時掃整個工作區,不是逐檔即時" echo "[jsc] kiro:已建立 $startfile(sessionStart 開始計時)與 $hookfile(JSC_CLI=kiro),兩份都已驗證。" echo "[jsc] kiro:SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 sdlc-gate.sh lock 寫入。" echo "[jsc] kiro:版本前置檢查接不上(kiro 沒有 pre-tool hook),改由 /jsc-cli:deploy 定期更新。" - echo "[jsc] kiro:註解範圍檢查只接得到規則提示(kiro 沒有 post-tool hook),寫檔後的自動掃描接不上,違規註解要靠 /jsc-review:code-review 補。" + echo "[jsc] kiro:註解範圍除了規則提示,每輪提示送出時會掃一次整個 git 工作區(kiro 沒有 post-tool hook),掃到的是上一輪寫的檔。" exit 1 ;; esac