Claude Code Hooks give you a precise way to weave your intentions into AI-assisted coding sessions. By injecting custom scripts before and after tool calls, you can automate repetitive tasks like code quality checks, activity logging, and external notifications — turning a good development flow into a great one.
What Are Claude Code Hooks?
Claude Code Hooks let you run shell commands or scripts at specific moments during a Claude Code session — before or after any tool is executed. Instead of the traditional loop of "AI works → human reviews → move on," Hooks enable a more refined cycle: "AI works → automated verification/notification → move on."
Here are a few scenarios where Hooks really shine:
Automatic code quality checks: Run a linter or formatter every time Claude edits a file and catch issues immediately.
Change history logging: Record which files were touched and when, making debugging and audits far easier.
External service notifications: Integrate with Slack or email to automatically alert team members when key tasks are completed.
Security guardrails: Detect access to sensitive files and block the operation if needed.
The Hooks Configuration File
Hooks are configured in ~/.claude/settings.json (global) or .claude/settings.json at the project root (project-specific). Here's a basic example:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo '🔧 Pre-Bash: $CLAUDE_TOOL_INPUT' >> /tmp/claude-hooks.log"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "cd $CLAUDE_PROJECT_DIR && npx eslint --fix $CLAUDE_TOOL_INPUT_FILE_PATH 2>/dev/null || true"
}
]
}
]
}
}Each entry specifies a matcher (the tool name to target) and a command to run. The configuration is straightforward JSON — easy to version-control and share with your team.
The Four Hook Types
Claude Code supports four distinct timing points for running scripts. Understanding each one is the foundation of effective Hooks usage.
PreToolUse: Runs Before a Tool Executes
PreToolUse fires immediately before Claude calls a tool. Its standout feature is the ability to block the tool from running based on your script's exit code: exit 0 to allow the tool to proceed, or exit non-zero to stop it.
This makes PreToolUse ideal for access control and catching dangerous operations before they happen.
#!/bin/bash
# Example: Block direct production deployments
TOOL_INPUT="$CLAUDE_TOOL_INPUT"
if echo "$TOOL_INPUT" | grep -q "production" && echo "$TOOL_INPUT" | grep -q "deploy"; then
echo "⚠️ Production deployments require a review step."
exit 1 # Block the tool call
fi
exit 0 # Allow normallyPostToolUse: Runs After a Tool Completes
PostToolUse fires right after a tool finishes. Since you have access to the tool's output, it's perfect for post-processing and verification.
Auto-formatting after edits is one of the most popular uses:
#!/bin/bash
# Example: Auto-run Prettier after every Edit
FILE="$CLAUDE_TOOL_INPUT_FILE_PATH"
if [[ "$FILE" =~ \.(js|ts|jsx|tsx|json|css)$ ]]; then
npx prettier --write "$FILE" 2>/dev/null
echo "✅ Formatted: $FILE"
fiStop: Fires When Claude Code Finishes a Session
The Stop hook runs exactly once when Claude Code ends a session. It's a natural fit for completion notifications and generating final summaries.
#!/bin/bash
# Example: Send a Slack notification on session end
SESSION_ID="$CLAUDE_SESSION_ID"
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-type: application/json' \
-d "{\"text\":\"✅ Claude Code session finished (ID: ${SESSION_ID})\"}"Notification: Fires on Notification Events
The Notification hook runs when Claude Code emits a notification — for example, when it pauses waiting for user input. This is useful for relaying mid-session status updates to external systems during long-running tasks.
Environment Variables Available in Hooks
When a Hook script runs, Claude Code automatically sets several environment variables so your script has the context it needs:
| Variable | Description |
|---|---|
| CLAUDE_TOOL_NAME | Name of the tool being called (e.g., Bash, Edit) |
| CLAUDE_TOOL_INPUT | Raw input passed to the tool (JSON) |
| CLAUDE_TOOL_INPUT_FILE_PATH | File path when the Edit tool is used |
| CLAUDE_PROJECT_DIR | Root directory of the current project |
| CLAUDE_SESSION_ID | Unique identifier for the session |
| CLAUDE_HOOK_EVENT_NAME | Name of the hook event that fired |
Combining these variables lets you write scripts that adapt to context — applying different logic depending on the tool, file type, or project.
Controlling Scope with the matcher Field
The matcher field determines which tools trigger a given hook. You can use exact tool names or regular expressions.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "/path/to/auto-lint.sh"
}
]
}
]
}
}The example above triggers the lint script after both Edit and Write tool calls. Setting matcher to an empty string will match all tools.
Practical Recipes
Recipe 1: Automatic TypeScript Type Checking
#!/bin/bash
# auto-typecheck.sh
FILE="$CLAUDE_TOOL_INPUT_FILE_PATH"
if [[ "$FILE" =~ \.tsx?$ ]]; then
cd "$CLAUDE_PROJECT_DIR"
npx tsc --noEmit --skipLibCheck 2>&1 | head -20
fi{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [{ "type": "command", "command": "/path/to/auto-typecheck.sh" }]
}
]
}
}Recipe 2: Secret Detection in Code
Prevent accidental commits of API keys, passwords, and other credentials:
#!/bin/bash
# secret-detector.sh
FILE="$CLAUDE_TOOL_INPUT_FILE_PATH"
PATTERNS="(API_KEY|SECRET|PASSWORD|TOKEN|PRIVATE_KEY)\s*=\s*['\"][^'\"]{8,}"
if [ -f "$FILE" ] && grep -qE "$PATTERNS" "$FILE"; then
echo "🚨 Potential secret detected in: $FILE"
grep -nE "$PATTERNS" "$FILE" | head -5
exit 1 # Block the write
fi
exit 0Recipe 3: Automatic Activity Logging
#!/bin/bash
# activity-logger.sh
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
TOOL="$CLAUDE_TOOL_NAME"
FILE="${CLAUDE_TOOL_INPUT_FILE_PATH:-N/A}"
LOG_FILE="$HOME/.claude/activity.log"
echo "[$TIMESTAMP] $TOOL → $FILE" >> "$LOG_FILE"Set this as a PostToolUse hook with an empty matcher, and every Claude Code action gets recorded to ~/.claude/activity.log automatically.
Recipe 4: Automatic Test Runs
#!/bin/bash
# auto-test.sh
FILE="$CLAUDE_TOOL_INPUT_FILE_PATH"
PROJECT_DIR="$CLAUDE_PROJECT_DIR"
if [[ "$FILE" =~ \.(test|spec)\.(js|ts)$ ]]; then
cd "$PROJECT_DIR"
npx jest "$FILE" --passWithNoTests 2>&1 | tail -10
fiTips for Safe and Reliable Hooks
Because Hooks execute shell commands directly, a misconfiguration can have unintended consequences. Keep these practices in mind:
Handle errors gracefully: Avoid set -e indiscriminately — use || true when you intentionally want a command to fail without stopping the Hook.
Set execute permissions: Don't forget chmod +x your-script.sh. If a Hook silently does nothing, missing permissions are often the culprit.
Use debug logging: While developing your scripts, write debug output to a log file (echo "debug info" >> /tmp/hooks-debug.log). It makes diagnosing issues much faster.
Design for idempotency: Hooks may run multiple times. Make sure running the same Hook twice in a row won't cause problems — especially for scripts that create files or modify state.
Closing Thoughts: Build a Smarter Development Environment
Once configured, Claude Code Hooks quietly do their job every session — enforcing quality, recording history, and keeping you in the loop without any extra effort. Start small with a simple activity log, then layer in auto-formatting and security checks as you go.
The goal is a virtuous cycle: "AI works → quality is automatically ensured → you focus on what matters most." With the right Hooks in place, that cycle becomes your everyday reality.
🌟 Go Deeper with Premium Content
Claude Lab offers premium articles that take Hooks even further:
- Combining MCP servers with Hooks — building advanced workflows that talk to external APIs
- Team-wide Hooks standardization — best practices for sharing Hook configs across an organization
- Claude Code + CI/CD integration — practical recipes for connecting Claude Code to GitHub Actions
Premium membership (from ¥280/month) gives you full access to all of these deep-dive articles. If you want to get the most out of Claude AI in your daily development work, we'd love to have you on board.