BattlecatAI
HomeBrowsePathsToolsLevel UpRewardsBookmarksSearchSubmit

Battlecat AI — Built on the AI Maturity Framework

Stop Asking Claude to Format Code: Build Hooks That Do It Automatically
L3 SupervisorPracticeintermediate7 min read

Stop Asking Claude to Format Code: Build Hooks That Do It Automatically

Tired of reminding Claude to format your code, send notifications, or follow project rules? Claude Code's hook system lets you build event-driven automations that fire at key moments — when files change, commands run, or Claude needs input — without relying on the LLM to remember.

automationworkflow optimizationhooksevent-driven programmingClaude Code

Claude keeps forgetting to format your code. You've asked it three times to run Prettier after editing files, but it only remembers half the time. Sound familiar?

There's a better way than hoping your AI assistant follows instructions consistently.

Why This Matters

Relying on LLMs for repetitive, rule-based tasks is like hiring a brilliant consultant to file your paperwork — technically possible, but wildly inefficient. Claude Code's hook system flips this dynamic by letting you build deterministic automations that fire at specific moments in Claude's lifecycle.

Instead of prompting "remember to format the code," you write a hook that automatically runs Prettier every time Claude edits a file. Instead of checking if Claude finished a task, you get a desktop notification the moment it needs your input.

Hooks provide deterministic control over Claude Code's behavior, ensuring certain actions always happen rather than relying on the LLM to choose to run them.

This isn't just about convenience — it's about reliability. Critical workflows like code formatting, file validation, and security checks shouldn't depend on an AI's memory. They should be automatic, consistent, and bulletproof.


Understanding Claude Code Hooks

Hooks are user-defined shell commands that execute automatically when specific events occur in Claude's workflow. Think of them as event listeners for your AI coding assistant.

The system supports multiple trigger points:

  • File operations: When Claude creates, edits, or deletes files
  • Command execution: Before and after Claude runs shell commands
  • User interactions: When Claude needs input or permission
  • Session events: At startup, shutdown, or context compaction
  • Tool usage: Before and after Claude uses specific tools

Each hook can run any shell command, from simple notifications to complex validation scripts. You can filter when hooks fire using matchers (think of them as conditional logic), and you can store hook configurations at the user, project, or workspace level.

For decisions that require judgment rather than deterministic rules, you can also use prompt-based hooks or agent-based hooks that use a Claude model to evaluate conditions.

The Three Types of Hooks

  1. Command hooks: Run shell commands directly
  2. Prompt-based hooks: Let Claude decide whether to proceed based on context
  3. Agent-based hooks: Use a separate Claude instance to evaluate complex conditions

Most automation scenarios use command hooks for their speed and reliability.


Setting Up Your First Hook: Desktop Notifications

Let's build a notification system so you never have to babysit Claude's terminal again. When Claude finishes a task or needs permission, you'll get an instant desktop alert.

Using the Interactive Setup

The fastest approach is Claude Code's built-in configuration interface:

  1. Open the hooks menu: Type /hooks in the Claude Code CLI
  2. Select the trigger: Choose Notification from the event list
  3. Configure the matcher: Set it to * to catch all notification types
  4. Add your command: Select + Add new hook… and enter the platform-specific command below
  5. Choose storage location: Select User settings to apply across all projects

Platform-Specific Commands

macOS (using AppleScript):

osascript -e 'display notification "Claude Code needs your attention" with title "Claude Code"'

Linux (using notify-send):

notify-send 'Claude Code' 'Claude Code needs your attention'

Windows (using PowerShell):

powershell.exe -Command "[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')"

Manual Configuration

Alternatively, add this configuration directly to ~/.claude/settings.json:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
          }
        ]
      }
    ]
  }
}

Now when you ask Claude to perform a task that requires permission, switch to another application — you'll receive an immediate notification when it's ready for your input.


Advanced Hook Patterns

Auto-Format Code After Every Edit

Stop reminding Claude to format code. This hook automatically runs Prettier on every file Claude modifies:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "str_replace_editor",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write {file_path}"
          }
        ]
      }
    ]
  }
}

The PostToolUse event fires after Claude uses any tool. The matcher str_replace_editor filters it to only file editing operations. The {file_path} variable gets automatically populated with the edited file's path.

Block Edits to Protected Files

Prevent Claude from accidentally modifying critical configuration files:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "str_replace_editor",
        "hooks": [
          {
            "type": "command",
            "command": "if [[ '{file_path}' == *'.env'* ]]; then echo 'ERROR: Cannot edit .env files' && exit 1; fi"
          }
        ]
      }
    ]
  }
}

This uses PreToolUse to intercept file operations before they execute. If the file path contains .env, the hook exits with an error code, blocking the edit.

Hooks that exit with non-zero status codes will block the triggering action and display an error message to Claude.

Re-inject Context After Compaction

When Claude's conversation gets too long, it automatically compacts older messages to save context space. Use hooks to re-inject critical information:

{
  "hooks": {
    "PostCompaction": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "cat project-guidelines.md"
          }
        ]
      }
    ]
  }
}

This hook runs after context compaction and outputs your project guidelines, ensuring Claude always has access to key project information even after conversation history gets compressed.

Smart Git Commits

Automatically commit Claude's changes with descriptive messages:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "str_replace_editor",
        "hooks": [
          {
            "type": "command",
            "command": "git add {file_path} && git commit -m 'Claude Code: Updated {file_path}'"
          }
        ]
      }
    ]
  }
}

Hook Architecture and Advanced Features

Matchers: Conditional Logic for Hooks

Matchers determine when hooks fire. They work differently depending on the event type:

  • Tool-based events: Match against tool names (str_replace_editor, bash, etc.)
  • Notification events: Match against notification types (permission_prompt, idle_prompt)
  • File events: Match against file paths or extensions
  • Empty matcher (""): Fires on all events of that type
  • Wildcard matcher ("*"): Same as empty, but more explicit

Variable Substitution

Hooks support dynamic variables that get populated at runtime:

  • {file_path}: Path to the file being operated on
  • {tool_name}: Name of the tool being used
  • {command}: Shell command being executed
  • {notification_type}: Type of notification being sent

Configuration Hierarchy

Hook configurations follow a precedence order:

  1. Project settings: .claude/settings.json in project root (highest priority)
  2. Workspace settings: .claude/settings.json in workspace root
  3. User settings: ~/.claude/settings.json (lowest priority)

This lets you define global defaults in user settings while overriding specific behaviors per project.

Async Hooks and Error Handling

Hooks run synchronously by default, blocking Claude until they complete. For long-running operations, you can make hooks asynchronous or add timeout handling:

# Run in background (async)
your-long-command &

# Add timeout
timeout 30s your-command

Failed hooks (non-zero exit codes) will block the triggering action and show error messages to Claude, giving you a safety mechanism for validation hooks.


The Bottom Line

Hooks transform Claude Code from a helpful assistant that sometimes forgets your preferences into a reliable automation platform that enforces your workflows consistently. Instead of repeatedly prompting Claude to format code, validate files, or send notifications, you define these behaviors once and let the system handle them automatically. The result is faster development cycles, fewer mistakes, and the confidence that critical tasks always happen — regardless of what Claude remembers or forgets.

Try This Now

  • 1Set up a desktop notification hook using `/hooks` in Claude Code CLI to get alerted when Claude needs input
  • 2Configure a PostToolUse hook with **Prettier** to automatically format code after every file edit
  • 3Create a PreToolUse hook to block Claude from editing sensitive files like `.env` or `package-lock.json`
  • 4Add a PostCompaction hook that re-injects your project guidelines after context compression
  • 5Explore the full hooks reference documentation to discover additional events like session startup and tool-specific triggers

How many Orkos does this deserve?

Rate this tutorial

Sources (1)

  • https://code.claude.com/docs/en/hooks-guide
← All L3 tutorialsBrowse all →