From 0c83495ba8d212097df1a7bc2c0d8f871c3a630e Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 26 Aug 2026 18:39:49 +0800 Subject: [PATCH 01/16] =?UTF-8?q?fix(version-guard):=20=E6=8A=8A=20jsc-ask?= =?UTF-8?q?:ask=20=E8=88=87=20jsc-gitea:wiki=20=E5=8A=A0=E9=80=B2=E8=B1=81?= =?UTF-8?q?=E5=85=8D=E6=B8=85=E5=96=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:版本守門 hook 的豁免清單新增 jsc-ask:ask 與 jsc-gitea:wiki 兩支技能,並在檔頭註解補上理由。 Why:jsc-cli:deploy 要問使用者 install/update/uninstall,一定會呼叫 jsc-ask:ask;ask 問完又一定寫回 wiki 才算完成。這兩支只要版本落後就被擋下,訊息還指回 /jsc-cli:deploy,deploy 卡在問不出模式那一步,形成死鎖,跟直接擋 deploy 是同一種問題。 How:在 case "$skill" 的豁免比對加上 jsc-ask:ask 與 jsc-gitea:wiki 兩個樣式,維持原本 exit 0 的略過行為;檔頭豁免清單註解同步補上兩條說明,講清楚死鎖成因。 Who:hooks/version-guard.sh 的外掛版本守門功能,影響 jsc-cli:deploy 的部署流程。 --- hooks/version-guard.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hooks/version-guard.sh b/hooks/version-guard.sh index 48aa6a7..307ba07 100755 --- a/hooks/version-guard.sh +++ b/hooks/version-guard.sh @@ -25,6 +25,11 @@ # jsc-hooks:hooks-install 更新後要重新接線,擋了會讓更新做一半卡住 # jsc-cli:models SDLC 閘門依賴它產生 model-tags.tsv # jsc-meta:* 開發技能組本身的工具,擋了就修不了技能組 +# jsc-ask:ask deploy 問「install/update/uninstall」一定會呼叫它; +# 它本身落後版本被擋下,訊息又指回 /jsc-cli:deploy, +# deploy 卡在問不出模式那一步,跟直接擋 deploy 是同一種死鎖 +# jsc-gitea:wiki jsc-ask:ask 問完一定寫回 wiki 才算完成,理由同上一條, +# 擋在這一步一樣是 deploy 做不完 # # 逃生門:JSC_VERSION_GUARD=off 完全略過檢查(離線工作時用)。 # @@ -199,7 +204,7 @@ domain=${domain%%:*} # 豁免清單 case "$skill" in - jsc-cli:deploy|jsc-hooks:hooks-install|jsc-cli:models|jsc-meta:*) exit 0 ;; + jsc-cli:deploy|jsc-hooks:hooks-install|jsc-cli:models|jsc-meta:*|jsc-ask:ask|jsc-gitea:wiki) exit 0 ;; esac # 更新指令依實際 CLI 給。印別的 CLI 的指令等於沒給指令,使用者照著打只會失敗。 -- 2.53.0 From 961af918bf7084e36b90725681bec53d668abe3f Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 26 Aug 2026 18:40:00 +0800 Subject: [PATCH 02/16] =?UTF-8?q?chore(gitignore):=20=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E6=9C=AC=E6=A9=9F=E6=8E=A5=E7=B7=9A=E6=AA=94=E7=9B=AE=E9=8C=84?= =?UTF-8?q?=20.kiro/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:在 .gitignore 的暫存分類底下新增 .kiro/ 一行,讓該目錄維持不追蹤。 Why:.kiro/ 是 jsc-hooks:hooks-install 在本機產生的接線檔,內容含本機絕對路徑。這種檔案因人而異,提交進存取庫會讓別人拉到錯的路徑,也會每次接線就冒出雜訊變更。 How:於 .gitignore 加上獨立註解區塊與 .kiro/ 樣式,只忽略不刪檔;本機既有目錄原地保留,git status 不再列出。 Who:hooks 存取庫的版本控管設定,配合 jsc-hooks:hooks-install 的接線產出。 --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 710e336..1db0849 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ Thumbs.db # 暫存 *.tmp *.log + +# 本機接線檔(jsc-hooks:hooks-install 產生,內含本機絕對路徑) +.kiro/ -- 2.53.0 From f81ceaf145fbff6e36b82336cd92ac76ee4af41c Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 26 Aug 2026 19:00:46 +0800 Subject: [PATCH 03/16] =?UTF-8?q?feat(comment-scope):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=A8=BB=E8=A7=A3=E7=AF=84=E5=9C=8D=E6=AA=A2=E6=9F=A5=20hook?= =?UTF-8?q?=20=E4=B8=A6=E6=8E=A5=E9=80=B2=20claude=20=E7=9A=84=20hooks.jso?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:新增第六支 hook `hooks/comment-scope.sh`,並在 `hooks/hooks.json` 補上兩個接線點:UserPromptSubmit 走 `prompt` 模式、PostToolUse 的 `Write|Edit|MultiEdit` 走掃描模式。 Why:程式碼註解常被寫進工單編號、專案代號、負責人這類文件相關資訊,讓註解變成過期文件。過去只能靠 `/jsc-review:code-review` 事後抓,回饋太慢;把規則搬到寫檔當下,模型可以立刻修正。 How:`prompt` 模式印出規則摘要注入提示。無參數模式從 stdin JSON 取 `file_path`(或環境變數 `JSC_CHANGED_FILE`),只掃 `git diff HEAD` 的新增行,不翻舊帳;markdown、純文字、資料檔與二進位檔一律跳過。命中就把警告與最多三行證據送到 stderr 並以 exit 2 交回模型就地修正,不擋寫入。逃生門為 `JSC_COMMENT_SCOPE=off`。規則正文的唯一來源在 `jsc-review` 的 `references/comment-scope.md`,本存取庫不留副本。 Who:`jsc-hooks` 的 hook 層,服務 `jsc-hooks:hooks-install` 的接線流程與 `jsc-review:code-review` 的註解契約檢查。 --- hooks/comment-scope.sh | 102 +++++++++++++++++++++++++++++++++++++++++ hooks/hooks.json | 13 ++++++ 2 files changed, 115 insertions(+) create mode 100755 hooks/comment-scope.sh diff --git a/hooks/comment-scope.sh b/hooks/comment-scope.sh new file mode 100755 index 0000000..061b16c --- /dev/null +++ b/hooks/comment-scope.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env sh +# comment-scope.sh — 程式碼註解不得夾帶文件相關資訊(hook > prompt 的強制層)。 +# 規則正文的唯一來源:jsc-review 的 references/comment-scope.md。本腳本只實作可用樣式判定的項目; +# 專案代號、客戶名稱這類無法用樣式判定的,交給 jsc-review:code-review 第 2 組人工審查。 +# +# 用法: +# comment-scope.sh prompt # UserPromptSubmit:注入規則摘要 +# comment-scope.sh # PostToolUse:掃描剛寫入的檔案,命中就發警告 +# +# 輸入相容: +# Claude: PostToolUse 的 stdin JSON,取 tool_input.file_path。 +# 其他 CLI: 環境變數 JSC_CHANGED_FILE。 +# 兩者都取不到就安靜降級(exit 0)。 +# +# 掃描範圍:檔案在 git 工作區內就只掃 `git diff HEAD` 的新增行,不翻舊帳; +# 不在 git 內或檔案尚未追蹤才整檔掃描。 +# +# 結束碼:0=沒命中或資料不足;2=命中,訊息走 stderr 交回模型自行修正(不擋寫入,檔案已經寫好了)。 +# 逃生門:JSC_COMMENT_SCOPE=off。 +set -u + +. "$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/lib.sh" 2>/dev/null || true + +[ "${JSC_COMMENT_SCOPE:-on}" = "off" ] && exit 0 + +if [ "${1:-}" = "prompt" ]; then + echo "[jsc] 程式碼註解只寫「為什麼這樣寫」,不寫「這件事記在哪份文件」。禁止寫入:議題與 PR 編號、變更單編號、wiki 頁編號與網址、工作包編號、TDD 待辦編號、使用者故事與驗收條件與測試案例編號、規格章節與稽核項編號、commit hash 與分支名、版本號與 Sprint 與里程碑、人名與認領者與 @ 提及、工時估算、專案代號與客戶名稱、產生來源署名、外部文件連結。" + echo "[jsc] 註解可以寫:日期與時間戳、需求變更歷程、RFC 與 ISO 標準編號、CVE 編號、第三方套件 issue 連結、授權標頭與 SPDX 標記、@deprecated 與 @since 等語言原生標記。命中禁止項就把編號指向的內容搬進註解,再刪掉編號。規則正文見 jsc-review 的 references/comment-scope.md。" + exit 0 +fi + +read_stdin 2>/dev/null || STDIN_JSON="" +file=$(json_str file_path 2>/dev/null || true) +[ -n "$file" ] || file="${JSC_CHANGED_FILE:-}" +[ -n "$file" ] && [ -f "$file" ] || exit 0 + +# 非程式碼檔不受本規則限制:markdown、純文字、資料檔沒有「程式碼註解」。 +case "$file" in + *.md|*.markdown|*.txt|*.rst|*.json|*.csv|*.tsv|*.svg|*.lock|*.log|*COMMIT_EDITMSG) exit 0 ;; +esac +# 二進位檔跳過。只認 NUL 位元組——拿「非可列印字元」當判準會把所有含中文的檔案誤判成二進位。 +raw=$(head -c 1024 "$file" 2>/dev/null | wc -c) +txt=$(head -c 1024 "$file" 2>/dev/null | LC_ALL=C tr -d '\000' | wc -c) +[ "$raw" = "$txt" ] || exit 0 + +dir=$(dirname -- "$file") +if git -C "$dir" rev-parse --is-inside-work-tree >/dev/null 2>&1 && + git -C "$dir" ls-files --error-unmatch -- "$file" >/dev/null 2>&1; then + lines=$(git -C "$dir" diff HEAD -- "$file" 2>/dev/null | sed -n 's/^+[^+]/&/p' | cut -c2-) + [ -n "$lines" ] || exit 0 +else + lines=$(cat "$file" 2>/dev/null) +fi + +# 只留註解行:行首註解符號,或行中出現 // 與 # 的行尾註解。 +comments=$(printf '%s\n' "$lines" | grep -E '^[[:space:]]*(//|#|--|\*|/\*|` 標記段落,重跑等同先移除再重裝;`has_comment_scope()` 供 `status` 判斷標記段落在不在。`smoke` 把 `comment-scope.sh` 無參數模式的 exit 2 視為設計行為,與 `sdlc-gate.sh check` 同列例外。 Who:`jsc-hooks` 的接線工具層,供 `jsc-hooks:hooks-install` 與 `jsc-hooks:repair` 呼叫。 --- tools/wire-cli.sh | 124 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 93 insertions(+), 31 deletions(-) diff --git a/tools/wire-cli.sh b/tools/wire-cli.sh index 2e9d70d..b136293 100755 --- a/tools/wire-cli.sh +++ b/tools/wire-cli.sh @@ -3,7 +3,7 @@ # 用法: # wire-cli.sh {claude|codex|copilot|antigravity|kiro} 接線 # wire-cli.sh purge {claude|codex|copilot|antigravity|kiro} 備份後移除該 CLI 的所有 hook -# wire-cli.sh smoke {claude|codex|copilot|antigravity|kiro} 跑一輪五支 hook,驗執行期 +# wire-cli.sh smoke {claude|codex|copilot|antigravity|kiro} 跑一輪六支 hook,驗執行期 # wire-cli.sh status {claude|codex|copilot|antigravity|kiro} 唯讀盤點接線現況,不寫檔也不執行 hook # # purge 移除的是「所有 hook」,含非 jsc 的第三方項目。安裝一律先 purge 再接線:混著別人的 @@ -14,22 +14,25 @@ # 跑起來不出錯(缺 node、路徑錯、權限不足都只在真的執行時才現形)。 # # 接線行為(依 CLI 而定,皆為冪等:重跑只取代既有的 jsc-hooks 標記段落,不會重複疊加): -# claude — 什麼都不用寫,hooks.json 已自動接線五支 hook +# claude — 什麼都不用寫,hooks.json 已自動接線六支 hook # codex — 在 shell rc 檔加上 codex 別名,轉呼叫 tools/jsc-wrap.sh codex(開始計時); # 在 config.toml 設 notify(每輪補 session-timer.sh start 再 mark,JSC_CLI=codex); -# 在 AGENTS.md 附加 STE100 規則段落(prompt 降級) +# 在 AGENTS.md 附加 STE100 與註解範圍規則段落(prompt 降級) # copilot — 在 shell rc 檔加上 copilot 別名,轉呼叫 tools/jsc-wrap.sh copilot; -# 在 copilot-instructions.md 附加 STE100 規則段落 +# 在 copilot-instructions.md 附加 STE100 與註解範圍規則段落 # antigravity — 在 shell rc 檔加上 agy 別名,轉呼叫 tools/jsc-wrap.sh antigravity; -# 在全域規則檔附加 STE100 規則段落 -# kiro — 在工作區 .kiro/hooks/ 下建立 jsc-hooks.json(每輪 mark 加 STE100) -# 與 jsc-hooks-session-start.json(sessionStart 開始計時),皆帶 JSC_CLI=kiro +# 在全域規則檔附加 STE100 與註解範圍規則段落 +# kiro — 在工作區 .kiro/hooks/ 下建立 jsc-hooks.json(每輪 mark 加 STE100 +# 與註解範圍規則)與 jsc-hooks-session-start.json(sessionStart 開始計時), +# 皆帶 JSC_CLI=kiro # # 覆蓋範圍要據實回報,不得暗示每個 CLI 都有保護: -# claude 五支 hook 全接,回報 wired -# codex、copilot、antigravity、kiro 只有別名或規則檔,接不上 PreToolUse 與 +# claude 六支 hook 全接,回報 wired +# codex、copilot、antigravity、kiro 只有別名或規則檔,接不上 PreToolUse、PostToolUse 與 # UserPromptSubmit,版本前置檢查與 SDLC 模型鎖 -# 都沒接上,一律回報 degraded 並在 reason 講明 +# 都沒接上,一律回報 degraded 並在 reason 講明。 +# 註解範圍檢查在這四個 CLI 只剩規則提示:沒有 post-tool +# hook,寫檔後的自動掃描接不上,違規註解只能靠模型自律 # 所有 jsc 標記段落都採整段重寫,重跑等同先移除舊內容再重裝 # # 寫入後自我驗證,通過才回報成功:每個寫過的檔案重新讀一次,確認標記段落存在且落在 @@ -76,6 +79,23 @@ esac # STE100 規則段落的唯一來源:ste100-guard.sh 的實際輸出 ste100_text() { sh "$HOOKS/ste100-guard.sh" 2>/dev/null | sed '/^exit /d'; } +# 註解範圍規則段落的唯一來源:comment-scope.sh prompt 的實際輸出。 +# 規則正文不在這裡抄一份:抄了就會跟腳本各自漂移,兩邊講的規則對不起來。 +comment_scope_text() { sh "$HOOKS/comment-scope.sh" prompt 2>/dev/null | sed '/^exit /d'; } + +# 寫進規則檔的完整段落:語言規則加註解範圍規則,共用同一組 jsc-hooks 標記。 +# 兩段合在一個標記段落裡,purge 與重跑接線都是整段處理,不必各自再記一組標記。 +rules_text() { ste100_text; comment_scope_text; } + +# 驗證:規則檔真的收到註解範圍那一段了嗎。比對字串取自腳本的第一行實際輸出, +# 不是另外抄一句關鍵字——抄的關鍵字改腳本時不會跟著改,驗證就會永遠通過。 +# $1=檔案 +has_comment_scope() { + _first=$(comment_scope_text | head -n1) + [ -n "$_first" ] || return 1 + grep -qF "$_first" "$1" 2>/dev/null +} + # 以標記整段取代(冪等);標記不存在就在檔尾新增;檔案不存在就建立。 # 適用 markdown 規則檔與 shell rc 檔:這兩種檔案沒有「區段」概念,附在檔尾就對了。 # $1=檔案 $2=開頭標記行 $3=結尾標記行 $4=標記之間要寫入的內容 @@ -588,7 +608,10 @@ if [ "$action" = smoke ]; then _h="$1"; _s="${2:-}" # 技能名一律清空:冒煙要驗的是「沒有技能情境時腳本跑得完」。留著繼承來的 JSC_SKILL, # sdlc-gate.sh wp-check skill 會拿它當真實呼叫判定,有未結清 PR 時就誤報成執行期錯誤。 - _out=$(printf '{}' | JSC_CLI="$cli" JSC_SKILL="" SKILL="" sh "$HOOKS/$_h" $_s 2>&1); _rc=$? + # JSC_CHANGED_FILE 同理清空:留著繼承來的檔名,comment-scope.sh 會真的去掃那個檔, + # 掃到違規註解就 exit 2,冒煙測試變成看環境臉色,測不出腳本本身跑不跑得完。 + _out=$(printf '{}' | JSC_CLI="$cli" JSC_SKILL="" SKILL="" JSC_CHANGED_FILE="" \ + sh "$HOOKS/$_h" $_s 2>&1); _rc=$? if [ "$_rc" -eq 0 ]; then printf '[jsc] %s%s:exit 0,正常。\n' "$_h" "${_s:+ $_s}" >> "$smoke_out" elif [ "$_h" = sdlc-gate.sh ] && [ "$_s" = check ] && [ "$_rc" -eq 2 ]; then @@ -596,6 +619,10 @@ if [ "$action" = smoke ]; then # (見 hooks/lib.sh 開頭)——鎖存在且模型不符時就該擋下該輪提示。那是 hook 正常 # 工作,不是執行期錯誤;把它算成錯誤會讓每個正在上鎖的工作階段都誤報一次失敗。 printf '[jsc] %s check:exit 2,SDLC 階段鎖擋下該輪提示,屬設計行為,不算錯誤。\n' "$_h" >> "$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" else smoke_fails=$((smoke_fails + 1)) printf '[jsc] %s%s:exit %s,執行期出錯:%s\n' "$_h" "${_s:+ $_s}" "$_rc" \ @@ -612,9 +639,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 也只有 {})。 + smoke_one comment-scope.sh prompt + smoke_one comment-scope.sh if [ "$smoke_fails" -eq 0 ]; then - printf 'status=ok reason=%s\n' "五支 hook 的每個接線模式都跑得完,沒有執行期錯誤" + printf 'status=ok reason=%s\n' "六支 hook 的每個接線模式都跑得完,沒有執行期錯誤" cat "$smoke_out"; rm -f "$smoke_out"; exit 0 fi printf 'status=failed reason=%s\n' "$smoke_fails 支 hook 有執行期錯誤" @@ -644,6 +675,13 @@ if [ "$action" = status ]; then else st_item "$1" "$2" missing; fi } + # 註解範圍規則寫進規則檔了嗎。與 STE100 共用同一個標記段落,所以要單獨比對內容: + # 標記在、內容卻是舊版只有 STE100 的那一份時,這一項才看得出來缺了。$1=項目名 $2=檔案 + st_comment_scope() { + if [ -f "$2" ] && has_comment_scope "$2"; then st_item "$1" "$2" present + else st_item "$1" "$2" missing; fi + } + # 別名段落只要任何一個既有 rc 檔帶有標記就算接上。$1=項目名 $2=標記名 st_rc_alias() { for _rc in "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.config/fish/config.fish"; do @@ -656,7 +694,12 @@ if [ "$action" = status ]; then case "$cli" in claude) if [ -f "$HOOKS/hooks.json" ]; then st_item hooks.json "$HOOKS/hooks.json" present - else st_item hooks.json "$HOOKS/hooks.json" missing; fi ;; + else st_item hooks.json "$HOOKS/hooks.json" missing; fi + # 六支 hook 全靠這一個檔宣告,只看檔案在不在會漏掉「檔在、某支沒接進去」。 + # 最後加進來的 comment-scope.sh 是最可能漏的一支,所以單獨列一項。 + if [ -f "$HOOKS/hooks.json" ] && grep -qF 'comment-scope.sh' "$HOOKS/hooks.json" 2>/dev/null + then st_item comment-scope "$HOOKS/hooks.json" present + else st_item comment-scope "$HOOKS/hooks.json" missing; fi ;; codex) config="${CODEX_HOME:-$HOME/.codex}/config.toml" @@ -669,17 +712,20 @@ if [ "$action" = status ]; then fi st_rc_alias alias jsc-hooks:codex st_block ste100 "${CODEX_HOME:-$HOME/.codex}/AGENTS.md" "" - st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查" ;; + st_comment_scope comment-scope "${CODEX_HOME:-$HOME/.codex}/AGENTS.md" + st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" ;; copilot) st_rc_alias alias jsc-hooks:copilot st_block ste100 "${JSC_COPILOT_INSTRUCTIONS:-$HOME/.config/copilot/copilot-instructions.md}" "" - st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查" ;; + 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 可接寫檔後掃描" ;; antigravity) st_rc_alias alias jsc-hooks:antigravity st_block ste100 "${JSC_ANTIGRAVITY_RULES:-$HOME/.antigravity/AGENTS.md}" "" - st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查" ;; + st_comment_scope comment-scope "${JSC_ANTIGRAVITY_RULES:-$HOME/.antigravity/AGENTS.md}" + st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" ;; kiro) # kiro 的 hook 檔綁在工作區,這裡看的一律是目前工作目錄底下那一份 @@ -687,7 +733,12 @@ 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 - st_degrade="SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查" ;; + # userPromptSubmit 那一筆的 run 串了兩支腳本,只驗 JSON 讀得懂會漏掉少接的那一支 + if [ -f ./.kiro/hooks/jsc-hooks.json ] && + grep -qF 'comment-scope.sh' ./.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 可接寫檔後掃描" ;; esac if [ "$st_missing" -gt 0 ]; then @@ -698,7 +749,7 @@ if [ "$action" = status ]; then printf 'status=degraded reason=%s\n' "$st_degrade" cat "$st_items"; rm -f "$st_items"; exit 1 fi - printf 'status=wired reason=%s\n' "hooks.json 自動接線全部五支 hook" + printf 'status=wired reason=%s\n' "hooks.json 自動接線全部六支 hook" cat "$st_items"; rm -f "$st_items"; exit 0 fi @@ -707,8 +758,11 @@ case "$cli" in bin=$(cli_bin claude) command -v "$bin" >/dev/null 2>&1 || skip "未偵測到 claude 執行檔" [ -f "$HOOKS/hooks.json" ] || fail "找不到 $HOOKS/hooks.json,claude 接不到任何 hook" + grep -qF 'comment-scope.sh' "$HOOKS/hooks.json" 2>/dev/null \ + || 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:由 hooks/hooks.json 自動接線全部六支 hook,無需寫入設定。" + echo "[jsc] claude:只有 claude 有 post-tool hook,comment-scope.sh 的寫檔後自動掃描只在這裡接得上。" exit 0 ;; codex) @@ -731,16 +785,18 @@ case "$cli" in toml_root_key "$config" notify \ || fail "$config 的 notify 沒有落在根層(被歸進某張表,codex 讀不到,hook 會靜靜失效)" write_alias_rc "jsc-hooks:codex" "$alias_line" || fail "無法把 $bin 別名寫進 shell rc 檔" - replace_block "$agents" "" "" "$(ste100_text)" \ + replace_block "$agents" "" "" "$(rules_text)" \ || fail "無法寫入 $agents" has_block "$agents" "" || fail "$agents 寫入後讀不到 jsc-hooks 標記段落" - printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查" + has_comment_scope "$agents" || fail "$agents 寫入後讀不到註解範圍規則" + printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-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:已在 $agents 寫入 STE100 規則段落(prompt 降級)。" + 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 補。" exit 1 ;; copilot) @@ -749,15 +805,17 @@ case "$cli" in instr="${JSC_COPILOT_INSTRUCTIONS:-$HOME/.config/copilot/copilot-instructions.md}" alias_line="alias $bin='sh \"$HERE/jsc-wrap.sh\" copilot'" write_alias_rc "jsc-hooks:copilot" "$alias_line" || fail "無法把 $bin 別名寫進 shell rc 檔" - replace_block "$instr" "" "" "$(ste100_text)" \ + replace_block "$instr" "" "" "$(rules_text)" \ || fail "無法寫入 $instr" has_block "$instr" "" || fail "$instr 寫入後讀不到 jsc-hooks 標記段落" - printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查" + has_comment_scope "$instr" || fail "$instr 寫入後讀不到註解範圍規則" + printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" echo "[jsc] copilot:已在 shell rc 加上 $bin 別名,轉呼叫 tools/jsc-wrap.sh copilot。" - echo "[jsc] copilot:已在 $instr 寫入 STE100 規則段落(prompt 降級)。" + 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 補。" exit 1 ;; antigravity) @@ -766,15 +824,17 @@ case "$cli" in rules="${JSC_ANTIGRAVITY_RULES:-$HOME/.antigravity/AGENTS.md}" alias_line="alias $bin='sh \"$HERE/jsc-wrap.sh\" antigravity'" write_alias_rc "jsc-hooks:antigravity" "$alias_line" || fail "無法把 $bin 別名寫進 shell rc 檔" - replace_block "$rules" "" "" "$(ste100_text)" \ + replace_block "$rules" "" "" "$(rules_text)" \ || fail "無法寫入 $rules" has_block "$rules" "" || fail "$rules 寫入後讀不到 jsc-hooks 標記段落" - printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查" + has_comment_scope "$rules" || fail "$rules 寫入後讀不到註解範圍規則" + printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍只剩規則提示、無 post-tool hook 可接寫檔後掃描" echo "[jsc] antigravity:已在 shell rc 加上 $bin 別名,轉呼叫 tools/jsc-wrap.sh antigravity。" - echo "[jsc] antigravity:已在 $rules 寫入 STE100 規則段落(prompt 降級)。" + 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 補。" exit 1 ;; kiro) @@ -798,10 +858,10 @@ EOF cat > "$hookfile" 2>/dev/null </dev/null || fail "$startfile 沒有接在 sessionStart" grep -qF '"userPromptSubmit"' "$hookfile" 2>/dev/null || fail "$hookfile 沒有接在 userPromptSubmit" - printf 'status=degraded reason=%s\n' "SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查" + 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 可接寫檔後掃描" 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 補。" exit 1 ;; esac -- 2.53.0 From 5d60e0f2313575a29f0286c134382e013360ac00 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 26 Aug 2026 19:01:13 +0800 Subject: [PATCH 05/16] =?UTF-8?q?feat(hooks-install):=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=85=AD=E6=94=AF=20hook=20=E8=88=87?= =?UTF-8?q?=E9=99=8D=E7=B4=9A=E8=AA=AA=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:`skills/hooks-install/SKILL.md`、`README.md`、`AGENTS.md` 三份文件一併改口徑為六支 hook,補上 `comment-scope.sh` 的職責、兩種模式、環境變數 `JSC_COMMENT_SCOPE` 與 `JSC_CHANGED_FILE`,以及各 CLI 的覆蓋範圍差異。 Why:文件停在五支會誤導操作者,讓人以為每個 CLI 都受同等保護。實際上只有 claude 有 post-tool hook,其他四個 CLI 只接得到規則提示,這個落差必須據實寫明。 How:SKILL.md 的 `description` 與正文加上 comment scope scanner,並註明 exit 2 在冒煙測試裡算健康;README.md 的 hook 表格新增一列,接線工具那列改寫 `smoke` 的例外說明,環境變數表補兩列,另加一段講明四個 CLI 的降級實情;AGENTS.md 新增一條規則,寫明規則正文的唯一來源在 `jsc-review`,本存取庫不留副本。 Who:`jsc-hooks` 的文件層與 `hooks-install` 技能,供操作者與後續 sub agent 依循。 --- AGENTS.md | 5 +++-- README.md | 13 +++++++++---- skills/hooks-install/SKILL.md | 12 +++++++----- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e627860..0e8bbc8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # jsc-hooks — 給 AI 助理的指引 -本 repo 是 jsc 技能組的 `hooks` domain(跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查),可同時被 Claude Code / Codex / Copilot / Antigravity / Kiro 使用。 +本 repo 是 jsc 技能組的 `hooks` domain(跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍檢查),可同時被 Claude Code / Codex / Copilot / Antigravity / Kiro 使用。 ## 規則 @@ -8,7 +8,8 @@ 2. 技能位於 `skills/{name}/SKILL.md`;處理任務前先比對需求與各技能的 `description`,相符就載入並依其步驟執行。 3. 技能準則的唯一來源:`plugins/meta` 存取庫的 `references/guidelines.md`。 4. 所有 hook 只放在 `jsc-hooks`;gitea 操作一律經由 `jsc-gitea` 的 `tools/gitea.sh`;問使用者一律依 `jsc-ask:ask` 的決策樹規則。 -5. 主 agent 不需要處理細節的流程,一律建立 sub agent 處理。 +5. 註解範圍規則正文的唯一來源:`jsc-review` 的 `references/comment-scope.md`。本存取庫只放 `hooks/comment-scope.sh` 的判定實作,不留規則清單副本,接線腳本要用規則文字時一律取腳本的實際輸出。 +6. 主 agent 不需要處理細節的流程,一律建立 sub agent 處理。 ## 呼叫慣例 diff --git a/README.md b/README.md index 471900a..72b9941 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,14 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安 | `hooks/session-timer.sh` | SessionStart / Stop / SessionEnd | 記錄工作階段起訖。子指令:`start` 記起始時間(已有紀錄就不動,給 claude 這種每階段有自己 session id 的 CLI)、`restart` 一律覆寫起始時間(給接不到 session id 的 kiro,不覆寫會把上一階段算進來)、`mark` 更新最後活動時間、`report` 供 `jsc-log:worklog` 取花費時間 | | `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/skill-usage.sh` | PostToolUse(Skill) | 記錄技能使用與呼叫鏈到 `$JSC_HOME/usage/*.jsonl`,供 `jsc-log:stats` 統計 | +| `hooks/comment-scope.sh` | UserPromptSubmit、PostToolUse(Write、Edit、MultiEdit) | 程式碼註解不得夾帶文件相關資訊:`prompt` 在每次提示注入規則摘要(禁止項與白名單各一行);無參數為寫檔後掃描,從 stdin JSON 取 `file_path`(或環境變數 `JSC_CHANGED_FILE`),只掃 `git diff HEAD` 的新增行、不翻舊帳,命中就把警告與最多三行證據送到 stderr 並以 exit 2 交回模型就地修正(不擋寫入,檔案已經寫好了)。markdown、純文字、資料檔與二進位檔一律跳過。只實作可用樣式判定的項目,專案代號、客戶名稱這類判不出來的交給 `/jsc-review:code-review`。規則正文的唯一來源在 `jsc-review` 的 `references/comment-scope.md`,本存取庫不留副本。逃生門 `JSC_COMMENT_SCOPE=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-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`,不是這裡。逃生門 `JSC_WP_GATE=off`。這道閘門只讀檔案、不打網路,PR 的真實合併狀態由 `jsc-sdlc/tools/wp-gate.sh` 查證 | -Claude 由 `hooks/hooks.json` 自動接線五支 hook;其他 CLI 用 `hooks-install` 技能接線、改裝包裝啟動器,或降級為規則檔。 +Claude 由 `hooks/hooks.json` 自動接線六支 hook;其他 CLI 用 `hooks-install` 技能接線、改裝包裝啟動器,或降級為規則檔。 -> 覆蓋範圍要據實看待:只有 claude 同時有 PreToolUse 與 UserPromptSubmit,五支 hook 全接得上,回報 `wired`。codex、copilot、antigravity、kiro 都沒有 pre-tool hook,接不上 `version-guard.sh` 的版本前置檢查,SDLC 模型鎖也只剩技能步驟檢查,這四個 CLI 一律回報 `degraded`,靠 `/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` 的版本前置檢查,SDLC 模型鎖也只剩技能步驟檢查,這四個 CLI 一律回報 `degraded`,靠 `/jsc-cli:deploy` 定期更新。codex 另外沒有工作階段開始事件,計時改由 `tools/jsc-wrap.sh` 的 `codex` 別名在啟動當下開始;沒走別名啟動時,時間從第一輪回應算起。 + +> `comment-scope.sh` 同樣分兩級:這四個 CLI 也沒有 post-tool hook,只接得到 `prompt` 模式的規則提示(寫進各自的規則檔,與 STE100 同一個標記段落),寫檔後的自動掃描接不上。也就是說在 codex、copilot、antigravity、kiro 上,違規註解只有規則提示擋一層,寫進去了不會有人叫,要靠 `/jsc-review:code-review` 第 2 組事後抓。只有 claude 兩級都有。 > `version-guard.sh report` 是非 hook 的子指令:印出每個已安裝 jsc plugin 的 > 「{domain} {本機} {遠端} {落後|最新|超前|查詢失敗}」,最後一行 `behind {落後個數}`。 @@ -46,7 +49,7 @@ 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` 回填。`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`)標記整段重寫,重跑等同先移除再重裝;寫完每個檔案會重讀驗證位置正確才回報成功(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 各跑一次,非零退出即為錯誤(唯一例外是 `sdlc-gate.sh check` 的 exit 2,那是階段鎖的設計行為),以 `status=ok\|failed` 回報。`status {cli}` 是唯讀盤點:只讀設定檔判斷標記段落在不在,不寫檔也不執行 hook,每個接線點印一行 `item{項目}{路徑}{present\|missing}`,以 `status=wired\|degraded\|unwired\|skipped` 回報(結束碼 0、1、5、3)。體檢類技能(`/jsc-cli:doctor`)只能用這個子命令,另外三個都會動到環境 | +| `tools/wire-cli.sh` | 單一 CLI 的 hook 生命週期,共三個用法。`{cli}` 是接線:對應的設定編輯、包裝別名安裝、hook 檔建立,皆以 ``(或 `# 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 的每個接線模式各跑一次,非零退出即為錯誤(例外有兩個:`sdlc-gate.sh check` 的 exit 2 是階段鎖的設計行為,`comment-scope.sh` 無參數模式的 exit 2 是掃到違規註解的設計行為),以 `status=ok\|failed` 回報。`status {cli}` 是唯讀盤點:只讀設定檔判斷標記段落在不在,不寫檔也不執行 hook,每個接線點印一行 `item{項目}{路徑}{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` 回報 | ## 失敗回報範本 @@ -67,7 +70,7 @@ Claude 由 `hooks/hooks.json` 自動接線五支 hook;其他 CLI 用 `hooks-in ### `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`),語言規則仍重寫到各自的規則檔(以 `` 標記整段取代,等同先移除再重裝,不重複追加)。codex、copilot、antigravity、kiro 的 SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 SDLC 技能直接呼叫 `sdlc-gate.sh lock` 寫入;這四個 CLI 沒有 pre-tool hook,版本前置檢查接不上,腳本會在 `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` 自動接線,無需寫入),接著 `tools/wire-cli.sh smoke {cli}` 驗執行期,最後 `tools/scan-hook-errors.sh --cli {cli}` 掃原生紀錄。codex、copilot、antigravity 由接線腳本裝上 `tools/jsc-wrap.sh` 包裝別名補上計時與用量回填(結束時自動跑 `tools/scan-logs.sh`),語言規則仍重寫到各自的規則檔(以 `` 標記整段取代,等同先移除再重裝,不重複追加)。codex、copilot、antigravity、kiro 的 SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 SDLC 技能直接呼叫 `sdlc-gate.sh lock` 寫入;這四個 CLI 沒有 pre-tool hook,版本前置檢查接不上,也沒有 post-tool hook,`comment-scope.sh` 只接得到規則提示、接不上寫檔後的自動掃描,腳本會在 `reason` 裡講明,只有 claude 回報 `wired`,也只有 claude 掃得到執行期錯誤紀錄。任一關卡出錯(purge、接線、冒煙失敗,或掃到 `jsc=true` 的執行期錯誤)就先寫 `ERROR_{HASH}`,再交給 `repair` 技能接手並以 `develop` PR 收尾;此時允許中止剩下的安裝,但修正一定要開始。掃到 `jsc=false` 的第三方 hook 錯誤只回報,不轉修正。 ### `repair` @@ -86,6 +89,8 @@ Claude 由 `hooks/hooks.json` 自動接線五支 hook;其他 CLI 用 `hooks-in | `JSC_VERSION_GUARD` | 設 `off` 完全略過版本前置檢查(離線工作用) | 啟用檢查 | | `JSC_VERSION_TTL` | 遠端版本查詢的快取秒數 | 預設 600 | | `JSC_WP_GATE` | 設 `off` 完全略過工作包 PR 閘門(`wp-check` 一律放行) | 啟用閘門 | +| `JSC_COMMENT_SCOPE` | 設 `off` 完全略過註解範圍檢查(`comment-scope.sh` 兩種模式都直接結束) | 啟用檢查 | +| `JSC_CHANGED_FILE` | 非 Claude CLI 要掃描的檔案路徑,代替 stdin JSON 的 `file_path`,供 `comment-scope.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`,再不行就安靜降級 | diff --git a/skills/hooks-install/SKILL.md b/skills/hooks-install/SKILL.md index 390188e..d5b910f 100644 --- a/skills/hooks-install/SKILL.md +++ b/skills/hooks-install/SKILL.md @@ -1,15 +1,16 @@ --- name: hooks-install -description: Wire jsc hooks (STE100 guard, session timer, skill usage logger, SDLC model gate, plugin version guard) into every installed AI CLI, purging all pre-existing hooks first — third-party ones included, backed up before removal. Drive it per CLI through tools/wire-cli.sh purge, tools/wire-cli.sh, tools/wire-cli.sh smoke and tools/scan-hook-errors.sh. Hand any hook error, wiring or runtime, to jsc-hooks:repair, which must finish with a PR against develop; aborting the rest of the install to start that repair is allowed. Use after installing or updating the jsc plugin set; not for writing new hooks. +description: Wire jsc hooks (STE100 guard, session timer, skill usage logger, SDLC model gate, plugin version guard, comment scope scanner) into every installed AI CLI, purging all pre-existing hooks first — third-party ones included, backed up before removal. Drive it per CLI through tools/wire-cli.sh purge, tools/wire-cli.sh, tools/wire-cli.sh smoke and tools/scan-hook-errors.sh. Hand any hook error, wiring or runtime, to jsc-hooks:repair, which must finish with a PR against develop; aborting the rest of the install to start that repair is allowed. Use after installing or updating the jsc plugin set; not for writing new hooks. --- # hooks-install — wire jsc hooks into every installed CLI -Goal: make the five hooks (`ste100-guard.sh`, `session-timer.sh`, `skill-usage.sh`, `sdlc-gate.sh`, `version-guard.sh`) effective in every CLI, with nothing else wired alongside them. +Goal: make the six hooks (`ste100-guard.sh`, `session-timer.sh`, `skill-usage.sh`, `sdlc-gate.sh`, `version-guard.sh`, `comment-scope.sh`) effective in every CLI, with nothing else wired alongside them. Install on a clean slate. Every CLI is purged of all hooks first, third-party ones included, so a later failure has exactly one owner. `tools/wire-cli.sh purge` backs up every file it touches before it removes anything, so the removal stays reversible. -Only claude has both PreToolUse and UserPromptSubmit, so only claude reports `wired`. On codex, copilot, antigravity and kiro the version guard cannot be wired at all and the SDLC gate degrades to the skill-step check, so all four report `degraded` — report that gap as the script words it instead of implying every CLI is covered. +Only claude has PreToolUse, PostToolUse and UserPromptSubmit, so only claude reports `wired`. On codex, copilot, antigravity and kiro the version guard cannot be wired at all and the SDLC gate degrades to the skill-step check, so all four report `degraded` — report that gap as the script words it instead of implying every CLI is covered. +`comment-scope.sh` degrades the same way on those four: they have no post-tool hook, so only its `prompt` mode reaches them, written into the same rule file as the STE100 block. The file is never scanned after a write there, and a comment that already carries an issue number is caught only by `jsc-review:code-review`. Say that plainly — a rule reminder is not the same protection as the scan claude gets. The lock file still works on those four because the SDLC skills call `sdlc-gate.sh lock {stage}` directly — that call is where the capability-tag comparison happens, so the gate keeps its force even where the prompt hook cannot be wired. The gate needs `$JSC_HOME/model-tags.tsv`; when it is missing, report that `jsc-cli:models` (or `jsc-cli/tools/model-tags.sh sync`) must run once, because `sdlc-gate.sh lock` refuses to lock without it. @@ -22,7 +23,7 @@ The detailed flow **MUST run as a sub agent**; the main agent only reports the s 1. Run `jsc-cli/tools/detect-clis.sh`. Done when you hold the list of installed CLIs; when the list is empty, report that and stop. 2. For each installed CLI, run `tools/wire-cli.sh purge {cli}`. The script backs up every file it touches, removes all hooks, re-reads each file to confirm the removal, and restores the backup by itself when a check fails. Done when every CLI has printed exactly one `status=purged|skipped|failed reason=...` line and you have noted the backup directory path from its `[jsc]` output. 3. For each installed CLI, run `tools/wire-cli.sh {cli}`. The script owns both the wiring and its verification: it writes the config, alias or hook file inside a `` (or `# jsc-hooks`) marker block, re-reads every file it wrote, and confirms the block is present and correctly placed before it prints a success status. Trust its first line, `status=wired|degraded|skipped|failed reason=...`. Exit 2 means a bad CLI name, not a wiring outcome — fix the name and rerun. Done when every installed CLI has printed exactly one `status=` line and none exited 2. -4. For each installed CLI, run `tools/wire-cli.sh smoke {cli}`. This runs all five hooks once each and catches what the wiring check cannot see: a hook that is wired correctly and still fails when it executes. Done when every CLI has printed one `status=ok|failed reason=...` line plus one result line per hook. +4. For each installed CLI, run `tools/wire-cli.sh smoke {cli}`. This runs all six hooks once each, every wired mode included, and catches what the wiring check cannot see: a hook that is wired correctly and still fails when it executes. Done when every CLI has printed one `status=ok|failed reason=...` line plus one result line per hook. 5. For each installed CLI, run `tools/scan-hook-errors.sh --cli {cli}`. Only claude keeps hook results in its native records and can answer `clean` or `errors`; codex, copilot, antigravity and kiro answer `unavailable`, and their runtime evidence comes from step 4 alone. Done when every CLI has printed one `status=clean|errors|unavailable reason=...` line and the four `unavailable` CLIs are reported as exactly that, not as clean. 6. For each error — `purge` failed, wiring failed, smoke failed, or a scanned error with `jsc=true` — run `tools/report-error.sh --hook {script name} --exit {code} --summary "{reason}" --cli {cli}` with the script's `[jsc]` output on stdin, then hand the failure to `jsc-hooks:repair`, which **MUST run as a sub agent** and must finish by opening a PR against `develop`. Aborting the remaining installs here is allowed as long as the repair starts. A scanned error with `jsc=false` belongs to a third-party hook: report it and leave it alone. Done when each error has either an `ERROR_{HASH}` page name on stdout, or an empty exit 0 meaning `JSC_WIKI_REPO_ERROR` and `JSC_WIKI_REPO` are both unset — in that second case carry the reason into step 7 instead. Skip this step when every CLI passed all four checks. 7. Report four results per CLI — purge, wiring, smoke, scan — each with the reason its script printed, plus any `ERROR_{HASH}` page name and repair PR URL. Done when every detected CLI has exactly one status per check and every repair has a PR against `develop`. @@ -33,6 +34,7 @@ The detailed flow **MUST run as a sub agent**; the main agent only reports the s - `session-timer.sh` takes `start` (keep an existing start time), `restart` (always overwrite it, for a CLI with no session id — kiro), `mark` and `report`. `wire-cli.sh` picks the right one per CLI; do not hand-edit the generated hook files. - `purge` reaches the user-level config only. Hooks that another plugin ships in its own `hooks.json` stay active, and uninstalling that plugin is the only way to clear them — say so when reporting, and treat their errors as third-party. - Backups land in `$JSC_HOME/backup/hooks/{cli}/{yyyyMMdd_HHmmss}/`, one directory per purge run, under the original file names. Hand that path to the user whenever a purge removed something. -- `smoke` treats `sdlc-gate.sh check` exit 2 as healthy: that exit is the stage lock blocking a turn on purpose, not a runtime error. +- `smoke` treats `sdlc-gate.sh check` exit 2 as healthy: that exit is the stage lock blocking a turn on purpose, not a runtime error. `comment-scope.sh` exit 2 counts as healthy for the same reason — it means the scan found a comment and warned about it. With no file name to scan the hook exits 0 in silence, which is what smoke normally sees. +- `comment-scope.sh` takes `prompt` (inject the rule summary at UserPromptSubmit) and no argument at all (scan the file just written at PostToolUse, reading `file_path` from stdin JSON or `JSC_CHANGED_FILE`). It scans only the lines a diff added, skips markdown and binary files, and turns off entirely with `JSC_COMMENT_SCOPE=off`. The rule text itself lives in one place only, `jsc-review`'s `references/comment-scope.md`; never restate the list anywhere in this repo. - `tools/report-error.sh` is operator- or skill-invoked only. Never wire it to fire from a failing hook: hooks stay silent and exit 0, and a failing hook that reports itself can loop. - Data lands in `$JSC_HOME` (default `~/.jsc`), consumed by `jsc-log:worklog` and `jsc-log:stats`. -- 2.53.0 From 7690b697c78de82aded6577e1e0ac4525742571f Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 26 Aug 2026 19:01:24 +0800 Subject: [PATCH 06/16] =?UTF-8?q?feat(manifest):=20=E4=B8=89=E4=BB=BD=20ma?= =?UTF-8?q?nifest=20=E7=89=88=E6=9C=AC=E5=8D=87=E5=88=B0=200.1.9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:`plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` 三份 manifest 的 `version` 由 0.1.8 升到 0.1.9。 Why:本次新增了第六支 hook,屬於功能增修。版本不升,`version-guard.sh` 的版本前置檢查與 `jsc-cli:deploy` 的更新判斷都看不出本機落後,使用者不會收到更新提示。 How:三份檔案同步改同一個版本號,維持三份 manifest 版本一致的既有慣例。 Who:`jsc-hooks` 的發佈中繼資料,供 `hooks/version-guard.sh` 與 `jsc-cli:deploy` 比對版本。 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index e21b3b4..dcf3007 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.1.8", + "version": "0.1.9", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index ed01596..13f0784 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.1.8", + "version": "0.1.9", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills" } diff --git a/plugin.json b/plugin.json index 5d43797..f6c6bfe 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.1.8", + "version": "0.1.9", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills/" } -- 2.53.0 From 8ede74e9e2253fd7a98934650b8dba71bf8f2ff6 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 27 Aug 2026 09:15:56 +0800 Subject: [PATCH 07/16] =?UTF-8?q?feat(comment-scope):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=20sweep=20=E6=A8=A1=E5=BC=8F=EF=BC=8C=E6=8E=83=E6=95=B4?= =?UTF-8?q?=E5=80=8B=20git=20=E5=B7=A5=E4=BD=9C=E5=8D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:`hooks/comment-scope.sh` 由兩種模式變三種,新增 `sweep [dir]`:掃整個 git 工作區這次改過的所有檔案,命中就把報告與最多三行證據送到 stderr 並 exit 2,找不到 git 就安靜 exit 0。`prompt` 與無參數單檔掃描兩個模式的判定邏輯一行不動。 Why:只有 claude 有 PostToolUse,拿得到「剛剛寫了哪個檔」。codex、copilot、antigravity、kiro 四個 CLI 都沒有 post-tool 事件,註解範圍檢查在那邊只剩規則提示,違規註解寫進去了不會有人叫。改掃整個工作區的 git diff,時機晚一點,涵蓋範圍一樣。 How:單檔判定抽成 `scan_file()`,兩種掃描模式共用同一份禁止樣式與白名單,不會各自漂移。`sweep` 以 `git rev-parse --show-toplevel` 找庫根,所以在子目錄跑也掃得到整個庫;逐檔報告累積在暫存檔再一次輸出,因為迴圈跑在管線的子行程裡,變數帶不回本 shell。 Who:`jsc-hooks` 的 hook 實作層,供 `tools/wire-cli.sh` 接線給 codex、copilot、antigravity、kiro 四個 CLI 使用。 --- hooks/comment-scope.sh | 166 ++++++++++++++++++++++++++--------------- 1 file changed, 106 insertions(+), 60 deletions(-) diff --git a/hooks/comment-scope.sh b/hooks/comment-scope.sh index 061b16c..d00d749 100755 --- a/hooks/comment-scope.sh +++ b/hooks/comment-scope.sh @@ -4,8 +4,13 @@ # 專案代號、客戶名稱這類無法用樣式判定的,交給 jsc-review:code-review 第 2 組人工審查。 # # 用法: -# comment-scope.sh prompt # UserPromptSubmit:注入規則摘要 -# comment-scope.sh # PostToolUse:掃描剛寫入的檔案,命中就發警告 +# comment-scope.sh prompt 注入規則摘要(UserPromptSubmit 或規則檔取文字用) +# comment-scope.sh 掃描剛寫入的單一檔案(PostToolUse) +# comment-scope.sh sweep [dir] 掃描整個工作區這次改過的所有檔案(沒有 post-tool hook 的 CLI 用) +# +# 為什麼要有 sweep:只有 claude 接得到 PostToolUse,逐檔精準掃得到。codex 只有每輪結束的 +# notify、kiro 只有 userPromptSubmit、copilot 與 antigravity 只有包裝別名,這四個都拿不到 +# 「剛剛寫了哪個檔」,只能改成掃整個工作區的 git diff。時機晚一點,涵蓋範圍一樣。 # # 輸入相容: # Claude: PostToolUse 的 stdin JSON,取 tool_input.file_path。 @@ -13,7 +18,7 @@ # 兩者都取不到就安靜降級(exit 0)。 # # 掃描範圍:檔案在 git 工作區內就只掃 `git diff HEAD` 的新增行,不翻舊帳; -# 不在 git 內或檔案尚未追蹤才整檔掃描。 +# 不在 git 內或檔案尚未追蹤才整檔掃描。sweep 一律只看 git diff。 # # 結束碼:0=沒命中或資料不足;2=命中,訊息走 stderr 交回模型自行修正(不擋寫入,檔案已經寫好了)。 # 逃生門:JSC_COMMENT_SCOPE=off。 @@ -29,43 +34,6 @@ if [ "${1:-}" = "prompt" ]; then exit 0 fi -read_stdin 2>/dev/null || STDIN_JSON="" -file=$(json_str file_path 2>/dev/null || true) -[ -n "$file" ] || file="${JSC_CHANGED_FILE:-}" -[ -n "$file" ] && [ -f "$file" ] || exit 0 - -# 非程式碼檔不受本規則限制:markdown、純文字、資料檔沒有「程式碼註解」。 -case "$file" in - *.md|*.markdown|*.txt|*.rst|*.json|*.csv|*.tsv|*.svg|*.lock|*.log|*COMMIT_EDITMSG) exit 0 ;; -esac -# 二進位檔跳過。只認 NUL 位元組——拿「非可列印字元」當判準會把所有含中文的檔案誤判成二進位。 -raw=$(head -c 1024 "$file" 2>/dev/null | wc -c) -txt=$(head -c 1024 "$file" 2>/dev/null | LC_ALL=C tr -d '\000' | wc -c) -[ "$raw" = "$txt" ] || exit 0 - -dir=$(dirname -- "$file") -if git -C "$dir" rev-parse --is-inside-work-tree >/dev/null 2>&1 && - git -C "$dir" ls-files --error-unmatch -- "$file" >/dev/null 2>&1; then - lines=$(git -C "$dir" diff HEAD -- "$file" 2>/dev/null | sed -n 's/^+[^+]/&/p' | cut -c2-) - [ -n "$lines" ] || exit 0 -else - lines=$(cat "$file" 2>/dev/null) -fi - -# 只留註解行:行首註解符號,或行中出現 // 與 # 的行尾註解。 -comments=$(printf '%s\n' "$lines" | grep -E '^[[:space:]]*(//|#|--|\*|/\*|" 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 -- 2.53.0 From b23c54607987d1d733ac6c0f552c39fa1a078019 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 27 Aug 2026 09:16:32 +0800 Subject: [PATCH 09/16] =?UTF-8?q?docs(hooks-install):=20=E6=93=9A=E5=AF=A6?= =?UTF-8?q?=E5=AF=AB=E6=98=8E=E5=90=84=20CLI=20=E7=9A=84=E8=A8=BB=E8=A7=A3?= =?UTF-8?q?=E7=AF=84=E5=9C=8D=E6=8E=83=E6=8F=8F=E6=99=82=E6=A9=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:`skills/hooks-install/SKILL.md`、`README.md`、`AGENTS.md` 三份文件一併改寫註解範圍的覆蓋範圍說明:`comment-scope.sh` 由兩種模式改為三種,並以表格列出五個 CLI 各自的掃描時機——claude 逐檔即時(PostToolUse)、codex 每輪結束(`notify`)、kiro 每輪提示送出時(`userPromptSubmit`,掃的是上一輪寫的檔)、copilot 與 antigravity 只有工作階段結束時由 `tools/jsc-wrap.sh` 收尾掃一次。README 的 `tools/jsc-wrap.sh` 那列補上收尾 sweep 與「不影響結束碼」的約定,`smoke` 例外說明改成掃描模式通用。 Why:舊文件寫的是「四個 CLI 只剩規則提示」,接上 sweep 之後那句話已經不實。但也不能倒過來寫成五支一樣:時機差一輪或差一整個工作階段,操作者要知道自己現在用的 CLI 什麼時候才會收到警告。文件不同步,操作者會對保護程度有錯誤預期。 How:SKILL.md 全份維持英文,正文改用一張 CLI 對掃描時機的表格,並註明 sweep 讀的是 `git diff HEAD`、涵蓋範圍與 claude 相同、不在 git 工作區內就安靜 exit 0,frontmatter 的 `description` 不動;README.md 維持 STE100 繁中,hook 一覽表那列補上三種模式與各 CLI 時機,原本的降級段落換成同一張表;AGENTS.md 的第 5 條補上三種模式與「不得寫成五支一樣」的要求。 Who:`jsc-hooks` 的文件層與 `hooks-install` 技能,供操作者與後續 sub agent 依循。 --- AGENTS.md | 2 +- README.md | 21 +++++++++++++++------ skills/hooks-install/SKILL.md | 17 ++++++++++++++--- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0e8bbc8..f0beeb2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,7 +8,7 @@ 2. 技能位於 `skills/{name}/SKILL.md`;處理任務前先比對需求與各技能的 `description`,相符就載入並依其步驟執行。 3. 技能準則的唯一來源:`plugins/meta` 存取庫的 `references/guidelines.md`。 4. 所有 hook 只放在 `jsc-hooks`;gitea 操作一律經由 `jsc-gitea` 的 `tools/gitea.sh`;問使用者一律依 `jsc-ask:ask` 的決策樹規則。 -5. 註解範圍規則正文的唯一來源:`jsc-review` 的 `references/comment-scope.md`。本存取庫只放 `hooks/comment-scope.sh` 的判定實作,不留規則清單副本,接線腳本要用規則文字時一律取腳本的實際輸出。 +5. 註解範圍規則正文的唯一來源:`jsc-review` 的 `references/comment-scope.md`。本存取庫只放 `hooks/comment-scope.sh` 的判定實作,不留規則清單副本,接線腳本要用規則文字時一律取腳本的實際輸出。`comment-scope.sh` 有 `prompt`、無參數逐檔掃描、`sweep` 掃整個 git 工作區三種模式;掃描時機每個 CLI 都不同(claude 逐檔即時、codex 每輪結束、kiro 每輪提示送出時、copilot 與 antigravity 只有工作階段結束時),談覆蓋範圍時一律據實分開講,不得寫成五支一樣。 6. 主 agent 不需要處理細節的流程,一律建立 sub agent 處理。 ## 呼叫慣例 diff --git a/README.md b/README.md index 72b9941..15dbf12 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,23 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安 | `hooks/session-timer.sh` | SessionStart / Stop / SessionEnd | 記錄工作階段起訖。子指令:`start` 記起始時間(已有紀錄就不動,給 claude 這種每階段有自己 session id 的 CLI)、`restart` 一律覆寫起始時間(給接不到 session id 的 kiro,不覆寫會把上一階段算進來)、`mark` 更新最後活動時間、`report` 供 `jsc-log:worklog` 取花費時間 | | `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/skill-usage.sh` | PostToolUse(Skill) | 記錄技能使用與呼叫鏈到 `$JSC_HOME/usage/*.jsonl`,供 `jsc-log:stats` 統計 | -| `hooks/comment-scope.sh` | UserPromptSubmit、PostToolUse(Write、Edit、MultiEdit) | 程式碼註解不得夾帶文件相關資訊:`prompt` 在每次提示注入規則摘要(禁止項與白名單各一行);無參數為寫檔後掃描,從 stdin JSON 取 `file_path`(或環境變數 `JSC_CHANGED_FILE`),只掃 `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/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-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`,不是這裡。逃生門 `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` 的版本前置檢查,SDLC 模型鎖也只剩技能步驟檢查,這四個 CLI 一律回報 `degraded`,靠 `/jsc-cli:deploy` 定期更新。codex 另外沒有工作階段開始事件,計時改由 `tools/jsc-wrap.sh` 的 `codex` 別名在啟動當下開始;沒走別名啟動時,時間從第一輪回應算起。 -> `comment-scope.sh` 同樣分兩級:這四個 CLI 也沒有 post-tool hook,只接得到 `prompt` 模式的規則提示(寫進各自的規則檔,與 STE100 同一個標記段落),寫檔後的自動掃描接不上。也就是說在 codex、copilot、antigravity、kiro 上,違規註解只有規則提示擋一層,寫進去了不會有人叫,要靠 `/jsc-review:code-review` 第 2 組事後抓。只有 claude 兩級都有。 +> `comment-scope.sh` 五個 CLI 都掃得到,但時機不同,不能當成五支一樣: + +| CLI | 掃描時機 | 接在哪裡 | +| --- | --- | --- | +| claude | 逐檔即時,寫完哪個檔就掃哪個 | PostToolUse | +| codex | 每輪結束,掃整個 git 工作區 | `config.toml` 的根層 `notify` | +| kiro | 每輪提示送出時,掃整個 git 工作區(掃到的是上一輪寫的檔) | `.kiro/hooks/jsc-hooks.json` 的 `userPromptSubmit` | +| copilot、antigravity | 工作階段結束時掃一次 | `tools/jsc-wrap.sh` 收尾 | + +> `sweep` 看的是 `git diff HEAD`,涵蓋範圍與 claude 一樣,差的是回饋速度:claude 當下就叫,其他四個要等到該輪或該階段結束。不在 git 工作區內時 `sweep` 安靜 exit 0,等於沒掃。規則提示(`prompt` 模式)在五個 CLI 都照樣寫進規則檔,與 STE100 共用同一個標記段落——晚一輪的警告,價值仍低於一開始就不要寫。判不出來的項目(專案代號、客戶名稱)一律交給 `/jsc-review:code-review` 第 2 組。 > `version-guard.sh report` 是非 hook 的子指令:印出每個已安裝 jsc plugin 的 > 「{domain} {本機} {遠端} {落後|最新|超前|查詢失敗}」,最後一行 `behind {落後個數}`。 @@ -46,10 +55,10 @@ 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` 回填。`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` 掃整個 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`)標記整段重寫,重跑等同先移除再重裝;寫完每個檔案會重讀驗證位置正確才回報成功(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 的每個接線模式各跑一次,非零退出即為錯誤(例外有兩個:`sdlc-gate.sh check` 的 exit 2 是階段鎖的設計行為,`comment-scope.sh` 無參數模式的 exit 2 是掃到違規註解的設計行為),以 `status=ok\|failed` 回報。`status {cli}` 是唯讀盤點:只讀設定檔判斷標記段落在不在,不寫檔也不執行 hook,每個接線點印一行 `item{項目}{路徑}{present\|missing}`,以 `status=wired\|degraded\|unwired\|skipped` 回報(結束碼 0、1、5、3)。體檢類技能(`/jsc-cli:doctor`)只能用這個子命令,另外三個都會動到環境 | +| `tools/wire-cli.sh` | 單一 CLI 的 hook 生命週期,共三個用法。`{cli}` 是接線:對應的設定編輯、包裝別名安裝、hook 檔建立,皆以 ``(或 `# 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 的每個接線模式各跑一次,非零退出即為錯誤(例外有兩個:`sdlc-gate.sh check` 的 exit 2 是階段鎖的設計行為,`comment-scope.sh` 掃描模式的 exit 2 是掃到違規註解的設計行為——`sweep` 在髒工作區本來就會回 2,不算 hook 壞掉),以 `status=ok\|failed` 回報。`status {cli}` 是唯讀盤點:只讀設定檔判斷標記段落在不在,不寫檔也不執行 hook,每個接線點印一行 `item{項目}{路徑}{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` 回報 | ## 失敗回報範本 @@ -70,7 +79,7 @@ Claude 由 `hooks/hooks.json` 自動接線六支 hook;其他 CLI 用 `hooks-in ### `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`),語言規則仍重寫到各自的規則檔(以 `` 標記整段取代,等同先移除再重裝,不重複追加)。codex、copilot、antigravity、kiro 的 SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 SDLC 技能直接呼叫 `sdlc-gate.sh lock` 寫入;這四個 CLI 沒有 pre-tool hook,版本前置檢查接不上,也沒有 post-tool hook,`comment-scope.sh` 只接得到規則提示、接不上寫檔後的自動掃描,腳本會在 `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` 自動接線,無需寫入),接著 `tools/wire-cli.sh smoke {cli}` 驗執行期,最後 `tools/scan-hook-errors.sh --cli {cli}` 掃原生紀錄。codex、copilot、antigravity 由接線腳本裝上 `tools/jsc-wrap.sh` 包裝別名補上計時與用量回填(結束時自動跑 `tools/scan-logs.sh`),語言規則仍重寫到各自的規則檔(以 `` 標記整段取代,等同先移除再重裝,不重複追加)。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` @@ -89,7 +98,7 @@ Claude 由 `hooks/hooks.json` 自動接線六支 hook;其他 CLI 用 `hooks-in | `JSC_VERSION_GUARD` | 設 `off` 完全略過版本前置檢查(離線工作用) | 啟用檢查 | | `JSC_VERSION_TTL` | 遠端版本查詢的快取秒數 | 預設 600 | | `JSC_WP_GATE` | 設 `off` 完全略過工作包 PR 閘門(`wp-check` 一律放行) | 啟用閘門 | -| `JSC_COMMENT_SCOPE` | 設 `off` 完全略過註解範圍檢查(`comment-scope.sh` 兩種模式都直接結束) | 啟用檢查 | +| `JSC_COMMENT_SCOPE` | 設 `off` 完全略過註解範圍檢查(`comment-scope.sh` 三種模式都直接結束) | 啟用檢查 | | `JSC_CHANGED_FILE` | 非 Claude CLI 要掃描的檔案路徑,代替 stdin JSON 的 `file_path`,供 `comment-scope.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`,再不行就安靜降級 | diff --git a/skills/hooks-install/SKILL.md b/skills/hooks-install/SKILL.md index d5b910f..b3ceabc 100644 --- a/skills/hooks-install/SKILL.md +++ b/skills/hooks-install/SKILL.md @@ -10,7 +10,17 @@ Goal: make the six hooks (`ste100-guard.sh`, `session-timer.sh`, `skill-usage.sh Install on a clean slate. Every CLI is purged of all hooks first, third-party ones included, so a later failure has exactly one owner. `tools/wire-cli.sh purge` backs up every file it touches before it removes anything, so the removal stays reversible. Only claude has PreToolUse, PostToolUse and UserPromptSubmit, so only claude reports `wired`. On codex, copilot, antigravity and kiro the version guard cannot be wired at all and the SDLC gate degrades to the skill-step check, so all four report `degraded` — report that gap as the script words it instead of implying every CLI is covered. -`comment-scope.sh` degrades the same way on those four: they have no post-tool hook, so only its `prompt` mode reaches them, written into the same rule file as the STE100 block. The file is never scanned after a write there, and a comment that already carries an issue number is caught only by `jsc-review:code-review`. Say that plainly — a rule reminder is not the same protection as the scan claude gets. + +`comment-scope.sh` now reaches all five, but on a different event and at a different moment each. Report the timing per CLI; never state it as one uniform behaviour: + +| CLI | Scanning moment | Wired through | +| --- | --- | --- | +| claude | Per file, the instant it is written | PostToolUse | +| codex | End of every turn, over the whole git worktree | `notify` in `config.toml` | +| kiro | On every prompt submit, over the whole git worktree — it sees what the previous turn wrote | `userPromptSubmit` in `.kiro/hooks/jsc-hooks.json` | +| copilot, antigravity | Once, when the session ends | `tools/jsc-wrap.sh` teardown | + +The `sweep` mode reads `git diff HEAD`, so its coverage matches what claude sees; only the feedback delay differs. Outside a git worktree `sweep` exits 0 in silence and nothing is scanned at all — say so when the user works outside git. The `prompt` rule reminder still goes into every rule file alongside the STE100 block, because a warning that arrives a turn late is worth less than not writing the comment in the first place. The lock file still works on those four because the SDLC skills call `sdlc-gate.sh lock {stage}` directly — that call is where the capability-tag comparison happens, so the gate keeps its force even where the prompt hook cannot be wired. The gate needs `$JSC_HOME/model-tags.tsv`; when it is missing, report that `jsc-cli:models` (or `jsc-cli/tools/model-tags.sh sync`) must run once, because `sdlc-gate.sh lock` refuses to lock without it. @@ -34,7 +44,8 @@ The detailed flow **MUST run as a sub agent**; the main agent only reports the s - `session-timer.sh` takes `start` (keep an existing start time), `restart` (always overwrite it, for a CLI with no session id — kiro), `mark` and `report`. `wire-cli.sh` picks the right one per CLI; do not hand-edit the generated hook files. - `purge` reaches the user-level config only. Hooks that another plugin ships in its own `hooks.json` stay active, and uninstalling that plugin is the only way to clear them — say so when reporting, and treat their errors as third-party. - Backups land in `$JSC_HOME/backup/hooks/{cli}/{yyyyMMdd_HHmmss}/`, one directory per purge run, under the original file names. Hand that path to the user whenever a purge removed something. -- `smoke` treats `sdlc-gate.sh check` exit 2 as healthy: that exit is the stage lock blocking a turn on purpose, not a runtime error. `comment-scope.sh` exit 2 counts as healthy for the same reason — it means the scan found a comment and warned about it. With no file name to scan the hook exits 0 in silence, which is what smoke normally sees. -- `comment-scope.sh` takes `prompt` (inject the rule summary at UserPromptSubmit) and no argument at all (scan the file just written at PostToolUse, reading `file_path` from stdin JSON or `JSC_CHANGED_FILE`). It scans only the lines a diff added, skips markdown and binary files, and turns off entirely with `JSC_COMMENT_SCOPE=off`. The rule text itself lives in one place only, `jsc-review`'s `references/comment-scope.md`; never restate the list anywhere in this repo. +- `smoke` treats `sdlc-gate.sh check` exit 2 as healthy: that exit is the stage lock blocking a turn on purpose, not a runtime error. `comment-scope.sh` exit 2 counts as healthy for the same reason — it means the scan found a comment and warned about it. The no-argument mode has no file name during smoke and exits 0 in silence; `sweep` depends on the worktree it runs in, so it answers 2 whenever that worktree happens to carry an offending comment. Neither is a broken hook. +- `comment-scope.sh` takes three modes: `prompt` (inject the rule summary at UserPromptSubmit), no argument at all (scan the file just written at PostToolUse, reading `file_path` from stdin JSON or `JSC_CHANGED_FILE`), and `sweep [dir]` (scan every file the git worktree changed, for the four CLIs with no post-tool hook). All scanning modes read only the lines a diff added, skip markdown and binary files, and turn off entirely with `JSC_COMMENT_SCOPE=off`. The rule text itself lives in one place only, `jsc-review`'s `references/comment-scope.md`; never restate the list anywhere in this repo. +- `jsc-wrap.sh` runs `sweep` after the CLI exits and always returns the CLI's own exit code. A `sweep` hit warns on stderr and changes nothing else — never let a comment warning turn a successful CLI run into a failed one. - `tools/report-error.sh` is operator- or skill-invoked only. Never wire it to fire from a failing hook: hooks stay silent and exit 0, and a failing hook that reports itself can loop. - Data lands in `$JSC_HOME` (default `~/.jsc`), consumed by `jsc-log:worklog` and `jsc-log:stats`. -- 2.53.0 From 542f0aab2c20fe3040228ba45cf9491baef44226 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 27 Aug 2026 09:20:44 +0800 Subject: [PATCH 10/16] =?UTF-8?q?feat(manifest):=20=E4=B8=89=E4=BB=BD=20ma?= =?UTF-8?q?nifest=20=E7=89=88=E6=9C=AC=E5=8D=87=E5=88=B0=200.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:把 plugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json 三份 manifest 的版本由 0.1.9 升到 0.2.0。 Why:0.1.9 已隨前一支 PR 發佈出去,本次新增的 sweep 模式與四支 CLI 的接線 沒有跟著升版,version-guard.sh 就看不出本機落後,使用者不會收到更新提示。 How:以 jsc-meta 的 sync-skill-manifest.sh 統一 bump,minor 進位、patch 歸零, 三份同步成同一個值。 Who:註解範圍檢查 hook 的跨 CLI 支援。 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index dcf3007..c7e0c3a 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.1.9", + "version": "0.2.0", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 13f0784..440650e 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.1.9", + "version": "0.2.0", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills" } diff --git a/plugin.json b/plugin.json index f6c6bfe..b014c28 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.1.9", + "version": "0.2.0", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills/" } -- 2.53.0 From e3a9c67781b565709900d922ac54fd9d633a6d79 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 27 Aug 2026 09:49:25 +0800 Subject: [PATCH 11/16] =?UTF-8?q?feat(lang-guard):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=AC=AC=E4=B8=83=E6=94=AF=20hook=EF=BC=8C=E5=BC=B7=E5=88=B6?= =?UTF-8?q?=E9=9D=9E=E7=A8=8B=E5=BC=8F=E7=A2=BC=E8=BC=B8=E5=87=BA=E7=B9=81?= =?UTF-8?q?=E4=B8=AD=E7=84=A1=E4=BA=82=E7=A2=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:新增 `hooks/lang-guard.sh` 與機檢字表 `hooks/simplified.txt`。腳本比照 `comment-scope.sh` 的結構,一樣三種模式:`prompt` 注入規則摘要、無參數掃剛寫入的單一檔案、`sweep [dir]` 掃整個 git 工作區這次改過的檔案。偵測三項:簡體字、亂碼(U+FFFD 與雙重編碼殘骸)、非 UTF-8 編碼。命中走 stderr 並 exit 2,資料不足或找不到 git 一律安靜 exit 0,逃生門 `JSC_LANG_GUARD=off`。 Why:使用者新增的規則是「非程式碼的輸出一律套用繁中無亂碼」,範圍涵蓋程式碼註解、commit 訊息、PR 描述、wiki 頁、對使用者的回報與各種文件。這條規則原本只有 `ste100-guard.sh` 的提示層,模型看得到卻沒有人檢查,寫出簡體字或亂碼不會有任何回饋。提示層擋不住的事,就要有機檢層。 How:簡體字樣式由字表組出,字表是單一真實來源,腳本裡不留第二份;字表讀不到就安靜跳過這一項,不中斷整支腳本。亂碼一律用位元組比對(`LC_ALL=C`),不靠語系的字元範圍,因為 `grep -E` 的字元範圍在不同語系下行為不一致。掃描深度沿用 `comment-scope.sh`:檔案已追蹤就只掃 `git diff HEAD` 的新增行,不翻舊帳。二進位檔只認 NUL 位元組判定,不拿「非可列印字元」當判準,那會把所有含中文的檔案誤判成二進位。字表刻意排除繁體也在用的字(后、台、干、只、里、面、制、志),並跳過 `simplified.txt`、`ste100-guard.sh`、`lang-guard.sh` 三份以簡體字與亂碼為討論對象的檔案,避免整支 hook 每次先抓到自己。 Who:`jsc-hooks` 的 hook 實作層,供 `hooks/hooks.json` 與 `tools/wire-cli.sh` 接線給五個 CLI 使用。 --- hooks/lang-guard.sh | 176 +++++++++++++++++++++++++++++++++++++++++++ hooks/simplified.txt | 109 +++++++++++++++++++++++++++ 2 files changed, 285 insertions(+) create mode 100755 hooks/lang-guard.sh create mode 100644 hooks/simplified.txt diff --git a/hooks/lang-guard.sh b/hooks/lang-guard.sh new file mode 100755 index 0000000..2954892 --- /dev/null +++ b/hooks/lang-guard.sh @@ -0,0 +1,176 @@ +#!/usr/bin/env sh +# lang-guard.sh — 非程式碼輸出一律繁體中文、UTF-8、無亂碼、無簡體字(hook > prompt 的強制層)。 +# 規則正文的唯一來源:jsc-meta 的 references/ste100.md。本腳本只實作可用樣式判定的三項: +# 簡體字、亂碼、非 UTF-8 編碼;用詞、標點、語氣那些判不出來的交給 ste100-guard.sh 的提示層。 +# +# 用法: +# lang-guard.sh prompt 注入規則摘要(UserPromptSubmit 或規則檔取文字用) +# lang-guard.sh 掃描剛寫入的單一檔案(PostToolUse) +# lang-guard.sh sweep [dir] 掃描整個工作區這次改過的所有檔案(沒有 post-tool hook 的 CLI 用) +# +# 為什麼要有 sweep:只有 claude 接得到 PostToolUse,逐檔精準掃得到。codex 只有每輪結束的 +# notify、kiro 只有 userPromptSubmit、copilot 與 antigravity 只有包裝別名,這四個都拿不到 +# 「剛剛寫了哪個檔」,只能改成掃整個工作區的 git diff。時機晚一點,涵蓋範圍一樣。 +# +# 輸入相容: +# Claude: PostToolUse 的 stdin JSON,取 tool_input.file_path。 +# 其他 CLI: 環境變數 JSC_CHANGED_FILE。 +# 兩者都取不到就安靜降級(exit 0)。 +# +# 掃描範圍跟 comment-scope.sh 有兩點刻意不同,不要照抄那支的判斷: +# 1. 三項檢查都掃整個檔案,不是只掃註解行。程式碼的任何位置都不該出現簡體字或亂碼—— +# 字串常值、識別字、資料內容一樣算輸出。comment-scope.sh 只掃註解,是因為它抓的是 +# 註解夾帶文件編號,那件事只發生在註解裡。 +# 2. markdown 與純文字檔要掃。它們正是「非程式碼輸出」的主場:README、wiki 頁、PR 描述、 +# commit 訊息都是這類檔案。comment-scope.sh 刻意跳過 .md,因為那裡沒有程式碼註解。 +# +# 掃描深度:檔案在 git 工作區內且已追蹤,就只掃 `git diff HEAD` 的新增行,不翻舊帳; +# 不在 git 內或檔案尚未追蹤才整檔掃描。sweep 一律只看 git diff。 +# +# 結束碼:0=沒命中或資料不足;2=命中,訊息走 stderr 交回模型自行修正(不擋寫入,檔案已經寫好了)。 +# 逃生門:JSC_LANG_GUARD=off。 +set -u + +. "$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/lib.sh" 2>/dev/null || true + +[ "${JSC_LANG_GUARD:-on}" = "off" ] && exit 0 + +HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +WORDLIST="$HERE/simplified.txt" + +if [ "${1:-}" = "prompt" ]; then + echo "[jsc] 所有非程式碼輸出一律繁體中文、UTF-8、無亂碼、無簡體字。適用範圍:程式碼註解、commit 訊息、PR 描述與標題、wiki 頁、對使用者的回報、README 與各種文件、錯誤訊息與日誌文字。程式碼本身的關鍵字、識別字、API 欄位名維持原樣,但其中的中文一樣要是繁體。" + echo "[jsc] 送出前自我檢查:1)簡體字(例:应、为、这、说、发、国)一律換成繁體;2)替代字元「U+FFFD」與雙重編碼亂碼(Ã、â 開頭的怪序列)代表編碼壞掉,重寫那段而不是保留;3)檔案一律存成 UTF-8,不加 BOM。規則正文見 jsc-meta 的 references/ste100.md。" + exit 0 +fi + +# 簡體字樣式:由 hooks/simplified.txt 組出,字表是單一真實來源,腳本裡不留第二份。 +# 讀不到字表就回傳 1,呼叫端安靜跳過這一項,不中斷整支腳本——少抓一項,好過整支 hook 死掉。 +simplified_pattern() { + [ -f "$WORDLIST" ] || return 1 + _p=$(sed -e 's/#.*//' -e 's/[[:space:]]//g' "$WORDLIST" 2>/dev/null \ + | grep -v '^$' | tr '\n' '|' | sed 's/|$//') + [ -n "$_p" ] || return 1 + printf '%s' "$_p" +} + +# 亂碼樣式一律用位元組比對(LC_ALL=C),不靠語系的字元範圍: +# 替代字元 U+FFFD(EF BF BD)本身; +# � ——「U+FFFD 的 UTF-8 位元組再被當成 Latin-1 讀一次」的雙重編碼殘骸; +# U+00C0–U+00FF 的字後面緊接 U+0080–U+00BF 的字(ä、æ¸、è©、ç”),這是 UTF-8 被當成 +# Latin-1 讀一次再存回 UTF-8 的固定長相。中文的 UTF-8 前導位元組落在 E4–E9,被誤讀後 +# 就變成 ä–é 開頭、後面接 U+0080–U+00BF 的兩三個字,所以前導字要收整個 Latin-1 字母段, +# 只收「Ã」會漏掉最常見的中文亂碼; +# â 後面接任何非 ASCII 字(’、“),引號與破折號被雙重編碼時的典型長相。 +# 用位元組比對是因為 grep -E 的字元範圍在不同語系下行為不一致,位元組範圍到哪都一樣。 +# 誤報防線:正常的西歐文字(câmara、crème、naïve)重音字後面接的是 ASCII 字母,不會命中。 +mojibake_pattern() { + _fffd=$(printf '\357\277\275') + _lo=$(printf '\200'); _hi=$(printf '\277') + _c3=$(printf '\303'); _c2=$(printf '\302') # U+00C0–U+00FF 與 U+0080–U+00BF 的前導位元組 + _bx=$(printf '\303\242') # 「â」 + _l2=$(printf '\302'); _h2=$(printf '\364') + printf '%s|%s|%s[%s-%s]%s[%s-%s]|%s[%s-%s]' \ + "$_fffd" '�' \ + "$_c3" "$_lo" "$_hi" "$_c2" "$_lo" "$_hi" \ + "$_bx" "$_l2" "$_h2" +} + +hit() { # $1=樣式 $2=說明;命中就把說明與最多三行證據印到 stdout + m=$(printf '%s\n' "$lines" | LC_ALL=C grep -nE "$1" 2>/dev/null | head -n 3) + [ -n "$m" ] || return 0 + printf ' %s\n' "$2" + printf '%s\n' "$m" | sed 's/^/ /' +} + +scan_file() { # $1=檔案路徑;命中就把報告印到 stdout 並回傳 1,沒命中回傳 0 + f=$1 + [ -f "$f" ] || return 0 + + # 產生檔與壓縮輸出跳過:內容不是人寫的,抓到也沒有人要改。 + case "$f" in + *.lock|*.min.js|*.min.css|*.map) return 0 ;; + esac + # 字表與兩支語言規則腳本跳過:這幾份檔案裡的簡體字與亂碼樣本是「被討論的對象」, + # 不是被使用。同一個道理,jsc-meta 的 ste100-lint.sh 也跳過 references/ste100.md。 + # 不跳過的話,這支 hook 每次都會先抓到自己,訊號全被自己的噪音蓋掉。 + case "$f" in + */simplified.txt|simplified.txt) return 0 ;; + */ste100-guard.sh|ste100-guard.sh) return 0 ;; + */lang-guard.sh|lang-guard.sh) return 0 ;; + esac + # 二進位檔跳過。只認 NUL 位元組——拿「非可列印字元」當判準會把所有含中文的檔案誤判成二進位。 + raw=$(head -c 1024 "$f" 2>/dev/null | wc -c) + txt=$(head -c 1024 "$f" 2>/dev/null | LC_ALL=C tr -d '\000' | wc -c) + [ "$raw" = "$txt" ] || return 0 + + d=$(dirname -- "$f") + if git -C "$d" rev-parse --is-inside-work-tree >/dev/null 2>&1 && + git -C "$d" ls-files --error-unmatch -- "$f" >/dev/null 2>&1; then + lines=$(git -C "$d" diff HEAD -- "$f" 2>/dev/null | sed -n 's/^+[^+]/&/p' | cut -c2-) + [ -n "$lines" ] || return 0 + else + lines=$(cat "$f" 2>/dev/null) + fi + + out=$( + _sp=$(simplified_pattern) && hit "$_sp" '簡體字,改成繁體' + hit "$(mojibake_pattern)" '亂碼:替代字元或雙重編碼殘骸,重寫這一段' + # 編碼檢查沒有行號可指,命中就整檔報一行。沒有 iconv 就跳過這一項。 + if command -v iconv >/dev/null 2>&1; then + printf '%s\n' "$lines" | iconv -f UTF-8 -t UTF-8 >/dev/null 2>&1 \ + || printf ' %s\n' '非 UTF-8 編碼,整檔轉存成 UTF-8(不加 BOM)' + fi + ) + + [ -n "$out" ] || return 0 + printf '%s\n' "$f" + printf '%s\n' "$out" + return 1 +} + +advice() { + printf ' 修法:簡體字換成對應繁體字;亂碼那段重打,不要留著半壞的字元;檔案存成 UTF-8。\n' + printf ' 規則正文見 jsc-meta 的 references/ste100.md,字表在 hooks/simplified.txt。誤判時用 JSC_LANG_GUARD=off 關閉。\n' +} + +if [ "${1:-}" = "sweep" ]; then + target=${2:-.} + [ -d "$target" ] || exit 0 + root=$(git -C "$target" rev-parse --show-toplevel 2>/dev/null) || exit 0 + [ -n "$root" ] || exit 0 + changed=$(git -C "$root" diff --name-only HEAD 2>/dev/null) + [ -n "$changed" ] || exit 0 + + # 報告累積在暫存檔:迴圈跑在管線的子行程裡,變數帶不回來。 + tmp=${TMPDIR:-/tmp}/jsc-lang-guard.$$ + : > "$tmp" 2>/dev/null || exit 0 + printf '%s\n' "$changed" | while IFS= read -r rel; do + [ -n "$rel" ] || continue + scan_file "$root/$rel" >> "$tmp" 2>/dev/null + done + if [ -s "$tmp" ]; then + { + printf '[jsc] 工作區有簡體字、亂碼或編碼問題,請就地修正:\n' + sed 's/^/ /' "$tmp" + advice + } >&2 + rm -f "$tmp" + exit 2 + fi + rm -f "$tmp" + exit 0 +fi + +read_stdin 2>/dev/null || STDIN_JSON="" +file=$(json_str file_path 2>/dev/null || true) +[ -n "$file" ] || file="${JSC_CHANGED_FILE:-}" +[ -n "$file" ] || exit 0 + +report=$(scan_file "$file") && exit 0 +{ + printf '[jsc] 這個檔案有簡體字、亂碼或編碼問題,請就地修正:\n' + printf '%s\n' "$report" + advice +} >&2 +exit 2 diff --git a/hooks/simplified.txt b/hooks/simplified.txt new file mode 100644 index 0000000..5ccc5ab --- /dev/null +++ b/hooks/simplified.txt @@ -0,0 +1,109 @@ +# simplified.txt — 機檢用的簡體字表,一行一個字,UTF-8。 +# 用途:hooks/lang-guard.sh 讀這份字表組出 grep 樣式,判定非程式碼輸出有沒有夾帶簡體字。 +# 這是本存取庫的單一真實來源,jsc-meta 的 tools/ste100-lint.sh 也優先讀這份。 +# 排除原則:只收簡化後才出現的字形,刻意排除繁體也在用的字(后、台、干、只、里、面、制、志)。 +# 加進那些字會讓「太后」「電視台」「干涉」「只有」「公里」「面板」「制度」「志工」全部誤報。 +# 增刪字元前先想清楚:這份字表寧可漏抓,也不可誤報。 +# `#` 開頭的行與空行由讀取端忽略。 +应 +为 +这 +说 +发 +国 +过 +对 +开 +关 +问 +题 +东 +车 +马 +鸟 +龙 +飞 +见 +无 +产 +业 +务 +书 +写 +学 +习 +报 +纸 +认 +识 +证 +际 +网 +络 +计 +划 +实 +现 +给 +条 +约 +级 +组 +织 +变 +换 +电 +脑 +两 +双 +单 +构 +价 +钱 +众 +议 +论 +传 +统 +么 +儿 +们 +从 +来 +时 +间 +长 +门 +闻 +声 +员 +图 +团 +转 +输 +达 +运 +进 +远 +连 +边 +还 +经 +该 +营 +规 +则 +范 +围 +参 +数 +类 +结 +态 +设 +备 +辑 +译 +码 +库 +档 -- 2.53.0 From bbad6566f7aa0173f3446433639c36ccd59eaf3a Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 27 Aug 2026 09:49:53 +0800 Subject: [PATCH 12/16] =?UTF-8?q?feat(wire-cli):=20=E6=8A=8A=20lang-guard.?= =?UTF-8?q?sh=20=E6=8E=A5=E5=88=B0=E4=BA=94=E5=80=8B=20CLI=20=E7=9A=84?= =?UTF-8?q?=E6=97=A2=E6=9C=89=E6=99=82=E6=A9=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:`hooks/hooks.json` 的 `UserPromptSubmit` 加 `lang-guard.sh prompt`、`PostToolUse` 的 `Write|Edit|MultiEdit` 加無參數模式;`tools/wire-cli.sh` 把 codex 的 `notify`、kiro 的 `userPromptSubmit` 串上 sweep,規則檔文字加上 `lang-guard.sh prompt` 的輸出,`smoke` 三種模式各跑一輪,`status` 補盤點項,寫入後驗證一併涵蓋;`tools/jsc-wrap.sh` 收尾再跑一次 sweep;`hooks/ste100-guard.sh` 的注入文字補上適用範圍與 UTF-8 無亂碼。 Why:hook 寫好了不接線等於沒寫。接的位置與 `comment-scope.sh` 完全一樣,因為兩支要解的是同一個問題——只有 claude 有 post-tool hook,其餘四個 CLI 拿不到「剛剛寫了哪個檔」,只能改掃整個工作區。各 CLI 的掃描時機本來就不同:claude 逐檔即時、codex 每輪結束、kiro 每輪提示送出時、copilot 與 antigravity 只有工作階段結束,回報時不能寫成五支一樣。 How:規則檔文字取 `lang-guard.sh prompt` 的實際輸出,不在接線腳本裡抄一份規則,抄了兩邊就會各自漂移。寫入後以 `has_lang_guard()` 比對腳本第一行實際輸出,確認規則檔真的收到那一段。`smoke` 沿用既有的 exit 2 例外:掃描模式掃到違規回 2 是設計行為,不是執行期錯誤。`jsc-wrap.sh` 的 sweep 一律加 `|| true`,包裝器原樣回傳 CLI 自己的結束碼——包裝器改掉結束碼,呼叫端的 `cmd && next` 就會誤判。 Who:`jsc-hooks` 的接線層,由 `hooks-install` 技能對 claude、codex、copilot、antigravity、kiro 五個 CLI 執行。 --- hooks/hooks.json | 8 +++ hooks/ste100-guard.sh | 2 +- tools/jsc-wrap.sh | 6 +- tools/wire-cli.sh | 163 +++++++++++++++++++++++++++++------------- 4 files changed, 129 insertions(+), 50 deletions(-) diff --git a/hooks/hooks.json b/hooks/hooks.json index 9f353b6..2e0e750 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -28,6 +28,10 @@ { "type": "command", "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/comment-scope.sh\" prompt" + }, + { + "type": "command", + "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/lang-guard.sh\" prompt" } ] } @@ -83,6 +87,10 @@ { "type": "command", "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/comment-scope.sh\"" + }, + { + "type": "command", + "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/lang-guard.sh\"" } ] } diff --git a/hooks/ste100-guard.sh b/hooks/ste100-guard.sh index 171d4ee..88a5da6 100755 --- a/hooks/ste100-guard.sh +++ b/hooks/ste100-guard.sh @@ -3,6 +3,6 @@ # Claude: UserPromptSubmit 的 stdout 會成為額外 context。 # 其他 CLI: 由 hooks-install 以各自的規則檔(AGENTS.md 等)落地,本腳本仍可被 wrapper 呼叫。 # 完整規則的唯一來源:jsc-meta 的 references/ste100.md。 -echo "[jsc] 輸出規則:STE100 繁體中文,擬人台灣感。短句、一句一指令、主動語態、術語一致;台灣用語(預設、支援、相容、資訊);全形標點;去 AI 味(不用「總的來說」「首先/其次/最後」開場收尾套路、不諂媚);直接講重點,UTF-8 無亂碼。完整規則見 jsc-meta 的 references/ste100.md。" +echo "[jsc] 輸出規則:STE100 繁體中文,擬人台灣感。適用範圍是所有非程式碼輸出——程式碼註解、commit 訊息、PR 描述、wiki 頁、對使用者的回報、README 與各種文件都算。短句、一句一指令、主動語態、術語一致;台灣用語(預設、支援、相容、資訊);全形標點;去 AI 味(不用「總的來說」「首先/其次/最後」開場收尾套路、不諂媚);直接講重點。一律 UTF-8 無亂碼、無簡體字,檔案不加 BOM。完整規則見 jsc-meta 的 references/ste100.md。" echo "[jsc] 送出前自我檢查,命中任一項就先改再送出:1)簡體字(例:应、为、这、说、后、发);2)句尾用半形標點(. , ! ?),應為全形(。,!?);3)套路句(「總的來說」「綜上所述」「首先…其次…最後」);4)中文並列用半形「/」,應改頓號「、」;5)諂媚開場(「好問題」「當然可以」)。" exit 0 diff --git a/tools/jsc-wrap.sh b/tools/jsc-wrap.sh index 74290a9..e379865 100755 --- a/tools/jsc-wrap.sh +++ b/tools/jsc-wrap.sh @@ -3,7 +3,8 @@ # 用法: jsc-wrap.sh {cli} [args...] # 行為: 匯出 JSC_CLI 與 JSC_SESSION_ID → session-timer start → 執行 CLI → # 結束後 session-timer mark、以 scan-logs.sh 回填用量、以 comment-scope.sh sweep -# 掃一次整個工作區的註解範圍,最後回傳 CLI 的結束碼。 +# 掃一次整個工作區的註解範圍,再以 lang-guard.sh sweep 掃一次繁中與編碼, +# 最後回傳 CLI 的結束碼。 # 注意: JSC_CLI 存的是 CLI 代號(antigravity、kiro),實際執行的是 cli_bin 對應的 # 執行檔(agy、kiro-cli)。直接拿代號當指令跑會 127,因為沒有這兩個執行檔。 HERE=$(cd "$(dirname "$0")" && pwd) @@ -32,4 +33,7 @@ sh "$HERE/scan-logs.sh" --cli "$cli" --session "$JSC_SESSION_ID" /dev/null | sed '/^exit /d'; } # 規則正文不在這裡抄一份:抄了就會跟腳本各自漂移,兩邊講的規則對不起來。 comment_scope_text() { sh "$HOOKS/comment-scope.sh" prompt 2>/dev/null | sed '/^exit /d'; } -# 寫進規則檔的完整段落:語言規則加註解範圍規則,共用同一組 jsc-hooks 標記。 -# 兩段合在一個標記段落裡,purge 與重跑接線都是整段處理,不必各自再記一組標記。 -rules_text() { ste100_text; comment_scope_text; } +# 繁中與編碼規則段落的唯一來源:lang-guard.sh prompt 的實際輸出。理由同上,不在這裡抄一份。 +lang_guard_text() { sh "$HOOKS/lang-guard.sh" prompt 2>/dev/null | sed '/^exit /d'; } + +# 寫進規則檔的完整段落:語言規則加註解範圍規則加繁中編碼規則,共用同一組 jsc-hooks 標記。 +# 三段合在一個標記段落裡,purge 與重跑接線都是整段處理,不必各自再記一組標記。 +rules_text() { ste100_text; comment_scope_text; lang_guard_text; } # 驗證:規則檔真的收到註解範圍那一段了嗎。比對字串取自腳本的第一行實際輸出, # 不是另外抄一句關鍵字——抄的關鍵字改腳本時不會跟著改,驗證就會永遠通過。 @@ -104,6 +112,14 @@ has_comment_scope() { grep -qF "$_first" "$1" 2>/dev/null } +# 驗證:規則檔真的收到繁中與編碼那一段了嗎。同樣取腳本的第一行實際輸出來比對,理由同上。 +# $1=檔案 +has_lang_guard() { + _first=$(lang_guard_text | head -n1) + [ -n "$_first" ] || return 1 + grep -qF "$_first" "$1" 2>/dev/null +} + # 以標記整段取代(冪等);標記不存在就在檔尾新增;檔案不存在就建立。 # 適用 markdown 規則檔與 shell rc 檔:這兩種檔案沒有「區段」概念,附在檔尾就對了。 # $1=檔案 $2=開頭標記行 $3=結尾標記行 $4=標記之間要寫入的內容 @@ -616,8 +632,8 @@ if [ "$action" = smoke ]; then _h="$1"; _s="${2:-}" # 技能名一律清空:冒煙要驗的是「沒有技能情境時腳本跑得完」。留著繼承來的 JSC_SKILL, # sdlc-gate.sh wp-check skill 會拿它當真實呼叫判定,有未結清 PR 時就誤報成執行期錯誤。 - # JSC_CHANGED_FILE 同理清空:留著繼承來的檔名,comment-scope.sh 會真的去掃那個檔, - # 掃到違規註解就 exit 2,冒煙測試變成看環境臉色,測不出腳本本身跑不跑得完。 + # JSC_CHANGED_FILE 同理清空:留著繼承來的檔名,comment-scope.sh 與 lang-guard.sh 會真的 + # 去掃那個檔,掃到違規就 exit 2,冒煙測試變成看環境臉色,測不出腳本本身跑不跑得完。 _out=$(printf '{}' | JSC_CLI="$cli" JSC_SKILL="" SKILL="" JSC_CHANGED_FILE="" \ sh "$HOOKS/$_h" $_s 2>&1); _rc=$? if [ "$_rc" -eq 0 ]; then @@ -632,6 +648,10 @@ if [ "$action" = smoke ]; then # 無參數模式冒煙時取不到檔名,正常會走 exit 0;sweep 則看工作區乾不乾淨——工作區剛好 # 有違規註解就回 2。那是 hook 正常工作,不是 hook 壞掉,不能因此判定接線失敗。 printf '[jsc] %s%s:exit 2,掃到違規註解並發出警告,屬設計行為,不算錯誤。\n' "$_h" "${_s:+ $_s}" >> "$smoke_out" + elif [ "$_h" = lang-guard.sh ] && [ "$_rc" -eq 2 ]; then + # 沿用同一條例外:lang-guard.sh 掃描模式的 exit 2 是「掃到簡體字、亂碼或編碼問題」的 + # 設計行為。sweep 一樣看工作區乾不乾淨,髒工作區本來就會回 2,不是 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" \ @@ -655,9 +675,14 @@ if [ "$action" = smoke ]; then smoke_one comment-scope.sh prompt smoke_one comment-scope.sh smoke_one comment-scope.sh sweep + # lang-guard.sh 同樣有三個接在不同事件的模式,三個都要驗,判定理由與 comment-scope.sh 相同: + # prompt 一律 exit 0,無參數模式取不到檔名時安靜 exit 0,sweep 在髒工作區回 2 由白名單放行。 + smoke_one lang-guard.sh prompt + smoke_one lang-guard.sh + smoke_one lang-guard.sh sweep if [ "$smoke_fails" -eq 0 ]; then - printf 'status=ok reason=%s\n' "六支 hook 的每個接線模式都跑得完,沒有執行期錯誤" + printf 'status=ok reason=%s\n' "七支 hook 的每個接線模式都跑得完,沒有執行期錯誤" cat "$smoke_out"; rm -f "$smoke_out"; exit 0 fi printf 'status=failed reason=%s\n' "$smoke_fails 支 hook 有執行期錯誤" @@ -694,6 +719,13 @@ if [ "$action" = status ]; then else st_item "$1" "$2" missing; fi } + # 繁中與編碼規則寫進規則檔了嗎。同樣與 STE100 共用標記段落,所以要單獨比對內容: + # 標記在、內容卻是舊版沒有這一段時,這一項才看得出來缺了。$1=項目名 $2=檔案 + st_lang_guard() { + if [ -f "$2" ] && has_lang_guard "$2"; then st_item "$1" "$2" present + else st_item "$1" "$2" missing; fi + } + # 別名段落只要任何一個既有 rc 檔帶有標記就算接上。$1=項目名 $2=標記名 st_rc_alias() { for _rc in "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.config/fish/config.fish"; do @@ -707,11 +739,14 @@ if [ "$action" = status ]; then claude) if [ -f "$HOOKS/hooks.json" ]; then st_item hooks.json "$HOOKS/hooks.json" present else st_item hooks.json "$HOOKS/hooks.json" missing; fi - # 六支 hook 全靠這一個檔宣告,只看檔案在不在會漏掉「檔在、某支沒接進去」。 - # 最後加進來的 comment-scope.sh 是最可能漏的一支,所以單獨列一項。 + # 七支 hook 全靠這一個檔宣告,只看檔案在不在會漏掉「檔在、某支沒接進去」。 + # 後來才加進來的 comment-scope.sh 與 lang-guard.sh 是最可能漏的兩支,所以各列一項。 if [ -f "$HOOKS/hooks.json" ] && grep -qF 'comment-scope.sh' "$HOOKS/hooks.json" 2>/dev/null then st_item comment-scope "$HOOKS/hooks.json" present - else st_item comment-scope "$HOOKS/hooks.json" missing; fi ;; + else st_item comment-scope "$HOOKS/hooks.json" missing; fi + if [ -f "$HOOKS/hooks.json" ] && grep -qF 'lang-guard.sh' "$HOOKS/hooks.json" 2>/dev/null + then st_item lang-guard "$HOOKS/hooks.json" present + else st_item lang-guard "$HOOKS/hooks.json" missing; fi ;; codex) config="${CODEX_HOME:-$HOME/.codex}/config.toml" @@ -729,22 +764,32 @@ if [ "$action" = status ]; then else st_item notify-sweep "$config" missing fi + # 兩支 sweep 各算一項:只驗其中一支會讓「舊版只接了註解範圍」看起來像接線完整 + if [ -f "$config" ] && grep -qF 'lang-guard.sh' "$config" 2>/dev/null && + grep -qF 'sweep' "$config" 2>/dev/null; then + st_item notify-lang-sweep "$config" present + else + st_item notify-lang-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 可接版本前置檢查,註解範圍改為每輪結束掃整個工作區,不是逐檔即時" ;; + st_lang_guard lang-guard "${CODEX_HOME:-$HOME/.codex}/AGENTS.md" + 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 可接版本前置檢查,註解範圍只在工作階段結束時掃一次整個工作區" ;; + st_lang_guard lang-guard "${JSC_COPILOT_INSTRUCTIONS:-$HOME/.config/copilot/copilot-instructions.md}" + 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 可接版本前置檢查,註解範圍只在工作階段結束時掃一次整個工作區" ;; + st_lang_guard lang-guard "${JSC_ANTIGRAVITY_RULES:-$HOME/.antigravity/AGENTS.md}" + st_degrade="STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍與繁中編碼只在工作階段結束時掃一次整個工作區" ;; kiro) # kiro 的 hook 檔綁在工作區,這裡看的一律是目前工作目錄底下那一份 @@ -762,7 +807,16 @@ if [ "$action" = status ]; then 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 可接版本前置檢查,註解範圍改為每輪提示送出時掃整個工作區,不是逐檔即時" ;; + # lang-guard.sh 的兩個模式同理各算一項 + if [ -f ./.kiro/hooks/jsc-hooks.json ] && + grep -qF 'lang-guard.sh\" prompt' ./.kiro/hooks/jsc-hooks.json 2>/dev/null + then st_item lang-guard ./.kiro/hooks/jsc-hooks.json present + else st_item lang-guard ./.kiro/hooks/jsc-hooks.json missing; fi + if [ -f ./.kiro/hooks/jsc-hooks.json ] && + grep -qF 'lang-guard.sh\" sweep' ./.kiro/hooks/jsc-hooks.json 2>/dev/null + then st_item lang-guard-sweep ./.kiro/hooks/jsc-hooks.json present + else st_item lang-guard-sweep ./.kiro/hooks/jsc-hooks.json missing; fi + st_degrade="SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍與繁中編碼改為每輪提示送出時掃整個工作區,不是逐檔即時" ;; esac if [ "$st_missing" -gt 0 ]; then @@ -773,7 +827,7 @@ if [ "$action" = status ]; then printf 'status=degraded reason=%s\n' "$st_degrade" cat "$st_items"; rm -f "$st_items"; exit 1 fi - printf 'status=wired reason=%s\n' "hooks.json 自動接線全部六支 hook" + printf 'status=wired reason=%s\n' "hooks.json 自動接線全部七支 hook" cat "$st_items"; rm -f "$st_items"; exit 0 fi @@ -784,9 +838,11 @@ case "$cli" in [ -f "$HOOKS/hooks.json" ] || fail "找不到 $HOOKS/hooks.json,claude 接不到任何 hook" grep -qF 'comment-scope.sh' "$HOOKS/hooks.json" 2>/dev/null \ || fail "$HOOKS/hooks.json 沒有接上 comment-scope.sh,註解範圍檢查不會生效" + grep -qF 'lang-guard.sh' "$HOOKS/hooks.json" 2>/dev/null \ + || fail "$HOOKS/hooks.json 沒有接上 lang-guard.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 的逐檔即時掃描只在這裡接得上;其他四個 CLI 改用 sweep 掃整個工作區,時機晚一輪或晚到工作階段結束。" + echo "[jsc] claude:由 hooks/hooks.json 自動接線全部七支 hook,無需寫入設定。" + echo "[jsc] claude:只有 claude 有 post-tool hook,comment-scope.sh 與 lang-guard.sh 的逐檔即時掃描只在這裡接得上;其他四個 CLI 改用 sweep 掃整個工作區,時機晚一輪或晚到工作階段結束。" exit 0 ;; codex) @@ -805,7 +861,8 @@ case "$cli" in # 寫過的檔一個都不會漏。 timer="sh '$HOOKS/session-timer.sh'" scope="sh '$HOOKS/comment-scope.sh'" - notify_line="notify = [\"env\", \"JSC_CLI=codex\", \"sh\", \"-c\", \"$timer start /dev/null \ || fail "$config 的 notify 沒有接到 comment-scope.sh sweep,codex 每輪結束不會掃註解範圍" + grep -qF "$lang sweep" "$config" 2>/dev/null \ + || fail "$config 的 notify 沒有接到 lang-guard.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 可接版本前置檢查,註解範圍改為每輪結束掃整個工作區,不是逐檔即時" + has_lang_guard "$agents" || fail "$agents 寫入後讀不到繁中與編碼規則" + 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,最後跑 comment-scope.sh sweep。" - echo "[jsc] codex:已在 $agents 寫入 STE100 與註解範圍規則段落(prompt 降級)。" + echo "[jsc] codex:已設定 $config 的 notify(根層鍵,已驗證),每輪補 session-timer.sh start 再 mark,最後跑 comment-scope.sh sweep 與 lang-guard.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:註解範圍除了規則提示,每輪結束會由 notify 掃一次整個 git 工作區(codex 沒有 post-tool hook,接不到逐檔即時掃描),回饋比 claude 晚一輪。" + echo "[jsc] codex:註解範圍與繁中編碼除了規則提示,每輪結束會由 notify 各掃一次整個 git 工作區(codex 沒有 post-tool hook,接不到逐檔即時掃描),回饋比 claude 晚一輪。" exit 1 ;; copilot) @@ -839,13 +899,14 @@ 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 可接版本前置檢查,註解範圍只在工作階段結束時掃一次整個工作區" + has_lang_guard "$instr" || fail "$instr 寫入後讀不到繁中與編碼規則" + 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:已在 $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:註解範圍除了規則提示,工作階段結束時由 jsc-wrap.sh 收尾掃一次整個 git 工作區(copilot 連逐輪事件都沒有),回饋要等到離開 CLI 才看得到。" + echo "[jsc] copilot:註解範圍與繁中編碼除了規則提示,工作階段結束時由 jsc-wrap.sh 收尾各掃一次整個 git 工作區(copilot 連逐輪事件都沒有),回饋要等到離開 CLI 才看得到。" exit 1 ;; antigravity) @@ -858,13 +919,14 @@ 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 可接版本前置檢查,註解範圍只在工作階段結束時掃一次整個工作區" + has_lang_guard "$rules" || fail "$rules 寫入後讀不到繁中與編碼規則" + 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:已在 $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:註解範圍除了規則提示,工作階段結束時由 jsc-wrap.sh 收尾掃一次整個 git 工作區(antigravity 連逐輪事件都沒有),回饋要等到離開 CLI 才看得到。" + echo "[jsc] antigravity:註解範圍與繁中編碼除了規則提示,工作階段結束時由 jsc-wrap.sh 收尾各掃一次整個 git 工作區(antigravity 連逐輪事件都沒有),回饋要等到離開 CLI 才看得到。" exit 1 ;; kiro) @@ -885,16 +947,16 @@ case "$cli" in "run": "sh \\"$HOOKS/session-timer.sh\\" restart "$hookfile" 2>/dev/null </dev/null \ || fail "$hookfile 的 run 沒有接到 comment-scope.sh sweep,kiro 每輪不會掃註解範圍" - printf 'status=degraded reason=%s\n' "SDLC 模型鎖降級為技能步驟檢查,無 pre-tool hook 可接版本前置檢查,註解範圍改為每輪提示送出時掃整個工作區,不是逐檔即時" + grep -qF 'lang-guard.sh' "$hookfile" 2>/dev/null || fail "$hookfile 的 run 沒有接到 lang-guard.sh" + grep -qF 'lang-guard.sh\" prompt' "$hookfile" 2>/dev/null \ + || fail "$hookfile 的 run 沒有接到 lang-guard.sh prompt,每輪不會注入繁中與編碼規則" + grep -qF 'lang-guard.sh\" sweep' "$hookfile" 2>/dev/null \ + || fail "$hookfile 的 run 沒有接到 lang-guard.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:註解範圍除了規則提示,每輪提示送出時會掃一次整個 git 工作區(kiro 沒有 post-tool hook),掃到的是上一輪寫的檔。" + echo "[jsc] kiro:註解範圍與繁中編碼除了規則提示,每輪提示送出時會各掃一次整個 git 工作區(kiro 沒有 post-tool hook),掃到的是上一輪寫的檔。" exit 1 ;; esac -- 2.53.0 From fe3c5723a5448f9699e9e0260769a2a5de24bd92 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 27 Aug 2026 09:49:53 +0800 Subject: [PATCH 13/16] =?UTF-8?q?feat(hooks-install):=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=88=B0=E4=B8=83=E6=94=AF=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:`README.md` 的 hook 一覽表新增 `lang-guard.sh` 一列(事件、三種模式、逃生門),所有「六支 hook」改七支,環境變數表補 `JSC_LANG_GUARD`,`tools/jsc-wrap.sh` 那列補收尾 sweep;`skills/hooks-install/SKILL.md` 六支改七支並補覆蓋範圍與備註,`description` 的括號清單加上這支;`AGENTS.md` 清單同步,並新增一條繁中無亂碼規則。 Why:文件寫六支、實際跑七支,使用者與 sub agent 都會照文件辦事,少接的那一支永遠沒有人發現。`AGENTS.md` 是各 CLI 讀得到的規則檔,新規則不寫進去就只剩 hook 在擋,模型自己不會知道。 How:README 維持 STE100 繁中;SKILL.md 維持整份英文,`description` 保持 4 句、保留觸發時機。規則正文一律不在本存取庫留副本,只指向 `jsc-meta` 的 `references/ste100.md`,字表位置指向 `hooks/simplified.txt`。 Who:`jsc-hooks` 的文件層,讀者是使用者與執行 `hooks-install` 的 sub agent。 --- AGENTS.md | 5 +++-- README.md | 18 ++++++++++-------- skills/hooks-install/SKILL.md | 15 ++++++++------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f0beeb2..5da8f18 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # jsc-hooks — 給 AI 助理的指引 -本 repo 是 jsc 技能組的 `hooks` domain(跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍檢查),可同時被 Claude Code / Codex / Copilot / Antigravity / Kiro 使用。 +本 repo 是 jsc 技能組的 `hooks` domain(跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍檢查、繁中與編碼檢查),可同時被 Claude Code / Codex / Copilot / Antigravity / Kiro 使用。 ## 規則 @@ -9,7 +9,8 @@ 3. 技能準則的唯一來源:`plugins/meta` 存取庫的 `references/guidelines.md`。 4. 所有 hook 只放在 `jsc-hooks`;gitea 操作一律經由 `jsc-gitea` 的 `tools/gitea.sh`;問使用者一律依 `jsc-ask:ask` 的決策樹規則。 5. 註解範圍規則正文的唯一來源:`jsc-review` 的 `references/comment-scope.md`。本存取庫只放 `hooks/comment-scope.sh` 的判定實作,不留規則清單副本,接線腳本要用規則文字時一律取腳本的實際輸出。`comment-scope.sh` 有 `prompt`、無參數逐檔掃描、`sweep` 掃整個 git 工作區三種模式;掃描時機每個 CLI 都不同(claude 逐檔即時、codex 每輪結束、kiro 每輪提示送出時、copilot 與 antigravity 只有工作階段結束時),談覆蓋範圍時一律據實分開講,不得寫成五支一樣。 -6. 主 agent 不需要處理細節的流程,一律建立 sub agent 處理。 +6. 所有非程式碼輸出一律繁體中文、UTF-8、無亂碼、無簡體字:程式碼註解、commit 訊息、PR 描述、wiki 頁、對使用者的回報、README 與各種文件都算。規則正文的唯一來源同樣是 `plugins/meta` 的 `references/ste100.md`,本存取庫只放 `hooks/lang-guard.sh` 的判定實作與 `hooks/simplified.txt` 的機檢字表。那份字表是本存取庫的單一真實來源,刻意排除繁體也在用的字(后、台、干、只、里、面、制、志),增刪前先確認不會製造誤報。`lang-guard.sh` 的三種模式與掃描時機跟 `comment-scope.sh` 一致,但它掃整個檔案而不只掃註解行,`.md` 與純文字檔也照掃。 +7. 主 agent 不需要處理細節的流程,一律建立 sub agent 處理。 ## 呼叫慣例 diff --git a/README.md b/README.md index 15dbf12..481c135 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,14 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安 | `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/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-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`,不是這裡。逃生門 `JSC_WP_GATE=off`。這道閘門只讀檔案、不打網路,PR 的真實合併狀態由 `jsc-sdlc/tools/wp-gate.sh` 查證 | -Claude 由 `hooks/hooks.json` 自動接線六支 hook;其他 CLI 用 `hooks-install` 技能接線、改裝包裝啟動器,或降級為規則檔。 +Claude 由 `hooks/hooks.json` 自動接線七支 hook;其他 CLI 用 `hooks-install` 技能接線、改裝包裝啟動器,或降級為規則檔。 -> 覆蓋範圍要據實看待:只有 claude 同時有 PreToolUse、PostToolUse 與 UserPromptSubmit,六支 hook 全接得上,回報 `wired`。codex、copilot、antigravity、kiro 都沒有 pre-tool hook,接不上 `version-guard.sh` 的版本前置檢查,SDLC 模型鎖也只剩技能步驟檢查,這四個 CLI 一律回報 `degraded`,靠 `/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` 的版本前置檢查,SDLC 模型鎖也只剩技能步驟檢查,這四個 CLI 一律回報 `degraded`,靠 `/jsc-cli:deploy` 定期更新。codex 另外沒有工作階段開始事件,計時改由 `tools/jsc-wrap.sh` 的 `codex` 別名在啟動當下開始;沒走別名啟動時,時間從第一輪回應算起。 -> `comment-scope.sh` 五個 CLI 都掃得到,但時機不同,不能當成五支一樣: +> `comment-scope.sh` 與 `lang-guard.sh` 五個 CLI 都掃得到,接的是同一批位置,但時機不同,不能當成五支一樣: | CLI | 掃描時機 | 接在哪裡 | | --- | --- | --- | @@ -42,7 +43,7 @@ Claude 由 `hooks/hooks.json` 自動接線六支 hook;其他 CLI 用 `hooks-in | kiro | 每輪提示送出時,掃整個 git 工作區(掃到的是上一輪寫的檔) | `.kiro/hooks/jsc-hooks.json` 的 `userPromptSubmit` | | copilot、antigravity | 工作階段結束時掃一次 | `tools/jsc-wrap.sh` 收尾 | -> `sweep` 看的是 `git diff HEAD`,涵蓋範圍與 claude 一樣,差的是回饋速度:claude 當下就叫,其他四個要等到該輪或該階段結束。不在 git 工作區內時 `sweep` 安靜 exit 0,等於沒掃。規則提示(`prompt` 模式)在五個 CLI 都照樣寫進規則檔,與 STE100 共用同一個標記段落——晚一輪的警告,價值仍低於一開始就不要寫。判不出來的項目(專案代號、客戶名稱)一律交給 `/jsc-review:code-review` 第 2 組。 +> 上表對 `comment-scope.sh` 與 `lang-guard.sh` 同時成立,兩支接在同一批位置。`sweep` 看的是 `git diff HEAD`,涵蓋範圍與 claude 一樣,差的是回饋速度:claude 當下就叫,其他四個要等到該輪或該階段結束。不在 git 工作區內時 `sweep` 安靜 exit 0,等於沒掃。規則提示(`prompt` 模式)在五個 CLI 都照樣寫進規則檔,三段(STE100、註解範圍、繁中編碼)共用同一個標記段落——晚一輪的警告,價值仍低於一開始就不要寫。判不出來的項目(專案代號、客戶名稱)一律交給 `/jsc-review:code-review` 第 2 組。 > `version-guard.sh report` 是非 hook 的子指令:印出每個已安裝 jsc plugin 的 > 「{domain} {本機} {遠端} {落後|最新|超前|查詢失敗}」,最後一行 `behind {落後個數}`。 @@ -55,10 +56,10 @@ 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` 掃整個 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/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`)標記整段重寫,重跑等同先移除再重裝;寫完每個檔案會重讀驗證位置正確才回報成功(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 的每個接線模式各跑一次,非零退出即為錯誤(例外有兩個:`sdlc-gate.sh check` 的 exit 2 是階段鎖的設計行為,`comment-scope.sh` 掃描模式的 exit 2 是掃到違規註解的設計行為——`sweep` 在髒工作區本來就會回 2,不算 hook 壞掉),以 `status=ok\|failed` 回報。`status {cli}` 是唯讀盤點:只讀設定檔判斷標記段落在不在,不寫檔也不執行 hook,每個接線點印一行 `item{項目}{路徑}{present\|missing}`,以 `status=wired\|degraded\|unwired\|skipped` 回報(結束碼 0、1、5、3)。體檢類技能(`/jsc-cli:doctor`)只能用這個子命令,另外三個都會動到環境 | +| `tools/wire-cli.sh` | 單一 CLI 的 hook 生命週期,共三個用法。`{cli}` 是接線:對應的設定編輯、包裝別名安裝、hook 檔建立,皆以 ``(或 `# 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 的每個接線模式各跑一次,非零退出即為錯誤(例外有三個:`sdlc-gate.sh check` 的 exit 2 是階段鎖的設計行為,`comment-scope.sh` 與 `lang-guard.sh` 掃描模式的 exit 2 是掃到違規的設計行為——`sweep` 在髒工作區本來就會回 2,不算 hook 壞掉),以 `status=ok\|failed` 回報。`status {cli}` 是唯讀盤點:只讀設定檔判斷標記段落在不在,不寫檔也不執行 hook,每個接線點印一行 `item{項目}{路徑}{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` 回報 | ## 失敗回報範本 @@ -79,7 +80,7 @@ Claude 由 `hooks/hooks.json` 自動接線六支 hook;其他 CLI 用 `hooks-in ### `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`),語言規則仍重寫到各自的規則檔(以 `` 標記整段取代,等同先移除再重裝,不重複追加)。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` 自動接線,無需寫入),接著 `tools/wire-cli.sh smoke {cli}` 驗執行期,最後 `tools/scan-hook-errors.sh --cli {cli}` 掃原生紀錄。codex、copilot、antigravity 由接線腳本裝上 `tools/jsc-wrap.sh` 包裝別名補上計時與用量回填(結束時自動跑 `tools/scan-logs.sh`),語言規則仍重寫到各自的規則檔(以 `` 標記整段取代,等同先移除再重裝,不重複追加)。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` @@ -99,7 +100,8 @@ Claude 由 `hooks/hooks.json` 自動接線六支 hook;其他 CLI 用 `hooks-in | `JSC_VERSION_TTL` | 遠端版本查詢的快取秒數 | 預設 600 | | `JSC_WP_GATE` | 設 `off` 完全略過工作包 PR 閘門(`wp-check` 一律放行) | 啟用閘門 | | `JSC_COMMENT_SCOPE` | 設 `off` 完全略過註解範圍檢查(`comment-scope.sh` 三種模式都直接結束) | 啟用檢查 | -| `JSC_CHANGED_FILE` | 非 Claude CLI 要掃描的檔案路徑,代替 stdin JSON 的 `file_path`,供 `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`,再不行就安靜降級 | diff --git a/skills/hooks-install/SKILL.md b/skills/hooks-install/SKILL.md index b3ceabc..41d6b68 100644 --- a/skills/hooks-install/SKILL.md +++ b/skills/hooks-install/SKILL.md @@ -1,17 +1,17 @@ --- name: hooks-install -description: Wire jsc hooks (STE100 guard, session timer, skill usage logger, SDLC model gate, plugin version guard, comment scope scanner) into every installed AI CLI, purging all pre-existing hooks first — third-party ones included, backed up before removal. Drive it per CLI through tools/wire-cli.sh purge, tools/wire-cli.sh, tools/wire-cli.sh smoke and tools/scan-hook-errors.sh. Hand any hook error, wiring or runtime, to jsc-hooks:repair, which must finish with a PR against develop; aborting the rest of the install to start that repair is allowed. Use after installing or updating the jsc plugin set; not for writing new hooks. +description: Wire jsc hooks (STE100 guard, session timer, skill usage logger, SDLC model gate, plugin version guard, comment scope scanner, language guard) into every installed AI CLI, purging all pre-existing hooks first — third-party ones included, backed up before removal. Drive it per CLI through tools/wire-cli.sh purge, tools/wire-cli.sh, tools/wire-cli.sh smoke and tools/scan-hook-errors.sh. Hand any hook error, wiring or runtime, to jsc-hooks:repair, which must finish with a PR against develop; aborting the rest of the install to start that repair is allowed. Use after installing or updating the jsc plugin set; not for writing new hooks. --- # hooks-install — wire jsc hooks into every installed CLI -Goal: make the six hooks (`ste100-guard.sh`, `session-timer.sh`, `skill-usage.sh`, `sdlc-gate.sh`, `version-guard.sh`, `comment-scope.sh`) effective in every CLI, with nothing else wired alongside them. +Goal: make the seven hooks (`ste100-guard.sh`, `session-timer.sh`, `skill-usage.sh`, `sdlc-gate.sh`, `version-guard.sh`, `comment-scope.sh`, `lang-guard.sh`) effective in every CLI, with nothing else wired alongside them. Install on a clean slate. Every CLI is purged of all hooks first, third-party ones included, so a later failure has exactly one owner. `tools/wire-cli.sh purge` backs up every file it touches before it removes anything, so the removal stays reversible. Only claude has PreToolUse, PostToolUse and UserPromptSubmit, so only claude reports `wired`. On codex, copilot, antigravity and kiro the version guard cannot be wired at all and the SDLC gate degrades to the skill-step check, so all four report `degraded` — report that gap as the script words it instead of implying every CLI is covered. -`comment-scope.sh` now reaches all five, but on a different event and at a different moment each. Report the timing per CLI; never state it as one uniform behaviour: +`comment-scope.sh` and `lang-guard.sh` both reach all five, wired at the same set of places, but on a different event and at a different moment each. Report the timing per CLI; never state it as one uniform behaviour: | CLI | Scanning moment | Wired through | | --- | --- | --- | @@ -20,7 +20,7 @@ Only claude has PreToolUse, PostToolUse and UserPromptSubmit, so only claude rep | kiro | On every prompt submit, over the whole git worktree — it sees what the previous turn wrote | `userPromptSubmit` in `.kiro/hooks/jsc-hooks.json` | | copilot, antigravity | Once, when the session ends | `tools/jsc-wrap.sh` teardown | -The `sweep` mode reads `git diff HEAD`, so its coverage matches what claude sees; only the feedback delay differs. Outside a git worktree `sweep` exits 0 in silence and nothing is scanned at all — say so when the user works outside git. The `prompt` rule reminder still goes into every rule file alongside the STE100 block, because a warning that arrives a turn late is worth less than not writing the comment in the first place. +The table above holds for both scanners. The `sweep` mode reads `git diff HEAD`, so its coverage matches what claude sees; only the feedback delay differs. Outside a git worktree `sweep` exits 0 in silence and nothing is scanned at all — say so when the user works outside git. Both `prompt` rule reminders still go into every rule file alongside the STE100 block, because a warning that arrives a turn late is worth less than not writing the offending text in the first place. The lock file still works on those four because the SDLC skills call `sdlc-gate.sh lock {stage}` directly — that call is where the capability-tag comparison happens, so the gate keeps its force even where the prompt hook cannot be wired. The gate needs `$JSC_HOME/model-tags.tsv`; when it is missing, report that `jsc-cli:models` (or `jsc-cli/tools/model-tags.sh sync`) must run once, because `sdlc-gate.sh lock` refuses to lock without it. @@ -33,7 +33,7 @@ The detailed flow **MUST run as a sub agent**; the main agent only reports the s 1. Run `jsc-cli/tools/detect-clis.sh`. Done when you hold the list of installed CLIs; when the list is empty, report that and stop. 2. For each installed CLI, run `tools/wire-cli.sh purge {cli}`. The script backs up every file it touches, removes all hooks, re-reads each file to confirm the removal, and restores the backup by itself when a check fails. Done when every CLI has printed exactly one `status=purged|skipped|failed reason=...` line and you have noted the backup directory path from its `[jsc]` output. 3. For each installed CLI, run `tools/wire-cli.sh {cli}`. The script owns both the wiring and its verification: it writes the config, alias or hook file inside a `` (or `# jsc-hooks`) marker block, re-reads every file it wrote, and confirms the block is present and correctly placed before it prints a success status. Trust its first line, `status=wired|degraded|skipped|failed reason=...`. Exit 2 means a bad CLI name, not a wiring outcome — fix the name and rerun. Done when every installed CLI has printed exactly one `status=` line and none exited 2. -4. For each installed CLI, run `tools/wire-cli.sh smoke {cli}`. This runs all six hooks once each, every wired mode included, and catches what the wiring check cannot see: a hook that is wired correctly and still fails when it executes. Done when every CLI has printed one `status=ok|failed reason=...` line plus one result line per hook. +4. For each installed CLI, run `tools/wire-cli.sh smoke {cli}`. This runs all seven hooks once each, every wired mode included, and catches what the wiring check cannot see: a hook that is wired correctly and still fails when it executes. Done when every CLI has printed one `status=ok|failed reason=...` line plus one result line per hook. 5. For each installed CLI, run `tools/scan-hook-errors.sh --cli {cli}`. Only claude keeps hook results in its native records and can answer `clean` or `errors`; codex, copilot, antigravity and kiro answer `unavailable`, and their runtime evidence comes from step 4 alone. Done when every CLI has printed one `status=clean|errors|unavailable reason=...` line and the four `unavailable` CLIs are reported as exactly that, not as clean. 6. For each error — `purge` failed, wiring failed, smoke failed, or a scanned error with `jsc=true` — run `tools/report-error.sh --hook {script name} --exit {code} --summary "{reason}" --cli {cli}` with the script's `[jsc]` output on stdin, then hand the failure to `jsc-hooks:repair`, which **MUST run as a sub agent** and must finish by opening a PR against `develop`. Aborting the remaining installs here is allowed as long as the repair starts. A scanned error with `jsc=false` belongs to a third-party hook: report it and leave it alone. Done when each error has either an `ERROR_{HASH}` page name on stdout, or an empty exit 0 meaning `JSC_WIKI_REPO_ERROR` and `JSC_WIKI_REPO` are both unset — in that second case carry the reason into step 7 instead. Skip this step when every CLI passed all four checks. 7. Report four results per CLI — purge, wiring, smoke, scan — each with the reason its script printed, plus any `ERROR_{HASH}` page name and repair PR URL. Done when every detected CLI has exactly one status per check and every repair has a PR against `develop`. @@ -44,8 +44,9 @@ The detailed flow **MUST run as a sub agent**; the main agent only reports the s - `session-timer.sh` takes `start` (keep an existing start time), `restart` (always overwrite it, for a CLI with no session id — kiro), `mark` and `report`. `wire-cli.sh` picks the right one per CLI; do not hand-edit the generated hook files. - `purge` reaches the user-level config only. Hooks that another plugin ships in its own `hooks.json` stay active, and uninstalling that plugin is the only way to clear them — say so when reporting, and treat their errors as third-party. - Backups land in `$JSC_HOME/backup/hooks/{cli}/{yyyyMMdd_HHmmss}/`, one directory per purge run, under the original file names. Hand that path to the user whenever a purge removed something. -- `smoke` treats `sdlc-gate.sh check` exit 2 as healthy: that exit is the stage lock blocking a turn on purpose, not a runtime error. `comment-scope.sh` exit 2 counts as healthy for the same reason — it means the scan found a comment and warned about it. The no-argument mode has no file name during smoke and exits 0 in silence; `sweep` depends on the worktree it runs in, so it answers 2 whenever that worktree happens to carry an offending comment. Neither is a broken hook. +- `smoke` treats `sdlc-gate.sh check` exit 2 as healthy: that exit is the stage lock blocking a turn on purpose, not a runtime error. `comment-scope.sh` and `lang-guard.sh` exit 2 count as healthy for the same reason — the scan found something and warned about it. Their no-argument mode has no file name during smoke and exits 0 in silence; `sweep` depends on the worktree it runs in, so it answers 2 whenever that worktree happens to carry an offending comment, a simplified character or a mojibake sequence. None of these is a broken hook. - `comment-scope.sh` takes three modes: `prompt` (inject the rule summary at UserPromptSubmit), no argument at all (scan the file just written at PostToolUse, reading `file_path` from stdin JSON or `JSC_CHANGED_FILE`), and `sweep [dir]` (scan every file the git worktree changed, for the four CLIs with no post-tool hook). All scanning modes read only the lines a diff added, skip markdown and binary files, and turn off entirely with `JSC_COMMENT_SCOPE=off`. The rule text itself lives in one place only, `jsc-review`'s `references/comment-scope.md`; never restate the list anywhere in this repo. -- `jsc-wrap.sh` runs `sweep` after the CLI exits and always returns the CLI's own exit code. A `sweep` hit warns on stderr and changes nothing else — never let a comment warning turn a successful CLI run into a failed one. +- `lang-guard.sh` takes the same three modes as `comment-scope.sh` and is wired at the same places, but it scans differently on purpose: it reads the whole file rather than comment lines only, and it does scan `.md` and plain-text files, because those are exactly the non-code output the rule targets. It flags three things — simplified characters (word list in `hooks/simplified.txt`, the single source of truth for this repo; a missing list skips that check in silence), mojibake (U+FFFD and double-encoding remnants), and non-UTF-8 encoding (decided by `iconv`; no `iconv` skips that check). It skips binaries, generated files, and the three files whose subject is those very characters (`simplified.txt`, `ste100-guard.sh`, `lang-guard.sh`). Turn it off with `JSC_LANG_GUARD=off`. The rule text lives only in `jsc-meta`'s `references/ste100.md`. +- `jsc-wrap.sh` runs both sweeps after the CLI exits and always returns the CLI's own exit code. A `sweep` hit warns on stderr and changes nothing else — never let a language or comment warning turn a successful CLI run into a failed one. - `tools/report-error.sh` is operator- or skill-invoked only. Never wire it to fire from a failing hook: hooks stay silent and exit 0, and a failing hook that reports itself can loop. - Data lands in `$JSC_HOME` (default `~/.jsc`), consumed by `jsc-log:worklog` and `jsc-log:stats`. -- 2.53.0 From aee2f196997ecdf56db9508cae47b2b60e641936 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 27 Aug 2026 09:49:53 +0800 Subject: [PATCH 14/16] =?UTF-8?q?feat(manifest):=20=E4=B8=89=E4=BB=BD=20ma?= =?UTF-8?q?nifest=20=E7=89=88=E6=9C=AC=E5=8D=87=E5=88=B0=200.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:把 `plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` 三份 manifest 的版本由 0.2.0 升到 0.2.1。 Why:0.2.0 已隨前一支 PR 發佈出去,本次新增的第七支 hook 與五個 CLI 的接線沒有跟著升版,`version-guard.sh` 就看不出本機落後,使用者不會收到更新提示。 How:以 jsc-meta 的 `sync-skill-manifest.sh` 統一 bump,patch 進位,三份同步成同一個值。 Who:繁中與編碼檢查 hook 的跨 CLI 支援。 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index c7e0c3a..f9b07cc 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.2.0", + "version": "0.2.1", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 440650e..2f273fd 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.2.0", + "version": "0.2.1", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills" } diff --git a/plugin.json b/plugin.json index b014c28..d2d316b 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.2.0", + "version": "0.2.1", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills/" } -- 2.53.0 From 75fba4715c213751dd57af3862ec5b9446b506e4 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 27 Aug 2026 09:53:55 +0800 Subject: [PATCH 15/16] =?UTF-8?q?feat(simplified):=20=E5=AD=97=E8=A1=A8?= =?UTF-8?q?=E6=93=B4=E5=85=85=E5=88=B0=201006=20=E5=AD=97=E4=B8=A6?= =?UTF-8?q?=E8=A3=9C=E9=BD=8A=E8=B7=B3=E9=81=8E=E6=B8=85=E5=96=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:簡體字表由 102 字擴充到 1006 字;lang-guard.sh 的跳過清單補上 jsc-meta 的 references/ste100.md 與 tools/ste100-lint.sh。 Why:102 字的舊表覆蓋率太低——拿 21 個常見簡體詞實測只抓到 4 個,「简体字」 「举办」「觉得」「战争」「医药」這類全部漏掉,規則等於形同虛設。跳過清單則是 對稱性問題:規則文件與機檢工具本身會列舉簡體字當範例,不跳過就每次先抓到自己。 How:字表逐字檢查「簡化後才出現的字形」,剔除正體也在用的字(后、台、干、只、 里、面、制、志、出、斗、丑、了、卷、咸、云、留、言、短、困、陷、游、封、吞), 再拿 jsc 全部 11 個存取庫共 221 個檔案的既有正體中文語料回歸驗證,誤報為零。 檔頭寫明排除原則與「改完必須拿語料重驗」的要求。跳過清單比照 ste100-lint.sh 的做法。 Who:非程式碼輸出的繁中無亂碼檢查。 --- hooks/lang-guard.sh | 2 + hooks/simplified.txt | 1086 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 1000 insertions(+), 88 deletions(-) diff --git a/hooks/lang-guard.sh b/hooks/lang-guard.sh index 2954892..e40e0e8 100755 --- a/hooks/lang-guard.sh +++ b/hooks/lang-guard.sh @@ -98,6 +98,8 @@ scan_file() { # $1=檔案路徑;命中就把報告印到 stdout 並回傳 1, */simplified.txt|simplified.txt) return 0 ;; */ste100-guard.sh|ste100-guard.sh) return 0 ;; */lang-guard.sh|lang-guard.sh) return 0 ;; + */references/ste100.md|ste100.md) return 0 ;; + */ste100-lint.sh|ste100-lint.sh) return 0 ;; esac # 二進位檔跳過。只認 NUL 位元組——拿「非可列印字元」當判準會把所有含中文的檔案誤判成二進位。 raw=$(head -c 1024 "$f" 2>/dev/null | wc -c) diff --git a/hooks/simplified.txt b/hooks/simplified.txt index 5ccc5ab..7c76e4d 100644 --- a/hooks/simplified.txt +++ b/hooks/simplified.txt @@ -1,109 +1,1019 @@ # simplified.txt — 機檢用的簡體字表,一行一個字,UTF-8。 # 用途:hooks/lang-guard.sh 讀這份字表組出 grep 樣式,判定非程式碼輸出有沒有夾帶簡體字。 # 這是本存取庫的單一真實來源,jsc-meta 的 tools/ste100-lint.sh 也優先讀這份。 -# 排除原則:只收簡化後才出現的字形,刻意排除繁體也在用的字(后、台、干、只、里、面、制、志)。 -# 加進那些字會讓「太后」「電視台」「干涉」「只有」「公里」「面板」「制度」「志工」全部誤報。 -# 增刪字元前先想清楚:這份字表寧可漏抓,也不可誤報。 +# +# 排除原則:只收簡化後才出現的字形,刻意排除繁體也在用的字(后、台、干、只、里、面、制、志、 +# 出、斗、丑、了、卷、咸、云、留、言、短、困、陷、游、封、吞)。加進那些字會讓「太后」 +# 「電視台」「干涉」「只有」「公里」「面板」「制度」「志工」「出現」「北斗」「丑時」 +# 「保留」「語言」「短句」「困難」「陷阱」「游標」「封裝」「吞掉異常」全部誤報。 +# +# 增刪字元前先想清楚:這份字表寧可漏抓,也不可誤報。改完一定要拿既有的正體中文語料 +# 重驗一次——本表定版時掃過 jsc 全部 11 個存取庫共 221 個檔案,誤報為零。 +# # `#` 開頭的行與空行由讀取端忽略。 -应 -为 -这 -说 -发 -国 -过 -对 -开 -关 -问 -题 -东 -车 -马 -鸟 -龙 -飞 -见 -无 -产 +万 +与 +专 业 -务 -书 -写 -学 +丛 +东 +丝 +两 +严 +丧 +个 +丰 +临 +为 +丽 +举 +么 +义 +乌 +乐 习 -报 -纸 -认 -识 -证 -际 -网 -络 -计 +乡 +书 +买 +乱 +亏 +亚 +产 +亩 +亲 +仅 +仆 +从 +仑 +仪 +们 +价 +伐 +众 +优 +伙 +会 +伞 +伟 +传 +伤 +伦 +伪 +体 +佣 +侠 +侦 +侨 +侩 +侬 +俩 +债 +偿 +儿 +党 +兰 +关 +兴 +兹 +养 +内 +冈 +写 +军 +农 +冯 +冲 +决 +冻 +净 +凉 +凑 +凛 +凭 +凯 +凶 +击 +凿 划 +刘 +则 +刚 +创 +删 +别 +剂 +剐 +剧 +劝 +务 +动 +励 +劲 +劳 +势 +勋 +区 +医 +华 +协 +单 +卖 +卢 +卤 +卫 +却 +厂 +厅 +历 +厉 +压 +厌 +厘 +厦 +县 +参 +双 +发 +变 +叶 +号 +叹 +吁 +吕 +吗 +吨 +听 +启 +吴 +呕 +员 +呛 +呜 +咏 +咙 +咛 +响 +哑 +哟 +唤 +啮 +啸 +喷 +嘱 +团 +园 +围 +国 +图 +圆 +圣 +坏 +块 +坛 +坞 +坟 +垄 +垒 +垦 +垫 +堑 +堕 +墙 +壮 +声 +壳 +壶 +处 +备 +复 +够 +头 +夸 +夺 +奋 +奖 +妆 +妇 +妈 +娄 +娇 +婴 +婶 +孙 +学 +孪 +宁 实 -现 -给 +审 +宪 +宽 +宾 +寝 +对 +寻 +导 +寿 +将 +尔 +尘 +尧 +尽 +屉 +届 +属 +屡 +屿 +岁 +岂 +岗 +岛 +岭 +岳 +岿 +峡 +峦 +崭 +巩 +币 +帅 +师 +帐 +帜 +带 +帧 +广 +庆 +庐 +库 +应 +庙 +庞 +废 +开 +异 +弃 +张 +弥 +弯 +强 +归 +当 +录 +彦 +彻 +径 +忧 +怀 +态 +怂 +怜 +总 +恳 +恼 +悦 +悬 +悯 +惊 +惨 +惩 +惯 +愿 +慑 +懑 +懒 +戏 +战 +户 +扑 +执 +扩 +扪 +扫 +扬 +扰 +抚 +抛 +抠 +抡 +抢 +护 +报 +担 +拟 +拢 +拣 +拥 +拦 +拧 +挚 +挛 +挝 +挞 +挟 +挠 +挣 +挤 +挥 +捞 +损 +换 +据 +掳 +掷 +揽 +搁 +搂 +搅 +携 +摄 +摇 +摊 +攒 +敌 +数 +斋 +斩 +断 +无 +旧 +时 +旷 +昼 +显 +晋 +晒 +晓 +晕 +暂 +朴 +机 +杀 +杂 +权 条 +来 +杨 +极 +构 +枢 +枣 +枪 +枫 +枭 +柜 +柠 +栈 +栋 +栏 +树 +样 +档 +桥 +桨 +桩 +梦 +椭 +楼 +樱 +欢 +欧 +歼 +殴 +毁 +毕 +毡 +气 +汇 +汉 +汤 +汹 +沟 +沥 +沦 +沪 +泪 +泻 +泼 +泽 +洁 +洒 +洼 +浅 +浇 +浊 +济 +浏 +浑 +浓 +涂 +涌 +涛 +涝 +涡 +润 +涨 +涩 +淀 +渊 +渍 +渗 +温 +湾 +湿 +溃 +滞 +满 +滤 +滥 +滦 +潍 +潜 +澜 +灭 +灯 +灵 +灶 +灾 +炉 +点 +炼 +烂 +烃 +烛 +烧 +烫 +烬 +热 +焖 +爱 +爷 +牵 +牺 +状 +犹 +狞 +独 +狭 +狮 +狰 +猎 +猪 +猫 +献 +玛 +环 +现 +琐 +琼 +瓮 +电 +画 +疗 +疟 +疡 +疮 +疯 +痈 +痒 +痪 +瘫 +癣 +皱 +盏 +盐 +监 +盖 +盗 +盘 +睁 +瞒 +矿 +码 +砖 +砚 +砺 +础 +硕 +确 +碱 +礼 +祸 +离 +种 +积 +称 +秽 +税 +稳 +穷 +窃 +窍 +窜 +窝 +竖 +竞 +笋 +笔 +笼 +筑 +筛 +筹 +签 +箩 +篓 +篮 +籴 +类 +粜 +粤 +粪 +粮 +紧 +纠 +红 +纤 约 级 +纪 +纫 +纬 +纯 +纱 +纳 +纵 +纷 +纸 +纹 +纺 +线 +练 组 +绅 +细 织 -变 -换 -电 -脑 -两 -双 -单 -构 -价 -钱 -众 -议 -论 -传 +终 +绍 +绎 +经 +绒 +结 +绕 +给 +绚 +络 +绝 +绞 统 -么 -儿 -们 -从 -来 -时 -间 -长 -门 -闻 -声 -员 -图 -团 +绣 +绦 +继 +绩 +绪 +续 +绳 +维 +绵 +绸 +绽 +绿 +缀 +缅 +缆 +缓 +缔 +缕 +缘 +缚 +缝 +缨 +缩 +缮 +网 +罗 +罚 +耸 +聂 +聋 +职 +联 +聪 +肃 +肤 +肾 +肿 +胀 +胁 +胆 +胜 +胡 +胫 +胶 +脉 +脏 +脐 +脑 +脱 +腊 +腻 +腾 +舰 +艰 +艳 +节 +芜 +芦 +苇 +苏 +苹 +范 +茎 +荡 +荣 +荤 +荧 +荫 +药 +莲 +获 +莹 +萝 +萤 +营 +萨 +蒋 +蒙 +蔑 +蔗 +蔷 +薮 +虏 +虑 +虽 +虾 +蚀 +蚁 +蚂 +蚕 +蛊 +蛮 +蜕 +蜗 +蜡 +蝇 +蝎 +衅 +衔 +补 +衬 +袜 +袭 +装 +裤 +见 +观 +规 +觅 +视 +览 +觉 +誉 +誊 +计 +订 +认 +讥 +讨 +让 +讫 +训 +议 +讯 +记 +讲 +讶 +许 +论 +讼 +设 +访 +证 +评 +诅 +识 +诈 +诉 +诊 +诌 +词 +译 +试 +诗 +诛 +询 +该 +详 +诫 +诬 +语 +诱 +说 +诵 +诸 +诺 +读 +课 +谁 +调 +谅 +谆 +谊 +谋 +谎 +谓 +谚 +谜 +谢 +谣 +谦 +谨 +谩 +谬 +谭 +谰 +谱 +谴 +贝 +贞 +负 +责 +贤 +质 +贩 +贪 +贫 +购 +贮 +贯 +贰 +贴 +贷 +贸 +费 +贺 +贼 +贾 +贿 +赁 +赂 +赃 +资 +赋 +赌 +赎 +赏 +赔 +赖 +赘 +赚 +赛 +赞 +赠 +赡 +赵 +赶 +趋 +跃 +踊 +踪 +躯 +车 +轧 +轨 +轩 转 +轮 +软 +轰 +轴 +载 +轿 +较 +辅 +辆 +辉 +辊 +辐 +辑 输 +辕 +辖 +辗 +辞 +边 +辽 达 +迁 +过 +迈 运 +还 +这 进 远 +违 连 -边 -还 -经 -该 -营 -规 -则 -范 -围 -参 -数 -类 -结 -态 -设 -备 -辑 -译 -码 -库 -档 +迟 +迭 +适 +选 +逊 +递 +逻 +遥 +邓 +邮 +邹 +邺 +邻 +郁 +郑 +郓 +郧 +酱 +酿 +释 +针 +钉 +钌 +钍 +钎 +钙 +钟 +钢 +钥 +钧 +钨 +钱 +钳 +钻 +铀 +铁 +铃 +铅 +铆 +铜 +铝 +铡 +铣 +铬 +铭 +铰 +铱 +银 +铸 +铺 +销 +锁 +锈 +锌 +锐 +锑 +错 +锚 +锡 +锣 +锥 +锦 +键 +锯 +锰 +锹 +镇 +镊 +镍 +镐 +镜 +镣 +镭 +镰 +长 +门 +闪 +问 +闯 +闰 +闲 +间 +闷 +闸 +闹 +闻 +闽 +阀 +阁 +阂 +阅 +阉 +阎 +阑 +阔 +队 +阳 +阴 +阵 +阶 +际 +陆 +陇 +陈 +陕 +险 +随 +隐 +隶 +难 +雾 +霉 +静 +韦 +韧 +韩 +韵 +页 +顶 +项 +顺 +须 +顽 +顾 +顿 +颂 +预 +颅 +领 +颇 +频 +颓 +颖 +颗 +题 +颜 +颧 +风 +飘 +飞 +饥 +饭 +饮 +饰 +饲 +饵 +饶 +馁 +馆 +馒 +马 +驭 +驯 +驱 +驴 +驶 +驹 +驻 +驼 +驾 +骁 +骂 +骄 +骆 +骇 +验 +骏 +骑 +骗 +骡 +骤 +鲜 +鳃 +鳞 +鸟 +鸠 +鸡 +鸣 +鸥 +鸦 +鸭 +鸯 +鸳 +鸽 +鹏 +鹤 +鹰 +麦 +黄 +齐 +齿 +龄 +龋 +龙 +龟 -- 2.53.0 From 667b60c8e9fde9230c166c70a1cc408158efeae4 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 27 Aug 2026 09:53:55 +0800 Subject: [PATCH 16/16] =?UTF-8?q?feat(manifest):=20=E4=B8=89=E4=BB=BD=20ma?= =?UTF-8?q?nifest=20=E7=89=88=E6=9C=AC=E5=8D=87=E5=88=B0=200.2.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:三份 manifest 由 0.2.1 升到 0.2.2。 Why:字表擴充後偵測能力大幅改變,本機沒跟著升版,version-guard.sh 就判不出落後, 使用者不會收到更新提示,繼續用覆蓋率只有兩成的舊字表。 How:以 jsc-meta 的 sync-skill-manifest.sh 統一 bump,三份同步成同一個值。 Who:非程式碼輸出的繁中無亂碼檢查。 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index f9b07cc..09c2b65 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.2.1", + "version": "0.2.2", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 2f273fd..56a1664 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.2.1", + "version": "0.2.2", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills" } diff --git a/plugin.json b/plugin.json index d2d316b..2290baf 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.2.1", + "version": "0.2.2", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查", "skills": "./skills/" } -- 2.53.0