Skip to main content

Overview

The Gemini bridge wraps the gemini CLI (from @google/gemini-cli), Google’s terminal-based coding agent. Gemini CLI supports model aliases, approval modes (default, auto_edit, yolo, plan), sandbox isolation, extensions, MCP servers, policy files, session management, and stream-json event streaming. It provides token usage data including cached token counts. The bridge is implemented by GeminiBridge and configured through GeminiBridgeBuilder. Access the builder through the AgentCtrl facade:
use Cognesy\AgentCtrl\AgentCtrl;
use Cognesy\AgentCtrl\Enum\AgentType;

// Dedicated factory method
$builder = AgentCtrl::gemini();

// Or via the generic factory
$builder = AgentCtrl::make(AgentType::Gemini);
// @doctest id="94a2"

Prerequisites

Install Gemini CLI globally:
# npm
npm install -g @google/gemini-cli

# Homebrew
brew install gemini-cli

# npx (no install)
npx @google/gemini-cli
# @doctest id="879c"
Configure authentication (one of):
# Gemini API key
export GEMINI_API_KEY=...

# Google Cloud API key
export GOOGLE_API_KEY=...

# Or authenticate via Google account (free tier)
gemini
# @doctest id="6bcf"

Basic Usage

use Cognesy\AgentCtrl\AgentCtrl;

$response = AgentCtrl::gemini()
    ->execute('Explain the architecture of this project.');

echo $response->text();
// @doctest id="1594"
With model selection:
$response = AgentCtrl::gemini()
    ->withModel('flash')
    ->execute('Review the test suite.');

echo $response->text();
// @doctest id="15d5"

Model Selection

Gemini CLI supports model aliases and full model names:
// Model aliases
AgentCtrl::gemini()->withModel('auto');        // Default (gemini-2.5-pro)
AgentCtrl::gemini()->withModel('pro');         // gemini-2.5-pro
AgentCtrl::gemini()->withModel('flash');       // gemini-2.5-flash
AgentCtrl::gemini()->withModel('flash-lite');  // gemini-2.5-flash-lite

// Full model name
AgentCtrl::gemini()->withModel('gemini-2.5-pro');
// @doctest id="a1f4"

Approval Modes

Gemini CLI supports four approval modes that control how tool execution is approved:
use Cognesy\AgentCtrl\Gemini\Domain\Enum\ApprovalMode;

// Default — prompt for approval on each tool use
AgentCtrl::gemini()->withApprovalMode(ApprovalMode::Default);

// Auto-edit — auto-approve edit tools, prompt for others
AgentCtrl::gemini()->withApprovalMode(ApprovalMode::AutoEdit);

// YOLO — auto-approve all tool executions
AgentCtrl::gemini()->yolo();

// Plan — read-only analysis mode
AgentCtrl::gemini()->planMode();
// @doctest id="f758"

Sandbox Mode

Enable sandboxed execution for process isolation:
AgentCtrl::gemini()
    ->withSandbox()
    ->execute('Analyze the codebase.');
// @doctest id="9f2e"
On macOS, this uses Seatbelt (sandbox-exec). Docker, Podman, and gVisor are also supported.

System Prompt

Gemini CLI reads instructions from a GEMINI.md file in the project root (similar to CLAUDE.md). You can also set the GEMINI_SYSTEM_MD environment variable to point to a custom system prompt file.

Include Directories

Add additional workspace directories for the agent to access:
AgentCtrl::gemini()
    ->withIncludeDirectories(['/projects/shared-lib', '/projects/config'])
    ->execute('Check for shared dependencies.');
// @doctest id="6ad5"

Extensions

Use specific extensions:
AgentCtrl::gemini()
    ->withExtensions(['my-extension'])
    ->execute('...');
// @doctest id="003d"

MCP Servers

Restrict which MCP servers are available:
AgentCtrl::gemini()
    ->withAllowedMcpServers(['filesystem', 'github'])
    ->execute('...');
// @doctest id="1095"

Policy Files

Load additional policy files for fine-grained tool approval rules:
AgentCtrl::gemini()
    ->withPolicy(['/path/to/policy.yaml'])
    ->execute('...');
// @doctest id="76e7"

Allowed Tools

Restrict which tools the agent can use:
AgentCtrl::gemini()
    ->withAllowedTools(['read_file', 'search_files', 'list_directory'])
    ->execute('Analyze the codebase structure.');
// @doctest id="537c"

Debug Mode

Enable debug output for troubleshooting CLI behavior:
AgentCtrl::gemini()
    ->debug()
    ->execute('Analyze the codebase.');
// @doctest id="4b47"

Streaming with Gemini

Gemini streams output as JSONL with the stream-json format. The bridge normalizes these into the standard callback API:
use Cognesy\AgentCtrl\AgentCtrl;
use Cognesy\AgentCtrl\Dto\AgentResponse;

$response = AgentCtrl::gemini()
    ->onText(fn(string $text) => print($text))
    ->onToolUse(fn(string $tool, array $input, ?string $output) => print("\n> [{$tool}]\n"))
    ->onError(fn(string $message, ?string $code) => print("\nError: {$message}\n"))
    ->onComplete(fn(AgentResponse $r) => print("\n--- Done ---\n"))
    ->executeStreaming('Analyze the error handling in this codebase.');
// @doctest id="be17"

Event Normalization

Gemini emits stream-json events that are normalized:
  • message (role=assistant, delta=true) — Text deltas delivered through onText().
  • tool_result — Tool results delivered through onToolUse() with tool name, input (from paired tool_use event), result, and error status.
  • error — Errors delivered through onError() with severity and message.
  • init, tool_use, result — Lifecycle events available through the wiretap() event system.

Session Management

Gemini CLI maintains session history. Agent-Ctrl extracts session IDs from the init event:
// First execution
$first = AgentCtrl::gemini()->execute('Create an implementation plan.');
$sessionId = $first->sessionId();

// Continue the most recent session
$next = AgentCtrl::gemini()
    ->continueSession()
    ->execute('Begin implementing the plan.');

// Resume a specific session by ID
if ($sessionId !== null) {
    $next = AgentCtrl::gemini()
        ->resumeSession((string) $sessionId)
        ->execute('Continue with the next step.');
}
// @doctest id="84fc"

Usage Data

Gemini provides token usage data from the result event stats:
$response = AgentCtrl::gemini()
    ->withModel('flash')
    ->execute('Analyze the project dependencies.');

$usage = $response->usage();
if ($usage !== null) {
    echo "Input tokens:    {$usage->input}\n";
    echo "Output tokens:   {$usage->output}\n";
    echo "Total tokens:    {$usage->total()}\n";

    if ($usage->cacheRead !== null) {
        echo "Cached tokens:   {$usage->cacheRead}\n";
    }
}
// @doctest id="bca0"

Data Availability

Data PointAvailableNotes
Text outputYesExtracted from message events (role=assistant, delta=true)
Tool callsYesNormalized from tool_use + tool_result event pairs
Session IDYesExtracted from init event
Token usageYesInput, output, cached tokens from result stats
CostNoGemini CLI does not expose cost data
Parse diagnosticsYesMalformed JSON line counts and samples

Complete Example

use Cognesy\AgentCtrl\AgentCtrl;
use Cognesy\AgentCtrl\Dto\AgentResponse;
use Cognesy\AgentCtrl\Gemini\Domain\Enum\ApprovalMode;

$response = AgentCtrl::gemini()
    ->withModel('pro')
    ->withApprovalMode(ApprovalMode::AutoEdit)
    ->withIncludeDirectories(['/projects/shared'])
    ->withTimeout(300)
    ->inDirectory('/projects/app')
    ->onText(fn(string $text) => print($text))
    ->onToolUse(fn(string $tool, array $input, ?string $output) => print("\n> [{$tool}]\n"))
    ->onComplete(fn(AgentResponse $r) => print("\n--- Complete ---\n"))
    ->executeStreaming('Review the application architecture and suggest improvements.');

if ($response->isSuccess()) {
    echo "\nReview completed successfully.\n";
    echo "Tools used: " . count($response->toolCalls) . "\n";

    $usage = $response->usage();
    if ($usage !== null) {
        echo "Tokens: {$usage->total()} (in: {$usage->input}, out: {$usage->output})\n";
    }
} else {
    echo "\nFailed with exit code: {$response->exitCode}\n";
}
// @doctest id="f56f"

Comparison with Other Bridges

FeatureClaude CodeCodexOpenCodePiGemini
System promptsYes (replace + append)NoNoYes (replace + append)Yes (GEMINI.md file)
Permission modesYes (4 levels)NoNoNoYes (4 modes)
Turn limitsYesNoNoNoYes (via settings)
Sandbox modesNoYes (3 levels)NoNoYes (Seatbelt/Docker/Podman/gVisor)
Image inputNoYesNoNoNo
Thinking levelsNoNoNoYes (6 levels)No
Named agentsNoNoYesNoNo
File attachmentsNoNoYesYes (@-prefix)No
ExtensionsNoNoNoYes (TypeScript)Yes
SkillsNoNoNoYesNo
Tool controlNoNoNoYes (select/disable)Yes (allowlist)
MCP serversNoNoNoNoYes
Policy engineNoNoNoNoYes
Session sharingNoNoYesNoNo
Session titlesNoNoYesNoNo
Ephemeral modeNoNoNoYesNo
API key overrideNoNoNoYesNo
Token usageNoYes (partial)Yes (full)YesYes
Cost trackingNoNoYesYesNo
Multi-provider modelsNoNoYesYesNo
Include directoriesNoNoNoNoYes
Debug modeNoNoNoNoYes
Free tierNoNoNoNoYes

Environment Variables

VariableDescription
GEMINI_API_KEYGemini API key
GOOGLE_API_KEYGoogle Cloud API key
GOOGLE_APPLICATION_CREDENTIALSService account JSON path
GOOGLE_CLOUD_PROJECTProject ID for Code Assist
GOOGLE_GENAI_USE_VERTEXAIEnable Vertex AI
GEMINI_SANDBOXEnable sandbox without CLI flag