Files
hooks/skills/hooks-install/SKILL.md
T
jiantw83 7f8615a9ed feat(hooks-install): 技能同步到八支 hook 與重啟閘門的降級說法
What:`skills/hooks-install/SKILL.md` 四處增修。`description` 與目標段落的 hook 清單加入 `restart-gate.sh`,七支改八支;降級說明補上「四個 CLI 連部署後重啟閘門也接不上」與後果一句;第 4 步的冒煙說明補上重啟閘門的每條判定路徑;備註新增 `restart-gate.sh` 一條,並在 `session-timer.sh` 那一條補上「`start` 與 `restart` 會清除閘門」。

Why:技能是接線這件事的對外說法。hook 加了一支、技能還寫七支,回報就會少一項,而且降級說法不補會暗示每個 CLI 都擋得下來——這正是準則明文禁止的。`session-timer.sh` 那條也要補:清除閘門掛在那兩個事件上,接線少了它們,閘門會一路擋到使用者自己下逃生門。

How:降級那一段把後果講明白,不只說「接不上」:在那四個 CLI 上一次技能呼叫都擋不下來,狀態檔照樣寫、下一個工作階段開始照樣清,重啟本身只靠 `jsc-cli:deploy` 的收尾訊息。備註那一條寫出豁免清單與逃生門,並講明豁免的理由是異動報告與工作日誌要寫得完,讓接線的人知道哪些技能在閘門升起時仍然叫得動。

Who:`/jsc-hooks:hooks-install` 的接線流程與對使用者的回報。
2026-08-27 16:34:17 +08:00

11 KiB

name, description
name description
hooks-install Wire jsc hooks (STE100 guard, session timer, skill usage logger, SDLC model gate, plugin version guard, post-deploy restart gate, 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 eight hooks (ste100-guard.sh, session-timer.sh, skill-usage.sh, sdlc-gate.sh, version-guard.sh, restart-gate.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 neither the version guard nor the post-deploy restart gate can 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. On those four the restart gate blocks no skill call whatsoever: the state file is still written and still cleared at the next session start, so the restart itself rests on the jsc-cli:deploy closing message.

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
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 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.

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

  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 eight hooks once each, every wired mode included, plus each decision path of the work-package check and of the restart gate, 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.

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.sh sets the first two itself.
  • 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. start and restart also clear the restart gate whenever they decide this SessionStart is a new session, so the wiring of those two events is what lowers the gate after a restart — a CLI wired without them keeps the gate up until the user sets JSC_RESTART_GATE=off.
  • restart-gate.sh blocks jsc skill calls while $JSC_HOME/restart-required exists, so a freshly deployed skill set is not used by a process still running the old one. jsc-cli:deploy writes that file through restart-gate.sh require {install|update} [{domain}...] at the end of an install or update. Exempt skills stay callable — jsc-cli:deploy, jsc-hooks:hooks-install, jsc-gitea:wiki, jsc-log:worklog, jsc-log:learn, jsc-meta:* — because the change report and the worklog still have to be finished after a deploy. Escape hatch: JSC_RESTART_GATE=off.
  • 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 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.
  • 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.