summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/core/src/core/prompts.test.ts166
-rw-r--r--packages/core/src/core/prompts.ts52
2 files changed, 202 insertions, 16 deletions
diff --git a/packages/core/src/core/prompts.test.ts b/packages/core/src/core/prompts.test.ts
index b994de98..99cf76c4 100644
--- a/packages/core/src/core/prompts.test.ts
+++ b/packages/core/src/core/prompts.test.ts
@@ -4,9 +4,13 @@
* 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 fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+import { GEMINI_CONFIG_DIR } from '../tools/memoryTool.js';
// Mock tool names if they are dynamically generated or complex
vi.mock('../tools/ls', () => ({ LSTool: { Name: 'list_directory' } }));
@@ -26,8 +30,15 @@ vi.mock('../tools/write-file', () => ({
vi.mock('../utils/gitUtils', () => ({
isGitRepository: vi.fn(),
}));
+vi.mock('node:fs');
describe('Core System Prompt (prompts.ts)', () => {
+ beforeEach(() => {
+ vi.resetAllMocks();
+ vi.stubEnv('GEMINI_SYSTEM_MD', undefined);
+ vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', undefined);
+ });
+
it('should return the base prompt when no userMemory is provided', () => {
vi.stubEnv('SANDBOX', undefined);
const prompt = getCoreSystemPrompt();
@@ -105,4 +116,157 @@ describe('Core System Prompt (prompts.ts)', () => {
expect(prompt).not.toContain('# Git Repository');
expect(prompt).toMatchSnapshot();
});
+
+ describe('GEMINI_SYSTEM_MD environment variable', () => {
+ it('should use default prompt when GEMINI_SYSTEM_MD is "false"', () => {
+ vi.stubEnv('GEMINI_SYSTEM_MD', 'false');
+ const prompt = getCoreSystemPrompt();
+ expect(fs.readFileSync).not.toHaveBeenCalled();
+ expect(prompt).not.toContain('custom system prompt');
+ });
+
+ it('should use default prompt when GEMINI_SYSTEM_MD is "0"', () => {
+ vi.stubEnv('GEMINI_SYSTEM_MD', '0');
+ const prompt = getCoreSystemPrompt();
+ expect(fs.readFileSync).not.toHaveBeenCalled();
+ expect(prompt).not.toContain('custom system prompt');
+ });
+
+ it('should throw error if GEMINI_SYSTEM_MD points to a non-existent file', () => {
+ const customPath = '/non/existent/path/system.md';
+ vi.stubEnv('GEMINI_SYSTEM_MD', customPath);
+ vi.mocked(fs.existsSync).mockReturnValue(false);
+ expect(() => getCoreSystemPrompt()).toThrow(
+ `missing system prompt file '${path.resolve(customPath)}'`,
+ );
+ });
+
+ it('should read from default path when GEMINI_SYSTEM_MD is "true"', () => {
+ const defaultPath = path.resolve(
+ path.join(GEMINI_CONFIG_DIR, 'system.md'),
+ );
+ vi.stubEnv('GEMINI_SYSTEM_MD', 'true');
+ vi.mocked(fs.existsSync).mockReturnValue(true);
+ vi.mocked(fs.readFileSync).mockReturnValue('custom system prompt');
+
+ const prompt = getCoreSystemPrompt();
+ expect(fs.readFileSync).toHaveBeenCalledWith(defaultPath, 'utf8');
+ expect(prompt).toBe('custom system prompt');
+ });
+
+ it('should read from default path when GEMINI_SYSTEM_MD is "1"', () => {
+ const defaultPath = path.resolve(
+ path.join(GEMINI_CONFIG_DIR, 'system.md'),
+ );
+ vi.stubEnv('GEMINI_SYSTEM_MD', '1');
+ vi.mocked(fs.existsSync).mockReturnValue(true);
+ vi.mocked(fs.readFileSync).mockReturnValue('custom system prompt');
+
+ const prompt = getCoreSystemPrompt();
+ expect(fs.readFileSync).toHaveBeenCalledWith(defaultPath, 'utf8');
+ expect(prompt).toBe('custom system prompt');
+ });
+
+ it('should read from custom path when GEMINI_SYSTEM_MD provides one, preserving case', () => {
+ const customPath = path.resolve('/custom/path/SyStEm.Md');
+ vi.stubEnv('GEMINI_SYSTEM_MD', customPath);
+ vi.mocked(fs.existsSync).mockReturnValue(true);
+ vi.mocked(fs.readFileSync).mockReturnValue('custom system prompt');
+
+ const prompt = getCoreSystemPrompt();
+ expect(fs.readFileSync).toHaveBeenCalledWith(customPath, 'utf8');
+ expect(prompt).toBe('custom system prompt');
+ });
+
+ it('should expand tilde in custom path when GEMINI_SYSTEM_MD is set', () => {
+ const homeDir = '/Users/test';
+ vi.spyOn(os, 'homedir').mockReturnValue(homeDir);
+ const customPath = '~/custom/system.md';
+ const expectedPath = path.join(homeDir, 'custom/system.md');
+ vi.stubEnv('GEMINI_SYSTEM_MD', customPath);
+ vi.mocked(fs.existsSync).mockReturnValue(true);
+ vi.mocked(fs.readFileSync).mockReturnValue('custom system prompt');
+
+ const prompt = getCoreSystemPrompt();
+ expect(fs.readFileSync).toHaveBeenCalledWith(
+ path.resolve(expectedPath),
+ 'utf8',
+ );
+ expect(prompt).toBe('custom system prompt');
+ });
+ });
+
+ describe('GEMINI_WRITE_SYSTEM_MD environment variable', () => {
+ it('should not write to file when GEMINI_WRITE_SYSTEM_MD is "false"', () => {
+ vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', 'false');
+ getCoreSystemPrompt();
+ expect(fs.writeFileSync).not.toHaveBeenCalled();
+ });
+
+ it('should not write to file when GEMINI_WRITE_SYSTEM_MD is "0"', () => {
+ vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', '0');
+ getCoreSystemPrompt();
+ expect(fs.writeFileSync).not.toHaveBeenCalled();
+ });
+
+ it('should write to default path when GEMINI_WRITE_SYSTEM_MD is "true"', () => {
+ const defaultPath = path.resolve(
+ path.join(GEMINI_CONFIG_DIR, 'system.md'),
+ );
+ vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', 'true');
+ getCoreSystemPrompt();
+ expect(fs.writeFileSync).toHaveBeenCalledWith(
+ defaultPath,
+ expect.any(String),
+ );
+ });
+
+ it('should write to default path when GEMINI_WRITE_SYSTEM_MD is "1"', () => {
+ const defaultPath = path.resolve(
+ path.join(GEMINI_CONFIG_DIR, 'system.md'),
+ );
+ vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', '1');
+ getCoreSystemPrompt();
+ expect(fs.writeFileSync).toHaveBeenCalledWith(
+ defaultPath,
+ expect.any(String),
+ );
+ });
+
+ it('should write to custom path when GEMINI_WRITE_SYSTEM_MD provides one', () => {
+ const customPath = path.resolve('/custom/path/system.md');
+ vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', customPath);
+ getCoreSystemPrompt();
+ expect(fs.writeFileSync).toHaveBeenCalledWith(
+ customPath,
+ expect.any(String),
+ );
+ });
+
+ it('should expand tilde in custom path when GEMINI_WRITE_SYSTEM_MD is set', () => {
+ const homeDir = '/Users/test';
+ vi.spyOn(os, 'homedir').mockReturnValue(homeDir);
+ const customPath = '~/custom/system.md';
+ const expectedPath = path.join(homeDir, 'custom/system.md');
+ vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', customPath);
+ getCoreSystemPrompt();
+ expect(fs.writeFileSync).toHaveBeenCalledWith(
+ path.resolve(expectedPath),
+ expect.any(String),
+ );
+ });
+
+ it('should expand tilde in custom path when GEMINI_WRITE_SYSTEM_MD is just ~', () => {
+ const homeDir = '/Users/test';
+ vi.spyOn(os, 'homedir').mockReturnValue(homeDir);
+ const customPath = '~';
+ const expectedPath = homeDir;
+ vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', customPath);
+ getCoreSystemPrompt();
+ expect(fs.writeFileSync).toHaveBeenCalledWith(
+ path.resolve(expectedPath),
+ expect.any(String),
+ );
+ });
+ });
});
diff --git a/packages/core/src/core/prompts.ts b/packages/core/src/core/prompts.ts
index 08df3ba2..b97264d7 100644
--- a/packages/core/src/core/prompts.ts
+++ b/packages/core/src/core/prompts.ts
@@ -6,6 +6,7 @@
import path from 'node:path';
import fs from 'node:fs';
+import os from 'node:os';
import { LSTool } from '../tools/ls.js';
import { EditTool } from '../tools/edit.js';
import { GlobTool } from '../tools/glob.js';
@@ -23,15 +24,24 @@ export function getCoreSystemPrompt(userMemory?: string): string {
// default path is .gemini/system.md but can be modified via custom path in GEMINI_SYSTEM_MD
let systemMdEnabled = false;
let systemMdPath = path.resolve(path.join(GEMINI_CONFIG_DIR, 'system.md'));
- const systemMdVar = process.env.GEMINI_SYSTEM_MD?.toLowerCase();
- if (systemMdVar && !['0', 'false'].includes(systemMdVar)) {
- systemMdEnabled = true; // enable system prompt override
- if (!['1', 'true'].includes(systemMdVar)) {
- systemMdPath = path.resolve(systemMdVar); // use custom path from GEMINI_SYSTEM_MD
- }
- // require file to exist when override is enabled
- if (!fs.existsSync(systemMdPath)) {
- throw new Error(`missing system prompt file '${systemMdPath}'`);
+ const systemMdVar = process.env.GEMINI_SYSTEM_MD;
+ if (systemMdVar) {
+ const systemMdVarLower = systemMdVar.toLowerCase();
+ if (!['0', 'false'].includes(systemMdVarLower)) {
+ systemMdEnabled = true; // enable system prompt override
+ if (!['1', 'true'].includes(systemMdVarLower)) {
+ let customPath = systemMdVar;
+ if (customPath.startsWith('~/')) {
+ customPath = path.join(os.homedir(), customPath.slice(2));
+ } else if (customPath === '~') {
+ customPath = os.homedir();
+ }
+ systemMdPath = path.resolve(customPath); // use custom path from GEMINI_SYSTEM_MD
+ }
+ // require file to exist when override is enabled
+ if (!fs.existsSync(systemMdPath)) {
+ throw new Error(`missing system prompt file '${systemMdPath}'`);
+ }
}
}
const basePrompt = systemMdEnabled
@@ -256,12 +266,24 @@ Your core function is efficient and safe assistance. Balance extreme conciseness
`.trim();
// if GEMINI_WRITE_SYSTEM_MD is set (and not 0|false), write base system prompt to file
- const writeSystemMdVar = process.env.GEMINI_WRITE_SYSTEM_MD?.toLowerCase();
- if (writeSystemMdVar && !['0', 'false'].includes(writeSystemMdVar)) {
- if (['1', 'true'].includes(writeSystemMdVar)) {
- fs.writeFileSync(systemMdPath, basePrompt); // write to default path, can be modified via GEMINI_SYSTEM_MD
- } else {
- fs.writeFileSync(path.resolve(writeSystemMdVar), basePrompt); // write to custom path from GEMINI_WRITE_SYSTEM_MD
+ const writeSystemMdVar = process.env.GEMINI_WRITE_SYSTEM_MD;
+ if (writeSystemMdVar) {
+ const writeSystemMdVarLower = writeSystemMdVar.toLowerCase();
+ if (!['0', 'false'].includes(writeSystemMdVarLower)) {
+ if (['1', 'true'].includes(writeSystemMdVarLower)) {
+ fs.mkdirSync(path.dirname(systemMdPath), { recursive: true });
+ fs.writeFileSync(systemMdPath, basePrompt); // write to default path, can be modified via GEMINI_SYSTEM_MD
+ } else {
+ let customPath = writeSystemMdVar;
+ if (customPath.startsWith('~/')) {
+ customPath = path.join(os.homedir(), customPath.slice(2));
+ } else if (customPath === '~') {
+ customPath = os.homedir();
+ }
+ const resolvedPath = path.resolve(customPath);
+ fs.mkdirSync(path.dirname(resolvedPath), { recursive: true });
+ fs.writeFileSync(resolvedPath, basePrompt); // write to custom path from GEMINI_WRITE_SYSTEM_MD
+ }
}
}