From 9924ede95557c93c4c0dfd7b3155047dba92fad4 Mon Sep 17 00:00:00 2001 From: Thomas Shephard Date: Wed, 7 Jan 2026 20:34:38 +0000 Subject: [PATCH 1/2] fix(core): use platform-specific shell commands in system prompt --- packages/core/src/core/prompts.test.ts | 39 +++++++++++++++++++++++++- packages/core/src/core/prompts.ts | 10 +++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/packages/core/src/core/prompts.test.ts b/packages/core/src/core/prompts.test.ts index ceb5019df87..0b4470c982e 100644 --- a/packages/core/src/core/prompts.test.ts +++ b/packages/core/src/core/prompts.test.ts @@ -4,12 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { getCoreSystemPrompt, resolvePathFromEnv } from './prompts.js'; import { isGitRepository } from '../utils/gitUtils.js'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; +import process from 'node:process'; import type { Config } from '../config/config.js'; import { CodebaseInvestigatorAgent } from '../agents/codebase-investigator.js'; import { GEMINI_DIR } from '../utils/paths.js'; @@ -52,10 +53,16 @@ vi.mock('../config/models.js', async (importOriginal) => { describe('Core System Prompt (prompts.ts)', () => { let mockConfig: Config; + const originalPlatform = process.platform; + beforeEach(() => { vi.resetAllMocks(); vi.stubEnv('GEMINI_SYSTEM_MD', undefined); vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', undefined); + Object.defineProperty(process, 'platform', { + value: 'linux', + configurable: true, + }); mockConfig = { getToolRegistry: vi.fn().mockReturnValue({ getAllToolNames: vi.fn().mockReturnValue([]), @@ -78,6 +85,36 @@ describe('Core System Prompt (prompts.ts)', () => { } as unknown as Config; }); + afterEach(() => { + Object.defineProperty(process, 'platform', { + value: originalPlatform, + configurable: true, + }); + }); + + it('should use Windows-specific commands when platform is win32', () => { + Object.defineProperty(process, 'platform', { + value: 'win32', + configurable: true, + }); + vi.mocked(isGitRepository).mockReturnValue(true); + const prompt = getCoreSystemPrompt(mockConfig); + expect(prompt).toContain("'Select-String'"); + expect(prompt).toContain("'Get-Content -Tail 10'"); + expect(prompt).toContain("'Get-Content -TotalCount 10'"); + expect(prompt).toContain('git status ; git diff HEAD ; git log -n 3'); + }); + + it('should use Linux-specific commands when platform is linux', () => { + // Platform is already mocked to linux in beforeEach + vi.mocked(isGitRepository).mockReturnValue(true); + const prompt = getCoreSystemPrompt(mockConfig); + expect(prompt).toContain("'grep'"); + expect(prompt).toContain("'tail'"); + expect(prompt).toContain("'head'"); + expect(prompt).toContain('git status && git diff HEAD && git log -n 3'); + }); + it('should include available_skills when provided in config', () => { const skills = [ { diff --git a/packages/core/src/core/prompts.ts b/packages/core/src/core/prompts.ts index 243582494f1..7a254789024 100644 --- a/packages/core/src/core/prompts.ts +++ b/packages/core/src/core/prompts.ts @@ -130,6 +130,12 @@ export function getCoreSystemPrompt( const interactiveMode = config.isInteractive(); + const isWindows = process.platform === 'win32'; + const commandSeparator = isWindows ? ';' : '&&'; + const grepCommand = isWindows ? 'Select-String' : 'grep'; + const tailCommand = isWindows ? 'Get-Content -Tail 10' : 'tail'; + const headCommand = isWindows ? 'Get-Content -TotalCount 10' : 'head'; + const skills = config.getSkillManager().getSkills(); let skillsPrompt = ''; if (skills.length > 0) { @@ -260,7 +266,7 @@ IT IS CRITICAL TO FOLLOW THESE GUIDELINES TO AVOID EXCESSIVE TOKEN CONSUMPTION. - If a command is expected to produce a lot of output, use quiet or silent flags where available and appropriate. - Always consider the trade-off between output verbosity and the need for information. If a command's full output is essential for understanding the result, avoid overly aggressive quieting that might obscure important details. - If a command does not have quiet/silent flags or for commands with potentially long output that may not be useful, redirect stdout and stderr to temp files in the project's temporary directory. For example: 'command > /out.log 2> /err.log'. -- After the command runs, inspect the temp files (e.g. '/out.log' and '/err.log') using commands like 'grep', 'tail', 'head', ... (or platform equivalents). Remove the temp files when done. +- After the command runs, inspect the temp files (e.g. '/out.log' and '/err.log') using commands like '${grepCommand}', '${tailCommand}', '${headCommand}', ... (or platform equivalents). Remove the temp files when done. `; } return ''; @@ -337,7 +343,7 @@ ${(function () { - \`git diff HEAD\` to review all changes (including unstaged changes) to tracked files in work tree since last commit. - \`git diff --staged\` to review only staged changes when a partial commit makes sense or was requested by the user. - \`git log -n 3\` to review recent commit messages and match their style (verbosity, formatting, signature line, etc.) -- Combine shell commands whenever possible to save time/steps, e.g. \`git status && git diff HEAD && git log -n 3\`. +- Combine shell commands whenever possible to save time/steps, e.g. \`git status ${commandSeparator} git diff HEAD ${commandSeparator} git log -n 3\`. - Always propose a draft commit message. Never just ask the user to give you the full commit message. - Prefer commit messages that are clear, concise, and focused more on "why" and less on "what".${ interactiveMode From f9c8ea25c3d03d7d2d573fd9bbb8d954b05dca2b Mon Sep 17 00:00:00 2001 From: Thomas Shephard Date: Thu, 8 Jan 2026 08:58:29 +0000 Subject: [PATCH 2/2] Change tests to mock platform --- packages/core/src/core/prompts.test.ts | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/packages/core/src/core/prompts.test.ts b/packages/core/src/core/prompts.test.ts index 0b4470c982e..93e71359765 100644 --- a/packages/core/src/core/prompts.test.ts +++ b/packages/core/src/core/prompts.test.ts @@ -53,16 +53,12 @@ vi.mock('../config/models.js', async (importOriginal) => { describe('Core System Prompt (prompts.ts)', () => { let mockConfig: Config; - const originalPlatform = process.platform; beforeEach(() => { vi.resetAllMocks(); vi.stubEnv('GEMINI_SYSTEM_MD', undefined); vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', undefined); - Object.defineProperty(process, 'platform', { - value: 'linux', - configurable: true, - }); + vi.spyOn(process, 'platform', 'get').mockReturnValue('linux'); mockConfig = { getToolRegistry: vi.fn().mockReturnValue({ getAllToolNames: vi.fn().mockReturnValue([]), @@ -86,17 +82,11 @@ describe('Core System Prompt (prompts.ts)', () => { }); afterEach(() => { - Object.defineProperty(process, 'platform', { - value: originalPlatform, - configurable: true, - }); + vi.restoreAllMocks(); }); it('should use Windows-specific commands when platform is win32', () => { - Object.defineProperty(process, 'platform', { - value: 'win32', - configurable: true, - }); + vi.spyOn(process, 'platform', 'get').mockReturnValue('win32'); vi.mocked(isGitRepository).mockReturnValue(true); const prompt = getCoreSystemPrompt(mockConfig); expect(prompt).toContain("'Select-String'"); @@ -106,7 +96,7 @@ describe('Core System Prompt (prompts.ts)', () => { }); it('should use Linux-specific commands when platform is linux', () => { - // Platform is already mocked to linux in beforeEach + vi.spyOn(process, 'platform', 'get').mockReturnValue('linux'); vi.mocked(isGitRepository).mockReturnValue(true); const prompt = getCoreSystemPrompt(mockConfig); expect(prompt).toContain("'grep'");