Claude Academy
Sign in

Vault / wiki/201/claude-code-hooks.md

updated 2026-05-28

Claude Code Hooks

Shell commands that fire on lifecycle events. The harness — not the model — runs them. This is how you build deterministic guardrails Claude can't ignore.

Event types

EventFires when
UserPromptSubmitUser submits a prompt
PreToolUseBefore a tool runs (can block)
PostToolUseAfter a tool runs
StopMain agent finishes a turn
SubagentStopA subagent finishes
NotificationDisplay-only notifications
SessionStartNew session begins

Configuration

In settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {"type": "command", "command": "pnpm lint --fix"}
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {"type": "command", "command": "./scripts/check-bash.sh"}
        ]
      }
    ]
  }
}

Hooks receive a JSON payload over stdin with event details. They can:

  • Print to stdout — shown to the user.
  • Exit code 0 — allow.
  • Exit code 1 (UserPromptSubmit-blocking) or 2 (PreToolUse-blocking) — block the action; stdout is sent to Claude as the reason.

Typical uses

  • Auto-format / auto-lint on Edit|Write.
  • Run tests on Stop (catches regressions before turn ends).
  • Block dangerous bash (rm -rf /, etc.) on PreToolUse.
  • Notify Slack on Stop when CI builds.
  • Audit log every tool call.

Hook vs permission

  • Permission = static yes/no based on the call.
  • Hook = dynamic — can inspect actual command, run other tools, format reasons.

Use permissions for blanket policies, hooks for context-dependent checks.

CCA-F angle

The CI/CD scenario tests hook patterns: how to fail a Claude Code run when tests don't pass, how to enforce conventional-commit messages, how to scan commits for secrets before push. Hooks are the answer.

See also