# EONYX Hooks: Automation Around Tool Calls


**Hooks** are shell commands that EONYX runs **around tool calls**: before (`before`) and after (`after`). They are a convenient way to bolt on auto-formatting, auditing, checks and guardrails without touching the agent's code.

<!--more-->

## How hooks work

A hook is declared in the config and fires around a tool call — below are the record format, the `before`/`after` phases and how to pass context into a hook.

### Format

Hooks are set in `eonyx.json` as an object where the key is the **tool name** and the value is `before` and/or `after`:

```json
"hooks": {
  "write": {
    "before": "echo \"writing: $EONYX_INPUT\" >> .eonyx/audit.log",
    "after":  "gofmt -l ."
  },
  "edit": {
    "after": "gofmt -l ."
  }
}
```

No "events" and no "matchers" — everything is tied to the tool name (`write`, `edit`, `bash`, `git_commit`, MCP tools and so on).

### The before and after phases

| Phase    | When                 | Effect                                                              |
| -------- | -------------------- | ------------------------------------------------------------------- |
| `before` | before the tool      | a non-zero exit code **blocks** the tool; the error goes to the model |
| `after`  | after a successful call | the output (stdout and stderr) is **appended** to the tool result (the model sees it) |

- An empty/missing hook is a no-op.
- An error in an `after` hook does not break the result: it is kept with a `(hook after <tool> failed: …)` note.
- `after` does not run if the tool itself failed.
- Every hook runs in the shared shell session with a **30 second** timeout.

{{< admonition type="tip" title="before can forbid things" >}}
Since a non-zero exit blocks the tool, a `before` hook is a guardrail. For example, forbid `git_commit` when tests have not been run, or `bash` in a dangerous directory.
{{< /admonition >}}

### Context through variables

Data is passed to the hook **through environment variables**, not by substitution into the command text — so the contents of the input/result cannot "break" your command (injection is impossible):

| Variable        | What it holds                               |
| --------------- | ------------------------------------------- |
| `EONYX_TOOL`    | tool name                                   |
| `EONYX_PHASE`   | `before` or `after`                         |
| `EONYX_CWD`     | the shell's current working directory       |
| `EONYX_INPUT`   | the tool's raw JSON input                   |
| `EONYX_RESULT`  | the tool result (only in `after`)           |

## Recipes

A few ready-made scenarios — auto-formatting, an audit log and blocking commits without green tests.

{{< tabs defaultTab="0" >}}
{{< tab title="Auto-format" markdown="true" >}}

Format the code after every edit:

```json
"hooks": {
  "write": { "after": "gofmt -w ." },
  "edit":  { "after": "gofmt -w ." },
  "multi_edit": { "after": "gofmt -w ." }
}
```

{{< /tab >}}
{{< tab title="Audit log" markdown="true" >}}

Log every shell command:

```json
"hooks": {
  "bash": { "before": "echo \"[$(date -Is)] $EONYX_INPUT\" >> .eonyx/bash-audit.log" }
}
```

{{< /tab >}}
{{< tab title="Guardrail" markdown="true" >}}

Do not allow commits until the tests are green:

```json
"hooks": {
  "git_commit": { "before": "go test ./... >/dev/null 2>&1 || { echo 'tests failing'; exit 1; }" }
}
```

A non-zero exit will block the commit, and the message will go to the model.

{{< /tab >}}
{{< /tabs >}}

{{< admonition type="warning" title="Hooks are your code" >}}
Hooks run in the shared shell session (the same one as the `bash` tool), with your privileges. Write them as carefully as any other script: keep them fast (30s limit) and idempotent.
{{< /admonition >}}

## Summary

{{< admonition type="abstract" title="Cheat sheet" >}}
- **Format**
  - the key is the tool name, the value is `before`/`after`
  - no events and no matchers
- **Phases**
  - `before` blocks the tool with a non-zero exit code
  - `after` appends its output to the result
- **Context**
  - passed through `EONYX_*` variables
  - no substitution into the text — injection is impossible
- **Limits**
  - 30s timeout, shared shell session
  - `after` errors do not break the result
- **Recipes**
  - auto-format, audit log, blocking commits without tests
{{< /admonition >}}

