From 99fa700231b0f003f3019cf9b8a3d2e0c28a8b46 Mon Sep 17 00:00:00 2001 From: Bryan Morgan Date: Thu, 19 Feb 2026 20:19:01 -0500 Subject: [PATCH 1/4] fix(ci): add fallback JSON extraction to issue triage workflow (#19593) --- .../workflows/gemini-automated-issue-triage.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gemini-automated-issue-triage.yml b/.github/workflows/gemini-automated-issue-triage.yml index 08b97db0a2..64609b5c3b 100644 --- a/.github/workflows/gemini-automated-issue-triage.yml +++ b/.github/workflows/gemini-automated-issue-triage.yml @@ -284,8 +284,21 @@ jobs: return; } } else { - core.setFailed(`Output is not valid JSON and does not contain a JSON markdown block.\nRaw output: ${rawOutput}`); - return; + // If no markdown block, try to find a raw JSON object in the output. + // The CLI may include debug/log lines (e.g. telemetry init, YOLO mode) + // before the actual JSON response. + const jsonObjectMatch = rawOutput.match(/(\{[\s\S]*"labels_to_set"[\s\S]*\})/); + if (jsonObjectMatch) { + try { + parsedLabels = JSON.parse(jsonObjectMatch[0]); + } catch (extractError) { + core.setFailed(`Found JSON-like content but failed to parse: ${extractError.message}\nRaw output: ${rawOutput}`); + return; + } + } else { + core.setFailed(`Output is not valid JSON and does not contain extractable JSON.\nRaw output: ${rawOutput}`); + return; + } } } From fb1b1b451d5165e256976c7fc2074c52681aafc7 Mon Sep 17 00:00:00 2001 From: Sandy Tao Date: Thu, 19 Feb 2026 17:03:10 -0800 Subject: [PATCH 2/4] feat(core): refine Edit and WriteFile tool schemas for Gemini 3 (#19476) --- .../coreToolsModelSnapshots.test.ts.snap | 37 +++---------------- .../definitions/model-family-sets/gemini-3.ts | 37 +++---------------- 2 files changed, 12 insertions(+), 62 deletions(-) diff --git a/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap b/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap index 8aa86f60a7..9767829f0e 100644 --- a/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap +++ b/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap @@ -1293,18 +1293,8 @@ Use this tool when the user's query implies needing the content of several files exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview > snapshot for tool: replace 1`] = ` { - "description": "Replaces text within a file. By default, replaces a single occurrence, but can replace multiple occurrences when \`expected_replacements\` is specified. This tool requires providing significant context around the change to ensure precise targeting. Always use the read_file tool to examine the file's current content before attempting a text replacement. - - The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response. - - Expectation for required parameters: - 1. \`old_string\` MUST be the exact literal text to replace (including all whitespace, indentation, newlines, and surrounding code etc.). - 2. \`new_string\` MUST be the exact literal text to replace \`old_string\` with (also including all whitespace, indentation, newlines, and surrounding code etc.). Ensure the resulting code is correct and idiomatic and that \`old_string\` and \`new_string\` are different. - 3. \`instruction\` is the detailed instruction of what needs to be changed. It is important to Make it specific and detailed so developers or large language models can understand what needs to be changed and perform the changes on their own if necessary. - 4. NEVER escape \`old_string\` or \`new_string\`, that would break the exact literal text requirement. - **Important:** If ANY of the above are not satisfied, the tool will fail. CRITICAL for \`old_string\`: Must uniquely identify the single instance to change. Include at least 3 lines of context BEFORE and AFTER the target text, matching whitespace and indentation precisely. If this string matches multiple locations, or does not match exactly, the tool will fail. - 5. Prefer to break down complex and long changes into multiple smaller atomic calls to this tool. Always check the content of the file after changes or not finding a string to match. - **Multiple replacements:** Set \`expected_replacements\` to the number of occurrences you want to replace. The tool will replace ALL occurrences that match \`old_string\` exactly. Ensure the number of replacements matches your expectation.", + "description": "Replaces text within a file. By default, replaces a single occurrence, but can replace multiple occurrences ONLY when \`expected_replacements\` is specified. This tool requires providing significant context around the change to ensure precise targeting. +The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response.", "name": "replace", "parametersJsonSchema": { "properties": { @@ -1318,29 +1308,15 @@ exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview > "type": "string", }, "instruction": { - "description": "A clear, semantic instruction for the code change, acting as a high-quality prompt for an expert LLM assistant. It must be self-contained and explain the goal of the change. - -A good instruction should concisely answer: -1. WHY is the change needed? (e.g., "To fix a bug where users can be null...") -2. WHERE should the change happen? (e.g., "...in the 'renderUserProfile' function...") -3. WHAT is the high-level change? (e.g., "...add a null check for the 'user' object...") -4. WHAT is the desired outcome? (e.g., "...so that it displays a loading spinner instead of crashing.") - -**GOOD Example:** "In the 'calculateTotal' function, correct the sales tax calculation by updating the 'taxRate' constant from 0.05 to 0.075 to reflect the new regional tax laws." - -**BAD Examples:** -- "Change the text." (Too vague) -- "Fix the bug." (Doesn't explain the bug or the fix) -- "Replace the line with this new line." (Brittle, just repeats the other parameters) -", + "description": "A clear, semantic instruction for the code change, acting as a high-quality prompt for an expert LLM assistant. It must be self-contained and explain the goal of the change.", "type": "string", }, "new_string": { - "description": "The exact literal text to replace \`old_string\` with, preferably unescaped. Provide the EXACT text. Ensure the resulting code is correct and idiomatic.", + "description": "The exact literal text to replace \`old_string\` with, unescaped. Provide the EXACT text. Ensure the resulting code is correct and idiomatic.", "type": "string", }, "old_string": { - "description": "The exact literal text to replace, preferably unescaped. For single replacements (default), include at least 3 lines of context BEFORE and AFTER the target text, matching whitespace and indentation precisely. If this string is not the exact literal text (i.e. you escaped it) or does not match exactly, the tool will fail.", + "description": "The exact literal text to replace, unescaped. If this string is not the exact literal text (i.e. you escaped it) or does not match exactly, the tool will fail.", "type": "string", }, }, @@ -1448,8 +1424,7 @@ exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview > exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview > snapshot for tool: write_file 1`] = ` { "description": "Writes content to a specified file in the local filesystem. - - The user has the ability to modify \`content\`. If modified, this will be stated in the response.", +The user has the ability to modify \`content\`. If modified, this will be stated in the response.", "name": "write_file", "parametersJsonSchema": { "properties": { diff --git a/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts b/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts index a532cac8ba..71e8aaec1c 100644 --- a/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts +++ b/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts @@ -64,8 +64,7 @@ export const GEMINI_3_SET: CoreToolSet = { write_file: { name: WRITE_FILE_TOOL_NAME, description: `Writes content to a specified file in the local filesystem. - - The user has the ability to modify \`content\`. If modified, this will be stated in the response.`, +The user has the ability to modify \`content\`. If modified, this will be stated in the response.`, parametersJsonSchema: { type: 'object', properties: { @@ -291,18 +290,8 @@ export const GEMINI_3_SET: CoreToolSet = { replace: { name: EDIT_TOOL_NAME, - description: `Replaces text within a file. By default, replaces a single occurrence, but can replace multiple occurrences when \`expected_replacements\` is specified. This tool requires providing significant context around the change to ensure precise targeting. Always use the ${READ_FILE_TOOL_NAME} tool to examine the file's current content before attempting a text replacement. - - The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response. - - Expectation for required parameters: - 1. \`old_string\` MUST be the exact literal text to replace (including all whitespace, indentation, newlines, and surrounding code etc.). - 2. \`new_string\` MUST be the exact literal text to replace \`old_string\` with (also including all whitespace, indentation, newlines, and surrounding code etc.). Ensure the resulting code is correct and idiomatic and that \`old_string\` and \`new_string\` are different. - 3. \`instruction\` is the detailed instruction of what needs to be changed. It is important to Make it specific and detailed so developers or large language models can understand what needs to be changed and perform the changes on their own if necessary. - 4. NEVER escape \`old_string\` or \`new_string\`, that would break the exact literal text requirement. - **Important:** If ANY of the above are not satisfied, the tool will fail. CRITICAL for \`old_string\`: Must uniquely identify the single instance to change. Include at least 3 lines of context BEFORE and AFTER the target text, matching whitespace and indentation precisely. If this string matches multiple locations, or does not match exactly, the tool will fail. - 5. Prefer to break down complex and long changes into multiple smaller atomic calls to this tool. Always check the content of the file after changes or not finding a string to match. - **Multiple replacements:** Set \`expected_replacements\` to the number of occurrences you want to replace. The tool will replace ALL occurrences that match \`old_string\` exactly. Ensure the number of replacements matches your expectation.`, + description: `Replaces text within a file. By default, replaces a single occurrence, but can replace multiple occurrences ONLY when \`expected_replacements\` is specified. This tool requires providing significant context around the change to ensure precise targeting. +The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response.`, parametersJsonSchema: { type: 'object', properties: { @@ -311,31 +300,17 @@ export const GEMINI_3_SET: CoreToolSet = { type: 'string', }, instruction: { - description: `A clear, semantic instruction for the code change, acting as a high-quality prompt for an expert LLM assistant. It must be self-contained and explain the goal of the change. - -A good instruction should concisely answer: -1. WHY is the change needed? (e.g., "To fix a bug where users can be null...") -2. WHERE should the change happen? (e.g., "...in the 'renderUserProfile' function...") -3. WHAT is the high-level change? (e.g., "...add a null check for the 'user' object...") -4. WHAT is the desired outcome? (e.g., "...so that it displays a loading spinner instead of crashing.") - -**GOOD Example:** "In the 'calculateTotal' function, correct the sales tax calculation by updating the 'taxRate' constant from 0.05 to 0.075 to reflect the new regional tax laws." - -**BAD Examples:** -- "Change the text." (Too vague) -- "Fix the bug." (Doesn't explain the bug or the fix) -- "Replace the line with this new line." (Brittle, just repeats the other parameters) -`, + description: `A clear, semantic instruction for the code change, acting as a high-quality prompt for an expert LLM assistant. It must be self-contained and explain the goal of the change.`, type: 'string', }, old_string: { description: - 'The exact literal text to replace, preferably unescaped. For single replacements (default), include at least 3 lines of context BEFORE and AFTER the target text, matching whitespace and indentation precisely. If this string is not the exact literal text (i.e. you escaped it) or does not match exactly, the tool will fail.', + 'The exact literal text to replace, unescaped. If this string is not the exact literal text (i.e. you escaped it) or does not match exactly, the tool will fail.', type: 'string', }, new_string: { description: - 'The exact literal text to replace `old_string` with, preferably unescaped. Provide the EXACT text. Ensure the resulting code is correct and idiomatic.', + 'The exact literal text to replace `old_string` with, unescaped. Provide the EXACT text. Ensure the resulting code is correct and idiomatic.', type: 'string', }, expected_replacements: { From cbfb2a4e2646989a5fed30ed3e6c5a7ffba7f0df Mon Sep 17 00:00:00 2001 From: gemini-cli-robot Date: Thu, 19 Feb 2026 20:10:05 -0500 Subject: [PATCH 3/4] Changelog for v0.30.0-preview.3 (#19585) Co-authored-by: gemini-cli-robot <224641728+gemini-cli-robot@users.noreply.github.com> Co-authored-by: Sam Roberts <158088236+g-samroberts@users.noreply.github.com> --- docs/changelogs/preview.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelogs/preview.md b/docs/changelogs/preview.md index e8ac4a2dc9..4cb6a3824b 100644 --- a/docs/changelogs/preview.md +++ b/docs/changelogs/preview.md @@ -1,4 +1,4 @@ -# Preview release: v0.30.0-preview.1 +# Preview release: v0.30.0-preview.3 Released: February 19, 2026 @@ -311,4 +311,4 @@ npm install -g @google/gemini-cli@preview [#19008](https://github.com/google-gemini/gemini-cli/pull/19008) **Full changelog**: -https://github.com/google-gemini/gemini-cli/compare/v0.29.0-preview.5...v0.30.0-preview.1 +https://github.com/google-gemini/gemini-cli/compare/v0.29.0-preview.5...v0.30.0-preview.3 From 5fd557347edf557230baf483a5a8f35e1c3f5e89 Mon Sep 17 00:00:00 2001 From: Adib234 <30782825+Adib234@users.noreply.github.com> Date: Thu, 19 Feb 2026 20:53:12 -0500 Subject: [PATCH 4/4] fix(plan): exclude EnterPlanMode tool from YOLO mode (#19570) --- docs/cli/plan-mode.md | 1 + docs/tools/planning.md | 2 ++ packages/core/src/config/config.test.ts | 38 ++++++++++++++++++++++++- packages/core/src/config/config.ts | 15 ++++++++-- 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/docs/cli/plan-mode.md b/docs/cli/plan-mode.md index b59b0c3198..03da2a6ac9 100644 --- a/docs/cli/plan-mode.md +++ b/docs/cli/plan-mode.md @@ -69,6 +69,7 @@ You can enter Plan Mode in three ways: 2. **Command:** Type `/plan` in the input box. 3. **Natural Language:** Ask the agent to "start a plan for...". The agent will then call the [`enter_plan_mode`] tool to switch modes. + - **Note:** This tool is not available when the CLI is in YOLO mode. ### The Planning Workflow diff --git a/docs/tools/planning.md b/docs/tools/planning.md index 686b27f058..458b172510 100644 --- a/docs/tools/planning.md +++ b/docs/tools/planning.md @@ -11,6 +11,8 @@ by the agent when you ask it to "start a plan" using natural language. In this mode, the agent is restricted to read-only tools to allow for safe exploration and planning. +> **Note:** This tool is not available when the CLI is in YOLO mode. + - **Tool name:** `enter_plan_mode` - **Display name:** Enter Plan Mode - **File:** `enter-plan-mode.ts` diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts index 1c8820f273..a899ee045f 100644 --- a/packages/core/src/config/config.test.ts +++ b/packages/core/src/config/config.test.ts @@ -1392,7 +1392,22 @@ describe('setApprovalMode with folder trust', () => { expect(updateSpy).toHaveBeenCalled(); }); - it('should not update system instruction when switching between non-Plan modes', () => { + it('should update system instruction when entering YOLO mode', () => { + const config = new Config(baseParams); + vi.spyOn(config, 'isTrustedFolder').mockReturnValue(true); + vi.spyOn(config, 'getToolRegistry').mockReturnValue({ + getTool: vi.fn().mockReturnValue(undefined), + unregisterTool: vi.fn(), + registerTool: vi.fn(), + } as Partial as ToolRegistry); + const updateSpy = vi.spyOn(config, 'updateSystemInstructionIfInitialized'); + + config.setApprovalMode(ApprovalMode.YOLO); + + expect(updateSpy).toHaveBeenCalled(); + }); + + it('should not update system instruction when switching between non-Plan/non-YOLO modes', () => { const config = new Config(baseParams); vi.spyOn(config, 'isTrustedFolder').mockReturnValue(true); const updateSpy = vi.spyOn(config, 'updateSystemInstructionIfInitialized'); @@ -2649,6 +2664,27 @@ describe('syncPlanModeTools', () => { expect(registeredTool).toBeUndefined(); }); + it('should NOT register EnterPlanModeTool when in YOLO mode, even if plan is enabled', async () => { + const config = new Config({ + ...baseParams, + approvalMode: ApprovalMode.YOLO, + plan: true, + }); + const registry = new ToolRegistry(config, config.getMessageBus()); + vi.spyOn(config, 'getToolRegistry').mockReturnValue(registry); + + const registerSpy = vi.spyOn(registry, 'registerTool'); + vi.spyOn(registry, 'getTool').mockReturnValue(undefined); + + config.syncPlanModeTools(); + + const { EnterPlanModeTool } = await import('../tools/enter-plan-mode.js'); + const registeredTool = registerSpy.mock.calls.find( + (call) => call[0] instanceof EnterPlanModeTool, + ); + expect(registeredTool).toBeUndefined(); + }); + it('should call geminiClient.setTools if initialized', async () => { const config = new Config(baseParams); const registry = new ToolRegistry(config, config.getMessageBus()); diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index fa32fd4d5f..406835310a 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -1793,7 +1793,11 @@ export class Config { const isPlanModeTransition = currentMode !== mode && (currentMode === ApprovalMode.PLAN || mode === ApprovalMode.PLAN); - if (isPlanModeTransition) { + const isYoloModeTransition = + currentMode !== mode && + (currentMode === ApprovalMode.YOLO || mode === ApprovalMode.YOLO); + + if (isPlanModeTransition || isYoloModeTransition) { this.syncPlanModeTools(); this.updateSystemInstructionIfInitialized(); } @@ -1803,8 +1807,13 @@ export class Config { * Synchronizes enter/exit plan mode tools based on current mode. */ syncPlanModeTools(): void { - const isPlanMode = this.getApprovalMode() === ApprovalMode.PLAN; const registry = this.getToolRegistry(); + if (!registry) { + return; + } + const approvalMode = this.getApprovalMode(); + const isPlanMode = approvalMode === ApprovalMode.PLAN; + const isYoloMode = approvalMode === ApprovalMode.YOLO; if (isPlanMode) { if (registry.getTool(ENTER_PLAN_MODE_TOOL_NAME)) { @@ -1817,7 +1826,7 @@ export class Config { if (registry.getTool(EXIT_PLAN_MODE_TOOL_NAME)) { registry.unregisterTool(EXIT_PLAN_MODE_TOOL_NAME); } - if (this.planEnabled) { + if (this.planEnabled && !isYoloMode) { if (!registry.getTool(ENTER_PLAN_MODE_TOOL_NAME)) { registry.registerTool(new EnterPlanModeTool(this, this.messageBus)); }