AI Starter Package
Guides/Claude Code Hooks

How to Set Up Claude Code Hooks

Hooks are deterministic automation that fires on every event — no prompting required. Use them to block dangerous commands, track every edit, restore context on session start, and build a safety net that never forgets.

AutomationSafetyAdvanced

What Are Hooks?

Hooks are scripts that Claude Code executes automatically when specific events occur. Unlike prompt-based instructions that Claude may interpret loosely, hooks are deterministic — they run the same code every single time the event fires.

Think of them as git hooks for your AI workflow. A pre-commit hook blocks bad commits; a PreToolUse hook blocks dangerous tool calls before they execute.

Key difference from CLAUDE.md rules
CLAUDE.md rules are suggestions that Claude follows with high reliability. Hooks are code that executes unconditionally. If you need a guarantee — not a strong suggestion — use a hook.

Hook Types

Claude Code supports several hook points. Each fires at a different moment in the session lifecycle:

1
PreToolUse
Fires before a tool runs. Block or modify tool calls before they execute.
2
PostToolUse
Fires after a tool completes. Inspect results, log outputs, trigger follow-up actions.
3
SessionStart
Fires when a new Claude Code session begins. Restore context, load memory, show status.
4
SessionEnd
Fires when a session ends. Consolidate memory, archive notes, update metrics.
5
PreCompact
Fires before context compaction. Save critical state so nothing is lost when the window shrinks.
6
SubagentStart
Fires when a sub-agent is spawned. Inject rules, set boundaries, pass context to child agents.

Setting Up settings.json

Hooks are configured in .claude/settings.json under the hooks key. Each hook has a matcher (which events to intercept) and a command (what script to run).

// .claude/settings.json
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"command": "node .claude/hooks/safety-gate.cjs"
}],
"SessionStart": [{
"command": "node .claude/hooks/restore-context.cjs"
}]
}
}

Safety Hooks

The highest-value hook is a PreToolUse safety gate on the Bash tool. This intercepts every shell command and blocks dangerous patterns before they execute.

// Block dangerous bash commands
const blocked = ['rm -rf', 'format', 'mkfs', 'dd if='];
const command = input.tool_input.command;
const match = blocked.find(b => command.includes(b));
if (match) {
output({ blocked: true, reason: `Blocked: ${match}` });
}

This runs as code, not as a suggestion. Even if Claude is convinced it should run rm -rf /, the hook stops it.

Intelligence Hooks

PostToolUse hooks let you record what Claude does. Every file edit, every command, every tool call — logged and structured.

Edit Tracking

  • Log every file edit with timestamp and diff summary
  • Build an audit trail of all changes per session
  • Feed edit history into memory for smarter context

Metrics Collection

  • Count tool calls per session for usage analysis
  • Track which files are edited most frequently
  • Measure session duration and productivity patterns

Session Hooks

SessionStart and SessionEnd hooks bookend every conversation. Use them to restore context at the start and consolidate memory at the end.

// SessionStart: restore working memory
const memory = fs.readFileSync('.claude/memory.md');
const kb = fs.readFileSync('.claude/knowledge-base.md');
output({ context: memory + kb });
// SessionEnd: save what was learned
const sessionLog = collectSessionEdits();
fs.appendFileSync('.claude/memory.md', sessionLog);

The PreCompact hook is equally critical — it fires before Claude's context window is compressed, giving you a chance to save state that would otherwise be lost.

Writing Hook Handlers

Hook handlers are Node.js scripts (typically .cjs files) that receive event data on stdin and write results to stdout as JSON.

// .claude/hooks/hook-handler.cjs
const fs = require('fs');
let input = '';
process.stdin.on('data', d => input += d);
process.stdin.on('end', () => {
const event = JSON.parse(input);
// Your logic here
const result = { status: 'ok' };
process.stdout.write(JSON.stringify(result));
});

Testing Your Hooks

Always test hooks before relying on them. The simplest approach is to pipe mock event data into your handler:

# Test a PreToolUse hook with a mock Bash event
echo '{"tool":"Bash","tool_input":{"command":"rm -rf /"}}' \
| node .claude/hooks/safety-gate.cjs
# Expected: {"blocked":true,"reason":"Blocked: rm -rf"}

After verifying with mock data, trigger real events in a test session and check your log files to confirm the hooks fired.

Common Mistakes

Hook script timeout
Hooks have a default timeout. If your handler does heavy I/O or network calls, it will silently fail. Keep handlers fast and lightweight.
Missing handler file
The command field points to a script that does not exist or is not executable. Always verify the path and run chmod +x on your handler.
Silent failures
Hooks that crash do not surface errors in the main session. Add try/catch blocks and write to a log file so you can debug after the fact.
Blocking on every tool call
An overly broad PreToolUse matcher slows down every action. Use specific matchers to target only the tools you need to intercept.
Forgetting to return JSON
Hook handlers must output valid JSON to stdout. Plain text or console.log debugging will break the hook protocol.
Not testing after changes
Edit a hook, forget to test it. Always trigger the event manually after modifying a handler to confirm it still works.

Get Pre-Built Hook Templates

The AI Starter Package includes battle-tested hook handlers for safety gates, edit tracking, session memory, context restoration, and metrics collection — ready to drop into any project.