import * as vscode from 'vscode'; export function registerChatTools(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.lm.registerTool('chat-tools-sample_tabCount', new TabCountTool())); context.subscriptions.push(vscode.lm.registerTool('chat-tools-sample_findFiles', new FindFilesTool())); context.subscriptions.push(vscode.lm.registerTool('chat-tools-sample_runInTerminal', new RunInTerminalTool())); } interface ITabCountParameters { tabGroup?: number; } export class TabCountTool implements vscode.LanguageModelTool { async invoke( options: vscode.LanguageModelToolInvocationOptions, _token: vscode.CancellationToken ) { const params = options.input; if (typeof params.tabGroup === 'number') { const group = vscode.window.tabGroups.all[Math.max(params.tabGroup - 1, 0)]; const nth = params.tabGroup === 1 ? '1st' : params.tabGroup === 2 ? '2nd' : params.tabGroup === 3 ? '3rd' : `${params.tabGroup}th`; return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`There are ${group.tabs.length} tabs open in the ${nth} tab group.`)]); } else { const group = vscode.window.tabGroups.activeTabGroup; return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`There are ${group.tabs.length} tabs open.`)]); } } async prepareInvocation( options: vscode.LanguageModelToolInvocationPrepareOptions, _token: vscode.CancellationToken ) { const confirmationMessages = { title: 'Count the number of open tabs', message: new vscode.MarkdownString( `Count the number of open tabs?` + (options.input.tabGroup !== undefined ? ` in tab group ${options.input.tabGroup}` : '') ), }; return { invocationMessage: 'Counting the number of tabs', confirmationMessages, }; } } interface IFindFilesParameters { pattern: string; } export class FindFilesTool implements vscode.LanguageModelTool { async invoke( options: vscode.LanguageModelToolInvocationOptions, token: vscode.CancellationToken ) { const params = options.input as IFindFilesParameters; const files = await vscode.workspace.findFiles( params.pattern, '**/node_modules/**', undefined, token ); const strFiles = files.map((f) => f.fsPath).join('\n'); return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`Found ${files.length} files matching "${params.pattern}":\n${strFiles}`)]); } async prepareInvocation( options: vscode.LanguageModelToolInvocationPrepareOptions, _token: vscode.CancellationToken ) { return { invocationMessage: `Searching workspace for "${options.input.pattern}"`, }; } } interface IRunInTerminalParameters { command: string; } async function waitForShellIntegration( terminal: vscode.Terminal, timeout: number ): Promise { let resolve: () => void; let reject: (e: Error) => void; const p = new Promise((_resolve, _reject) => { resolve = _resolve; reject = _reject; }); const timer = setTimeout(() => reject(new Error('Could not run terminal command: shell integration is not enabled')), timeout); const listener = vscode.window.onDidChangeTerminalShellIntegration((e) => { if (e.terminal === terminal) { clearTimeout(timer); listener.dispose(); resolve(); } }); await p; } export class RunInTerminalTool implements vscode.LanguageModelTool { async invoke( options: vscode.LanguageModelToolInvocationOptions, _token: vscode.CancellationToken ) { const params = options.input as IRunInTerminalParameters; const terminal = vscode.window.createTerminal('Language Model Tool User'); terminal.show(); try { await waitForShellIntegration(terminal, 5000); } catch (e) { return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart((e as Error).message)]); } const execution = terminal.shellIntegration!.executeCommand(params.command); const terminalStream = execution.read(); let terminalResult = ''; for await (const chunk of terminalStream) { terminalResult += chunk; } return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(terminalResult)]); } async prepareInvocation( options: vscode.LanguageModelToolInvocationPrepareOptions, _token: vscode.CancellationToken ) { const confirmationMessages = { title: 'Run command in terminal', message: new vscode.MarkdownString( `Run this command in a terminal?` + `\n\n\`\`\`\n${options.input.command}\n\`\`\`\n` ), }; return { invocationMessage: `Running command in terminal`, confirmationMessages, }; } }