From 912677fba084f566632879ba8bec978cdfcb2f6a Mon Sep 17 00:00:00 2001 From: David Kaya Date: Thu, 26 Mar 2026 19:27:36 +0100 Subject: [PATCH] feat: add tool metadata registry with human-readable labels Add a builtin tool label registry mapping all ~30 official SDK tool IDs to human-readable display names. Update listApprovalToolDefinitions to resolve labels from the registry instead of showing raw tool IDs like read_bash or fetch_copilot_cli_documentation. Expand the fallback runtime tool catalog from 6 to 23 non-internal standard tools. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/shared/domain/tooling.ts | 81 ++++++++++++++++++++++++++++++++---- tests/shared/tooling.test.ts | 39 ++++++++++++++++- 2 files changed, 111 insertions(+), 9 deletions(-) diff --git a/src/shared/domain/tooling.ts b/src/shared/domain/tooling.ts index 286ae4c..5b3ef60 100644 --- a/src/shared/domain/tooling.ts +++ b/src/shared/domain/tooling.ts @@ -89,15 +89,82 @@ const lspApprovalOperations = [ { suffix: 'references', label: 'References' }, ] as const; +// Human-readable labels for built-in runtime tools. +// Both bash and powershell variants are included for forward compatibility. +const builtinToolLabels: ReadonlyMap = new Map([ + // Shell tools + ['bash', 'Execute shell commands'], + ['powershell', 'Execute shell commands'], + ['read_bash', 'Read shell output'], + ['read_powershell', 'Read shell output'], + ['write_bash', 'Write shell input'], + ['write_powershell', 'Write shell input'], + ['stop_bash', 'Stop shell session'], + ['stop_powershell', 'Stop shell session'], + ['list_bash', 'List shell sessions'], + ['list_powershell', 'List shell sessions'], + // File tools + ['view', 'View files'], + ['create', 'Create files'], + ['edit', 'Edit files'], + ['apply_patch', 'Apply patch'], + // Search tools + ['grep', 'Search file contents'], + ['rg', 'Search file contents'], + ['glob', 'Find files by pattern'], + // Agent tools + ['task', 'Run sub-agent'], + ['read_agent', 'Read agent results'], + ['list_agents', 'List agents'], + // Web tools + ['web_fetch', 'Fetch web content'], + ['web_search', 'Search the web'], + // Other tools + ['skill', 'Invoke skill'], + ['show_file', 'Show file'], + ['fetch_copilot_cli_documentation', 'Fetch CLI docs'], + ['update_todo', 'Update checklist'], + ['store_memory', 'Store memory'], + ['sql', 'Query session data'], + ['lsp', 'Language server'], +]); + +export function resolveToolLabel(toolId: string): string { + return builtinToolLabels.get(toolId) ?? toolId; +} + // Fallback runtime tools used before sidecar capabilities are loaded or when the // CLI cannot report its built-in tool catalog dynamically. const fallbackRuntimeApprovalTools: ReadonlyArray = [ - { id: 'glob', label: 'glob', description: 'Match files by glob pattern.' }, - { id: 'lsp', label: 'lsp', description: 'Query configured language servers.' }, - { id: 'rg', label: 'rg', description: 'Search file contents with ripgrep.' }, - { id: 'view', label: 'view', description: 'Read files and list directories.' }, - { id: 'web_fetch', label: 'web_fetch', description: 'Fetch content from a URL.' }, - { id: 'web_search', label: 'web_search', description: 'Search the web for current information.' }, + // Shell tools (bash variants — SDK reports these on all platforms) + { id: 'bash', label: 'Execute shell commands' }, + { id: 'read_bash', label: 'Read shell output' }, + { id: 'write_bash', label: 'Write shell input' }, + { id: 'stop_bash', label: 'Stop shell session' }, + { id: 'list_bash', label: 'List shell sessions' }, + // File tools + { id: 'view', label: 'View files' }, + { id: 'create', label: 'Create files' }, + { id: 'edit', label: 'Edit files' }, + { id: 'apply_patch', label: 'Apply patch' }, + // Search tools + { id: 'grep', label: 'Search file contents' }, + { id: 'glob', label: 'Find files by pattern' }, + // Agent tools + { id: 'task', label: 'Run sub-agent' }, + { id: 'read_agent', label: 'Read agent results' }, + { id: 'list_agents', label: 'List agents' }, + // Web tools + { id: 'web_fetch', label: 'Fetch web content' }, + { id: 'web_search', label: 'Search the web' }, + // Other tools + { id: 'skill', label: 'Invoke skill' }, + { id: 'show_file', label: 'Show file' }, + { id: 'fetch_copilot_cli_documentation', label: 'Fetch CLI docs' }, + { id: 'update_todo', label: 'Update checklist' }, + { id: 'store_memory', label: 'Store memory' }, + { id: 'sql', label: 'Query session data' }, + { id: 'lsp', label: 'Language server' }, ]; export function createWorkspaceSettings(): WorkspaceSettings { @@ -171,7 +238,7 @@ export function listApprovalToolDefinitions( for (const tool of runtimeApprovalTools) { registerApprovalTool(toolsById, { id: tool.id, - label: tool.label, + label: resolveToolLabel(tool.id), description: tool.description, kind: 'builtin', providerId: `builtin:${tool.id}`, diff --git a/tests/shared/tooling.test.ts b/tests/shared/tooling.test.ts index ad87c1f..e908520 100644 --- a/tests/shared/tooling.test.ts +++ b/tests/shared/tooling.test.ts @@ -4,6 +4,7 @@ import { listApprovalToolDefinitions, normalizeWorkspaceSettings, resolveProjectToolingSettings, + resolveToolLabel, resolveWorkspaceToolingSettings, validateLspProfileDefinition, validateMcpServerDefinition, @@ -206,8 +207,7 @@ describe('tooling settings helpers', () => { expect(tools).toContainEqual({ id: 'web_fetch', - label: 'web_fetch', - description: 'Fetch content from a URL.', + label: 'Fetch web content', kind: 'builtin', providerIds: ['builtin:web_fetch'], providerNames: ['Built-in'], @@ -251,6 +251,41 @@ describe('tooling settings helpers', () => { expect(tools.some((tool) => tool.id === 'web_fetch')).toBe(false); }); + test('resolves human-readable labels from the tool metadata registry', () => { + expect(resolveToolLabel('bash')).toBe('Execute shell commands'); + expect(resolveToolLabel('read_bash')).toBe('Read shell output'); + expect(resolveToolLabel('web_fetch')).toBe('Fetch web content'); + expect(resolveToolLabel('fetch_copilot_cli_documentation')).toBe('Fetch CLI docs'); + expect(resolveToolLabel('glob')).toBe('Find files by pattern'); + expect(resolveToolLabel('lsp')).toBe('Language server'); + }); + + test('passes through unknown tool IDs as labels', () => { + expect(resolveToolLabel('my_custom_tool')).toBe('my_custom_tool'); + expect(resolveToolLabel('')).toBe(''); + }); + + test('fallback catalog includes all standard non-internal tools with friendly labels', () => { + const tools = listApprovalToolDefinitions({ mcpServers: [], lspProfiles: [] }); + const builtinTools = tools.filter((t) => t.kind === 'builtin'); + + expect(builtinTools.length).toBeGreaterThanOrEqual(20); + expect(builtinTools.some((t) => t.id === 'bash')).toBe(true); + expect(builtinTools.some((t) => t.id === 'web_fetch')).toBe(true); + expect(builtinTools.some((t) => t.id === 'task')).toBe(true); + expect(builtinTools.some((t) => t.id === 'store_memory')).toBe(true); + + // Labels should be human-readable, not raw IDs + const bashTool = builtinTools.find((t) => t.id === 'bash'); + expect(bashTool?.label).toBe('Execute shell commands'); + + // Internal tools should not appear in the fallback + expect(builtinTools.some((t) => t.id === 'ask_user')).toBe(false); + expect(builtinTools.some((t) => t.id === 'report_intent')).toBe(false); + expect(builtinTools.some((t) => t.id === 'task_complete')).toBe(false); + expect(builtinTools.some((t) => t.id === 'exit_plan_mode')).toBe(false); + }); + test('resolves workspace and project tooling with accepted discovered MCP servers', () => { const workspaceTooling = resolveWorkspaceToolingSettings(normalizeWorkspaceSettings({ tooling: {