Contents

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.

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.

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

"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).

PhaseWhenEffect
beforebefore the toola non-zero exit code blocks the tool; the error goes to the model
afterafter a successful callthe 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.
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.

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):

VariableWhat it holds
EONYX_TOOLtool name
EONYX_PHASEbefore or after
EONYX_CWDthe shell’s current working directory
EONYX_INPUTthe tool’s raw JSON input
EONYX_RESULTthe tool result (only in after)

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

Format the code after every edit:

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

Log every shell command:

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

Do not allow commits until the tests are green:

"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.

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.
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