Contents

EONYX Hooks: Automation Around Tool Calls

Hooks are shell commands that run around a tool call.
One before it, one after it.
They bolt on formatting, auditing and guardrails without touching the agent’s code.

Hooks live in eonyx.json.
The key is the tool name.
The value is before, after, or both.

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

No events.
No matchers.
Everything hangs off the tool name: write, edit, bash, git_commit, MCP tools and the rest.

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 or missing hook does nothing.
  • 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
A non-zero exit blocks the tool.
That makes before a guardrail.
Forbid git_commit when tests have not run.
Or forbid bash in a dangerous directory.

Data reaches the hook through environment variables.
Nothing is substituted into the command text.
So the input or the result cannot break your command, and 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)

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

No commits until the tests are green:

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

The non-zero exit blocks the commit.
The message goes to the model.

Hooks are your code
Hooks run in the shared shell session, the same one the bash tool uses.
They run with your privileges.
Write them as carefully as any other script.
Keep them fast, under the 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