From 9032eff9fc25c9a9136d361626f1fcde15441bbf Mon Sep 17 00:00:00 2001 From: openasocket Date: Thu, 19 Feb 2026 22:42:51 -0500 Subject: [PATCH 01/45] MAESTRO: feat: add gemini-cli to ToolType, agent definitions, and capabilities Add 'gemini-cli' as a first-class agent type based on Gemini CLI v0.29.5: - ToolType union in shared/types.ts - Full agent definition with CLI argument builders (-p, -y, -m, --output-format stream-json, --resume, --approval-mode plan, --include-directories) - Real capabilities replacing placeholder values (resume, read-only, JSON output, streaming, model selection, etc.) - VALID_TOOL_TYPES in agent-output-parser.ts Record map entries deferred to GEMINI-02. Co-Authored-By: Claude Opus 4.6 --- src/main/agents/capabilities.ts | 44 ++++++++++++------------- src/main/agents/definitions.ts | 20 +++++++++++ src/main/parsers/agent-output-parser.ts | 1 + src/shared/types.ts | 2 +- 4 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/main/agents/capabilities.ts b/src/main/agents/capabilities.ts index 30fc36c19..6314efa41 100644 --- a/src/main/agents/capabilities.ts +++ b/src/main/agents/capabilities.ts @@ -197,31 +197,31 @@ export const AGENT_CAPABILITIES: Record = { }, /** - * Gemini CLI - Google's Gemini model CLI + * Gemini CLI - Google's Gemini model CLI (v0.29.5) + * https://github.com/google-gemini/gemini-cli * - * PLACEHOLDER: Most capabilities set to false until Gemini CLI is stable - * and can be tested. Update this configuration when integrating the agent. + * Verified capabilities based on Gemini CLI v0.29.5 flags and output format. */ 'gemini-cli': { - supportsResume: false, - supportsReadOnlyMode: false, - supportsJsonOutput: false, - supportsSessionId: false, - supportsImageInput: true, // Gemini supports multimodal - supportsImageInputOnResume: false, // Not yet investigated - supportsSlashCommands: false, - supportsSessionStorage: false, - supportsCostTracking: false, - supportsUsageStats: false, - supportsBatchMode: false, - requiresPromptToStart: false, // Not yet investigated - supportsStreaming: true, // Likely streams - supportsResultMessages: false, - supportsModelSelection: false, // Not yet investigated - supportsStreamJsonInput: false, - supportsThinkingDisplay: false, // Not yet investigated - supportsContextMerge: false, // Not yet investigated - PLACEHOLDER - supportsContextExport: false, // Not yet investigated - PLACEHOLDER + supportsResume: true, // --resume latest|index + supportsReadOnlyMode: true, // --approval-mode plan + supportsJsonOutput: true, // --output-format json|stream-json + supportsSessionId: true, // session_id in JSON output + supportsImageInput: true, // Gemini is multimodal + supportsImageInputOnResume: false, // No image flag for resume + supportsSlashCommands: false, // Gemini uses /slash commands but not exposed in JSON + supportsSessionStorage: true, // ~/.gemini/tmp/project/chats/ + supportsCostTracking: false, // Free tier / no cost data in output + supportsUsageStats: true, // Token stats in JSON output + supportsBatchMode: true, // -p flag for non-interactive + requiresPromptToStart: true, // Needs -p for batch mode + supportsStreaming: true, // stream-json output + supportsResultMessages: true, // 'result' event in stream-json + supportsModelSelection: true, // -m/--model flag + supportsStreamJsonInput: false, // No stdin JSON streaming + supportsThinkingDisplay: true, // Tracks thought tokens + supportsContextMerge: true, // Can receive transferred context + supportsContextExport: true, // Can export context for transfer }, /** diff --git a/src/main/agents/definitions.ts b/src/main/agents/definitions.ts index eb935768a..b53ded937 100644 --- a/src/main/agents/definitions.ts +++ b/src/main/agents/definitions.ts @@ -174,6 +174,26 @@ export const AGENT_DEFINITIONS: AgentDefinition[] = [ binaryName: 'gemini', command: 'gemini', args: [], + batchModePrefix: [], + batchModeArgs: ['-y'], + jsonOutputArgs: ['--output-format', 'stream-json'], + resumeArgs: (sessionId: string) => ['--resume', sessionId], + readOnlyArgs: ['--approval-mode', 'plan'], + yoloModeArgs: ['-y'], + workingDirArgs: (dir: string) => ['--include-directories', dir], + imageArgs: undefined, + modelArgs: (modelId: string) => ['-m', modelId], + promptArgs: (prompt: string) => ['-p', prompt], + configOptions: [ + { + key: 'contextWindow', + type: 'number' as const, + label: 'Context Window Size', + description: + 'Maximum context window size in tokens. Common values: 1048576 (Gemini 2.5 Pro), 32767 (Gemini 2.5 Flash).', + default: 1048576, + }, + ], }, { id: 'qwen3-coder', diff --git a/src/main/parsers/agent-output-parser.ts b/src/main/parsers/agent-output-parser.ts index 1dad9cf43..07419b367 100644 --- a/src/main/parsers/agent-output-parser.ts +++ b/src/main/parsers/agent-output-parser.ts @@ -41,6 +41,7 @@ const VALID_TOOL_TYPES: ToolType[] = [ 'codex', 'terminal', 'factory-droid', + 'gemini-cli', ]; /** diff --git a/src/shared/types.ts b/src/shared/types.ts index da00aa305..33e1bf26d 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -1,7 +1,7 @@ // Shared type definitions for Maestro CLI and Electron app // These types are used by both the CLI tool and the renderer process -export type ToolType = 'claude-code' | 'opencode' | 'codex' | 'terminal' | 'factory-droid'; +export type ToolType = 'claude-code' | 'opencode' | 'codex' | 'terminal' | 'factory-droid' | 'gemini-cli'; /** * ThinkingMode controls how AI reasoning/thinking content is displayed. From 0aeefd33b49c60d1d924758287d6eaa5fb2aa26e Mon Sep 17 00:00:00 2001 From: openasocket Date: Thu, 19 Feb 2026 22:44:27 -0500 Subject: [PATCH 02/45] MAESTRO: feat: add gemini-cli to AGENT_ARTIFACTS in contextGroomer.ts Add gemini-cli entry to the AGENT_ARTIFACTS record with 15 artifact patterns for context transfer stripping: 6 slash commands (/help, /tools, /memory, /stats, /restore, /mcp) and 9 brand/model references (Gemini, Google, model names). Co-Authored-By: Claude Opus 4.6 --- src/renderer/services/contextGroomer.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/renderer/services/contextGroomer.ts b/src/renderer/services/contextGroomer.ts index c8abc901d..5dd529be5 100644 --- a/src/renderer/services/contextGroomer.ts +++ b/src/renderer/services/contextGroomer.ts @@ -100,6 +100,25 @@ export const AGENT_ARTIFACTS: Record = { 'Opus', 'Sonnet', ], + 'gemini-cli': [ + // Slash commands + '/help', + '/tools', + '/memory', + '/stats', + '/restore', + '/mcp', + // Brand and model references + 'Gemini', + 'Google', + 'Gemini CLI', + 'gemini-cli', + 'gemini-2.5-pro', + 'gemini-2.5-flash', + 'gemini-2.0-flash', + 'gemini-3-pro-preview', + 'gemini-3-flash-preview', + ], terminal: [ // Terminal has no agent-specific artifacts ], From 3d58ff47ffd1c28ada7ff551449acf3a37eab200 Mon Sep 17 00:00:00 2001 From: openasocket Date: Thu, 19 Feb 2026 22:49:47 -0500 Subject: [PATCH 03/45] MAESTRO: feat: add gemini-cli to AGENT_TARGET_NOTES and getAgentDisplayName in contextGroomer.ts Co-Authored-By: Claude Opus 4.6 --- src/renderer/services/contextGroomer.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/renderer/services/contextGroomer.ts b/src/renderer/services/contextGroomer.ts index 5dd529be5..100dfa3a0 100644 --- a/src/renderer/services/contextGroomer.ts +++ b/src/renderer/services/contextGroomer.ts @@ -151,6 +151,14 @@ export const AGENT_TARGET_NOTES: Record = { It supports multiple model providers (Claude, GPT, Gemini). It can read and edit files, run commands, search code, and interact with git. It has tiered autonomy levels for controlling operation permissions. + `, + 'gemini-cli': ` + Gemini CLI is Google's AI coding assistant powered by Gemini models. + It can read and edit files, run shell commands, search code, fetch web pages, and use Google Search. + It supports MCP (Model Context Protocol) servers for extensibility. + It has approval modes: default, auto_edit, yolo, and plan (read-only). + Models include gemini-2.5-pro, gemini-2.5-flash, and gemini-3 preview models. + It supports tool calling with 15+ built-in tools. `, terminal: ` Terminal is a raw shell interface. @@ -167,6 +175,7 @@ export function getAgentDisplayName(agentType: ToolType): string { opencode: 'OpenCode', codex: 'OpenAI Codex', 'factory-droid': 'Factory Droid', + 'gemini-cli': 'Gemini CLI', terminal: 'Terminal', }; return names[agentType] || agentType; From a1edf6297b847dcefdeb506a81d783d92c87fdce Mon Sep 17 00:00:00 2001 From: openasocket Date: Thu, 19 Feb 2026 23:49:45 -0500 Subject: [PATCH 04/45] MAESTRO: feat: enable gemini-cli as supported agent in wizard AgentSelectionScreen Change gemini-cli from placeholder (supported: false, 'Coming soon') to fully supported agent with description "Google's AI coding assistant" and brandColor '#4285F4'. SVG sparkle logo was already in place. Co-Authored-By: Claude Opus 4.6 --- .../components/Wizard/screens/AgentSelectionScreen.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/renderer/components/Wizard/screens/AgentSelectionScreen.tsx b/src/renderer/components/Wizard/screens/AgentSelectionScreen.tsx index 4dae3ace6..15eb0d7a8 100644 --- a/src/renderer/components/Wizard/screens/AgentSelectionScreen.tsx +++ b/src/renderer/components/Wizard/screens/AgentSelectionScreen.tsx @@ -37,7 +37,7 @@ export interface AgentTile { /** * Define the agents to display in the grid - * Supported agents: Claude Code, Codex, OpenCode (shown first) + * Supported agents: Claude Code, Codex, OpenCode, Factory Droid, Gemini CLI (shown first) * Unsupported agents: shown ghosted with "Coming soon" (at bottom) */ export const AGENT_TILES: AgentTile[] = [ @@ -70,14 +70,14 @@ export const AGENT_TILES: AgentTile[] = [ description: "Factory's AI coding assistant", brandColor: '#3B82F6', // Factory blue }, - // Coming soon agents at the bottom { id: 'gemini-cli', name: 'Gemini CLI', - supported: false, - description: 'Coming soon', + supported: true, + description: "Google's AI coding assistant", brandColor: '#4285F4', // Google blue }, + // Coming soon agents at the bottom { id: 'qwen3-coder', name: 'Qwen3 Coder', From f944ba003881ea5f664a79edf50774cc83ec292d Mon Sep 17 00:00:00 2001 From: openasocket Date: Thu, 19 Feb 2026 23:52:52 -0500 Subject: [PATCH 05/45] MAESTRO: feat: add gemini-cli display name to sessionValidation and NewInstanceModal maps Co-Authored-By: Claude Opus 4.6 --- src/renderer/components/NewInstanceModal.tsx | 1 + src/renderer/utils/sessionValidation.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/renderer/components/NewInstanceModal.tsx b/src/renderer/components/NewInstanceModal.tsx index c7c505be1..292e62d92 100644 --- a/src/renderer/components/NewInstanceModal.tsx +++ b/src/renderer/components/NewInstanceModal.tsx @@ -1499,6 +1499,7 @@ export function EditAgentModal({ 'claude-code': 'Claude Code', codex: 'Codex', opencode: 'OpenCode', + 'gemini-cli': 'Gemini CLI', 'factory-droid': 'Factory Droid', }; const agentName = agentNameMap[session.toolType] || session.toolType; diff --git a/src/renderer/utils/sessionValidation.ts b/src/renderer/utils/sessionValidation.ts index d89858f13..63057086b 100644 --- a/src/renderer/utils/sessionValidation.ts +++ b/src/renderer/utils/sessionValidation.ts @@ -112,6 +112,7 @@ export function getProviderDisplayName(toolType: ToolType): string { 'claude-code': 'Claude Code', opencode: 'OpenCode', codex: 'Codex', + 'gemini-cli': 'Gemini CLI', 'factory-droid': 'Factory Droid', terminal: 'Terminal', }; From 649bdb4eff9d1114630e72d35b81762b9fd41a67 Mon Sep 17 00:00:00 2001 From: openasocket Date: Thu, 19 Feb 2026 23:58:13 -0500 Subject: [PATCH 06/45] MAESTRO: feat: add gemini-cli to all Record maps, error patterns, and hardcoded arrays Adds gemini-cli across the remaining codebase integration points: - Error patterns: GEMINI_ERROR_PATTERNS with auth, rate limit, token exhaustion, network, permission, and crash detection (7 patterns across 6 categories) - Context windows: 1048576 tokens (1M) in both usage-aggregator.ts and contextUsage.ts - Session storage: KNOWN_AGENT_IDS - Group chat: VALID_MODERATOR_AGENT_IDS - New Instance modal: SUPPORTED_AGENTS - Inline wizard: supportedWizardAgents (both locations) - Beta labels: isBeta checks in SettingsModal and EditGroupChatModal - Updated test: NewInstanceModal test for unsupported agent uses 'terminal' instead Co-Authored-By: Claude Opus 4.6 --- .../components/NewInstanceModal.test.tsx | 6 +- src/main/agents/session-storage.ts | 2 +- src/main/group-chat/group-chat-storage.ts | 2 +- src/main/parsers/error-patterns.ts | 66 +++++++++++++++++++ src/main/parsers/usage-aggregator.ts | 1 + .../components/EditGroupChatModal.tsx | 2 +- src/renderer/components/NewInstanceModal.tsx | 2 +- src/renderer/components/SettingsModal.tsx | 2 +- src/renderer/hooks/batch/useInlineWizard.ts | 4 +- src/renderer/utils/contextUsage.ts | 1 + 10 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/__tests__/renderer/components/NewInstanceModal.test.tsx b/src/__tests__/renderer/components/NewInstanceModal.test.tsx index 5c6a631fb..9c5eec0fc 100644 --- a/src/__tests__/renderer/components/NewInstanceModal.test.tsx +++ b/src/__tests__/renderer/components/NewInstanceModal.test.tsx @@ -1774,9 +1774,9 @@ describe('NewInstanceModal', () => { it('should have tabindex=-1 for unsupported agents (coming soon)', async () => { // Note: tabIndex is based on isSupported (in SUPPORTED_AGENTS), not availability - // gemini-cli is not in SUPPORTED_AGENTS so it should have tabIndex=-1 + // terminal is not in SUPPORTED_AGENTS so it should have tabIndex=-1 vi.mocked(window.maestro.agents.detect).mockResolvedValue([ - createAgentConfig({ id: 'gemini-cli', name: 'Gemini CLI', available: false }), + createAgentConfig({ id: 'terminal', name: 'Terminal', available: false }), ]); render( @@ -1790,7 +1790,7 @@ describe('NewInstanceModal', () => { ); await waitFor(() => { - const option = screen.getByRole('option', { name: /Gemini CLI/i }); + const option = screen.getByRole('option', { name: /Terminal/i }); expect(option).toHaveAttribute('tabIndex', '-1'); }); }); diff --git a/src/main/agents/session-storage.ts b/src/main/agents/session-storage.ts index 79c438f8f..60fce6595 100644 --- a/src/main/agents/session-storage.ts +++ b/src/main/agents/session-storage.ts @@ -22,7 +22,7 @@ const LOG_CONTEXT = '[AgentSessionStorage]'; /** * Known agent IDs that have session storage support */ -const KNOWN_AGENT_IDS: ToolType[] = ['claude-code', 'codex', 'opencode', 'factory-droid']; +const KNOWN_AGENT_IDS: ToolType[] = ['claude-code', 'codex', 'opencode', 'factory-droid', 'gemini-cli']; /** * Session origin types - indicates how the session was created diff --git a/src/main/group-chat/group-chat-storage.ts b/src/main/group-chat/group-chat-storage.ts index d9064c16a..4af82d041 100644 --- a/src/main/group-chat/group-chat-storage.ts +++ b/src/main/group-chat/group-chat-storage.ts @@ -21,7 +21,7 @@ import type { ModeratorConfig, GroupChatHistoryEntry } from '../../shared/group- * Valid agent IDs that can be used as moderators. * Must match available agents from agent-detector. */ -const VALID_MODERATOR_AGENT_IDS: ToolType[] = ['claude-code', 'codex', 'opencode', 'factory-droid']; +const VALID_MODERATOR_AGENT_IDS: ToolType[] = ['claude-code', 'codex', 'opencode', 'factory-droid', 'gemini-cli']; // --------------------------------------------------------------------------- // Write serialization & atomic file I/O diff --git a/src/main/parsers/error-patterns.ts b/src/main/parsers/error-patterns.ts index 4a47dd180..e7d3acabc 100644 --- a/src/main/parsers/error-patterns.ts +++ b/src/main/parsers/error-patterns.ts @@ -29,6 +29,7 @@ const VALID_TOOL_TYPES = new Set([ 'codex', 'terminal', 'factory-droid', + 'gemini-cli', ]); /** @@ -684,6 +685,70 @@ export const FACTORY_DROID_ERROR_PATTERNS: AgentErrorPatterns = { ], }; +// ============================================================================ +// Gemini CLI Error Patterns +// ============================================================================ + +export const GEMINI_ERROR_PATTERNS: AgentErrorPatterns = { + auth_expired: [ + { + pattern: /credentials.*expired|oauth.*expired|authentication.*failed|login.*required/i, + message: 'Gemini authentication expired. Run: gemini login', + recoverable: true, + }, + { + pattern: /GEMINI_API_KEY.*invalid|invalid.*api.?key/i, + message: 'Invalid Gemini API key. Check your GEMINI_API_KEY environment variable.', + recoverable: true, + }, + ], + + rate_limited: [ + { + pattern: /rate.?limit|too many requests|429|quota.*exceeded|resource.*exhausted/i, + message: 'Gemini API rate limit exceeded. Wait and retry.', + recoverable: true, + }, + ], + + token_exhaustion: [ + { + pattern: /turn.*limit.*exceeded|FatalTurnLimitedError|Maximum session turns exceeded/i, + message: 'Gemini turn limit exceeded. Start a new session.', + recoverable: false, + }, + { + pattern: /context.*length|token.*limit/i, + message: 'Gemini context length exceeded. Start a new session.', + recoverable: false, + }, + ], + + network_error: [ + { + pattern: /network.*error|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|fetch.*failed/i, + message: 'Network error. Check your internet connection.', + recoverable: true, + }, + ], + + permission_denied: [ + { + pattern: /path.*not.*in.*workspace|permission.*denied|sandbox/i, + message: 'Permission denied. File is outside the workspace sandbox.', + recoverable: false, + }, + ], + + agent_crashed: [ + { + pattern: /FatalInputError|FatalConfigError|unhandled.*exception|SIGKILL|SIGTERM/i, + message: 'Gemini CLI crashed unexpectedly. Check logs for details.', + recoverable: false, + }, + ], +}; + // ============================================================================ // SSH Error Patterns // ============================================================================ @@ -875,6 +940,7 @@ const patternRegistry = new Map([ ['opencode', OPENCODE_ERROR_PATTERNS], ['codex', CODEX_ERROR_PATTERNS], ['factory-droid', FACTORY_DROID_ERROR_PATTERNS], + ['gemini-cli', GEMINI_ERROR_PATTERNS], ]); /** diff --git a/src/main/parsers/usage-aggregator.ts b/src/main/parsers/usage-aggregator.ts index fdc1ad4e3..a73a9bda5 100644 --- a/src/main/parsers/usage-aggregator.ts +++ b/src/main/parsers/usage-aggregator.ts @@ -46,6 +46,7 @@ export const DEFAULT_CONTEXT_WINDOWS: Record = { codex: 200000, // OpenAI o3/o4-mini context window opencode: 128000, // OpenCode (depends on model, 128k is conservative default) 'factory-droid': 200000, // Factory Droid (varies by model, defaults to Claude Opus) + 'gemini-cli': 1048576, // Gemini CLI (Gemini 2.5 Pro 1M token context) terminal: 0, // Terminal has no context window }; diff --git a/src/renderer/components/EditGroupChatModal.tsx b/src/renderer/components/EditGroupChatModal.tsx index 13120e15c..3649a24c6 100644 --- a/src/renderer/components/EditGroupChatModal.tsx +++ b/src/renderer/components/EditGroupChatModal.tsx @@ -362,7 +362,7 @@ export function EditGroupChatModal({ > {availableTiles.map((tile) => { const isBeta = - tile.id === 'codex' || tile.id === 'opencode' || tile.id === 'factory-droid'; + tile.id === 'codex' || tile.id === 'opencode' || tile.id === 'factory-droid' || tile.id === 'gemini-cli'; return (