Claude Hooks

View Source

Claude provides a modern atom-based hook system that integrates with Claude Code to ensure your Elixir code is production-ready. The hooks use sensible defaults and can be configured with simple atom shortcuts.

📋 Quick Reference: See the Hooks Cheatsheet for a concise reference of configuration options and patterns.

Documentation

For complete documentation on Claude Code's hook system:

For a quick reference of all hook configurations, see the Hook Configuration Cheatsheet.

What Claude Installs

When you run mix igniter.install claude, it automatically sets up default hooks:

%{
  hooks: %{
    stop: [:compile, :format],
    subagent_stop: [:compile, :format],
    post_tool_use: [:compile, :format],
    # These only run on git commit commands
    pre_tool_use: [:compile, :format, :unused_deps]
  }
}

This provides:

  1. Format checking - Alerts when Elixir files need formatting
  2. Compilation checking - Catches errors and warnings immediately
  3. Pre-commit validation - Ensures clean code before commits

Available Hook Atoms

Claude provides these atom shortcuts that expand to full hook configurations:

Available Hooks

  • :compile - Runs mix compile --warnings-as-errors with halt_pipeline?: true (stops on failure)
    • For stop/subagent_stop: Uses blocking?: false to prevent infinite loops
  • :format - Runs mix format --check-formatted (checks only, doesn't auto-format)
    • For stop/subagent_stop: Uses blocking?: false to prevent infinite loops
  • :unused_deps - Runs mix deps.unlock --check-unused (pre_tool_use on git commits only)

Hook Events

Different hook events run at different times:

  • pre_tool_use - Before tool execution (can block tools)
  • post_tool_use - After tool execution completes successfully
  • user_prompt_submit - Before processing user prompts (can add context or block)
  • notification - When Claude needs permission or input is idle
  • stop - When Claude Code finishes responding (main agent)
  • subagent_stop - When a sub-agent finishes responding
  • pre_compact - Before context compaction (manual or automatic)
  • session_start - When Claude Code starts or resumes a session

⚠️ Stop Hook Loop Prevention

Stop and subagent_stop hooks use blocking?: false by default to prevent infinite loops:

  1. When a stop hook fails with blocking?: true, it sends error feedback to Claude
  2. Claude tries to fix the issue and finishes responding again
  3. This triggers the stop hook again, creating an infinite loop

The default blocking?: false setting keeps you informed about issues without causing Claude to get stuck. If you need blocking behavior, explicitly set blocking?: true but be aware of the loop risk.

Advanced Configuration

You can mix atom shortcuts with explicit configurations:

%{
  hooks: %{
    # Standard hooks
    post_tool_use: [:compile, :format],

    # Custom hook with options
    stop: [
      :format,
      {"custom_task", halt_pipeline?: true},
      {"cmd echo 'Done'", blocking?: false}
    ],

    # Conditional execution
    pre_tool_use: [
      {"test", when: "Bash", command: ~r/^git push/}
    ],
    
    # Control output verbosity (rarely needed)
    subagent_stop: [
      {:compile, output: :full},  # WARNING: Can cause context overflow
      :format                      # Default :none - recommended
    ]
  }
}

Hook Options

  • :when - Tool/event matcher (atoms, strings, or lists)
  • :command - Additional command pattern for Bash (string or regex)
  • :halt_pipeline? - Stop subsequent hooks on failure (default: false)
  • :blocking? - Treat non-zero exit as blocking error (default: true)
  • :env - Environment variables as a map
  • :output - Control output verbosity (default: :none)
    • :none - Only show pipeline summary on failures (prevents context overflow) [Recommended]
    • :full - Show complete hook output plus pipeline summary (use sparingly - can cause context issues)

Hook Event Reporting (Experimental)

Claude supports sending hook events to external systems for monitoring and integration.

Webhook Reporter

Note: This feature is experimental and the API may change in future releases.

Send hook events to HTTP endpoints:

%{
  reporters: [
    {:webhook,
      url: "https://example.com/webhook",
      headers: %{"Authorization" => "Bearer token"},
      timeout: 5000,
      retry_count: 3
    }
  ]
}

The webhook reporter sends the raw Claude Code hook event data as JSON, including:

  • Event type and timestamp
  • Tool information (for tool-related events)
  • Session and project context

How It Works

  1. Configuration: Define hooks in .claude.exs using atoms, strings, or tuples with options
  2. Installation: mix claude.install creates .claude/settings.json with a dispatcher command
  3. Execution: Claude Code runs mix claude.hooks.run <event> passing JSON via stdin
  4. Expansion: The dispatcher reads .claude.exs and expands atoms to full commands
  5. Running: Commands execute as Mix tasks (default) or shell commands (with "cmd " prefix)
  6. Communication: Hooks return exit codes only (0 = success, non-zero = failure, no JSON output)
  7. Reporting: Events are dispatched to configured reporters for external integration

Request a New Hook

Have an idea for a new standardized hook that would be useful for Elixir development? We'd love to hear from you!

Request a new hook →

Popular requests might be added as default hooks in future releases!

Troubleshooting

Hooks not running?

  • Check .claude/settings.json exists and contains hook configuration
  • Verify .claude.exs has the correct hook definitions
  • Run mix claude.install to regenerate hook configuration
  • Use Claude Code's /hooks command to verify hooks are registered

Compilation/format errors not showing?

  • Ensure hooks are defined for the right events (post_tool_use, stop, etc.)
  • Check that the :compile and :format atoms are included
  • Verify Mix is available in your PATH

Need help?

Learn More

For more details on hook events, configuration, and advanced usage, see the official documentation.