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 依循。
8.5 KiB
name, description
| name | description |
|---|---|
| hooks-install | 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 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 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:
| 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.
Treat any hook error as repair work, whether it appeared while wiring or while running. Stopping the remaining installs to start that repair is the right call; leaving a broken hook wired is not.
The detailed flow MUST run as a sub agent; the main agent only reports the summary.
Steps
- 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. - 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 onestatus=purged|skipped|failed reason=...line and you have noted the backup directory path from its[jsc]output. - 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 onestatus=line and none exited 2. - 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 onestatus=ok|failed reason=...line plus one result line per hook. - For each installed CLI, run
tools/scan-hook-errors.sh --cli {cli}. Only claude keeps hook results in its native records and can answercleanorerrors; codex, copilot, antigravity and kiro answerunavailable, and their runtime evidence comes from step 4 alone. Done when every CLI has printed onestatus=clean|errors|unavailable reason=...line and the fourunavailableCLIs are reported as exactly that, not as clean. - For each error —
purgefailed, wiring failed, smoke failed, or a scanned error withjsc=true— runtools/report-error.sh --hook {script name} --exit {code} --summary "{reason}" --cli {cli}with the script's[jsc]output on stdin, then hand the failure tojsc-hooks:repair, which MUST run as a sub agent and must finish by opening a PR againstdevelop. Aborting the remaining installs here is allowed as long as the repair starts. A scanned error withjsc=falsebelongs to a third-party hook: report it and leave it alone. Done when each error has either anERROR_{HASH}page name on stdout, or an empty exit 0 meaningJSC_WIKI_REPO_ERRORandJSC_WIKI_REPOare both unset — in that second case carry the reason into step 7 instead. Skip this step when every CLI passed all four checks. - 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 againstdevelop.
Notes
- Every hook script accepts both stdin JSON and environment variables (
JSC_CLI,JSC_SESSION_ID,JSC_SKILL,JSC_TOOL_NAME,JSC_MODEL);jsc-wrap.shsets the first two itself. session-timer.shtakesstart(keep an existing start time),restart(always overwrite it, for a CLI with no session id — kiro),markandreport.wire-cli.shpicks the right one per CLI; do not hand-edit the generated hook files.purgereaches the user-level config only. Hooks that another plugin ships in its ownhooks.jsonstay 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. smoketreatssdlc-gate.sh checkexit 2 as healthy: that exit is the stage lock blocking a turn on purpose, not a runtime error.comment-scope.shexit 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;sweepdepends 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.shtakes three modes:prompt(inject the rule summary at UserPromptSubmit), no argument at all (scan the file just written at PostToolUse, readingfile_pathfrom stdin JSON orJSC_CHANGED_FILE), andsweep [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 withJSC_COMMENT_SCOPE=off. The rule text itself lives in one place only,jsc-review'sreferences/comment-scope.md; never restate the list anywhere in this repo.jsc-wrap.shrunssweepafter the CLI exits and always returns the CLI's own exit code. Asweephit warns on stderr and changes nothing else — never let a comment warning turn a successful CLI run into a failed one.tools/report-error.shis 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 byjsc-log:worklogandjsc-log:stats.