@@ -16,7 +16,7 @@ import { getPromptsTypeForLanguageId, PromptsType } from '../promptTypes.js';
1616import { IPromptsService } from '../service/promptsService.js' ;
1717import { Iterable } from '../../../../../../base/common/iterator.js' ;
1818import { PromptHeader , PromptHeaderAttributes } from '../promptFileParser.js' ;
19- import { getValidAttributeNames } from './promptValidator.js' ;
19+ import { getValidAttributeNames , isGithubTarget , knownGithubCopilotTools } from './promptValidator.js' ;
2020import { localize } from '../../../../../../nls.js' ;
2121
2222export class PromptHeaderAutocompletion implements CompletionItemProvider {
@@ -55,13 +55,13 @@ export class PromptHeaderAutocompletion implements CompletionItemProvider {
5555 return undefined ;
5656 }
5757
58- const parser = this . promptsService . getParsedPromptFile ( model ) ;
59- const header = parser . header ;
58+ const parsedAST = this . promptsService . getParsedPromptFile ( model ) ;
59+ const header = parsedAST . header ;
6060 if ( ! header ) {
6161 return undefined ;
6262 }
6363
64- const headerRange = parser . header . range ;
64+ const headerRange = parsedAST . header . range ;
6565 if ( position . lineNumber < headerRange . startLineNumber || position . lineNumber >= headerRange . endLineNumber ) {
6666 // if the position is not inside the header, we don't provide any completions
6767 return undefined ;
@@ -72,42 +72,45 @@ export class PromptHeaderAutocompletion implements CompletionItemProvider {
7272 const colonPosition = colonIndex !== - 1 ? new Position ( position . lineNumber , colonIndex + 1 ) : undefined ;
7373
7474 if ( ! colonPosition || position . isBeforeOrEqual ( colonPosition ) ) {
75- return this . providePropertyCompletions ( model , position , headerRange , colonPosition , promptType ) ;
75+ return this . provideAttributeNameCompletions ( model , position , header , colonPosition , promptType ) ;
7676 } else if ( colonPosition && colonPosition . isBefore ( position ) ) {
7777 return this . provideValueCompletions ( model , position , header , colonPosition , promptType ) ;
7878 }
7979 return undefined ;
8080 }
81- private async providePropertyCompletions (
81+ private async provideAttributeNameCompletions (
8282 model : ITextModel ,
8383 position : Position ,
84- headerRange : Range ,
84+ header : PromptHeader ,
8585 colonPosition : Position | undefined ,
8686 promptType : PromptsType ,
8787 ) : Promise < CompletionList | undefined > {
8888
8989 const suggestions : CompletionItem [ ] = [ ] ;
90- const supportedProperties = new Set ( getValidAttributeNames ( promptType , false ) ) ;
91- this . removeUsedProperties ( supportedProperties , model , headerRange , position ) ;
9290
93- const getInsertText = ( property : string ) : string => {
91+ const isGitHubTarget = isGithubTarget ( promptType , header . target ) ;
92+ const attributesToPropose = new Set ( getValidAttributeNames ( promptType , false , isGitHubTarget ) ) ;
93+ for ( const attr of header . attributes ) {
94+ attributesToPropose . delete ( attr . key ) ;
95+ }
96+ const getInsertText = ( key : string ) : string => {
9497 if ( colonPosition ) {
95- return property ;
98+ return key ;
9699 }
97- const valueSuggestions = this . getValueSuggestions ( promptType , property ) ;
100+ const valueSuggestions = this . getValueSuggestions ( promptType , key ) ;
98101 if ( valueSuggestions . length > 0 ) {
99- return `${ property } : \${0:${ valueSuggestions [ 0 ] } }` ;
102+ return `${ key } : \${0:${ valueSuggestions [ 0 ] } }` ;
100103 } else {
101- return `${ property } : \$0` ;
104+ return `${ key } : \$0` ;
102105 }
103106 } ;
104107
105108
106- for ( const property of supportedProperties ) {
109+ for ( const attribute of attributesToPropose ) {
107110 const item : CompletionItem = {
108- label : property ,
111+ label : attribute ,
109112 kind : CompletionItemKind . Property ,
110- insertText : getInsertText ( property ) ,
113+ insertText : getInsertText ( attribute ) ,
111114 insertTextRules : CompletionItemInsertTextRule . InsertAsSnippet ,
112115 range : new Range ( position . lineNumber , 1 , position . lineNumber , ! colonPosition ? model . getLineMaxColumn ( position . lineNumber ) : colonPosition . column ) ,
113116 } ;
@@ -127,28 +130,29 @@ export class PromptHeaderAutocompletion implements CompletionItemProvider {
127130
128131 const suggestions : CompletionItem [ ] = [ ] ;
129132 const lineContent = model . getLineContent ( position . lineNumber ) ;
130- const property = lineContent . substring ( 0 , colonPosition . column - 1 ) . trim ( ) ;
133+ const attribute = lineContent . substring ( 0 , colonPosition . column - 1 ) . trim ( ) ;
131134
132- if ( ! getValidAttributeNames ( promptType , true ) . includes ( property ) ) {
135+ const isGitHubTarget = isGithubTarget ( promptType , header . target ) ;
136+ if ( ! getValidAttributeNames ( promptType , true , isGitHubTarget ) . includes ( attribute ) ) {
133137 return undefined ;
134138 }
135139
136140 if ( promptType === PromptsType . prompt || promptType === PromptsType . agent ) {
137141 // if the position is inside the tools metadata, we provide tool name completions
138- const result = this . provideToolCompletions ( model , position , header ) ;
142+ const result = this . provideToolCompletions ( model , position , header , isGitHubTarget ) ;
139143 if ( result ) {
140144 return result ;
141145 }
142146 }
143147
144148 const bracketIndex = lineContent . indexOf ( '[' ) ;
145149 if ( bracketIndex !== - 1 && bracketIndex <= position . column - 1 ) {
146- // if the property is already inside a bracket, we don't provide value completions
150+ // if the value is already inside a bracket, we don't provide value completions
147151 return undefined ;
148152 }
149153
150154 const whilespaceAfterColon = ( lineContent . substring ( colonPosition . column ) . match ( / ^ \s * / ) ?. [ 0 ] . length ) ?? 0 ;
151- const values = this . getValueSuggestions ( promptType , property ) ;
155+ const values = this . getValueSuggestions ( promptType , attribute ) ;
152156 for ( const value of values ) {
153157 const item : CompletionItem = {
154158 label : value ,
@@ -158,7 +162,7 @@ export class PromptHeaderAutocompletion implements CompletionItemProvider {
158162 } ;
159163 suggestions . push ( item ) ;
160164 }
161- if ( property === PromptHeaderAttributes . handOffs && ( promptType === PromptsType . agent ) ) {
165+ if ( attribute === PromptHeaderAttributes . handOffs && ( promptType === PromptsType . agent ) ) {
162166 const value = [
163167 '' ,
164168 ' - label: Start Implementation' ,
@@ -177,39 +181,39 @@ export class PromptHeaderAutocompletion implements CompletionItemProvider {
177181 return { suggestions } ;
178182 }
179183
180- private removeUsedProperties ( properties : Set < string > , model : ITextModel , headerRange : Range , position : Position ) : void {
181- for ( let i = headerRange . startLineNumber ; i <= headerRange . endLineNumber ; i ++ ) {
182- if ( i !== position . lineNumber ) {
183- const lineText = model . getLineContent ( i ) ;
184- const colonIndex = lineText . indexOf ( ':' ) ;
185- if ( colonIndex !== - 1 ) {
186- const property = lineText . substring ( 0 , colonIndex ) . trim ( ) ;
187- properties . delete ( property ) ;
184+ private getValueSuggestions ( promptType : string , attribute : string ) : string [ ] {
185+ switch ( attribute ) {
186+ case PromptHeaderAttributes . applyTo :
187+ if ( promptType === PromptsType . instructions ) {
188+ return [ `'**'` , `'**/*.ts, **/*.js'` , `'**/*.php'` , `'**/*.py'` ] ;
189+ }
190+ break ;
191+ case PromptHeaderAttributes . agent :
192+ case PromptHeaderAttributes . mode :
193+ if ( promptType === PromptsType . prompt ) {
194+ // Get all available agents (builtin + custom)
195+ const agents = this . chatModeService . getModes ( ) ;
196+ const suggestions : string [ ] = [ ] ;
197+ for ( const agent of Iterable . concat ( agents . builtin , agents . custom ) ) {
198+ suggestions . push ( agent . name ) ;
199+ }
200+ return suggestions ;
201+ }
202+ case PromptHeaderAttributes . target :
203+ if ( promptType === PromptsType . agent ) {
204+ return [ 'vscode' , 'github-copilot' ] ;
205+ }
206+ break ;
207+ case PromptHeaderAttributes . tools :
208+ if ( promptType === PromptsType . prompt || promptType === PromptsType . agent ) {
209+ return [ '[]' , `['search', 'edit', 'fetch']` ] ;
210+ }
211+ break ;
212+ case PromptHeaderAttributes . model :
213+ if ( promptType === PromptsType . prompt || promptType === PromptsType . agent ) {
214+ return this . getModelNames ( promptType === PromptsType . agent ) ;
188215 }
189- }
190- }
191- }
192-
193- private getValueSuggestions ( promptType : string , property : string ) : string [ ] {
194- if ( promptType === PromptsType . instructions && property === PromptHeaderAttributes . applyTo ) {
195- return [ `'**'` , `'**/*.ts, **/*.js'` , `'**/*.php'` , `'**/*.py'` ] ;
196- }
197- if ( promptType === PromptsType . prompt && ( property === PromptHeaderAttributes . agent || property === PromptHeaderAttributes . mode ) ) {
198- // Get all available agents (builtin + custom)
199- const agents = this . chatModeService . getModes ( ) ;
200- const suggestions : string [ ] = [ ] ;
201- for ( const agent of Iterable . concat ( agents . builtin , agents . custom ) ) {
202- suggestions . push ( agent . name ) ;
203- }
204- return suggestions ;
205- }
206- if ( property === PromptHeaderAttributes . tools && ( promptType === PromptsType . prompt || promptType === PromptsType . agent ) ) {
207- return [ '[]' , `['search', 'edit', 'fetch']` ] ;
208- }
209- if ( property === PromptHeaderAttributes . model && ( promptType === PromptsType . prompt || promptType === PromptsType . agent ) ) {
210- return this . getModelNames ( promptType === PromptsType . agent ) ;
211216 }
212-
213217 return [ ] ;
214218 }
215219
@@ -226,14 +230,15 @@ export class PromptHeaderAutocompletion implements CompletionItemProvider {
226230 return result ;
227231 }
228232
229- private provideToolCompletions ( model : ITextModel , position : Position , header : PromptHeader ) : CompletionList | undefined {
233+ private provideToolCompletions ( model : ITextModel , position : Position , header : PromptHeader , isGitHubTarget : boolean ) : CompletionList | undefined {
230234 const toolsAttr = header . getAttribute ( PromptHeaderAttributes . tools ) ;
231235 if ( ! toolsAttr || toolsAttr . value . type !== 'array' || ! toolsAttr . range . containsPosition ( position ) ) {
232236 return undefined ;
233237 }
234238 const getSuggestions = ( toolRange : Range ) => {
235239 const suggestions : CompletionItem [ ] = [ ] ;
236- for ( const toolName of this . languageModelToolsService . getQualifiedToolNames ( ) ) {
240+ const toolNames = isGitHubTarget ? Object . keys ( knownGithubCopilotTools ) : this . languageModelToolsService . getQualifiedToolNames ( ) ;
241+ for ( const toolName of toolNames ) {
237242 let insertText : string ;
238243 if ( ! toolRange . isEmpty ( ) ) {
239244 const firstChar = model . getValueInRange ( toolRange ) . charCodeAt ( 0 ) ;
0 commit comments