Hooks
Run shell commands on agent lifecycle events: before/after tool calls, session start/stop, compaction. Wire-compatible with Claude Code settings.json hooks.
Hooks fire shell commands at lifecycle events - before/after tool calls, on session start/stop, around compaction. Wire-compatible with Claude Code, so your existing .claude/settings.json hooks work as-is.
Quick example
Auto-format every TypeScript file the agent edits:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "prettier --write $(echo $HOOK_TOOL_INPUT | jq -r '.path // empty')",
"async": true
}
]
}
]
}
}/hooks inside Empryo lists active hooks and toggles them per session.
Events
| Event | Fires | Matches |
|---|---|---|
PreToolUse | Before a tool runs | Tool name |
PostToolUse | After a tool succeeds | Tool name |
PostToolUseFailure | After a tool fails | Tool name |
UserPromptSubmit | On user message | - |
Stop / StopFailure | Turn ends | - |
SessionStart / SessionEnd | Session boundary | - |
PreCompact / PostCompact | Around compaction | - |
SubagentStart / SubagentStop | Spark/ember spawn/exit | - |
Notification | System notifications | - |
Rule schema
{
"matcher": "Bash|Edit", // tool name, pipe-separated or regex
"hooks": [
{
"type": "command",
"command": "my-hook.sh",
"async": false, // run in background
"timeout": 10, // seconds (default 10)
"once": false, // fire once per session
"if": "Bash(git *)" // extra glob filter
}
]
}The if field narrows by first string arg: Bash(rm *), Edit(*.ts), etc. * matches any sequence, ? one char.
Tool names use Claude Code conventions (Bash, Edit, Write, Read, Grep, Glob, WebSearch, Agent). Empryo maps its internal names automatically.
Config sources (merged in order)
~/.claude/settings.json.claude/settings.json.claude/settings.local.json~/.empryo/config.json.empryo/config.json
Set "disableAllHooks": true in any file to kill all hooks.
Recipes
Protocol
Hooks read JSON from stdin, optionally write JSON to stdout. Exit code 0 = success, 2 = block, anything else = log warning.
stdin
{
"session_id": "abc",
"cwd": "/proj",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "git status" }
}stdout (optional - PreToolUse can deny, modify input, inject context)
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Blocked by policy",
"updatedInput": { "command": "git status --short" },
"additionalContext": "This repo uses trunk-based dev"
}
}PostToolUse can inject additionalContext - no deny.
Safety
- Hooks die on
Ctrl+Xalong with the agent. - Default timeout 10s - hooks should be fast.
- Non-blocking errors (non-zero exit ≠ 2) log a warning, don't stop the agent.