documentation/guide-hooks.md

# Claude Hooks

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](../cheatsheets/hooks.cheatmd) for a concise reference of configuration options and patterns.

## Documentation

For complete documentation on Claude Code's hook system:
- [Official Hooks Reference](https://docs.anthropic.com/en/docs/claude-code/hooks) - Complete API reference
- [Hooks Guide](https://docs.anthropic.com/en/docs/claude-code/hooks-guide) - Getting started with examples

For a quick reference of all hook configurations, see the [Hook Configuration Cheatsheet](../cheatsheets/hooks.cheatmd).

## What Claude Installs

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

```elixir
%{
  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:

```elixir
%{
  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:

```elixir
%{
  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 →](https://github.com/bradleygolden/claude/issues/new?title=Hook%20Request:%20[Name]&body=**Hook%20Name:**%20%0A**Mix%20Task%20or%20Command:**%20%0A**Use%20Case:**%20%0A**Which%20Events:**%20%0A%0APlease%20describe%20what%20this%20hook%20would%20do%20and%20why%20it%20would%20be%20useful%20for%20Elixir%20developers.)

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?**
- 💬 [GitHub Discussions](https://github.com/bradleygolden/claude/discussions)
- 🐛 [Issue Tracker](https://github.com/bradleygolden/claude/issues)

## Learn More

For more details on hook events, configuration, and advanced usage, see the [official documentation](https://docs.anthropic.com/en/docs/claude-code/hooks).