summaryrefslogtreecommitdiff
path: root/packages/core/src
diff options
context:
space:
mode:
authorchristine betts <[email protected]>2025-07-15 22:20:00 +0000
committerGitHub <[email protected]>2025-07-15 22:20:00 +0000
commit222e362fc2b390ae8259de8d9507b8c239fb59e4 (patch)
tree9a9152c66aa1cecf06efc46fa916e3a2c28810e8 /packages/core/src
parentb61016f2a513fb721125dec9b4bfe128f92890cb (diff)
[ide-mode] Thread active file through to system prompt (#4264)
Diffstat (limited to 'packages/core/src')
-rw-r--r--packages/core/src/core/client.test.ts6
-rw-r--r--packages/core/src/core/client.ts6
-rw-r--r--packages/core/src/core/prompts.test.ts35
-rw-r--r--packages/core/src/core/prompts.ts27
4 files changed, 56 insertions, 18 deletions
diff --git a/packages/core/src/core/client.test.ts b/packages/core/src/core/client.test.ts
index 03793bda..1f9d4e31 100644
--- a/packages/core/src/core/client.test.ts
+++ b/packages/core/src/core/client.test.ts
@@ -346,7 +346,7 @@ describe('Gemini Client (client.ts)', () => {
model: 'test-model',
config: {
abortSignal,
- systemInstruction: getCoreSystemPrompt(''),
+ systemInstruction: getCoreSystemPrompt(client['config'], ''),
temperature: 0.5,
topP: 1,
},
@@ -374,7 +374,7 @@ describe('Gemini Client (client.ts)', () => {
model: 'test-model', // Should use current model from config
config: {
abortSignal,
- systemInstruction: getCoreSystemPrompt(''),
+ systemInstruction: getCoreSystemPrompt(client['config'], ''),
temperature: 0,
topP: 1,
responseSchema: schema,
@@ -409,7 +409,7 @@ describe('Gemini Client (client.ts)', () => {
model: customModel,
config: {
abortSignal,
- systemInstruction: getCoreSystemPrompt(''),
+ systemInstruction: getCoreSystemPrompt(client['config'], ''),
temperature: 0.9,
topP: 1, // from default
topK: 20,
diff --git a/packages/core/src/core/client.ts b/packages/core/src/core/client.ts
index 80979812..1163a2f0 100644
--- a/packages/core/src/core/client.ts
+++ b/packages/core/src/core/client.ts
@@ -238,7 +238,7 @@ export class GeminiClient {
];
try {
const userMemory = this.config.getUserMemory();
- const systemInstruction = getCoreSystemPrompt(userMemory);
+ const systemInstruction = getCoreSystemPrompt(this.config, userMemory);
const generateContentConfigWithThinking = isThinkingSupported(
this.config.getModel(),
)
@@ -354,7 +354,7 @@ export class GeminiClient {
model || this.config.getModel() || DEFAULT_GEMINI_FLASH_MODEL;
try {
const userMemory = this.config.getUserMemory();
- const systemInstruction = getCoreSystemPrompt(userMemory);
+ const systemInstruction = getCoreSystemPrompt(this.config, userMemory);
const requestConfig = {
abortSignal,
...this.generateContentConfig,
@@ -447,7 +447,7 @@ export class GeminiClient {
try {
const userMemory = this.config.getUserMemory();
- const systemInstruction = getCoreSystemPrompt(userMemory);
+ const systemInstruction = getCoreSystemPrompt(this.config, userMemory);
const requestConfig = {
abortSignal,
diff --git a/packages/core/src/core/prompts.test.ts b/packages/core/src/core/prompts.test.ts
index bb7b0b52..6a7f0f8a 100644
--- a/packages/core/src/core/prompts.test.ts
+++ b/packages/core/src/core/prompts.test.ts
@@ -4,9 +4,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { describe, it, expect, vi } from 'vitest';
+import { describe, it, expect, vi, beforeEach } from 'vitest';
import { getCoreSystemPrompt } from './prompts.js';
import { isGitRepository } from '../utils/gitUtils.js';
+import { Config } from '../config/config.js';
// Mock tool names if they are dynamically generated or complex
vi.mock('../tools/ls', () => ({ LSTool: { Name: 'list_directory' } }));
@@ -26,11 +27,25 @@ vi.mock('../tools/write-file', () => ({
vi.mock('../utils/gitUtils', () => ({
isGitRepository: vi.fn(),
}));
+vi.mock('../config/config.js');
describe('Core System Prompt (prompts.ts)', () => {
+ let mockConfig: Config;
+
+ beforeEach(() => {
+ const MockedConfig = vi.mocked(Config, true);
+ MockedConfig.mockImplementation(() => {
+ const mock = {
+ getIdeMode: vi.fn().mockReturnValue(false),
+ };
+ return mock as unknown as Config;
+ });
+ mockConfig = new Config({} as never);
+ });
+
it('should return the base prompt when no userMemory is provided', () => {
vi.stubEnv('SANDBOX', undefined);
- const prompt = getCoreSystemPrompt();
+ const prompt = getCoreSystemPrompt(mockConfig);
expect(prompt).not.toContain('---\n\n'); // Separator should not be present
expect(prompt).toContain('You are an interactive CLI agent'); // Check for core content
expect(prompt).toMatchSnapshot(); // Use snapshot for base prompt structure
@@ -38,7 +53,7 @@ describe('Core System Prompt (prompts.ts)', () => {
it('should return the base prompt when userMemory is empty string', () => {
vi.stubEnv('SANDBOX', undefined);
- const prompt = getCoreSystemPrompt('');
+ const prompt = getCoreSystemPrompt(mockConfig, '');
expect(prompt).not.toContain('---\n\n');
expect(prompt).toContain('You are an interactive CLI agent');
expect(prompt).toMatchSnapshot();
@@ -46,7 +61,7 @@ describe('Core System Prompt (prompts.ts)', () => {
it('should return the base prompt when userMemory is whitespace only', () => {
vi.stubEnv('SANDBOX', undefined);
- const prompt = getCoreSystemPrompt(' \n \t ');
+ const prompt = getCoreSystemPrompt(mockConfig, ' \n \t ');
expect(prompt).not.toContain('---\n\n');
expect(prompt).toContain('You are an interactive CLI agent');
expect(prompt).toMatchSnapshot();
@@ -56,7 +71,7 @@ describe('Core System Prompt (prompts.ts)', () => {
vi.stubEnv('SANDBOX', undefined);
const memory = 'This is custom user memory.\nBe extra polite.';
const expectedSuffix = `\n\n---\n\n${memory}`;
- const prompt = getCoreSystemPrompt(memory);
+ const prompt = getCoreSystemPrompt(mockConfig, memory);
expect(prompt.endsWith(expectedSuffix)).toBe(true);
expect(prompt).toContain('You are an interactive CLI agent'); // Ensure base prompt follows
@@ -65,7 +80,7 @@ describe('Core System Prompt (prompts.ts)', () => {
it('should include sandbox-specific instructions when SANDBOX env var is set', () => {
vi.stubEnv('SANDBOX', 'true'); // Generic sandbox value
- const prompt = getCoreSystemPrompt();
+ const prompt = getCoreSystemPrompt(mockConfig);
expect(prompt).toContain('# Sandbox');
expect(prompt).not.toContain('# MacOS Seatbelt');
expect(prompt).not.toContain('# Outside of Sandbox');
@@ -74,7 +89,7 @@ describe('Core System Prompt (prompts.ts)', () => {
it('should include seatbelt-specific instructions when SANDBOX env var is "sandbox-exec"', () => {
vi.stubEnv('SANDBOX', 'sandbox-exec');
- const prompt = getCoreSystemPrompt();
+ const prompt = getCoreSystemPrompt(mockConfig);
expect(prompt).toContain('# MacOS Seatbelt');
expect(prompt).not.toContain('# Sandbox');
expect(prompt).not.toContain('# Outside of Sandbox');
@@ -83,7 +98,7 @@ describe('Core System Prompt (prompts.ts)', () => {
it('should include non-sandbox instructions when SANDBOX env var is not set', () => {
vi.stubEnv('SANDBOX', undefined); // Ensure it's not set
- const prompt = getCoreSystemPrompt();
+ const prompt = getCoreSystemPrompt(mockConfig);
expect(prompt).toContain('# Outside of Sandbox');
expect(prompt).not.toContain('# Sandbox');
expect(prompt).not.toContain('# MacOS Seatbelt');
@@ -93,7 +108,7 @@ describe('Core System Prompt (prompts.ts)', () => {
it('should include git instructions when in a git repo', () => {
vi.stubEnv('SANDBOX', undefined);
vi.mocked(isGitRepository).mockReturnValue(true);
- const prompt = getCoreSystemPrompt();
+ const prompt = getCoreSystemPrompt(mockConfig);
expect(prompt).toContain('# Git Repository');
expect(prompt).toMatchSnapshot();
});
@@ -101,7 +116,7 @@ describe('Core System Prompt (prompts.ts)', () => {
it('should not include git instructions when not in a git repo', () => {
vi.stubEnv('SANDBOX', undefined);
vi.mocked(isGitRepository).mockReturnValue(false);
- const prompt = getCoreSystemPrompt();
+ const prompt = getCoreSystemPrompt(mockConfig);
expect(prompt).not.toContain('# Git Repository');
expect(prompt).toMatchSnapshot();
});
diff --git a/packages/core/src/core/prompts.ts b/packages/core/src/core/prompts.ts
index 3b23f735..4a0c7c6c 100644
--- a/packages/core/src/core/prompts.ts
+++ b/packages/core/src/core/prompts.ts
@@ -17,8 +17,13 @@ import { WriteFileTool } from '../tools/write-file.js';
import process from 'node:process';
import { isGitRepository } from '../utils/gitUtils.js';
import { MemoryTool, GEMINI_CONFIG_DIR } from '../tools/memoryTool.js';
+import { Config } from '../config/config.js';
+import { ideContext } from '../services/ideContext.js';
-export function getCoreSystemPrompt(userMemory?: string): string {
+export function getCoreSystemPrompt(
+ config: Config,
+ userMemory?: string,
+): string {
// if GEMINI_SYSTEM_MD is set (and not 0|false), override system prompt from file
// default path is .gemini/system.md but can be modified via custom path in GEMINI_SYSTEM_MD
let systemMdEnabled = false;
@@ -153,7 +158,25 @@ ${(function () {
}
return '';
})()}
-
+${(function () {
+ if (config.getIdeMode()) {
+ const activeFile = ideContext.getActiveFileContext();
+ if (activeFile?.filePath) {
+ let prompt = `
+# IDE Mode
+You are running in IDE mode. The user has the following file open:
+- Path: ${activeFile.filePath}`;
+ if (activeFile.cursor) {
+ prompt += `
+- Cursor Position: Line ${activeFile.cursor.line}, Character ${activeFile.cursor.character}`;
+ }
+ prompt += `
+Focus on providing contextually relevant assistance for this file.`;
+ return prompt;
+ }
+ }
+ return '';
+})()}
# Examples (Illustrating Tone and Workflow)
<example>
user: 1 + 2