---
title: "Gemini CLI Hook System: Exhaustive Reference"
description: "This document provides an exhaustive reference for the Gemini CLI hook system, covering configuration, JSON schemas, tool names, edge cases, version differences, and comparisons with Claude Code."
type: skill
canonical_url: https://claudary.paisolsolutions.com/skills/gemini-cli-hooks
source: "Claudary"
difficulty: intermediate
author: "Claude Code Knowledge Pack"
date: 2026-07-10T11:24:45.165Z
license: CC-BY-4.0
attribution: "Gemini CLI Hook System: Exhaustive Reference — Claudary (https://claudary.paisolsolutions.com/skills/gemini-cli-hooks)"
---

# Gemini CLI Hook System: Exhaustive Reference
This document provides an exhaustive reference for the Gemini CLI hook system, covering configuration, JSON schemas, tool names, edge cases, version differences, and comparisons with Claude Code.

## Overview

# Gemini CLI Hook System: Exhaustive Reference

This document provides an exhaustive reference for the Gemini CLI hook system, covering configuration, JSON schemas, tool names, edge cases, version differences, and comparisons with Claude Code.

**Last Updated:** January 2026
**Gemini CLI Versions Covered:** v0.15.x through v0.23.x

---

## Table of Contents

1. [Overview](#overview)
2. [Settings File Locations](#settings-file-locations)
3. [Hook Configuration JSON Schema](#hook-configuration-json-schema)
4. [Hook Events](#hook-events)
5. [Tool Names](#tool-names)
6. [Input JSON Schema](#input-json-schema)
7. [Output JSON Schema](#output-json-schema)
8. [Exit Code Behavior](#exit-code-behavior)
9. [Decision Field Values](#decision-field-values)
10. [Matcher Patterns](#matcher-patterns)
11. [Configuration Merge Strategies](#configuration-merge-strategies)
12. [Environment Variables](#environment-variables)
13. [CLI Commands for Hook Management](#cli-commands-for-hook-management)
14. [Claude Code Migration](#claude-code-migration)
15. [Gemini CLI vs Claude Code Comparison](#gemini-cli-vs-claude-code-comparison)
16. [Version History and Changes](#version-history-and-changes)
17. [Known Bugs and Issues](#known-bugs-and-issues)
18. [Edge Cases and Quirks](#edge-cases-and-quirks)
19. [Best Practices](#best-practices)
20. [Security Considerations](#security-considerations)
21. [Sources and References](#sources-and-references)

---

## Overview

Gemini CLI hooks are scripts or programs executed at specific points in the agentic loop, allowing interception and customization of behavior without modifying CLI source code.

**Key Characteristics:**
- Hooks run **synchronously** within the agent loop
- Communication uses **JSON over stdin/stdout** with exit code signaling
- Hooks mirror the contract used by Claude Code for compatibility
- The system was developed starting in late 2024 and matured through 2025

**Sources:**
- [Gemini CLI Hooks Documentation](https://geminicli.com/docs/hooks/)
- [GitHub Issue #9070: Comprehensive Hooking System](https://github.com/google-gemini/gemini-cli/issues/9070)

---

## Settings File Locations

### Configuration Hierarchy (Lowest to Highest Precedence)

| Priority | Location               | Description                           |
| -------- | ---------------------- | ------------------------------------- |
| 1        | Hardcoded defaults     | Built into Gemini CLI                 |
| 2        | System defaults        | OS-specific location (see below)      |
| 3        | User settings          | ~/.gemini/settings.json               |
| 4        | Project settings       | .gemini/settings.json in project root |
| 5        | System overrides       | /etc/gemini-cli/settings.json         |
| 6        | Environment variables  | Runtime overrides                     |
| 7        | Command-line arguments | Highest priority                      |

### System Default Locations by OS

| OS      | Path                                                        |
| ------- | ----------------------------------------------------------- |
| Linux   | /etc/gemini-cli/system-defaults.json                        |
| macOS   | /Library/Application Support/GeminiCli/system-defaults.json |
| Windows | C:\\ProgramData\\gemini-cli\\system-defaults.json              |

The path can be overridden using GEMINI_CLI_SYSTEM_DEFAULTS_PATH environment variable.

### Format Migration (September 2025)

- **09/10/25**: New nested JSON format supported in stable release
- **09/17/25**: Automatic migration from old format began
- Legacy v1 configuration documented separately

**Source:** [Gemini CLI Configuration](https://geminicli.com/docs/get-started/configuration/)

---

## Hook Configuration JSON Schema

### Basic Structure

```json
{
  "hooks": {
    "EventName": [
      {
        "matcher": "pattern",
        "hooks": [
          {
            "name": "unique-hook-identifier",
            "type": "command",
            "command": "./path/to/script.sh",
            "description": "Human-readable description",
            "timeout": 30000
          }
        ]
      }
    ],
    "disabled": ["hook-name-1", "hook-name-2"]
  }
}
```

### Hook Object Properties

| Property    | Type   | Required    | Default      | Description                                   |
| ----------- | ------ | ----------- | ------------ | --------------------------------------------- |
| name        | string | Recommended | Command path | Unique identifier for enable/disable commands |
| type        | string | Yes         | -            | Hook type; currently only "command" supported |
| command     | string | Yes         | -            | Path to script; supports $GEMINI_PROJECT_DIR  |
| description | string | No          | -            | Shown in /hooks panel                         |
| timeout     | number | No          | 60000        | Timeout in milliseconds                       |
| matcher     | string | No          | -            | Pattern to filter when hook runs              |

### Extension Hooks (v0.21.0+)

Extensions can include a hooks/hooks.json file:

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "WriteFile",
        "hooks": [
          {
            "type": "command",
            "command": "${extensionPath}/scripts/lint.sh"
          }
        ]
      }
    ]
  }
}
```

Extension hooks support ${extensionPath} variable substitution.

**Source:** [GitHub Issue #14449: Hook Support in Extensions](https://github.com/google-gemini/gemini-cli/issues/14449)

---

## Hook Events

### Complete Event List

| Event               | Trigger Point                  | Matcher Applies To                            |
| ------------------- | ------------------------------ | --------------------------------------------- |
| SessionStart        | Session initialization         | startup, resume, clear                        |
| SessionEnd          | Session termination            | exit, clear, logout, prompt_input_exit, other |
| BeforeAgent         | Before planning phase          | N/A                                           |
| AfterAgent          | After agent loop completes     | N/A                                           |
| BeforeModel         | Before LLM request             | N/A                                           |
| AfterModel          | After LLM response             | N/A                                           |
| BeforeToolSelection | Pre-tool filtering             | N/A                                           |
| BeforeTool          | Before tool execution          | Tool names                                    |
| AfterTool           | After tool execution           | Tool names                                    |
| PreCompress         | Before context compression     | manual, auto                                  |
| Notification        | Permission/notification events | ToolPermission                                |

### Event Capabilities

| Event               | Can Block | Can Modify Input        | Can Inject Context      |
| ------------------- | --------- | ----------------------- | ----------------------- |
| BeforeTool          | Yes       | Yes (via deny + reason) | Yes                     |
| AfterTool           | Yes       | N/A                     | Yes (additionalContext) |
| BeforeAgent         | Yes       | N/A                     | Yes (additionalContext) |
| AfterAgent          | Yes       | N/A                     | N/A                     |
| BeforeModel         | Yes       | Yes (llm_request)       | N/A                     |
| AfterModel          | Yes       | Yes (llm_response)      | N/A                     |
| BeforeToolSelection | No        | Yes (toolConfig)        | N/A                     |
| SessionStart        | No        | N/A                     | Yes (additionalContext) |

**Source:** [Hooks Reference](https://geminicli.com/docs/hooks/reference/)

---

## Tool Names

### Core Tool Names for Matchers

| Category        | Tool Names                                      |
| --------------- | ----------------------------------------------- |
| File Operations | read_file, read_many_files, write_file, replace |
| File System     | list_directory, glob, search_file_content       |
| Shell Execution | run_shell_command                               |
| Web/External    | google_web_search, web_fetch                    |
| Agent Features  | write_todos, save_memory, delegate_to_agent     |

### MCP Tool Name Format

MCP tools follow the pattern: mcp__<server_name>__<tool_name>

Example: mcp__github__create_issue

### Shell Tool Details (run_shell_command)

The shell tool executes commands with platform-specific behavior:

| Platform    | Execution Method                   |
| ----------- | ---------------------------------- |
| Windows     | powershell.exe -NoProfile -Command |
| Linux/macOS | bash -c                            |

**Configuration options:**
```json
{
  "tools": {
    "shell": {
      "enableInteractiveShell": true,
      "showColor": true,
      "pager": "cat"
    }
  }
}
```

When run_shell_command executes, it sets GEMINI_CLI=1 environment variable in subprocesses.

**Source:** [Shell Tool Documentation](https://geminicli.com/docs/tools/shell/)

---

## Input JSON Schema

### Base Fields (All Events)

Every hook receives these fields via stdin:

```json
{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "BeforeTool",
  "timestamp": "2025-12-01T10:30:00Z"
}
```

### Tool Events (BeforeTool, AfterTool)

```json
{
  "session_id": "...",
  "transcript_path": "...",
  "cwd": "...",
  "hook_event_name": "BeforeTool",
  "timestamp": "...",
  "tool_name": "write_file",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": { },
  "mcp_context": {
    "server_name": "github",
    "tool_name": "create_issue",
    "command": "...",
    "args": [],
    "cwd": "...",
    "url": "...",
    "tcp": "..."
  }
}
```

**MCP Context Transport Types:**
- stdio: command, args, cwd
- SSE/HTTP: url
- WebSocket: tcp

### Agent Events (BeforeAgent, AfterAgent)

```json
{
  "session_id": "...",
  "hook_event_name": "BeforeAgent",
  "prompt": "User's submitted text",
  "prompt_response": "Final model response",
  "stop_hook_active": true
}
```

### Model Events (BeforeModel, AfterModel, BeforeToolSelection)

```json
{
  "session_id": "...",
  "hook_event_name": "BeforeModel",
  "llm_request": {
    "model": "gemini-2.0-flash",
    "messages": [
      { "role": "user", "content": "..." },
      { "role": "model", "content": "..." }
    ],
    "config": {
      "temperature": 0.7,
      "maxOutputTokens": 8192,
      "topP": 0.95,
      "topK": 40
    },
    "toolConfig": {
      "mode": "AUTO",
      "allowedFunctionNames": ["read_file", "write_file"]
    }
  },
  "llm_response": { }
}
```

### Session Events

**SessionStart:**
```json
{
  "hook_event_name": "SessionStart",
  "source": "startup"
}
```
Values: "startup" | "resume" | "clear"

**SessionEnd:**
```json
{
  "hook_event_name": "SessionEnd",
  "reason": "exit"
}
```
Values: "exit" | "clear" | "logout" | "prompt_input_exit" | "other"

### Notification Events

```json
{
  "hook_event_name": "Notification",
  "notification_type": "ToolPermission",
  "message": "...",
  "details": { }
}
```

---

## Output JSON Schema

### Common Output Fields

```json
{
  "decision": "allow",
  "reason": "Explanation for agent",
  "systemMessage": "Message for user",
  "continue": true,
  "stopReason": "User-facing termination message",
  "suppressOutput": false,
  "hookSpecificOutput": { }
}
```

### hookSpecificOutput by Event

**SessionStart, BeforeAgent, AfterTool:**
```json
{
  "hookSpecificOutput": {
    "hookEventName": "AfterTool",
    "additionalContext": "Extra context appended to agent"
  }
}
```

**BeforeModel:**
```json
{
  "hookSpecificOutput": {
    "llm_request": { },
    "llm_response": { }
  }
}
```

**AfterModel:**
```json
{
  "hookSpecificOutput": {
    "llm_response": { }
  }
}
```

**BeforeToolSelection:**
```json
{
  "hookSpecificOutput": {
    "toolConfig": {
      "mode": "ANY",
      "allowedFunctionNames": ["read_file", "write_file"]
    }
  }
}
```

### Stable LLMRequest Object

```json
{
  "model": "string",
  "messages": [
    {
      "role": "user | model | system",
      "content": "string or array of parts"
    }
  ],
  "config": {
    "temperature": 0.7,
    "maxOutputTokens": 8192,
    "topP": 0.95,
    "topK": 40
  },
  "toolConfig": {
    "mode": "AUTO | ANY | NONE",
    "allowedFunctionNames": ["tool1", "tool2"]
  }
}
```

### Stable LLMResponse Object

```json
{
  "text": "string",
  "candidates": [
    {
      "content": {
        "role": "model",
        "parts": ["string"]
      },
      "finishReason": "STOP | MAX_TOKENS | SAFETY | RECITATION | OTHER",
      "index": 0,
      "safetyRatings": [
        {
          "category": "HARM_CATEGORY_...",
          "probability": "NEGLIGIBLE | LOW | MEDIUM | HIGH",
          "blocked": false
        }
      ]
    }
  ],
  "usageMetadata": {
    "promptTokenCount": 100,
    "candidatesTokenCount": 200,
    "totalTokenCount": 300
  }
}
```

**Note:** In v1, model hooks are primarily text-focused. Non-text parts in the content array are simplified to string representation.

---

## Exit Code Behavior

| Exit Code | Behavior             | stdout Handling                                        |
| --------- | -------------------- | ------------------------------------------------------ |
| 0         | Success              | Parsed as JSON; falls back to systemMessage if invalid |
| 2         | Blocking error       | stderr shown to agent/user; operation may be blocked   |
| Other     | Non-blocking warning | stderr logged but execution continues                  |

### Exit Code Semantics Comparison

| Aspect   | Gemini CLI          | Claude Code |
| -------- | ------------------- | ----------- |
| Success  | 0                   | 0           |
| Blocking | 2                   | 2           |
| Warning  | Non-zero (except 2) | Non-zero    |

**Source:** [Hooks Reference](https://geminicli.com/docs/hooks/reference/)

---

## Decision Field Values

### Available Decision Values

| Value   | Effect             | Use Case                             |
| ------- | ------------------ | ------------------------------------ |
| allow   | Operation proceeds | Explicitly permit action             |
| deny    | Operation blocked  | Block with reason shown to agent     |
| block   | Operation blocked  | Similar to deny                      |
| ask     | Prompt user        | Request user confirmation            |
| approve | Approve pending    | Approve a previously asked operation |

### Decision Examples

**Deny a tool execution:**
```json
{
  "decision": "deny",
  "reason": "Potential secret detected in file content"
}
```

**Allow with

---

Source: [Claudary](https://claudary.paisolsolutions.com/skills/gemini-cli-hooks) · https://claudary.paisolsolutions.com
