# `ExAthena.Hooks`
[🔗](https://github.com/udin-io/ex_athena/blob/v0.7.1/lib/ex_athena/hooks.ex#L1)

Lifecycle hooks the loop fires at key transitions.

Shape mirrors Claude Code's SDK so existing hook code ports cleanly:

    hooks = %{
      PreToolUse: [%{matcher: "Write|Edit", hooks: [&deny_protected_paths/2]}],
      PostToolUse: [%{matcher: "Bash", hooks: [&capture_test_output/2]}],
      Stop: [&log_stop/2]
    }

Each hook callback receives `(input_map, tool_use_id)` and returns one of:

  * `:ok` — continue with no side effects.
  * `{:deny, reason}` — only meaningful from `PreToolUse` /
    `PermissionRequest`; deny the tool call.
  * `{:halt, reason}` — stop the loop immediately.
  * `{:inject, message_or_messages}` — append the given message
    (or list of messages) to the conversation. Useful for
    `experimental.chat.system.transform`-style context injection.
  * `{:transform, new_prompt}` — only valid from `UserPromptSubmit`;
    rewrites the incoming user prompt before it enters the loop.
  * `{:augment, text}` — only valid from `PostToolUse`; appends `text`
    to the tool result content visible to the model on the next turn.
    Multiple augments from different hooks are joined with `"\n"`.

Hooks are matched by `:matcher` (regex run against `tool_name`); a `nil`
matcher or a missing `:matcher` key fires for every tool. Lifecycle-only
events are passed as plain function lists, not wrapped in matcher maps.

## Catalog of supported events

  * Session: `:SessionStart`, `:SessionEnd`
  * Per-turn: `:UserPromptSubmit`, `:ChatParams`, `:Stop`, `:StopFailure`
  * Per-tool: `:PreToolUse`, `:PostToolUse`, `:PostToolUseFailure`,
    `:PermissionRequest`, `:PermissionDenied`
  * Subagent: `:SubagentStart`, `:SubagentStop`
  * Compaction: `:PreCompact`, `:PreCompactStage`, `:PostCompact`
  * Notification: `:Notification`

# `hook_fun`

```elixir
@type hook_fun() :: (map(), String.t() -&gt; term())
```

# `lifecycle_outputs`

```elixir
@type lifecycle_outputs() :: %{
  halt: nil | {:halt, term()},
  injects: [ExAthena.Messages.Message.t()],
  transform: nil | String.t()
}
```

# `matcher`

```elixir
@type matcher() :: String.t() | Regex.t() | nil
```

# `matcher_group`

```elixir
@type matcher_group() :: %{matcher: matcher(), hooks: [hook_fun()]}
```

# `t`

```elixir
@type t() :: %{optional(atom()) =&gt; [matcher_group()] | [hook_fun()]}
```

# `events`

```elixir
@spec events() :: [atom()]
```

Catalog of every hook event ex_athena fires today. Useful for hosts
that want to enumerate or validate user-supplied hook tables.

# `run_lifecycle`

```elixir
@spec run_lifecycle(t(), atom(), map()) :: :ok | {:halt, term()}
```

Fire lifecycle hooks that aren't scoped to a tool (Stop, SessionStart,
etc.). Backward-compatible: returns `:ok | {:halt, reason}` like before.
Use `run_lifecycle_with_outputs/3` for events that may inject messages
or transform prompts.

# `run_lifecycle_with_outputs`

```elixir
@spec run_lifecycle_with_outputs(t(), atom(), map()) :: lifecycle_outputs()
```

Like `run_lifecycle/3` but returns a structured outputs map so callers
can act on `{:inject, msg}` and `{:transform, prompt}` returns. Used
by the kernel to thread `UserPromptSubmit` transforms and
`:inject`-driven message injection across hook events.

Returns `%{halt:, injects:, transform:}`. `halt` short-circuits the
remaining callbacks (denies / halts always win); `injects` accumulates
in order; `transform` is last-write-wins.

# `run_post_tool_use`

```elixir
@spec run_post_tool_use(t(), String.t(), map(), String.t() | nil) ::
  :ok | {:halt, term()} | {:augment, String.t()}
```

Fire `PostToolUse` hooks matching `tool_name`.

Returns:
  * `:ok` — all hooks returned `:ok` (or ignored returns).
  * `{:halt, term()}` — a hook requested a hard stop; no further hooks run.
  * `{:augment, String.t()}` — one or more hooks returned `{:augment, text}`;
    multiple augments are joined with `"\n"`. `:halt` takes priority over any
    accumulated augment text.

# `run_pre_tool_use`

```elixir
@spec run_pre_tool_use(t(), String.t(), map(), String.t() | nil) ::
  :ok | {:deny, term()} | {:halt, term()}
```

Fire `PreToolUse` hooks matching `tool_name`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
