Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompts/node/panel/startDebugging.tsx
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { BasePromptElementProps, PromptElement, PromptPiece, PromptSizing, SystemMessage, TextChunk, UserMessage } from '@vscode/prompt-tsx';
7
import type * as vscode from 'vscode';
8
import { ChatFetchResponseType, ChatLocation } from '../../../../platform/chat/common/commonTypes';
9
import { IEndpointProvider } from '../../../../platform/endpoint/common/endpointProvider';
10
import { IEnvService } from '../../../../platform/env/common/envService';
11
import { IExtensionsService } from '../../../../platform/extensions/common/extensionsService';
12
import { IFileSystemService } from '../../../../platform/filesystem/common/fileSystemService';
13
import { IIgnoreService } from '../../../../platform/ignore/common/ignoreService';
14
import { ICodeOrDocsSearchItem } from '../../../../platform/remoteSearch/common/codeOrDocsSearchClient';
15
import { IWorkspaceService } from '../../../../platform/workspace/common/workspaceService';
16
import { CancellationToken } from '../../../../util/vs/base/common/cancellation';
17
import { ResourceSet } from '../../../../util/vs/base/common/map';
18
import { basename, dirname } from '../../../../util/vs/base/common/path';
19
import { URI } from '../../../../util/vs/base/common/uri';
20
import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation';
21
import { ChatResponseProgressPart } from '../../../../vscodeTypes';
22
import { getSchemasForTypeAsList } from '../../../onboardDebug/node/parseLaunchConfigFromResponse';
23
import { Turn } from '../../../prompt/common/conversation';
24
import { CopilotIdentityRules } from '../base/copilotIdentity';
25
import { InstructionMessage } from '../base/instructionMessage';
26
import { PromptRenderer } from '../base/promptRenderer';
27
import { SafetyRules } from '../base/safetyRules';
28
import { Tag } from '../base/tag';
29
import { HistoryWithInstructions } from './conversationHistory';
30
import { FileVariable } from './fileVariable';
31
import { ProjectLabels } from './projectLabels';
32
import { workspaceVisualFileTree } from './workspace/visualFileTree';
33
import { MultirootWorkspaceStructure, WorkspaceStructureMetadata } from './workspace/workspaceStructure';
34
35
export const enum StartDebuggingType {
36
UserQuery,
37
CommandLine,
38
}
39
40
interface IStartDebuggingFromUserQuery {
41
type: StartDebuggingType.UserQuery;
42
userQuery?: string;
43
}
44
45
interface IStartDebuggingFromCommandLine {
46
type: StartDebuggingType.CommandLine;
47
args: readonly string[];
48
/** cwd with the ${workspaceFolder} replaced */
49
relativeCwd?: string;
50
/** absolute cwd */
51
absoluteCwd: string;
52
}
53
54
const enum OutputStyle {
55
Readable,
56
ConfigOnly,
57
}
58
59
export type StartDebuggingInput = IStartDebuggingFromUserQuery | IStartDebuggingFromCommandLine;
60
61
export interface StartDebuggingPromptProps extends BasePromptElementProps {
62
input: StartDebuggingInput;
63
history: readonly Turn[];
64
}
65
66
export interface StartDebuggingPromptState {
67
docSearchResults?: ICodeOrDocsSearchItem[];
68
resources?: URI[];
69
schema?: string[];
70
}
71
72
function getLaunchConfigExamples(inputType: StartDebuggingType, outputStyle: OutputStyle) {
73
const o1Object = {
74
configurations: [{
75
type: 'node',
76
request: 'launch',
77
name: 'Launch Program',
78
program: '${workspaceFolder}/app/index.js',
79
args: ['--serve'],
80
}],
81
};
82
83
const o2Object = {
84
configurations: [{
85
type: 'cppvsdbg',
86
request: 'launch',
87
name: 'Launch Program',
88
program: '${workspaceFolder}/${input:executableName}.exe',
89
stopAtEntry: true,
90
}],
91
inputs: [
92
{
93
type: 'promptString',
94
id: 'executableName',
95
description: 'Name of your executable',
96
}
97
]
98
};
99
100
const styleOutput = (output: any) => outputStyle === OutputStyle.ConfigOnly
101
? JSON.stringify(output) : `\`\`\`json\n${JSON.stringify(output, null, 2)}\n\`\`\``;
102
103
if (inputType === StartDebuggingType.UserQuery) {
104
return `# Example
105
User:
106
My operating system is macOS.
107
Create a debug configuration to do the following: launch my node app
108
109
Assistant:
110
${styleOutput(o1Object)}
111
112
# Example
113
User:
114
My operating system is Windows.
115
Create a debug configuration to do the following: debug my c++ program
116
117
Assistant:
118
${styleOutput(o2Object)}
119
`;
120
} else {
121
return `# Example
122
User:
123
My operating system is macOS.
124
In the working directory \${workspaceFolder}/app, I ran this on the command line: node ./index --serve
125
126
Assistant:
127
${styleOutput(o1Object)}
128
129
# Example
130
User:
131
My operating system is Windows.
132
In the working directory \${workspaceFolder}, I ran this on the command line: make test
133
134
Assistant:
135
${styleOutput(o2Object)}
136
`;
137
}
138
}
139
140
export class StartDebuggingPrompt extends PromptElement<StartDebuggingPromptProps, StartDebuggingPromptState> {
141
constructor(props: StartDebuggingPromptProps,
142
@IWorkspaceService private readonly workspace: IWorkspaceService,
143
@IEndpointProvider private readonly endpointProvider: IEndpointProvider,
144
@IInstantiationService private readonly instantiationService: IInstantiationService,
145
@IExtensionsService private readonly extensionsService: IExtensionsService,
146
@IFileSystemService private readonly fileSystemService: IFileSystemService,
147
@IIgnoreService private readonly ignoreService: IIgnoreService,
148
@IEnvService private readonly envService: IEnvService,
149
) {
150
super(props);
151
}
152
153
override async prepare(sizing: PromptSizing, progress: vscode.Progress<vscode.ChatResponseProgressPart> | undefined, token: vscode.CancellationToken): Promise<StartDebuggingPromptState> {
154
if (token.isCancellationRequested) {
155
return {};
156
}
157
158
if (token.isCancellationRequested) {
159
return {};
160
}
161
const debuggerType = await this.getDebuggerType(progress, token);
162
const [resources, schema] = await Promise.all([
163
this.getResources(debuggerType, progress, token),
164
this.getSchema(debuggerType, progress, token)
165
]);
166
return { resources, schema };
167
}
168
169
private async getFiles(requestedFiles: string[], structureMetadata?: WorkspaceStructureMetadata): Promise<URI[] | undefined> {
170
const fileResults = new ResourceSet();
171
const returnedUris = MultirootWorkspaceStructure.toURIs(this.workspace, requestedFiles);
172
173
const fileExists = (file: URI) => this.fileSystemService.stat(file).then(() => true, () => false);
174
175
const tryAdd = async (file: URI) => {
176
if (fileResults.has(file)) {
177
return true;
178
}
179
180
const [exists, ignored] = await Promise.all([
181
fileExists(file),
182
this.ignoreService.isCopilotIgnored(file),
183
]);
184
if (exists && !ignored) {
185
fileResults.add(file);
186
return true;
187
}
188
189
return false;
190
};
191
192
const todo: Promise<unknown>[] = returnedUris.map(async ({ file, relativePath }) => {
193
if (!structureMetadata || await fileExists(file)) {
194
return tryAdd(file);
195
}
196
197
// The model sometimes doesn't fully qualify the path to nested files.
198
// In these cases, try to guess what it means by looking at the what it does give us
199
const bestGuess = structureMetadata.value
200
.flatMap(root => root.tree.files.filter(f => f.path.endsWith(relativePath)))
201
.sort((a, b) => a.path.length - b.path.length) // get the least-nested candidate
202
.at(0);
203
if (bestGuess) {
204
return tryAdd(bestGuess);
205
}
206
});
207
208
const defaultWorkspaceFolder = this.workspace.getWorkspaceFolders().at(0);
209
const fileNeedle = returnedUris.at(0) ?? (defaultWorkspaceFolder && { file: defaultWorkspaceFolder, workspaceFolder: defaultWorkspaceFolder });
210
if (fileNeedle) {
211
for (const file of ['launch.json', 'tasks.json']) {
212
todo.push(tryAdd(URI.joinPath(fileNeedle.workspaceFolder, '.vscode', file)));
213
}
214
215
for (const usefulFile of ['README.md', 'CONTRIBUTING.md']) {
216
const folderFsPath = fileNeedle.workspaceFolder.fsPath;
217
// limit this to avoid looking for files in parent directories of the workspace
218
todo.push(nearestDirectoryWhere(fileNeedle.file.fsPath, dir =>
219
dir.length >= folderFsPath.length ? tryAdd(URI.joinPath(URI.file(dir), usefulFile)) : Promise.resolve(undefined)));
220
}
221
}
222
223
await Promise.all(todo);
224
225
return [...fileResults];
226
}
227
228
private async getResources(debuggerType: string | undefined, progress: vscode.Progress<vscode.ChatResponseProgressPart> | undefined, token: vscode.CancellationToken): Promise<URI[] | undefined> {
229
const r = await this.queryModelForRequestedFiles(debuggerType, progress, token);
230
if (!r?.requestedFiles.length || token.isCancellationRequested) {
231
return;
232
}
233
return this.getFiles(r.requestedFiles, r.structureMetadata);
234
}
235
236
private async queryModelForRequestedFiles(debuggerType: string | undefined, progress: vscode.Progress<vscode.ChatResponseProgressPart> | undefined, token: vscode.CancellationToken) {
237
const endpoint = await this.endpointProvider.getChatEndpoint('copilot-fast');
238
const promptRenderer = this.props.input.type === StartDebuggingType.CommandLine
239
? PromptRenderer.create(
240
this.instantiationService,
241
endpoint,
242
ReferenceFilesFromCliPrompt,
243
{ debuggerType, input: this.props.input, os: this.envService.OS }
244
) : PromptRenderer.create(
245
this.instantiationService,
246
endpoint,
247
ReferenceFilesFromQueryPrompt,
248
{ debuggerType, input: this.props.input, os: this.envService.OS }
249
);
250
251
const prompt = await promptRenderer.render(undefined, token);
252
const structureMetadata = prompt.metadata.get(WorkspaceStructureMetadata);
253
const fetchResult = await endpoint.makeChatRequest(
254
'referenceFiles',
255
prompt.messages,
256
undefined,
257
token,
258
ChatLocation.Panel,
259
);
260
261
if (fetchResult.type !== ChatFetchResponseType.Success) {
262
return undefined;
263
}
264
265
let requestedFiles: string[] | undefined;
266
try {
267
requestedFiles = JSON.parse(fetchResult.value);
268
} catch {
269
return;
270
}
271
if (!Array.isArray(requestedFiles)) {
272
return;
273
}
274
if (this.props.input.type === StartDebuggingType.UserQuery) {
275
// We will check for existing config
276
requestedFiles.push('launch.json');
277
if (!this.props.input.userQuery) {
278
requestedFiles.push('README.md');
279
}
280
}
281
progress?.report(new ChatResponseProgressPart('Requesting resources'));
282
return { requestedFiles, structureMetadata };
283
}
284
285
private async getSchema(debuggerType: string | undefined, progress: vscode.Progress<vscode.ChatResponseProgressPart> | undefined, token: vscode.CancellationToken): Promise<string[] | undefined> {
286
if (!debuggerType) {
287
return;
288
}
289
const schema = getSchemasForTypeAsList(debuggerType, this.extensionsService);
290
if (!schema) {
291
return;
292
}
293
progress?.report(new ChatResponseProgressPart('Identified launch config properties'));
294
return schema;
295
}
296
297
private async getDebuggerType(progress: vscode.Progress<vscode.ChatResponseProgressPart> | undefined, token: vscode.CancellationToken): Promise<string | undefined> {
298
const endpoint = await this.endpointProvider.getChatEndpoint('copilot-fast');
299
300
const promptRenderer = PromptRenderer.create(
301
this.instantiationService,
302
endpoint,
303
DebugTypePrompt,
304
{ debuggerTypes: this.getAllDebuggerTypes(), input: this.props.input, os: this.envService.OS }
305
);
306
307
const prompt = await promptRenderer.render(undefined, token);
308
const fetchResult = await endpoint.makeChatRequest(
309
'debugType',
310
prompt.messages,
311
undefined,
312
token,
313
ChatLocation.Panel,
314
);
315
316
if (fetchResult.type !== ChatFetchResponseType.Success) {
317
return undefined;
318
}
319
320
// The model likes to return text like "You should use `node", so detect backticks
321
return /`(.*?)`/.exec(fetchResult.value)?.[1] || fetchResult.value;
322
}
323
324
private getAllDebuggerTypes(): string[] {
325
return this.extensionsService.allAcrossExtensionHosts.filter(e => !!e.packageJSON?.contributes?.debuggers).map(e => {
326
const result: string[] = [];
327
for (const d of e.packageJSON?.contributes?.debuggers) {
328
if (d.type === '*' || d.deprecated) {
329
continue;
330
}
331
result.push(`- ${d.type}: ${d.label} (${e.id})`);
332
}
333
return result;
334
}).flat();
335
}
336
337
override render(state: StartDebuggingPromptState, sizing: PromptSizing): PromptPiece | undefined {
338
const style = this.props.input.type === StartDebuggingType.CommandLine ? OutputStyle.ConfigOnly : OutputStyle.Readable;
339
return (
340
<>
341
<SystemMessage priority={1000}>
342
{style === OutputStyle.ConfigOnly ? (
343
<>
344
You 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 />
345
</>
346
) : (
347
<>
348
You 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 />
349
</>
350
)}
351
<CopilotIdentityRules />
352
<SafetyRules />
353
</SystemMessage>
354
<HistoryWithInstructions historyPriority={600} passPriority history={this.props.history}>
355
<InstructionMessage priority={1000}>
356
{style === OutputStyle.Readable && <>
357
The 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 />
358
</>}
359
Print out the VS Code `launch.json` file needed to debug the command, formatted as JSON.<br />
360
If there are build steps needed before the program can be debugged, be sure to include a `preLaunchTask` property in the launch configuration.
361
If you include a `preLaunchTask` property, {state.resources?.some(r => r.path.endsWith('launch.json'))
362
? <>{' '}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.</>
363
: <>{' '}you MUST also include `tasks.json` file in your response that contains that configuration.</>
364
}{style === OutputStyle.Readable && <>
365
{' '}Include a brief one or two sentence explaination of any such task definition is needed.<br />
366
</>}<br />
367
Pay 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 />
368
If 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 />
369
Do not give any other explanation.<br />
370
If 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 />
371
Always include the following properties in the launch.json file:<br />
372
- 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 />
373
- request: the request type of this launch configuration. Currently, launch and attach are supported.<br />
374
- name: the reader-friendly name to appear in the Debug launch configuration dropdown.<br />
375
If a result is not a valid answer, but it still relates to Visual Studio Code, please still respond.<br />
376
Please do not guess a response and instead just respond with a polite apology if you are unsure.<br />
377
If you believe the given context given to you is incorrect or not relevant you may ignore it.<br />
378
{getLaunchConfigExamples(this.props.input.type, style)}<br />
379
</InstructionMessage>
380
</HistoryWithInstructions>
381
<UserMessage priority={700}>
382
{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 /></>}
383
{state.docSearchResults && state.docSearchResults.map((result) => {
384
if (result?.title && result.contents) {
385
// eslint-disable-next-line local/code-no-unused-expressions
386
<TextChunk>
387
##{result?.title?.trim()} - {result.path}<br />
388
{result.contents}
389
</TextChunk>;
390
}
391
})}
392
</UserMessage>
393
<UserMessage priority={850}>
394
{state.schema && <>Below is a list of properties that the launch config might include. <br />
395
{state.schema.map((property) => {
396
return <TextChunk>{property}<br /></TextChunk>;
397
})})<br /></>}
398
</UserMessage>
399
<UserMessage priority={700} flexGrow={1}>
400
{this.props.input.type === StartDebuggingType.UserQuery
401
? <>
402
If 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 />
403
<MultirootWorkspaceStructure maxSize={1000} />
404
</>
405
: <StructureOfWorkingDirectory input={this.props.input} />
406
}
407
</UserMessage>
408
<UserMessage priority={800}>
409
{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 /></>}
410
{state.resources && state.resources.map((resource) => {
411
const containingFolder = this.workspace.getWorkspaceFolder(resource);
412
const name = containingFolder ? resource.path.substring(containingFolder.path.length + 1) : basename(resource.path);
413
return <FileVariable variableName={name} variableValue={resource}></FileVariable>;
414
})}
415
</UserMessage>
416
<UserMessage priority={850}>
417
{this.props.input.type === StartDebuggingType.UserQuery && state.resources?.some(r => r.path.endsWith('launch.json')) && (
418
<>
419
{this.props.input.userQuery
420
? <>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 /></>
421
: <>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 /></>
422
}
423
If no match is found, include the new configuration that was generated. End the response with GENERATED_CONFIG.<br />
424
</>
425
)}
426
</UserMessage>
427
{style === OutputStyle.ConfigOnly
428
? <UserMessage priority={850}>
429
<Tag name='example'>
430
In this example, we're debugging a simple Python file, so we only need a launch.json:<br />
431
<Tag name='request'>
432
In the working directory, I ran this on the command line: `python main.py`<br />
433
</Tag>
434
<Tag name='response'>
435
launch.json:<br />
436
```json<br />
437
{JSON.stringify({
438
type: 'python',
439
request: 'launch',
440
name: 'Launch Program',
441
program: '${workspaceFolder}/main.py',
442
}, null, '\t')}<br />
443
```<br />
444
</Tag>
445
</Tag>
446
<Tag name='example'>
447
In this example, generate both a launch.json and tasks.json because the program needs to be built before it can be debugged:<br />
448
<Tag name='request'>
449
In the working directory, I ran this on the command line: `./my-program.exe`<br />
450
</Tag>
451
<Tag name='response'>
452
launch.json:<br />
453
```json<br />
454
{JSON.stringify({
455
configurations: [{
456
'type': 'cppvsdbg',
457
'request': 'launch',
458
'name': 'Launch Program',
459
'program': '${workspaceFolder}/my-program.exe',
460
'preLaunchTask': 'build'
461
}]
462
}, null, '\t')}<br />
463
```<br />
464
tasks.json:<br />
465
```json<br />
466
{JSON.stringify({
467
tasks: [{
468
'type': 'shell',
469
'label': 'build',
470
'command': 'make',
471
'args': ['build']
472
}]
473
}, null, '\t')}<br />
474
```<br />
475
</Tag>
476
</Tag>
477
</UserMessage>
478
: <UserMessage priority={850}>
479
<Tag name='example'>
480
In this example, we're debugging a simple Python file, so we only need a launch.json:<br />
481
<Tag name='request'>
482
Here's a description of the app I want to debug: "python file"<br />
483
In my workspace I have the files main.py, tox.ini, and README.md.<br />
484
</Tag>
485
<Tag name='response'>
486
Here is your `launch.json` configuration:<br />
487
```json<br />
488
{JSON.stringify({
489
type: 'python',
490
request: 'launch',
491
name: 'Launch Program',
492
program: '${workspaceFolder}/main.py',
493
}, null, '\t')}<br />
494
```<br />
495
</Tag>
496
</Tag>
497
<Tag name='example'>
498
In this example, generate both a launch.json and tasks.json because the program needs to be built before it can be debugged:<br />
499
<Tag name='request'>
500
Here's a description of the app I want to debug: "my-program"<br />
501
In my workspace I have the files Makefile, my-program.cpp.<br />
502
</Tag>
503
<Tag name='response'>
504
Here is your `launch.json` configuration:<br />
505
```json<br />
506
{JSON.stringify({
507
configurations: [{
508
'type': 'cppvsdbg',
509
'request': 'launch',
510
'name': 'Launch Program',
511
'program': '${workspaceFolder}/my-program.exe',
512
'preLaunchTask': 'build'
513
}]
514
}, null, '\t')}<br />
515
```<br />
516
It looks like you build your project using your Makefile, so let's add a `tasks.json` to do that before each debug session:<br />
517
```json<br />
518
{JSON.stringify({
519
tasks: [{
520
'type': 'shell',
521
'label': 'build',
522
'command': 'make',
523
'args': ['build']
524
}]
525
}, null, '\t')}<br />
526
```<br />
527
</Tag>
528
</Tag>
529
</UserMessage>}
530
<InputDescription priority={900} input={this.props.input} os={this.envService.OS} />
531
</>
532
);
533
}
534
}
535
type WorkingDirectoryStructureProps = {
536
input: IStartDebuggingFromCommandLine;
537
} & BasePromptElementProps;
538
539
class StructureOfWorkingDirectory extends PromptElement<WorkingDirectoryStructureProps, void> {
540
constructor(
541
props: WorkingDirectoryStructureProps,
542
@IInstantiationService private readonly instantiationService: IInstantiationService,
543
@IWorkspaceService private readonly workspaceService: IWorkspaceService,
544
) {
545
super(props);
546
}
547
override async render(_state: void, sizing: PromptSizing, _progress: unknown, token?: CancellationToken) {
548
const maxSize = sizing.tokenBudget / 2; // note: size in the tree is in chars, /2 to be safe
549
const wf = this.props.input.relativeCwd
550
? this.workspaceService.getWorkspaceFolder(URI.file(this.props.input.absoluteCwd))
551
: undefined;
552
553
if (wf) {
554
const tree = await this.instantiationService.invokeFunction(accessor => workspaceVisualFileTree(accessor, wf, { maxLength: maxSize }, token ?? CancellationToken.None));
555
return <>
556
My workspace folder (`${'{'}workspaceFolder{'}'}`) has the following structure:<br />
557
<br />
558
<meta value={new WorkspaceStructureMetadata([{ label: '', tree }])} local />
559
{tree.tree}
560
</>;
561
}
562
563
return <MultirootWorkspaceStructure maxSize={maxSize} />;
564
}
565
}
566
567
class ReferenceFilesFromQueryPrompt extends PromptElement<{
568
debuggerType: string | undefined;
569
input: IStartDebuggingFromUserQuery;
570
os: string;
571
} & BasePromptElementProps, void> {
572
override render(_state: void, _sizing: PromptSizing): PromptPiece {
573
return (
574
<>
575
<SystemMessage priority={10}>
576
You 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 />
577
The user will give you a file tree. Make sure to fully qualify paths you return from the tree, including their parent directories:<br />
578
Do 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 />
579
<TextChunk priority={8}>
580
# Example 1<br />
581
## User: <br />
582
I am working in a workspace that has the following structure: {`
583
\`\`\`
584
src/
585
index.js
586
app.js
587
package.json
588
\`\`\`
589
`}
590
I want to: Create node app launch configuration<br />
591
## Response:<br />
592
{JSON.stringify(['package.json', 'src/index.js', 'src/app.js'])}<br /><br />
593
594
# Example 2<br />
595
## User: <br />
596
I am working in a workspace that has the following structure: {`
597
\`\`\`
598
src/
599
main.rs
600
lib.rs
601
Cargo.toml
602
\`\`\`
603
`}
604
I want to: Launch a rust app with lldb<br />
605
## Response:<br />
606
{JSON.stringify(['Cargo.toml', 'src/main.rs'])}<br /><br />
607
608
# Example 3<br />
609
## User: <br />
610
I want to: Launch a go app<br />
611
## Response:<br />
612
{JSON.stringify(['main.go', 'go.mod'])}<br /><br />
613
614
</TextChunk>
615
</SystemMessage>
616
<UserMessage priority={7}>
617
<MultirootWorkspaceStructure maxSize={1000} />
618
</UserMessage>
619
<InputDescription priority={4} {...this.props} />
620
</>
621
);
622
}
623
}
624
625
class ReferenceFilesFromCliPrompt extends PromptElement<{
626
debuggerType: string | undefined;
627
input: IStartDebuggingFromCommandLine;
628
os: string;
629
} & BasePromptElementProps, void> {
630
override render(_state: void, _sizing: PromptSizing): PromptPiece {
631
return (
632
<>
633
<SystemMessage priority={10}>
634
You 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 />
635
For example, when running a command `make tests`, you should ask for the `Makefile` because it contains information about how the tests are run.<br />
636
The user will give you a file tree. Make sure to fully qualify paths you return from the tree, including their parent directories:<br />
637
Do 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 />
638
<TextChunk priority={8}>
639
# Example<br />
640
## User: <br />
641
I am working in a workspace that has the following structure: {`
642
\`\`\`
643
myapp/
644
package.json
645
\`\`\`
646
`}
647
I ran this on the command line: `npm run start`<br />
648
## Response:<br />
649
{JSON.stringify(['myapp/package.json'])}<br /><br />
650
651
# Example<br />
652
## User: <br />
653
I ran this on the command line: cargo run<br />
654
## Response:<br />
655
{JSON.stringify(['Cargo.toml'])}<br /><br />
656
</TextChunk>
657
</SystemMessage>
658
<UserMessage priority={7} flexGrow={1}>
659
<StructureOfWorkingDirectory input={this.props.input} />
660
</UserMessage>
661
<InputDescription priority={4} {...this.props} />
662
</>
663
);
664
}
665
}
666
667
class InputDescription extends PromptElement<{
668
input: StartDebuggingInput;
669
debuggerType?: string;
670
os: string;
671
} & BasePromptElementProps> {
672
override render() {
673
if (this.props.input.type === StartDebuggingType.UserQuery && this.props.input.userQuery) {
674
return <UserMessage>
675
Here'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>;
676
} else if (this.props.input.type === StartDebuggingType.UserQuery) {
677
if (this.props.debuggerType) {
678
return <UserMessage>
679
I want to use the ${this.props.debuggerType} debug type for my configuration.
680
</UserMessage>;
681
} else {
682
return <UserMessage>
683
Find an existing launch config for my app or create one based on my project stucture and workspace</UserMessage>;
684
}
685
} else {
686
return <UserMessage>
687
My operating system is {this.props.os}.<br />
688
In the working directory `{(this.props.input.relativeCwd || this.props.input.absoluteCwd).replaceAll('\\', '/')}`, I ran this on the command line:<br />
689
{'```\n' + this.props.input.args.map(a => a.replaceAll('\n', '\\n')).join(' \\\n ') + '\n```'}
690
</UserMessage>;
691
}
692
}
693
}
694
695
class DebugTypePrompt extends PromptElement<{
696
input: StartDebuggingInput;
697
debuggerTypes: string[];
698
os: string;
699
} & BasePromptElementProps, void> {
700
override render(_state: void, _sizing: PromptSizing): PromptPiece {
701
const cli = this.props.input.type === StartDebuggingType.CommandLine;
702
return (
703
<>
704
<SystemMessage priority={10}>
705
You 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 />
706
You are a debugging expert. Your job is to return the debug type to use for launch config for the given use case.<br />
707
Pay 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 />
708
{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 /></>}
709
The 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 />
710
<br />
711
<TextChunk priority={8}>
712
# Example 1<br />
713
## User: <br />
714
{cli ? 'npx mocha' : 'Node.js'}<br />
715
## Response:<br />
716
`node`<br /><br />
717
718
# Example 2<br />
719
## User: <br />
720
{cli ? 'python3 example.py' : 'Python'}<br />
721
## Response:<br />
722
`debugpy`<br /><br />
723
724
# Example 3<br />
725
## User: <br />
726
{cli ? 'mvn test -Dtest=TestCircle' : 'Java'}<br />
727
## Response:<br />
728
`java`<br /><br />
729
</TextChunk>
730
Suggest the right debug type for my use case. Print ONLY the debug type. NEVER print any other explanation.<br />
731
</SystemMessage>
732
<ProjectLabels flexGrow={1} priority={7} embeddedInsideUserMessage={false} />
733
<InputDescription {...this.props} priority={6} />
734
<UserMessage priority={5} flexGrow={1}>
735
<TextChunk>Here are the debug types I have installed:</TextChunk>
736
<TextChunk flexGrow={1} breakOnWhitespace>
737
{this.props.debuggerTypes.join('\n')}
738
</TextChunk>
739
</UserMessage>
740
</>
741
);
742
}
743
}
744
745
async function nearestDirectoryWhere<T>(rootDir: string, predicate: (directory: string) => Promise<T | undefined>): Promise<T | undefined> {
746
while (true) {
747
const value = await predicate(rootDir);
748
if (value !== undefined) {
749
return value;
750
}
751
752
const parent = dirname(rootDir);
753
if (parent === rootDir) {
754
return undefined;
755
}
756
757
rootDir = parent;
758
}
759
}
760
761