Files
hooks/skills/hooks-install/SKILL.md
T
jiantw83 7d67538a2a docs(hooks-install): 豁免清單補齊為九支,與實作和準則對齊
What:`skills/hooks-install/SKILL.md` 的 Notes 段落,重啟閘門的豁免技能清單從六支補到九支,補上 `jsc-ask:ask`、`jsc-git:pr`、`jsc-git:commit`,並寫明「閘門認技能名不認呼叫鏈」以及清單的唯一來源在 `hooks/restart-gate.sh`。

Why:這三支是使用者在同一輪追問後才裁定加入的,實作 `hooks/restart-gate.sh` 與準則 `guidelines.md`「部署後重啟閘門」都已經是九支,只有這份技能文件還停在六支。技能文件是接線時唯一會被讀到的說明,少列三支會讓人以為 `deploy` 問模式、收尾開 PR 都會被擋,反而去下逃生門。

How:只改那一行,補上三支與兩句說明,並指向清單的唯一來源,避免下次又各自維護一份。三份 manifest 版本同步升到 0.2.5。

Who:`jsc-hooks:hooks-install` 技能文件,以及部署後重啟閘門這條規則的說明一致性。
2026-08-27 16:49:02 +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:*, jsc-ask:ask, jsc-git:pr, jsc-git:commit — because the change report and the worklog still have to be finished after a deploy, and the first six reach that finish line only through the last three: the deploy asks for its mode, the report closes with a PR. The gate matches skill names, not call chains, so a nested call to anything off the list is blocked all the same. hooks/restart-gate.sh owns the list; guidelines.md「部署後重啟閘門」carries the same nine with a reason per entry. 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.