Advanced Tutorial
Automate Security Checks, Formatting, and Tests with Hooks
Event-driven automation for your AI coding workflow
Claude Code hooks are shell commands that run automatically before or after specific events — like pre-commit checks, post-edit formatting, or custom validation on every file change. They turn Claude Code from a smart assistant into a fully automated development pipeline.
Core Concept
What Are Hooks?
Hooks are the automation layer of Claude Code. They let you define shell commands that fire automatically when specific events occur during a Claude Code session — before a tool runs, after a file is edited, when a session starts, or when Claude sends a notification.
If you have used Git hooks (pre-commit, post-merge) or CI/CD pipeline triggers, the concept is identical. The difference is that Claude Code hooks operate at the AI interaction level: you can intercept and modify Claude's behavior in real time, not just at commit boundaries.
Hooks are defined in your settings.json file — either at the project level (.claude/settings.json in your repo) or at the user level (~/.claude/settings.json). Project-level hooks are shared with your team via version control. User-level hooks are personal preferences.
Event Reference
Built-in Hook Events
Fires before Claude executes any tool (file write, bash command, etc.). Use this to validate, block, or modify operations before they happen.
Fires after Claude completes a tool operation. Use this for post-processing — formatting code after edits, running tests after file changes, or logging what changed.
Fires once when a Claude Code session begins. Use this for environment setup — loading context files, checking prerequisites, or displaying project status.
Fires when Claude sends a notification (e.g., when a background task completes). Use this to trigger alerts, sounds, or external integrations.
Step by Step
Creating Your First Hook
Create your settings file
If it does not already exist, create .claude/settings.json in your project root. This file configures Claude Code's behavior for this specific project.
Define the hook event and matcher
Choose which event to hook into (PreToolUse, PostToolUse, SessionStart, or Notification). If hooking tool events, add a matcher to filter by tool name — "Edit" for file edits, "Bash" for shell commands, "Write" for file creation.
Write the shell command
Your hook command is any valid shell expression. Environment variables like $CLAUDE_FILE_PATH and $CLAUDE_TOOL_INPUT give you context about what Claude is doing. Exit code 0 means success; non-zero exit codes block the operation (for PreToolUse hooks).
Test the hook
Start a new Claude Code session and trigger the event. Ask Claude to edit a file or run a command to verify your hook fires correctly. Check the Claude Code output for hook execution messages.
Copy-Paste Ready
Practical Examples
Auto-format on every file edit
Run Prettier automatically after Claude edits any file, ensuring consistent formatting without manual intervention.
{
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"command": "npx prettier --write $CLAUDE_FILE_PATH"
}]
}
}Security check before bash commands
Block dangerous commands like rm -rf or force pushes before they execute.
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"command": "echo \"$CLAUDE_TOOL_INPUT\" | grep -qE 'rm -rf|--force|--hard' && exit 1 || exit 0"
}]
}
}Run tests after code changes
Automatically run relevant tests when Claude modifies source files, catching regressions immediately.
{
"hooks": {
"PostToolUse": [{
"matcher": "Edit",
"command": "npm test -- --findRelatedTests $CLAUDE_FILE_PATH"
}]
}
}Load project context on session start
Automatically surface unread messages, pending PRs, or project status when you start a Claude Code session.
{
"hooks": {
"SessionStart": [{
"command": "cat ~/project/STATUS.md && gh pr list --state open"
}]
}
}Common Questions
Frequently Asked Questions
What is the difference between a hook and a slash command?
Slash commands are user-triggered — you type /command to run them manually. Hooks are event-triggered — they run automatically when a specific event occurs (like a file edit or tool call). Think of slash commands as buttons you press, and hooks as automated reactions.
How do I debug a hook that is not firing?
Check three things: (1) the hook is in the correct settings.json file (project-level .claude/settings.json or user-level ~/.claude/settings.json), (2) the event matcher matches the actual event name (case-sensitive), and (3) the shell command is valid and executable. Run the command manually in your terminal first to verify it works.
Do hooks impact Claude Code performance?
Hooks run synchronously — Claude waits for them to complete before proceeding. Fast hooks (linting a single file, running a formatter) add negligible delay. Slow hooks (full test suites, large builds) will block Claude's workflow. Keep hooks under 5 seconds for the best experience, or use async patterns for heavier operations.
Can I share hooks across my team?
Yes. Project-level hooks live in .claude/settings.json within your repository, so they are version-controlled and shared with everyone who clones the repo. User-level hooks in ~/.claude/settings.json are personal and stay on your machine. Use project-level for team standards (linting, security checks) and user-level for personal preferences.
Master Claude Code automation
Stop reading about it. Build something.