# 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 (e.g., `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 (e.g. "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 (e.g., `read_file`, `run_shell_command`). See the [Tools Reference](/docs/tools) for a full list of available tool names. - **MCP Tools**: Tools from MCP servers follow the naming pattern `mcp____`. - **Regex Support**: Matchers support regular expressions (e.g., `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 (e.g., 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 (e.g., 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 (e.g., 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 } } ```