Hooks Implementation — Automate with Lifecycle Hooks
About 10 minutes
By configuring hooks (Hooks), you can automatically execute any shell commands at the timing when Claude Code runs tools or returns responses. This page covers the 4 hook event types, environment variables, the meaning of exit codes, and how to configure them in .claude/settings.json — in order.
What Are Hooks?
Section titled “What Are Hooks?”A hook is a mechanism that allows shell commands to run at specific timings when Claude Code calls tools or generates responses. Hooks are configured in the hooks key of .claude/settings.json.
The main uses of hooks are as follows.
- Logging: Record which tools were called and when to a file
- Validation: Block dangerous commands from being executed
- Auto-formatting: Automatically run a linter after a file is saved
- Notification: Notify via system notification when Claude completes its work
The 4 Hook Event Types
Section titled “The 4 Hook Event Types”Claude Code has 4 hook event types.
| Event | Timing | Main Use |
|---|---|---|
PreToolUse | Before tool execution | Validation, blocking |
PostToolUse | After tool execution | Logging, notification, post-processing |
UserPromptSubmit | When the user inputs | Prompt transformation, pre-processing |
Stop | When the response ends | Completion notification, post-processing |
PreToolUse is called before tool execution, so it functions as a gatekeeper that can block the use of specific tools. PostToolUse is called after tool execution, so it is suitable for adding side effects (log writing, notifications, etc.).
The Structure of the Configuration File
Section titled “The Structure of the Configuration File”Hooks are configured in the hooks key of .claude/settings.json.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo 'Tool: $CLAUDE_TOOL_NAME'"
}
]
}
]
}
}The structure of the configuration is as follows.
hooks: The root key for hook configurationPreToolUse/PostToolUse/UserPromptSubmit/Stop: Event namematcher: Which tool name to apply the hook to. Writing"Bash"means it only runs when the Bash tool is used. When omitted, it applies to all toolstype: The type of hook. Currently only"command"is supportedcommand: The shell command to execute
Environment Variables
Section titled “Environment Variables”During hook execution, Claude Code provides the following environment variables.
| Variable | Content | Available Events |
|---|---|---|
$CLAUDE_TOOL_NAME | Name of the called tool (e.g., Bash, Read) | PreToolUse, PostToolUse |
$CLAUDE_TOOL_INPUT | Input to the tool (JSON format) | PreToolUse, PostToolUse |
$CLAUDE_TOOL_RESULT | Tool execution result (JSON format) | PostToolUse only |
Since $CLAUDE_TOOL_INPUT is a JSON string, you can use the jq command to extract specific fields.
# Example of extracting the command string passed to the Bash tool
echo "$CLAUDE_TOOL_INPUT" | jq -r '.command'The Meaning of Exit Codes
Section titled “The Meaning of Exit Codes”The behavior of Claude Code changes depending on the exit code of the hook script.
| Exit code | Meaning |
|---|---|
0 | Success. Continue tool execution |
1 | Error. If PreToolUse, blocks tool execution |
2 | Non-blocking error. Display an error message but continue execution |
If a PreToolUse hook returns exit 1, Claude aborts that tool’s execution. Even if a PostToolUse hook returns exit 1, the tool operations already executed cannot be undone.
Best Practices
Section titled “Best Practices”Keep Hooks Fast
Section titled “Keep Hooks Fast”If hooks perform heavy processing (network communication, large file processing, etc.), Claude’s response slows down. Run heavy processing in the background.
# Example of running in the background (add & to background it)
some-heavy-command "$CLAUDE_TOOL_NAME" &Make Errors Explicit
Section titled “Make Errors Explicit”When a hook blocks, always output an error message before exit 1. Without a message, the user won’t know why it was blocked.
echo 'ERROR: npm run build requires explicit user approval.'
exit 1Design PreToolUse Carefully
Section titled “Design PreToolUse Carefully”If too many blocking hooks are set, Claude Code becomes hard to use. Narrow down the blocking conditions specifically to avoid accidentally blocking necessary operations.
Keep Logs
Section titled “Keep Logs”Recording tool usage logs with a PostToolUse hook to a file is useful for auditing and debugging.
echo "$(date): $CLAUDE_TOOL_NAME" >> /tmp/claude-tool-log.txtWrite Hook Scripts to Be Unit-Testable
Section titled “Write Hook Scripts to Be Unit-Testable”Rather than writing long commands directly in the command field, it is easier to test and maintain if you extract them as shell scripts in a separate file and call them.
Hands-on Exercise
Section titled “Hands-on Exercise”Step 1: Check the Current Hook Configuration
Section titled “Step 1: Check the Current Hook Configuration”cat .claude/settings.jsonCheck the presence of the hooks key and the current configuration contents.
✅ Verify: If the file contents are displayed, the read was successful. If the
hookskey is absent, add it in the next step.
Step 2: Add a PreToolUse Hook to Prevent Accidentally Running npm run build
Section titled “Step 2: Add a PreToolUse Hook to Prevent Accidentally Running npm run build”Add the following configuration to the hooks section of .claude/settings.json.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -q 'npm run build'; then echo 'ERROR: npm run build requires explicit user approval.'; exit 1; fi"
}
]
}
]
}
}This hook blocks the Bash tool input and displays an error message if it contains npm run build.
✅ Verify: After saving
.claude/settings.json, confirm withcat .claude/settings.jsonthat the configuration has been correctly recorded.
Step 3: Confirm the Hook Blocks Correctly
Section titled “Step 3: Confirm the Hook Blocks Correctly”In a Claude Code session, make the following request.
Please run npm run buildWhen Claude tries to run npm run build with the Bash tool, confirm that the hook returns exit 1 and blocks it.
✅ Verify: If Claude’s response contains something to the effect that “npm run build was blocked” or “explicit user approval is required,” the hook is working correctly.
Step 4: Add a Stop Hook to Notify macOS When Work Is Complete
Section titled “Step 4: Add a Stop Hook to Notify macOS When Work Is Complete”Add a Stop hook that sends a macOS notification when Claude finishes a response.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -q 'npm run build'; then echo 'ERROR: npm run build requires explicit user approval.'; exit 1; fi"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude has finished working\" with title \"Claude Code\"'"
}
]
}
]
}
}✅ Verify: If “Claude has finished working” appears in the macOS Notification Center when Claude returns the next response, it is a success. On non-macOS environments, replace
osascriptwith the appropriate command.
Summary
Section titled “Summary”- Hooks are a mechanism to run shell commands at specific timings in the Claude Code lifecycle
- The 4 event types are
PreToolUse(before tool),PostToolUse(after tool),UserPromptSubmit(on input), andStop(on end) - During hook execution,
$CLAUDE_TOOL_NAME,$CLAUDE_TOOL_INPUT, and$CLAUDE_TOOL_RESULTare available - Returning
exit 1inPreToolUsecan block tool execution - Configure hooks in the
hookskey of.claude/settings.json, designing them to be lightweight, explicit, and testable
Next Steps
Section titled “Next Steps”- MCP Plugins Integration — Learn about configuring MCP plugins that integrate external tools with Claude Code
- Harness Testing and Validation — Move on to systematically verifying that hooks function as intended
Frequently Asked Questions
Section titled “Frequently Asked Questions”Q: How does Claude behave when blocked by a PreToolUse hook?
A: When the hook returns exit 1, Claude does not execute that tool and receives the error message written to stdout as context. After that, Claude tries a different approach or reports to the user based on the content of the error.
Q: If a PostToolUse hook returns exit 1, can the tool’s execution result be cancelled?
A: It cannot be cancelled. PostToolUse is called after tool execution, so operations that have already completed (such as file writes) cannot be undone. Perform pre-execution validation with PreToolUse.
Q: What happens if matcher is omitted?
A: The hook executes for all tool calls. Not just Bash, but also Read, Write, and Glob become targets, so if the hook performs heavy processing, the response may slow down significantly. If you want to target only specific tools, explicitly specify matcher.
Q: Is it necessary to restart Claude Code after changing the hook configuration?
A: Changes to .claude/settings.json are read across sessions. To reflect changes in the current session, restart Claude Code or start a new session.
Q: What command can be used instead of osascript on Windows?
A: On Windows, you can use powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('Claude has finished working')". On Linux, notify-send "Claude has finished working" works (requires libnotify).
See the references for the external specifications and background sources used on this page.[1][2]
References
Section titled “References”- Anthropic, Claude Code documentation
- Anthropic, Claude API documentation