Skip to content

feat: per-agent skill scoping, add skills: to .agent.md frontmatter - #328237

Open
Hadrian de Oliveira (hd-o) wants to merge 7 commits into
microsoft:mainfrom
hd-o:feat/agent-md-skills
Open

feat: per-agent skill scoping, add skills: to .agent.md frontmatter#328237
Hadrian de Oliveira (hd-o) wants to merge 7 commits into
microsoft:mainfrom
hd-o:feat/agent-md-skills

Conversation

@hd-o

@hd-o Hadrian de Oliveira (hd-o) commented Jul 30, 2026

Copy link
Copy Markdown

Resolves #307630

Summary

Adds a skills: frontmatter field on custom agents (.agent.md) so authors can control which skills appear in that agent’s model-invocable skill catalog. Similar to tools: field.

This change makes loop engineering easier with vscode-copilot (I'm not sure how copilot cloud agents work), allowing minimal session context on specialized agents, specially for teams using monorepos with lots of shared skills. It also gets vscode-copilot closer to the capabilities offered by other harnesses like Codex and Open Code that already provide this feature.

This is different than Claude Code's skills: which means "preloaded skills into subagent context", which I recommend to be implemented in vscode/copilot as field named preload-skills (#293752).

Semantics

Value Behavior
omit skills All model-invocable skills (backward compatible)
skills: [] No skills in the catalog
skills: [a, b] Only skills named a / b (that also pass existing filters)
skills: ['*'] Explicit all

Existing per-skill filters still apply after the whitelist (disable-model-invocation, required description, session type, etc.). Unknown skill names do not fail agent load; the editor reports a warning.

Implementation

  • Parse skills on PromptHeader (sequence or comma-separated scalar)
  • Thread through ICustomAgent.skillsCustomChatMode.customSkills
  • Filter catalog in core ComputeAutomaticInstructions and Copilot AutomaticInstructionsCollector
  • Expose allowedSkills on ChatRequestModeInstructions for the extension path
  • Editor: attribute metadata, completion from findAgentSkills(), validation, hovers
  • Docs: agent-customization skill reference

Tests

  • Parser: sequence / empty / * / scalar / omit
  • Catalog filter: undefined / [] / ['*'] / subset / disableModelInvocation
  • Mode refresh of customSkills
  • Prompts service header plumbing
  • Extension collector allowedSkills filter
Before After
agent-md-before agent-md-after
skills-found-before skills-found-after
Autocomplete
agent-md-intelli-after

Allow .agent.md authors to restrict which skills appear in the
model-invocable catalog via skills: (omit=all, []=none, ['*']=all),
mirroring agents: filtering in automatic instructions. Includes
parser/mode plumbing, editor completion and validation, and tests.

Co-authored-by: Hadrian de Oliveira <hd-o@users.noreply.github.com>
Fail closed on malformed skills: values, tighten validator lookups,
precompute the catalog allow-set, and clean up test call formatting.

Co-authored-by: Hadrian de Oliveira <hd-o@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 30, 2026 17:06
@hd-o Hadrian de Oliveira (hd-o) changed the title Feat/agent md skills feat: per-agent skill scoping, add skills: to .agent.md frontmatter Jul 30, 2026
@hd-o

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds per-agent skills: allow-lists for custom agents and threads them through parsing, chat modes, instruction collection, editor tooling, and Copilot.

Changes:

  • Parses, validates, completes, and documents skills: frontmatter.
  • Filters model-invocable skill catalogs in core and Copilot.
  • Adds API plumbing, caching, and test coverage.

Reviewed changes

Copilot reviewed 27 out of 28 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts Exposes allowed skills.
src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts Tests service plumbing.
src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptFileParser.test.ts Tests parsing semantics.
src/vs/workbench/contrib/chat/test/common/promptSyntax/computeAutomaticInstructions.test.ts Tests catalog filtering.
src/vs/workbench/contrib/chat/test/common/chatModeService.test.ts Tests mode refreshes.
src/vs/workbench/contrib/chat/test/browser/promptSyntax/languageProviders/promptValidator.test.ts Tests validation.
src/vs/workbench/contrib/chat/test/browser/promptSyntax/languageProviders/promptHovers.test.ts Tests hover text.
src/vs/workbench/contrib/chat/common/tools/builtinTools/runSubagentTool.ts Updates subagent collection call.
src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsServiceImpl.ts Maps parsed skills to agents.
src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts Extends custom-agent data.
src/vs/workbench/contrib/chat/common/promptSyntax/promptFileParser.ts Parses skills:.
src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptValidator.ts Validates skill names.
src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptHeaderAutocompletion.ts Suggests available skills.
src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptFileAttributes.ts Registers field metadata.
src/vs/workbench/contrib/chat/common/promptSyntax/computeAutomaticInstructions.ts Filters the core catalog.
src/vs/workbench/contrib/chat/common/model/chatModel.ts Extends mode instructions.
src/vs/workbench/contrib/chat/common/chatService/chatServiceImpl.ts Forwards enabled skills.
src/vs/workbench/contrib/chat/common/chatService/chatService.ts Extends request options.
src/vs/workbench/contrib/chat/common/chatModes.ts Stores and observes skills.
src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts Adds skills to mode instructions.
src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts Supplies skills during collection.
src/vs/workbench/api/test/common/extHostTypeConverters.test.ts Tests API conversion.
src/vs/workbench/api/common/extHostTypeConverters.ts Converts allowed skills.
extensions/copilot/src/util/vs/workbench/contrib/chat/common/promptSyntax/promptFileParser.ts Mirrors parser support.
extensions/copilot/src/platform/promptFiles/test/node/automaticInstructionsCollector.spec.ts Tests extension filtering.
extensions/copilot/src/platform/promptFiles/node/automaticInstructionsCollector.ts Filters the extension catalog.
extensions/copilot/src/extension/agents/vscode-node/agentTypes.ts Serializes generated agents.
extensions/copilot/assets/prompts/skills/agent-customization/references/agents.md Documents the field.

// When the extension is responsible for instruction collection, skip the core path entirely.
if (this.configurationService.getValue<boolean>(ChatConfiguration.CollectInstructionsInExtension) !== true) {
const computer = this.instantiationService.createInstance(ComputeAutomaticInstructions, ChatModeKind.Agent, modeTools, undefined, getChatSessionType(invocation.context.sessionResource));
const computer = this.instantiationService.createInstance(ComputeAutomaticInstructions, ChatModeKind.Agent, modeTools, undefined, undefined, getChatSessionType(invocation.context.sessionResource));

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid. Will push update soon

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: dcf7bd1

enabledTools: modeKind === ChatModeKind.Agent ? this.input.selectedToolsModel.userSelectedTools.get() : undefined,
enabledSubAgents: modeKind === ChatModeKind.Agent ? this.input.currentModeObs.get().agents?.get() : undefined
enabledSubAgents: modeKind === ChatModeKind.Agent ? this.input.currentModeObs.get().agents?.get() : undefined,
enabledSkills: modeKind === ChatModeKind.Agent ? this.input.currentModeObs.get().customSkills?.get() : undefined

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional for this PR. Can be a follow-up PR if needed?

return { id, uri, name, description, model, tools, handOffs, argumentHint, target, visibility, agents, agentInstructions, source, sessionTypes, hooks, enabled };
// Claude's `skills:` means "preload into subagent context", not a catalog whitelist.
// Only VS Code / default-target agents use `skills:` as an allow-list.
const skills = target === Target.Claude ? undefined : ast.header.skills;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid. Will push update soon

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: 1cb0d06

Comment on lines +331 to +335
for (const item of value.items) {
if (item.type === 'scalar' && item.value) {
skills.push(item.value);
}
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot shouldn't this have the same behavior as the tools: field?

Comment on lines +333 to +337
for (const item of value.items) {
if (item.type === 'scalar' && item.value) {
skills.push(item.value);
}
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awaiting on the discussion above for decision

Pass the selected subagent's skills (or the current mode's
allowedSkills) into modeInstructions and ComputeAutomaticInstructions
so skills: [] no longer falls back to the full catalog.

Co-authored-by: Hadrian de Oliveira <hd-o@users.noreply.github.com>
Only propagate header skills for VS Code and default-target agents.
GitHub Copilot does not support skills as an attribute, so ignore it
there the same way Claude preload semantics are ignored.

Co-authored-by: Hadrian de Oliveira <hd-o@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 29 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

src/vs/workbench/contrib/chat/test/common/tools/builtinTools/runSubagentTool.test.ts:1139

  • As above, the captured createInstance arguments include the constructor at index 0, so index 3 is the enabledSubagents argument. Read index 4 to assert the allowed-skills value; otherwise this test always fails.
			assert.deepStrictEqual(capturedComputeArgs[0][3], ['security-review']);

extensions/copilot/src/platform/promptFiles/node/automaticInstructionsCollector.ts:184

  • allowedSkills now changes the automatic catalog, but the Responses API mode-change check still compares only URI/name/content/builtin/metadata. If the same agent is edited from all/subset skills to skills: [], the request reuses previous_response_id, whose state still contains the old catalog, so the removed skills remain visible to the model. Include allowedSkills in areModeInstructionsEqual (with array equality) and cover a skills-only mode update.
		const indexEntry = await this._buildCustomizationsIndex(instructionFiles, tools, modeInstructions2?.allowedSubagents, modeInstructions2?.allowedSkills, sessionType, telemetry, token);

src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptFileAttributes.ts:149

  • The hover says these are the skills “available” to the agent, although this field only filters the automatically advertised catalog and does not remove user-invocable skill commands. Reword the hover to say “advertised in the model-invocable skill catalog” and update the duplicate description/test so the UI matches the documented semantics.
		description: localize('promptHeader.agent.skills', 'Skills available to this agent. Omit for all skills; use `[]` for none; use `[\'*\']` for all.'),

src/vs/workbench/contrib/chat/test/common/tools/builtinTools/runSubagentTool.test.ts:1114

  • capturedComputeArgs records the full createInstance call, including ComputeAutomaticInstructions at index 0. The enabled-skills constructor argument is therefore at index 4, while index 3 is enabledSubagents; this assertion currently compares undefined with [] and fails.

This issue also appears on line 1139 of the same file.

			assert.deepStrictEqual(capturedComputeArgs[0][3], []);

@hd-o

Copy link
Copy Markdown
Author

Martin Aeschlimann (@aeschli) Paul (@pwang347) [not urgent] copilot is ignoring my comments 🥲 (I increased my budget already). A review from you would be highly appreciated 🙏 no rush, whenever you have time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: per-agent skill scoping (skills property in .agent.md frontmatter)

4 participants