Working with Autohand
Hooks Reference
Hooks let you run custom scripts in response to agent events. Use them to enforce coding standards, run tests automatically, notify your team, or integrate with external tools.
Overview
Hooks are shell commands that execute at specific points during agent operation. They enable automation without modifying agent behavior directly.
Common use cases:
- Run linters after file changes
- Execute tests after code modifications
- Send notifications on task completion
- Validate changes against team policies
- Log agent actions for audit trails
- Integrate with CI/CD pipelines
View hooks: Use /hooks during a session to see currently configured hooks.
Configuration
Hooks are configured in your ~/.autohand/config.json or project-level .autohand/config.json.
Basic structure
{
"hooks": {
"on_file_change": [
"npm run lint --fix {{file}}"
],
"after_tool_call": [
"echo 'Tool {{tool}} completed'"
],
"on_session_end": [
"./scripts/notify-slack.sh"
]
}
}
Hook format
Each hook is an array of shell commands. Commands run in order, and execution stops if any command fails (non-zero exit code).
{
"hooks": {
"event_name": [
"command1",
"command2",
"command3"
]
}
}
Hook events
Autohand supports hooks for these lifecycle events:
Session events
| Event | Trigger | Variables |
|---|---|---|
on_session_start | Session begins | {{session_id}}, {{project}} |
on_session_end | Session ends | {{session_id}}, {{duration}} |
on_session_resume | Session resumed | {{session_id}} |
Tool events
| Event | Trigger | Variables |
|---|---|---|
before_tool_call | Before any tool executes | {{tool}}, {{args}} |
after_tool_call | After any tool completes | {{tool}}, {{result}}, {{duration}} |
on_tool_error | Tool execution fails | {{tool}}, {{error}} |
File events
| Event | Trigger | Variables |
|---|---|---|
on_file_change | File is modified | {{file}}, {{action}} |
on_file_create | New file created | {{file}} |
on_file_delete | File deleted | {{file}} |
on_file_read | File is read | {{file}} |
Command events
| Event | Trigger | Variables |
|---|---|---|
before_command | Before shell command runs | {{command}}, {{cwd}} |
after_command | After shell command completes | {{command}}, {{exit_code}}, {{output}} |
Message events
| Event | Trigger | Variables |
|---|---|---|
on_user_message | User sends a message | {{message}} |
on_agent_response | Agent responds | {{response}}, {{tokens}} |
Error events
| Event | Trigger | Variables |
|---|---|---|
on_error | Any error occurs | {{error}}, {{context}} |
on_permission_denied | Action blocked by permissions | {{action}}, {{resource}} |
Auto-mode events
| Event | Trigger | Variables |
|---|---|---|
on_automode_start | Auto-mode loop begins | {{task}}, {{max_iterations}} |
on_automode_stop | Auto-mode loop ends | {{task}}, {{iterations}}, {{reason}} |
on_automode_iteration | Each loop iteration completes | {{iteration}}, {{total}} |
Sub-agent events
| Event | Trigger | Variables |
|---|---|---|
on_subagent_start | Sub-agent task begins | {{agent}}, {{task}} |
on_subagent_stop | Sub-agent task completes | {{agent}}, {{task}}, {{duration}} |
Notification events
| Event | Trigger | Variables |
|---|---|---|
on_permission_request | Agent requests permission for an action | {{tool}}, {{action}} |
on_notification | System notification is generated | {{message}}, {{level}} |
Template variables
Use double curly braces to include event data in your commands:
{
"hooks": {
"on_file_change": [
"eslint {{file}} --fix",
"prettier --write {{file}}"
]
}
}
Available variables
| Variable | Description | Example |
|---|---|---|
{{file}} | File path | src/utils/auth.ts |
{{tool}} | Tool name | write_file |
{{command}} | Shell command | npm test |
{{session_id}} | Current session ID | abc123 |
{{project}} | Project directory | /home/user/myapp |
{{timestamp}} | ISO timestamp | 2026-01-16T10:30:00Z |
{{duration}} | Execution time (ms) | 1234 |
{{exit_code}} | Command exit code | 0 |
{{error}} | Error message | File not found |
Environment variables
Standard environment variables are available in hook commands:
{
"hooks": {
"on_session_end": [
"curl -X POST $SLACK_WEBHOOK -d '{\"text\": \"Session complete\"}'"
]
}
}
Examples
Auto-lint on file changes
Run ESLint and Prettier whenever the agent modifies a file:
{
"hooks": {
"on_file_change": [
"if [[ {{file}} == *.ts || {{file}} == *.tsx ]]; then eslint {{file}} --fix; fi",
"prettier --write {{file}}"
]
}
}
Run tests after changes
Execute related tests when files change:
{
"hooks": {
"on_file_change": [
"npm test -- --findRelatedTests {{file}}"
]
}
}
Slack notifications
Send a message when a session ends:
{
"hooks": {
"on_session_end": [
"curl -X POST $SLACK_WEBHOOK -H 'Content-type: application/json' -d '{\"text\": \"Autohand session completed in {{duration}}ms\"}'"
]
}
}
Audit logging
Log all tool calls for compliance:
{
"hooks": {
"after_tool_call": [
"echo '{{timestamp}} | {{tool}} | {{duration}}ms' >> ~/.autohand/audit.log"
]
}
}
Block dangerous commands
Prevent certain commands from executing:
{
"hooks": {
"before_command": [
"if [[ '{{command}}' == *'rm -rf'* ]]; then echo 'Blocked dangerous command' && exit 1; fi"
]
}
}
Type checking on TypeScript files
Run type checker when TypeScript files change:
{
"hooks": {
"on_file_change": [
"if [[ {{file}} == *.ts || {{file}} == *.tsx ]]; then npx tsc --noEmit; fi"
]
}
}
Git pre-commit style check
Validate code style before committing:
{
"hooks": {
"before_command": [
"if [[ '{{command}}' == 'git commit'* ]]; then npm run lint && npm run typecheck; fi"
]
}
}
Advanced configuration
Conditional hooks
Use shell conditionals to run hooks selectively:
{
"hooks": {
"on_file_change": [
"case {{file}} in *.test.ts) npm test {{file}};; *.ts) eslint {{file}};; esac"
]
}
}
External scripts
For complex logic, use external scripts:
{
"hooks": {
"on_file_change": [
"./scripts/on-change.sh {{file}}"
],
"on_session_end": [
"./scripts/session-complete.sh {{session_id}} {{duration}}"
]
}
}
Timeout configuration
Set timeouts for long-running hooks:
{
"hooks": {
"on_file_change": [
{
"command": "npm test -- --findRelatedTests {{file}}",
"timeout": 30000
}
]
}
}
Async hooks
Run hooks in the background without blocking:
{
"hooks": {
"on_file_change": [
{
"command": "npm run build",
"async": true
}
]
}
}
Debugging hooks
View configured hooks
Use the /hooks command to see all configured hooks:
/hooks
# Output:
# on_file_change:
# - eslint {{file}} --fix
# - prettier --write {{file}}
# on_session_end:
# - ./scripts/notify.sh
Enable debug logging
Set AUTOHAND_DEBUG=true to see hook execution details:
AUTOHAND_DEBUG=true autohand
# Shows:
# [hook] on_file_change triggered for src/auth.ts
# [hook] Running: eslint src/auth.ts --fix
# [hook] Completed in 234ms (exit code: 0)
Test hooks manually
Test your hook commands outside of Autohand first:
# Replace variables manually to test
eslint src/auth.ts --fix
prettier --write src/auth.ts
Common issues
| Issue | Solution |
|---|---|
| Hook not running | Check event name spelling and JSON syntax |
| Command not found | Use full paths or ensure command is in PATH |
| Variables not expanding | Use {{variable}} not $variable |
| Hook blocking agent | Use async: true for slow commands |
| Permission denied | Make scripts executable with chmod +x |
Best practices
Keep hooks fast
Hooks run synchronously by default. Slow hooks create a poor user experience. For long-running tasks, use async: true.
Handle failures gracefully
If a hook fails, subsequent hooks won't run. Design hooks to be resilient:
{
"hooks": {
"on_file_change": [
"eslint {{file}} --fix || true"
]
}
}
Use project-level config
Put team hooks in .autohand/config.json and commit to version control so everyone gets the same behavior.
Document your hooks
Add comments in your AGENTS.md explaining what hooks do and why:
# AGENTS.md
## Hooks
The following hooks are configured:
- **on_file_change**: Auto-formats code with Prettier and ESLint
- **on_session_end**: Posts summary to #dev-updates Slack channel
Frequently asked questions
What are hooks in Autohand CLI?
Hooks are shell scripts that run automatically in response to specific agent events. They let you automate workflows like running linters after file changes, sending Slack notifications on commits, or blocking dangerous commands. Hooks are configured in ~/.autohand/config.json or .autohand/config.json at the project level.
What events can trigger hooks in Autohand?
Autohand supports over 20 hook events grouped into categories: session events (session-start, session-end), tool events (pre-tool, post-tool), file events (file-modified), command events (pre-clear, pre-prompt), auto-mode events (automode:start, automode:checkpoint, automode:complete), and team events (teammate-spawned, task-completed).
How do I create a hook in Autohand?
Add a hook entry to the hooks object in your config.json file. Each hook has an event name and a command to run. Template variables like {{file}}, {{tool}}, and {{session_id}} inject context into your command. Example: { "hooks": { "file-modified": "eslint --fix {{file}}" } } runs ESLint every time the agent modifies a file.