From fb8d89f3cd784fb9830fd7330ecf390af6f1b6da Mon Sep 17 00:00:00 2001 From: "A.K.M. Adib" Date: Tue, 27 Jan 2026 16:48:46 -0500 Subject: [PATCH] fix build issues and address comment by bot --- docs/cli/commands.md | 6 +++ .../cli/src/ui/commands/planCommand.test.ts | 37 ++++++++++++------- packages/cli/src/ui/commands/planCommand.ts | 11 ++++-- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/docs/cli/commands.md b/docs/cli/commands.md index 67f5afe7ae..0d020f9873 100644 --- a/docs/cli/commands.md +++ b/docs/cli/commands.md @@ -245,6 +245,12 @@ Slash commands provide meta-level control over the CLI itself. - **`nodesc`** or **`nodescriptions`**: - **Description:** Hide tool descriptions, showing only the tool names. +- **`/plan`** + - **Description:** Switch to Plan Mode (read-only) and view the current plan + if one has been generated. + - **Note:** This feature requires the `experimental.plan` setting to be + enabled in your configuration. + - **`/privacy`** - **Description:** Display the Privacy Notice and allow users to select whether they consent to the collection of their data for service improvement diff --git a/packages/cli/src/ui/commands/planCommand.test.ts b/packages/cli/src/ui/commands/planCommand.test.ts index 65670adb8b..bedf38a278 100644 --- a/packages/cli/src/ui/commands/planCommand.test.ts +++ b/packages/cli/src/ui/commands/planCommand.test.ts @@ -24,12 +24,15 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => { }); vi.mock('node:fs', () => ({ - existsSync: vi.fn(), promises: { + access: vi.fn(), readdir: vi.fn(), stat: vi.fn(), readFile: vi.fn(), }, + constants: { + F_OK: 0, + }, })); vi.mock('node:path', async (importOriginal) => { @@ -91,7 +94,7 @@ describe('planCommand', () => { it('should switch to plan mode if enabled', async () => { vi.mocked(mockContext.services.config!.isPlanEnabled).mockReturnValue(true); - vi.mocked(fs.existsSync).mockReturnValue(false); // No plans found case + vi.mocked(fs.promises.access).mockRejectedValue(new Error('No entry')); // No plans found case if (!planCommand.action) throw new Error('Action missing'); await planCommand.action(mockContext, ''); @@ -107,7 +110,7 @@ describe('planCommand', () => { it('should show "No plans found" if directory does not exist', async () => { vi.mocked(mockContext.services.config!.isPlanEnabled).mockReturnValue(true); - vi.mocked(fs.existsSync).mockReturnValue(false); + vi.mocked(fs.promises.access).mockRejectedValue(new Error('No entry')); if (!planCommand.action) throw new Error('Action missing'); await planCommand.action(mockContext, ''); @@ -120,10 +123,12 @@ describe('planCommand', () => { it('should show "No plans found" if directory is empty of .md files', async () => { vi.mocked(mockContext.services.config!.isPlanEnabled).mockReturnValue(true); - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fs.promises.readdir).mockImplementation( - async () => ['not-a-plan.txt'] as Array>, - ); + vi.mocked(fs.promises.access).mockResolvedValue(undefined); + vi.mocked( + fs.promises.readdir as unknown as ( + path: fs.PathLike, + ) => Promise, + ).mockResolvedValue(['not-a-plan.txt']); if (!planCommand.action) throw new Error('Action missing'); await planCommand.action(mockContext, ''); @@ -136,10 +141,12 @@ describe('planCommand', () => { it('should find and display the latest plan', async () => { vi.mocked(mockContext.services.config!.isPlanEnabled).mockReturnValue(true); - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fs.promises.readdir).mockImplementation( - async () => ['old-plan.md', 'new-plan.md'] as Array>, - ); + vi.mocked(fs.promises.access).mockResolvedValue(undefined); + vi.mocked( + fs.promises.readdir as unknown as ( + path: fs.PathLike, + ) => Promise, + ).mockResolvedValue(['old-plan.md', 'new-plan.md']); vi.mocked(fs.promises.stat).mockImplementation(async (filePath) => { const pathStr = filePath as string; @@ -173,8 +180,12 @@ describe('planCommand', () => { it('should handle errors when reading plans', async () => { vi.mocked(mockContext.services.config!.isPlanEnabled).mockReturnValue(true); - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fs.promises.readdir).mockRejectedValue(new Error('Read error')); + vi.mocked(fs.promises.access).mockResolvedValue(undefined); + vi.mocked( + fs.promises.readdir as unknown as ( + path: fs.PathLike, + ) => Promise, + ).mockRejectedValue(new Error('Read error')); if (!planCommand.action) throw new Error('Action missing'); await planCommand.action(mockContext, ''); diff --git a/packages/cli/src/ui/commands/planCommand.ts b/packages/cli/src/ui/commands/planCommand.ts index 1dab094df9..7946c0e25c 100644 --- a/packages/cli/src/ui/commands/planCommand.ts +++ b/packages/cli/src/ui/commands/planCommand.ts @@ -5,7 +5,7 @@ */ import { CommandKind, type SlashCommand } from './types.js'; -import { ApprovalMode, coreEvents } from '@google/gemini-cli-core'; +import { ApprovalMode, coreEvents, debugLogger } from '@google/gemini-cli-core'; import { MessageType } from '../types.js'; import * as fs from 'node:fs'; import * as path from 'node:path'; @@ -17,7 +17,10 @@ export const planCommand: SlashCommand = { autoExecute: true, action: async (context) => { const config = context.services.config; - if (!config) return; + if (!config) { + debugLogger.debug('Plan command: config is not available in context'); + return; + } // Check if plan mode is enabled if (!config.isPlanEnabled()) { @@ -36,7 +39,9 @@ export const planCommand: SlashCommand = { const plansDir = config.storage.getProjectTempPlansDir(); try { - if (!fs.existsSync(plansDir)) { + try { + await fs.promises.access(plansDir, fs.constants.F_OK); + } catch { coreEvents.emitFeedback('info', 'No plans found.'); return; }