mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 16:57:46 +00:00
update /tools to new slash command arch (#4236)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: matt korwel <matt.korwel@gmail.com>
This commit is contained in:
108
packages/cli/src/ui/commands/toolsCommand.test.ts
Normal file
108
packages/cli/src/ui/commands/toolsCommand.test.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { toolsCommand } from './toolsCommand.js';
|
||||
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
|
||||
import { MessageType } from '../types.js';
|
||||
import { Tool } from '@google/gemini-cli-core';
|
||||
|
||||
// Mock tools for testing
|
||||
const mockTools = [
|
||||
{
|
||||
name: 'file-reader',
|
||||
displayName: 'File Reader',
|
||||
description: 'Reads files from the local system.',
|
||||
schema: {},
|
||||
},
|
||||
{
|
||||
name: 'code-editor',
|
||||
displayName: 'Code Editor',
|
||||
description: 'Edits code files.',
|
||||
schema: {},
|
||||
},
|
||||
] as Tool[];
|
||||
|
||||
describe('toolsCommand', () => {
|
||||
it('should display an error if the tool registry is unavailable', async () => {
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: {
|
||||
getToolRegistry: () => Promise.resolve(undefined),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!toolsCommand.action) throw new Error('Action not defined');
|
||||
await toolsCommand.action(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve tool registry.',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('should display "No tools available" when none are found', async () => {
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: {
|
||||
getToolRegistry: () =>
|
||||
Promise.resolve({ getAllTools: () => [] as Tool[] }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!toolsCommand.action) throw new Error('Action not defined');
|
||||
await toolsCommand.action(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
text: expect.stringContaining('No tools available'),
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('should list tools without descriptions by default', async () => {
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: {
|
||||
getToolRegistry: () =>
|
||||
Promise.resolve({ getAllTools: () => mockTools }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!toolsCommand.action) throw new Error('Action not defined');
|
||||
await toolsCommand.action(mockContext, '');
|
||||
|
||||
const message = (mockContext.ui.addItem as vi.Mock).mock.calls[0][0].text;
|
||||
expect(message).not.toContain('Reads files from the local system.');
|
||||
expect(message).toContain('File Reader');
|
||||
expect(message).toContain('Code Editor');
|
||||
});
|
||||
|
||||
it('should list tools with descriptions when "desc" arg is passed', async () => {
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: {
|
||||
getToolRegistry: () =>
|
||||
Promise.resolve({ getAllTools: () => mockTools }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!toolsCommand.action) throw new Error('Action not defined');
|
||||
await toolsCommand.action(mockContext, 'desc');
|
||||
|
||||
const message = (mockContext.ui.addItem as vi.Mock).mock.calls[0][0].text;
|
||||
expect(message).toContain('Reads files from the local system.');
|
||||
expect(message).toContain('Edits code files.');
|
||||
});
|
||||
});
|
||||
66
packages/cli/src/ui/commands/toolsCommand.ts
Normal file
66
packages/cli/src/ui/commands/toolsCommand.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { type CommandContext, type SlashCommand } from './types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
|
||||
export const toolsCommand: SlashCommand = {
|
||||
name: 'tools',
|
||||
description: 'list available Gemini CLI tools',
|
||||
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;
|
||||
}
|
||||
|
||||
const toolRegistry = await context.services.config?.getToolRegistry();
|
||||
if (!toolRegistry) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve tool registry.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
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));
|
||||
|
||||
let message = 'Available Gemini CLI tools:\n\n';
|
||||
|
||||
if (geminiTools.length > 0) {
|
||||
geminiTools.forEach((tool) => {
|
||||
if (useShowDescriptions && tool.description) {
|
||||
message += ` - \u001b[36m${tool.displayName} (${tool.name})\u001b[0m:\n`;
|
||||
|
||||
const greenColor = '\u001b[32m';
|
||||
const resetColor = '\u001b[0m';
|
||||
|
||||
// Handle multi-line descriptions
|
||||
const descLines = tool.description.trim().split('\n');
|
||||
for (const descLine of descLines) {
|
||||
message += ` ${greenColor}${descLine}${resetColor}\n`;
|
||||
}
|
||||
} else {
|
||||
message += ` - \u001b[36m${tool.displayName}\u001b[0m\n`;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
message += ' No tools available\n';
|
||||
}
|
||||
message += '\n';
|
||||
|
||||
message += '\u001b[0m';
|
||||
|
||||
context.ui.addItem({ type: MessageType.INFO, text: message }, Date.now());
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user