Capstone: Build a Hook Pipeline
Time: 30-60 minutes | Skills: Lifecycle Events, Hook Configuration, Matchers, Commands
The Scenario
You are setting up a Claude Code environment for a team project. The team lead wants the following rules enforced automatically — no one should have to remember them manually:
- Protected files: Claude must never edit
.env,package-lock.json, or anything in aconfig/production/directory - Code formatting: All JavaScript and TypeScript files must be formatted after every edit
- Audit trail: Every tool Claude uses must be logged with a timestamp
- Session greeting: When a session starts, Claude should display the current Git branch and any uncommitted changes
Your goal is to build a hook pipeline that implements all four requirements.
Requirements
Create a .claude/settings.json that includes:
- A PreToolUse hook that blocks edits to protected files
- A PostToolUse hook that auto-formats
.jsand.tsfiles after edits - A PreToolUse hook (without a matcher) that logs every tool call
- A SessionStart hook that displays the Git branch and status
Step-by-Step Guide
Part 1: Set Up a Test Project
Create a project with some files to test against:
mkdir hook-pipeline
cd hook-pipeline
git init
mkdir -p config/production .claude
echo "DB_PASSWORD=secret" > .env
echo "module.exports = { prod: true }" > config/production/db.js
echo "function greet() { return 'hello' }" > app.js
echo "const x = 1" > utils.ts
git add -A
git commit -m "Initial setup"
Part 2: Write the Hook Configuration
Create .claude/settings.json with hooks for all four requirements. Here are some hints:
For the file protection hook:
- Use PreToolUse with an Edit matcher
- Check
$CLAUDE_FILE_PATHagainst your protected patterns - Exit with code 1 to block, code 0 to allow
For the auto-format hook:
- Use PostToolUse with an Edit matcher
- Check if the file extension is
.jsor.tsbefore formatting - Use Prettier or another formatter (or just
echofor testing)
For the audit log hook:
- Use PreToolUse without a matcher (fires for all tools)
- Append to
.claude/audit.log - Include a timestamp and the tool name
For the session greeting:
- Use SessionStart with no matcher
- Run
git branch --show-currentandgit status --short
Do not try to write all four hooks at once. Start with one (like the audit log — it is the simplest), test it, then add the next one. This makes debugging much easier.
Part 3: Test Each Hook
Test your hooks one at a time:
- Audit log: Start Claude and ask it to read a file. Check
.claude/audit.logfor the entry. - Session greeting: Restart Claude and look for the Git branch/status output.
- Auto-format: Ask Claude to edit
app.js. Verify the file is formatted after the edit. - File protection: Ask Claude to edit
.env. It should be blocked.
Part 4: Test the Full Pipeline
Now test all hooks working together:
- Start Claude Code (session greeting should appear)
- Ask Claude to add a function to
app.js(should be logged and formatted) - Ask Claude to edit
.env(should be blocked and logged) - Ask Claude to read
utils.ts(should be logged but no formatting — it was a read, not an edit) - Check
.claude/audit.logfor a complete record of all actions
- JSON syntax errors break all hooks. Validate your JSON before testing.
- Exit codes matter. A hook that accidentally exits with 1 will block actions you want to allow.
- Shell compatibility. If you are on native Windows (not WSL), commands like
grepanddatemay not work. Use PowerShell equivalents or switch to WSL for testing.
Part 5: Refine and Extend
After verifying the basic pipeline works, consider these extensions:
- Add a SessionEnd hook that summarizes the session (total edits, blocked actions)
- Add a Notification hook that plays a sound or shows a popup
- Modify the file protection hook to also block Write tool calls (not just Edit)
Self-Assessment
- Created a PreToolUse hook that blocks edits to protected files (.env, package-lock.json, production config)
- Created a PostToolUse hook that auto-formats JavaScript/TypeScript files after edits
- Created a PreToolUse hook that logs every tool call with a timestamp
- Created a SessionStart hook that displays Git branch and status
- Verified all hooks fire correctly during a Claude session
- Tested the file protection hook by attempting to edit a protected file (was it blocked?)
- Reviewed the audit log for a complete record of the session
How was this lesson? Take 2 minutes to share your feedback — it helps us make the tutorials better for everyone.