Skip to content

Commit 324d55e

Browse files
authored
fix: discover AGENTS.md and CLAUDE.md at workspace roots (#4989)
The provider only found copilot-instructions.md (via customInstructionsService.getAgentInstructions()). AGENTS.md and CLAUDE.md live at the workspace root and are also agent instructions but weren't discovered. Now stats workspace roots for these files and includes them as agent-instructions items.
1 parent bab22b9 commit 324d55e

2 files changed

Lines changed: 52 additions & 1 deletion

File tree

src/extension/chatSessions/vscode-node/copilotCLICustomizationProvider.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import * as l10n from '@vscode/l10n';
77
import * as vscode from 'vscode';
88
import { ICustomInstructionsService } from '../../../platform/customInstructions/common/customInstructionsService';
99
import { INSTRUCTION_FILE_EXTENSION, SKILL_FILENAME } from '../../../platform/customInstructions/common/promptTypes';
10+
import { IFileSystemService } from '../../../platform/filesystem/common/fileSystemService';
1011
import { ILogService } from '../../../platform/log/common/logService';
1112
import { IPromptsService } from '../../../platform/promptFiles/common/promptsService';
13+
import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService';
1214
import { CancellationToken } from '../../../util/vs/base/common/cancellation';
1315
import { isCancellationError } from '../../../util/vs/base/common/errors';
1416
import { Emitter } from '../../../util/vs/base/common/event';
1517
import { Disposable } from '../../../util/vs/base/common/lifecycle';
1618
import { basename } from '../../../util/vs/base/common/resources';
19+
import { URI } from '../../../util/vs/base/common/uri';
1720
import { IChatPromptFileService } from '../common/chatPromptFileService';
1821
import { ICopilotCLIAgents } from '../copilotcli/node/copilotCli';
1922

@@ -42,6 +45,8 @@ export class CopilotCLICustomizationProvider extends Disposable implements vscod
4245
@ICustomInstructionsService private readonly customInstructionsService: ICustomInstructionsService,
4346
@IPromptsService private readonly promptsService: IPromptsService,
4447
@ILogService private readonly logService: ILogService,
48+
@IWorkspaceService private readonly workspaceService: IWorkspaceService,
49+
@IFileSystemService private readonly fileSystemService: IFileSystemService,
4550
) {
4651
super();
4752

@@ -90,12 +95,26 @@ export class CopilotCLICustomizationProvider extends Disposable implements vscod
9095
* Collects all instruction items from the prompt file service,
9196
* categorizing them with groupKeys and badges matching the core
9297
* implementation:
93-
* - agent-instructions: copilot-instructions.md files
98+
* - agent-instructions: AGENTS.md, CLAUDE.md, copilot-instructions.md
9499
* - context-instructions: files with an applyTo pattern (badge = pattern)
95100
* - on-demand-instructions: files without an applyTo pattern
96101
*/
97102
private async getInstructionItems(token: CancellationToken): Promise<vscode.ChatSessionCustomizationItem[]> {
103+
// Collect agent instruction URIs from customInstructionsService
104+
// (copilot-instructions.md) plus workspace-root AGENTS.md and CLAUDE.md
98105
const agentInstructionUriList = await this.customInstructionsService.getAgentInstructions();
106+
const rootFileNames = ['AGENTS.md', 'CLAUDE.md'];
107+
for (const folder of this.workspaceService.getWorkspaceFolders()) {
108+
for (const fileName of rootFileNames) {
109+
const uri = URI.joinPath(folder, fileName);
110+
try {
111+
await this.fileSystemService.stat(uri);
112+
agentInstructionUriList.push(uri);
113+
} catch {
114+
// file doesn't exist
115+
}
116+
}
117+
}
99118

100119
const items: vscode.ChatSessionCustomizationItem[] = [];
101120
const seenUris = new Set<string>();

src/extension/chatSessions/vscode-node/test/copilotCLICustomizationProvider.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ describe('CopilotCLICustomizationProvider', () => {
160160
mockCustomInstructionsService,
161161
mockPromptsService,
162162
new TestLogService(),
163+
{ getWorkspaceFolders: () => [] } as any,
164+
{ stat: () => Promise.reject(new Error('not found')) } as any,
163165
));
164166
});
165167

@@ -364,6 +366,36 @@ describe('CopilotCLICustomizationProvider', () => {
364366
expect(instrItems.map(i => i.name)).toEqual(['AGENTS.md', 'CLAUDE.md', 'copilot-instructions.md']);
365367
});
366368

369+
it('discovers AGENTS.md and CLAUDE.md from workspace roots via filesystem', async () => {
370+
const workspaceRoot = URI.file('/workspace');
371+
const agentsUri = URI.file('/workspace/AGENTS.md');
372+
const claudeUri = URI.file('/workspace/CLAUDE.md');
373+
const existingUris = new Set([agentsUri.toString(), claudeUri.toString()]);
374+
375+
const testProvider = disposables.add(new CopilotCLICustomizationProvider(
376+
mockPromptFileService,
377+
mockCopilotCLIAgents,
378+
mockCustomInstructionsService,
379+
mockPromptsService,
380+
new TestLogService(),
381+
{ getWorkspaceFolders: () => [workspaceRoot] } as any,
382+
{
383+
stat: (uri: URI) => existingUris.has(uri.toString())
384+
? Promise.resolve({ type: 1, ctime: 0, mtime: 0, size: 0 })
385+
: Promise.reject(new Error('not found')),
386+
} as any,
387+
));
388+
389+
mockPromptFileService.setInstructions([]);
390+
mockCustomInstructionsService.setAgentInstructionUris([]);
391+
392+
const items = await testProvider.provideChatSessionCustomizations(undefined!);
393+
const instrItems = items.filter(i => i.type === FakeChatSessionCustomizationType.Instructions);
394+
expect(instrItems).toHaveLength(2);
395+
expect(instrItems.every(i => i.groupKey === 'agent-instructions')).toBe(true);
396+
expect(instrItems.map(i => i.name)).toEqual(['AGENTS.md', 'CLAUDE.md']);
397+
});
398+
367399
it('uses context-instructions groupKey with badge for instructions with applyTo pattern', async () => {
368400
const uri = URI.file('/workspace/.github/style.instructions.md');
369401
mockPromptFileService.setInstructions([{ uri }]);

0 commit comments

Comments
 (0)