Overview
Hooks are configured in the hooks section of your creor.json. Each hook event maps to an array of hook entries. When the event fires, Creor runs each matching hook entry as a shell command.
Hooks run in the project's working directory and inherit the system environment. They receive context via both environment variables and a JSON payload on stdin.
Note
Hook Events
The following lifecycle events are available:
| Event | When It Fires | Can Block? |
|---|---|---|
| tool.execute.before | Before a tool runs. Receives tool name and input args. | Yes |
| tool.execute.after | After a tool completes. Receives tool name and output. | No |
| tool.execute.failure | When a tool fails. Receives tool name and error. | No |
| shell.env | Before any shell command. Hook stdout sets environment variables (KEY=VALUE format). | No |
| command.execute.before | Before a slash command executes. Receives command name and arguments. | Yes |
| chat.message | When a new chat message is sent. Receives session ID and agent name. | Yes |
| session.start | When a new session begins. | No |
| session.end | When a session ends. Always runs asynchronously. | No |
| notification | When the agent sends a notification. Receives the message text. | No |
| pre.compact | Before context compaction runs. Receives trigger type and message count. | No |
Hook Entry Config
Each hook entry supports the following fields:
| Field | Type | Default | Description |
|---|---|---|---|
| command | string | (required) | Shell command to execute |
| description | string | — | Human-readable description shown in the UI |
| enabled | boolean | true | Set to false to temporarily disable without removing |
| timeout | number | 30000 | Timeout in milliseconds. Hook is killed if it exceeds this. |
| matcher | string | — (all tools) | Tool name filter for tool.* events. Exact name, pipe-separated list ("Edit|Write"), or regex. |
| statusMessage | string | — | Message shown in the UI while the hook runs |
| async | boolean | false | Fire-and-forget: don't wait for the hook to finish before continuing |
Tip
async: true for hooks that should not block the agent loop — like sending Slack notifications or logging to an external service.Environment Variables
Creor sets the following environment variables before running a hook command. The exact set depends on the event type:
| Variable | Events | Description |
|---|---|---|
| HOOK_EVENT | All | The event name (e.g., "tool.execute.before") |
| HOOK_SESSION_ID | Most | Current session ID |
| HOOK_TOOL_NAME | tool.* | Name of the tool being executed |
| HOOK_CALL_ID | tool.execute.* | Unique ID for this tool call |
| HOOK_TOOL_OUTPUT | tool.execute.after | Tool output (truncated to 8KB) |
| HOOK_TOOL_ERROR | tool.execute.failure | Error message (truncated to 500 chars) |
| HOOK_PROJECT_DIR | All | Absolute path to the project directory |
| HOOK_CWD | shell.env | Current working directory for the shell command |
| HOOK_COMMAND | command.execute.before | Slash command name |
| HOOK_ARGUMENTS | command.execute.before | Command arguments |
| HOOK_AGENT | chat.message | Agent name handling the message |
| HOOK_MESSAGE | notification | Notification message text (truncated to 500 chars) |
| HOOK_SOURCE | session.start | Session start source (e.g., "startup") |
| HOOK_TRIGGER | pre.compact | Compaction trigger ("auto" or "manual") |
JSON Payload
In addition to environment variables, Creor writes a JSON payload to the hook's stdin. This provides structured access to the same data plus any additional fields.
You can read this payload in your hook script:
Blocking Hooks
Hooks on tool.execute.before, command.execute.before, and chat.message can block execution. There are two ways to block:
Exit Code 2
If the hook exits with code 2, Creor blocks the operation. The stderr output is used as the reason:
JSON Decision Output
Alternatively, print a JSON object to stdout with "decision": "block":
Modifying Tool Input
For tool.execute.before, the JSON output can also include an updatedInput field to modify the tool's arguments before execution: