What Can Hooks Do?
Enforce policies
Block dangerous commands, require confirmation for specific actions, or restrict file access.
Add context
Inject additional instructions or information when specific tools are called.
Run side effects
Execute scripts, send notifications, or log events when things happen.
Modify permissions
Dynamically grant or restrict permissions based on the situation.
Quick Example
Create.devin/hooks.v1.json in your project:
./scripts/check-command.sh before every shell command execution. The script receives event data on stdin and can block the action by returning a non-zero exit code.
Hook Events
Hooks can respond to these lifecycle events:| Event | When it fires |
|---|---|
PreToolUse | Before a tool executes |
PostToolUse | After a tool finishes |
PermissionRequest | When a permission decision is needed |
UserPromptSubmit | When the user submits a message |
Stop | When the agent wants to stop |
SessionStart | When a session begins |
SessionEnd | When a session ends |
Hook Format
Each hook has a type (command or prompt), an optional matcher (regex on tool name), and configuration:
| Field | Description |
|---|---|
matcher | Regex matched against the tool name. Empty string matches all tools. |
type | "command" to run a shell command, or "prompt" to evaluate an LLM prompt. |
command | Shell command to run (for command type). |
prompt | LLM prompt to evaluate (for prompt type). |
timeout | Timeout in seconds (optional). |
Command Hooks
Command hooks run a shell command. Event data is passed as JSON on stdin, and the command can return JSON on stdout to control the outcome. Input (stdin):| Output field | Description |
|---|---|
decision | "approve", "block", or "deny" |
reason | Explanation shown to the agent |
DEVIN_PROJECT_DIR environment variable is automatically set to the project root directory.
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success — hook continues normally |
| 2 | Block — action is denied |
| Other | Error — logged but doesn’t block |
Where Hooks Live
Devin for Terminal reads hooks from the following locations. All use the same JSON format.Project-Level
| Location | Description |
|---|---|
.devin/hooks.v1.json | Standalone hooks file (recommended) |
.devin/config.json | "hooks" key in the config file |
.devin/config.local.json | "hooks" key (local override, gitignored) |
.claude/settings.json | "hooks" key (Claude Code format) |
.claude/settings.local.json | "hooks" key (Claude Code format) |
User-Level (Global)
| Location | Description |
|---|---|
~/.config/devin/config.json | "hooks" key in user config |
~/.claude.json | "hooks" key (Claude Code format) |
~/.claude/settings.json | "hooks" key (Claude Code format) |
~/.claude/settings.local.json | "hooks" key (Claude Code format) |
In
.devin/hooks.v1.json, the hooks object is the entire file (no wrapper key needed). In all other locations, hooks are nested under the "hooks" key in a settings file.Hooks from
.claude/ paths are loaded when read_config_from.claude is enabled (the default). You can disable this in your user config if needed.Verifying Hooks
Use the/hooks slash command to see all currently loaded hooks and their source files:
Next Steps
Lifecycle Hooks
Deep dive into each event type and what data is available.
Claude Code Hooks Docs
Full reference for the hook format.

