By Sagar Shankaran, Founder of CallSphere
Deep dive into Claude Code hooks — pre and post tool execution hooks that let you enforce linting, run tests automatically, validate changes, and build custom CI-like workflows.
Key takeaways
Claude Code hooks are user-defined shell commands that execute automatically at specific points during Claude Code's agentic workflow. They let you inject custom logic before or after Claude Code performs actions — similar to git hooks, but for AI-assisted development.
Hooks solve a fundamental problem: you want Claude Code to follow specific procedures (run linting after every edit, validate JSON schemas, check for secrets) but you do not want to repeat these instructions in every conversation. Hooks make these procedures automatic and enforceable.
Claude Code supports hooks at several execution points:
flowchart LR
USER(["User message"])
LOOP{"messages.create<br/>agent loop"}
THINK["Extended thinking<br/>optional"]
TOOL{"stop_reason<br/>tool_use?"}
EXEC["Execute tool<br/>append tool_result"]
DONE(["stop_reason<br/>end_turn"])
USER --> LOOP --> THINK --> TOOL
TOOL -->|Yes| EXEC --> LOOP
TOOL -->|No| DONE
style LOOP fill:#4f46e5,stroke:#4338ca,color:#fff
style THINK fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style DONE fill:#059669,stroke:#047857,color:#fff
PreToolUse hooks run before Claude Code executes a tool. They can inspect the planned action and either allow it, modify it, or block it.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit",
"hook": "python3 .claude/hooks/pre-edit-check.py"
}
]
}
}
Use cases:
PostToolUse hooks run after a tool completes. They can inspect the result and trigger follow-up actions.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hook": "npx eslint --fix $CLAUDE_FILE_PATH"
},
{
"matcher": "Write",
"hook": "npx prettier --write $CLAUDE_FILE_PATH"
}
]
}
}
Use cases:
Notification hooks trigger when specific events occur, such as Claude Code requesting user input or completing a long task.
{
"hooks": {
"Notification": [
{
"matcher": "",
"hook": "terminal-notifier -message '$CLAUDE_NOTIFICATION' -title 'Claude Code'"
}
]
}
}
Hooks are defined in .claude/settings.json at the project level or ~/.claude/settings.json globally.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hook": "python3 .claude/hooks/validate-bash-command.py",
"timeout": 5000
},
{
"matcher": "Edit|Write",
"hook": ".claude/hooks/check-protected-files.sh"
}
],
"PostToolUse": [
{
"matcher": "Edit",
"hook": ".claude/hooks/post-edit.sh"
},
{
"matcher": "Write",
"hook": ".claude/hooks/post-write.sh"
}
],
"Notification": [
{
"matcher": "",
"hook": "notify-send 'Claude Code' '$CLAUDE_NOTIFICATION'"
}
]
}
}
The matcher field determines which tool triggers the hook. It supports:
"Edit" — only Edit tool calls"Edit|Write" — both Edit and Write"" — matches all tools/events| Variable | Description |
|---|---|
$CLAUDE_TOOL_NAME |
The tool being called (Read, Edit, Write, Bash, etc.) |
$CLAUDE_FILE_PATH |
The file being operated on (for file tools) |
$CLAUDE_BASH_COMMAND |
The command being executed (for Bash tool) |
$CLAUDE_NOTIFICATION |
The notification message (for Notification hooks) |
$CLAUDE_PROJECT_DIR |
The project root directory |
#!/bin/bash
# .claude/hooks/post-edit.sh
FILE="$CLAUDE_FILE_PATH"
case "$FILE" in
*.ts|*.tsx|*.js|*.jsx)
npx prettier --write "$FILE" 2>/dev/null
npx eslint --fix "$FILE" 2>/dev/null
;;
*.py)
ruff format "$FILE" 2>/dev/null
ruff check --fix "$FILE" 2>/dev/null
;;
*.go)
gofmt -w "$FILE" 2>/dev/null
;;
*.rs)
rustfmt "$FILE" 2>/dev/null
;;
esac
This hook auto-formats every file Claude Code edits, ensuring consistent style without Claude needing to worry about formatting.
#!/bin/bash
# .claude/hooks/check-protected-files.sh
PROTECTED_PATTERNS=(
"*.lock"
"package-lock.json"
"yarn.lock"
"migrations/versions/*.py"
".env*"
"*.pem"
"*.key"
)
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$CLAUDE_FILE_PATH" == $pattern ]]; then
echo "BLOCKED: Cannot modify protected file: $CLAUDE_FILE_PATH"
exit 1
fi
done
exit 0
When a PreToolUse hook exits with a non-zero code, Claude Code blocks the tool execution and shows the hook's output to the model, which then adjusts its approach.
#!/bin/bash
# .claude/hooks/post-edit-test.sh
FILE="$CLAUDE_FILE_PATH"
# Find and run related test files
if [[ "$FILE" == *.py ]]; then
TEST_FILE="${FILE/app\//tests/test_}"
if [[ -f "$TEST_FILE" ]]; then
pytest "$TEST_FILE" -x --tb=short -q 2>&1 | tail -5
fi
elif [[ "$FILE" == *.ts || "$FILE" == *.tsx ]]; then
TEST_FILE="${FILE%.ts*}.test${FILE##*.ts}"
if [[ -f "$TEST_FILE" ]]; then
npx vitest run "$TEST_FILE" --reporter=verbose 2>&1 | tail -10
fi
fi
#!/bin/bash
# .claude/hooks/scan-secrets.sh
FILE="$CLAUDE_FILE_PATH"
# Skip binary files and known safe patterns
if file "$FILE" | grep -q "binary"; then
exit 0
fi
# Check for common secret patterns
PATTERNS=(
"sk-[a-zA-Z0-9]{20,}"
"AKIA[0-9A-Z]{16}"
"ghp_[a-zA-Z0-9]{36}"
"-----BEGIN (RSA |EC )?PRIVATE KEY-----"
"password\s*=\s*["'][^"']{8,}["']"
)
for pattern in "${PATTERNS[@]}"; do
if grep -qP "$pattern" "$FILE" 2>/dev/null; then
echo "BLOCKED: Potential secret detected in $FILE matching pattern: $pattern"
exit 1
fi
done
exit 0
Hooks have a configurable timeout (default: 60 seconds). If a hook exceeds its timeout, it is killed and treated as a failure for PreToolUse (blocks the action) or a warning for PostToolUse.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hook": "npm test -- --timeout 30000",
"timeout": 45000
}
]
}
}
Hooks can write to stdout and stderr. This output is captured and fed back to Claude Code's model, allowing it to react to hook results. For example, if a linting hook reports errors, Claude Code will see those errors and can fix them in its next tool call.
Both hooks and CLAUDE.md instructions influence Claude Code's behavior, but they work differently:
| Aspect | CLAUDE.md | Hooks |
|---|---|---|
| Enforcement | Advisory (model follows them but can deviate) | Mandatory (PreToolUse hooks block execution) |
| Execution | Interpreted by the AI model | Executed as shell commands |
| Timing | Read at session start | Run at each tool call |
| Reliability | High but not guaranteed | Guaranteed (scripts run regardless) |
Use CLAUDE.md for: coding conventions, architecture guidelines, style preferences
Use hooks for: formatting enforcement, security scanning, file protection, automated testing
The combination is powerful: CLAUDE.md tells Claude Code how to write code, and hooks verify that the code meets your standards after every change.
When your hooks are committed to the repository in .claude/settings.json and .claude/hooks/, every team member who uses Claude Code gets the same automated checks. This creates a consistent development experience:
This is essentially a local CI pipeline that runs on every AI-generated edit.
Claude Code hooks transform your AI coding assistant from a tool that follows suggestions into one that enforces standards. By combining PreToolUse hooks (for protection and validation) with PostToolUse hooks (for formatting and testing), you create guardrails that ensure Claude Code's output meets your team's quality bar automatically, every time.

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
Anthropic unveiled 10 pre-built finance agent templates on May 5, 2026 across pitchbook building, KYC screening, and month-end close. What each template does and the hours it replaces.
How the modern agent eval stack actually flows: instrument, trace, dataset, evaluator, score, CI gate. The full pipeline that keeps agents from regressing.
Anthropic shipped finance plugins for Claude Cowork and Claude Code on May 5, 2026. How analysts use them in practice and what the plugin model means for adoption.
Version your prompts in git, run a 50-case eval suite on every PR, block merges below threshold, and ship a new agent prompt with confidence — full GitHub Actions tutorial.
Standard benchmarks miss agent regressions because they grade only final outputs. Trajectory-aware evals in CI catch the 20–40% of regressions that single-turn scoring hides.
A practical engineering deep dive into Claude Sonnet 4.6 migration, covering architecture, tradeoffs, and what production teams need to know about model upgrade.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI