Zum Inhalt springen

Practical Recipes

Knowledge

Ready-Made Hook Configurations to Copy

The following recipes can be directly added to your .claude/settings.json. Each solves a concrete problem -- deterministically and reliably.

Auto-Format After Every Change

Problem: Claude sometimes formats code inconsistently -- especially with large changes or when the context is full.

Solution: PostToolUse Hook on Edit and Write.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write $TOOL_INPUT_FILE_PATH && npx eslint --fix $TOOL_INPUT_FILE_PATH"
          }
        ]
      }
    ]
  }
}

Result: Every file Claude changes or creates is immediately formatted and linted. No more manual cleanup.

*Performance Tip

Prettier and ESLint on individual files are fast (under 1 second). The overhead is minimal, but the quality gain is enormous.

Tests Before Every Commit

Problem: Claude sometimes commits code that breaks tests -- especially during complex refactorings.

Solution: PreToolUse Hook with if filter on git commit.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "if": "Bash(git commit*)",
        "hooks": [
          {
            "type": "command",
            "command": "npm test 2>&1; if [ $? -ne 0 ]; then echo 'Tests failed -- commit blocked.' >&2; exit 2; fi"
          }
        ]
      }
    ]
  }
}

Result: No commit goes through if tests fail. Claude gets the feedback and automatically fixes the errors.

!Exit Code 2 Is Crucial

Without exit 2, the commit would go through despite failing tests. Exit code 2 blocks the action and gives Claude the feedback via stderr.

TypeScript Check After File Changes

Problem: Claude sometimes produces type errors that only surface at build time.

Solution: PostToolUse Hook on Write for TypeScript files.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "if echo $TOOL_INPUT_FILE_PATH | grep -q '\\.tsx\\?$'; then npx tsc --noEmit 2>&1 | head -20; fi"
          }
        ]
      }
    ]
  }
}

Result: Type errors become visible immediately, not at the next build. Claude sees the errors in context and can fix them directly.

Security Scan After New Dependencies

Problem: New npm packages can introduce known security vulnerabilities.

Solution: PostToolUse Hook on Write that runs an audit when package.json changes.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "if echo $TOOL_INPUT_FILE_PATH | grep -q 'package.json'; then npm audit --audit-level=high 2>&1; if [ $? -ne 0 ]; then echo 'Security vulnerabilities found!' >&2; exit 2; fi; fi"
          }
        ]
      }
    ]
  }
}

Result: Security vulnerabilities are caught early -- before the code goes to production.

Restore Context After Compact

Problem: After context compaction, Claude forgets important project details that are in CLAUDE.md but get lost in the compacted context.

Solution: PostCompact Hook that re-injects critical information.

{
  "hooks": {
    "PostCompact": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "cat .claude/post-compact-context.md"
          }
        ]
      }
    ]
  }
}

Create a file .claude/post-compact-context.md with the most important project info:

## Critical Project Context (after Compact)
- We are working on Feature X on branch feature/xyz
- Architecture: Next.js 15 App Router + Zustand
- DO NOT change: src/lib/auth.ts (being worked on by another team)
- Current task: [updated manually]

*Keep Post-Compact Context Small

The file should be short and precise -- 20-30 lines maximum. Too much context after a compact is counterproductive because the compact was meant to free up space.

Desktop Notification When Claude Is Waiting

Problem: Claude is done or waiting for input, but you're in another window.

Solution: Notification Hook with an OS-specific command.

{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code is waiting for you!\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

iPlatform-Specific

The example uses osascript for macOS. For Linux, use notify-send "Claude Code is waiting for you!", for Windows use powershell -Command "New-BurntToastNotification -Text 'Claude Code is waiting for you!'".

Understand

Combining Hooks

The true power of Hooks shows in combination. A typical project configuration looks like this:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "npx prettier --write $TOOL_INPUT_FILE_PATH" },
          { "type": "command", "command": "npx eslint --fix $TOOL_INPUT_FILE_PATH" }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "if": "Bash(git commit*)",
        "hooks": [
          { "type": "command", "command": "npm test" }
        ]
      },
      {
        "matcher": "Bash",
        "if": "Bash(git push*--force*)",
        "hooks": [
          { "type": "command", "command": "echo 'Force-push blocked!' >&2 && exit 2" }
        ]
      }
    ],
    "PostCompact": [
      {
        "hooks": [
          { "type": "command", "command": "cat .claude/post-compact-context.md" }
        ]
      }
    ]
  }
}

This configuration ensures: Code is always formatted, tests run before every commit, force pushes are impossible, and no context is lost after a compact.

Apply

Start with a single Hook -- for example, the auto-format Hook. Make sure it works, then gradually add more. Every Hook you add is one less guarantee you have to think about.

*Debugging Tip

If a Hook doesn't work as expected, test the command manually in the terminal first. The most common errors: wrong path, missing permissions, or missing tool (e.g., Prettier not installed).

Reflect

Hooks transform "I hope Claude does it right" into "I know it runs correctly." This is a fundamental difference in collaborating with AI agents: You give freedom in the solution but set hard boundaries on quality. This principle -- creative freedom within firm guardrails -- is the key to productive work with Claude Code.