mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 14:10:37 -07:00
feat(cli): give visibility to /tools list command in the TUI and follow the subcommand pattern of other commands (#21213)
This commit is contained in:
@@ -67,7 +67,7 @@ describe('toolsCommand', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should list tools without descriptions by default', async () => {
|
||||
it('should list tools without descriptions by default (no args)', async () => {
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: {
|
||||
@@ -88,6 +88,27 @@ describe('toolsCommand', () => {
|
||||
expect(message.tools[1].displayName).toBe('Code Editor');
|
||||
});
|
||||
|
||||
it('should list tools without descriptions when "list" arg is passed', async () => {
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: {
|
||||
getToolRegistry: () => ({ getAllTools: () => mockTools }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!toolsCommand.action) throw new Error('Action not defined');
|
||||
await toolsCommand.action(mockContext, 'list');
|
||||
|
||||
const [message] = (mockContext.ui.addItem as ReturnType<typeof vi.fn>).mock
|
||||
.calls[0];
|
||||
expect(message.type).toBe(MessageType.TOOLS_LIST);
|
||||
expect(message.showDescriptions).toBe(false);
|
||||
expect(message.tools).toHaveLength(2);
|
||||
expect(message.tools[0].displayName).toBe('File Reader');
|
||||
expect(message.tools[1].displayName).toBe('Code Editor');
|
||||
});
|
||||
|
||||
it('should list tools with descriptions when "desc" arg is passed', async () => {
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
@@ -105,9 +126,65 @@ describe('toolsCommand', () => {
|
||||
expect(message.type).toBe(MessageType.TOOLS_LIST);
|
||||
expect(message.showDescriptions).toBe(true);
|
||||
expect(message.tools).toHaveLength(2);
|
||||
expect(message.tools[0].displayName).toBe('File Reader');
|
||||
expect(message.tools[0].description).toBe(
|
||||
'Reads files from the local system.',
|
||||
);
|
||||
expect(message.tools[1].displayName).toBe('Code Editor');
|
||||
expect(message.tools[1].description).toBe('Edits code files.');
|
||||
});
|
||||
|
||||
it('should have "list" and "desc" subcommands', () => {
|
||||
expect(toolsCommand.subCommands).toBeDefined();
|
||||
const names = toolsCommand.subCommands?.map((s) => s.name);
|
||||
expect(names).toContain('list');
|
||||
expect(names).toContain('desc');
|
||||
expect(names).not.toContain('descriptions');
|
||||
});
|
||||
|
||||
it('subcommand "list" should display tools without descriptions', async () => {
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: {
|
||||
getToolRegistry: () => ({ getAllTools: () => mockTools }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const listCmd = toolsCommand.subCommands?.find((s) => s.name === 'list');
|
||||
if (!listCmd?.action) throw new Error('Action not defined');
|
||||
await listCmd.action(mockContext, '');
|
||||
|
||||
const [message] = (mockContext.ui.addItem as ReturnType<typeof vi.fn>).mock
|
||||
.calls[0];
|
||||
expect(message.showDescriptions).toBe(false);
|
||||
expect(message.tools).toHaveLength(2);
|
||||
expect(message.tools[0].displayName).toBe('File Reader');
|
||||
expect(message.tools[1].displayName).toBe('Code Editor');
|
||||
});
|
||||
|
||||
it('subcommand "desc" should display tools with descriptions', async () => {
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: {
|
||||
getToolRegistry: () => ({ getAllTools: () => mockTools }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const descCmd = toolsCommand.subCommands?.find((s) => s.name === 'desc');
|
||||
if (!descCmd?.action) throw new Error('Action not defined');
|
||||
await descCmd.action(mockContext, '');
|
||||
|
||||
const [message] = (mockContext.ui.addItem as ReturnType<typeof vi.fn>).mock
|
||||
.calls[0];
|
||||
expect(message.showDescriptions).toBe(true);
|
||||
expect(message.tools).toHaveLength(2);
|
||||
expect(message.tools[0].displayName).toBe('File Reader');
|
||||
expect(message.tools[0].description).toBe(
|
||||
'Reads files from the local system.',
|
||||
);
|
||||
expect(message.tools[1].displayName).toBe('Code Editor');
|
||||
expect(message.tools[1].description).toBe('Edits code files.');
|
||||
});
|
||||
|
||||
|
||||
@@ -41,7 +41,16 @@ async function listTools(
|
||||
context.ui.addItem(toolsListItem);
|
||||
}
|
||||
|
||||
const toolsDescSubCommand: SlashCommand = {
|
||||
const listSubCommand: SlashCommand = {
|
||||
name: 'list',
|
||||
description: 'List available Gemini CLI tools.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
action: async (context: CommandContext): Promise<void> =>
|
||||
listTools(context, false),
|
||||
};
|
||||
|
||||
const descSubCommand: SlashCommand = {
|
||||
name: 'desc',
|
||||
altNames: ['descriptions'],
|
||||
description: 'List available Gemini CLI tools with descriptions.',
|
||||
@@ -57,11 +66,11 @@ export const toolsCommand: SlashCommand = {
|
||||
'List available Gemini CLI tools. Use /tools desc to include descriptions.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: false,
|
||||
subCommands: [toolsDescSubCommand],
|
||||
subCommands: [listSubCommand, descSubCommand],
|
||||
action: async (context: CommandContext, args?: string): Promise<void> => {
|
||||
const subCommand = args?.trim();
|
||||
|
||||
// Keep backward compatibility for typed arguments while exposing desc in TUI via subcommands.
|
||||
// Keep backward compatibility for typed arguments while exposing subcommands in TUI.
|
||||
const useShowDescriptions =
|
||||
subCommand === 'desc' || subCommand === 'descriptions';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user