Claude Code Hooks: Event-Driven Customization Deep Dive
Master Claude Code hooks for event-driven automation. Learn PreToolUse, PostToolUse, and Stop hooks with practical examples and security patterns.
Claude Code Hooks: Event-Driven Customization Deep Dive
Hooks are Claude Code's event-driven automation system. They let you intercept actions, validate operations, and trigger custom behavior at key moments. Whether you want to prevent dangerous commands, log all file changes, or generate summaries at session end, hooks make it possible.
This guide covers everything about hooks: the event types, how to create them, security considerations, and practical examples for real-world use cases.
What Are Hooks?
Hooks are Markdown files that execute in response to specific Claude Code events. They're like middleware in a web framework—they intercept the flow, can modify or block operations, and add custom behavior.
User Request → PreToolUse Hook → Tool Execution → PostToolUse Hook → Response
↓ ↓
(can block) (can log/notify)
Unlike commands (which users invoke) or skills (which provide context), hooks run automatically when their trigger conditions are met.
Hook Event Types
Claude Code supports several hook events:
PreToolUse
Fires before a tool executes. Can block or modify the operation.
Use cases:
- Validate bash commands for safety
- Prevent writes to protected files
- Require confirmation for destructive operations
- Enforce coding standards before edits
PostToolUse
Fires after a tool executes successfully. Cannot block (already happened).
Use cases:
- Log file changes
- Track command history
- Trigger notifications
- Update external systems
Stop
Fires when a conversation ends or Claude Code stops.
Use cases:
- Generate session summaries
- Save conversation context
- Clean up temporary files
- Push logs to external systems
SubagentStop
Fires when a subagent completes its task.
Use cases:
- Aggregate subagent results
- Validate subagent output
- Chain subagent operations
SessionStart
Fires when a new session begins.
Use cases:
- Load project context
- Check environment requirements
- Initialize connections
SessionEnd
Fires when a session terminates.
Use cases:
- Save session state
- Generate reports
- Clean up resources
UserPromptSubmit
Fires when the user submits a prompt.
Use cases:
- Log user queries
- Validate input format
- Trigger pre-processing
PreCompact
Fires before context compaction.
Use cases:
- Preserve important context
- Save conversation state
- Export key information
Notification
Fires when Claude Code sends a notification.
Use cases:
- Custom notification handling
- External alerting integration
- Notification logging
Creating Your First Hook
Basic Structure
Hooks are Markdown files with YAML frontmatter:
---
name: my-hook
event: PreToolUse
tools: [Bash]
---
# Hook Title
Instructions for what this hook should do.
Frontmatter Fields
| Field | Required | Description |
|---|---|---|
name | Yes | Unique identifier for the hook |
event | Yes | Event type (PreToolUse, PostToolUse, etc.) |
tools | No | Filter by tool (Bash, Write, Edit, etc.) |
description | No | What this hook does |
enabled | No | Whether hook is active (default: true) |
File Location
Hooks can be placed in:
# Project-level hooks
.claude/hooks/
.claude/hooks/pre-tool-use/
.claude/hooks/post-tool-use/
# User-level hooks
~/.claude/hooks/
# Plugin hooks
~/.claude/plugins/my-plugin/hooks/
Simple Example: Command Safety
Create .claude/hooks/command-safety.md:
---
name: command-safety
event: PreToolUse
tools: [Bash]
---
# Command Safety Check
Before executing any bash command, validate it for safety.
## Blocked Patterns
These commands should NEVER be executed:
- `rm -rf /` or variations targeting root
- `sudo rm -rf` without explicit user confirmation
- `chmod 777` on sensitive directories
- `curl | bash` or `wget | sh` (remote execution)
- Any command containing credentials in plain text
## Warning Patterns
These require explicit confirmation:
- `git push --force` (destructive to history)
- `DROP TABLE` or `DELETE FROM` without WHERE
- `npm publish` (public release)
- Modifications to `/etc/` or system directories
## Response
If a blocked pattern is detected:
1. STOP execution immediately
2. Explain why the command is blocked
3. Suggest a safer alternative
If a warning pattern is detected:
1. Ask for explicit user confirmation
2. Explain the risks
3. Only proceed with "yes" or "confirm"
PreToolUse Hooks in Detail
PreToolUse is the most powerful hook type—it can block operations before they happen.
Filtering by Tool
---
event: PreToolUse
tools: [Bash]
---
Available tools:
Bash- Shell command executionRead- File readingWrite- File creationEdit- File modificationGlob- File pattern matchingGrep- Content searching- Any MCP tool by name
Blocking Operations
To block an operation, the hook should instruct Claude to refuse:
---
name: protect-env-files
event: PreToolUse
tools: [Write, Edit]
---
# Protect Environment Files
Never modify `.env` files directly through Claude Code.
## Protected Patterns
- `.env`
- `.env.local`
- `.env.production`
- `.env.*`
- Any file containing `API_KEY`, `SECRET`, or `PASSWORD` in the name
## When Triggered
If an operation targets a protected file:
1. BLOCK the operation
2. Explain that env files should be edited manually for security
3. Suggest the user edit the file directly in their editor
Modifying Operations
Hooks can transform operations:
---
name: sanitize-sql
event: PreToolUse
tools: [Bash]
---
# SQL Sanitization
When executing SQL commands, ensure safety.
## Instructions
If the bash command contains SQL:
1. Check for parameterized queries
2. If raw string interpolation is detected, warn the user
3. Suggest using prepared statements instead
Conditional Logic
Hooks can have complex conditions:
---
name: production-safeguard
event: PreToolUse
tools: [Bash]
---
# Production Safeguard
Prevent accidental production modifications.
## Detection
Check if the command targets production:
- Contains `prod` or `production` in arguments
- References production URLs or IPs
- Uses production database connection strings
## Branch Check
Also verify current git branch:
- If on `main` or `master`, require extra confirmation
- If on `production` branch, block all destructive operations
## Response
For production-targeting commands:
1. Clearly identify this is a production operation
2. List what the command will do
3. Require explicit confirmation: "Yes, execute in production"
4. Log the confirmation for audit trail
PostToolUse Hooks
PostToolUse hooks run after successful tool execution. They're perfect for logging, notifications, and triggering side effects.
Logging File Changes
---
name: change-logger
event: PostToolUse
tools: [Write, Edit]
---
# Change Logger
Log all file modifications for audit trail.
## Instructions
After any file write or edit:
1. Note the file path
2. Note the type of change (create, modify, delete)
3. Note the timestamp
4. If significant change, summarize what was modified
## Log Format
Maintain a mental log of changes that can be summarized when requested:
| Time | File | Action | Summary |
|------|------|--------|---------|
| HH:MM | path/file.ts | Modified | Added error handling |
## Usage
When user asks for "what changed" or "session summary", provide this log.
Triggering Notifications
---
name: test-failure-alert
event: PostToolUse
tools: [Bash]
---
# Test Failure Alert
Alert when tests fail.
## Detection
After any bash command that appears to be a test:
- `npm test`
- `yarn test`
- `pytest`
- `go test`
- `cargo test`
## On Failure
If the command output indicates test failures:
1. Clearly highlight the failure
2. Extract the failing test names
3. Show relevant error messages
4. Suggest next steps (examine specific test, check recent changes)
External Integrations
---
name: slack-notify
event: PostToolUse
tools: [Bash]
---
# Slack Notification
Notify on significant operations.
## Significant Operations
- Deployments (`deploy`, `publish`, `release`)
- Database migrations (`migrate`, `db:push`)
- Major git operations (`merge`, `push to main`)
## Notification
When a significant operation completes:
1. Format a summary message
2. If Slack MCP is available, send notification
3. If not, inform user that notification would be sent
## Message Format
:rocket: **Deployment Complete**
- Environment: staging
- Branch: feature/new-auth
- Time: 2025-01-16 10:30 UTC
- Status: Success
Stop Hooks
Stop hooks run when a conversation or session ends. They're ideal for summaries and cleanup.
Session Summary
---
name: session-summary
event: Stop
---
# Session Summary Generator
Generate a summary when the session ends.
## Summary Contents
1. **Duration**: How long the session lasted
2. **Files Modified**: List of all files changed
3. **Commands Run**: Significant bash commands executed
4. **Key Decisions**: Important choices made
5. **Pending Tasks**: Anything left incomplete
6. **Next Steps**: Recommended follow-up actions
## Format
### Session Summary - [Date]
**Duration**: X hours Y minutes
**Files Changed**:
- `path/file.ts` - Description of change
- `path/other.ts` - Description
**Key Accomplishments**:
- Implemented feature X
- Fixed bug in Y
**Next Steps**:
- [ ] Run full test suite
- [ ] Update documentation
- [ ] Deploy to staging
Cleanup Operations
---
name: cleanup
event: Stop
---
# Session Cleanup
Clean up temporary resources when session ends.
## Cleanup Tasks
1. **Temporary Files**: Note any temp files created
2. **Pending Changes**: Warn about uncommitted changes
3. **Running Processes**: Identify any background processes started
4. **Open Connections**: Note any database or API connections
## Warnings
If any of these exist, warn the user:
- Uncommitted git changes
- Background processes still running
- Temporary files that should be deleted
- Draft files that need review
Security Considerations
Hooks have significant power—they can block operations and modify behavior. Use them responsibly.
Principle of Least Privilege
Only hook into events you need:
# Good - specific tools
tools: [Bash]
# Risky - all tools
# (no tools filter = matches everything)
Avoid False Positives
Be specific in pattern matching:
# Too broad - blocks legitimate rm commands
Block: Any command containing "rm"
# Better - specific dangerous patterns
Block: `rm -rf /`, `rm -rf ~`, `sudo rm -rf`
User Override
Allow users to bypass hooks when needed:
## User Override
If user explicitly says "bypass safety check" or "I understand the risks":
- Log the override
- Proceed with the operation
- Note that safety was bypassed
Audit Trail
Log sensitive operations:
## Audit Requirements
For any blocked or overridden operation:
1. Log the operation attempted
2. Log the hook that triggered
3. Log the outcome (blocked/allowed/overridden)
4. Include timestamp
Practical Hook Examples
1. Commit Message Validator
---
name: commit-validator
event: PreToolUse
tools: [Bash]
---
# Commit Message Validator
Ensure commits follow conventional commit format.
## Conventional Commit Format
type(scope): description
[optional body]
[optional footer]
Types: feat, fix, docs, style, refactor, test, chore
## Validation
When detecting a git commit command:
1. Check if message follows format
2. If not, suggest the correct format
3. Block commit until format is correct
## Example
Bad
git commit -m "fixed stuff"
Good
git commit -m "fix(auth): resolve login redirect issue"
2. Dependency Audit
---
name: dependency-audit
event: PostToolUse
tools: [Bash]
---
# Dependency Audit
Check for security issues after dependency changes.
## Trigger Commands
- `npm install`
- `yarn add`
- `pnpm add`
## After Dependency Installation
1. Check for `npm audit` warnings in output
2. If vulnerabilities found, summarize severity levels
3. Suggest `npm audit fix` for automatic fixes
4. For critical vulnerabilities, recommend immediate action
3. Branch Protection
---
name: branch-protection
event: PreToolUse
tools: [Bash]
---
# Branch Protection
Prevent direct modifications to protected branches.
## Protected Branches
- main
- master
- production
- release/*
## Blocked Operations
On protected branches, block:
- `git commit` (should use feature branches)
- `git push --force`
- `git reset --hard`
## Allowed Operations
On protected branches, allow:
- `git pull`
- `git fetch`
- `git status`
- `git log`
## Response
If blocked:
"Direct commits to [branch] are not allowed. Please:
1. Create a feature branch: `git checkout -b feature/your-change`
2. Make your changes there
3. Open a pull request"
4. API Key Detection
---
name: secret-detection
event: PreToolUse
tools: [Write, Edit, Bash]
---
# Secret Detection
Prevent accidental exposure of secrets.
## Secret Patterns
Detect and block:
- API keys: `sk-`, `pk_`, `api_key=`
- AWS credentials: `AKIA`, `aws_secret`
- Private keys: `-----BEGIN RSA PRIVATE KEY-----`
- Tokens: `ghp_`, `gho_`, `github_pat_`
- Generic: `password=`, `secret=`, `token=`
## In File Operations
If writing content containing secrets:
1. BLOCK the operation
2. Highlight the detected secret pattern
3. Suggest using environment variables instead
## In Bash Commands
If command contains hardcoded secrets:
1. BLOCK the operation
2. Suggest using environment variables
3. Show secure alternative
5. Code Style Enforcement
---
name: style-enforcement
event: PreToolUse
tools: [Write, Edit]
---
# Code Style Enforcement
Ensure code follows project standards.
## TypeScript Standards
- Use strict TypeScript (no `any`)
- Prefer `interface` over `type` for objects
- Use named exports
- Add JSDoc for public functions
## File Naming
- Components: PascalCase (`Button.tsx`)
- Utilities: camelCase (`formatDate.ts`)
- Tests: `*.test.ts` or `*.spec.ts`
- Styles: `*.module.css`
## Before Writing/Editing
Check if the proposed changes follow standards:
1. Correct file naming
2. Proper TypeScript types
3. Appropriate exports
4. Documentation where needed
If violations detected, suggest corrections before proceeding.
Hook Organization
Project-Level Hooks
For project-specific behavior:
.claude/
└── hooks/
├── pre-commit-check.md
├── deploy-safety.md
└── test-requirements.md
User-Level Hooks
For personal preferences across all projects:
~/.claude/
└── hooks/
├── global-safety.md
├── my-preferences.md
└── notification-prefs.md
Plugin Hooks
Bundled with plugins for distribution:
my-plugin/
├── plugin.json
└── hooks/
├── plugin-specific-hook.md
└── another-hook.md
Hook Loading Order
Hooks are loaded in this order:
- Built-in hooks
- User-level hooks (~/.claude/hooks/)
- Plugin hooks
- Project-level hooks (.claude/hooks/)
Later hooks can override earlier ones.
Debugging Hooks
Check If Hooks Are Loaded
> /hooks
Active hooks:
- command-safety (PreToolUse, Bash)
- change-logger (PostToolUse, Write, Edit)
- session-summary (Stop)
Test Hook Behavior
> I want to run: rm -rf /
[command-safety hook triggers]
This command is blocked for safety. The `rm -rf /` pattern
could delete your entire filesystem. If you need to delete
files, please specify the exact path.
Hook Conflicts
If hooks conflict:
- Later-loaded hooks take precedence
- More specific tool filters take precedence
- Project hooks override user hooks
Conclusion
Hooks transform Claude Code from a reactive assistant to a proactive guardian of your development workflow. They prevent mistakes, enforce standards, and automate tedious tasks.
Start with safety hooks—prevent destructive commands and protect sensitive files. Then expand to logging and notifications. Finally, add workflow automation that fits your team's needs.
The best hooks are invisible when things go right but invaluable when they prevent disasters. Build yours with that philosophy in mind.
Deciding between commands, skills, and agents? Check out our Decision Framework to choose the right extension type.