Claude Academy
advanced16 min

Advanced Hook Patterns: Quality Gates

Learning Objectives

  • Chain hooks into a quality gate pipeline
  • Build PreToolUse guards that block dangerous operations
  • Use UserPromptSubmit for input validation
  • Coordinate agent teams with TeammateIdle hooks

Beyond Simple Formatting

The hooks you've built so far — auto-formatting and notifications — are the foundation. But hooks can do much more. They can enforce code quality standards, prevent dangerous operations conditionally, validate inputs, and coordinate multi-agent workflows.

In this lesson, we build a complete quality gate pipeline — a series of hooks that ensure every change Claude makes meets your standards.

PreToolUse Guards

PreToolUse hooks fire before Claude executes a tool. Exit code 2 blocks the action. This gives you veto power over any operation.

Guard: No Production Config Changes

{

"hooks": {

"PreToolUse": [

{

"matcher": "Write(config/production/**)",

"command": "echo 'BLOCKED: Production config files cannot be modified by Claude. Edit manually.' && exit 2"

}

]

}

}

Claude receives the error message and knows exactly why the action was blocked.

Guard: No Writes to Main Branch

{

"hooks": {

"PreToolUse": [

{

"matcher": "Write(*)",

"command": "branch=$(git branch --show-current 2>/dev/null); if [ \"$branch\" = 'main' ] || [ \"$branch\" = 'master' ]; then echo 'BLOCKED: Cannot write files while on main/master branch. Create a feature branch first.' && exit 2; fi"

}

]

}

}

This checks the current git branch before any file write. If you're on main/master, the write is blocked. Claude must create a feature branch first.

Guard: No Large File Writes

{

"hooks": {

"PreToolUse": [

{

"matcher": "Write(*)",

"command": "if [ -f \"$file\" ] && [ $(wc -l < \"$file\" 2>/dev/null || echo 0) -gt 500 ]; then echo 'WARNING: This file has 500+ lines. Consider splitting it.' && exit 2; fi"

}

]

}

}

Blocks writes to files over 500 lines, encouraging modular code.

PostToolUse Quality Checks

Beyond formatting, PostToolUse can run quality checks after Claude writes code:

Type Check After Write

{

"hooks": {

"PostToolUse": [

{

"matcher": "Write(*.ts)",

"command": "npx prettier --write $file && npx tsc --noEmit --pretty 2>&1 | head -20 || true"

}

]

}

}

After every TypeScript file write: format with Prettier, then run the type checker. If there are type errors, they appear in Claude's context and it can fix them.

Test After Write

{

"hooks": {

"PostToolUse": [

{

"matcher": "Write(src/services/*/.ts)",

"command": "test_file=$(echo $file | sed 's/.ts$/.test.ts/'); if [ -f \"$test_file\" ]; then npx vitest run \"$test_file\" --reporter=verbose 2>&1 | tail -15; fi"

}

]

}

}

After writing a service file, if a corresponding test file exists, run those tests. Claude immediately sees if its changes broke anything.

UserPromptSubmit: Input Validation

UserPromptSubmit fires after you submit a prompt but before Claude processes it. Use it for:

Prompt Logging

{

"hooks": {

"UserPromptSubmit": [

{

"command": "echo \"$(date '+%Y-%m-%d %H:%M:%S'): prompt submitted\" >> ~/.claude/prompt-audit.log"

}

]

}

}

Creates an audit trail of when prompts are submitted. Useful for teams tracking Claude Code usage.

Content Filtering

{

"hooks": {

"UserPromptSubmit": [

{

"command": "echo \"$prompt\" | grep -qi 'deploy to production' && echo 'BLOCKED: Use the deploy pipeline, not Claude Code, for production deployments.' && exit 2 || true"

}

]

}

}

Blocks prompts that mention deploying to production — directing users to the proper deployment pipeline instead.

TeammateIdle: Agent Coordination

When using agent teams (Module 10), TeammateIdle fires when an agent has no work:

{

"hooks": {

"TeammateIdle": [

{

"command": "echo \"$(date): Agent idle, checking for unassigned tasks\" >> ~/.claude/team-log.txt"

}

]

}

}

The Complete Quality Gate Pipeline

Here's a full quality gate configuration that combines guards, formatters, checks, and notifications:

{

"hooks": {

"PreToolUse": [

{

"matcher": "Write(config/production/**)",

"command": "echo 'BLOCKED: Cannot modify production configs' && exit 2"

},

{

"matcher": "Write(.generated.)",

"command": "echo 'BLOCKED: Cannot modify generated files' && exit 2"

},

{

"matcher": "Bash(git push --force*)",

"command": "echo 'BLOCKED: Force push not allowed' && exit 2"

}

],

"PostToolUse": [

{

"matcher": "Write(*.{ts,tsx})",

"command": "npx prettier --write $file"

},

{

"matcher": "Write(*.py)",

"command": "python -m black $file"

},

{

"matcher": "Write(src/*/.ts)",

"command": "npx tsc --noEmit 2>&1 | head -10 || true"

}

],

"Notification": [

{

"command": "osascript -e 'display notification \"Claude needs your attention\" with title \"Claude Code\" sound name \"Glass\"'"

}

],

"TaskCompleted": [

{

"command": "osascript -e 'display notification \"Task complete!\" with title \"Claude Code\" sound name \"Hero\"'"

}

],

"SessionStart": [

{

"command": "echo 'Quality gates active: production config guard, auto-format, type-check'"

}

]

}

}

What This Pipeline Does

1. SessionStart: Logs that quality gates are active

2. PreToolUse guards: Blocks production config changes, generated file edits, and force pushes

3. PostToolUse formatters: Auto-formats TypeScript and Python files

4. PostToolUse type check: Runs TypeScript type checking after writes

5. Notification: Desktop alert when Claude needs you

6. TaskCompleted: Sound + visual alert when a background task finishes

The Flow

Claude wants to write a file

PreToolUse: Is it a blocked file? → Yes: BLOCKED (exit 2)

→ No: Continue

Claude writes the file

PostToolUse #1: Run formatter (Prettier/Black)

PostToolUse #2: Run type checker

Claude sees type errors (if any) and can fix them

[Later] Claude finishes and goes idle

Notification: Desktop alert

Hook Ordering

When multiple hooks are defined for the same event, they run in the order they're listed:

{

"PostToolUse": [

{ "matcher": "Write(*.ts)", "command": "command-A" },

{ "matcher": "Write(*.ts)", "command": "command-B" },

{ "matcher": "Write(*.ts)", "command": "command-C" }

]

}

Execution order: A → B → C. If command-A fails, B and C still run.

Key Takeaway

Advanced hooks create quality gates that enforce standards automatically. PreToolUse guards block dangerous operations (production configs, force pushes, generated files) with exit code 2. PostToolUse chains formatters and type checkers so Claude's output is always clean. UserPromptSubmit validates input before processing. The complete quality gate pipeline — guards, formatters, checks, notifications — runs silently in the background, catching issues before they reach your codebase.