From f81ceaf145fbff6e36b82336cd92ac76ee4af41c Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 26 Aug 2026 19:00:46 +0800 Subject: [PATCH] =?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:]]*(//|#|--|\*|/\*|