Gemini CLI is a TypeScript monorepo that implements an AI-powered coding agent for the terminal. It connects to the Gemini family of models via the Google GenAI SDK, wraps model interactions in a tool-calling agent loop, and presents results through a React/Ink terminal UI. The project spans seven packages — a CLI frontend, a core engine, an SDK for programmatic use, an A2A protocol server, a devtools inspector, a VS Code companion extension, and a shared test utilities library.
This guide covers the architecture, configuration internals, deployment, security model, extension system, and operational concerns for administrators, platform engineers, and contributors.
Architecture Overview
Monorepo Structure
google-gemini/gemini-cli/
├── packages/
│ ├── cli/ # Terminal UI (React/Ink), slash commands, config
│ ├── core/ # Agent loop, model client, tools, hooks, sandbox
│ ├── sdk/ # Programmatic agent API (@google/gemini-cli-sdk)
│ ├── a2a-server/ # Agent-to-Agent protocol server (Express + SSE)
│ ├── devtools/ # Chrome DevTools-style inspector
│ ├── vscode-ide-companion/ # VS Code extension for IDE integration
│ └── test-utils/ # Shared test harness, fixtures, mock MCP servers
├── docs/ # User-facing documentation (markdown)
├── schemas/ # JSON Schema for settings.json
├── scripts/ # Build, release, and utility scripts
├── evals/ # Component evaluation tests
├── integration-tests/ # Cross-package integration tests
├── memory-tests/ # Memory leak and regression tests
├── perf-tests/ # Performance benchmarks
└── sea/ # Single Executable Application packaging
Package Dependency Graph
cli → core
sdk → core
a2a-server → core
devtools → core (events)
vscode-ide-companion → (standalone)
test-utils → core
The core package is the foundation. It owns the agent loop, model communication, tool registry, hook system, sandbox management, and configuration. All other packages depend on it.
Runtime Architecture
User Input
│
▼
┌─────────────────────────────────┐
│ CLI (packages/cli) │
│ React/Ink TUI │
│ ├── CommandService │
│ ├── SlashCommandResolver │
│ ├── ExtensionManager │
│ └── LoadedSettings │
└──────────────┬──────────────────┘
│
▼
┌─────────────────────────────────┐
│ Core (packages/core) │
│ ├── Config │
│ ├── GeminiChat (agent loop) │
│ │ ├── GeminiClient │
│ │ ├── BaseLlmClient │
│ │ └── ContentGenerator │
│ ├── Scheduler │
│ │ ├── Policy Engine │
│ │ ├── Confirmation Bus │
│ │ └── Tool Modifier │
│ ├── HookSystem │
│ ├── Tool Registry │
│ ├── Sandbox Manager │
│ └── Services │
│ ├── ShellExecutionService │
│ ├── FileDiscoveryService │
│ ├── GitService │
│ ├── ModelConfigService │
│ └── ChatRecordingService │
└──────────────┬──────────────────┘
│
▼
┌─────────────────────────────────┐
│ Google GenAI SDK │
│ Gemini API / Vertex AI │
└─────────────────────────────────┘
Key Architectural Decisions
- React/Ink for TUI: The terminal UI uses React with the Ink renderer, enabling component-based rendering with state management, effects, and context
- Event-driven tool execution: Tools execute through a Scheduler that manages confirmation, policy checks, hooks, and state transitions via an EventEmitter-based message bus
- Layered configuration: Four config scopes (System, SystemDefaults, User, Workspace) merge via deep-merge with more-specific scopes winning
- Model routing via strategies: A
CompositeStrategychains multiple routing strategies (ApprovalMode, Classifier, Override, Default, Fallback) to select the optimal model per request - File-based session persistence: Conversations are stored as JSON files in the project's temp directory, enabling resume, replay, and recording
Component Inventory
packages/core
The engine. Contains all model communication, tool execution, and agent orchestration logic.
| Component | File | Purpose |
|---|---|---|
Config |
config/config.ts |
Central configuration object. Holds session, model, sandbox, auth, and path settings |
GeminiChat |
core/geminiChat.ts |
Main agent loop. Orchestrates model calls, tool execution, and conversation flow |
GeminiClient |
core/client.ts |
Manages chat sessions with the Gemini API. Handles initialization, history, streaming |
BaseLlmClient |
core/baseLlmClient.ts |
Low-level LLM communication. Handles retries, content generation, JSON generation, embeddings |
ContentGenerator |
core/contentGenerator.ts |
Creates the GenAI client with auth, proxy, and base URL configuration |
Turn |
core/turn.ts |
Represents a single model turn with content parts, tool calls, and metadata |
Scheduler |
scheduler/scheduler.ts |
Manages parallel tool execution, confirmation flow, and state transitions |
SchedulerStateManager |
scheduler/state-manager.ts |
Tracks tool call states through their lifecycle |
HookSystem |
hooks/hookSystem.ts |
Fires lifecycle events and aggregates hook results |
HookRegistry |
hooks/hookRegistry.ts |
Stores and retrieves hook configurations |
HookRunner |
hooks/hookRunner.ts |
Executes hook commands as child processes |
HookAggregator |
hooks/hookAggregator.ts |
Merges results from multiple hooks for the same event |
Logger |
core/logger.ts |
Manages conversation logs, checkpoints, and session persistence |
ModelConfigService |
services/modelConfigService.ts |
Resolves model aliases, applies config overrides, manages model definitions |
ShellExecutionService |
services/shellExecutionService.ts |
Executes shell commands with sandbox wrapping and output capture |
FileDiscoveryService |
services/fileDiscoveryService.ts |
Finds files using ripgrep for fast codebase search |
GitService |
services/gitService.ts |
Git operations: status, diff, log, commit |
WorktreeService |
services/worktreeService.ts |
Manages git worktrees for isolated execution |
ChatRecordingService |
services/chatRecordingService.ts |
Records and replays conversations for testing |
LoopDetectionService |
services/loopDetectionService.ts |
Detects and breaks infinite agent loops |
ExecutionLifecycleService |
services/executionLifecycleService.ts |
Manages execution phases: initialize, run, finalize |
SandboxedFileSystemService |
services/sandboxedFileSystemService.ts |
File operations with sandbox path validation |
LocalSandboxManager |
services/sandboxManager.ts |
Manages Docker/Podman sandbox containers |
NoopSandboxManager |
services/sandboxManager.ts |
Pass-through sandbox for unsandboxed execution |
FolderTrustDiscoveryService |
services/FolderTrustDiscoveryService.ts |
Scans workspace for configurations before trust is granted |
PolicyEngine |
policy/policy-engine.ts |
Evaluates tool calls against policy rules |
CoreEventEmitter |
utils/events.ts |
Typed event bus for internal communication |
SimpleExtensionLoader |
utils/extensionLoader.ts |
Loads and initializes extensions from manifests |
SchemaValidator |
utils/schemaValidator.ts |
JSON Schema validation for settings and configs |
CacheService |
utils/cache.ts |
TTL-based cache with Map or WeakMap storage backends |
FileExclusions |
utils/ignorePatterns.ts |
Manages .gitignore and custom file exclusion patterns |
WorkspaceContext |
utils/workspaceContext.ts |
Resolves and validates workspace directories |
Note
The
Configclass atpackages/core/src/config/config.tsis the single most important class in the system. It initializes fromConfigParameters, manages auth, model selection, sandbox configuration, tool registry, MCP servers, hook system, and policy engine. Nearly every service receives aConfiginstance.
packages/cli
The terminal frontend. Owns the React/Ink UI, slash commands, settings loading, and extension management.
| Component | File | Purpose |
|---|---|---|
App.tsx |
ui/App.tsx |
Root React component |
LoadedSettings |
config/settings.ts |
Loads and merges settings across all scopes |
ExtensionManager |
config/extension-manager.ts |
Installs, loads, validates, and manages extensions |
ExtensionRegistryClient |
config/extensionRegistryClient.ts |
Queries the extension registry |
LoadedTrustedFolders |
config/trustedFolders.ts |
Manages folder trust decisions |
CommandService |
services/CommandService.ts |
Registers and dispatches slash commands |
SlashCommandResolver |
services/SlashCommandResolver.ts |
Resolves slash command names to handlers, with conflict detection |
BuiltinCommandLoader |
services/BuiltinCommandLoader.ts |
Loads built-in slash commands |
FileCommandLoader |
services/FileCommandLoader.ts |
Loads TOML-based custom commands from .gemini/commands/ |
SkillCommandLoader |
services/SkillCommandLoader.ts |
Loads skill-based commands |
McpPromptLoader |
services/McpPromptLoader.ts |
Loads prompts from MCP servers as slash commands |
ActivityLogger |
utils/activityLogger.ts |
Logs session activity events |
SessionSelector |
utils/sessionUtils.ts |
Lists, selects, and deletes sessions |
PersistentState |
utils/persistentState.ts |
Manages persistent UI state across sessions |
Important
The
ExtensionManageratpackages/cli/src/config/extension-manager.tsis a critical class. It extendsExtensionLoader, handles installation from npm/local paths, validates manifests, and manages enable/disable state. It enforces that extensions are loaded exactly once per session to prevent resource leaks.
packages/sdk
Programmatic API for building custom agents on top of the Gemini CLI engine.
| Component | File | Purpose |
|---|---|---|
GeminiCliAgent |
agent.ts |
Creates and resumes agent sessions |
GeminiCliSession |
session.ts |
Manages a single conversation session with streaming |
SdkTool |
tool.ts |
Base class for custom tools |
SdkToolInvocation |
tool.ts |
Represents a single tool invocation with parameters |
ModelVisibleError |
tool.ts |
Error class for errors the model should see |
SdkAgentFilesystem |
fs.ts |
Sandbox-aware file system operations |
SdkAgentShell |
shell.ts |
Shell command execution for SDK agents |
packages/a2a-server
Agent-to-Agent protocol server. Enables Gemini CLI to act as a backend agent accessible via HTTP/SSE.
| Component | File | Purpose |
|---|---|---|
app.ts |
http/app.ts |
Express app with A2A routes |
server.ts |
http/server.ts |
HTTP server lifecycle |
CommandRegistry |
commands/command-registry.ts |
A2A command registration |
InitCommand |
commands/init.ts |
Initializes agent context |
MemoryCommand |
commands/memory.ts |
Memory operations (show, add, refresh, list) |
RestoreCommand |
commands/restore.ts |
Checkpoint restore |
ExtensionsCommand |
commands/extensions.ts |
Extension management via A2A |
Events are streamed to clients via Server-Sent Events using the CoderAgentEvent enum:
TextContentEvent: Text output from the modelToolCallConfirmationEvent: Tool requiring approvalToolCallUpdateEvent: Tool execution statusStateChangeEvent: Task state transitionsThoughtEvent: Model thinking/reasoningCitationEvent: Source citations
packages/devtools
Chrome DevTools-style inspector for debugging Gemini CLI sessions.
| Component | File | Purpose |
|---|---|---|
DevTools |
src/index.ts |
EventEmitter-based devtools server |
App.tsx |
client/src/App.tsx |
React client for the inspector UI |
packages/vscode-ide-companion
VS Code extension that bridges the IDE with Gemini CLI.
| Component | File | Purpose |
|---|---|---|
IDEServer |
src/ide-server.ts |
Local HTTP server for MCP transport |
DiffManager |
src/diff-manager.ts |
Manages file diffs for review |
OpenFilesManager |
src/open-files-manager.ts |
Tracks open files in the editor |
The IDE server exposes MCP endpoints (POST /mcp, GET /mcp) with CORS protection that only allows connections from localhost/127.0.0.1.
System Requirements
Runtime Requirements
| Requirement | Version | Notes |
|---|---|---|
| Node.js | >= 20.0.0 | Required. Checked at startup |
| npm | >= 10 | Ships with Node 20 |
| Git | Any recent | Required for worktrees, checkpointing, git tools |
| Docker or Podman | Optional | Required for container-based sandboxing |
Build Requirements
| Tool | Version | Notes |
|---|---|---|
| Node.js | >= 20 | Monorepo uses npm workspaces |
| TypeScript | 5.x | Strict mode enabled |
| Vitest | Latest | Test runner |
| React | 18+ | For Ink-based TUI |
Platform Support
| Platform | Sandbox Options |
|---|---|
| macOS | Docker, Podman, sandbox-exec (seatbelt profiles) |
| Linux | Docker, Podman |
| Windows | Docker, native Windows sandbox |
Configuration Reference
Settings Schema
The complete settings schema is defined at schemas/settings.schema.json. Below is the authoritative reference for all configuration categories.
Tip
Reference the JSON schema URL in your settings file for IDE autocompletion:
"$schema": "https://raw.githubusercontent.com/google-gemini/gemini-cli/main/schemas/settings.schema.json"
Settings Scope Hierarchy
System Settings (GEMINI_CLI_SYSTEM_SETTINGS_PATH)
↓ overrides
System Defaults (GEMINI_CLI_SYSTEM_DEFAULTS_PATH)
↓ overrides
User Settings (~/.gemini/settings.json)
↓ overrides
Workspace Settings (.gemini/settings.json)
Warning
Admin policy paths (
adminPolicyPaths) set at the System scope cannot be overridden by workspace settings. This is the mechanism for enforcing organization-wide security policies.
General Settings
| Key | Type | Default | Description |
|---|---|---|---|
general.vimMode |
boolean | false |
Enable Vim keybindings |
general.preferredEditor |
string | - | Editor for /editor command |
general.defaultApprovalMode |
enum | "default" |
default, auto_edit, plan |
general.devtools |
boolean | false |
Enable DevTools inspector |
general.enableAutoUpdate |
boolean | true |
Auto-update checks |
general.enableAutoUpdateNotification |
boolean | true |
Update notifications |
general.enableNotifications |
boolean | false |
Terminal notifications |
general.notificationMethod |
enum | "auto" |
auto, osc9, osc777, bell |
general.checkpointing.enabled |
boolean | false |
Git checkpoint snapshots |
general.plan.enabled |
boolean | true |
Enable Plan Mode |
general.plan.directory |
string | - | Plan artifacts directory |
general.plan.modelRouting |
boolean | true |
Auto Pro/Flash switching |
general.retryFetchErrors |
boolean | true |
Retry on fetch failures |
general.maxAttempts |
number | 10 |
Max model request retries (cap: 10) |
general.topicUpdateNarration |
boolean | true |
Structured progress reporting |
general.debugKeystrokeLogging |
boolean | false |
Log keystrokes |
general.sessionRetention.enabled |
boolean | true |
Auto session cleanup |
general.sessionRetention.maxAge |
string | "30d" |
Max session age |
general.sessionRetention.maxCount |
number | - | Max sessions to keep |
general.sessionRetention.minRetention |
string | "1d" |
Minimum retention |
Model Settings
| Key | Type | Default | Description |
|---|---|---|---|
model.name |
string | - | Model name override |
model.maxSessionTurns |
number | -1 |
Max turns per session (-1 = unlimited) |
model.compressionThreshold |
number | 0.5 |
Context compression trigger (0.0-1.0) |
model.disableLoopDetection |
boolean | false |
Disable loop detection |
model.skipNextSpeakerCheck |
boolean | true |
Skip speaker check |
model.summarizeToolOutput |
object | - | Per-tool output summarization config |
Model Configuration System
The modelConfigs setting controls model aliases, overrides, definitions, ID resolutions, and fallback chains.
Key model aliases:
| Alias | Base Model | Purpose |
|---|---|---|
chat-base |
- | Base chat config (temperature: 1, topP: 0.95, thinking enabled) |
chat-base-2.5 |
chat-base |
Gemini 2.5 thinking budget: 8192 |
chat-base-3 |
chat-base |
Gemini 3 thinking level: HIGH |
classifier |
gemini-2.5-flash-lite |
Task complexity classification |
prompt-completion |
gemini-2.5-flash-lite |
Prompt autocomplete |
web-search |
gemini-3-flash-preview |
Google Search integration |
web-fetch |
gemini-3-flash-preview |
URL content fetching |
loop-detection |
gemini-3-flash-preview |
Infinite loop detection |
summarizer-default |
gemini-2.5-flash-lite |
Context summarization |
Model families and tiers:
| Model ID | Tier | Family | Preview | Thinking |
|---|---|---|---|---|
gemini-3.1-pro-preview |
Pro | gemini-3 | Yes | Yes |
gemini-3-pro-preview |
Pro | gemini-3 | Yes | Yes |
gemini-3-flash-preview |
Flash | gemini-3 | Yes | No |
gemini-3.1-flash-lite-preview |
Flash-Lite | gemini-3 | Yes | No |
gemini-2.5-pro |
Pro | gemini-2.5 | No | No |
gemini-2.5-flash |
Flash | gemini-2.5 | No | No |
gemini-2.5-flash-lite |
Flash-Lite | gemini-2.5 | No | No |
Fallback chains:
The system defines three fallback chains (preview, default, lite) that determine retry behavior when a model is unavailable:
- Preview chain:
gemini-3-pro-preview→gemini-3-flash-preview - Default chain:
gemini-2.5-pro→gemini-2.5-flash - Lite chain:
gemini-2.5-flash-lite→gemini-2.5-flash→gemini-2.5-pro
UI Settings
| Key | Type | Default | Description |
|---|---|---|---|
ui.theme |
string | - | UI theme name |
ui.autoThemeSwitching |
boolean | true |
Auto light/dark switching |
ui.terminalBackgroundPollingInterval |
number | 60 |
Background color poll interval (seconds) |
ui.customThemes |
object | {} |
Custom theme definitions |
ui.hideBanner |
boolean | false |
Hide startup banner |
ui.hideFooter |
boolean | false |
Hide status footer |
ui.hideContextSummary |
boolean | false |
Hide context info above input |
ui.hideWindowTitle |
boolean | false |
Hide window title |
ui.showLineNumbers |
boolean | true |
Line numbers in chat |
ui.showCitations |
boolean | false |
Show model citations |
ui.showModelInfoInChat |
boolean | false |
Show model name per turn |
ui.showUserIdentity |
boolean | true |
Show signed-in user |
ui.showMemoryUsage |
boolean | false |
Display memory stats |
ui.showShortcutsHint |
boolean | true |
Show "? for shortcuts" |
ui.compactToolOutput |
boolean | true |
Compact file/directory output |
ui.dynamicWindowTitle |
boolean | true |
Status icons in title |
ui.showStatusInTitle |
boolean | false |
Thoughts in title |
ui.inlineThinkingMode |
enum | "off" |
off or full |
ui.useAlternateBuffer |
boolean | false |
Alternate screen buffer |
ui.incrementalRendering |
boolean | true |
Reduce flicker (needs alt buffer) |
ui.useBackgroundColor |
boolean | true |
Background colors |
ui.renderProcess |
boolean | true |
Ink render process |
ui.terminalBuffer |
boolean | false |
New terminal buffer architecture |
ui.showSpinner |
boolean | true |
Operation spinner |
ui.loadingPhrases |
enum | "off" |
tips, witty, all, off |
ui.errorVerbosity |
enum | "low" |
low or full |
ui.escapePastedAtSymbols |
boolean | false |
Escape pasted @ symbols |
ui.hideTips |
boolean | false |
Hide tips |
ui.showCompatibilityWarnings |
boolean | true |
Terminal warnings |
ui.showHomeDirectoryWarning |
boolean | true |
Home dir warning |
ui.collapseDrawerDuringApproval |
boolean | true |
Collapse drawer on approval |
ui.footer.items |
string[] | - | Footer item IDs |
ui.footer.showLabels |
boolean | true |
Footer labels |
ui.footer.hideCWD |
boolean | false |
Hide working directory |
ui.footer.hideSandboxStatus |
boolean | false |
Hide sandbox status |
ui.footer.hideModelInfo |
boolean | false |
Hide model info |
ui.footer.hideContextPercentage |
boolean | true |
Hide context % |
ui.accessibility.screenReader |
boolean | false |
Screen reader mode |
Output Settings
| Key | Type | Default | Description |
|---|---|---|---|
output.format |
enum | "text" |
text or json |
Privacy Settings
| Key | Type | Default | Description |
|---|---|---|---|
privacy.usageStatisticsEnabled |
boolean | true |
Usage statistics collection |
Billing Settings
| Key | Type | Default | Description |
|---|---|---|---|
billing.overageStrategy |
enum | "ask" |
ask, always, never |
IDE Settings
| Key | Type | Default | Description |
|---|---|---|---|
ide.enabled |
boolean | false |
Enable IDE integration |
ide.hasSeenNudge |
boolean | false |
IDE nudge shown |
Security Settings
| Key | Type | Default | Description |
|---|---|---|---|
security.auth.selectedType |
string | - | Current auth method |
security.folderTrust.enabled |
boolean | true |
Folder trust system |
Tools Settings
| Key | Type | Default | Description |
|---|---|---|---|
tools.allowed |
string[] | - | Auto-approved tool names |
tools.sandbox |
boolean | true |
Enable tool sandboxing |
Advanced Settings
| Key | Type | Default | Description |
|---|---|---|---|
policyPaths |
string[] | [] |
Additional policy file paths |
adminPolicyPaths |
string[] | [] |
Admin policy paths (cannot be overridden) |
mcpServers |
object | {} |
MCP server configurations |
hooks |
object | - | Hook event configurations |
telemetry |
object | - | Telemetry (OTLP) configuration |
MCP Server Configuration
Each MCP server entry under mcpServers:
{
"mcpServers": {
"server-name": {
"command": "node",
"args": ["./server.js"],
"env": { "KEY": "value" },
"timeout": 30000,
"trust": true
}
}
}
The MCPServerConfig class at packages/core/src/config/config.ts:465 manages server lifecycle, connection state, and tool discovery.
Environment Variables Reference
| Variable | Purpose | Required For |
|---|---|---|
GEMINI_API_KEY |
Gemini API key | USE_GEMINI auth |
GOOGLE_CLOUD_PROJECT |
GCP project ID | USE_VERTEX_AI auth |
GOOGLE_CLOUD_LOCATION |
GCP region | USE_VERTEX_AI auth |
GOOGLE_API_KEY |
Google API key | Vertex express mode |
GOOGLE_GENAI_USE_VERTEXAI |
Force Vertex AI mode | Vertex AI |
GEMINI_MODEL |
Override model name | - |
GEMINI_SANDBOX |
Sandbox mode | Sandboxing |
GEMINI_SANDBOX_IMAGE |
Custom sandbox Docker image | Docker sandbox |
GEMINI_SANDBOX_PROXY_COMMAND |
Proxy setup for sandbox | Corporate proxy |
SANDBOX_FLAGS |
Additional sandbox flags | - |
SANDBOX_MOUNTS |
Additional sandbox mounts | - |
BUILD_SANDBOX |
Build sandbox image | Dev |
GEMINI_CONFIG_DIR |
Override config directory | - |
GEMINI_CLI_HOME |
Override CLI home directory | - |
GEMINI_CLI_SYSTEM_SETTINGS_PATH |
System settings file path | Enterprise deployment |
GEMINI_CLI_SYSTEM_DEFAULTS_PATH |
System defaults file path | Enterprise deployment |
GEMINI_CLI_CUSTOM_HEADERS |
Custom HTTP headers | Proxy/middleware |
GEMINI_DEBUG_LOG_FILE |
Debug log file path | Debugging |
DEBUG |
Enable debug logging | Debugging |
DEBUG_MODE |
Debug mode flag | Debugging |
MCP_DEBUG |
MCP protocol debug logging | MCP debugging |
HTTPS_PROXY |
HTTPS proxy URL | Corporate network |
HTTP_PROXY |
HTTP proxy URL | Corporate network |
NO_PROXY |
Proxy bypass list | Corporate network |
GOOGLE_VERTEX_BASE_URL |
Custom Vertex AI endpoint | Custom infrastructure |
GOOGLE_GEMINI_BASE_URL |
Custom Gemini API endpoint | Custom infrastructure |
GEMINI_CLI_IDE_WORKSPACE_PATH |
IDE workspace path | IDE integration |
GEMINI_CLI_IDE_SERVER_PORT |
IDE companion server port | IDE integration |
VSCODE_PID |
VS Code process ID | IDE detection |
GEMINI_FOLDER_TRUST |
Override folder trust | CI/CD |
GEMINI_YOLO_MODE |
Force YOLO mode | CI/CD |
CHECKPOINTING |
Enable checkpointing | - |
OTEL_EXPORTER_OTLP_ENDPOINT |
OpenTelemetry endpoint | Observability |
Security Architecture
Authentication
Four authentication types, defined in AuthType enum:
| Type | Constant | Mechanism |
|---|---|---|
| Google OAuth | LOGIN_WITH_GOOGLE |
Browser-based OAuth2 flow |
| Gemini API Key | USE_GEMINI |
GEMINI_API_KEY env var |
| Vertex AI | USE_VERTEX_AI |
GCP project + location or GOOGLE_API_KEY |
| Application Default Credentials | COMPUTE_ADC |
gcloud auth application-default |
API keys are stored in the OS keychain via HybridTokenStorage (service name: gemini-cli-api-key). The storage uses a TTL cache (30 seconds) to minimize keychain access.
Folder Trust System
Before loading any workspace configuration, Gemini CLI scans the .gemini/ directory via FolderTrustDiscoveryService:
Discovered items:
- Custom commands (
.gemini/commands/*.toml) - Skills (
.gemini/skills/*/SKILL.md) - Agents (
.gemini/agents/*.md) - MCP servers (from
settings.json) - Hooks (from
settings.json) - Settings (non-mcpServer, non-hook keys)
Security warnings are raised for:
tools.allowed— auto-approving toolssecurity.folderTrust.enabled = false— disabling trusttools.sandbox = false— disabling sandboxing- Custom agents present
Users must explicitly trust a folder before its configurations take effect.
Sandbox System
Caution
Running without a sandbox (empty
GEMINI_SANDBOX) means shell commands execute directly on the host. Use sandboxing in any environment where you're working with untrusted code or running in YOLO mode.
Sandbox implementations:
| Class | Mode | Description |
|---|---|---|
LocalSandboxManager |
docker/podman | Container-based isolation |
NoopSandboxManager |
none | Direct host execution |
Safe command allowlist (packages/core/src/sandbox/utils/commandSafety.ts):
The sandbox evaluates commands before execution. Known read-only commands bypass confirmation:
- Always safe:
cat,cd,cut,echo,grep,head,id,ls,pwd,stat,tail,tr,uname,wc,which,whoami - Conditionally safe:
base64(no-o/--output),find(no-exec/-delete),rg(no--pre/--hostname-bin),git(read-only subcommands only:status,log,diff,show,branchwithout write flags),sed(only-n {N}ppatterns)
Commands are parsed with shell-quote to handle pipes, &&, ||, and bash -c wrappers.
Policy Engine
The policy engine evaluates tool calls against rules to determine approval:
Tool Call → Policy Engine → PolicyDecision
├── ALLOW (auto-approve)
├── DENY (block)
└── ASK (prompt user)
Approval modes (ApprovalMode enum):
| Mode | Behavior |
|---|---|
DEFAULT |
All tool calls require approval |
AUTO_EDIT |
Edit tools auto-approved, shell commands ask |
YOLO |
All tools auto-approved |
PLAN |
Read-only mode, write tools denied |
Admin policies take precedence over workspace policies via the MODES_BY_PERMISSIVENESS ordering.
Data Sanitization
The agent-sanitization-utils.ts module sanitizes sensitive data before it enters logs or telemetry:
- PEM block redaction: Iterative parser (not regex) to avoid ReDoS
- Key-value pair redaction: Matches
password=,api_key:,--tokenpatterns - Space-separated keyword redaction: Handles
password mySecretValuepatterns - File path redaction: Redacts paths to
.key,.pem,.p12,.pfxfiles
Sensitive key patterns include: password, apikey, token, secret, credential, auth, access_token, refresh_token, session_id, cookie, private_key, client_secret.
Important
The sanitization uses an iterative approach for PEM blocks specifically to prevent Regular Expression Denial of Service (ReDoS) attacks. The test suite includes a ReDoS attack string that must process in under 50ms.
Custom Base URL Validation
When using GOOGLE_VERTEX_BASE_URL or GOOGLE_GEMINI_BASE_URL:
- URLs must be valid (parsed with
URLconstructor) - Non-localhost URLs must use HTTPS
- Invalid or HTTP URLs throw
Errorat startup
Model Routing
Strategy Chain
The CompositeStrategy chains routing strategies in priority order:
- OverrideStrategy — Honors explicit model overrides (via
--modelflag orcontext.requestedModel) - ApprovalModeStrategy — Routes based on Plan/Act mode:
PLANmode → Pro model (high-quality planning)- Approved plan exists → Flash model (efficient implementation)
- ClassifierStrategy — Uses LLM classifier to route between Pro and Flash:
- Evaluates task complexity (steps, ambiguity, strategic planning, debugging depth)
- 4+ steps → complex → Pro
- Single step, clear intent → simple → Flash
- DefaultStrategy — Returns the configured default model
- FallbackStrategy — Last resort when other strategies fail
Model ID Resolution
Models go through a resolution pipeline based on context:
User-facing model ID
→ modelIdResolutions (preview access, custom tools, Gemini 3.1)
→ Final API model ID
Example: auto resolves to gemini-3-pro-preview by default, but falls back to gemini-2.5-pro if the user doesn't have preview access.
Classifier Model
The classifier runs on gemini-2.5-flash-lite with a 512-token thinking budget and generates a JSON response:
{
"reasoning": "Simple single-file edit task",
"model_choice": "flash"
}
Hook System Internals
Architecture
Event Trigger
│
▼
HookSystem.fire*Event()
│
▼
HookPlanner.plan()
│ (selects matching hooks)
▼
HookRunner.executeHook()
│ (spawns child process)
▼
HookAggregator.aggregateResults()
│ (merges all hook outputs)
▼
HookEventHandler.handle*()
│ (applies decisions)
▼
Result
Hook Events
| Event | Input | Output Capabilities |
|---|---|---|
BeforeTool |
Tool name, args, MCP context | block, allow, modify args, stop, add context |
AfterTool |
Tool name, result | add context |
BeforeModel |
LLM request (model, config, contents) | modify request, block |
AfterModel |
Model response | add context |
BeforeToolSelection |
- | control tool calling mode (NONE, ANY, AUTO) |
Aggregation Rules
When multiple hooks fire for the same event:
- Block decisions use OR logic: If any hook returns
"decision": "block", the tool is blocked - Continue=false takes precedence: If any hook returns
"continue": false, execution stops - Reasons are concatenated: All hook reasons are joined with newlines
- BeforeToolSelection merges: NONE mode takes precedence over ANY, which takes precedence over AUTO. Allowed function names are unioned
- BeforeModel uses field replacement: Later hooks override earlier hooks' fields (last writer wins)
- AdditionalContext is concatenated: All hooks' additional context is joined
Hook Execution
Hooks execute as child processes via HookRunner:
- Default timeout: 30,000ms (configurable per hook)
- Hook receives input via stdin as JSON
- Hook returns output via stdout as JSON
- Timeout is treated as failure
- Hook errors are aggregated but don't automatically block (unless the hook returned
"decision": "block"before failing)
Trusted Hooks
The TrustedHooksManager tracks which hooks have been approved by the user. Untrusted hooks trigger a confirmation prompt before first execution.
Extension System Internals
Extension Manifest Schema
gemini-extension.json:
{
"name": "extension-name",
"version": "1.0.0",
"description": "What this extension does",
"mcpServers": {},
"skills": {},
"commands": {},
"hooks": {},
"policies": {},
"themes": {}
}
Extension Lifecycle
- Discovery:
ExtensionManagerscans enabled extensions - Validation: Manifest validated against schema
- Loading: Components registered (MCP servers, commands, skills, etc.)
- Initialization: MCP servers started, hooks registered
- Runtime: Extension components active in the session
- Shutdown: MCP servers stopped, resources cleaned up
Note
Extensions are loaded exactly once per session. Calling
loadExtensions()a second time throws"Extensions already loaded, only load extensions once."This prevents resource leaks from duplicate MCP server processes.
Extension Installation Types
| Type | Source | Storage |
|---|---|---|
| npm | gemini extensions install <package> |
~/.gemini/extensions/ |
| Local link | gemini extensions link <path> |
Symlink to local directory |
| Workspace | .gemini/ directory |
Project-local |
Extension Registry
The ExtensionRegistryClient queries a remote registry for available extensions. It supports ordering by relevance, downloads, or name.
Admin Controls
Administrators can restrict extension features via system settings:
- Disable MCP servers globally
- Disable extensions
- Disable skills
- Restrict which extensions can be installed
These restrictions are checked before executing deferred commands in the CLI's argument parser.
Scheduler and Tool Execution
Tool Call Lifecycle
Model proposes tool call
│
▼
Scheduler receives tool call
│
▼
Policy Engine evaluates
│
├── ALLOW → Execute directly
├── DENY → Return error to model
└── ASK → Enter confirmation flow
│
▼
BeforeTool hooks fire
│
├── Block → Return error
├── Modify args → Rebuild invocation
└── Allow → Continue
│
▼
User confirmation (if needed)
│
├── Approve → Execute
├── Reject → Return rejection
└── Edit → Modify args, re-confirm
│
▼
Tool executes
│
▼
AfterTool hooks fire
│
▼
Result returned to model
State Transitions
Tool calls progress through states managed by SchedulerStateManager:
PENDING → VALIDATING → WAITING → EXECUTING → COMPLETED
↘ ↗
→ REJECTED / ERROR →
Parallel Execution
The Scheduler supports parallel tool execution. Multiple tool calls from a single model turn execute concurrently, with the state manager tracking each independently.
Tool Modification
The ToolModificationHandler allows users to edit tool arguments during the confirmation flow. This is particularly useful for file edit tools where the user wants to adjust the proposed changes.
Session Management Internals
Session Storage
Sessions are stored as JSON files in the project's temporary directory:
~/.gemini/projects/<project-hash>/chats/<session-id>.json
The Storage class manages the project-specific temp directory, and ChatRecordingService handles read/write operations.
Session File Format
Sessions are stored as ConversationRecord objects containing:
sessionId: UUIDmessages: Array of conversation turnsmetadata: Model, timestamp, session state
Checkpointing
When enabled, Logger creates git-based checkpoints:
- Creates a checkpoint tag at the current commit
- Stores conversation state alongside
performRestore()can restore both:- Conversation history: Reloads the chat state
- Git state: Resets the working tree to the checkpoint
Session Retention
The sessionRetention settings control automatic cleanup:
- Sessions older than
maxAgeare deleted - If
maxCountis set, only the most recent N sessions are kept minRetentionprovides a safety floor (cannot delete sessions newer than this)
Telemetry and Observability
Built-in Telemetry
Gemini CLI collects usage statistics via Clearcut logging (Google's telemetry pipeline):
- URL:
https://play.googleapis.com/log - Controlled by:
privacy.usageStatisticsEnabled - Events: model usage, error rates, feature adoption
OpenTelemetry Support
Configure OTLP export for enterprise observability:
{
"telemetry": {
"endpoint": "https://your-otel-collector:4318"
}
}
Environment variable: OTEL_EXPORTER_OTLP_ENDPOINT
Debug Logging
The DebugLogger class provides structured debug output:
DEBUG=* gemini # All debug output
DEBUG=hooks gemini # Only hook debug output
GEMINI_DEBUG_LOG_FILE=/tmp/debug.log gemini # Write to file
DevTools Inspector
Enable the Chrome DevTools-style inspector:
{
"general": {
"devtools": true
}
}
The DevTools class (extends EventEmitter) broadcasts events that the inspector client consumes via WebSocket.
Performance
Context Compression
When conversation context approaches the model's limit, Gemini CLI triggers compression:
- Threshold:
model.compressionThreshold(default: 0.5) - Method: A summarization model (from
chat-compression-*aliases) condenses earlier conversation - Model selection: Follows the current model family (Gemini 3 uses
chat-compression-3-pro, 2.5 useschat-compression-2.5-pro)
Tool Output Summarization
Large tool outputs can be summarized before adding to context:
{
"model": {
"summarizeToolOutput": {
"run_shell_command": {
"tokenBudget": 2000
}
}
}
}
Currently only run_shell_command supports summarization.
Loop Detection
LoopDetectionService monitors for infinite loops:
- Detects repeated identical tool calls
- Uses a lightweight LLM call to confirm the loop
- Optionally double-checks with a Pro model
- Injects a corrective message to break the loop
Disable with model.disableLoopDetection: true.
File Discovery
File search uses ripgrep (bundled) via FileDiscoveryService. If ripgrep download fails at startup, falls back to slower Node.js-based search.
Retry Strategy
retryWithBackoff() in packages/core/src/utils/retry.ts:
- Exponential backoff with jitter
- Configurable
maxAttempts(default: 10, enforced max: 10) - Respects
shouldRetryOnContentfor content validation - Handles quota errors with specific retry logic:
RetryableQuotaError: Retry with backoffTerminalQuotaError: Stop retryingModelNotFoundError: Try fallback chain
Deployment Patterns
Individual Developer Setup
npm install -g @google/gemini-cli
gemini # Sign in with Google
Team Deployment with Shared Config
- Create system-level settings:
export GEMINI_CLI_SYSTEM_SETTINGS_PATH=/opt/gemini/system-settings.json
{
"general": {
"defaultApprovalMode": "default"
},
"adminPolicyPaths": ["/opt/gemini/policies/"],
"model": {
"name": "gemini-2.5-pro"
}
}
- Distribute via configuration management (Puppet, Chef, Ansible)
CI/CD Integration
export GEMINI_API_KEY="$CI_GEMINI_KEY"
export GEMINI_SANDBOX=docker
export GEMINI_FOLDER_TRUST=true
gemini --yolo -p "Run the full test suite and report results" --output-format json
Enterprise Deployment with Vertex AI
export GOOGLE_CLOUD_PROJECT="enterprise-project"
export GOOGLE_CLOUD_LOCATION="us-central1"
export GEMINI_CLI_SYSTEM_SETTINGS_PATH="/etc/gemini/settings.json"
System settings enforce:
{
"adminPolicyPaths": ["/etc/gemini/admin-policies/"],
"security": {
"folderTrust": { "enabled": true }
}
}
Custom Endpoint Deployment
For private Gemini API endpoints or proxy setups:
export GOOGLE_VERTEX_BASE_URL="https://my-proxy.internal/vertex"
# or
export GOOGLE_GEMINI_BASE_URL="https://my-proxy.internal/gemini"
export GEMINI_CLI_CUSTOM_HEADERS="X-Custom-Auth=token123"
Warning
Custom base URLs must use HTTPS unless they point to localhost. HTTP URLs are rejected at startup with a clear error message.
Operations
Health Monitoring
Monitor Gemini CLI health through:
- Debug logs:
GEMINI_DEBUG_LOG_FILEfor persistent logging - Telemetry: OTLP export for metrics and traces
- DevTools: Real-time inspection of agent behavior
- Session recording:
--record-responsesfor reproducible debugging
Error Hierarchy
The codebase defines a structured error hierarchy:
Error
├── FatalError (non-recoverable, exits the session)
│ ├── FatalAuthenticationError
│ ├── FatalInputError
│ ├── FatalSandboxError
│ ├── FatalConfigError
│ ├── FatalTurnLimitedError
│ ├── FatalToolExecutionError
│ └── FatalCancellationError
├── ForbiddenError (403)
│ └── AccountSuspendedError
├── UnauthorizedError (401)
├── BadRequestError (400)
├── CanceledError (user cancellation)
├── ChangeAuthRequestedError (auth method switch)
├── FetchError (network failures)
├── PrivateIpError (blocked private IP access)
├── InvalidStreamError (malformed model stream)
├── AgentExecutionStoppedError (hook stop)
├── AgentExecutionBlockedError (policy block)
├── TerminalQuotaError (unrecoverable quota limit)
├── RetryableQuotaError (temporary quota limit)
├── ValidationRequiredError (requires user action)
├── ModelNotFoundError (invalid model)
└── ShellParserInitializationError
Quota Error Handling
| Error Type | Behavior |
|---|---|
RetryableQuotaError |
Retry with backoff (per-minute limits) |
TerminalQuotaError |
Stop retrying (daily limits). Offer AI credits if available |
ValidationRequiredError |
Prompt user for action (e.g., billing setup) |
Common Operational Issues
Problem: MCP server processes orphaned after crash
MCP servers are child processes. If the CLI crashes, they may persist. Kill orphaned processes:
ps aux | grep "mcp-server" | grep -v grep | awk '{print $2}' | xargs kill
Problem: Settings changes not taking effect
Settings are loaded at startup. Most changes require restart. Check "Requires restart: yes" in the schema documentation.
Problem: Extension conflicts
The SlashCommandConflictHandler detects when multiple extensions register the same command name. It logs warnings and uses priority-based resolution.
Problem: Hook execution slowing down sessions
Profile hooks with DEBUG=hooks gemini. Hooks run sequentially per event. Slow hooks directly impact response time.
Troubleshooting (Admin)
Debug Mode
# Full debug output
DEBUG=* gemini
# Specific subsystems
DEBUG=hooks,mcp,sandbox gemini
# Write to file
GEMINI_DEBUG_LOG_FILE=/var/log/gemini-debug.log gemini
Session Forensics
# List all sessions for a project
gemini --list-sessions
# Session files are at:
ls ~/.gemini/projects/*/chats/
Config Debugging
Check effective merged settings:
/about
This shows: CLI version, OS, sandbox mode, model, auth type, GCP project, IDE client, user email, and tier.
Extension Debugging
# Validate extension manifest
gemini extensions validate
# List with status
gemini extensions list
# Check MCP server connections
/mcp list
Network Debugging
For proxy and connectivity issues:
# Test basic connectivity
curl -v https://generativelanguage.googleapis.com/
# Check proxy config
echo $HTTPS_PROXY $HTTP_PROXY $NO_PROXY
# MCP-specific debug
MCP_DEBUG=true gemini
Model Response Debugging
Record and replay model responses for reproducible debugging:
# Record a session
gemini --record-responses /tmp/session-recording.json -p "reproduce the bug"
# Replay the recording
gemini --fake-responses /tmp/session-recording.json -p "reproduce the bug"
Developer Internals
Build System
The project uses npm workspaces with TypeScript:
# Install all dependencies
npm install
# Build all packages
npm run build
# Run tests
npm test
# Run specific package tests
npm test -w packages/core
Testing Architecture
| Test Type | Location | Runner |
|---|---|---|
| Unit tests | packages/*/src/**/*.test.ts |
Vitest |
| Integration tests | integration-tests/ |
Vitest |
| Component evals | evals/ |
Custom harness |
| Memory tests | memory-tests/ |
Custom harness with heap snapshots |
| Performance tests | perf-tests/ |
Custom harness with timing |
| SEA tests | sea/ |
Custom launcher tests |
Key Test Utilities
| Class | Purpose |
|---|---|
TestRig |
Full integration test harness with CLI subprocess management |
InteractiveRun |
Manages an interactive CLI session in tests |
TestMcpServerBuilder |
Builds mock MCP servers for testing |
MemoryTestHarness |
Heap snapshot comparison for memory leak detection |
PerfTestHarness |
Timing and performance regression testing |
ComponentRig |
Lightweight component-level test harness |
SEA (Single Executable Application)
The sea/ directory packages Gemini CLI as a standalone binary:
- Uses Node.js SEA (Single Executable Application) feature
- Bundles all dependencies into a single executable
- Tests verify filesystem operations, rename handling, and permission checks
Release Process
Release scripts live in scripts/releasing/:
| Script | Purpose |
|---|---|
release.ts |
Main release orchestrator |
| Options | --commit, --pullRequestNumber, --channel, --cli-package-name, --dry-run |
Code Quality Patterns
- Strict TypeScript: All packages use
strict: true - Error boundaries: FatalError hierarchy for non-recoverable errors
- Debug logging:
debugLoggerthroughout for structured diagnostics - Schema validation:
SchemaValidatorfor all external input - Sanitization: Agent sanitization utils for all sensitive data
- Cache management:
CacheServicewith TTL and configurable storage backends
Integration Points
SDK Integration
Build custom agents programmatically:
import { GeminiCliAgent } from '@google/gemini-cli-sdk';
const agent = new GeminiCliAgent({
instructions: 'You are a code review assistant.',
model: 'gemini-2.5-pro',
tools: [myCustomTool],
skills: [mySkill],
});
const session = agent.session();
const stream = session.sendStream('Review the latest PR');
for await (const event of stream) {
if (event.type === 'content') {
process.stdout.write(event.value);
}
}
SDK classes:
| Class | Purpose |
|---|---|
GeminiCliAgent |
Creates sessions, manages configuration |
GeminiCliSession |
Sends messages, streams responses |
SdkTool |
Base class for custom tools |
SdkToolInvocation |
Tool invocation with parameter access |
ModelVisibleError |
Errors surfaced to the model |
SdkAgentFilesystem |
Sandboxed file operations |
SdkAgentShell |
Shell command execution |
Session resume:
const session = await agent.resumeSession(previousSessionId);
A2A Protocol Integration
Expose Gemini CLI as an A2A-compatible agent server:
import { createApp } from '@google/gemini-cli-a2a-server';
const app = createApp(config);
app.listen(3000);
Events stream via SSE using CoderAgentEvent types.
VS Code Integration
The VS Code companion extension:
- Starts a local HTTP server on
GEMINI_CLI_IDE_SERVER_PORT - Exposes MCP endpoints for tool communication
- Manages file diffs via
DiffManagerandDiffContentProvider - Tracks open files via
OpenFilesManager - CORS restricts access to localhost origins only
Enable IDE mode:
{
"ide": {
"enabled": true
}
}
MCP Protocol
Gemini CLI acts as an MCP client, connecting to external tool servers:
- Transport: stdio (command-based servers) or HTTP (SSE-based servers)
- Discovery: Tools and prompts are discovered from connected servers
- Lifecycle: Servers start with the session and stop on exit
- Token storage: MCP OAuth tokens stored via
HybridTokenStorage - Debug:
MCP_DEBUG=truefor protocol-level logging
Known Issues and Open Feature Requests
Based on the project's GitHub issue tracker:
Known Bugs (Resolved)
- #25622: Gemini Error — general error handling (closed)
- #25584: Shift+Tab not working on Windows for Plan/Act toggle (closed)
- #24880: 403 API Error after Google sign-in — commonly a quota/access issue (closed, 31 comments)
- #25323: RipGrep download failure should not slow startup (closed)
- #18503: Letter 'm' printed at startup — terminal escape code issue (closed)
- #20603: Cannot find module 'color-convert' in Colab (closed)
- #20894: Maximum update depth exceeded — React rendering loop (closed)
Open Feature Requests
- #15269: Missing subagent hook events
- #15267: Test and refine hook migration tooling
- #15263: Granular extension hook control
- #15167: UI for BeforeTool hooks within tool confirmation
- #15018: Admin rules that can be overridden
- #14964: Subagent optimization flow within
/agentsUI - #11802: OTLP headers for telemetry
- #11303: Change
--yoloto be a policy shortcut
Open UX/Security Issues
- #15268: Interactive hook configuration GUI
- #15266: Hook execution confirmation UX
- #15258: Natural language hook building
- #15179: Recursive subagent delegation
- #15014: Screen reader text repetition on Windows
- #14724: Hooks SessionStart "compress" vs "compact" incompatibility
- #14540: Complete CI/CD security review
Quick Reference
File Layout
~/.gemini/ # User config directory
├── settings.json # User settings
├── GEMINI.md # Global memory
├── extensions/ # Installed extensions
└── projects/ # Project-specific data
└── <hash>/
└── chats/ # Session files
.gemini/ # Workspace config (in project root)
├── settings.json # Workspace settings
├── commands/ # Custom TOML commands
├── skills/ # Custom skills (SKILL.md)
├── agents/ # Custom agents (.md)
└── policies/ # Policy files
Config Precedence (highest wins)
- CLI flags (
--model,--yolo, etc.) - Workspace settings (
.gemini/settings.json) - User settings (
~/.gemini/settings.json) - System settings (
GEMINI_CLI_SYSTEM_SETTINGS_PATH) - System defaults (
GEMINI_CLI_SYSTEM_DEFAULTS_PATH) - Built-in defaults
Key Source Files
| Purpose | Path |
|---|---|
| CLI entry point | packages/cli/index.ts |
| Core entry point | packages/core/src/index.ts |
| Settings schema | schemas/settings.schema.json |
| Config class | packages/core/src/config/config.ts |
| Agent loop | packages/core/src/core/geminiChat.ts |
| Scheduler | packages/core/src/scheduler/scheduler.ts |
| Hook system | packages/core/src/hooks/hookSystem.ts |
| Model routing | packages/core/src/routing/strategies/ |
| Sandbox safety | packages/core/src/sandbox/utils/commandSafety.ts |
| Extension manager | packages/cli/src/config/extension-manager.ts |
| Settings loader | packages/cli/src/config/settings.ts |
| SDK agent | packages/sdk/src/agent.ts |
| A2A server | packages/a2a-server/src/index.ts |