Each hook event fires at a specific point in the agent’s lifecycle. Use the matcher field (a regex) to filter which tool invocations trigger your hook.
Fires before a tool executes. Use this to block, modify, or add context to tool calls.
Stdin data:
| Field | Description | Example |
|---|
tool_name | Name of the tool being called | exec, edit, mcp__github__create_issue |
tool_input | Arguments passed to the tool | { "command": "rm -rf /", "shell_id": "main" } |
Example — Block destructive commands:
{
"PreToolUse": [
{
"matcher": "exec",
"hooks": [
{
"type": "command",
"command": "python3 -c \"import sys, json; data = json.load(sys.stdin); cmd = data.get('tool_input', {}).get('command', ''); sys.exit(2 if 'rm -rf' in cmd else 0)\""
}
]
}
]
}
Example — Require confirmation for writes outside src/:
Use a script that inspects the tool input and returns a decision:
{
"PreToolUse": [
{
"matcher": "edit",
"hooks": [
{
"type": "command",
"command": "./scripts/check-edit-path.sh",
"timeout": 5
}
]
}
]
}
PostToolUse
Fires after a tool finishes executing. Use this for logging, validation, or triggering follow-up actions.
Stdin data:
| Field | Description |
|---|
tool_name | Name of the tool that ran |
tool_input | Arguments that were passed |
tool_response | Object with success (boolean), output (string), and error (string or null) |
Example — Log all shell commands:
{
"PostToolUse": [
{
"matcher": "exec",
"hooks": [
{
"type": "command",
"command": "sh -c 'cat >> ~/.devin-command-log'"
}
]
}
]
}
PermissionRequest
Fires when the agent needs a permission decision. Use this to implement custom approval logic.
Stdin data:
| Field | Description |
|---|
tool_name | Tool requesting permission |
tool_input | Arguments for the tool call |
Example — Auto-approve git commands:
{
"PermissionRequest": [
{
"matcher": "exec",
"hooks": [
{
"type": "command",
"command": "python3 -c \"import sys, json; data = json.load(sys.stdin); cmd = data.get('tool_input', {}).get('command', ''); print(json.dumps({'decision': 'approve'})) if cmd.startswith('git ') else sys.exit(0)\""
}
]
}
]
}
UserPromptSubmit
Fires when the user submits a message. Use this to add context or trigger workflows.
Stdin data:
| Field | Description |
|---|
prompt | The user’s message text |
Example — Add project context for deploy-related prompts:
{
"UserPromptSubmit": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "./scripts/add-deploy-context.sh"
}
]
}
]
}
Stop
Fires when the agent decides to stop (finish its turn). Use this to add follow-up instructions or prevent premature stopping.
Stdin data:
| Field | Description |
|---|
stop_hook_active | Whether a stop hook is already active |
Example — Remind agent to run tests:
{
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "echo '{\"decision\": \"block\", \"reason\": \"Please run the test suite before stopping.\"}'"
}
]
}
]
}
Be careful with stop hooks that block — they can cause the agent to loop if the condition isn’t eventually satisfied.
SessionStart
Fires when a new session begins. Use this for initialization, logging, or environment setup.
Stdin data:
| Field | Description |
|---|
source | How the session was started |
Example — Run setup script:
{
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "./scripts/dev-setup.sh",
"timeout": 10
}
]
}
]
}
SessionEnd
Fires when a session ends. Use this for cleanup or final logging.
Stdin data:
| Field | Description |
|---|
reason | Why the session ended |
Matching Multiple Events
A single hooks file can define hooks for multiple events:
{
"PreToolUse": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "./scripts/audit.sh" }
]
}
],
"PostToolUse": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "./scripts/audit.sh" }
]
}
]
}
Using the Matcher
The matcher field is a regex matched against the tool name:
| Matcher | Matches |
|---|
"" (empty) | All tools |
"exec" | The exec tool |
"exec|edit" | exec or edit |
"mcp__.*" | All MCP tools |