Claude Code Hooks: Making AI Deterministic
One of the main challenges in AI development has been the struggle between AI's creative skills and its dependability. Hooks transform AI systems from probabilistic helpers into reliable tools with guaranteed actions.
In This Guide
- 1. The fundamental challenge: creativity vs determinism
- 2. Understanding hooks: the building blocks of determinism
- 3. Hook event types: PreToolUse, PostToolUse, Notification, Stop
- 4. Real-world examples: code formatting and security guardrails
The Fundamental Challenge: Creativity vs Determinism
Large language models are fundamentally probabilistic. They're designed to predict the next best word, which is what makes them so incredibly creative. But what happens when you don't need creativity? What if you require a rock-solid, deterministic part of your workflow?
Here's the problem we've all faced: You can tell an LLM in a prompt, "Hey, always format the code using this style guide." But after a long and complex conversation, it might simply forget. Suddenly, its greatest strength becomes a real weakness. That creativity you loved? Now it causes inconsistency.
From Suggestions to Guarantees
Normal Prompt Instruction
A suggestion. A polite request that the AI might follow if it remembers.
Claude Code Hook
A command. A programmatic rule that the AI is absolutely required to execute every single time.
This is not a minor feature. It's a fundamental shift in how we control AI agents. We're moving beyond just prompting and crossing our fingers. We're now embedding rules directly into the AI's core operational loop. We're literally turning our suggestions into law.
Understanding Hooks: The Building Blocks of Determinism
A hook is a shell command you define that runs automatically and reliably at a very precise point in Claude's execution loop. The key words here are automatic and reliable.
At its core, Claude's entire operation is event-driven. Think of it like a series of checkpoints in a video game:
- The moment you submit a prompt? That's a checkpoint.
- Right before Claude uses a tool? Another checkpoint.
- After a tool completes successfully? Yet another checkpoint.
Hooks are how we insert our own custom logic into those checkpoints. We intercept the flow and add our rules at precisely the right times.
The Four Types of Hook Events
These checkpoints are perfect for different jobs. Claude Code provides four main hook event types:
PreToolUse
Your security guard. It runs before a tool is used, so you can inspect what's about to happen and even block it if it looks dangerous.
PostToolUse
The cleanup crew. It runs after a tool succeeds, allowing you to enforce standards, such as running a code formatter.
Notification
Your pager. It buzzes you when Claude needs your help or when something important happens.
Stop
Session bookend. It fires when Claude's session ends, perfect for final logging or cleanup.
Anatomy of a Hook: Three Simple Parts
Every hook consists of just three simple parts:
- Event - The trigger (like PostToolUse)
- Matcher - Your filter (like "only match files ending in .js" or tools like "Edit|Write")
- Command - The actual shell script that does the work
It's that easy. When, where, and what.
Example 1: Automatic Code Formatting
Let's start with a classic example. You want your code formatted perfectly every single time Claude touches it.
Directory Structure
~/.claude/
settings.json
hooks/
format_code.sh Hook Configuration
In your .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|MultiEdit|Write",
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/format_code.sh"
}
]
}
]
}
} The Formatting Script
Create ~/.claude/hooks/format_code.sh:
#!/usr/bin/env bash
set -e
# Read JSON input from stdin
INPUT=$(cat)
# Extract the file path from the tool response
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_response.filePath // .tool_input.file_path // empty')
# Exit if no file path found
if [ -z "$FILE_PATH" ]; then
exit 0
fi
# Check file extension and run appropriate formatter
if [[ "$FILE_PATH" == *.js || "$FILE_PATH" == *.jsx ]]; then
echo "Formatting JavaScript file: $FILE_PATH"
npx prettier --write "$FILE_PATH"
elif [[ "$FILE_PATH" == *.ts || "$FILE_PATH" == *.tsx ]]; then
echo "Formatting TypeScript file: $FILE_PATH"
npx prettier --write "$FILE_PATH"
elif [[ "$FILE_PATH" == *.py ]]; then
echo "Formatting Python file: $FILE_PATH"
black "$FILE_PATH" 2>/dev/null || true
elif [[ "$FILE_PATH" == *.go ]]; then
echo "Formatting Go file: $FILE_PATH"
gofmt -w "$FILE_PATH"
fi Make it executable:
chmod +x ~/.claude/hooks/format_code.sh
The event is set to PostToolUse, which means this hook fires after Claude has
finished writing to a file. The matcher targets any of the file editing tools. Now your code
is automatically formatted based on file type.
Example 2: Security Guardrails
Now let's raise the stakes. This is a security guardrail hook that can save you from disaster.
Hook Configuration
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/security_check.sh"
}
]
}
]
}
} The Security Check Script
#!/usr/bin/env bash
set -e
# Read JSON input from stdin
INPUT=$(cat)
# Extract the command that's about to be run
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# Define dangerous patterns
DANGEROUS_PATTERNS=(
"rm -rf /"
"rm -rf /*"
"chmod 777"
"curl.*\|.*bash"
"wget.*\|.*bash"
)
# Check for dangerous patterns
for pattern in "${DANGEROUS_PATTERNS[@]}"; do
if echo "$COMMAND" | grep -qE "$pattern"; then
# Block the command
echo "{\"continue\": false, \"stopReason\": \"Blocked dangerous command: Pattern '$pattern' detected\"}"
exit 0
fi
done
# Check for sudo usage
if echo "$COMMAND" | grep -q "^sudo\|[[:space:]]sudo[[:space:]]"; then
echo "{\"continue\": false, \"stopReason\": \"Sudo commands require manual approval\"}"
exit 0
fi
# Allow the command to proceed
echo '{"continue": true}'
This hook shows the real power of PreToolUse. Before Claude can execute any
bash command, this script checks against dangerous patterns and blocks them entirely. The
command never even executes.
Key Insight: This isn't just a suggestion or a warning. It's a hard guardrail that makes your AI agent dramatically safer to use. You're preventing disasters before they happen.
Key Benefits of Hooks
Code Quality
- Auto-format code on save
- Run linters automatically
- Enforce project conventions
Security
- Block dangerous commands
- Protect sensitive files
- Require approval for sudo
Auditing
- Log all AI actions
- Track file changes
- Enable compliance
Safety Considerations
This level of power is incredible, but it needs to be handled carefully. Hooks run automatically with your user privileges. They have access to your environment. That makes them unbelievably powerful, and it means you must treat them with the same care you'd give to any other executable code.
- Treat hooks as code (because they are!)
- Review them carefully before implementing
- Know exactly what each command does
- Never run a hook that you don't fully understand and trust
Getting Started
Ready to try hooks yourself? Getting started is easy:
/hooks add That's it. Claude will launch an interactive editor that walks you through setting up your first hook. Start with something simple like code formatting and work your way up to more complex implementations.
Continue Your Journey
Now that you understand the fundamentals of hooks, you're ready to build a complete audit system:
- Implementation Guide - Build a production-ready audit system
- Skills Conceptual Guide - Understand how skills complement hooks
- Browse Skills - See how others use hooks in their skills
The Transformation
Hooks give us something we've never really had before: serious, predictable, deterministic control over our AI assistants. This isn't wishful thinking anymore. This is guaranteed behavior.
Now that you can enforce the rules, what will you build?