mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-16 14:27:24 -07:00
Merge branch 'main' into restart-resume
This commit is contained in:
@@ -172,7 +172,7 @@ describe('GeminiAgent', () => {
|
||||
unsubscribe: vi.fn(),
|
||||
}),
|
||||
getApprovalMode: vi.fn().mockReturnValue('default'),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(false),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(true),
|
||||
getGemini31LaunchedSync: vi.fn().mockReturnValue(false),
|
||||
getHasAccessToPreviewModel: vi.fn().mockReturnValue(false),
|
||||
getCheckpointingEnabled: vi.fn().mockReturnValue(false),
|
||||
@@ -650,7 +650,7 @@ describe('Session', () => {
|
||||
getMessageBus: vi.fn().mockReturnValue(mockMessageBus),
|
||||
setApprovalMode: vi.fn(),
|
||||
setModel: vi.fn(),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(false),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(true),
|
||||
getCheckpointingEnabled: vi.fn().mockReturnValue(false),
|
||||
getGitService: vi.fn().mockResolvedValue({} as GitService),
|
||||
waitForMcpInit: vi.fn(),
|
||||
|
||||
@@ -92,7 +92,7 @@ describe('GeminiAgent Session Resume', () => {
|
||||
getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
|
||||
},
|
||||
getApprovalMode: vi.fn().mockReturnValue('default'),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(false),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(true),
|
||||
getModel: vi.fn().mockReturnValue('gemini-pro'),
|
||||
getHasAccessToPreviewModel: vi.fn().mockReturnValue(false),
|
||||
getGemini31LaunchedSync: vi.fn().mockReturnValue(false),
|
||||
@@ -204,6 +204,11 @@ describe('GeminiAgent Session Resume', () => {
|
||||
name: 'YOLO',
|
||||
description: 'Auto-approves all tools',
|
||||
},
|
||||
{
|
||||
id: ApprovalMode.PLAN,
|
||||
name: 'Plan',
|
||||
description: 'Read-only mode',
|
||||
},
|
||||
],
|
||||
currentModeId: ApprovalMode.DEFAULT,
|
||||
},
|
||||
|
||||
@@ -2622,13 +2622,13 @@ describe('loadCliConfig approval mode', () => {
|
||||
expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT);
|
||||
});
|
||||
|
||||
it('should throw error when --approval-mode=plan is used but experimental.plan setting is missing', async () => {
|
||||
it('should allow plan approval mode by default when --approval-mode=plan is used', async () => {
|
||||
process.argv = ['node', 'script.js', '--approval-mode', 'plan'];
|
||||
const argv = await parseArguments(createTestMergedSettings());
|
||||
const settings = createTestMergedSettings({});
|
||||
|
||||
const config = await loadCliConfig(settings, 'test-session', argv);
|
||||
expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT);
|
||||
expect(config.getApprovalMode()).toBe(ApprovalMode.PLAN);
|
||||
});
|
||||
|
||||
it('should pass planSettings.directory from settings to config', async () => {
|
||||
|
||||
@@ -424,12 +424,10 @@ describe('SettingsSchema', () => {
|
||||
expect(setting).toBeDefined();
|
||||
expect(setting.type).toBe('boolean');
|
||||
expect(setting.category).toBe('Experimental');
|
||||
expect(setting.default).toBe(false);
|
||||
expect(setting.default).toBe(true);
|
||||
expect(setting.requiresRestart).toBe(true);
|
||||
expect(setting.showInDialog).toBe(true);
|
||||
expect(setting.description).toBe(
|
||||
'Enable planning features (Plan Mode and tools).',
|
||||
);
|
||||
expect(setting.description).toBe('Enable Plan Mode.');
|
||||
});
|
||||
|
||||
it('should have hooksConfig.notifications setting in schema', () => {
|
||||
|
||||
@@ -1823,8 +1823,8 @@ const SETTINGS_SCHEMA = {
|
||||
label: 'Plan',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: false,
|
||||
description: 'Enable planning features (Plan Mode and tools).',
|
||||
default: true,
|
||||
description: 'Enable Plan Mode.',
|
||||
showInDialog: true,
|
||||
},
|
||||
taskTracker: {
|
||||
|
||||
@@ -151,7 +151,7 @@ describe('BuiltinCommandLoader', () => {
|
||||
vi.clearAllMocks();
|
||||
mockConfig = {
|
||||
getFolderTrust: vi.fn().mockReturnValue(true),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(false),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(true),
|
||||
getEnableExtensionReloading: () => false,
|
||||
getEnableHooks: () => false,
|
||||
getEnableHooksUI: () => false,
|
||||
@@ -351,7 +351,7 @@ describe('BuiltinCommandLoader profile', () => {
|
||||
vi.resetModules();
|
||||
mockConfig = {
|
||||
getFolderTrust: vi.fn().mockReturnValue(false),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(false),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(true),
|
||||
getCheckpointingEnabled: () => false,
|
||||
getEnableExtensionReloading: () => false,
|
||||
getEnableHooks: () => false,
|
||||
|
||||
@@ -131,4 +131,12 @@ describe('compressCommand', () => {
|
||||
await compressCommand.action!(context, '');
|
||||
expect(context.ui.setPendingItem).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
describe('metadata', () => {
|
||||
it('should have the correct name and aliases', () => {
|
||||
expect(compressCommand.name).toBe('compress');
|
||||
expect(compressCommand.altNames).toContain('summarize');
|
||||
expect(compressCommand.altNames).toContain('compact');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ import { CommandKind } from './types.js';
|
||||
|
||||
export const compressCommand: SlashCommand = {
|
||||
name: 'compress',
|
||||
altNames: ['summarize'],
|
||||
altNames: ['summarize', 'compact'],
|
||||
description: 'Compresses the context by replacing it with a summary',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
|
||||
@@ -110,4 +110,28 @@ describe('toolsCommand', () => {
|
||||
);
|
||||
expect(message.tools[1].description).toBe('Edits code files.');
|
||||
});
|
||||
|
||||
it('should expose a desc subcommand for TUI discoverability', async () => {
|
||||
const descSubCommand = toolsCommand.subCommands?.find(
|
||||
(cmd) => cmd.name === 'desc',
|
||||
);
|
||||
expect(descSubCommand).toBeDefined();
|
||||
expect(descSubCommand?.description).toContain('descriptions');
|
||||
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: {
|
||||
getToolRegistry: () => ({ getAllTools: () => mockTools }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!descSubCommand?.action) throw new Error('Action not defined');
|
||||
await descSubCommand.action(mockContext, '');
|
||||
|
||||
const [message] = (mockContext.ui.addItem as ReturnType<typeof vi.fn>).mock
|
||||
.calls[0];
|
||||
expect(message.type).toBe(MessageType.TOOLS_LIST);
|
||||
expect(message.showDescriptions).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,43 +11,60 @@ import {
|
||||
} from './types.js';
|
||||
import { MessageType, type HistoryItemToolsList } from '../types.js';
|
||||
|
||||
async function listTools(
|
||||
context: CommandContext,
|
||||
showDescriptions: boolean,
|
||||
): Promise<void> {
|
||||
const toolRegistry = context.services.config?.getToolRegistry();
|
||||
if (!toolRegistry) {
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve tool registry.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const tools = toolRegistry.getAllTools();
|
||||
// Filter out MCP tools by checking for the absence of a serverName property
|
||||
const geminiTools = tools.filter((tool) => !('serverName' in tool));
|
||||
|
||||
const toolsListItem: HistoryItemToolsList = {
|
||||
type: MessageType.TOOLS_LIST,
|
||||
tools: geminiTools.map((tool) => ({
|
||||
name: tool.name,
|
||||
displayName: tool.displayName,
|
||||
description: tool.description,
|
||||
})),
|
||||
showDescriptions,
|
||||
};
|
||||
|
||||
context.ui.addItem(toolsListItem);
|
||||
}
|
||||
|
||||
const toolsDescSubCommand: SlashCommand = {
|
||||
name: 'desc',
|
||||
altNames: ['descriptions'],
|
||||
description: 'List available Gemini CLI tools with descriptions.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
action: async (context: CommandContext): Promise<void> =>
|
||||
listTools(context, true),
|
||||
};
|
||||
|
||||
export const toolsCommand: SlashCommand = {
|
||||
name: 'tools',
|
||||
description: 'List available Gemini CLI tools. Usage: /tools [desc]',
|
||||
description:
|
||||
'List available Gemini CLI tools. Use /tools desc to include descriptions.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: false,
|
||||
subCommands: [toolsDescSubCommand],
|
||||
action: async (context: CommandContext, args?: string): Promise<void> => {
|
||||
const subCommand = args?.trim();
|
||||
|
||||
// Default to NOT showing descriptions. The user must opt in with an argument.
|
||||
let useShowDescriptions = false;
|
||||
if (subCommand === 'desc' || subCommand === 'descriptions') {
|
||||
useShowDescriptions = true;
|
||||
}
|
||||
// Keep backward compatibility for typed arguments while exposing desc in TUI via subcommands.
|
||||
const useShowDescriptions =
|
||||
subCommand === 'desc' || subCommand === 'descriptions';
|
||||
|
||||
const toolRegistry = context.services.config?.getToolRegistry();
|
||||
if (!toolRegistry) {
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve tool registry.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const tools = toolRegistry.getAllTools();
|
||||
// Filter out MCP tools by checking for the absence of a serverName property
|
||||
const geminiTools = tools.filter((tool) => !('serverName' in tool));
|
||||
|
||||
const toolsListItem: HistoryItemToolsList = {
|
||||
type: MessageType.TOOLS_LIST,
|
||||
tools: geminiTools.map((tool) => ({
|
||||
name: tool.name,
|
||||
displayName: tool.displayName,
|
||||
description: tool.description,
|
||||
})),
|
||||
showDescriptions: useShowDescriptions,
|
||||
};
|
||||
|
||||
context.ui.addItem(toolsListItem);
|
||||
await listTools(context, useShowDescriptions);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -231,7 +231,7 @@ const createMockConfig = (overrides = {}): Config =>
|
||||
getDebugMode: vi.fn(() => false),
|
||||
getAccessibility: vi.fn(() => ({})),
|
||||
getMcpServers: vi.fn(() => ({})),
|
||||
isPlanEnabled: vi.fn(() => false),
|
||||
isPlanEnabled: vi.fn(() => true),
|
||||
getToolRegistry: () => ({
|
||||
getTool: vi.fn(),
|
||||
}),
|
||||
|
||||
@@ -86,7 +86,7 @@ describe('useApprovalModeIndicator', () => {
|
||||
(value: ApprovalMode) => void
|
||||
>,
|
||||
isYoloModeDisabled: vi.fn().mockReturnValue(false),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(false),
|
||||
isPlanEnabled: vi.fn().mockReturnValue(true),
|
||||
isTrustedFolder: vi.fn().mockReturnValue(true) as Mock<() => boolean>,
|
||||
getCoreTools: vi.fn().mockReturnValue([]) as Mock<() => string[]>,
|
||||
getToolDiscoveryCommand: vi.fn().mockReturnValue(undefined) as Mock<
|
||||
|
||||
Reference in New Issue
Block a user