Hooks: Automatic Actions
Shell commands that fire deterministically on lifecycle events. Unlike asking Claude to remember something, hooks guarantee it always happens. 24+ events, 4 hook types, zero guesswork.
Event Fires
Claude edits a file → PostToolUse event triggers
JSON Piped to stdin
Hook receives {"tool_name":"Edit","tool_input":{"file_path":"..."}}
Hook Command Runs
npx prettier --write "$FILE_PATH" executes
Result Returned
Exit 0 = success. Exit 2 = block action. stdout fed back to Claude as context.
What Are Hooks?
Hooks are deterministic automation — shell commands, LLM prompts, HTTP calls, or even full agent tasks that execute at specific points in Claude Code's lifecycle. They don't rely on Claude "remembering" to do something. They just run.
The key insight: hooks handle the predictable stuff (formatting, validation, notifications) so Claude can focus on the creative problem-solving.
How Data Flows
Every hook receives structured JSON on stdin and communicates back via exit codes and stdout.
All 24+ Hook Events
Claude Code exposes events across the entire session lifecycle. Grouped by category:
Session Lifecycle
| Event | When It Fires | Use For |
|---|---|---|
SessionStart | Session begins or resumes | Load context, validate environment, restore state |
SessionEnd | Session terminates | Archive state, write summaries, cleanup |
Stop | Claude finishes its turn | Run tests, verify completeness, send notifications |
StopFailure | Turn ends due to API error | Log errors, send alerts |
Tool Execution
| Event | When It Fires | Use For |
|---|---|---|
PreToolUse | Before a tool call executes | Block dangerous commands, validate inputs |
PostToolUse | After a tool call succeeds | Format code, lint, log activity |
PostToolUseFailure | After a tool call fails | Log failures, retry logic |
User Interaction
| Event | When It Fires | Use For |
|---|---|---|
UserPromptSubmit | User sends a message | Inject dynamic context, modify prompts, memory recall |
PermissionRequest | Permission dialog appears | Auto-approve trusted operations |
PermissionDenied | Tool call denied by auto mode | Log denials, suggest alternatives |
Notification | Claude sends a notification | Custom notification routing |
Agents & Tasks
| Event | When It Fires | Use For |
|---|---|---|
SubagentStart | Subagent spawned | Log agent activity, resource tracking |
SubagentStop | Subagent finishes | Aggregate results, cleanup |
TaskCreated | Task created via TaskCreate | External task tracking integration |
TaskCompleted | Task marked complete | Progress notifications |
TeammateIdle | Agent team member goes idle | Rebalance work, notify |
Configuration & Context
| Event | When It Fires | Use For |
|---|---|---|
CwdChanged | Working directory changes | Reload environment, switch contexts |
FileChanged | Watched file changes | Reload .env, trigger rebuilds |
ConfigChange | Config file modified | Audit config changes, reload settings |
InstructionsLoaded | CLAUDE.md/rules loaded | Validate instructions, inject extras |
PreCompact | Before context compaction | Save critical state before compression |
PostCompact | After context compaction | Re-inject critical context |
WorktreeCreate | Worktree created | Initialize worktree environment |
WorktreeRemove | Worktree removed | Cleanup temporary state |
Hook Types
Hooks come in four flavors, from simple to sophisticated:
Command
Run a shell command. Exit code 0 = allow, exit code 2 = block. The simplest and most common type. No extra cost.
Prompt
A single LLM call for judgment-based decisions. Uses Haiku by default. Consumes a small number of tokens per invocation.
Agent
Multi-turn verification with tool access. For complex validation that needs to read files, run commands, and reason. Consumes more tokens.
HTTP
POST event data to external endpoints. Send events to Slack, log to a dashboard, trigger CI/CD pipelines. No token cost, but needs an endpoint.
Real Examples
These are production-ready hook configurations. Drop them into your ~/.claude/settings.json.
Auto-format code after every edit
{
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}]
}]
}
}
Runs Prettier on every file Claude edits or writes. The matcher filters which tools trigger this hook.
Block edits to protected files
{
"hooks": {
"PreToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "jq -r '.tool_input.file_path' | grep -qE '(\\.env|package-lock\\.json|yarn\\.lock)$' && exit 2 || exit 0"
}]
}]
}
}
Exit code 2 blocks the action. Prevents Claude from modifying .env, lockfiles, or other protected files.
Block dangerous shell commands
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "echo \"$CLAUDE_TOOL_INPUT\" | grep -qE 'rm -rf|DROP TABLE' && exit 2 || exit 0"
}]
}]
}
}
Catches destructive patterns before they run. Add your own patterns to the regex.
Desktop notification when Claude needs input
{
"hooks": {
"Notification": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}]
}]
}
}
macOS notification. Replace osascript with notify-send on Linux.
Audit-log all Bash commands
{
"hooks": {
"PostToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
}]
}]
}
}
Logs every Bash command Claude runs. Useful for security reviews and compliance.
Run tests before Claude stops
{
"hooks": {
"Stop": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "npm test 2>&1 | tail -5"
}]
}]
}
}
Runs your test suite every time Claude finishes. Test output is shown to Claude as context.
Audit config changes
{
"hooks": {
"ConfigChange": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "jq -c '{timestamp: now | todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
}]
}]
}
}
Creates an audit trail of every configuration change. Useful for enterprise environments.
Prompt-based: verify task completion
{
"hooks": {
"Stop": [{
"hooks": [{
"type": "prompt",
"prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
}]
}]
}
}
Uses Haiku to judge whether Claude's work is done. If tasks remain, Claude keeps going. Note: consumes tokens per invocation.
Agent-based: verify tests pass before stopping
{
"hooks": {
"Stop": [{
"hooks": [{
"type": "agent",
"prompt": "Verify that all unit tests pass. Run the test suite and check the results.",
"timeout": 120
}]
}]
}
}
A full multi-turn agent that runs tests, reads output, and makes a judgment call. The most powerful hook type. Consumes more tokens.
Auto-approve plan mode exit
{
"hooks": {
"PermissionRequest": [{
"matcher": "ExitPlanMode",
"hooks": [{
"type": "command",
"command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
}]
}]
}
}
Automatically approves exiting plan mode without a permission dialog.
Where to Configure
| Scope | File | Shareable? | Who It Affects |
|---|---|---|---|
| Personal | ~/.claude/settings.json | No | All your projects |
| Project | .claude/settings.json | Yes (commit) | Everyone on this project |
| Local project | .claude/settings.local.json | No (gitignored) | Just you, this project |
| Plugin | plugin-name/hooks/hooks.json | Yes | Anyone with the plugin |
| Enterprise | Managed policy settings | Admin-controlled | Org-wide |
CLAUDE.md instructions are suggestions — Claude might forget. Hooks are guarantees — they run every time, regardless of context. Use CLAUDE.md for guidelines and hooks for enforcement.
Hook Input & Output
Hooks receive event data as JSON on stdin. You can use jq to extract specific fields:
For PreToolUse hooks: exit 0 = allow, exit 2 = block the action. Any other exit code is treated as a hook error (action still proceeds). Make sure your blocking hooks explicitly exit 2.
What Does It Cost?
| Hook Type | Cost | Details |
|---|---|---|
| Command | Included | Runs your shell commands. No extra Claude tokens consumed. |
| HTTP | Included | POSTs to your endpoint. No Claude tokens, but you need a server to receive. |
| Prompt | Tokens | Single Haiku call per invocation. Very cheap but adds up if triggered on every tool use. |
| Agent | Tokens | Multi-turn agent. Most expensive hook type. Use sparingly (e.g., on Stop, not PostToolUse). |