Merge pull request 'feat/comment-scope-hook' (#26) from feat/comment-scope-hook into develop
Reviewed-on: #26
This commit was merged in pull request #26.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jsc-hooks",
|
||||
"version": "0.1.8",
|
||||
"version": "0.1.9",
|
||||
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查",
|
||||
"skills": "./skills",
|
||||
"author": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jsc-hooks",
|
||||
"version": "0.1.8",
|
||||
"version": "0.1.9",
|
||||
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查",
|
||||
"skills": "./skills"
|
||||
}
|
||||
|
||||
@@ -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 處理。
|
||||
|
||||
## 呼叫慣例
|
||||
|
||||
|
||||
@@ -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 -->`(或 `# 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<TAB>{項目}<TAB>{路徑}<TAB>{present\|missing}`,以 `status=wired\|degraded\|unwired\|skipped` 回報(結束碼 0、1、5、3)。體檢類技能(`/jsc-cli:doctor`)只能用這個子命令,另外三個都會動到環境 |
|
||||
| `tools/wire-cli.sh` | 單一 CLI 的 hook 生命週期,共三個用法。`{cli}` 是接線:對應的設定編輯、包裝別名安裝、hook 檔建立,皆以 `<!-- jsc-hooks -->`(或 `# jsc-hooks`)標記整段重寫,重跑等同先移除再重裝;寫完每個檔案會重讀驗證位置正確才回報成功(codex 的 `notify` 必須是根層鍵、kiro 的 JSON 必須成對且 `on`、`run` 在最上層),以 `status=wired\|degraded\|skipped\|failed` 回報。`purge {cli}` 是移除:把該 CLI 的**所有** hook 清掉,含非 jsc 的第三方項目,動到的檔案先原樣備份到 `$JSC_HOME/backup/hooks/{cli}/{yyyyMMdd_HHmmss}/`,備份失敗就不移除,移除後重讀驗證,驗不過自動還原備份,以 `status=purged\|skipped\|failed` 回報。`smoke {cli}` 是執行期冒煙測試:六支 hook 的每個接線模式各跑一次,非零退出即為錯誤(例外有兩個:`sdlc-gate.sh check` 的 exit 2 是階段鎖的設計行為,`comment-scope.sh` 無參數模式的 exit 2 是掃到違規註解的設計行為),以 `status=ok\|failed` 回報。`status {cli}` 是唯讀盤點:只讀設定檔判斷標記段落在不在,不寫檔也不執行 hook,每個接線點印一行 `item<TAB>{項目}<TAB>{路徑}<TAB>{present\|missing}`,以 `status=wired\|degraded\|unwired\|skipped` 回報(結束碼 0、1、5、3)。體檢類技能(`/jsc-cli:doctor`)只能用這個子命令,另外三個都會動到環境 |
|
||||
| `tools/scan-hook-errors.sh` | 掃 CLI 原生紀錄找 hook 的執行期錯誤(接線寫對、跑起來出錯)。只有 claude 有 hook 結果紀錄,掃 `~/.claude/projects/**/*.jsonl` 的 `hook_non_blocking_error` 與非空 `hookErrors`;codex、copilot、antigravity、kiro 沒有等價紀錄,一律回報 `unavailable` 並指向 `wire-cli.sh smoke {cli}`。每筆錯誤附加一行 JSON 到 `$JSC_HOME/errors/hooks.jsonl`,`jsc` 欄位標明是不是 jsc 自己的 hook(第三方 hook 的錯誤只回報,不由 jsc 修正);去重與 `scan-logs.sh` 同法,重掃只讀新增段落,以 `status=clean\|errors\|unavailable` 回報 |
|
||||
|
||||
## 失敗回報範本
|
||||
@@ -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`),語言規則仍重寫到各自的規則檔(以 `<!-- jsc-hooks -->` 標記整段取代,等同先移除再重裝,不重複追加)。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`),語言規則仍重寫到各自的規則檔(以 `<!-- jsc-hooks -->` 標記整段取代,等同先移除再重裝,不重複追加)。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`,再不行就安靜降級 |
|
||||
|
||||
|
||||
Executable
+102
@@ -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:]]*(//|#|--|\*|/\*|<!--|;|%)|[[:space:]](//|#)[[:space:]]' || true)
|
||||
[ -n "$comments" ] || exit 0
|
||||
|
||||
# 白名單先剪掉,再比對禁止樣式。剪掉而不是整行放行——同一行可能一半合規、一半違規。
|
||||
cleaned=$(printf '%s\n' "$comments" | sed -E \
|
||||
-e 's#SPDX-License-Identifier:[^[:space:]]*##g' \
|
||||
-e 's#CVE-[0-9]{4}-[0-9]+##g' \
|
||||
-e 's#(RFC|ISO|IEEE|ANSI|ECMA|UTF|SHA|MD|AES|RSA|HMAC|PBKDF|TLS|SSL|HTTP|BIG|EUC|JIS|GB|RS|IPV|X)-?[0-9]+(-[0-9]+)?##g' \
|
||||
-e 's#@(deprecated|since|param|returns?|throws|type|typedef|example|see|link|inheritdoc|override|nullable|internal)##g' \
|
||||
-e 's#https?://(github|gitlab|bitbucket)\.com/[^[:space:]]*##g' \
|
||||
-e 's#https?://[^[:space:]]*[{<][^[:space:]]*##g' \
|
||||
-e 's#[0-9]{4}[-/][0-9]{1,2}[-/][0-9]{1,2}##g')
|
||||
|
||||
hit() { # $1=樣式 $2=說明;命中就把說明與最多三行證據印到 stdout
|
||||
m=$(printf '%s\n' "$cleaned" | grep -nE "$1" | head -n 3)
|
||||
[ -n "$m" ] || return 0
|
||||
printf ' %s\n' "$2"
|
||||
printf '%s\n' "$m" | sed 's/^/ /'
|
||||
}
|
||||
|
||||
out=$(
|
||||
hit '(^|[^[:alnum:]_/])#[0-9]+' '議題編號(#123)'
|
||||
hit '(^|[^[:alnum:]_])![0-9]+' 'PR、MR 編號(!45)'
|
||||
hit '[A-Z]{2,6}-[0-9]{1,6}' '工作包、故事、驗收、測試案例、變更單、議題編號(前綴加流水號)'
|
||||
hit '(QUESTION|PLAN|ANALYZE|DELIVER|MAINTAIN|REPO|LOG|LEARN|ERROR|CHECK|REPORT)_([A-Z0-9]{8}|CONTENTS)' 'jsc wiki 頁面編號'
|
||||
hit '(todo|TODO|待辦)[[:space:]]*#?[0-9]+' 'TDD 待辦編號'
|
||||
hit '([Ss]print|里程碑|[Mm]ilestone)[[:space:]]*[0-9]+' 'Sprint、里程碑編號'
|
||||
hit '(^|[^[:alnum:].])v[0-9]+\.[0-9]+|版本[[:space:]]*v?[0-9]+\.[0-9]+' '版本號'
|
||||
hit '(commit|提交|hash|SHA)[[:space:]:]*[0-9a-f]{7,40}' 'commit hash'
|
||||
hit '(branch|分支)[[:space:]:]*[a-z]+/[a-z0-9-]+' '分支名稱'
|
||||
hit '(^|[[:space:]])@[a-zA-Z][a-zA-Z0-9_.-]{2,}' '人名、認領者、@ 提及(含 @author)'
|
||||
hit 'https?://[^[:space:]]*(wiki|confluence|atlassian|notion\.so|docs\.google|sharepoint)' 'wiki 或外部文件連結'
|
||||
hit 'https?://[^[:space:]]*/(issues|pulls)/[0-9]+' '內部議題、PR 連結'
|
||||
hit '([Gg]enerated (with|by)|Co-Authored-By|本檔(案)?由|AI (產生|生成|撰寫))' '產生來源署名'
|
||||
hit '預估[[:space:]]*[0-9]+[[:space:]]*(小時|分鐘|人日|人天|天)|[0-9]+[[:space:]]*(人日|人天|工時)' '工時估算'
|
||||
hit '(規格書|需求書|準則|規範|清單|guidelines)[^。]{0,8}第[[:space:]]*[0-9]+|(規格書|需求書|SRS)[^。]{0,6}[0-9]+(\.[0-9]+)+' '規格文件章節、稽核檢查項編號'
|
||||
)
|
||||
|
||||
[ -n "$out" ] || exit 0
|
||||
|
||||
{
|
||||
printf '[jsc] 程式碼註解夾帶了文件相關資訊,請就地修正:%s\n' "$file"
|
||||
printf '%s\n' "$out"
|
||||
printf ' 修法:把編號指向的內容搬進註解,然後刪掉編號。搬不動就代表那件事不該用註解表達。\n'
|
||||
printf ' 規則正文與白名單見 jsc-review 的 references/comment-scope.md。誤判時用 JSC_COMMENT_SCOPE=off 關閉。\n'
|
||||
} >&2
|
||||
exit 2
|
||||
@@ -24,6 +24,10 @@
|
||||
{
|
||||
"type": "command",
|
||||
"command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/sdlc-gate.sh\" wp-check prompt"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/comment-scope.sh\" prompt"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -72,6 +76,15 @@
|
||||
"command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/skill-usage.sh\""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "Write|Edit|MultiEdit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/comment-scope.sh\""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jsc-hooks",
|
||||
"version": "0.1.8",
|
||||
"version": "0.1.9",
|
||||
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查",
|
||||
"skills": "./skills/"
|
||||
}
|
||||
|
||||
@@ -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 `<!-- jsc-hooks -->` (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`.
|
||||
|
||||
+93
-31
@@ -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" "<!-- jsc-hooks -->"
|
||||
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}" "<!-- jsc-hooks -->"
|
||||
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}" "<!-- jsc-hooks -->"
|
||||
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" "<!-- jsc-hooks -->" "<!-- /jsc-hooks -->" "$(ste100_text)" \
|
||||
replace_block "$agents" "<!-- jsc-hooks -->" "<!-- /jsc-hooks -->" "$(rules_text)" \
|
||||
|| fail "無法寫入 $agents"
|
||||
has_block "$agents" "<!-- jsc-hooks -->" || 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" "<!-- jsc-hooks -->" "<!-- /jsc-hooks -->" "$(ste100_text)" \
|
||||
replace_block "$instr" "<!-- jsc-hooks -->" "<!-- /jsc-hooks -->" "$(rules_text)" \
|
||||
|| fail "無法寫入 $instr"
|
||||
has_block "$instr" "<!-- jsc-hooks -->" || 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" "<!-- jsc-hooks -->" "<!-- /jsc-hooks -->" "$(ste100_text)" \
|
||||
replace_block "$rules" "<!-- jsc-hooks -->" "<!-- /jsc-hooks -->" "$(rules_text)" \
|
||||
|| fail "無法寫入 $rules"
|
||||
has_block "$rules" "<!-- jsc-hooks -->" || 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 <<EOF || fail "無法寫入 $hookfile"
|
||||
{
|
||||
"name": "jsc-hooks",
|
||||
"description": "jsc session timer + STE100 guard bridge (auto-generated by jsc-hooks:hooks-install, do not edit by hand)",
|
||||
"description": "jsc session timer + STE100 guard + comment scope bridge (auto-generated by jsc-hooks:hooks-install, do not edit by hand)",
|
||||
"on": ["sessionEnd", "userPromptSubmit"],
|
||||
"env": { "JSC_CLI": "kiro" },
|
||||
"run": "sh \\"$HOOKS/session-timer.sh\\" mark </dev/null; sh \\"$HOOKS/ste100-guard.sh\\" </dev/null"
|
||||
"run": "sh \\"$HOOKS/session-timer.sh\\" mark </dev/null; sh \\"$HOOKS/ste100-guard.sh\\" </dev/null; sh \\"$HOOKS/comment-scope.sh\\" prompt </dev/null"
|
||||
}
|
||||
EOF
|
||||
for f in "$startfile" "$hookfile"; do
|
||||
@@ -812,9 +872,11 @@ EOF
|
||||
done
|
||||
grep -qF '"sessionStart"' "$startfile" 2>/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
|
||||
|
||||
Reference in New Issue
Block a user