feat(plan): add persistent plan file storage (#17563)

This commit is contained in:
Jerop Kipruto
2026-01-26 16:57:27 -05:00
committed by GitHub
parent 018dc0d5cf
commit 13bc5f620c
11 changed files with 238 additions and 5 deletions
+27 -2
View File
@@ -47,6 +47,7 @@ import {
} from '../test-utils/mock-message-bus.js';
const rootDir = path.resolve(os.tmpdir(), 'gemini-cli-test-root');
const plansDir = path.resolve(os.tmpdir(), 'gemini-cli-test-plans');
// --- MOCKS ---
vi.mock('../core/client.js');
@@ -84,7 +85,7 @@ const mockConfigInternal = {
getBaseLlmClient: vi.fn(), // Initialize as a plain mock function
getFileSystemService: () => fsService,
getIdeMode: vi.fn(() => false),
getWorkspaceContext: () => new WorkspaceContext(rootDir),
getWorkspaceContext: () => new WorkspaceContext(rootDir, [plansDir]),
getApiKey: () => 'test-key',
getModel: () => 'test-model',
getSandbox: () => false,
@@ -126,10 +127,13 @@ describe('WriteFileTool', () => {
tempDir = fs.mkdtempSync(
path.join(os.tmpdir(), 'write-file-test-external-'),
);
// Ensure the rootDir for the tool exists
// Ensure the rootDir and plansDir for the tool exists
if (!fs.existsSync(rootDir)) {
fs.mkdirSync(rootDir, { recursive: true });
}
if (!fs.existsSync(plansDir)) {
fs.mkdirSync(plansDir, { recursive: true });
}
// Setup GeminiClient mock
mockGeminiClientInstance = new (vi.mocked(GeminiClient))(
@@ -206,6 +210,9 @@ describe('WriteFileTool', () => {
if (fs.existsSync(rootDir)) {
fs.rmSync(rootDir, { recursive: true, force: true });
}
if (fs.existsSync(plansDir)) {
fs.rmSync(plansDir, { recursive: true, force: true });
}
vi.clearAllMocks();
});
@@ -813,6 +820,24 @@ describe('WriteFileTool', () => {
/File path must be within one of the workspace directories/,
);
});
it('should allow paths within the plans directory', () => {
const params = {
file_path: path.join(plansDir, 'my-plan.md'),
content: '# My Plan',
};
expect(() => tool.build(params)).not.toThrow();
});
it('should reject paths that try to escape the plans directory', () => {
const params = {
file_path: path.join(plansDir, '..', 'escaped.txt'),
content: 'malicious',
};
expect(() => tool.build(params)).toThrow(
/File path must be within one of the workspace directories/,
);
});
});
describe('specific error types for write failures', () => {