Path: blob/main/extensions/copilot/src/extension/prompts/node/panel/startDebugging.tsx
13405 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { BasePromptElementProps, PromptElement, PromptPiece, PromptSizing, SystemMessage, TextChunk, UserMessage } from '@vscode/prompt-tsx';6import type * as vscode from 'vscode';7import { ChatFetchResponseType, ChatLocation } from '../../../../platform/chat/common/commonTypes';8import { IEndpointProvider } from '../../../../platform/endpoint/common/endpointProvider';9import { IEnvService } from '../../../../platform/env/common/envService';10import { IExtensionsService } from '../../../../platform/extensions/common/extensionsService';11import { IFileSystemService } from '../../../../platform/filesystem/common/fileSystemService';12import { IIgnoreService } from '../../../../platform/ignore/common/ignoreService';13import { ICodeOrDocsSearchItem } from '../../../../platform/remoteSearch/common/codeOrDocsSearchClient';14import { IWorkspaceService } from '../../../../platform/workspace/common/workspaceService';15import { CancellationToken } from '../../../../util/vs/base/common/cancellation';16import { ResourceSet } from '../../../../util/vs/base/common/map';17import { basename, dirname } from '../../../../util/vs/base/common/path';18import { URI } from '../../../../util/vs/base/common/uri';19import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation';20import { ChatResponseProgressPart } from '../../../../vscodeTypes';21import { getSchemasForTypeAsList } from '../../../onboardDebug/node/parseLaunchConfigFromResponse';22import { Turn } from '../../../prompt/common/conversation';23import { CopilotIdentityRules } from '../base/copilotIdentity';24import { InstructionMessage } from '../base/instructionMessage';25import { PromptRenderer } from '../base/promptRenderer';26import { SafetyRules } from '../base/safetyRules';27import { Tag } from '../base/tag';28import { HistoryWithInstructions } from './conversationHistory';29import { FileVariable } from './fileVariable';30import { ProjectLabels } from './projectLabels';31import { workspaceVisualFileTree } from './workspace/visualFileTree';32import { MultirootWorkspaceStructure, WorkspaceStructureMetadata } from './workspace/workspaceStructure';3334export const enum StartDebuggingType {35UserQuery,36CommandLine,37}3839interface IStartDebuggingFromUserQuery {40type: StartDebuggingType.UserQuery;41userQuery?: string;42}4344interface IStartDebuggingFromCommandLine {45type: StartDebuggingType.CommandLine;46args: readonly string[];47/** cwd with the ${workspaceFolder} replaced */48relativeCwd?: string;49/** absolute cwd */50absoluteCwd: string;51}5253const enum OutputStyle {54Readable,55ConfigOnly,56}5758export type StartDebuggingInput = IStartDebuggingFromUserQuery | IStartDebuggingFromCommandLine;5960export interface StartDebuggingPromptProps extends BasePromptElementProps {61input: StartDebuggingInput;62history: readonly Turn[];63}6465export interface StartDebuggingPromptState {66docSearchResults?: ICodeOrDocsSearchItem[];67resources?: URI[];68schema?: string[];69}7071function getLaunchConfigExamples(inputType: StartDebuggingType, outputStyle: OutputStyle) {72const o1Object = {73configurations: [{74type: 'node',75request: 'launch',76name: 'Launch Program',77program: '${workspaceFolder}/app/index.js',78args: ['--serve'],79}],80};8182const o2Object = {83configurations: [{84type: 'cppvsdbg',85request: 'launch',86name: 'Launch Program',87program: '${workspaceFolder}/${input:executableName}.exe',88stopAtEntry: true,89}],90inputs: [91{92type: 'promptString',93id: 'executableName',94description: 'Name of your executable',95}96]97};9899const styleOutput = (output: any) => outputStyle === OutputStyle.ConfigOnly100? JSON.stringify(output) : `\`\`\`json\n${JSON.stringify(output, null, 2)}\n\`\`\``;101102if (inputType === StartDebuggingType.UserQuery) {103return `# Example104User:105My operating system is macOS.106Create a debug configuration to do the following: launch my node app107108Assistant:109${styleOutput(o1Object)}110111# Example112User:113My operating system is Windows.114Create a debug configuration to do the following: debug my c++ program115116Assistant:117${styleOutput(o2Object)}118`;119} else {120return `# Example121User:122My operating system is macOS.123In the working directory \${workspaceFolder}/app, I ran this on the command line: node ./index --serve124125Assistant:126${styleOutput(o1Object)}127128# Example129User:130My operating system is Windows.131In the working directory \${workspaceFolder}, I ran this on the command line: make test132133Assistant:134${styleOutput(o2Object)}135`;136}137}138139export class StartDebuggingPrompt extends PromptElement<StartDebuggingPromptProps, StartDebuggingPromptState> {140constructor(props: StartDebuggingPromptProps,141@IWorkspaceService private readonly workspace: IWorkspaceService,142@IEndpointProvider private readonly endpointProvider: IEndpointProvider,143@IInstantiationService private readonly instantiationService: IInstantiationService,144@IExtensionsService private readonly extensionsService: IExtensionsService,145@IFileSystemService private readonly fileSystemService: IFileSystemService,146@IIgnoreService private readonly ignoreService: IIgnoreService,147@IEnvService private readonly envService: IEnvService,148) {149super(props);150}151152override async prepare(sizing: PromptSizing, progress: vscode.Progress<vscode.ChatResponseProgressPart> | undefined, token: vscode.CancellationToken): Promise<StartDebuggingPromptState> {153if (token.isCancellationRequested) {154return {};155}156157if (token.isCancellationRequested) {158return {};159}160const debuggerType = await this.getDebuggerType(progress, token);161const [resources, schema] = await Promise.all([162this.getResources(debuggerType, progress, token),163this.getSchema(debuggerType, progress, token)164]);165return { resources, schema };166}167168private async getFiles(requestedFiles: string[], structureMetadata?: WorkspaceStructureMetadata): Promise<URI[] | undefined> {169const fileResults = new ResourceSet();170const returnedUris = MultirootWorkspaceStructure.toURIs(this.workspace, requestedFiles);171172const fileExists = (file: URI) => this.fileSystemService.stat(file).then(() => true, () => false);173174const tryAdd = async (file: URI) => {175if (fileResults.has(file)) {176return true;177}178179const [exists, ignored] = await Promise.all([180fileExists(file),181this.ignoreService.isCopilotIgnored(file),182]);183if (exists && !ignored) {184fileResults.add(file);185return true;186}187188return false;189};190191const todo: Promise<unknown>[] = returnedUris.map(async ({ file, relativePath }) => {192if (!structureMetadata || await fileExists(file)) {193return tryAdd(file);194}195196// The model sometimes doesn't fully qualify the path to nested files.197// In these cases, try to guess what it means by looking at the what it does give us198const bestGuess = structureMetadata.value199.flatMap(root => root.tree.files.filter(f => f.path.endsWith(relativePath)))200.sort((a, b) => a.path.length - b.path.length) // get the least-nested candidate201.at(0);202if (bestGuess) {203return tryAdd(bestGuess);204}205});206207const defaultWorkspaceFolder = this.workspace.getWorkspaceFolders().at(0);208const fileNeedle = returnedUris.at(0) ?? (defaultWorkspaceFolder && { file: defaultWorkspaceFolder, workspaceFolder: defaultWorkspaceFolder });209if (fileNeedle) {210for (const file of ['launch.json', 'tasks.json']) {211todo.push(tryAdd(URI.joinPath(fileNeedle.workspaceFolder, '.vscode', file)));212}213214for (const usefulFile of ['README.md', 'CONTRIBUTING.md']) {215const folderFsPath = fileNeedle.workspaceFolder.fsPath;216// limit this to avoid looking for files in parent directories of the workspace217todo.push(nearestDirectoryWhere(fileNeedle.file.fsPath, dir =>218dir.length >= folderFsPath.length ? tryAdd(URI.joinPath(URI.file(dir), usefulFile)) : Promise.resolve(undefined)));219}220}221222await Promise.all(todo);223224return [...fileResults];225}226227private async getResources(debuggerType: string | undefined, progress: vscode.Progress<vscode.ChatResponseProgressPart> | undefined, token: vscode.CancellationToken): Promise<URI[] | undefined> {228const r = await this.queryModelForRequestedFiles(debuggerType, progress, token);229if (!r?.requestedFiles.length || token.isCancellationRequested) {230return;231}232return this.getFiles(r.requestedFiles, r.structureMetadata);233}234235private async queryModelForRequestedFiles(debuggerType: string | undefined, progress: vscode.Progress<vscode.ChatResponseProgressPart> | undefined, token: vscode.CancellationToken) {236const endpoint = await this.endpointProvider.getChatEndpoint('copilot-fast');237const promptRenderer = this.props.input.type === StartDebuggingType.CommandLine238? PromptRenderer.create(239this.instantiationService,240endpoint,241ReferenceFilesFromCliPrompt,242{ debuggerType, input: this.props.input, os: this.envService.OS }243) : PromptRenderer.create(244this.instantiationService,245endpoint,246ReferenceFilesFromQueryPrompt,247{ debuggerType, input: this.props.input, os: this.envService.OS }248);249250const prompt = await promptRenderer.render(undefined, token);251const structureMetadata = prompt.metadata.get(WorkspaceStructureMetadata);252const fetchResult = await endpoint.makeChatRequest(253'referenceFiles',254prompt.messages,255undefined,256token,257ChatLocation.Panel,258);259260if (fetchResult.type !== ChatFetchResponseType.Success) {261return undefined;262}263264let requestedFiles: string[] | undefined;265try {266requestedFiles = JSON.parse(fetchResult.value);267} catch {268return;269}270if (!Array.isArray(requestedFiles)) {271return;272}273if (this.props.input.type === StartDebuggingType.UserQuery) {274// We will check for existing config275requestedFiles.push('launch.json');276if (!this.props.input.userQuery) {277requestedFiles.push('README.md');278}279}280progress?.report(new ChatResponseProgressPart('Requesting resources'));281return { requestedFiles, structureMetadata };282}283284private async getSchema(debuggerType: string | undefined, progress: vscode.Progress<vscode.ChatResponseProgressPart> | undefined, token: vscode.CancellationToken): Promise<string[] | undefined> {285if (!debuggerType) {286return;287}288const schema = getSchemasForTypeAsList(debuggerType, this.extensionsService);289if (!schema) {290return;291}292progress?.report(new ChatResponseProgressPart('Identified launch config properties'));293return schema;294}295296private async getDebuggerType(progress: vscode.Progress<vscode.ChatResponseProgressPart> | undefined, token: vscode.CancellationToken): Promise<string | undefined> {297const endpoint = await this.endpointProvider.getChatEndpoint('copilot-fast');298299const promptRenderer = PromptRenderer.create(300this.instantiationService,301endpoint,302DebugTypePrompt,303{ debuggerTypes: this.getAllDebuggerTypes(), input: this.props.input, os: this.envService.OS }304);305306const prompt = await promptRenderer.render(undefined, token);307const fetchResult = await endpoint.makeChatRequest(308'debugType',309prompt.messages,310undefined,311token,312ChatLocation.Panel,313);314315if (fetchResult.type !== ChatFetchResponseType.Success) {316return undefined;317}318319// The model likes to return text like "You should use `node", so detect backticks320return /`(.*?)`/.exec(fetchResult.value)?.[1] || fetchResult.value;321}322323private getAllDebuggerTypes(): string[] {324return this.extensionsService.allAcrossExtensionHosts.filter(e => !!e.packageJSON?.contributes?.debuggers).map(e => {325const result: string[] = [];326for (const d of e.packageJSON?.contributes?.debuggers) {327if (d.type === '*' || d.deprecated) {328continue;329}330result.push(`- ${d.type}: ${d.label} (${e.id})`);331}332return result;333}).flat();334}335336override render(state: StartDebuggingPromptState, sizing: PromptSizing): PromptPiece | undefined {337const style = this.props.input.type === StartDebuggingType.CommandLine ? OutputStyle.ConfigOnly : OutputStyle.Readable;338return (339<>340<SystemMessage priority={1000}>341{style === OutputStyle.ConfigOnly ? (342<>343You are a Visual Studio Code assistant who specializes in debugging and creating launch configurations. Your task is to create a launch configuration for the user's query.<br />344</>345) : (346<>347You are a Visual Studio Code assistant who specializes in debugging, searching for existing launch configurations, and creating launch configurations. Your task is to find an existing launch configuration that matches the query or to create a launch configuration for the user's query if no match is found. If there's no query, still provide a response, checking for existing configurations in the launch.json file, if any.<br />348</>349)}350<CopilotIdentityRules />351<SafetyRules />352</SystemMessage>353<HistoryWithInstructions historyPriority={600} passPriority history={this.props.history}>354<InstructionMessage priority={1000}>355{style === OutputStyle.Readable && <>356The user cannot see the context you are given, so you must not mention it. If you want to refer to it, you must include it in your reply.<br />357</>}358Print out the VS Code `launch.json` file needed to debug the command, formatted as JSON.<br />359If there are build steps needed before the program can be debugged, be sure to include a `preLaunchTask` property in the launch configuration.360If you include a `preLaunchTask` property, {state.resources?.some(r => r.path.endsWith('launch.json'))361? <>{' '}it must either refer to an existing a suitable task in the `tasks.json` file, or you must include a `tasks.json` file in your response that contains that configuration.</>362: <>{' '}you MUST also include `tasks.json` file in your response that contains that configuration.</>363}{style === OutputStyle.Readable && <>364{' '}Include a brief one or two sentence explaination of any such task definition is needed.<br />365</>}<br />366Pay attention to my operating system and suggest the best tool for the platform I'm working on. For example, for debugging native code on Windows, you would not suggest the `lldb` type.<br />367If there are unknowns, such as the path to the program, use the `inputs` field in the launch.json schema to prompt the user with an informative message. Input types may either be `promptString` for free text input or `pickString` with an `options` array for enumerations.<br />368Do not give any other explanation.<br />369If there are unknowns, such as the path to the program, use the `inputs` field in the launch.json schema to prompt the user with an informative message. Input types may either be `promptString` for free text input or `pickString` with an `options` array for enumerations. Do not include a default value for the input field.<br />370Always include the following properties in the launch.json file:<br />371- type: the type of debugger to use for this launch configuration. Every installed debug extension introduces a type: node for the built-in Node debugger, for example, or php and go for the PHP and Go extensions.<br />372- request: the request type of this launch configuration. Currently, launch and attach are supported.<br />373- name: the reader-friendly name to appear in the Debug launch configuration dropdown.<br />374If a result is not a valid answer, but it still relates to Visual Studio Code, please still respond.<br />375Please do not guess a response and instead just respond with a polite apology if you are unsure.<br />376If you believe the given context given to you is incorrect or not relevant you may ignore it.<br />377{getLaunchConfigExamples(this.props.input.type, style)}<br />378</InstructionMessage>379</HistoryWithInstructions>380<UserMessage priority={700}>381{state.docSearchResults && state.docSearchResults.length > 0 && <>Below is a list of information from the Visual Studio Code documentation which might be relevant to the question. <br /></>}382{state.docSearchResults && state.docSearchResults.map((result) => {383if (result?.title && result.contents) {384// eslint-disable-next-line local/code-no-unused-expressions385<TextChunk>386##{result?.title?.trim()} - {result.path}<br />387{result.contents}388</TextChunk>;389}390})}391</UserMessage>392<UserMessage priority={850}>393{state.schema && <>Below is a list of properties that the launch config might include. <br />394{state.schema.map((property) => {395return <TextChunk>{property}<br /></TextChunk>;396})})<br /></>}397</UserMessage>398<UserMessage priority={700} flexGrow={1}>399{this.props.input.type === StartDebuggingType.UserQuery400? <>401If a program property is included in the launch config, and its path does not exist in the workspace or there are multiple files that could work, use the `inputs` field in the launch.json schema to prompt the user with an informative message.<br />402<MultirootWorkspaceStructure maxSize={1000} />403</>404: <StructureOfWorkingDirectory input={this.props.input} />405}406</UserMessage>407<UserMessage priority={800}>408{state.resources && state.resources.length > 0 && <>Below is a list of file contents from the workspace that might be useful in building the launch config. <br /></>}409{state.resources && state.resources.map((resource) => {410const containingFolder = this.workspace.getWorkspaceFolder(resource);411const name = containingFolder ? resource.path.substring(containingFolder.path.length + 1) : basename(resource.path);412return <FileVariable variableName={name} variableValue={resource}></FileVariable>;413})}414</UserMessage>415<UserMessage priority={850}>416{this.props.input.type === StartDebuggingType.UserQuery && state.resources?.some(r => r.path.endsWith('launch.json')) && (417<>418{this.props.input.userQuery419? <>Search in that provided launch.json file for an existing configuration based on the query "{this.props.input.userQuery}". Pay particular attention to the name of the launch configuration and compare it to the query. If a match is found, include that configuration. Do not include the whole launch.json context. End the response with HAS_MATCH.<br /></>420: <>Scan any provided documentation to determine which configuration in the provided launch.json file is recommended, if any. Show some, not all, of the launch configurations that are available. End the response with HAS_CONFIG_NO_QUERY.<br /></>421}422If no match is found, include the new configuration that was generated. End the response with GENERATED_CONFIG.<br />423</>424)}425</UserMessage>426{style === OutputStyle.ConfigOnly427? <UserMessage priority={850}>428<Tag name='example'>429In this example, we're debugging a simple Python file, so we only need a launch.json:<br />430<Tag name='request'>431In the working directory, I ran this on the command line: `python main.py`<br />432</Tag>433<Tag name='response'>434launch.json:<br />435```json<br />436{JSON.stringify({437type: 'python',438request: 'launch',439name: 'Launch Program',440program: '${workspaceFolder}/main.py',441}, null, '\t')}<br />442```<br />443</Tag>444</Tag>445<Tag name='example'>446In this example, generate both a launch.json and tasks.json because the program needs to be built before it can be debugged:<br />447<Tag name='request'>448In the working directory, I ran this on the command line: `./my-program.exe`<br />449</Tag>450<Tag name='response'>451launch.json:<br />452```json<br />453{JSON.stringify({454configurations: [{455'type': 'cppvsdbg',456'request': 'launch',457'name': 'Launch Program',458'program': '${workspaceFolder}/my-program.exe',459'preLaunchTask': 'build'460}]461}, null, '\t')}<br />462```<br />463tasks.json:<br />464```json<br />465{JSON.stringify({466tasks: [{467'type': 'shell',468'label': 'build',469'command': 'make',470'args': ['build']471}]472}, null, '\t')}<br />473```<br />474</Tag>475</Tag>476</UserMessage>477: <UserMessage priority={850}>478<Tag name='example'>479In this example, we're debugging a simple Python file, so we only need a launch.json:<br />480<Tag name='request'>481Here's a description of the app I want to debug: "python file"<br />482In my workspace I have the files main.py, tox.ini, and README.md.<br />483</Tag>484<Tag name='response'>485Here is your `launch.json` configuration:<br />486```json<br />487{JSON.stringify({488type: 'python',489request: 'launch',490name: 'Launch Program',491program: '${workspaceFolder}/main.py',492}, null, '\t')}<br />493```<br />494</Tag>495</Tag>496<Tag name='example'>497In this example, generate both a launch.json and tasks.json because the program needs to be built before it can be debugged:<br />498<Tag name='request'>499Here's a description of the app I want to debug: "my-program"<br />500In my workspace I have the files Makefile, my-program.cpp.<br />501</Tag>502<Tag name='response'>503Here is your `launch.json` configuration:<br />504```json<br />505{JSON.stringify({506configurations: [{507'type': 'cppvsdbg',508'request': 'launch',509'name': 'Launch Program',510'program': '${workspaceFolder}/my-program.exe',511'preLaunchTask': 'build'512}]513}, null, '\t')}<br />514```<br />515It looks like you build your project using your Makefile, so let's add a `tasks.json` to do that before each debug session:<br />516```json<br />517{JSON.stringify({518tasks: [{519'type': 'shell',520'label': 'build',521'command': 'make',522'args': ['build']523}]524}, null, '\t')}<br />525```<br />526</Tag>527</Tag>528</UserMessage>}529<InputDescription priority={900} input={this.props.input} os={this.envService.OS} />530</>531);532}533}534type WorkingDirectoryStructureProps = {535input: IStartDebuggingFromCommandLine;536} & BasePromptElementProps;537538class StructureOfWorkingDirectory extends PromptElement<WorkingDirectoryStructureProps, void> {539constructor(540props: WorkingDirectoryStructureProps,541@IInstantiationService private readonly instantiationService: IInstantiationService,542@IWorkspaceService private readonly workspaceService: IWorkspaceService,543) {544super(props);545}546override async render(_state: void, sizing: PromptSizing, _progress: unknown, token?: CancellationToken) {547const maxSize = sizing.tokenBudget / 2; // note: size in the tree is in chars, /2 to be safe548const wf = this.props.input.relativeCwd549? this.workspaceService.getWorkspaceFolder(URI.file(this.props.input.absoluteCwd))550: undefined;551552if (wf) {553const tree = await this.instantiationService.invokeFunction(accessor => workspaceVisualFileTree(accessor, wf, { maxLength: maxSize }, token ?? CancellationToken.None));554return <>555My workspace folder (`${'{'}workspaceFolder{'}'}`) has the following structure:<br />556<br />557<meta value={new WorkspaceStructureMetadata([{ label: '', tree }])} local />558{tree.tree}559</>;560}561562return <MultirootWorkspaceStructure maxSize={maxSize} />;563}564}565566class ReferenceFilesFromQueryPrompt extends PromptElement<{567debuggerType: string | undefined;568input: IStartDebuggingFromUserQuery;569os: string;570} & BasePromptElementProps, void> {571override render(_state: void, _sizing: PromptSizing): PromptPiece {572return (573<>574<SystemMessage priority={10}>575You are a Visual Studio Code assistant who specializes in debugging and creating launch configurations. Your job is to return an array of file names that may contain useful information to translate a user query into a VS Code debug configuration.<br />576The user will give you a file tree. Make sure to fully qualify paths you return from the tree, including their parent directories:<br />577Do not give any other explanation and return only a JSON array of strings. Avoid wrapping the whole response in triple backticks. Do not include any other information in your response.<br />578<TextChunk priority={8}>579# Example 1<br />580## User: <br />581I am working in a workspace that has the following structure: {`582\`\`\`583src/584index.js585app.js586package.json587\`\`\`588`}589I want to: Create node app launch configuration<br />590## Response:<br />591{JSON.stringify(['package.json', 'src/index.js', 'src/app.js'])}<br /><br />592593# Example 2<br />594## User: <br />595I am working in a workspace that has the following structure: {`596\`\`\`597src/598main.rs599lib.rs600Cargo.toml601\`\`\`602`}603I want to: Launch a rust app with lldb<br />604## Response:<br />605{JSON.stringify(['Cargo.toml', 'src/main.rs'])}<br /><br />606607# Example 3<br />608## User: <br />609I want to: Launch a go app<br />610## Response:<br />611{JSON.stringify(['main.go', 'go.mod'])}<br /><br />612613</TextChunk>614</SystemMessage>615<UserMessage priority={7}>616<MultirootWorkspaceStructure maxSize={1000} />617</UserMessage>618<InputDescription priority={4} {...this.props} />619</>620);621}622}623624class ReferenceFilesFromCliPrompt extends PromptElement<{625debuggerType: string | undefined;626input: IStartDebuggingFromCommandLine;627os: string;628} & BasePromptElementProps, void> {629override render(_state: void, _sizing: PromptSizing): PromptPiece {630return (631<>632<SystemMessage priority={10}>633You are a Visual Studio Code assistant who specializes in debugging and creating launch configurations. Your job is to return an array of file names that may contain useful information to translate a command line invocation into a VS Code debug configuration and build task.<br />634For example, when running a command `make tests`, you should ask for the `Makefile` because it contains information about how the tests are run.<br />635The user will give you a file tree. Make sure to fully qualify paths you return from the tree, including their parent directories:<br />636Do not give any other explanation and return only a JSON array of strings. Avoid wrapping the whole response in triple backticks. Do not include any other information in your response.<br />637<TextChunk priority={8}>638# Example<br />639## User: <br />640I am working in a workspace that has the following structure: {`641\`\`\`642myapp/643package.json644\`\`\`645`}646I ran this on the command line: `npm run start`<br />647## Response:<br />648{JSON.stringify(['myapp/package.json'])}<br /><br />649650# Example<br />651## User: <br />652I ran this on the command line: cargo run<br />653## Response:<br />654{JSON.stringify(['Cargo.toml'])}<br /><br />655</TextChunk>656</SystemMessage>657<UserMessage priority={7} flexGrow={1}>658<StructureOfWorkingDirectory input={this.props.input} />659</UserMessage>660<InputDescription priority={4} {...this.props} />661</>662);663}664}665666class InputDescription extends PromptElement<{667input: StartDebuggingInput;668debuggerType?: string;669os: string;670} & BasePromptElementProps> {671override render() {672if (this.props.input.type === StartDebuggingType.UserQuery && this.props.input.userQuery) {673return <UserMessage>674Here's a description of the app I want to debug: {this.props.input.userQuery}{this.props.debuggerType ? ` and the debugging type: ${this.props.debuggerType}` : ''}</UserMessage>;675} else if (this.props.input.type === StartDebuggingType.UserQuery) {676if (this.props.debuggerType) {677return <UserMessage>678I want to use the ${this.props.debuggerType} debug type for my configuration.679</UserMessage>;680} else {681return <UserMessage>682Find an existing launch config for my app or create one based on my project stucture and workspace</UserMessage>;683}684} else {685return <UserMessage>686My operating system is {this.props.os}.<br />687In the working directory `{(this.props.input.relativeCwd || this.props.input.absoluteCwd).replaceAll('\\', '/')}`, I ran this on the command line:<br />688{'```\n' + this.props.input.args.map(a => a.replaceAll('\n', '\\n')).join(' \\\n ') + '\n```'}689</UserMessage>;690}691}692}693694class DebugTypePrompt extends PromptElement<{695input: StartDebuggingInput;696debuggerTypes: string[];697os: string;698} & BasePromptElementProps, void> {699override render(_state: void, _sizing: PromptSizing): PromptPiece {700const cli = this.props.input.type === StartDebuggingType.CommandLine;701return (702<>703<SystemMessage priority={10}>704You are a Visual Studio Code assistant. Your job is to assist users in using Visual Studio Code by providing knowledge to accomplish their task. Please do not guess a response and instead just respond with a polite apology if you are unsure.<br />705You are a debugging expert. Your job is to return the debug type to use for launch config for the given use case.<br />706Pay attention to my operating system and suggest the best tool for the platform I'm working on. For example, for debugging native code on Windows, you would not suggest the `lldb` type.<br />707{this.props.input.type === StartDebuggingType.CommandLine && <>The command I give you is used to run code that I'm working on. Although the command itself might not directly be my program, you should suggest a tool to debug the likely language I'm working in.<br /></>}708The user will list the debug types they have installed, but this is not a complete list of debug types available. You may suggest a type outside of that list if it's a better fit.<br />709<br />710<TextChunk priority={8}>711# Example 1<br />712## User: <br />713{cli ? 'npx mocha' : 'Node.js'}<br />714## Response:<br />715`node`<br /><br />716717# Example 2<br />718## User: <br />719{cli ? 'python3 example.py' : 'Python'}<br />720## Response:<br />721`debugpy`<br /><br />722723# Example 3<br />724## User: <br />725{cli ? 'mvn test -Dtest=TestCircle' : 'Java'}<br />726## Response:<br />727`java`<br /><br />728</TextChunk>729Suggest the right debug type for my use case. Print ONLY the debug type. NEVER print any other explanation.<br />730</SystemMessage>731<ProjectLabels flexGrow={1} priority={7} embeddedInsideUserMessage={false} />732<InputDescription {...this.props} priority={6} />733<UserMessage priority={5} flexGrow={1}>734<TextChunk>Here are the debug types I have installed:</TextChunk>735<TextChunk flexGrow={1} breakOnWhitespace>736{this.props.debuggerTypes.join('\n')}737</TextChunk>738</UserMessage>739</>740);741}742}743744async function nearestDirectoryWhere<T>(rootDir: string, predicate: (directory: string) => Promise<T | undefined>): Promise<T | undefined> {745while (true) {746const value = await predicate(rootDir);747if (value !== undefined) {748return value;749}750751const parent = dirname(rootDir);752if (parent === rootDir) {753return undefined;754}755756rootDir = parent;757}758}759760761