1
>
/*---------------------------------------------------------------------------------------------
promptFileAttributes.ts
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 { dirname } from '../../../../../../base/common/resources.js';
7
>
import { URI } from '../../../../../../base/common/uri.js';
8
>
import { localize } from '../../../../../../nls.js';
9
>
import { SpecedToolAliases } from '../../tools/languageModelToolsService.js';
10
>
import { CLAUDE_AGENTS_SOURCE_FOLDER, isInClaudeRulesFolder } from '../config/promptFileLocations.js';
11
>
import { PromptHeader, PromptHeaderAttributes } from '../promptFileParser.js';
12
>
import { PromptsType, Target } from '../promptTypes.js';
13
>
14
>
export namespace GithubPromptHeaderAttributes {
15
>
export const mcpServers = 'mcp-servers';
16
>
export const github = 'github';
17
>
}
18
>
19
>
export namespace ClaudeHeaderAttributes {
20
>
export const disallowedTools = 'disallowedTools';
21
>
}
22
>
23
>
export function isTarget(value: unknown): value is Target {
24
return value === Target.VSCode || value === Target.GitHubCopilot || value === Target.Claude || value === Target.Undefined;
25
}
27
>
28
>
interface IAttributeDefinition {
29
>
readonly type: string;
30
>
readonly description: string;
31
>
readonly defaults?: readonly string[];
32
>
readonly items?: readonly { name: string; description?: string }[];
33
>
readonly enums?: readonly { name: string; description?: string }[];
34
>
}
35
>
36
>
const booleanAttributeEnumValues: readonly IValueEntry[] = [
37
>
{ name: 'true' },
38
>
{ name: 'false' }
39
>
];
40
>
41
>
const targetAttributeEnumValues: readonly IValueEntry[] = [
42
>
{ name: 'vscode' },
43
>
{ name: 'github-copilot' },
44
>
];
45
>
46
>
// Attribute metadata for prompt files (`*.prompt.md`).
47
>
export const promptFileAttributes: Record<string, IAttributeDefinition> = {
48
>
[PromptHeaderAttributes.name]: {
49
>
type: 'scalar',
50
>
description: localize('promptHeader.prompt.name', 'The name of the prompt. This is also the name of the slash command that will run this prompt.'),
51
>
},
52
>
[PromptHeaderAttributes.description]: {
53
>
type: 'scalar',
54
>
description: localize('promptHeader.prompt.description', 'The description of the reusable prompt, what it does and when to use it.'),
55
>
},
56
>
[PromptHeaderAttributes.argumentHint]: {
57
>
type: 'scalar',
58
>
description: localize('promptHeader.prompt.argumentHint', 'The argument-hint describes what inputs the prompt expects or supports.'),
59
>
},
60
>
[PromptHeaderAttributes.model]: {
61
>
type: 'scalar | sequence',
62
>
description: localize('promptHeader.prompt.model', 'The model to use in this prompt. Can also be a list of models. The first available model will be used.'),
63
>
},
64
>
[PromptHeaderAttributes.tools]: {
65
>
type: 'scalar | sequence',
66
>
description: localize('promptHeader.prompt.tools', 'The tools to use in this prompt.'),
67
>
defaults: ['[]', '[\'search\', \'edit\', \'web\']'],
68
>
},
69
>
[PromptHeaderAttributes.agent]: {
70
>
type: 'scalar',
71
>
description: localize('promptHeader.prompt.agent.description', 'The agent to use when running this prompt.'),
72
>
},
73
>
[PromptHeaderAttributes.mode]: {
74
>
type: 'scalar',
75
>
description: localize('promptHeader.prompt.agent.description', 'The agent to use when running this prompt.'),
76
>
},
77
>
};
78
>
79
>
// Attribute metadata for instructions files (`*.instructions.md`).
80
>
export const instructionAttributes: Record<string, IAttributeDefinition> = {
81
>
[PromptHeaderAttributes.name]: {
82
>
type: 'scalar',
83
>
description: localize('promptHeader.instructions.name', 'The name of the instruction file as shown in the UI. If not set, the name is derived from the file name.'),
84
>
},
85
>
[PromptHeaderAttributes.description]: {
86
>
type: 'scalar',
87
>
description: localize('promptHeader.instructions.description', 'The description of the instruction file. It can be used to provide additional context or information about the instructions and is passed to the language model as part of the prompt.'),
88
>
},
89
>
[PromptHeaderAttributes.applyTo]: {
90
>
type: 'scalar',
91
>
description: localize('promptHeader.instructions.applyToRange', 'One or more glob pattern (separated by comma) that describe for which files the instructions apply to. Based on these patterns, the file is automatically included in the prompt, when the context contains a file that matches one or more of these patterns. Use `**` when you want this file to always be added.\nExample: `**/*.ts`, `**/*.js`, `client/**`'),
92
>
defaults: [
93
>
'\'**\'',
94
>
'\'**/*.ts, **/*.js\'',
95
>
'\'**/*.php\'',
96
>
'\'**/*.py\''
97
>
],
98
>
},
99
>
[PromptHeaderAttributes.excludeAgent]: {
100
>
type: 'scalar | sequence',
101
>
description: localize('promptHeader.instructions.excludeAgent', 'One or more agents to exclude from using this instruction file.'),
102
>
},
103
>
};
104
>
105
>
// Attribute metadata for custom agent files (`*.agent.md`).
106
>
export const customAgentAttributes: Record<string, IAttributeDefinition> = {
107
>
[PromptHeaderAttributes.name]: {
108
>
type: 'scalar',
109
>
description: localize('promptHeader.agent.name', 'The name of the agent as shown in the UI.'),
110
>
},
111
>
[PromptHeaderAttributes.description]: {
112
>
type: 'scalar',
113
>
description: localize('promptHeader.agent.description', 'The description of the custom agent, what it does and when to use it.'),
114
>
},
115
>
[PromptHeaderAttributes.argumentHint]: {
116
>
type: 'scalar',
117
>
description: localize('promptHeader.agent.argumentHint', 'The argument-hint describes what inputs the custom agent expects or supports.'),
118
>
},
119
>
[PromptHeaderAttributes.model]: {
120
>
type: 'scalar | sequence',
121
>
description: localize('promptHeader.agent.model', 'Specify the model that runs this custom agent. Can also be a list of models. The first available model will be used.'),
122
>
},
123
>
[PromptHeaderAttributes.tools]: {
124
>
type: 'scalar | sequence',
125
>
description: localize('promptHeader.agent.tools', 'The set of tools that the custom agent has access to.'),
126
>
defaults: ['[]', '[search, edit, web]'],
127
>
},
128
>
[PromptHeaderAttributes.handOffs]: {
129
>
type: 'sequence',
130
>
description: localize('promptHeader.agent.handoffs', 'Possible handoff actions when the agent has completed its task.'),
131
>
},
132
>
[PromptHeaderAttributes.target]: {
133
>
type: 'scalar',
134
>
description: localize('promptHeader.agent.target', 'The target to which the header attributes like tools apply to. Possible values are `github-copilot` and `vscode`.'),
135
>
enums: targetAttributeEnumValues,
136
>
},
137
>
[PromptHeaderAttributes.infer]: {
138
>
type: 'scalar',
139
>
description: localize('promptHeader.agent.infer', 'Controls visibility of the agent.'),
140
>
enums: booleanAttributeEnumValues,
141
>
},
142
>
[PromptHeaderAttributes.agents]: {
143
>
type: 'sequence',
144
>
description: localize('promptHeader.agent.agents', 'One or more agents that this agent can use as subagents. Use \'*\' to specify all available agents.'),
145
>
defaults: ['["*"]'],
146
>
},
147
>
[PromptHeaderAttributes.userInvocable]: {
148
>
type: 'scalar',
149
>
description: localize('promptHeader.agent.userInvocable', 'Whether the agent can be selected and invoked by users in the UI.'),
150
>
enums: booleanAttributeEnumValues,
151
>
},
152
>
[PromptHeaderAttributes.disableModelInvocation]: {
153
>
type: 'scalar',
154
>
description: localize('promptHeader.agent.disableModelInvocation', 'If true, prevents the agent from being invoked as a subagent.'),
155
>
enums: booleanAttributeEnumValues,
156
>
},
157
>
[PromptHeaderAttributes.advancedOptions]: {
158
>
type: 'map',
159
>
description: localize('promptHeader.agent.advancedOptions', 'Advanced options for custom agent behavior.'),
160
>
},
161
>
[GithubPromptHeaderAttributes.github]: {
162
>
type: 'map',
163
>
description: localize('promptHeader.agent.github', 'GitHub-specific configuration for the agent, such as token permissions.'),
164
>
},
165
>
[PromptHeaderAttributes.hooks]: {
166
>
type: 'map',
167
>
description: localize('promptHeader.agent.hooks', 'Lifecycle hooks scoped to this agent. Define hooks that run only while this agent is active.'),
168
>
},
169
>
};
170
>
171
>
// Attribute metadata for skill files (`SKILL.md`).
172
>
export const skillAttributes: Record<string, IAttributeDefinition> = {
173
>
[PromptHeaderAttributes.name]: {
174
>
type: 'scalar',
175
>
description: localize('promptHeader.skill.name', 'The name of the skill.'),
176
>
},
177
>
[PromptHeaderAttributes.description]: {
178
>
type: 'scalar',
179
>
description: localize('promptHeader.skill.description', 'The description of the skill. The description is added to every request and will be used by the agent to decide when to load the skill.'),
180
>
},
181
>
[PromptHeaderAttributes.argumentHint]: {
182
>
type: 'scalar',
183
>
description: localize('promptHeader.skill.argumentHint', 'Hint shown during autocomplete to indicate expected arguments. Example: [issue-number] or [filename] [format]'),
184
>
},
185
>
[PromptHeaderAttributes.userInvocable]: {
186
>
type: 'scalar',
187
>
description: localize('promptHeader.skill.userInvocable', 'Set to false to hide from the / menu. Use for background knowledge users should not invoke directly. Default: true.'),
188
>
enums: booleanAttributeEnumValues,
189
>
},
190
>
[PromptHeaderAttributes.disableModelInvocation]: {
191
>
type: 'scalar',
192
>
description: localize('promptHeader.skill.disableModelInvocation', 'Set to true to prevent the agent from automatically loading this skill. Use for workflows you want to trigger manually with /name. Default: false.'),
193
>
enums: booleanAttributeEnumValues,
194
>
},
195
>
[PromptHeaderAttributes.license]: {
196
>
type: 'scalar | map',
197
>
description: localize('promptHeader.skill.license', 'License information for the skill.'),
198
>
},
199
>
[PromptHeaderAttributes.compatibility]: {
200
>
type: 'scalar | map',
201
>
description: localize('promptHeader.skill.compatibility', 'Compatibility metadata for environments or runtimes.'),
202
>
},
203
>
[PromptHeaderAttributes.metadata]: {
204
>
type: 'map',
205
>
description: localize('promptHeader.skill.metadata', 'Additional metadata for the skill.'),
206
>
},
207
>
[PromptHeaderAttributes.context]: {
208
>
type: 'scalar',
209
>
description: localize('promptHeader.skill.context', 'Controls how the skill is loaded. Set to \'fork\' to spawn a subagent with the skill instructions instead of returning them inline.'),
210
>
enums: [{ name: 'fork', description: localize('promptHeader.skill.context.fork', 'Spawn a subagent with the skill instructions injected as system context.') }],
211
>
},
212
>
};
213
>
214
>
const allAttributeNames: Record<PromptsType, string[]> = {
215
>
[PromptsType.prompt]: Object.keys(promptFileAttributes),
216
>
[PromptsType.instructions]: Object.keys(instructionAttributes),
217
>
[PromptsType.agent]: Object.keys(customAgentAttributes),
218
>
[PromptsType.skill]: Object.keys(skillAttributes),
219
>
[PromptsType.hook]: [], // hooks are JSON files, not markdown with YAML frontmatter
220
>
};
221
>
const githubCopilotAgentAttributeNames = [PromptHeaderAttributes.name, PromptHeaderAttributes.description, PromptHeaderAttributes.tools, PromptHeaderAttributes.target, GithubPromptHeaderAttributes.mcpServers, GithubPromptHeaderAttributes.github, PromptHeaderAttributes.infer];
222
>
const recommendedAttributeNames: Record<PromptsType, string[]> = {
223
>
[PromptsType.prompt]: allAttributeNames[PromptsType.prompt].filter(name => !isNonRecommendedAttribute(name)),
224
>
[PromptsType.instructions]: allAttributeNames[PromptsType.instructions].filter(name => !isNonRecommendedAttribute(name)),
225
>
[PromptsType.agent]: allAttributeNames[PromptsType.agent].filter(name => !isNonRecommendedAttribute(name)),
226
>
[PromptsType.skill]: allAttributeNames[PromptsType.skill].filter(name => !isNonRecommendedAttribute(name)),
227
>
[PromptsType.hook]: [], // hooks are JSON files, not markdown with YAML frontmatter
228
>
};
229
>
230
>
export function getValidAttributeNames(promptType: PromptsType, includeNonRecommended: boolean, target: Target): string[] {
231
if (target === Target.Claude) {
232
if (promptType === PromptsType.instructions) {