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

EventTriggerVariables
on_session_startSession begins{{session_id}}, {{project}}
on_session_endSession ends{{session_id}}, {{duration}}
on_session_resumeSession resumed{{session_id}}

Tool events

EventTriggerVariables
before_tool_callBefore any tool executes{{tool}}, {{args}}
after_tool_callAfter any tool completes{{tool}}, {{result}}, {{duration}}
on_tool_errorTool execution fails{{tool}}, {{error}}

File events

EventTriggerVariables
on_file_changeFile is modified{{file}}, {{action}}
on_file_createNew file created{{file}}
on_file_deleteFile deleted{{file}}
on_file_readFile is read{{file}}

Command events

EventTriggerVariables
before_commandBefore shell command runs{{command}}, {{cwd}}
after_commandAfter shell command completes{{command}}, {{exit_code}}, {{output}}

Message events

EventTriggerVariables
on_user_messageUser sends a message{{message}}
on_agent_responseAgent responds{{response}}, {{tokens}}

Error events

EventTriggerVariables
on_errorAny error occurs{{error}}, {{context}}
on_permission_deniedAction blocked by permissions{{action}}, {{resource}}

Auto-mode events

EventTriggerVariables
on_automode_startAuto-mode loop begins{{task}}, {{max_iterations}}
on_automode_stopAuto-mode loop ends{{task}}, {{iterations}}, {{reason}}
on_automode_iterationEach loop iteration completes{{iteration}}, {{total}}

Sub-agent events

EventTriggerVariables
on_subagent_startSub-agent task begins{{agent}}, {{task}}
on_subagent_stopSub-agent task completes{{agent}}, {{task}}, {{duration}}

Notification events

EventTriggerVariables
on_permission_requestAgent requests permission for an action{{tool}}, {{action}}
on_notificationSystem 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

VariableDescriptionExample
{{file}}File pathsrc/utils/auth.ts
{{tool}}Tool namewrite_file
{{command}}Shell commandnpm test
{{session_id}}Current session IDabc123
{{project}}Project directory/home/user/myapp
{{timestamp}}ISO timestamp2026-01-16T10:30:00Z
{{duration}}Execution time (ms)1234
{{exit_code}}Command exit code0
{{error}}Error messageFile 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

IssueSolution
Hook not runningCheck event name spelling and JSON syntax
Command not foundUse full paths or ensure command is in PATH
Variables not expandingUse {{variable}} not $variable
Hook blocking agentUse async: true for slow commands
Permission deniedMake 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.