Skip to main content

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:

  1. Protected files: Claude must never edit .env, package-lock.json, or anything in a config/production/ directory
  2. Code formatting: All JavaScript and TypeScript files must be formatted after every edit
  3. Audit trail: Every tool Claude uses must be logged with a timestamp
  4. 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:

  1. A PreToolUse hook that blocks edits to protected files
  2. A PostToolUse hook that auto-formats .js and .ts files after edits
  3. A PreToolUse hook (without a matcher) that logs every tool call
  4. 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_PATH against 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 .js or .ts before formatting
  • Use Prettier or another formatter (or just echo for 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-current and git status --short
Build Incrementally

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:

  1. Audit log: Start Claude and ask it to read a file. Check .claude/audit.log for the entry.
  2. Session greeting: Restart Claude and look for the Git branch/status output.
  3. Auto-format: Ask Claude to edit app.js. Verify the file is formatted after the edit.
  4. File protection: Ask Claude to edit .env. It should be blocked.

Part 4: Test the Full Pipeline

Now test all hooks working together:

  1. Start Claude Code (session greeting should appear)
  2. Ask Claude to add a function to app.js (should be logged and formatted)
  3. Ask Claude to edit .env (should be blocked and logged)
  4. Ask Claude to read utils.ts (should be logged but no formatting — it was a read, not an edit)
  5. Check .claude/audit.log for a complete record of all actions
Common Pitfalls
  • 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 grep and date may 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

Help Us Improve

How was this lesson? Take 2 minutes to share your feedback — it helps us make the tutorials better for everyone.

Give Feedback →