| # Hooks reference |
|
|
| This document provides the technical specification for Gemini CLI hooks, |
| including JSON schemas and API details. |
|
|
| ## Global hook mechanics |
|
|
| - **Communication**: `stdin` for Input (JSON), `stdout` for Output (JSON), and |
| `stderr` for logs and feedback. |
| - **Exit codes**: |
| - `0`: Success. `stdout` is parsed as JSON. **Preferred for all logic.** |
| - `2`: System Block. The action is blocked; `stderr` is used as the rejection |
| reason. |
| - `Other`: Warning. A non-fatal failure occurred; the CLI continues with a |
| warning. |
| - **Silence is Mandatory**: Your script **must not** print any plain text to |
| `stdout` other than the final JSON. |
| |
| --- |
|
|
| ## Configuration schema |
|
|
| Hooks are defined in `settings.json` within the `hooks` object. Each event (for |
| example, `BeforeTool`) contains an array of **hook definitions**. |
|
|
| ### Hook definition |
|
|
| | Field | Type | Required | Description | |
| | :----------- | :-------- | :------- | :-------------------------------------------------------------------------------------- | |
| | `matcher` | `string` | No | A regex (for tools) or exact string (for lifecycle) to filter when the hook runs. | |
| | `sequential` | `boolean` | No | If `true`, hooks in this group run one after another. If `false`, they run in parallel. | |
| | `hooks` | `array` | **Yes** | An array of **hook configurations**. | |
|
|
| ### Hook configuration |
|
|
| | Field | Type | Required | Description | |
| | :------------ | :------- | :-------- | :------------------------------------------------------------------- | |
| | `type` | `string` | **Yes** | The execution engine. Currently only `"command"` is supported. | |
| | `command` | `string` | **Yes\*** | The shell command to execute. (Required when `type` is `"command"`). | |
| | `name` | `string` | No | A friendly name for identifying the hook in logs and CLI commands. | |
| | `timeout` | `number` | No | Execution timeout in milliseconds (default: 60000). | |
| | `description` | `string` | No | A brief explanation of the hook's purpose. | |
|
|
| --- |
|
|
| ## Base input schema |
|
|
| All hooks receive these common fields via `stdin`: |
|
|
| ```typescript |
| { |
| "session_id": string, // Unique ID for the current session |
| "transcript_path": string, // Absolute path to session transcript JSON |
| "cwd": string, // Current working directory |
| "hook_event_name": string, // The firing event (for example "BeforeTool") |
| "timestamp": string // ISO 8601 execution time |
| } |
| ``` |
|
|
| --- |
|
|
| ## Common output fields |
|
|
| Most hooks support these fields in their `stdout` JSON: |
|
|
| | Field | Type | Description | |
| | :--------------- | :-------- | :----------------------------------------------------------------------------- | |
| | `systemMessage` | `string` | Displayed immediately to the user in the terminal. | |
| | `suppressOutput` | `boolean` | If `true`, hides internal hook metadata from logs/telemetry. | |
| | `continue` | `boolean` | If `false`, stops the entire agent loop immediately. | |
| | `stopReason` | `string` | Displayed to the user when `continue` is `false`. | |
| | `decision` | `string` | `"allow"` or `"deny"` (alias `"block"`). Specific impact depends on the event. | |
| | `reason` | `string` | The feedback/error message provided when a `decision` is `"deny"`. | |
|
|
| --- |
|
|
| ## Tool hooks |
|
|
| ### Matchers and tool names |
|
|
| For `BeforeTool` and `AfterTool` events, the `matcher` field in your settings is |
| compared against the name of the tool being executed. |
|
|
| - **Built-in Tools**: You can match any built-in tool (for example, `read_file`, |
| `run_shell_command`). See the [Tools Reference](../reference/tools) for a full |
| list of available tool names. |
| - **MCP Tools**: Tools from MCP servers follow the naming pattern |
| `mcp_<server_name>_<tool_name>`. |
| - **Regex Support**: Matchers support regular expressions (for example, |
| `matcher: "read_.*"` matches all file reading tools). |
|
|
| ### `BeforeTool` |
|
|
| Fires before a tool is invoked. Used for argument validation, security checks, |
| and parameter rewriting. |
|
|
| - **Input Fields**: |
| - `tool_name`: (`string`) The name of the tool being called. |
| - `tool_input`: (`object`) The raw arguments generated by the model. |
| - `mcp_context`: (`object`) Optional metadata for MCP-based tools. |
| - `original_request_name`: (`string`) The original name of the tool being |
| called, if this is a tail tool call. |
| - **Relevant Output Fields**: |
| - `decision`: Set to `"deny"` (or `"block"`) to prevent the tool from |
| executing. |
| - `reason`: Required if denied. This text is sent **to the agent** as a tool |
| error, allowing it to respond or retry. |
| - `hookSpecificOutput.tool_input`: An object that **merges with and |
| overrides** the model's arguments before execution. |
| - `continue`: Set to `false` to **kill the entire agent loop** immediately. |
| - **Exit Code 2 (Block Tool)**: Prevents execution. Uses `stderr` as the |
| `reason` sent to the agent. **The turn continues.** |
| |
| ### `AfterTool` |
|
|
| Fires after a tool executes. Used for result auditing, context injection, or |
| hiding sensitive output from the agent. |
|
|
| - **Input Fields**: |
| - `tool_name`: (`string`) |
| - `tool_input`: (`object`) The original arguments. |
| - `tool_response`: (`object`) The result containing `llmContent`, |
| `returnDisplay`, and optional `error`. |
| - `mcp_context`: (`object`) |
| - `original_request_name`: (`string`) The original name of the tool being |
| called, if this is a tail tool call. |
| - **Relevant Output Fields**: |
| - `decision`: Set to `"deny"` to hide the real tool output from the agent. |
| - `reason`: Required if denied. This text **replaces** the tool result sent |
| back to the model. |
| - `hookSpecificOutput.additionalContext`: Text that is **appended** to the |
| tool result for the agent. |
| - `hookSpecificOutput.tailToolCallRequest`: (`{ name: string, args: object }`) |
| A request to execute another tool immediately after this one. The result of |
| this "tail call" will replace the original tool's response. Ideal for |
| programmatic tool routing. |
| - `continue`: Set to `false` to **kill the entire agent loop** immediately. |
| - **Exit Code 2 (Block Result)**: Hides the tool result. Uses `stderr` as the |
| replacement content sent to the agent. **The turn continues.** |
| |
| --- |
|
|
| ## Agent hooks |
|
|
| ### `BeforeAgent` |
|
|
| Fires after a user submits a prompt, but before the agent begins planning. Used |
| for prompt validation or injecting dynamic context. |
|
|
| - **Input Fields**: |
| - `prompt`: (`string`) The original text submitted by the user. |
| - **Relevant Output Fields**: |
| - `hookSpecificOutput.additionalContext`: Text that is **appended** to the |
| prompt for this turn only. |
| - `decision`: Set to `"deny"` to block the turn and **discard the user's |
| message** (it will not appear in history). |
| - `continue`: Set to `false` to block the turn but **save the message to |
| history**. |
| - `reason`: Required if denied or stopped. |
| - **Exit Code 2 (Block Turn)**: Aborts the turn and erases the prompt from |
| context. Same as `decision: "deny"`. |
| |
| ### `AfterAgent` |
|
|
| Fires once per turn after the model generates its final response. Primary use |
| case is response validation and automatic retries. |
|
|
| - **Input Fields**: |
| - `prompt`: (`string`) The user's original request. |
| - `prompt_response`: (`string`) The final text generated by the agent. |
| - `stop_hook_active`: (`boolean`) Indicates if this hook is already running as |
| part of a retry sequence. |
| - **Relevant Output Fields**: |
| - `decision`: Set to `"deny"` to **reject the response** and force a retry. |
| - `reason`: Required if denied. This text is sent **to the agent as a new |
| prompt** to request a correction. |
| - `continue`: Set to `false` to **stop the session** without retrying. |
| - `hookSpecificOutput.clearContext`: If `true`, clears conversation history |
| (LLM memory) while preserving UI display. |
| - **Exit Code 2 (Retry)**: Rejects the response and triggers an automatic retry |
| turn using `stderr` as the feedback prompt. |
| |
| --- |
|
|
| ## Model hooks |
|
|
| ### `BeforeModel` |
|
|
| Fires before sending a request to the LLM. Operates on a stable, SDK-agnostic |
| request format. |
|
|
| - **Input Fields**: |
| - `llm_request`: (`object`) Contains `model`, `messages`, and `config` |
| (generation params). |
| - **Relevant Output Fields**: |
| - `hookSpecificOutput.llm_request`: An object that **overrides** parts of the |
| outgoing request (for example, changing models or temperature). |
| - `hookSpecificOutput.llm_response`: A **Synthetic Response** object. If |
| provided, the CLI skips the LLM call entirely and uses this as the response. |
| - `decision`: Set to `"deny"` to block the request and abort the turn. |
| - **Exit Code 2 (Block Turn)**: Aborts the turn and skips the LLM call. Uses |
| `stderr` as the error message. |
| |
| ### `BeforeToolSelection` |
|
|
| Fires before the LLM decides which tools to call. Used to filter the available |
| toolset or force specific tool modes. |
|
|
| - **Input Fields**: |
| - `llm_request`: (`object`) Same format as `BeforeModel`. |
| - **Relevant Output Fields**: |
| - `hookSpecificOutput.toolConfig.mode`: (`"AUTO" | "ANY" | "NONE"`) |
| - `"NONE"`: Disables all tools (Wins over other hooks). |
| - `"ANY"`: Forces at least one tool call. |
| - `hookSpecificOutput.toolConfig.allowedFunctionNames`: (`string[]`) Whitelist |
| of tool names. |
| - **Union Strategy**: Multiple hooks' whitelists are **combined**. |
| - **Limitations**: Does **not** support `decision`, `continue`, or |
| `systemMessage`. |
| |
| ### `AfterModel` |
|
|
| Fires immediately after an LLM response chunk is received. Used for real-time |
| redaction or PII filtering. |
|
|
| - **Input Fields**: |
| - `llm_request`: (`object`) The original request. |
| - `llm_response`: (`object`) The model's response (or a single chunk during |
| streaming). |
| - **Relevant Output Fields**: |
| - `hookSpecificOutput.llm_response`: An object that **replaces** the model's |
| response chunk. |
| - `decision`: Set to `"deny"` to discard the response chunk and block the |
| turn. |
| - `continue`: Set to `false` to **kill the entire agent loop** immediately. |
| - **Note on Streaming**: Fired for **every chunk** generated by the model. |
| Modifying the response only affects the current chunk. |
| - **Exit Code 2 (Block Response)**: Aborts the turn and discards the model's |
| output. Uses `stderr` as the error message. |
| |
| --- |
|
|
| ## Lifecycle & system hooks |
|
|
| ### `SessionStart` |
|
|
| Fires on application startup, resuming a session, or after a `/clear` command. |
| Used for loading initial context. |
|
|
| - **Input fields**: |
| - `source`: (`"startup" | "resume" | "clear"`) |
| - **Relevant output fields**: |
| - `hookSpecificOutput.additionalContext`: (`string`) |
| - **Interactive**: Injected as the first turn in history. |
| - **Non-interactive**: Prepended to the user's prompt. |
| - `systemMessage`: Shown at the start of the session. |
| - **Advisory only**: `continue` and `decision` fields are **ignored**. Startup |
| is never blocked. |
|
|
| ### `SessionEnd` |
|
|
| Fires when the CLI exits or a session is cleared. Used for cleanup or final |
| telemetry. |
|
|
| - **Input Fields**: |
| - `reason`: (`"exit" | "clear" | "logout" | "prompt_input_exit" | "other"`) |
| - **Relevant Output Fields**: |
| - `systemMessage`: Displayed to the user during shutdown. |
| - **Best Effort**: The CLI **will not wait** for this hook to complete and |
| ignores all flow-control fields (`continue`, `decision`). |
|
|
| ### `Notification` |
|
|
| Fires when the CLI emits a system alert (for example, Tool Permissions). Used |
| for external logging or cross-platform alerts. |
|
|
| - **Input Fields**: |
| - `notification_type`: (`"ToolPermission"`) |
| - `message`: Summary of the alert. |
| - `details`: JSON object with alert-specific metadata (for example, tool name, |
| file path). |
| - **Relevant Output Fields**: |
| - `systemMessage`: Displayed alongside the system alert. |
| - **Observability Only**: This hook **cannot** block alerts or grant permissions |
| automatically. Flow-control fields are ignored. |
| |
| ### `PreCompress` |
|
|
| Fires before the CLI summarizes history to save tokens. Used for logging or |
| state saving. |
|
|
| - **Input Fields**: |
| - `trigger`: (`"auto" | "manual"`) |
| - **Relevant Output Fields**: |
| - `systemMessage`: Displayed to the user before compression. |
| - **Advisory Only**: Fired asynchronously. It **cannot** block or modify the |
| compression process. Flow-control fields are ignored. |
|
|
| --- |
|
|
| ## Stable Model API |
|
|
| Gemini CLI uses these structures to ensure hooks don't break across SDK updates. |
|
|
| **LLMRequest**: |
|
|
| ```typescript |
| { |
| "model": string, |
| "messages": Array<{ |
| "role": "user" | "model" | "system", |
| "content": string // Non-text parts are filtered out for hooks |
| }>, |
| "config": { "temperature": number, ... }, |
| "toolConfig": { "mode": string, "allowedFunctionNames": string[] } |
| } |
| |
| ``` |
|
|
| **LLMResponse**: |
|
|
| ```typescript |
| { |
| "candidates": Array<{ |
| "content": { "role": "model", "parts": string[] }, |
| "finishReason": string |
| }>, |
| "usageMetadata": { "totalTokenCount": number } |
| } |
| ``` |
|
|