CLAUDE.md@plugins/itp-hooks · diff
git:20261022.ba8b0b9 to git:20261028.0834ecf
3 added, 2 removed. Audit A to A.
# itp-hooks Plugin
> Claude Code hooks for ITP workflow enforcement, code correctness, and commit validation.
**Hub**: [Root CLAUDE.md](../../CLAUDE.md) | **Sibling**: [gh-tools CLAUDE.md](../gh-tools/CLAUDE.md) | **Spoke index**: [docs/spoke-index.md](./docs/spoke-index.md)
## Invariants — read before editing
1. **Keep this file a slim hub.** The size guard warns at 36k and Claude Code stops fully loading a `CLAUDE.md` over **40,000 characters** (counted in UTF-16 code units, not bytes — see `hooks/posttooluse-claude-md-size-budget-reminder.ts`). Prettier repads every cell in a table to its widest row, so one long cell inflates the whole table; **keep every Purpose cell to a short clause** and put the narrative in the hook's own spoke. Measure after formatting, never before.
2. **`hooks.json` is the SSoT for registration**, not this file. A hook listed here but unregistered there does not run.
3. **`pretooluse-pueue-wrap-guard.ts` MUST stay the LAST PreToolUse entry** in `hooks.json` (audited by `tasks/hook-lint/pueue-wrap-last.sh`) — it auto-wraps long-running commands, so any guard after it never sees the unwrapped command. Details: [pueue-wrap-guard](./docs/pretooluse-bash-guard-orchestrator.md#pueue-wrap-guard).
4. **A new Bash guard goes into `BASH_GUARD_REGISTRY`** in `hooks/pretooluse-bash-guard-orchestrator.ts`, not into `hooks.json` (#111). Export its `main()` and gate the top-level call on `import.meta.main`. The exception is a guard that rewrites the command through `updatedInput`, which stays a separate entry.
5. **No full-table snapshot docs.** A spoke owns one subject; a doc that mirrors these tables drifts out of sync with the spokes. [docs/spoke-index.md](./docs/spoke-index.md) is an index of subjects, not a snapshot of hook rows.
6. **TypeScript/Bun is preferred for new hooks** (per `lifecycle-reference.md`); bash is acceptable only for simple pattern matching.
## Hooks
Registration lives in `hooks/hooks.json`. Each row is one clause; the spoke is the SSoT for that hook.
### PreToolUse
Orchestrator arc: [pretooluse-write-edit-orchestrator.md](./docs/pretooluse-write-edit-orchestrator.md). "Write\|Edit orch." in the Matcher column means the hook runs as a subhook of that orchestrator rather than as its own process. "Bash orch." means the hook runs inside [`pretooluse-bash-guard-orchestrator.ts`](./docs/pretooluse-bash-guard-orchestrator.md) rather than as its own process.
| Hook | Matcher | Purpose | Spoke |
| ----------------------------------------------------- | ------------------------ | --------------------------------------------- | ------------------------------------------------------------------ |
| `pretooluse-version-guard.ts` | Write\|Edit orch. | Hardcoded-version blocker for markdown | [→](./docs/version-guard.md) |
| `pretooluse-process-storm-guard.mjs` | Bash orch. + Write\|Edit | Blocks fork-bomb patterns | [→](./docs/process-storm-guard.md) |
| `pretooluse-cwd-deletion-guard.ts` | Bash orch. | Blocks deleting the CWD | [→](./docs/cwd-deletion-guard.md) |
| `pretooluse-git-worktree-guard.ts` | Bash orch. | Enforces worktree-per-branch | [→](./docs/git-worktree-guard.md) |
| `pretooluse-vale-claude-md-guard.ts` | Write\|Edit orch. | Rejects CLAUDE.md edits with vale findings | [→](./docs/vale-terminology-enforcement.md) |
| `pretooluse-hoisted-deps-guard.ts` | Write\|Edit orch. | pyproject.toml hoisting/path-escape policy | [→](./docs/hoisted-deps-guard.md) |
| `pretooluse-gpu-optimization-guard.ts` | Write\|Edit orch. | GPU optimization enforcement (6 checks) | [→](./docs/gpu-optimization-guard.md) |
| `pretooluse-file-size-guard.ts` | Write\|Edit orch. | Per-extension file-size bloat prevention | [→](./docs/file-size-guard.md) |
- | `pretooluse-write-edit-orchestrator.ts` | Write\|Edit | Runs the 10 Write\|Edit subhooks in-process | [→](./docs/pretooluse-write-edit-orchestrator.md) |
+ | `pretooluse-write-edit-orchestrator.ts` | Write\|Edit | Runs the 11 Write\|Edit subhooks in-process | [→](./docs/pretooluse-write-edit-orchestrator.md) |
| `pretooluse-native-binary-guard.ts` | Write\|Edit orch. | Launchd services must be native binaries | [→](./docs/native-binary-guard.md) |
+ | `pretooluse-html-measure-guard.ts` | Write\|Edit orch. + Bash orch. | Denies HTML/CSS that fixes text width | [→](./docs/html-measure-guard.md) |
| `pretooluse-pyi-stub-guard.ts` | Write\|Edit orch. | Blocks top-level defs in Python `__init__` | [→](./docs/pyi-stub-guard.md) |
| `pretooluse-uv-enforcement-guard.ts` | Bash orch. | Blocks non-UV Python package operations | [→](./docs/uv-enforcement-guard.md) |
| `pretooluse-pueue-local-guard.ts` | Bash orch. | Pueue commands must target the local daemon | [→](./docs/pueue-local-guard.md) |
| `pretooluse-cargo-tty-guard.ts` | Bash (in pueue-wrap) | Backgrounded cargo to PUEUE (via pueue-wrap) | [→](./docs/cargo-tty-guard.md) |
| `pretooluse-skill-plugin-root-guard.ts` | Write\|Edit orch. | Skills must use `cc-plugin-root` | [→](./docs/skill-plugin-root-guard.md) |
- | `pretooluse-bash-guard-orchestrator.ts` | Bash | Runs the 25 Bash guards in one bun process | [→](./docs/pretooluse-bash-guard-orchestrator.md) |
+ | `pretooluse-bash-guard-orchestrator.ts` | Bash | Runs the 26 Bash guards in one bun process | [→](./docs/pretooluse-bash-guard-orchestrator.md) |
| `pretooluse-pueue-wrap-guard.ts` | Bash | Auto-wraps long-running commands (see #3) | [→](./docs/pretooluse-bash-guard-orchestrator.md#pueue-wrap-guard) |
| `pretooluse-webfetch-fallback-guard.ts` | WebFetch | Denies built-in WebFetch; no escape hatch | `~/.claude/webfetch-fallback-CLAUDE.md` |
| `pretooluse-release-notes-extensiveness-guard.ts` | Bash orch. | Hard-blocks releases with thin notes | [→](./docs/release-notes-extensiveness-guard.md) |
| `pretooluse-gmail-body-guard.ts` | Bash orch. | Blocks bad-rendering `gmail draft` bodies | [→](./docs/gmail-body-guard.md) |
| `pretooluse-markdown-commit-hard-wrap-guard.ts` | Bash orch. | Blocks a `git commit` adding `.md` hard wraps | [→](./docs/markdown-hard-wrap-reminder.md) |
| `pretooluse-secret-exposure-guard.ts` | Write\|Edit | Hard-blocks live credentials in new content | [→](./docs/secret-and-pii-exposure-guard.md) |
| `pretooluse-headless-claude-p-guard.ts` | Bash orch. | Blocks unworkable headless `claude -p` calls | [→](./docs/headless-claude-p.md) |
| `pretooluse-askuserquestion-line-terminator-guard.ts` | AskUserQuestion | Blocks newlines in option label/description | [→](./docs/askuserquestion-option-line-terminator-guard.md) |
| `pretooluse-pkill-option-after-pattern-guard.ts` | Bash orch. | Blocks pkill/pgrep options after the pattern | [→](./docs/pkill-option-after-pattern-guard.md) |
| `pretooluse-broad-process-signal-guard.ts` | Bash orch. + Write, Edit | Blocks broad kill/pkill/killall (+ scripts) | [→](./docs/broad-process-signal-guard.md) |
| `pretooluse-old-git-guard.ts` | Bash orch. | Blocks hook-firing git below 2.54 | [→](./docs/old-git-guard.md) |
| `pretooluse-long-runtime-haiku-monitor-reminder.ts` | Bash orch. | Long run: start a `haiku` health monitor | [→](./docs/long-runtime-haiku-monitor-reminder.md) |
### PostToolUse
Orchestrator arc: [posttooluse-write-edit-orchestrator.md](./docs/posttooluse-write-edit-orchestrator.md). "PostToolUse orch." in the Matcher column means the hook runs inside that orchestrator.
| Hook | Matcher | Purpose | Spoke |
| ------------------------------------------------- | ----------------- | --------------------------------------------- | ----------------------------------------------------------------------------- |
| `posttooluse-reminder.ts` | Bash\|Write\|Edit | Context-aware reminders (UV, Pueue, ADR, …) | [→](./docs/posttooluse-reminder.md) |
| `code-correctness-guard.sh` | Bash\|Write\|Edit | Silent-failure detection ONLY | [→](./docs/code-correctness-philosophy.md) |
| `posttooluse-pushover-budget-reminder.ts` | Bash\|Write\|Edit | Pushover message-budget nudge + limits SSoT | [→](./docs/pushover-budget-reminder.md) |
| `posttooluse-invented-fallback-reminder.ts` | Bash\|Write\|Edit | Official-values nudge on invented fallbacks | [→](./docs/invented-fallback-reminder.md) |
| `posttooluse-vale-claude-md.ts` | PostToolUse orch. | Informational vale check on CLAUDE.md edits | [→](./docs/vale-terminology-enforcement.md) |
| `posttooluse-glossary-sync.ts` | Write\|Edit | Auto-sync GLOSSARY.md to Vale vocabulary | [→](./docs/vale-terminology-enforcement.md) |
| `posttooluse-terminology-sync.ts` | Write\|Edit | CLAUDE.md → GLOSSARY.md sync + dupe detection | [→](./docs/vale-terminology-enforcement.md) |
| `posttooluse-readme-pypi-links.ts` | Write\|Edit | PyPI badge/link consistency in READMEs | [→](./docs/readme-pypi-links.md) |
| `posttooluse-markdown-table-guard.ts` | Write\|Edit | Per-edit GFM table structural guard | [→](./docs/markdown-table-guard.md) |
| `posttooluse-ssot-principles.ts` | PostToolUse orch. | SSoT/DI reminder with ast-grep detection | [→](./docs/ssot-principles.md) |
| `posttooluse-memory-efficiency-reminder.ts` | PostToolUse orch. | Once-per-session memory-efficiency reminder | [→](./docs/posttooluse-write-edit-orchestrator.md#memory-efficiency-reminder) |
| `posttooluse-ty-type-check.ts` | PostToolUse orch. | ty type check on .py/.pyi edits | [→](./docs/ty-type-checker.md) |
| `posttooluse-write-edit-orchestrator.ts` | Write\|Edit | PostToolUse multi-aggregation orchestrator | [→](./docs/posttooluse-write-edit-orchestrator.md) |
| `posttooluse-tsc-type-check.ts` | PostToolUse orch. | tsc project-scoped check on .ts/.tsx edits | [→](./docs/tsc-type-check.md) |
| `posttooluse-oxlint-check.ts` | PostToolUse orch. | oxlint correctness+suspicious on JS/TS | [→](./docs/js-ts-lint.md) |
| `posttooluse-biome-lint.ts` | PostToolUse orch. | biome complementary-to-oxlint JS/TS lint | [→](./docs/js-ts-lint.md) |
| `posttooluse-python-preference-nudge.ts` | PostToolUse orch. | Language-preference reminder on `.py` edits | [→](./docs/python-preference-nudge.md) |
| `posttooluse-pii-exposure-reminder.ts` | Write\|Edit | Reminder on third-party email/phone on disk | [→](./docs/secret-and-pii-exposure-guard.md) |
| `posttooluse-markdown-hard-wrap-reminder.ts` | PostToolUse orch. | Reminds on net-new hard-wrapped `.md` prose | [→](./docs/markdown-hard-wrap-reminder.md) |
| `posttooluse-bash-markdown-hard-wrap-reminder.ts` | Bash | Same, for `.md` written by a shell command | [→](./docs/markdown-hard-wrap-reminder.md) |
| `posttooluse-claude-md-size-budget-reminder.ts` | PostToolUse orch. | CLAUDE.md character-budget reminder (see #1) | [→](./docs/posttooluse-write-edit-orchestrator.md) |
### SessionStart
| Hook | Purpose | Spoke |
| ----------------------------------- | ------------------------------------------------ | -------------------------------------- |
| `sessionstart-proto-floor-check.sh` | Warns when proto is too old for bare `bun` hooks | [→](./scripts/proto-floor.sh) (header) |
| `sessionstart-git-version-check.sh` | Warns when the git on PATH is below 2.54 | [→](./docs/old-git-guard.md) |
### Stop
| Hook | Purpose | Spoke |
| ---------------------------- | -------------------------------------------------- | ------------------------------ |
| `stop-hook-error-summary.ts` | Summarizes session hook errors on Claude exit | [→](./docs/stop-hooks.md) |
| `stop-ty-project-check.ts` | Project-wide ty check on exit (only if .py edited) | [→](./docs/ty-type-checker.md) |
## Escape hatches
Every escape marker, its consumer hook, case and window rules and reason length: [docs/marketplace-escape-hatch-marker-reference.md](../../docs/marketplace-escape-hatch-marker-reference.md), generated from `hooks/lib/escape-hatch-marker-registry-iter111.ts` (add a marker there, then `bash tasks/hook-lint/marker-reference-doc.sh`). Not markers, so not in the registry: the command-prefix assignments `ALLOW_BARE_BRANCH=1` (git worktree guard), `ALLOW_UMBRELLA_REPO=1` (umbrella no-repo guard), `PR_BLOCKING_REVIEW_OK=1` (PR review-invitation guard) and `ALLOW_LEGACY_TS=1` (TypeScript install-command guard), plus the lowercase comment `# gpu-optimization-bypass: <reason>` (GPU optimization guard, exact spelling with `#` and colon).
`pretooluse-webfetch-fallback-guard.ts` has **no** escape hatch, by design.
## Environment variables (hook context)
Set by Claude Code when a hook fires — hooks read them, users do not set them.
| Variable | Description |
| ------------------------ | ------------------------------------------------------------------- |
| `CLAUDE_SESSION_ID` | Session UUID; per-session gate files and session-scoped caches |
| `CLAUDE_CONVERSATION_ID` | Conversation UUID (alias surfaced by some hook events) |
| `CLAUDE_PROJECT_DIR` | Project root Claude is working in; resolves `.claude/` config |
| `CLAUDE_HOOK_SPAWNED` | `1` when running via a wrapper; guards against recursive invocation |
## Skills
- [hooks-development](./skills/hooks-development/SKILL.md)
- [setup](./skills/setup/SKILL.md)
## References
- [docs/spoke-index.md](./docs/spoke-index.md) — annotated index of all spokes in [`docs/`](./docs/)
- [lifecycle-reference.md](skills/hooks-development/references/lifecycle-reference.md) — hook lifecycle and best practices
- [bootstrap-monorepo.md](../itp/skills/bootstrap-monorepo/references/bootstrap-monorepo.md) — monorepo scaffolding patterns