Hook Reference
Every built-in hook — what it does, when it runs, and a link to its source on GitHub.
Hook Reference
All hooks live in .claude/hooks/ and receive the tool payload as JSON on stdin. Exit 0 allows the action; exit 2 blocks it (PreToolUse and Stop). PostToolUse hooks can't block the call that already ran — they warn, inject context, or persist state for a later gate.
The kit ships 27 hooks. The standard profile wires up 23; four are opt-in (marked below). Each links to its current source — that's the source of truth, so this page never drifts.
Shared parsing/state helpers live in .claude/hooks/lib/ (json-parse.sh, state-counter.sh, project-commands.sh). The blocking hooks fail closed: if the library can't be loaded they exit 2 rather than silently passing.
Want to enable the opt-in hooks or write your own? See Enable Opt-In Hooks and the Hooks guide.
SessionStart
session-start.sh
Injects Tier-1 context before the first turn: confirms CODEBASE_MAP.md / CLAUDE.project.md presence, the top rules from tasks/lessons/_index.md, the active task in tasks/todo.md, and the current branch + uncommitted-tree summary. Also resets the transient session state files (hook-firings, quality-gate-history, bash-budget, and last_quality_gate) so a new session starts clean. Emits {"additionalContext": "..."}; stays silent when there's nothing substantive to add.
UserPromptSubmit
prompt-router.sh
Inspects the prompt and injects a domain-specific reminder when it hits a sensitive inflection — auth/security, billing/payments, migrations/schema, deploy/release, dependencies. Uses word-boundary matching so authentication, permissions, migrations, etc. trigger. Silent when nothing matches.
skill-extract-reminder.sh · opt-in
Periodically reminds Claude to capture non-obvious discoveries as a skill. Enabled by the strict profile.
PreToolUse — Edit / Write / NotebookEdit
protect-files.sh
Blocks edits to secret-bearing or generated files — .env* (except .example/.template/.sample/.test), credentials, private keys (*.pem, *.key, id_rsa…), certificates, cloud service-account JSON, and lock files (package-lock.json, pnpm-lock.yaml, composer.lock, Pipfile.lock, …). Exit 2.
protect-changes.sh
Blocks architectural edits unless CLAUDE_APPROVED=1: dependency manifests, migrations/schema, auth-logic paths (*/lib/auth/*, */middleware/auth*, */auth/*, */security/*, */permissions/*), and CI workflows. Presentational UI under */components/* is exempt. Build configs (tsconfig, next.config, tailwind.config, Dockerfile, …) block only when CCK_PROTECT_BUILD_CONFIGS=1 (set by the strict profile); in the standard profile they emit a non-blocking heads-up.
glob-guidance.sh
Non-blocking. Surfaces a one-shot, per-pattern nudge for cross-cutting file patterns that don't map to a single directory where a subdir CLAUDE.md would carry the guidance — test files (*.test.*, *.spec.*, test_*.py) and migrations anywhere in the tree. Emits to stderr (the PreToolUse feedback channel) and always exits 0; fires once per pattern per session via .hook-state/glob-guidance-fired. Customise the case table in the script.
PreToolUse — Bash
branch-protect.sh
Blocks direct pushes to main/master (explicit, HEAD:, or bare push while on the branch) and force pushes. Allows --force-with-lease.
block-dangerous-commands.sh
Blocks hard-to-reverse commands: recursive rm on /, ~/$HOME, ., or a wildcard; git reset --hard; git clean -fd; SQL DROP/TRUNCATE; docker system prune -a; recursive chmod/chown on /.
conventional-commit.sh
Validates git commit -m messages against the conventional-commit format (feat, fix, refactor, test, docs, chore, perf, ci, build, style). Skips heredoc / no--m commits.
PreToolUse — Task
subagent-pre.sh
Appends an open telemetry row to .hook-state/agent-invocations.jsonl for each sub-agent (Task) invocation, so sub-agent usage can be measured.
PreToolUse — MCP
mcp-gate.sh
MCP supply-chain / prompt-injection governance (matcher mcp__.*). Blocks (exit 2) any mcp__<server>__<tool> call whose <server> is absent from .claude/mcp-allowlist.txt — an unexpected or malicious MCP server can't be invoked until you explicitly trust it. Inert until you create that allowlist: with no file it never blocks, and only emits a one-shot per-session reminder that MCP results are untrusted data, not instructions. Audit configured servers against the allowlist with /mcp-audit.
PostToolUse — Edit / Write / NotebookEdit
secret-scan.sh
Scans the edited file for accidentally committed secrets — AWS keys, generic API-key/token assignments, private keys, JWTs, GitHub/Slack/Google/Stripe/SendGrid/Twilio/npm/PyPI tokens, and DB connection strings with credentials. Warns (exit 0); flip to exit 2 to make it blocking.
unicode-scan.sh
Detects invisible Unicode that signals Glassworm-style supply-chain attacks or obfuscation — variation selectors (U+FE00–FE0F, U+E0100–E01EF), zero-width characters, mid-file BOM, and the Tags block. Allowlist a file with a // kit-allow-unicode comment in the first 5 lines. Warns (exit 0).
loop-detect.sh
Tracks recent edits to the same file in a session-scoped temp file. Warns at 4 edits, signals (exit 2) at 6 — a nudge to re-read the goal instead of looping.
quality-gate.sh
After an edit, runs a fast language-appropriate check (TS → tsc --noEmit; JS → npm run lint; Python → ruff/py_compile; Go → go vet; Rust → cargo check) under a 30 s timeout and records the verdict to .hook-state/last_quality_gate.json. Never blocks — it persists evidence for stop-gate.sh to act on. Skipped silently when no suitable tool is found.
auto-lint.sh · opt-in
Runs the project linter (eslint, ruff, clippy, …) after edits.
auto-format.sh · opt-in
Runs the project formatter (prettier, black, gofmt, rustfmt, …) after edits.
skill-compliance.sh · opt-in
Surfaces checklist items from any skill relevant to the edited file.
PostToolUse — Bash
bash-budget.sh
Tracks cumulative Bash output (chars ÷ 4 ≈ tokens) across the session and emits a one-shot warning when it crosses a threshold (BASH_BUDGET_THRESHOLD, default 50 000) — Bash output is the #1 context-window consumer. Measures and signals; never rewrites or truncates.
PostToolUse — Read
read-budget.sh
Symmetric to bash-budget for file reads: tracks cumulative Read output (chars ÷ 4 ≈ tokens) and emits a one-shot warning when it crosses READ_BUDGET_THRESHOLD (default 100 000) — a nudge toward tiered, on-demand loading instead of reading whole trees. Never blocks; writes .hook-state/read-budget.json.
PostToolUse — Task
subagent-post.sh
Closes the latest open row in .hook-state/agent-invocations.jsonl with finished_at + duration_seconds, completing the sub-agent telemetry pair.
PostToolUseFailure
tool-failure-observe.sh
Fires after a tool call errors (a Bash non-zero exit, a failed Edit, …). Pure observability — it can't prevent the failure. Counts failures per session by tool in .hook-state/tool-failures.json; session-end.sh folds the total into the /scorecard so a thrashing session (lots of failed tool calls) is visible. Always exits 0.
Stop
stop-gate.sh
Reads .hook-state/last_quality_gate.json and blocks completion (exit 2) when the last quality gate failed — enforcing CLAUDE.md → Verification deterministically. Escape hatch: SKIP_QUALITY_GATE=1 (or CLAUDE_SKIP_QUALITY_GATE=1). Runs before task-complete-notify.sh.
task-complete-notify.sh
Desktop notification + sound when Claude finishes (after stop-gate.sh clears). macOS (osascript) and Linux (notify-send).
StopFailure
stop-failure-observe.sh
Fires only when a turn ends on an API-level error (rate limit, auth, server) — not on a deliberate stop-gate block. Its stdout/exit are ignored by Claude Code (notification/logging only), so it just records the API-error count + last message in .hook-state/stop-failures.json. The /scorecard reads it to tell "the session died on infra" from "the agent skipped its steps".
SessionEnd
session-end.sh
Appends a JSON-line audit record to reports/session-audit.log (session id, reason, duration, last quality-gate status) plus scorecard inputs, so /scorecard can summarize sessions. The reports/ directory is self-gitignored.
journal-fold.sh
Folds /note journal findings/decisions — and the inter-agent handoff scratchpad — into tasks/handoff-<session-id>.md at session end, so mid-session memory survives into the next session.