To execute custom setup, cleanup, or validation steps during AI developer cycles, withhandoff supports repository-defined hooks. You can use hooks to automate formatting, compile assets, prepare test environments, or run test suites before the agent finishes its turn.
Hook Lifecycle
The harness executes hooks at key lifecycle stages of a turn. This allows you to hook into the agent's workspace before, during, and after file modifications.
Execution Sequence
Hooks are executed in the following chronological sequence during an agent turn:
pre-turn: Runs before the agent starts processing a conversation turn. Use this to sync local dependencies, configure environment variables, or spin up mock servers.post-write: Runs immediately after the agent writes changes to a file. Use this to run auto-formatters, generate code/types, or compile build targets.validate: Runs before the agent completes its turn. Use this to execute linter rules, test suites, or other regression checks.
How a Hook Resolves
When a hook is triggered (e.g., validate), the runner checks if .handoff/hooks.json exists at the root of the project. If present, it parses the JSON and looks up the event name. The parser normalizes event keys to support kebab-case (e.g., pre-turn), CamelCase (e.g., PreTurn), or lowercase (e.g., preturn). If the event configuration is found, it executes the mapped hook handlers.
Configuration
You can define hooks globally in your project using a configuration file.
JSON Configuration
Define your hooks in .handoff/hooks.json at your repository root. The configuration consists of an object mapping hook event names to arrays of hook handlers or hook groups:
{
"hooks": {
"pre-turn": [
{
"type": "command",
"command": "npm run db:setup"
}
],
"validate": [
{
"type": "command",
"command": "npm run lint && npm test"
}
]
}
}
Hook Handler Schema
Each hook handler inside the event arrays is defined by the following fields:
| Field | Required | Description |
|---|---|---|
type | Yes | The type of hook handler. Currently supports "command". |
command | Yes | The shell command to execute in the sandbox environment. |
Input & Output Contract
Hooks are executed directly inside the sandbox container containing the project files, providing them with full access to the project workspace.
Environment & Context
- Execution Directory: Commands are run in the context of the repository workspace root.
- Errors and Logs: Standard output (stdout) and standard error (stderr) of the executed hooks are captured. If a hook fails, its output is shown directly to the agent.
Exit Codes
Hook commands communicate their status back to the runner via shell exit codes:
0(Success): Indicates the hook completed successfully. The agent is permitted to proceed with the turn.- Non-zero (Failure): Indicates validation or hook execution failed. The harness immediately halts progress, fails the turn, and feeds the hook's output back to the agent as a validation error so it can fix the issue.
Supported Hook Events
The following event catalog details each supported hook event:
pre-turn
- Description: Triggers before the agent begins its turn.
- Purpose: Prepare the workspace environment or sync mock databases.
- Example JSON Configuration:
"pre-turn": [
{
"type": "command",
"command": "npm run db:migrate"
}
]
post-write
- Description: Triggers after the agent writes changes to a file.
- Purpose: Format the modified file or trigger hot-compilation.
- Example JSON Configuration:
"post-write": [
{
"type": "command",
"command": "npx prettier --write ."
}
]
validate
- Description: Triggers before the agent completes its turn.
- Purpose: Verify that all tests pass and that there are no compiler or linting errors.
- Example JSON Configuration:
"validate": [
{
"type": "command",
"command": "npm run lint && npm test"
}
]