treat copilot-instructions.md file as a reusable (.prompt.md) file

This commit is contained in:
Oleg Solomko
2025-03-19 13:51:01 -07:00
parent 5e9acd0b1a
commit 89f495e958
5 changed files with 34 additions and 17 deletions
@@ -6,14 +6,14 @@
import { MarkdownToken } from './tokens/markdownToken.js';
import { VSBuffer } from '../../../../base/common/buffer.js';
import { LeftBracket } from '../simpleCodec/tokens/brackets.js';
import { PartialMarkdownImage } from './parsers/markdownImage.js';
import { ReadableStream } from '../../../../base/common/stream.js';
import { LeftAngleBracket } from '../simpleCodec/tokens/angleBrackets.js';
import { ExclamationMark } from '../simpleCodec/tokens/exclamationMark.js';
import { BaseDecoder } from '../../../../base/common/codecs/baseDecoder.js';
import { SimpleDecoder, TSimpleToken } from '../simpleCodec/simpleDecoder.js';
import { MarkdownCommentStart, PartialMarkdownCommentStart } from './parsers/markdownComment.js';
import { MarkdownLinkCaption, PartialMarkdownLink, PartialMarkdownLinkCaption } from './parsers/markdownLink.js';
import { ExclamationMark } from '../simpleCodec/tokens/exclamationMark.js';
import { PartialMarkdownImage } from './parsers/markdownImage.js';
/**
* Tokens handled by this decoder.
+20 -5
View File
@@ -13,14 +13,22 @@ import { basename } from '../../../base/common/path.js';
export const PROMPT_FILE_EXTENSION = '.prompt.md';
/**
* Check if provided path is a prompt file.
* Copilot custom instructions file name.
*/
const COPILOT_CUSTOM_INSTRUCTIONS_FILENAME = 'copilot-instructions.md';
/**
* Check if provided path is a reusable prompt file.
*/
export const isPromptFile = (
fileUri: URI,
): boolean => {
return fileUri
.path
.endsWith(PROMPT_FILE_EXTENSION);
const filename = basename(fileUri.path);
const hasPromptFileExtension = filename.endsWith(PROMPT_FILE_EXTENSION);
const isCustomInstructionsFile = (filename === COPILOT_CUSTOM_INSTRUCTIONS_FILENAME);
return hasPromptFileExtension || isCustomInstructionsFile;
};
/**
@@ -37,5 +45,12 @@ export const getCleanPromptName = (
`Provided path '${fileUri.fsPath}' is not a prompt file.`,
);
return basename(fileUri.path, PROMPT_FILE_EXTENSION);
// if a Copilot custom instructions file, remove `markdown` file extension
// otherwise, remove the `prompt` file extension
const fileExtension = (fileUri.path.endsWith(COPILOT_CUSTOM_INSTRUCTIONS_FILENAME))
? '.md'
: PROMPT_FILE_EXTENSION;
// otherwise, remove the prompt file extension
return basename(fileUri.path, fileExtension);
};
@@ -5,8 +5,8 @@
import assert from 'assert';
import { URI } from '../../../../base/common/uri.js';
import { getCleanPromptName, isPromptFile } from '../../common/constants.js';
import { randomInt } from '../../../../base/common/numbers.js';
import { getCleanPromptName, isPromptFile } from '../../common/constants.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
@@ -30,6 +30,11 @@ suite('Prompt Constants', () => {
getCleanPromptName(URI.file(`./${expectedPromptName}.prompt.md`)),
expectedPromptName,
);
assert.strictEqual(
getCleanPromptName(URI.file('.github/copilot-instructions.md')),
'copilot-instructions',
);
});
test('• throws if not a prompt file URI provided', () => {
@@ -65,6 +70,10 @@ suite('Prompt Constants', () => {
assert(
isPromptFile(URI.file(`./some-${randomInt(1000)}.prompt.md`)),
);
assert(
isPromptFile(URI.file('.github/copilot-instructions.md')),
);
});
test('• returns `false` for non-prompt files', () => {
@@ -11,6 +11,7 @@ import { CancellationError } from '../../../../../../base/common/errors.js';
import { PromptContentsProviderBase } from './promptContentsProviderBase.js';
import { VSBufferReadableStream } from '../../../../../../base/common/buffer.js';
import { CancellationToken } from '../../../../../../base/common/cancellation.js';
import { isPromptFile } from '../../../../../../platform/prompts/common/constants.js';
import { OpenFailed, NotPromptFile, ResolveError, FolderReference } from '../../promptFileReferenceErrors.js';
import { FileChangesEvent, FileChangeType, IFileService } from '../../../../../../platform/files/common/files.js';
@@ -102,7 +103,7 @@ export class FilePromptContentProvider extends PromptContentsProviderBase<FileCh
}
// if URI doesn't point to a prompt snippet file, don't try to resolve it
if (!this.isPromptSnippet()) {
if (isPromptFile(this.uri) === false) {
throw new NotPromptFile(this.uri);
}
@@ -10,7 +10,6 @@ import { assert } from '../../../../../../base/common/assert.js';
import { CancellationError } from '../../../../../../base/common/errors.js';
import { VSBufferReadableStream } from '../../../../../../base/common/buffer.js';
import { CancellationToken } from '../../../../../../base/common/cancellation.js';
import { isPromptFile } from '../../../../../../platform/prompts/common/constants.js';
import { ObservableDisposable } from '../../../../../../base/common/observableDisposable.js';
import { FailedToResolveContentsStream, ResolveError } from '../../promptFileReferenceErrors.js';
import { cancelPreviousCalls } from '../../../../../../base/common/decorators/cancelPreviousCalls.js';
@@ -140,11 +139,4 @@ export abstract class PromptContentsProviderBase<
return this;
}
/**
* Check if the current URI points to a prompt snippet.
*/
public isPromptSnippet(): boolean {
return isPromptFile(this.uri);
}
}