EONYX Hooks: Automation Around Tool Calls
Contents
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.
1 ⚙️ How hooks work
1.1 Format
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.
1.2 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 or missing hook does nothing.
- An error in an
afterhook does not break the result. It is kept with a(hook after <tool> failed: …)note. afterdoes 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
Forbid
Or forbid
That makes
before a guardrail.Forbid
git_commit when tests have not run.Or forbid
bash in a dangerous directory.1.3 Context through variables
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.
| 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) |
2 🍳 Recipes
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
They run with your privileges.
Write them as carefully as any other script.
Keep them fast, under the 30s limit, and idempotent.
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.
3 📋 Summary
Cheat sheet
- Format
- the key is the tool name, the value is
before/after - no events and no matchers
- the key is the tool name, the value is
- Phases
beforeblocks the tool with a non-zero exit codeafterappends its output to the result
- Context
- passed through
EONYX_*variables - no substitution into the text — injection is impossible
- passed through
- Limits
- 30s timeout, shared shell session
aftererrors do not break the result
- Recipes
- auto-format, audit log, blocking commits without tests