@@ -43,7 +43,7 @@ import { IPathService } from '../../../../services/path/common/pathService.js';
4343import { generateCustomizationDebugReport } from './aiCustomizationDebugPanel.js' ;
4444import { parseHooksFromFile } from '../../common/promptSyntax/hookCompatibility.js' ;
4545import { formatHookCommandLabel } from '../../common/promptSyntax/hookSchema.js' ;
46- import { HOOK_METADATA } from '../../common/promptSyntax/hookTypes.js' ;
46+ import { HookType , HOOK_METADATA } from '../../common/promptSyntax/hookTypes.js' ;
4747import { parse as parseJSONC } from '../../../../../base/common/json.js' ;
4848import { Schemas } from '../../../../../base/common/network.js' ;
4949import { OS } from '../../../../../base/common/platform.js' ;
@@ -65,6 +65,8 @@ export interface IAICustomizationListItem {
6565 readonly description ?: string ;
6666 readonly storage : PromptsStorage ;
6767 readonly promptType : PromptsType ;
68+ /** When set, overrides `storage` for display grouping purposes. */
69+ readonly groupKey ?: string ;
6870 nameMatches ?: IMatch [ ] ;
6971 descriptionMatches ?: IMatch [ ] ;
7072}
@@ -75,7 +77,7 @@ export interface IAICustomizationListItem {
7577interface IGroupHeaderEntry {
7678 readonly type : 'group-header' ;
7779 readonly id : string ;
78- readonly storage : PromptsStorage ;
80+ readonly groupKey : string ;
7981 readonly label : string ;
8082 readonly icon : ThemeIcon ;
8183 readonly count : number ;
@@ -311,7 +313,7 @@ export class AICustomizationListWidget extends Disposable {
311313 private allItems : IAICustomizationListItem [ ] = [ ] ;
312314 private displayEntries : IListEntry [ ] = [ ] ;
313315 private searchQuery : string = '' ;
314- private readonly collapsedGroups = new Set < PromptsStorage > ( ) ;
316+ private readonly collapsedGroups = new Set < string > ( ) ;
315317 private readonly dropdownActionDisposables = this . _register ( new DisposableStore ( ) ) ;
316318
317319 private readonly delayedFilter = new Delayer < void > ( 200 ) ;
@@ -827,6 +829,37 @@ export class AICustomizationListWidget extends Disposable {
827829 } ) ;
828830 }
829831 }
832+
833+ // Also include hooks defined in agent frontmatter (not in sessions window)
834+ // TODO: add this back when Copilot CLI supports this
835+ const agents = ! this . workspaceService . isSessionsWindow ? await this . promptsService . getCustomAgents ( CancellationToken . None ) : [ ] ;
836+ for ( const agent of agents ) {
837+ if ( ! agent . hooks ) {
838+ continue ;
839+ }
840+ for ( const hookType of Object . values ( HookType ) ) {
841+ const hookCommands = agent . hooks [ hookType ] ;
842+ if ( ! hookCommands || hookCommands . length === 0 ) {
843+ continue ;
844+ }
845+ const hookMeta = HOOK_METADATA [ hookType ] ;
846+ for ( let i = 0 ; i < hookCommands . length ; i ++ ) {
847+ const hook = hookCommands [ i ] ;
848+ const cmdLabel = formatHookCommandLabel ( hook , OS ) ;
849+ const truncatedCmd = cmdLabel . length > 60 ? cmdLabel . substring ( 0 , 57 ) + '...' : cmdLabel ;
850+ items . push ( {
851+ id : `${ agent . uri . toString ( ) } #hook:${ hookType } [${ i } ]` ,
852+ uri : agent . uri ,
853+ name : hookMeta ?. label ?? hookType ,
854+ filename : basename ( agent . uri ) ,
855+ description : `${ agent . name } : ${ truncatedCmd || localize ( 'hookUnset' , "(unset)" ) } ` ,
856+ storage : agent . source . storage ,
857+ groupKey : 'agents' ,
858+ promptType,
859+ } ) ;
860+ }
861+ }
862+ }
830863 } else {
831864 // For instructions, fetch prompt files and group by storage
832865 const promptFiles = await this . promptsService . listPromptFiles ( promptType , CancellationToken . None ) ;
@@ -940,15 +973,17 @@ export class AICustomizationListWidget extends Disposable {
940973 // Group items by storage
941974 const promptType = sectionToPromptType ( this . currentSection ) ;
942975 const visibleSources = new Set ( this . workspaceService . getStorageSourceFilter ( promptType ) . sources ) ;
943- const groups : { storage : PromptsStorage ; label : string ; icon : ThemeIcon ; description : string ; items : IAICustomizationListItem [ ] } [ ] = [
944- { storage : PromptsStorage . local , label : localize ( 'workspaceGroup' , "Workspace" ) , icon : workspaceIcon , description : localize ( 'workspaceGroupDescription' , "Customizations stored as files in your project folder and shared with your team via version control." ) , items : [ ] } ,
945- { storage : PromptsStorage . user , label : localize ( 'userGroup' , "User" ) , icon : userIcon , description : localize ( 'userGroupDescription' , "Customizations stored locally on your machine in a central location. Private to you and available across all projects." ) , items : [ ] } ,
946- { storage : PromptsStorage . extension , label : localize ( 'extensionGroup' , "Extensions" ) , icon : extensionIcon , description : localize ( 'extensionGroupDescription' , "Read-only customizations provided by installed extensions." ) , items : [ ] } ,
947- { storage : PromptsStorage . plugin , label : localize ( 'pluginGroup' , "Plugins" ) , icon : pluginIcon , description : localize ( 'pluginGroupDescription' , "Read-only customizations provided by installed plugins." ) , items : [ ] } ,
948- ] . filter ( g => visibleSources . has ( g . storage ) ) ;
976+ const groups : { groupKey : string ; label : string ; icon : ThemeIcon ; description : string ; items : IAICustomizationListItem [ ] } [ ] = [
977+ { groupKey : PromptsStorage . local , label : localize ( 'workspaceGroup' , "Workspace" ) , icon : workspaceIcon , description : localize ( 'workspaceGroupDescription' , "Customizations stored as files in your project folder and shared with your team via version control." ) , items : [ ] } ,
978+ { groupKey : PromptsStorage . user , label : localize ( 'userGroup' , "User" ) , icon : userIcon , description : localize ( 'userGroupDescription' , "Customizations stored locally on your machine in a central location. Private to you and available across all projects." ) , items : [ ] } ,
979+ { groupKey : PromptsStorage . extension , label : localize ( 'extensionGroup' , "Extensions" ) , icon : extensionIcon , description : localize ( 'extensionGroupDescription' , "Read-only customizations provided by installed extensions." ) , items : [ ] } ,
980+ { groupKey : PromptsStorage . plugin , label : localize ( 'pluginGroup' , "Plugins" ) , icon : pluginIcon , description : localize ( 'pluginGroupDescription' , "Read-only customizations provided by installed plugins." ) , items : [ ] } ,
981+ { groupKey : 'agents' , label : localize ( 'agentsGroup' , "Agents" ) , icon : agentIcon , description : localize ( 'agentsGroupDescription' , "Hooks defined in agent files." ) , items : [ ] } ,
982+ ] . filter ( g => visibleSources . has ( g . groupKey as PromptsStorage ) || g . groupKey === 'agents' ) ;
949983
950984 for ( const item of matchedItems ) {
951- const group = groups . find ( g => g . storage === item . storage ) ;
985+ const key = item . groupKey ?? item . storage ;
986+ const group = groups . find ( g => g . groupKey === key ) ;
952987 if ( group ) {
953988 group . items . push ( item ) ;
954989 }
@@ -967,12 +1002,12 @@ export class AICustomizationListWidget extends Disposable {
9671002 continue ;
9681003 }
9691004
970- const collapsed = this . collapsedGroups . has ( group . storage ) ;
1005+ const collapsed = this . collapsedGroups . has ( group . groupKey ) ;
9711006
9721007 this . displayEntries . push ( {
9731008 type : 'group-header' ,
974- id : `group-${ group . storage } ` ,
975- storage : group . storage ,
1009+ id : `group-${ group . groupKey } ` ,
1010+ groupKey : group . groupKey ,
9761011 label : group . label ,
9771012 icon : group . icon ,
9781013 count : group . items . length ,
@@ -997,10 +1032,10 @@ export class AICustomizationListWidget extends Disposable {
9971032 * Toggles the collapsed state of a group.
9981033 */
9991034 private toggleGroup ( entry : IGroupHeaderEntry ) : void {
1000- if ( this . collapsedGroups . has ( entry . storage ) ) {
1001- this . collapsedGroups . delete ( entry . storage ) ;
1035+ if ( this . collapsedGroups . has ( entry . groupKey ) ) {
1036+ this . collapsedGroups . delete ( entry . groupKey ) ;
10021037 } else {
1003- this . collapsedGroups . add ( entry . storage ) ;
1038+ this . collapsedGroups . add ( entry . groupKey ) ;
10041039 }
10051040 this . filterItems ( ) ;
10061041 }
0 commit comments