diff --git a/src/extension/chatSessions/vscode-node/copilotCLICustomizationProvider.ts b/src/extension/chatSessions/vscode-node/copilotCLICustomizationProvider.ts index 5ab233178..17bbe462c 100644 --- a/src/extension/chatSessions/vscode-node/copilotCLICustomizationProvider.ts +++ b/src/extension/chatSessions/vscode-node/copilotCLICustomizationProvider.ts @@ -95,26 +95,33 @@ export class CopilotCLICustomizationProvider extends Disposable implements vscod * - on-demand-instructions: files without an applyTo pattern */ private async getInstructionItems(token: CancellationToken): Promise { - const agentInstructionUris = new Set( - (await this.customInstructionsService.getAgentInstructions()).map(uri => uri.toString()) - ); + const agentInstructionUriList = await this.customInstructionsService.getAgentInstructions(); const items: vscode.ChatSessionCustomizationItem[] = []; + const seenUris = new Set(); + + // Emit agent instruction files (AGENTS.md, CLAUDE.md, copilot-instructions.md) + // that come from customInstructionsService but may not appear in + // chatPromptFileService.instructions. + for (const uri of agentInstructionUriList) { + seenUris.add(uri.toString()); + items.push({ + uri, + type: vscode.ChatSessionCustomizationType.Instructions, + name: basename(uri), + groupKey: 'agent-instructions', + }); + } for (const instruction of this.chatPromptFileService.instructions) { const uri = instruction.uri; - const name = deriveNameFromUri(uri, INSTRUCTION_FILE_EXTENSION); - if (agentInstructionUris.has(uri.toString())) { - items.push({ - uri, - type: vscode.ChatSessionCustomizationType.Instructions, - name, - groupKey: 'agent-instructions', - }); - continue; + if (seenUris.has(uri.toString())) { + continue; // already emitted as agent instruction } + const name = deriveNameFromUri(uri, INSTRUCTION_FILE_EXTENSION); + let pattern: string | undefined; let description: string | undefined; try { diff --git a/src/extension/chatSessions/vscode-node/test/copilotCLICustomizationProvider.spec.ts b/src/extension/chatSessions/vscode-node/test/copilotCLICustomizationProvider.spec.ts index f5acc4d4c..2688044d1 100644 --- a/src/extension/chatSessions/vscode-node/test/copilotCLICustomizationProvider.spec.ts +++ b/src/extension/chatSessions/vscode-node/test/copilotCLICustomizationProvider.spec.ts @@ -348,6 +348,22 @@ describe('CopilotCLICustomizationProvider', () => { expect(instrItems[0].badge).toBeUndefined(); }); + it('emits agent instructions not in chatPromptFileService.instructions', async () => { + const agentsUri = URI.file('/workspace/AGENTS.md'); + const claudeUri = URI.file('/workspace/CLAUDE.md'); + const copilotUri = URI.file('/workspace/.github/copilot-instructions.md'); + // Agent instructions are NOT in chatPromptFileService.instructions — + // they come only from customInstructionsService.getAgentInstructions(). + mockPromptFileService.setInstructions([]); + mockCustomInstructionsService.setAgentInstructionUris([agentsUri, claudeUri, copilotUri]); + + const items = await provider.provideChatSessionCustomizations(undefined!); + const instrItems = items.filter(i => i.type === FakeChatSessionCustomizationType.Instructions); + expect(instrItems).toHaveLength(3); + expect(instrItems.every(i => i.groupKey === 'agent-instructions')).toBe(true); + expect(instrItems.map(i => i.name)).toEqual(['AGENTS.md', 'CLAUDE.md', 'copilot-instructions.md']); + }); + it('uses context-instructions groupKey with badge for instructions with applyTo pattern', async () => { const uri = URI.file('/workspace/.github/style.instructions.md'); mockPromptFileService.setInstructions([{ uri }]);