Zum Inhalt springen

Hook Events & Handlers

Knowledge

The Most Important Hook Events

Claude Code fires events at 24 points in the workflow. Each event is an intervention point where you can enforce deterministic behavior. Here are the most important ones:

EventWhen Does It Fire?Typical Use
PreToolUseBefore every tool executionValidation, blocking dangerous actions
PostToolUseAfter every tool executionAuto-formatting, linting, type checks
SessionStartWhen a new session startsLoad context, check environment
StopClaude has finished the taskQuality gates, final checks
UserPromptSubmitBefore a prompt is processedContext injection, prompt enrichment
SubagentStartSubagent is launchedMonitoring, configuration
SubagentStopSubagent has a resultResult validation
TaskCreatedNew task in agent teamsCoordination, logging
TaskCompletedTask completedQuality control
PreCompactBefore context compactionPreserve important info
PostCompactAfter context compactionRestore context
WorktreeCreateGit worktree createdSetup, initialization
WorktreeRemoveGit worktree removedCleanup
NotificationClaude is waiting for inputDesktop notification

iPreToolUse and PostToolUse Are the Stars

These two events cover 80% of all use cases. PreToolUse blocks dangerous actions, PostToolUse ensures automatic quality assurance after every change.

The 4 Handler Types

Every Hook needs a handler -- the logic that executes when the event fires:

1. Command Handler (Default)

The most common type. Executes a shell script and receives the event context as JSON via stdin.

{
  "type": "command",
  "command": "npx prettier --write $TOOL_INPUT_FILE_PATH"
}

The script receives all relevant information: which tool was called, with which arguments, and what the result was. Perfect for linting, tests, formatting, and security checks.

2. HTTP Handler

Sends a POST request to an HTTP endpoint. Ideal for logging, webhooks, or integration with external systems.

{
  "type": "http",
  "url": "https://your-server.com/hooks/log",
  "headers": { "Authorization": "Bearer $TOKEN" }
}

3. Prompt Handler

Uses a lightweight LLM (Haiku) for a single evaluation turn. Good for decisions that simulate human judgment.

{
  "type": "prompt",
  "prompt": "Check whether this code change breaks the existing API. Respond only with PASS or FAIL."
}

!Prompt Handlers Are Not Deterministic

The prompt handler uses an LLM -- meaning its responses can vary. For hard rules like "no force push," a command handler is better suited.

4. Agent Handler

The most powerful type: A multi-turn LLM with tool access that can execute up to 50 turns. For complex checks like "Has this change updated all tests?"

{
  "type": "agent",
  "prompt": "Check whether the changed files have corresponding test updates. Run the tests and report.",
  "maxTurns": 10
}

Exit Codes: The Language of Hooks

Hooks communicate with Claude Code through exit codes:

Exit CodeMeaningBehavior
0All OKContinue action. stdout is added to the context
2BlockAction is PREVENTED. stderr is returned as feedback to Claude
OtherNon-blocking errorAction continues. stderr is shown in verbose mode (Ctrl+O)

This is elegant: Exit code 0 means "continue," exit code 2 means "stop." Everything else is treated as a non-critical error and proceeds.

#!/bin/bash
# Example: Block force-push
if echo "$1" | grep -q "push.*--force"; then
  echo "Force-push is not allowed!" >&2
  exit 2
fi
exit 0

Matchers and if Filters

Not every Hook should fire on every tool call. There are two filtering mechanisms for this:

Matcher: Regex on the Tool Name

{
  "matcher": "Edit|Write",
  "hooks": [{ "type": "command", "command": "npx prettier --write" }]
}

This Hook only fires on Edit and Write calls, not on Bash or Read.

if Filter: Tool + Argument

{
  "matcher": "Bash",
  "if": "Bash(git commit*)",
  "hooks": [{ "type": "command", "command": "npm test" }]
}

The if filter is powerful: It matches not only the tool name but also the arguments. Bash(git commit*) fires only when Claude wants to execute a git commit command -- not on every Bash call.

*Combining Matcher + if

The matcher filters broadly by tool type, the if filter refines by arguments. Together, they provide precise control without overhead.

Where Hooks Are Stored

Hooks can be defined in four locations, with ascending priority:

  1. Global (~/.claude/settings.json) -- Applies to all projects. Ideal for personal preferences like formatting or notifications.
  2. Project (.claude/settings.json) -- Applies to the entire team. Committed to the Git repository.
  3. Local (.claude/settings.local.json) -- Personal project overrides. NOT committed (in .gitignore).
  4. Skill/Agent Frontmatter -- Hooks within a Skill or agent definition. Applies only to that Skill.

iTeam Hooks vs. Personal Hooks

Quality hooks like linting and tests belong in the project settings (.claude/settings.json) so the whole team benefits. Desktop notifications or personal formatting preferences belong in the local or global config.

Understand

Try out the Hook Event Simulator: Select a scenario and observe which events fire in what order. Click on individual events to see details and example configurations.

Claude Code writes a file using the Write tool.

ActionFile is being written

Click on a hook event to see details. Use arrow keys to navigate.

Apply

Consider for your current project: Which actions should ALWAYS run after a file change? Which commands should NEVER be executed without a check? The answers will show you which events and handlers you need. In the next section, you'll get ready-made recipes to copy.