Lifecycle Hooks
Execute custom logic at specific points during agent execution with lifecycle hooks.
What Are Lifecycle Hooks?
Lifecycle hooks allow agents to execute custom logic at specific points during their execution. Hooks are defined in an agent's AGENT.md file and can either run a shell script or spawn another agent.
Use hooks for:
- Setup — Load context or validate environment on startup
- Validation — Check tool calls before they execute
- Logging — Audit tool usage and agent activity
- Cleanup — Perform wrap-up tasks before agent returns
- Notifications — Alert on errors or completions
Hook Events
Hooks fire at specific points in the agent lifecycle:
| Event | When It Fires | Blocking | Use Cases |
|---|---|---|---|
onStart |
Agent begins a conversation | Yes | Load context, validate environment |
onStop |
Agent is about to return | Yes | Cleanup, summaries, commit changes |
beforeToolCall |
Before any tool is invoked | Yes | Validation, rate limiting, audit |
afterToolCall |
After a tool completes | No | Logging, notifications |
onError |
Agent encounters an error | No | Error reporting, recovery |
beforeMessage |
Before sending to LLM | Yes | Prompt enrichment |
afterMessage |
After receiving from LLM | No | Response logging, analytics |
Blocking behavior:
- Yes — Hook must complete before the operation proceeds
- No — Default is non-blocking, but can be set to blocking via
blocking: true
Hook Actions
Script Action
Execute a shell script with template variables substituted.
hooks:
- event: onStop
action: script
script: |
echo "Agent completed in ms"
echo "Token usage: in / out"
Exit codes:
| Code | Meaning | Effect |
|---|---|---|
0 |
Success | Continue normally |
1 |
Failure | Respect onFailure behavior |
2 |
Skip | Skip the operation (e.g., skip tool call for beforeToolCall) |
Agent Action
Spawn another agent to handle the hook.
hooks:
- event: onStop
action: agent
agent: wrap-up-agent
prompt: |
The conversation is ending. Please perform any wrap-up tasks.
Summary of session:
- Agent:
- Duration: ms
- Tool calls:
The spawned agent runs with full access to its configured tools.
Configuration
Define hooks in the AGENT.md frontmatter:
---
name: my-agent
description: Agent with lifecycle hooks
model:
provider: anthropic
model: claude-sonnet-4-20250514
hooks:
- event: onStart
action: script
script: |
if [ -f "/CONVENTIONS.md" ]; then
cat "/CONVENTIONS.md"
fi
captureOutput: true
- event: beforeToolCall
action: script
filter:
tools: [Bash]
script: |
if echo '' | grep -qE 'rm\s+-rf'; then
echo "BLOCKED: Dangerous command pattern"
exit 1
fi
onFailure: abort
- event: onStop
action: agent
agent: wrap-up
prompt: "Summarize what was done and commit any changes"
---
Hook Definition Fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
event |
string | Yes | — | When the hook fires |
action |
string | Yes | — | script or agent |
script |
string | If action=script | — | Shell script to execute |
agent |
string | If action=agent | — | Agent name to spawn |
prompt |
string | If action=agent | — | Prompt for spawned agent |
filter |
object | No | — | Conditions for when hook applies |
blocking |
boolean | No | varies | Whether to wait for completion |
timeout |
integer | No | 30000 | Max execution time in ms |
onFailure |
string | No | warn |
warn, abort, or ignore |
captureOutput |
boolean | No | false | Capture stdout as {{hook_output}} |
enabled |
boolean | No | true | Whether hook is active |
name |
string | No | — | Optional name for logging |
Filtering Hooks
Limit when hooks run with filters:
hooks:
- event: beforeToolCall
action: script
filter:
tools: [Bash, Write] # Only for these tools
excludeTools: [Read, Glob] # Never for these tools
workspaces: ["/home/user/projects/*"]
script: |
# Only runs for Bash/Write in matching workspaces
Filter Fields
| Field | Type | Description |
|---|---|---|
tools |
string[] | Only run for these tools |
excludeTools |
string[] | Don't run for these tools |
workspaces |
string[] | Only in matching workspaces (glob patterns) |
Failure Behavior
Control what happens when a hook fails:
| Value | Description |
|---|---|
warn |
Log a warning and continue (default) |
abort |
Abort the current operation |
ignore |
Silently continue without logging |
hooks:
- event: beforeToolCall
action: script
script: |
# Validate tool call
if [ dangerous ]; then exit 1; fi
onFailure: abort # Block the tool call
Context Variables
Hooks receive context through template substitution ({{variable}}).
Common Variables (All Hooks)
| Variable | Type | Description |
|---|---|---|
{{agent_name}} |
string | Agent name |
{{agent_id}} |
string | Agent instance ID |
{{conversation_id}} |
string | Current conversation ID |
{{workspace}} |
string | Working directory |
{{timestamp}} |
string | ISO 8601 timestamp |
{{json_context}} |
string | Full context as JSON |
onStart Variables
| Variable | Description |
|---|---|
{{trigger_type}} |
How started: cli, trigger, api, subagent |
{{trigger_name}} |
Trigger name if applicable |
{{parameters.<name>}} |
Individual parameter values |
{{parent_agent}} |
Parent agent name if subagent |
onStop Variables
| Variable | Description |
|---|---|
{{duration_ms}} |
Total conversation duration |
{{message_count}} |
Number of messages exchanged |
{{tool_call_count}} |
Number of tool invocations |
{{token_usage.input}} |
Input tokens consumed |
{{token_usage.output}} |
Output tokens generated |
{{exit_reason}} |
complete, error, timeout, user_cancel |
{{files_modified}} |
List of modified files |
{{hook_output}} |
Output from previous hooks |
beforeToolCall Variables
| Variable | Description |
|---|---|
{{tool_name}} |
Tool being called |
{{tool_args}} |
Tool arguments object |
{{tool_args.<param>}} |
Individual argument |
{{tool_call_index}} |
Call number (1-indexed) |
afterToolCall Variables
| Variable | Description |
|---|---|
{{tool_name}} |
Tool that was called |
{{tool_result}} |
Tool output |
{{tool_error}} |
Error message if failed |
{{tool_success}} |
Whether tool succeeded |
{{tool_duration_ms}} |
Execution time |
onError Variables
| Variable | Description |
|---|---|
{{error_type}} |
tool_error, api_error, timeout, permission_denied |
{{error_message}} |
Human-readable error |
{{error_stack}} |
Stack trace if available |
{{last_tool}} |
Last tool before error |
{{recoverable}} |
Whether error is recoverable |
Examples
Load Project Context
hooks:
- name: load-context
event: onStart
action: script
script: |
cd ""
echo "=== Project Structure ==="
find . -type f -name "*.go" | head -20
echo ""
echo "=== Recent Commits ==="
git log --oneline -5 2>/dev/null || echo "Not a git repo"
captureOutput: true
timeout: 10000
The captured output is available in the system prompt as {{hook_output}}.
Validate Dangerous Commands
hooks:
- name: bash-guard
event: beforeToolCall
action: script
filter:
tools: [Bash]
script: |
CMD=''
if echo "$CMD" | grep -qE 'rm\s+-rf\s+/|rm\s+-rf\s+~|mkfs'; then
echo "BLOCKED: Destructive command pattern"
exit 1
fi
exit 0
onFailure: abort
Log Tool Usage
hooks:
- name: tool-logger
event: afterToolCall
action: script
script: |
mkdir -p ~/.familiar/logs
echo '{"ts":"","agent":"","tool":"","duration":,"success":}' \
>> ~/.familiar/logs/tool-usage.jsonl
blocking: false
onFailure: ignore
Wrap-Up Agent
hooks:
- name: session-wrap-up
event: onStop
action: agent
agent: wrap-up
prompt: |
The session is ending.
Session stats:
- Duration: ms
- Tool calls:
- Files modified:
Please:
1. Check for uncommitted changes
2. Verify tests pass if code was modified
3. Update relevant documentation
timeout: 120000
Error Notifications
hooks:
- name: error-alert
event: onError
action: script
script: |
curl -X POST "https://hooks.slack.com/services/..." \
-H "Content-Type: application/json" \
-d '{"text": "Agent error: "}'
blocking: false
onFailure: ignore
Execution Logs
Hook executions are logged for debugging:
Location: ~/Library/Application Support/com.usefamiliar.desktop/agents/<agent-name>/hook-logs/
Each log entry includes:
- Hook name and event
- Input context
- Output or error
- Execution duration
- Spawned agent info (for agent actions)
Best Practices
Keep Hooks Fast
Long-running hooks slow down agents. Use reasonable timeouts:
hooks:
- event: beforeToolCall
timeout: 5000 # 5 seconds max
Use Non-Blocking for Logging
Logging shouldn't slow down execution:
hooks:
- event: afterToolCall
blocking: false # Don't wait
onFailure: ignore # Don't fail on log errors
Filter Appropriately
Don't run hooks unnecessarily:
hooks:
- event: beforeToolCall
filter:
tools: [Bash, Write] # Only for risky tools
Capture Context on Start
Load project context once at startup:
hooks:
- event: onStart
captureOutput: true # Available as