| name | hooks-system |
| description | Lifecycle hook patterns — PreToolUse, PostToolUse, UserPromptSubmit, Stop, SubagentStop. Use when configuring hooks, writing validation, auto-format on save, stdin payloads, exit-2 blocking, or debugging a hook. |
| disable-model-invocation | true |
Hooks System
Version: 1.0.0
Purpose: Lifecycle hook patterns for validation, automation, security, and metrics in Claude Code workflows
Status: Production Ready
Overview
Hooks are lifecycle callbacks that execute at specific points in the Claude Code workflow. They enable:
- Validation (block dangerous operations before execution)
- Automation (auto-format code after file changes)
- Security (enforce safety policies on commands and tools)
- Metrics (track tool usage, performance, costs)
- Quality Control (run tests after implementation changes)
- Context Injection (load project-specific context at session start)
Hooks transform Claude Code from a reactive assistant into a proactive, policy-enforced development environment.
Hook Types Reference
Claude Code provides 7 hook types that fire at different lifecycle stages:
| Hook Type | When It Fires | Receives | Can Modify | Use Cases |
|---|
| PreToolUse | Before tool execution | Tool name, input | Tool input, can block | Validation, security checks, permission gates |
| PostToolUse | After tool completion | Tool name, input, output | Nothing (read-only) | Auto-format, metrics, notifications |
| UserPromptSubmit | User submits prompt | Prompt text | Nothing (read-only) | Complexity analysis, model routing, context injection |
| SessionStart | Session begins | Session metadata | Nothing (read-only) | Load project context, initialize environment |
| Stop | Main session stops | Session metadata | Nothing (read-only) | Completion validation, cleanup, final reports |
| SubagentStop | Sub-agent (Task) completes | Task metadata, output | Nothing (read-only) | Task metrics, result validation |
| Notification | System notification | Notification data | Nothing (read-only) | Alert logging, external integrations |
| PermissionRequest | Tool needs permission | Tool name, action | Nothing (read-only) | Custom approval workflows |
Key Concepts:
- PreToolUse: Only hook that can block or modify execution
- PostToolUse: Cannot modify output, but can trigger follow-up actions
- Matcher: Regex pattern to filter which tools trigger the hook
- Hooks Array: Commands to execute when hook fires (can run multiple)
Hook Configuration in settings.json
Hooks are configured in .claude/settings.json under the "hooks" key:
Basic Structure
{
"hooks": {
"PreToolUse": [
{
"matcher": "^(Write|Edit)$",
"hooks": ["echo 'File change detected'"]
}
],
"PostToolUse": [
{
"matcher": "^(Write|Edit)$",
"hooks": ["bun run format"]
}
]
}
}
Configuration Properties
matcher (required):
- Regex pattern to match tool names
- Uses JavaScript regex syntax
- Examples:
"^Write$" - Matches only Write tool
"^(Write|Edit)$" - Matches Write or Edit
".*" - Matches all tools (use sparingly)
"^Bash$" - Matches Bash tool
hooks (required):
- Array of commands to execute
- Commands run sequentially
- Can be shell commands or custom scripts
- Each command runs in its own shell context
continueOnError (optional, default: true):
true: Continue workflow if hook fails
false: Stop workflow on hook failure
- Use
false for critical validation hooks
timeout (optional, default: 30000ms):
- Maximum execution time for hook command
- In milliseconds (30000 = 30 seconds)
- Hook is killed if timeout exceeded
Advanced Configuration Example
{
"hooks": {
"PreToolUse": [
{
"matcher": "^Write$",
"hooks": [
"node scripts/validate-file.js",
"node scripts/check-secrets.js"
],
"continueOnError": false,
"timeout": 10000
}
],
"PostToolUse": [
{
"matcher": "^(Write|Edit)$",
"hooks": ["bun run format", "bun run lint --fix"],
"continueOnError": true,
"timeout": 60000
}
Ready-To-Use Hook Templates
Template 1: File Protection Hook
Purpose: Block writes to sensitive files (secrets, credentials, config)
Hook Type: PreToolUse
Matcher: "^(Write|Edit)$"
Configuration:
{
"hooks": {
"PreToolUse": [
{
"matcher": "^(Write|Edit)$",
"hooks": ["node scripts/protect-files.js"],
"continueOnError": false,
"timeout": 5000
}
]
}
}
Script: hooks/protect-files.ts
#!/usr/bin/env bun
import { readFileSync } from "node:fs";
const PROTECTED_PATTERNS = [
/\.env$/,
/\.env\./,
/credentials\.json$/,
/secrets\.yaml$/,
/id_rsa$/,
/\.pem$/,
/\.key$/
];
let filePath = "";
try {
const payload = JSON.parse(readFileSync("/dev/stdin", "utf-8"));
filePath = payload.tool_input?.file_path ?? "";
} catch {
process.exit(0);
}
const isProtected = PROTECTED_PATTERNS.some(pattern => pattern.test(filePath));
if (isProtected) {
console.log(`BLOCKED: cannot modify protected file: ${filePath}`);
process.exit(2);
}
process.exit(0);
When to Use:
- Protecting credentials and secrets
- Preventing accidental config file modifications
- Enforcing file-level permissions in team workflows
Template 2: Auto-Format Hook
Purpose: Automatically format code after file changes
Hook Type: PostToolUse
Matcher: "^(Write|Edit)$"
Configuration:
{
"hooks": {
"PostToolUse": [
{
"matcher": "^(Write|Edit)$",
"hooks": [
"bun run format",
"bun run lint --fix"
],
"continueOnError": true,
"timeout": 60000
}
]
}
}
package.json Scripts:
{
"scripts": {
"format": "prettier --write .",
"lint": "eslint . --ext .ts,.tsx,.js,.jsx"
}
}
When to Use:
- Maintaining consistent code style
- Automatic linting and formatting
- Reducing manual formatting overhead
- Enforcing team style guidelines
Benefits:
- Every file change is auto-formatted
- No manual "run prettier" steps needed
- Consistent style across all changes
- Catches lint errors immediately
Template 3: Security Command Blocker
Purpose: Block dangerous bash commands (rm -rf /, force push, etc.)
Hook Type: PreToolUse
Matcher: "^Bash$"
Configuration:
{
"hooks": {
"PreToolUse": [
{
"matcher": "^Bash$",
"hooks": ["node scripts/security-check.js"],
"continueOnError": false,
"timeout": 5000
}
]
}
}
Script: scripts/security-check.js
#!/usr/bin/env node
const DANGEROUS_COMMANDS = [
/rm\s+-rf\s+\//,
/rm\s+-rf\s+~\//,
/git\s+push\s+.*--force/,
/git\s+reset\s+--hard/,
/chmod\s+777/,
/sudo\s+rm/,
/:\(\)\{\s*:\|:&\s*\};:/,
/dd\s+if=.*of=\/dev\//,
/mkfs/,
/>\s*\/dev\/sd/
];
let command = "";
try {
const payload = JSON.parse(readFileSync("/dev/stdin", "utf-8"));
command = payload.tool_input?.command ?? "";
} catch {
process.exit(0);
}
const isDangerous = DANGEROUS_COMMANDS.some(pattern => pattern.test(command));
if (isDangerous) {
.();
.();
process.();
}
process.();
When to Use:
- Production environments
- Shared development machines
- Preventing accidental destructive commands
- Enforcing security policies
Protected Against:
- Recursive deletion of root or home directories
- Force pushing to protected branches
- Destructive git operations
- System-level permission changes
- Fork bombs and other malicious commands
Template 4: Task Complexity Analyzer
Purpose: Analyze prompt complexity and suggest appropriate model tier
Hook Type: UserPromptSubmit
Matcher: ".*" (all prompts)
Configuration:
{
"hooks": {
"UserPromptSubmit": [
{
"matcher": ".*",
"hooks": ["node scripts/analyze-complexity.js"]
}
]
}
}
Script: scripts/analyze-complexity.js
#!/usr/bin/env node
const fs = require('fs');
const args = process.argv.slice(2);
const prompt = args.join(' ');
let score = 0;
if (prompt.length > 500) score += 2;
if (prompt.length > 1000) score += 3;
const complexKeywords = [
'implement', 'refactor', 'architect', 'design',
'optimize', 'performance', 'security', 'scale'
];
const simpleKeywords = ['fix', 'update', 'change', 'modify'];
complexKeywords.forEach(keyword => {
if (prompt.toLowerCase().includes(keyword)) score += 2;
});
simpleKeywords.forEach(keyword => {
if (prompt.toLowerCase().includes(keyword)) score -= ;
});
recommendation;
(score >= ) {
recommendation = ;
} (score >= ) {
recommendation = ;
} {
recommendation = ;
}
logEntry = {
: ().(),
: prompt.(, ),
score,
recommendation
};
fs.(, .(logEntry) + );
.();
process.();
When to Use:
- Cost optimization (use cheaper models for simple tasks)
- Automatic model routing based on task complexity
- Performance tracking (are prompts getting more complex?)
- Budget management (track usage patterns)
Template 5: Metrics Collector
Purpose: Log tool usage to track productivity and patterns
Hook Type: PostToolUse
Matcher: ".*" (all tools)
Configuration:
{
"hooks": {
"PostToolUse": [
{
"matcher": ".*",
"hooks": ["node scripts/collect-metrics.js"]
}
]
}
}
Script: scripts/collect-metrics.js
#!/usr/bin/env node
const fs = require('fs');
const args = process.argv.slice(2);
const toolName = args[0] || 'unknown';
const duration = args[1] || '0';
const metric = {
timestamp: new Date().toISOString(),
tool: toolName,
duration: parseInt(duration),
session: process.env.CLAUDE_SESSION_ID || 'unknown'
};
const metricsPath = '.claude/metrics.json';
fs.appendFileSync(metricsPath, JSON.stringify(metric) + '\n');
const today = new Date().toISOString().split('T')[0];
const metrics = fs.readFileSync(metricsPath, 'utf-8')
.split('\n')
.filter(line => line)
.map( => .(line))
.( m..(today));
toolCounts = metrics.( {
acc[m.] = (acc[m.] || ) + ;
acc;
}, {});
.();
process.();
When to Use:
- Tracking tool usage patterns
- Performance monitoring
- Cost analysis (which tools are expensive?)
- Productivity metrics (how many files changed today?)
Metrics Collected:
- Tool name (Write, Edit, Bash, etc.)
- Execution duration
- Timestamp
- Session ID
Template 6: Test Runner Hook
Purpose: Automatically run tests when test files are modified
Hook Type: PostToolUse
Matcher: "^(Write|Edit)$"
Configuration:
{
"hooks": {
"PostToolUse": [
{
"matcher": "^(Write|Edit)$",
"hooks": ["node scripts/auto-test.js"]
}
]
}
}
Script: scripts/auto-test.js
#!/usr/bin/env node
const { execSync } = require('child_process');
const path = require('path');
const args = process.argv.slice(2);
const filePath = args[0] || '';
const isTestFile = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filePath);
if (!isTestFile) {
console.log('Not a test file, skipping auto-test');
process.exit(0);
}
console.log(`Running tests for: ${filePath}`);
try {
const output = execSync(`bun test ${filePath}`, {
encoding: 'utf-8',
timeout: 30000
});
console.log(output);
console.log('✅ Tests passed');
process.exit(0);
} catch (error) {
console.error('❌ Tests failed:');
console.(error. || error.);
process.();
}
When to Use:
- Test-driven development workflows
- Immediate feedback on test changes
- Catching broken tests before commit
- Continuous validation during implementation
Template 7: Session Context Injector
Purpose: Load project-specific context at session start
Hook Type: SessionStart
Matcher: ".*"
Configuration:
{
"hooks": {
"SessionStart": [
{
"matcher": ".*",
"hooks": ["node scripts/load-context.js"]
}
]
}
}
Script: scripts/load-context.js
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const contextFiles = [
'CLAUDE.md',
'README.md',
'ARCHITECTURE.md',
'.claude/context.json'
];
const context = contextFiles
.filter(file => fs.existsSync(file))
.map(file => {
const content = fs.readFileSync(file, 'utf-8');
return `--- ${file} ---\n${content}\n`;
})
.join('\n');
const sessionContext = '.claude/session-context.txt';
fs.writeFileSync(sessionContext, context);
console.log('Session context loaded:');
contextFiles.forEach(file => {
if (fs.existsSync(file)) {
console.log(` ✅ ${file}`);
}
});
process.exit(0);
When to Use:
- Ensuring Claude has project context every session
- Loading team guidelines and conventions
- Auto-loading architecture documentation
- Reducing need for manual context sharing
Template 8: Completion Evaluator
Purpose: Validate that deliverables were produced before session ends
Hook Type: Stop
Matcher: ".*"
Configuration:
{
"hooks": {
"Stop": [
{
"matcher": ".*",
"hooks": ["node scripts/evaluate-completion.js"]
}
]
}
}
Script: scripts/evaluate-completion.js
#!/usr/bin/env node
const fs = require('fs');
const { execSync } = require('child_process');
const deliverablesPath = 'ai-docs/deliverables';
if (!fs.existsSync(deliverablesPath)) {
console.log('⚠️ Warning: No deliverables directory found');
process.exit(0);
}
const files = fs.readdirSync(deliverablesPath);
if (files.length === 0) {
console.log('⚠️ Warning: No deliverables produced in this session');
} else {
console.log(`✅ Session completed with ${files.length} deliverables:`);
files.forEach(file => console.log(` - ${file}`));
}
try {
const status = execSync('git status --short', { encoding: 'utf-8' });
const changedFiles = status.split('\n').( line).;
.();
} (error) {
}
process.();
When to Use:
- Ensuring deliverables are produced
- Session quality validation
- Tracking productivity (files changed, deliverables created)
- Alerting when session ends without output
Hook Chains
Execution Flow
PreToolUse → Tool Execution → PostToolUse
-
PreToolUse hooks run first
- Can modify tool input
- Can block execution (exit code 2 — any other non-zero code is a hook
error, which is logged and the tool still runs)
- If blocked, tool never executes
-
Tool executes
- Only if PreToolUse passed
- Normal tool behavior
-
PostToolUse hooks run after
- Cannot modify tool output
- Cannot block tool (already executed)
- Can trigger follow-up actions
Multiple Hooks on Same Event
When multiple hooks are configured for the same event, they execute sequentially in array order:
{
"hooks": {
"PostToolUse": [
{
"matcher": "^(Write|Edit)$",
"hooks": [
"bun run format",
"bun run lint --fix",
"bun test"
]
}
]
}
}
Execution Order:
bun run format executes
- Wait for completion (or timeout)
bun run lint --fix executes
- Wait for completion (or timeout)
bun test executes
- PostToolUse complete
Error Handling:
- If
continueOnError: true (default), failure in step 1 doesn't stop step 2
- If
continueOnError: false, failure in step 1 stops entire chain
Combining Hooks for Comprehensive Workflows
Example: Full-Stack Quality Chain
{
"hooks": {
"PreToolUse": [
{
"matcher": "^(Write|Edit)$",
"hooks": ["node scripts/protect-files.js"],
"continueOnError": false
},
{
"matcher": "^Bash$",
"hooks": ["node scripts/security-check.js"],
"continueOnError": false
}
],
"PostToolUse": [
{
"matcher": "^(Write|Edit)$",
"hooks": [
"bun run format",
"bun run lint --fix"
Workflow:
- User submits prompt → Complexity analyzed
- User requests file write → File protection checked (PreToolUse)
- Write tool executes → File written
- PostToolUse chain runs:
- Format code with Prettier
- Fix lint issues
- Run relevant tests
- Log metrics
- User requests bash command → Security check (PreToolUse)
- Session ends → Completion evaluation
This creates a fully automated quality pipeline with zero manual intervention.
Best Practices
Do:
- ✅ Use PreToolUse for validation - It's the only hook that can block execution
- ✅ Set appropriate timeouts - Hooks should complete quickly (5-30 seconds)
- ✅ Log hook activity - Track what hooks are doing for debugging
- ✅ Use specific matchers - Avoid
.* matcher when possible (reduces overhead)
- ✅ Exit with proper codes - Exit 0 to allow, exit 2 to block. Any other
non-zero code is a hook error: it is logged and the tool runs anyway, so
exit 1 silently fails open when you meant to block.
- ✅ Read the payload from stdin - never
process.argv, never an env var
- ✅ Fail open on bad input - if the payload will not parse,
exit 0; a hook
bug must never block the user's work
- ✅ Make hooks idempotent - Safe to run multiple times
- ✅ Test hooks independently - Run hook scripts manually before adding to config
- ✅ Use continueOnError wisely - False for critical validation, true for nice-to-have
Don't:
- ❌ Don't use PostToolUse for validation - Tool already executed, too late to block
- ❌ Don't run expensive operations - Hooks should be fast (<30s)
- ❌ Don't rely on environment state - Each hook runs in fresh shell
- ❌ Don't modify files in PreToolUse - Modifying files during validation causes confusion
- ❌ Don't use
.* matcher everywhere - Creates overhead on every tool call
- ❌ Don't swallow errors silently - Log errors even with continueOnError: true
Performance Tips:
Fast Hooks (<5s):
- File validation checks
- Regex-based security checks
- Simple logging/metrics
- Quick script executions
Medium Hooks (5-30s):
- Code formatting (prettier)
- Linting with auto-fix
- Unit test execution
- Complexity analysis
Slow Hooks (>30s) - AVOID:
- Full test suite execution (use PostToolUse with specific test files instead)
- External API calls with retries
- Large file processing
- Heavy computations
Optimization:
- Use hook matchers to limit when hooks run
- Run heavy operations in background (don't block workflow)
- Cache results when possible
- Use incremental tools (only format changed files)
Examples
Example 1: Full-Stack Development Hook Setup
Scenario: React/TypeScript project with comprehensive quality automation
Configuration:
{
"hooks": {
"PreToolUse": [
{
"matcher": "^(Write|Edit)$",
"hooks": ["node scripts/protect-files.js"],
"continueOnError": false,
"timeout": 5000
},
{
"matcher": "^Bash$",
"hooks": ["node scripts/security-check.js"],
"continueOnError": false,
"timeout": 5000
}
],
"PostToolUse": [
{
"matcher": "^(Write|Edit)$"
Workflow:
- Session starts → Project context loaded (CLAUDE.md, README.md, ARCHITECTURE.md)
- User: "Implement login feature" → Complexity analyzed (score: 5, recommend Sonnet)
- Claude writes LoginForm.tsx →
- PreToolUse: File protection check passes ✅
- Write tool executes
- PostToolUse chain:
- Format with Prettier ✅
- Lint with ESLint ✅
- Run LoginForm.test.tsx ✅
- User: "Run database migration" →
- PreToolUse: Security check blocks
rm -rf / in migration script ❌
- Error shown to user
- Session ends → Completion evaluation: 5 files changed, 3 deliverables created
Benefits:
- Zero manual formatting/linting
- Immediate test feedback
- Dangerous commands blocked
- Full audit trail via metrics
- Consistent quality across all changes
Example 2: Security-Hardened Hook Configuration
Scenario: Production environment with strict security policies
Configuration:
{
"hooks": {
"PreToolUse": [
{
"matcher": "^Write$",
"hooks": [
"node scripts/security/check-secrets.js",
"node scripts/security/validate-permissions.js",
"node scripts/security/check-file-size.js"
],
"continueOnError": false,
"timeout": 10000
},
{
"matcher": "^Edit$",
"hooks": [
"node scripts/security/backup-file.js",
"node scripts/security/check-secrets.js"
],
"continueOnError": false,
"timeout": 10000
}
Security Layers:
- Secrets Detection - Blocks files containing API keys, passwords, tokens
- Permission Validation - Ensures Claude can only write to allowed directories
- File Size Limits - Prevents writing huge files (>10MB)
- File Backups - Auto-backup before editing critical files
- Command Whitelist - Only allow pre-approved bash commands
- Dangerous Command Blocker - Block rm, format, force push, etc.
- Audit Logging - Log every tool use to audit trail
Example Script: scripts/security/check-secrets.js
#!/usr/bin/env node
const fs = require('fs');
const SECRETS_PATTERNS = [
/api[_-]?key["\s:=]+[a-zA-Z0-9]{20,}/i,
/password["\s:=]+.{8,}/i,
/bearer\s+[a-zA-Z0-9\-._~+/]+=*/i,
/AIza[0-9A-Za-z-_]{35}/,
/sk-[a-zA-Z0-9]{48}/,
/xox[baprs]-[0-9a-zA-Z-]{10,}/,
/github_pat_[a-zA-Z0-9]{82}/
];
let content = "";
try {
const payload = JSON.parse(readFileSync("/dev/stdin", "utf-8"));
content = payload.tool_input?.content ?? "";
} catch {
process.exit(0);
}
const foundSecret = SECRETS_PATTERNS.find(pattern => pattern.test(content));
if (foundSecret) {
console.log('BLOCKED: content contains a potential secret');
console.();
.();
process.();
}
process.();
Example 3: Multi-Agent Workflow with Routing Hooks
Scenario: Complex workflows that route to different agents based on prompt
Configuration:
{
"hooks": {
"UserPromptSubmit": [
{
"matcher": ".*",
"hooks": ["node scripts/routing/analyze-intent.js"]
}
],
"SubagentStop": [
{
"matcher": ".*",
"hooks": ["node scripts/routing/collect-results.js"]
}
]
}
}
Script: scripts/routing/analyze-intent.js
#!/usr/bin/env node
const fs = require('fs');
const args = process.argv.slice(2);
const prompt = args.join(' ');
const intents = {
'ui-design': ['design', 'figma', 'mockup', 'ui', 'ux', 'interface'],
'backend': ['api', 'database', 'server', 'endpoint', 'authentication'],
'testing': ['test', 'spec', 'coverage', 'e2e', 'unit test'],
'devops': ['deploy', 'docker', 'ci/cd', 'kubernetes', 'pipeline'],
'review': ['review', 'audit', 'analyze', 'check quality']
};
let detectedIntent = 'general';
let maxScore = 0;
for (const [intent, keywords] of Object.entries(intents)) {
const score = keywords.(
prompt.().(kw)
).;
(score > maxScore) {
maxScore = score;
detectedIntent = intent;
}
}
routingDecision = {
: ().(),
: prompt.(, ),
: detectedIntent,
: maxScore
};
fs.(, .(routingDecision));
.();
agentMap = {
: ,
: ,
: ,
: ,
:
};
suggestedAgent = agentMap[detectedIntent] || ;
.();
process.();
Script: scripts/routing/collect-results.js
#!/usr/bin/env node
const fs = require('fs');
const args = process.argv.slice(2);
const agentName = args[0];
const status = args[1] || 'completed';
let results = [];
if (fs.existsSync('.claude/agent-results.json')) {
results = JSON.parse(fs.readFileSync('.claude/agent-results.json', 'utf-8'));
}
results.push({
timestamp: new Date().toISOString(),
agent: agentName,
status: status
});
fs.writeFileSync('.claude/agent-results.json', JSON.stringify(results, null, 2));
console.log(`Collected result from ${agentName}: ${status}`);
console.log(`Total agents completed: ${results.length}`);
process.exit(0);
Workflow:
- User: "Design login page and implement API"
- UserPromptSubmit hook:
- Analyzes intent: Mixed (ui-design + backend)
- Suggests: Use orchestrator to coordinate multiple agents
- Orchestrator launches:
- designer agent (for UI)
- backend-developer agent (for API)
- SubagentStop hook fires after each:
- designer completes → Result collected
- backend-developer completes → Result collected
- Final report: 2 agents completed successfully
Troubleshooting
Problem: Hook never fires
Cause: Matcher regex doesn't match tool name
Solution: Test regex pattern
node -e "console.log(/^Write$/.test('Write'))"
node -e "console.log(/^write$/.test('Write'))"
Fix:
{
"matcher": "^Write$"
}
Problem: Hook blocks workflow unintentionally
Cause: Hook script exits with non-zero code (failure)
Solution: Debug hook script independently
node scripts/protect-files.js /path/to/file
echo $?
Fix: Ensure script exits with correct code
if (isProtected) {
console.error('File protected');
}
if (isProtected) {
console.log('BLOCKED: file is protected');
process.exit(2);
}
console.log('File allowed');
process.exit(0);
Problem: Hook times out
Cause: Hook script takes too long (>30s default)
Solution: Increase timeout or optimize script
{
"matcher": "^(Write|Edit)$",
"hooks": ["bun test"],
"timeout": 120000
}
Better Solution: Optimize script to run faster
execSync('bun test');
const testFile = filePath.replace(/\.ts$/, '.test.ts');
if (fs.existsSync(testFile)) {
execSync(`bun test ${testFile}`);
}
Problem: Hooks interfere with each other
Cause: Multiple hooks modify same files simultaneously
Solution: Use proper ordering in hooks array
{
"PostToolUse": [
{
"matcher": "^(Write|Edit)$",
"hooks": [
"bun run format",
"bun run lint --fix"
]
}
]
}
Explanation: Hooks in array run sequentially, not parallel. This ensures format completes before lint starts.
Summary
Hooks enable proactive, policy-enforced development in Claude Code:
- 7 Hook Types - PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, SubagentStop, Notification, PermissionRequest
- PreToolUse = Validation - Only hook that can block execution
- PostToolUse = Automation - Auto-format, metrics, notifications
- Matchers = Filtering - Regex patterns to control when hooks fire
- Chains = Workflows - Multiple hooks execute sequentially
- Templates = Ready-to-Use - 8 production-ready templates for common needs
Use Cases:
- Security (block dangerous commands, detect secrets)
- Quality (auto-format, lint, test after changes)
- Metrics (track tool usage, performance, costs)
- Context (load project docs at session start)
- Validation (ensure deliverables produced)
Master hooks and transform Claude Code into a zero-overhead, fully automated development environment.
Inspired By:
- Frontend plugin auto-format hooks (prettier + eslint on every file change)
- Code Analysis plugin security checks (dangerous command detection)
- Orchestration plugin metrics collection (tool usage tracking)
- Multi-agent workflows with routing hooks (intent-based agent selection)