Hooks are shell commands (or HTTP calls, MCP tool calls or model prompts) that Claude Code runs at fixed points in its lifecycle, such as before a tool runs or when Claude finishes a reply. Unlike instructions in CLAUDE.md, a hook always runs. This tool writes the JSON for you so the nesting and field names are right.
How do I use this tool?
- Choose an event, for example
PostToolUse. - If the event supports matchers, enter one, such as
Edit|Write. Leave it empty to match everything. - Enter the command and an optional timeout in seconds.
- Add more handlers or events if needed, or load a recipe.
- Copy the JSON into the right settings file and run
/hooksin Claude Code to confirm it appears.
What does the hooks JSON look like?
The structure has three levels: event, then a list of matcher groups, then a list of handlers.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write", "timeout": 60 }
]
}
]
}
}
Handler types are command, http, mcp_tool, prompt and agent (agent is experimental). The default timeout is 600 seconds for command, http and mcp_tool, 30 for prompt and 60 for agent. Hook input arrives on stdin as JSON with fields such as tool_name and tool_input.
Which events accept a matcher?
| Event | Matcher filters |
|---|---|
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied | Tool name, e.g. Bash, Edit|Write, mcp__.* |
SessionStart | startup, resume, clear, compact, fork |
SessionEnd | clear, resume, logout, prompt_input_exit, other |
Notification | Type, e.g. permission_prompt, idle_prompt |
SubagentStart, SubagentStop | Agent type |
PreCompact, PostCompact | manual, auto |
FileChanged | Literal filenames, e.g. .envrc|.env |
Stop, UserPromptSubmit, PostToolBatch, CwdChanged and others | No matcher, fires every time |
The tool lists all 33 documented events. A matcher made only of letters, digits, _, -, spaces, commas and | is an exact name or list. For FileChanged and StopFailure, only letters, digits, _ and | count as an exact list, so separate values with |. Anything else is an unanchored JavaScript regex, so Edit.* also matches NotebookEdit. A matcher on an event without matcher support is ignored. Full details are in the hooks reference.
What do exit codes mean?
- 0: success. If stdout is a single JSON object it is parsed for fields such as
decision,continueorhookSpecificOutput. - 2: blocking error. On
PreToolUsethe tool call is blocked and stderr goes to Claude as the reason. OnStopClaude keeps working. - Any other code: non-blocking error. The action goes ahead. Exit 1 does not block, unless a command or http hook sets
"onFailure": "block"(Claude Code v2.1.295 or later).
Where do I put the hooks block?
Use ~/.claude/settings.json for all your projects, .claude/settings.json to share with your team through git, or .claude/settings.local.json for this project only. Managed policy settings apply to a whole organisation. Settings precedence from highest is managed, command line, local project, shared project, then user. Claude Code reloads settings files when they change, so most hook edits apply without a restart. In commands, use "$CLAUDE_PROJECT_DIR" to reach scripts in your repository.
Recipes included
- Format after edit:
PostToolUse, matcherEdit|Write, runs Prettier on the edited file. - Block edits to .env:
PreToolUse, matcherEdit|Write, runs a script that exits 2 when the path contains.env. - Notify when Claude needs you:
Notificationwithosascripton macOS,notify-sendon Linux or a PowerShell message box on Windows. - Run tests before stopping:
Stophook that runs your test command and exits 2 on failure, after checkingstop_hook_active. - Git status at session start:
SessionStart, matcherstartup|resume, runsgit status --short --branch. Its stdout is added as context.
Common mistakes
- Using exit 1 to block. Only exit 2 or a JSON decision blocks, unless you set
onFailuretoblock. - A Stop hook that never checks
stop_hook_active. Claude Code overrides a Stop hook after eight blocks in a row with no tool call in between, but your hook should exit early itself. - Forgetting
chmod +xon hook scripts on macOS and Linux. - A bare
mcp__memorymatcher. It matches nothing; usemcp__memory__.*. - Assuming
jqis installed. The official recipes use it to read stdin.
Hooks work best next to a clear CLAUDE.md and well-scoped skills. For the step-by-step guide, see the official hooks guide. Need hooks wired into CI or your internal tools? See our custom software development service.
Last updated: October 19, 2026