DEV Community

WEDGE Method Dev
WEDGE Method Dev

Posted on

39 Claude Code Hooks That Run Automatically: The Complete Pattern Library

Claude Code hooks are scripts that execute automatically when specific events occur - before or after tool calls, on notifications, or when the session starts. I've built 39 hooks that automate my entire development workflow.

Here are 10 of the most impactful patterns.

How Hooks Work

Hooks fire on three event types:

  • PreToolUse - Before Claude executes a tool (edit, bash, write)
  • PostToolUse - After a tool completes
  • Notification - When Claude sends a notification

Configure in .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit",
        "command": "bash .claude/hooks/lint-check.sh"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Hook 1: Auto-Lint on Every Edit

#!/bin/bash
# .claude/hooks/lint-check.sh
# Runs ESLint on any file being edited

FILE=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path // empty')
if [[ "$FILE" == *.ts || "$FILE" == *.tsx ]]; then
    npx eslint "$FILE" --fix 2>/dev/null
fi
Enter fullscreen mode Exit fullscreen mode

Hook 2: Secret Scanner

#!/bin/bash
# Blocks any edit that would introduce a secret

INPUT=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.new_string // empty')
if echo "$INPUT" | grep -qE '(sk-[a-zA-Z0-9]{20,}|AKIA[A-Z0-9]{16}|ghp_[a-zA-Z0-9]{36})'; then
    echo "BLOCKED: Potential secret detected in edit"
    exit 1
fi
Enter fullscreen mode Exit fullscreen mode

Hook 3: Test Runner After Changes

#!/bin/bash
# PostToolUse hook - runs tests after any file edit

FILE=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path // empty')
if [[ "$FILE" == *.ts ]]; then
    # Find and run related test file
    TEST_FILE="${FILE%.ts}.test.ts"
    if [[ -f "$TEST_FILE" ]]; then
        npx jest "$TEST_FILE" --silent 2>&1 | tail -5
    fi
fi
Enter fullscreen mode Exit fullscreen mode

Hook 4: Import Verifier

#!/bin/bash
# Checks that all imports in edited files actually exist

FILE=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path // empty')
if [[ "$FILE" == *.ts || "$FILE" == *.tsx ]]; then
    npx tsc --noEmit "$FILE" 2>&1 | grep "Cannot find module" | head -5
fi
Enter fullscreen mode Exit fullscreen mode

Hook 5: Build Validator

#!/bin/bash
# PostToolUse: ensures build still passes after changes

if [[ -f "package.json" ]]; then
    npm run build 2>&1 | tail -3
    if [[ $? -ne 0 ]]; then
        echo "WARNING: Build failed after last change"
    fi
fi
Enter fullscreen mode Exit fullscreen mode

Hook 6: Git Diff Size Limiter

#!/bin/bash
# PreToolUse on Bash: warns if uncommitted changes are getting large

LINES_CHANGED=$(git diff --stat | tail -1 | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+')
if [[ "$LINES_CHANGED" -gt 500 ]]; then
    echo "WARNING: $LINES_CHANGED lines changed without commit. Consider committing."
fi
Enter fullscreen mode Exit fullscreen mode

Hook 7: Dependency Checker

#!/bin/bash
# After package.json edits, verify no vulnerabilities

FILE=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path // empty')
if [[ "$FILE" == *"package.json" ]]; then
    npm audit --production 2>&1 | grep -E "vulnerabilities|found"
fi
Enter fullscreen mode Exit fullscreen mode

Hook 8: Documentation Sync

#!/bin/bash
# When a function signature changes, flag docs for update

FILE=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path // empty')
OLD=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.old_string // empty')

if echo "$OLD" | grep -q "export function\|export const.*=.*=>"; then
    README_REF=$(grep -l "$(basename $FILE)" README.md docs/*.md 2>/dev/null)
    if [[ -n "$README_REF" ]]; then
        echo "NOTE: Function signature changed. Update docs: $README_REF"
    fi
fi
Enter fullscreen mode Exit fullscreen mode

Hook 9: Performance Guard

#!/bin/bash
# Warns about common performance anti-patterns

NEW=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.new_string // empty')

if echo "$NEW" | grep -qE 'useEffect.*\[\].*fetch|setTimeout.*0|JSON\.parse.*JSON\.stringify'; then
    echo "PERF WARNING: Detected potential performance anti-pattern"
fi
Enter fullscreen mode Exit fullscreen mode

Hook 10: Deployment Blocker

#!/bin/bash
# PreToolUse on Bash: blocks deploy commands if tests haven't passed

CMD=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.command // empty')
if echo "$CMD" | grep -qE 'vercel deploy|git push|npm publish'; then
    LAST_TEST=$(cat .claude/last-test-result 2>/dev/null)
    if [[ "$LAST_TEST" != "passed" ]]; then
        echo "BLOCKED: Cannot deploy - tests haven't passed. Run tests first."
        exit 1
    fi
fi
Enter fullscreen mode Exit fullscreen mode

The Power of Chaining

The real productivity gain comes from combining hooks:

  1. You edit a file → lint hook auto-fixes formatting
  2. Edit completes → test hook runs related tests
  3. Tests pass → build hook verifies compilation
  4. Build passes → deploy hook allows deployment

Zero manual intervention. Every change is validated automatically.

Getting Started

Start with just 2-3 hooks (secret scanner + lint + test runner). Add more as you identify patterns in your workflow.

The full collection of 39 hooks, plus 125 custom commands and 13 MCP server configs, is in my Claude Code Mastery Guide.


What repetitive dev workflow would you automate first? Drop a comment - I might have a hook pattern for it.

Top comments (0)