Hooks, part one: events and configuration
Hooks run your own logic on lifecycle events — deterministically, every time the event fires. The main events:
| Event | When | Use for |
|---|---|---|
| PreToolUse | Before a tool runs | Validation, approve/deny/modify |
| PostToolUse | After a tool completes | Feedback, formatting, logging |
| UserPromptSubmit | You submit a prompt | Add context, validate |
| Stop / SubagentStop | Agent considers stopping | Completeness checks |
| SessionStart / SessionEnd | Session boundaries | Load context / cleanup |
| PreCompact | Before compaction | Preserve critical info |
| Notification | Claude notifies you | Logging, reactions |
{ "hooks": { "PostToolUse": [{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write", "timeout": 30 }] }] }}Matchers select which tools trigger the hook: exact ("Write"), alternation ("Read|Write|Edit"), wildcard ("*"), or regex against MCP tools ("mcp__.*__delete.*"). Hooks receive JSON on stdin (tool_name, tool_input, cwd…) and speak through exit codes: 0 = success, 2 = blocking error whose stderr is fed back to Claude.
Check your understanding
Section titled “Check your understanding”Question 1Which exit code makes a hook block the action and feed stderr back to Claude?
Exit 2 is the blocking error; 0 is success with stdout in the transcript.
Question 2You edited hooks.json mid-session. What must you do?
Hooks are loaded at session start and cannot be hot-swapped.
Question 3Which matcher fires on every MCP tool?
Matchers are regex-capable; mcp__.* matches all MCP tool names.