Claude Code hooks are shell commands the harness runs automatically on events — before or after a tool runs, when a session starts, or when you submit a prompt. They let you enforce policies and automate your workflow deterministically, outside the model’s judgment. Here’s every event, how to add one, and when to reach for a hook.
Each event fires at a specific point and receives event-specific JSON on stdin.
PreToolUseBefore a tool runs. Can allow, deny, or ask — the deterministic place to block risky actions.
PostToolUseAfter a tool completes. Add advisory context or run follow-ups like format-on-save or tests.
UserPromptSubmitWhen you submit a prompt. Inject context or block the prompt before Claude sees it.
SessionStartAt session start, resume, or clear. Inject repo and branch context into the conversation.
SessionEndWhen a session ends. Clean up temporary state or log the session.
StopWhen the main agent finishes responding. Common for “run the test suite on stop”.
SubagentStopWhen a spawned subagent finishes its work.
PreCompactJust before Claude Code compacts the context window.
NotificationOn notifications — for example when Claude needs permission or goes idle.
Format every file right after Claude edits it — a PostToolUse hook in your settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}Hooks receive the event JSON on stdin. Exit 0 to continue (optionally printing additionalContext to advise Claude); exit 2 to block the action.
The three ways to change Claude Code’s behavior are not interchangeable:
Full breakdown in the anatomy of a 2026 Claude Code plugin.
See advisory, fail-safe hooks in action:
Hooks are shell commands the Claude Code harness runs automatically on events — before or after a tool runs, at session start, when you submit a prompt, and more. Because the harness runs them (not the model), they fire deterministically every time, which makes them ideal for enforcing policies and automating your workflow.
PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, Stop, SubagentStop, PreCompact, and Notification. Each event carries event-specific JSON on stdin — the tool name, tool input and output, the prompt, or session info.
Add a hooks block to your settings.json (user or project scope), or ship a hooks/hooks.json file inside a plugin. Each entry has a matcher (for example "Edit|Write") and a command to run. Plugins reference bundled scripts with ${CLAUDE_PLUGIN_ROOT} so they resolve wherever the plugin is installed.
Yes. A PreToolUse hook can deny a tool (exit code 2, or a permissionDecision of "deny"), which is how you enforce hard rules like “never write outside the repo” or “never commit a secret”. Exit 0 with additionalContext is advisory — it surfaces a suggestion to Claude without blocking.
In hooks/hooks.json at the plugin root, which Claude Code auto-detects. The command references bundled scripts via ${CLAUDE_PLUGIN_ROOT} so they resolve to wherever the plugin is installed.
Hooks run arbitrary commands with your permissions, so only install hooks you trust. Well-built plugin hooks are fail-safe (any error exits 0 and does nothing) and advisory by default, so they surface a suggestion without ever blocking your work or breaking a session.
A well-written hook is near-instant. Keep hook commands fast and fail-safe; heavy or long-running work belongs in a slash command or a subagent, not a hook.