Agent skill frontmatter is not recognized when SKILL.md has a UTF-8 BOM
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
- Copilot Chat Extension Version: 0.65.0
- VS Code Version: 1.137.0 (commit 645f29cc31)
- OS Version: Windows
- Feature: Agent mode / agent skill discovery
- Selected model: Model-independent
- Tested with Insiders: No
## Description
Agent skills whose `SKILL.md` begins with a UTF-8 BOM are not reliably included in the skills advertised to the agent.
Removing the BOM, without changing the file content, makes the skill load reliably.
Whether a BOM-prefixed skill loads appears to depend on whether VS Code already has an open text model for the file. This makes discovery appear nondeterministic across window reloads and sessions.
## Steps to Reproduce
1. Create `.github/skills/bom-test/SKILL.md`.
2. Save it as **UTF-8 with BOM** with this content:
```markdown
---
name: bom-test
description: Tests skill discovery with a UTF-8 BOM
---
# BOM test
```
3. Close the file so it has no open editor model.
4. Reload VS Code and start an Agent chat.
5. Ask the agent what skills it has access to
6. Observe that `bom-test` is absent.
7. Open Agent Customisations, note that bom-test is present, but has no description
8. Save the same file as **UTF-8 without BOM**.
9. Start another Agent request.
10. Observe that `bom-test` is now advertised. Editing the BOM-prefixed file in VS Code before discovery can also cause it to load, making the result session-dependent.
## Expected Behavior
UTF-8 with BOM should be accepted for `SKILL.md`, and the skill should always be advertised.
## Actual Behavior
The BOM prevents the YAML frontmatter delimiter from being recognized on the raw file-read path. Consequently, the skill metadata is unavailable and the skill is omitted from the advertised list.
---
_Some Copilot generated analysis below, feel free to ignore if unhelpful:_
## Source Analysis
`PromptsService.parseNew()` uses two different input paths:
- For an existing text model, it parses `textModel.getValue()`.
- Otherwise, it parses `fileService.readFile(uri).value.toString()`.
The raw `VSBuffer.toString()` path preserves the UTF-8 BOM as U+FEFF.
`PromptFileParser.parse()` recognizes frontmatter using:
```ts
linesWithEOL[0].match(/^---[\s\r\n]*$/)
```
For a BOM-prefixed file, the first line is effectively `\uFEFF---`, so this expression does not match and `header` remains undefined.
The editor text-file decoding path handles the BOM as an encoding marker, explaining why the result can differ when the file already has a text model.
Relevant source:
- `src/vs/workbench/contrib/chat/common/promptSyntax/promptFileParser.ts`
- `src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsServiceImpl.ts`
- `src/vs/base/common/buffer.ts`
- `src/vs/workbench/services/textfile/common/encoding.ts`
## Suggested Fix
Strip one leading U+FEFF before parsing prompt files, or allow it in the opening frontmatter delimiter check.
A regression test should parse this input successfully:
```ts
'\uFEFF---\nname: bom-test\ndescription: test\n---\nbody'
```
Contributor guide
Assessment
This issue has not been assessed yet.