Claude Code hooks let you run custom scripts at specific points in the workflow — before a tool runs, after a tool completes, or when the user submits a prompt. The most valuable hook: automated code review before every commit.
What hooks are
Hooks are shell commands that fire on events:
-
PreToolUse— before Claude runs a tool (Bash, Edit, Write, etc.) -
PostToolUse— after a tool completes -
UserPromptSubmit— when you send a message
You configure them in .claude/settings.json or project-level CLAUDE.md.
The pre-commit review hook
Here's a hook that runs a quick code review before every git commit:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "node scripts/pre-commit-review.js",
"condition": "toolInput.command.includes('git commit')"
}
]
}
}
The condition field ensures the hook only fires when Claude is about to run git commit, not every Bash command.
// scripts/pre-commit-review.js
const { execSync } = require('child_process');
// Get the staged diff
const diff = execSync('git diff --cached').toString();
if (diff.length === 0) {
console.log('No staged changes');
process.exit(0);
}
const checks = [];
// Check for console.log left in
if (diff.includes('console.log')) {
checks.push('WARNING: console.log found in staged changes');
}
// Check for TODO/FIXME
const todoMatches = diff.match(/\+.*(?:TODO|FIXME|HACK|XXX)/gi);
if (todoMatches) {
checks.push(\`WARNING: \${todoMatches.length} TODO/FIXME comments found\`);
}
// Check for .env or secrets
if (diff.includes('.env') || diff.includes('API_KEY') || diff.includes('SECRET')) {
checks.push('CRITICAL: Possible secrets in staged changes');
}
// Check for large files
const stagedFiles = execSync('git diff --cached --name-only').toString().split('\n');
for (const file of stagedFiles) {
if (!file) continue;
try {
const size = execSync(\`wc -c < "\${file}"\`).toString().trim();
if (parseInt(size) > 1000000) {
checks.push(\`WARNING: Large file staged: \${file} (\${Math.round(parseInt(size)/1024)}KB)\`);
}
} catch {}
}
if (checks.length > 0) {
console.log('Pre-commit review findings:');
checks.forEach(c => console.log(\` \${c}\`));
}
Other useful hooks
Auto-format on file write:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"command": "npx prettier --write $TOOL_FILE_PATH",
"condition": "toolInput.file_path?.endsWith('.ts') || toolInput.file_path?.endsWith('.tsx')"
}
]
}
}
Lint check after edits:
{
"PostToolUse": [
{
"matcher": "Edit",
"command": "npx eslint $TOOL_FILE_PATH --quiet",
"condition": "toolInput.file_path?.match(/\.(ts|tsx|js|jsx)$/)"
}
]
}
Session context on startup:
{
"UserPromptSubmit": [
{
"matcher": "*",
"command": "echo 'Branch: '$(git branch --show-current)' | Last commit: '$(git log --oneline -1)",
"condition": "true"
}
]
}
The hook lifecycle
You type a message
→ UserPromptSubmit hooks fire
→ Claude decides to use a tool
→ PreToolUse hooks fire (can block the tool)
→ Tool executes
→ PostToolUse hooks fire (can add context)
PreToolUse hooks can return non-zero to block the tool. This is how you prevent Claude from running destructive commands or committing secrets.
Production hook patterns
Block dangerous commands:
{
"PreToolUse": [
{
"matcher": "Bash",
"command": "echo 'BLOCKED: destructive command'",
"condition": "toolInput.command.match(/rm -rf|drop table|force push/i)",
"blocking": true
}
]
}
Auto-test after implementation:
{
"PostToolUse": [
{
"matcher": "Write",
"command": "npm test -- --passWithNoTests 2>/dev/null",
"condition": "toolInput.file_path?.includes('/src/')"
}
]
}
Track file changes:
{
"PostToolUse": [
{
"matcher": "Edit|Write",
"command": "echo $(date) $TOOL_FILE_PATH >> .claude/change-log.txt"
}
]
}
Why hooks matter
Without hooks, Claude Code is reactive — it does what you ask. With hooks, it's proactive — it checks, validates, and guards automatically. The pre-commit review hook alone has caught secrets in staged files, oversized binaries, and leftover debug statements that would have shipped to production.
Hooks are the difference between "AI that writes code" and "AI that writes code responsibly."
The Ship Fast Skill Pack includes 5 production-tested hooks alongside 10 Claude Code skills. Pre-commit review, auto-formatting, lint gates, test runners, and session context — all configured and ready to use.
Top comments (0)