Hooks
Hooks are user-defined shell commands that fire in response to agent lifecycle and tool events. Use them to enforce policies, send notifications, log activity, or inject additional instructions.
Settings files and scope
Hooks can be configured in any of these files:
~/.afk/hooks.json— daemon-owned user file on the selected machine; edit it from Preferences → Hooks. Project and local files stay with the checkout and are edited from/hooks.<projectDir>/.afk/hooks.json— project-level AFK hooks<projectDir>/.afk/hooks.local.json— project-local AFK hooks (gitignored)~/.afk/settings.json,<projectDir>/.afk/settings.json, and<projectDir>/.afk/settings.local.json— legacy compatibility read path only. The AFK editor writes to hooks files so it does not overwrite Claude Code-specific settings.<pluginRoot>/hooks.json— plugin-provided hooks
Scope precedence (later overrides earlier for the same event):
- User settings/hooks
- User-scope plugin hooks
- Project settings/hooks
- Project-scope plugin hooks
- Project local settings/hooks
The selected daemon must be connected to load hooks. If loading fails, reconnect it and retry; an unavailable configuration is not treated as an empty file. Preferences → Hooks reads only the selected machine’s user configuration.
Hook file format
AFK uses a definition-first flat event-key format. Put reusable hook definitions under hooks and declare on keys such as SessionStart:spawn, BeforeToolUse:bash, and BeforeCompact:auto.
{
"hooks": [
{
"name": "load-context-on-start",
"on": ["SessionStart:spawn", "SessionStart:resume"],
"prompt": "Load relevant context: $ARGUMENTS",
"json_output": true
},
{
"name": "guard-bash",
"on": ["BeforeToolUse:bash"],
"command": "python3 ./hooks/check-policy.py",
"blocking": true
}
]
}Legacy top-level event buckets and Claude Code-style hook maps are still accepted as compatibility inputs, but AFK normalizes them into the same internal definition list.
Hook object fields
| Field | Default | Description |
|---|---|---|
on | — | Flat event keys that trigger this hook definition. |
command | — | Shell command run via bash -lc in the project directory. |
prompt | — | Model-evaluated prompt hook. Use $ARGUMENTS to insert hook input JSON. |
blocking | true | Whether AFK waits for the command and allows it to block the event. |
json_output | false | Parse stdout as JSON and merge it into AFK's hook result. |
timeout_seconds | 30 | Optional timeout; hook is killed if it exceeds this. |
Exit codes
| Exit code | Behavior |
|---|---|
0 | Proceed normally. |
2 | Block the event. Stderr is used as the user-visible reason (falls back to stdout). |
| Any other | AFK logs the failure and continues. |
Hook input and output
AFK sends structured event data to hook commands and can read structured JSON responses when json_output: true is enabled. Use this to block unsafe actions, add context, answer repeated prompts, or route notifications through team systems.
Hook commands run with the current project directory available in the environment. Keep hook scripts small, deterministic, and safe to run on developer machines or shared daemons.
For blocking hooks, exit code 2 means “stop this action”; stderr is shown as the user-visible reason. Other non-zero exits are treated as hook failures and AFK continues.
Supported event groups
Hooks can run around tool use, approvals, session lifecycle, messages, compaction, notifications, MCP prompts, worktrees, and sub-agent events. The hook editor in AFK shows the available event keys for your current version.
| Event group | Common uses |
|---|---|
| Tool execution | Block risky commands, log edits, or require local policy checks. |
| Session lifecycle | Load context on start, send completion notifications, or clean up resources. |
| Messages and compaction | Add recurring instructions or preserve important context during summaries. |
| MCP prompts | Apply team defaults to repeated structured prompts. |
| Worktrees and sub-agents | Notify teams when parallel work starts or finishes. |
Practical examples
Block bash entirely
{
"BeforeToolUse": {
"command": "python3 ./hooks/check-policy.py",
"blocking": true
}
}Add a reminder to every compaction
{
"BeforeCompact": {
"command": "echo '{\"additional_instructions\":\"Preserve pending release tasks and rollback notes.\"}'",
"json_output": true
}
}Send notifications asynchronously
{
"Notify": {
"command": "./scripts/send-notification.sh",
"blocking": false
}
}Troubleshooting
My hook never runs
- Check the event name matches exactly, including capitalization.
- Verify the settings file is valid JSON.
- Confirm the hook command is runnable under
bash -lc. - After changing hooks via the UI, the running session picks up changes — no restart needed.
My hook blocks unexpectedly
Exit code 2 is treated as an intentional block. Any stderr content becomes the user-visible reason shown in the browser.
My JSON output is ignored
- Ensure
json_output: trueis set on the hook object. - Verify stdout contains valid JSON only (no extra logging to stdout).
- Use only the JSON keys AFK consumes for that specific event.