Quick Start
The following example demonstrates how to use a Hook to block dangerous commands — automatically preventing execution when the Agent attempts to runrm -rf.
Step 1: Create the script
~/.qoder/settings.json:
rm -rf. The Hook will block the execution and notify the Agent.
Configuration
Configuration File Locations
Hook configuration is loaded from the following three files. All three levels are merged and executed together:Configuration Format
| Field | Required | Description |
|---|---|---|
type | Yes | Fixed value: "command" |
command | Yes | The shell command to execute |
timeout | No | Timeout in seconds (default: 60) |
matcher | No | Match condition; matches all if omitted |
Matcher Rules
matcher filters the scope of hook triggers. Different events match different fields (see individual event descriptions).
| Syntax | Meaning | Example |
|---|---|---|
Omitted or "*" | Match all | All tools trigger |
| Exact value | Exact match | "Bash" matches only the Bash tool |
| separated | Match multiple values | "Write|Edit" matches Write or Edit |
| Regular expression | Regex match | "mcp__.*" matches all MCP tools |
Writing Hook Scripts
Hook scripts receive JSON input via stdin and control behavior through exit codes and stdout. This section describes the common input/output format for all events. Additional fields specific to each event are described in Supported Events.Input
Hook scripts receive JSON data via stdin. All events include the following common fields:| Field | Description |
|---|---|
session_id | Current session ID |
cwd | Current working directory |
hook_event_name | Name of the triggered event |
jq:
Output
Hooks control behavior through exit codes and stdout. exit 0 indicates success. Qoder CLI parses stdout; some events (such as UserPromptSubmit, SessionStart) support plain-text context injection or fine-grained JSON control — see individual event descriptions for details. exit 2 indicates a blocking error, effective only for events that support blocking. stdout is ignored; stderr is fed back to the Agent as an error message. The exact effect depends on the event: PreToolUse blocks tool execution, UserPromptSubmit rejects the prompt, Stop prevents the Agent from stopping, and so on. Other exit codes are treated as non-blocking errors that do not affect the execution flow; stderr is only recorded in the logs.Environment Variables
The following environment variables are available when hook scripts execute:| Variable | Description |
|---|---|
QODER_PROJECT_DIR | Working directory of the current project |
Supported Events
Qoder CLI supports the following Hook events, covering all stages of the session lifecycle.SessionStart
Triggered when a session starts. matcher field: Session source| matcher value | Trigger scenario |
|---|---|
startup | New session started |
resume | Existing session resumed |
compact | After context compaction completes |
SessionEnd
Triggered when a session ends. matcher field: End reason| matcher value | Trigger scenario |
|---|---|
prompt_input_exit | User exits input (Ctrl+D, etc.) |
other | Other reasons |
UserPromptSubmit
Triggered after the user submits a prompt but before the Agent processes it. You can inject additional context for the Agent or validate and block specific types of prompts. Does not support matcher — all configured hooks are executed. In addition to common fields, UserPromptSubmit also receives aprompt field containing the text submitted by the user:
additionalContext field in a JSON response. Plain text is simpler, but make sure it does not start with {, otherwise it will be parsed as JSON.
To block a prompt, use exit 2 (stderr content is shown to the user), or exit 0 with a JSON output containing decision: "block". exit 2 is suitable for pure blocking scenarios; the JSON approach is more flexible, allowing the same script to conditionally block or pass through.
| Field | Description |
|---|---|
decision | Set to "block" to prevent prompt processing and remove it from context; omit to allow through |
reason | Reason shown to the user when blocked; not added to context |
additionalContext | Context string injected for the Agent (passed via hookSpecificOutput) |
JSON format is not mandatory. For simple cases, exit 0 with plain text output is sufficient to inject context. JSON is only needed when you need to block the prompt or require fine-grained control.For complete examples, see Inject Context on Prompt Submit and Block Prompts Containing Sensitive Information.
PreToolUse
Triggered before tool execution. Can block tool execution. matcher field: Tool name (e.g.Bash, Write, Edit, Read, Glob, Grep; MCP tool names like mcp__server__tool)
Additional input fields:
PostToolUse
Triggered after a tool executes successfully. matcher field: Tool name Additional input fields:PostToolUseFailure
Triggered after a tool execution fails. matcher field: Tool name Additional input fields:Stop
Triggered when the Agent finishes responding (main Agent, with no pending tool calls). Can prevent the Agent from stopping and let it continue working. Preventing the Agent from stopping: exit code 2; stderr content is injected into the conversation as a message, and the Agent continues working.SubagentStart / SubagentStop
Triggered when a sub-agent starts and completes. SubagentStop is similar to Stop and can prevent the sub-agent from stopping. matcher field: Agent type name Additional input fields:PreCompact
Triggered before context compaction. matcher field: Trigger method| matcher value | Trigger scenario |
|---|---|
manual | User manually runs /compact |
auto | Automatically triggered when context window is full |
Notification
Triggered on notification events (permission requests, task completion, etc.). matcher field: Notification type| matcher value | Trigger scenario |
|---|---|
permission | Permission request notification |
result | Agent result notification |
PermissionRequest
Triggered when a tool requires user authorization to execute. matcher field: Tool name Additional input fields:Practical Examples
Desktop Notifications
Pop up a desktop notification when the Agent completes a task or requires authorization. Script~/.qoder/hooks/notify.sh (macOS):
Auto-Lint After Writing Files
Automatically run lint checks every time the Agent writes or edits a file. Script${project}/.qoder/hooks/auto-lint.sh:
PostToolUse, matcher Write|Edit, command .qoder/hooks/auto-lint.sh.
Keep the Agent Working
When the Agent stops, check whether there are unfinished tasks; if so, inject a message to keep the Agent working. Script~/.qoder/hooks/check-continue.sh:
Stop, command ~/.qoder/hooks/check-continue.sh.
Inject Context on Prompt Submit
Automatically inject the current git branch information as Agent context before each user query. Script~/.qoder/hooks/inject-branch.sh:
UserPromptSubmit, command ~/.qoder/hooks/inject-branch.sh.
Block Prompts Containing Sensitive Information
Intercept prompts containing sensitive keywords such as passwords and keys, preventing them from being sent to the Agent. Script~/.qoder/hooks/block-sensitive.sh:
UserPromptSubmit, command ~/.qoder/hooks/block-sensitive.sh.