Add agent-customization skill with primitives documentation (#3196)

* Add agent-customization skill with primitives documentation

* Update assets/prompts/skills/agent-customization/primitives/workspace-instructions.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: rename agentCustomizationSkill setting to follow Advanced naming convention

* Updated skills.md based on spec

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Harald Kirschner
2026-01-27 10:53:43 -08:00
committed by GitHub
parent 8449b5653e
commit dac3d77abc
12 changed files with 1110 additions and 3 deletions
@@ -0,0 +1,70 @@
---
name: agent-customization
description: 'Work with VS Code agent customization files (.instructions.md, .prompt.md, .agent.md, SKILL.md, copilot-instructions.md, AGENTS.md). Use when asked to: create, update, review, fix, or debug instructions/prompts/agents/skills; save coding preferences or conventions; understand how customization files work; troubleshoot why instructions/skills are not applying or being ignored; compare customization approaches; migrate or refactor existing customizations; set up project guidelines; configure applyTo patterns; define tool restrictions; create reusable workflows; build custom agent modes or personas; package domain knowledge; add frontmatter; fix YAML syntax; or share team standards.'
---
# Agent Customization
## Decision Flow
| Primitive | When to Use |
|-----------|-------------|
| Workspace Instructions | Always-on, applies everywhere in the project |
| File Instructions | Explicit via `applyTo` patterns, or on-demand via `description` |
| MCP | Integrates external systems, APIs, or data |
| Custom Agents | Subagents for context isolation, or multi-stage workflows with tool restrictions |
| Prompts | Single focused task with parameterized inputs |
| Skills | On-demand workflow with bundled assets (scripts/templates) |
## Quick Reference
Consult the reference docs for templates, domain examples, advanced frontmatter options, asset organization, anti-patterns, and creation checklists.
| Type | File | Location | Key Field | Reference |
|------|------|----------|-----------|---------|
| Workspace Instructions | `copilot-instructions.md`, `AGENTS.md` | `.github/` or root | N/A (always applies) | [Link](./primitives/workspace-instructions.md) |
| File Instructions | `*.instructions.md` | `.github/instructions/` | `applyTo` (explicit), `description` (on-demand) | [Link](./primitives/instructions.md) |
| Prompts | `*.prompt.md` | `.github/prompts/` | `description` (required) | [Link](./primitives/prompts.md) |
| Custom Agents | `*.agent.md` | `.github/agents/` | `description` (on-demand), `infer` (subagent) | [Link](./primitives/agents.md) |
| Skills | `SKILL.md` | `.github/skills/<name>/` | `description` (on-demand) | [Link](./primitives/skills.md) |
**User-level**: `{{USER_PROMPTS_FOLDER}}/` (*.prompt.md, *.instructions.md, *.agent.md; not skills)
Customizations roam with user's settings sync
## Creation Process
If you need to explore or validate patterns in the codebase, use a read-only subagent. If the ask-questions tool is available, use it to interview the user and clarify requirements.
Follow these steps when creating any customization file.
### 1. Determine Scope
Ask the user where they want the customization:
- **Workspace**: For project-specific, team-shared customizations → `.github/` folder
- **User profile**: For personal, cross-workspace customizations → `{{USER_PROMPTS_FOLDER}}/`
### 2. Choose the Right Primitive
Use the Decision Flow above to select the appropriate file type based on the user's need.
### 3. Create the File
Create the file directly at the appropriate path:
- Use the location tables in each reference file
- Include required frontmatter as needed
- Add the body content following the templates
### 4. Validate
After creating:
- Confirm the file is in the correct location
- Verify frontmatter syntax (YAML between `---` markers)
- Check that `description` is present and meaningful
## Edge Cases
**Instructions vs Skill?** Does this apply to *most* work, or *specific* tasks? Most → Instructions. Specific → Skill.
**Skill vs Prompt?** Multi-step workflow with bundled assets → Skill. Single focused task with inputs → Prompt.
**Skill vs Custom Agent?** Same capabilities for all steps → Skill. Need context isolation (subagent returns single output) or different tool restrictions per stage → Custom Agent.
@@ -0,0 +1,150 @@
# [Custom Agents (.agent.md)](https://code.visualstudio.com/docs/copilot/customization/custom-agents)
Custom personas with specific tools, instructions, and behaviors.
## Locations
| Path | Scope |
|------|-------|
| `.github/agents/*.agent.md` | Workspace |
| `<profile>/agents/*.agent.md` | User profile |
## Frontmatter
```yaml
---
description: "<required>" # For agent picker and subagent discovery
name: "Agent Name" # Optional, defaults to filename
tools: ["search", "fetch"] # Optional: built-in, MCP (<server>/*), extension
model: "Claude Sonnet 4" # Optional, uses picker default
argument-hint: "Task..." # Optional, input guidance
infer: true # Optional, enable on-demand subagent discovery (default: true)
handoffs: [...] # Optional, transitions to other agents
---
```
## Tools
Specify tool names in the `tools` array. Sources: built-in tools, tool sets, MCP servers (`<server>/*`), or extension-contributed tools.
To discover available tools, search your current tool list or use the tool search capability.
**Special**: `[]` = no tools, omit = defaults. Body reference: `#tool:<name>`
### Tool Aliases
Common aliases for restricting agent capabilities:
| Alias | Purpose |
|-------|---------|
| `shell` | Execute shell commands |
| `read` | Read file contents |
| `edit` | Edit files (exact tools vary) |
| `search` | Search files or text |
| `custom-agent` | Invoke other custom agents as subagents |
| `web` | Fetch URLs and web search |
| `todo` | Create and manage task lists |
### Common Restriction Patterns
```yaml
# Read-only agent (no editing, no execution)
tools: ["read", "search"]
# MCP-only agent
tools: ["myserver/*"]
# No terminal access
tools: ["read", "edit", "search"]
# Planning agent (research only)
tools: ["search", "web", "read"]
```
For the full list of available tools, see the [VS Code documentation](https://code.visualstudio.com/docs/copilot/customization/custom-agents).
## Template
```markdown
---
description: "Generate implementation plans"
tools: ["search", "fetch", "githubRepo", "usages"]
---
You are in planning mode. Generate plans, don't edit code. Include: Overview, Requirements, Steps, Testing.
```
## Invocation
- **Manual**: Agents dropdown or `Chat: Switch Agent...` command
- **On-demand (subagent)**: When `infer: true`, parent agent can delegate based on `description` match—like skills and instructions
## When to Use
**Key Signal**: Orchestrated multi-stage processes with role-based tool restrictions. Different stages need different capabilities or strict handoffs.
## Domain Examples
| Domain | Example Workflow |
|--------|------------------|
| Engineering | planner → implementer → reviewer → deployer |
| Product | research → strategy → execution → measurement |
| Analytics | scope → build → analyze → report |
| Support | triage → troubleshoot → escalate → close |
| Content | research → write → edit → publish |
## Creation Process
### 1. Gather Requirements
- What role or persona should this agent embody?
- What specific tools does this role need (and which should it NOT have)?
- Should this be workspace-specific or personal (user profile)?
- Will this agent hand off to other agents?
### 2. Determine Location
| Scope | Path |
|-------|------|
| Workspace | `.github/agents/<name>.agent.md` |
| User profile | `<profile>/agents/<name>.agent.md` |
### 3. Create the File
```markdown
---
description: "<role description for agent picker>"
tools: ["<minimal tool set>"]
---
You are a <role>. Your responsibilities:
- <primary responsibility>
- <constraints on behavior>
<Specific instructions for this role>
```
### 4. Configure Handoffs (optional)
If this agent is part of a workflow:
```yaml
handoffs:
- agent: "next-stage-agent"
condition: "When <trigger condition>"
```
## Core Principles
1. **Single role per agent**: Each agent embodies one persona with focused responsibilities
2. **Minimal tool set**: Only include tools required for the role—excess tools dilute focus and increase risk
3. **Clear boundaries**: Define what the agent should NOT do as clearly as what it should do
4. **Explicit handoffs**: When workflows span agents, define clear transition triggers
5. **Discoverable via description**: The `description` field drives agent picker display—make it actionable
6. **Keyword-rich for subagent discovery**: When `infer: true`, the `description` determines automatic delegation—include trigger words and use cases so the parent agent knows when to invoke this subagent
## Anti-patterns
- **Swiss-army agents**: Agents with many tools that try to do everything
- **Missing constraints**: Agents without clear boundaries on what they shouldn't attempt
- **Role confusion**: Description doesn't match the persona defined in the body
- **Circular handoffs**: Agent A hands to B which hands back to A without progress criteria
- **Tool sprawl**: Using `tools: []` to disable all tools when specific restrictions would suffice
- **Vague subagent descriptions**: Generic descriptions like "A helpful agent" that don't help the parent decide when to delegate—use phrases like "use proactively after code changes" for clear triggers
@@ -0,0 +1,176 @@
# [File-Specific Instructions (.instructions.md)](https://code.visualstudio.com/docs/copilot/customization/custom-instructions)
Conditional guidelines that apply to specific file types, folders, or tasks using glob patterns.
## Locations
| Path | Scope |
|------|-------|
| `.github/instructions/*.instructions.md` | Workspace |
| `<profile>/instructions/*.instructions.md` | User profile (cross-workspace) |
## Frontmatter
```yaml
---
description: "<required>" # For on-demand discovery (like skills)
name: "Instruction Name" # Optional, defaults to filename
applyTo: "**/*.ts" # Optional, explicit trigger for file patterns
---
```
## applyTo Patterns
| Pattern | Effect |
|---------|--------|
| `"**"` | Always include |
| `"**/*.ts"` | Match .ts files |
| `["src/**", "lib/**"]` | Match multiple (OR) |
| `"**/test/**"` | Match paths containing test |
| `"src/api/**/*.ts"` | Match specific folder + extension |
**Trigger**: Applied when creating or modifying files that match the pattern. Not applied for read-only operations.
**No applyTo**: Instructions can still be loaded implicitly via `description` matching, or manually attached via `Add Context > Instructions`.
## Template
```markdown
---
description: "TypeScript coding standards"
applyTo: "**/*.ts"
---
# TypeScript Guidelines
- Use `interface` for object shapes, `type` for unions
- Avoid `any`, enable strict mode
- JSDoc for public APIs
- Reference tools with #tool:<name> syntax
```
## Examples
### Language-Specific
```yaml
# python-standards.instructions.md
---
description: "Python coding standards"
applyTo: "**/*.py"
---
```
### Framework-Specific
```yaml
# react-components.instructions.md
---
description: "React component patterns"
applyTo: ["src/components/**", "src/pages/**"]
---
```
### Folder-Specific
```yaml
# backend-api.instructions.md
---
description: "Backend API conventions"
applyTo: "src/api/**"
---
```
### Task-Based (On-Demand)
```yaml
# database-migrations.instructions.md
---
description: "Database migration patterns and best practices"
# No applyTo—loaded implicitly when the agent detects migration-related tasks
---
```
## Invocation
- **Explicit (applyTo)**: Auto-attaches when files matching the glob pattern are in context (creating/modifying)
- **Implicit (description)**: Agent loads on-demand when the description fits the current task—like skills
- **Manual**: Chat view → `Add Context``Instructions`
- **Configure**: Chat view → gear icon → `Chat Instructions`
- **New file**: `Chat: New Instructions File` command
## Settings
| Setting | Purpose |
|---------|---------|
| `chat.instructionsFilesLocations` | Additional folders to search for `.instructions.md` |
## Task-Specific Settings (deprecated)
Prefer `.instructions.md` files. Legacy settings for specific workflows:
| Setting | Scenario |
|---------|----------|
| `github.copilot.chat.reviewSelection.instructions` | Code review |
| `github.copilot.chat.commitMessageGeneration.instructions` | Commit messages |
| `github.copilot.chat.pullRequestDescriptionGeneration.instructions` | PR descriptions |
## When to Use
- **Language-specific rules** (Python style, TypeScript patterns) → use `applyTo`
- **Framework conventions** (React, Vue, backend frameworks) → use `applyTo`
- **Folder-based guidelines** (frontend vs backend, tests vs source) → use `applyTo`
- **Task-focused instructions** (API development, migrations, refactoring) → rely on `description` for on-demand loading
**Key Signal**: Use `applyTo` when the instruction is file-based; use a descriptive `description` when the instruction is task-based.
## Creation Process
### 1. Gather Requirements
- **File-based or task-based?** Use `applyTo` for file patterns; omit for task-focused instructions loaded via `description`
- What file types, folders, or patterns should this apply to?
- What coding standards, conventions, or guidelines are needed? Research current codebase using a subagent if needed.
- Should this be workspace-specific or personal (user profile)?
### 2. Determine Location
| Scope | Path |
|-------|------|
| Workspace | `.github/instructions/<name>.instructions.md` |
| User profile | `<profile>/instructions/<name>.instructions.md` |
### 3. Create the File
```markdown
---
description: "<clear, concise description>"
applyTo: "<glob pattern or array>" # Optional—omit for task-based instructions
---
# <Title>
<Guidelines organized by topic>
```
### 4. Verify Activation
- For `applyTo` patterns: Create or edit a matching file to confirm auto-attachment
- For `description`-based: Perform a task matching the description and verify the instruction is loaded
- For manual instructions: Add via `Add Context > Instructions` in chat
## Core Principles
1. **Keyword-rich descriptions**: The `description` frontmatter is how instructions are discovered—include relevant trigger words and use cases
2. **One concern per file**: Layer multiple instruction files for different concerns (e.g., separate files for testing, styling, documentation)
3. **Concise and actionable**: Instructions share context window with conversation—keep them focused
4. **Show, don't tell**: Include brief code examples over lengthy explanations
5. **Pattern precision**: Use specific `applyTo` patterns to avoid over-matching (e.g., `src/api/**/*.ts` not `**/*.ts`)
6. **Reference, don't duplicate**: Use `#tool:<name>` and Markdown links instead of copying content
## Anti-patterns
- **Vague descriptions**: Generic descriptions that don't help discovery (e.g., "Helpful coding tips")
- **Overly broad patterns**: `applyTo: "**"` with content only relevant to specific files
- **Duplicating project docs**: Copying README or CONTRIBUTING content instead of linking
- **Mixing concerns**: Combining unrelated guidelines (testing + API design + styling) in one file
- **Stale instructions**: Guidelines that contradict current codebase patterns
- **Verbose prose**: Long paragraphs instead of actionable bullet points
@@ -0,0 +1,119 @@
# [Prompts (.prompt.md)](https://code.visualstudio.com/docs/copilot/customization/prompt-files)
Reusable task templates triggered on-demand in chat.
## Locations
| Path | Scope |
|------|-------|
| `.github/prompts/*.prompt.md` | Workspace |
| `<profile>/prompts/*.prompt.md` | User profile |
## Frontmatter
```yaml
---
description: "<required>" # Prompt description
name: "Prompt Name" # Optional, defaults to filename
agent: "agent" # Optional: ask, edit, agent, or custom agent
tools: ["search", "fetch"] # Optional: built-in, MCP (<server>/*), extension
model: "Claude Sonnet 4" # Optional, uses picker default
argument-hint: "Task..." # Optional, input guidance
---
```
## Variables
| Syntax | Description |
|--------|-------------|
| `${workspaceFolder}` | Workspace path |
| `${file}`, `${fileBasename}` | Current file |
| `${selection}`, `${selectedText}` | Editor selection |
| `${input:varName}` | Prompt user for input |
| `${input:varName:placeholder}` | With placeholder text |
**Context references**: Use Markdown links for files, `#tool:<name>` for tools.
## Template
```markdown
---
name: "Create React Form"
description: "Generate a React form component"
argument-hint: "Provide form requirements"
---
Generate a React form component with the following requirements:
- Use TypeScript and functional components
- Include form validation
- Follow existing patterns in #tool:codebase
```
## Invocation
- Chat: Type `/` → select prompt (add extra info: `/create-form formName=MyForm`)
- Command: `Chat: Run Prompt...`
- Editor: Open prompt file → play button in title bar
**Tip**: Use `chat.promptFilesRecommendations` to show prompts as actions when starting a new chat.
## Tool Priority
When prompt references an agent, tools are resolved: prompt tools → agent tools → default agent tools.
## When to Use
**Key Signal**: Single focused task with parameterized inputs. Reusable prompt run once per task with different inputs each time.
- Generate test cases for specific code
- Summarize metrics with custom parameters
- Create READMEs from specs
- One-off generation tasks
## Creation Process
### 1. Gather Requirements
- What specific task should this prompt accomplish?
- What inputs/variables does the user need to provide?
- Should this be workspace-specific or personal (user profile)?
- Does it need specific tools or a particular agent mode?
### 2. Determine Location
| Scope | Path |
|-------|------|
| Workspace | `.github/prompts/<name>.prompt.md` |
| User profile | `<profile>/prompts/<name>.prompt.md` |
### 3. Create the File
```markdown
---
description: "<what this prompt does>"
argument-hint: "<guidance for user input>"
---
<Clear task instructions with ${variables} for user input>
<Examples of expected output if helpful>
```
### 4. Test the Prompt
- Ask the user to invoke with `/prompt-name` in chat
- Confirm output matches intended behavior
## Core Principles
1. **Single task focus**: One prompt = one well-defined task; don't combine unrelated operations
2. **Clear input contract**: Use `${input:varName:placeholder}` with descriptive placeholders
3. **Output examples**: Show expected output format when quality depends on specific structure
4. **Reuse over duplication**: Reference instruction files instead of copying guidelines
5. **Appropriate tooling**: Only specify `tools` when the task requires more than defaults
## Anti-patterns
- **Multi-task prompts**: Combining "create and test and deploy" in one prompt
- **Missing context**: Prompts that assume knowledge not provided in variables
- **Hardcoded values**: Embedding specific file paths or names instead of using variables
- **Vague descriptions**: Descriptions that don't help users understand when to use the prompt
- **Over-tooling**: Specifying many tools when the task only needs search or file access
@@ -0,0 +1,182 @@
# [Agent Skills (SKILL.md)](https://code.visualstudio.com/docs/copilot/customization/agent-skills)
Folders of instructions, scripts, and resources that agents loads when relevant for specialized tasks.
## Structure
```
.github/skills/<skill-name>/
├── SKILL.md # Required
├── test-template.js # Scripts
├── examples/ # Example scenarios
└── docs/ # Additional docs
```
## Locations
| Path | Scope |
|------|-------|
| `.github/skills/<name>/` | Project (recommended) |
| `.claude/skills/<name>/` | Project (legacy) |
| `~/.copilot/skills/<name>/` | Personal (recommended) |
| `~/.claude/skills/<name>/` | Personal (legacy) |
## SKILL.md Format
### Frontmatter (required)
```yaml
---
name: skill-name # Must match parent folder name
description: 'What the skill does and when to use it. For on-demand discovery. Max 1024 chars.'
---
```
#### `name` field rules
- **Must match the parent directory name** (e.g., `pdf-processing/SKILL.md` requires `name: pdf-processing`)
- 1-64 characters
- Lowercase alphanumeric and hyphens only (`a-z`, `0-9`, `-`)
- Must not start or end with `-`
- Must not contain consecutive hyphens (`--`)
#### Optional frontmatter fields
```yaml
license: Apache-2.0 # License name or bundled file reference
compatibility: Requires git, docker, jq # Environment requirements (max 500 chars)
```
### Body
- What the skill accomplishes
- When to use the skill
- Step-by-step procedures
- Examples of input/output
- References to scripts/resources via relative paths: `[test script](./test-template.js)`
## Template
```markdown
---
name: webapp-testing
description: 'Test web applications using Playwright. Use when verifying frontend functionality, debugging UI, capturing screenshots.'
---
# Web Application Testing
## When to Use
- Verify frontend functionality
- Debug UI behavior
- Capture browser screenshots
## Procedure
1. Start the web server
2. Run tests with `[test script](./test-template.js)`
3. Review screenshots in `./screenshots/`
## References
- [examples/login-test.js](./examples/login-test.js)
```
## Progressive Loading
Skills use three-level loading for efficient context:
1. **Discovery** (~100 tokens): Agent reads `name` and `description` from frontmatter (always available)
2. **Instructions** (<5000 tokens recommended): When description matches current task, loads `SKILL.md` body
3. **Resources** (as needed): Additional files (scripts, examples) load only when referenced
Keep file references one level deep from `SKILL.md`. Avoid deeply nested reference chains.
## When to Use
**Key Signal**: Repeatable, on-demand workflows with bundled assets (scripts, templates, reference docs).
## Domain Examples
| Domain | Examples |
|--------|----------|
| Engineering | Microservice deployment, incident response runbook, security review |
| Product | User research synthesis, roadmap prioritization, launch validation |
| Data | Customer segmentation, data quality validation, cohort analysis |
| Design | Design critique framework, user flow docs, design-to-dev handoff |
| Sales | Enterprise qualification, demo prep checklist, proposal framework |
## Asset Organization
| Folder | Purpose |
|--------|----------|
| `scripts/` | Code that runs each time (profiling, deployment, data fetch) |
| `references/` | Docs that clarify complex bits (architecture, API schemas) |
| `assets/` | Templates and boilerplate (Terraform, SQL templates, dashboard JSON) |
## Creation Process
### 1. Gather Requirements
- What workflow or domain knowledge should this skill provide?
- What resources are needed (scripts, templates, reference docs)?
- Should this be project-specific or personal?
### 2. Determine Location
| Scope | Path |
|-------|------|
| Project | `.github/skills/<skill-name>/SKILL.md` |
| Personal | `~/.copilot/skills/<skill-name>/SKILL.md` |
### 3. Create the Skill Folder
```
<skill-name>/
├── SKILL.md # Required: instructions and metadata
├── scripts/ # Optional: executable code
├── references/ # Optional: documentation to load as needed
└── assets/ # Optional: templates, boilerplate
```
### 4. Write SKILL.md
```markdown
---
name: <skill-name>
description: '<keyword-rich description of what and when>'
---
# <Skill Title>
## When to Use
<Triggers and use cases>
## Procedure
<Step-by-step workflow>
## Resources
- [script.py](./scripts/script.py)
- [reference.md](./references/reference.md)
```
### 5. Add Resources
Create supporting files as needed, referencing them from SKILL.md with relative paths.
## Core Principles
1. **Keyword-rich descriptions**: The `description` frontmatter is how skills are discovered—include all relevant trigger words and use cases
2. **Progressive loading**: Keep SKILL.md under 500 lines; move detailed content to reference files
3. **Relative path references**: Always use `./` relative paths to reference skill resources
4. **Minimal context footprint**: Only load resources when needed—context window is shared
5. **Self-contained workflows**: Include all procedural knowledge needed to complete the task
## Anti-patterns
- **Vague descriptions**: Generic descriptions that don't help discovery (e.g., "A helpful skill")
- **Monolithic SKILL.md**: Putting everything in one file instead of using reference files
- **Absolute paths**: Using system paths instead of relative references
- **Missing procedures**: Descriptions of what but not how—skills need step-by-step guidance
- **Untested scripts**: Including scripts without verifying they work in the target environment
- **Duplicate content**: Copying reference content into SKILL.md instead of linking
- **Name mismatch**: Folder name doesn't match the `name` field in frontmatter
@@ -0,0 +1,129 @@
# [Workspace Instructions](https://code.visualstudio.com/docs/copilot/customization/custom-instructions)
Guidelines that automatically apply to all chat requests across your entire workspace.
## File Types (Choose One)
Workspaces should have **only one** of these files—not both:
| File | Purpose |
|------|---------|
| `.github/copilot-instructions.md` | Project-wide coding standards and preferences (Recommended) |
| `AGENTS.md` | Same, but using an open standard |
## copilot-instructions.md
Single file at workspace root that applies to every chat request automatically.
**Location**: `.github/copilot-instructions.md`
### Template
```markdown
# Project Guidelines
## Code Style
- Use TypeScript strict mode
- Prefer functional components in React
- Follow existing patterns in the codebase
## Architecture
- Services use dependency injection
- All public APIs need JSDoc documentation
## Testing
- Write unit tests for business logic
- Use integration tests for API endpoints
```
### Cross-Editor Support
This file is also detected by GitHub Copilot in Visual Studio and GitHub.com, enabling shared instructions across editors.
## AGENTS.md
Instructions for projects using multiple AI agents. Placed at workspace root or in subfolders.
**Location**: Workspace root, or subfolders. Use `AGENTS.md` files in subfolders for different parts of your project (e.g., frontend vs backend).
## When to Use
- **General coding standards** that apply everywhere
- **Team preferences** shared through version control
- **Project-wide requirements** like testing standards or documentation rules
**Key Signal**: Context that's always available. Standards, guidelines, and expectations that apply broadly across work.
## Domain Examples
| Domain | Examples |
|--------|----------|
| Engineering | TypeScript strict mode, test coverage, accessibility (WCAG 2.1 AA) |
| Product | User stories in briefs, accessibility/i18n in specs |
| Data | SQL query comments, metric definitions linked |
| Support | Empathetic tone, escalation includes impact/context |
| Design | Use shared component library, include error states |
## Creation Process
### 1. Assess Existing Instructions
Check if a workspace instructions file already exists:
- `.github/copilot-instructions.md`
- `AGENTS.md` at workspace root
If one exists, update it rather than creating a second file. Workspaces should only have one.
### 2. Choose File Type (If None Exists)
| File | Best For |
|------|----------|
| `copilot-instructions.md` (Recommended) | VS Code/GitHub Copilot-specific, cross-editor support |
| `AGENTS.md` | Open standard, supports subfolder organization |
### 3. Create or Update
**For copilot-instructions.md**:
```
.github/copilot-instructions.md
```
**For AGENTS.md** (supports hierarchy):
```
/AGENTS.md # Root-level defaults
/frontend/AGENTS.md # Frontend-specific
/backend/AGENTS.md # Backend-specific
```
### 4. Structure Content
Research existing guidelines and conventions in the workspace using a subagent as needed.
```markdown
# Project Guidelines
## Code Style
<Language and formatting preferences>
## Architecture
<Patterns and structure conventions>
## Testing
<Coverage and testing requirements>
```
## Core Principles
1. **Concise and actionable**: Every line should guide behavior—remove filler prose
2. **Project-specific focus**: Include conventions not obvious from reading the code
3. **Link, don't duplicate**: Reference READMEs, ADRs, and docs instead of copying
4. **Keep current**: Update when practices change—stale instructions cause confusion
## Anti-patterns
- **Using both file types**: Having both `copilot-instructions.md` and `AGENTS.md` in the same workspace
- **Duplicating project docs**: Copying README or CONTRIBUTING content instead of linking to them
- **Obvious instructions**: Stating conventions already enforced by linters or obvious from code
- **Verbose prose**: Long explanations instead of clear, scannable guidelines
- **Stale content**: Instructions that no longer match actual project practices
- **Kitchen sink**: Trying to include every possible guideline instead of focusing on what matters most
+11 -1
View File
@@ -4203,6 +4203,16 @@
"experimental"
]
},
"github.copilot.chat.agentCustomizationSkill.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "%github.copilot.config.agentCustomizationSkill.enabled%",
"tags": [
"advanced",
"experimental",
"onExp"
]
},
"github.copilot.chat.searchSubagent.enabled": {
"type": "boolean",
"default": false,
@@ -5577,4 +5587,4 @@
"node-gyp": "npm:node-gyp@10.3.1",
"zod": "3.25.76"
}
}
}
+2 -1
View File
@@ -393,5 +393,6 @@
"copilot.tools.runSubagent.description": "Runs a task within an isolated subagent context. Enables efficient organization of tasks and context window management.",
"copilot.tools.searchSubagent.name": "Search Subagent",
"copilot.tools.searchSubagent.description": "Launch an iterative search-focused subagent to find relevant code in your workspace.",
"github.copilot.config.searchSubagent.enabled": "Enable the search subagent tool for iterative code exploration in the workspace."
"github.copilot.config.searchSubagent.enabled": "Enable the search subagent tool for iterative code exploration in the workspace.",
"github.copilot.config.agentCustomizationSkill.enabled": "Enable the built-in agent customization skill."
}
@@ -0,0 +1,250 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { ILogService } from '../../../platform/log/common/logService';
import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext';
import { Disposable } from '../../../util/vs/base/common/lifecycle';
import { Emitter } from '../../../util/vs/base/common/event';
const SKILL_FOLDER_NAME = 'agent-customization';
const SKILL_FILENAME = 'SKILL.md';
const SKILL_SCHEME = 'copilot-skill';
/**
* Placeholder in SKILL.md that will be replaced with the actual user prompts folder path.
*/
const USER_PROMPTS_FOLDER_PLACEHOLDER = '{{USER_PROMPTS_FOLDER}}';
/**
* Provides the built-in agent-customization skill that teaches agents
* how to work with VS Code's customization system (instructions, prompts, agents, skills).
*
* Uses a FileSystemProvider (instead of TextDocumentContentProvider) so that VS Code's
* fileService.readFile() can read the skill content during prompt parsing, not just when
* the file is opened in an editor.
*/
export class AgentCustomizationSkillProvider extends Disposable implements vscode.ChatSkillProvider, vscode.FileSystemProvider {
private readonly _skillContentUri: vscode.Uri;
private _cachedContent: Uint8Array | undefined;
private readonly _onDidChangeFile = this._register(new Emitter<vscode.FileChangeEvent[]>());
readonly onDidChangeFile = this._onDidChangeFile.event;
constructor(
@ILogService private readonly logService: ILogService,
@IVSCodeExtensionContext private readonly extensionContext: IVSCodeExtensionContext,
) {
super();
// Create a virtual URI for the dynamically generated skill content
this._skillContentUri = vscode.Uri.from({
scheme: SKILL_SCHEME,
path: `/${SKILL_FOLDER_NAME}/${SKILL_FILENAME}`
});
// Register a FileSystemProvider to serve the dynamic skill content.
// This is required because VS Code's promptsService uses fileService.readFile()
// to read skill content, which only works with FileSystemProvider, not TextDocumentContentProvider.
this._register(vscode.workspace.registerFileSystemProvider(SKILL_SCHEME, this, { isReadonly: true }));
}
// #region FileSystemProvider implementation
watch(_uri: vscode.Uri, _options: { readonly recursive: boolean; readonly excludes: readonly string[] }): vscode.Disposable {
// No need to watch - content is static after first load
return { dispose: () => { } };
}
/**
* Converts a virtual URI path to the corresponding physical URI in the extension assets.
* Virtual: /agent-customization/primitives/agents.md
* Physical: extensionUri/assets/prompts/skills/agent-customization/primitives/agents.md
*/
private _toAssetUri(virtualPath: string): vscode.Uri | undefined {
// Ensure the path is within our skill folder
const prefix = `/${SKILL_FOLDER_NAME}`;
if (!virtualPath.startsWith(prefix)) {
return undefined;
}
// Get the relative path after /agent-customization
const relativePath = virtualPath.substring(prefix.length);
// Build the full asset path
return vscode.Uri.joinPath(
this.extensionContext.extensionUri,
'assets',
'prompts',
'skills',
SKILL_FOLDER_NAME,
...relativePath.split('/').filter(Boolean)
);
}
async stat(uri: vscode.Uri): Promise<vscode.FileStat> {
// Handle the dynamic SKILL.md file
if (uri.path === `/${SKILL_FOLDER_NAME}/${SKILL_FILENAME}`) {
const content = await this._getSkillContentBytes();
return {
type: vscode.FileType.File,
ctime: 0,
mtime: Date.now(),
size: content.length
};
}
// Handle root and skill folder directories
if (uri.path === `/${SKILL_FOLDER_NAME}` || uri.path === '/') {
return {
type: vscode.FileType.Directory,
ctime: 0,
mtime: 0,
size: 0
};
}
// Handle nested files/directories (e.g., /agent-customization/primitives/agents.md)
const assetUri = this._toAssetUri(uri.path);
if (assetUri) {
try {
return await vscode.workspace.fs.stat(assetUri);
} catch {
// Fall through to FileNotFound
}
}
throw vscode.FileSystemError.FileNotFound(uri);
}
async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
if (uri.path === '/' || uri.path === '') {
return [[SKILL_FOLDER_NAME, vscode.FileType.Directory]];
}
// For paths within the skill folder, enumerate actual directory contents
const assetUri = this._toAssetUri(uri.path);
if (assetUri) {
try {
return await vscode.workspace.fs.readDirectory(assetUri);
} catch {
// Fall through to empty result
}
}
return [];
}
createDirectory(_uri: vscode.Uri): void {
throw vscode.FileSystemError.NoPermissions('Readonly file system');
}
async readFile(uri: vscode.Uri): Promise<Uint8Array> {
// The root SKILL.md has dynamic content (placeholder injection)
if (uri.path === `/${SKILL_FOLDER_NAME}/${SKILL_FILENAME}`) {
return this._getSkillContentBytes();
}
// All other files are read directly from the assets folder
const assetUri = this._toAssetUri(uri.path);
if (assetUri) {
try {
return await vscode.workspace.fs.readFile(assetUri);
} catch {
// Fall through to FileNotFound
}
}
throw vscode.FileSystemError.FileNotFound(uri);
}
writeFile(_uri: vscode.Uri, _content: Uint8Array, _options: { readonly create: boolean; readonly overwrite: boolean }): void {
throw vscode.FileSystemError.NoPermissions('Readonly file system');
}
delete(_uri: vscode.Uri, _options: { readonly recursive: boolean }): void {
throw vscode.FileSystemError.NoPermissions('Readonly file system');
}
rename(_oldUri: vscode.Uri, _newUri: vscode.Uri, _options: { readonly overwrite: boolean }): void {
throw vscode.FileSystemError.NoPermissions('Readonly file system');
}
// #endregion
/**
* Gets the user prompts folder path from the extension context's globalStorageUri.
* The globalStorageUri is typically: `.../User/globalStorage/<extension-id>/`
* The user prompts folder is: `.../User/prompts/`
*/
private _getUserPromptsFolder(): string {
const globalStorageUri = this.extensionContext.globalStorageUri;
// Navigate up from globalStorage/<extension-id>/ to User/ and then to prompts/
// globalStorageUri: file:///Users/.../User/globalStorage/github.copilot-chat/
// We want: file:///Users/.../User/prompts/
const userFolderUri = vscode.Uri.joinPath(globalStorageUri, '..', '..');
const userPromptsFolderUri = vscode.Uri.joinPath(userFolderUri, 'prompts');
return userPromptsFolderUri.fsPath;
}
/**
* Reads the SKILL.md template and injects the user prompts folder path.
* Returns bytes for use by the FileSystemProvider.
*/
private async _getSkillContentBytes(): Promise<Uint8Array> {
if (this._cachedContent) {
return this._cachedContent;
}
try {
// Build the URI to the original skill file within the extension
const skillTemplateUri = vscode.Uri.joinPath(
this.extensionContext.extensionUri,
'assets',
'prompts',
'skills',
SKILL_FOLDER_NAME,
SKILL_FILENAME
);
// Read the template content
const templateBytes = await vscode.workspace.fs.readFile(skillTemplateUri);
const templateContent = new TextDecoder().decode(templateBytes);
// Replace the placeholder with the actual user prompts folder path
const userPromptsFolder = this._getUserPromptsFolder();
const processedContent = templateContent.replace(USER_PROMPTS_FOLDER_PLACEHOLDER, userPromptsFolder);
this._cachedContent = new TextEncoder().encode(processedContent);
this.logService.trace(`[AgentCustomizationSkillProvider] Injected user prompts folder: ${userPromptsFolder}`);
return this._cachedContent;
} catch (error) {
this.logService.error(`[AgentCustomizationSkillProvider] Error reading skill template: ${error}`);
return new Uint8Array();
}
}
async provideSkills(
_context: unknown,
token: vscode.CancellationToken
): Promise<vscode.ChatResource[]> {
try {
if (token.isCancellationRequested) {
return [];
}
this.logService.trace(`[AgentCustomizationSkillProvider] Providing skill at ${this._skillContentUri.toString()}`);
return [{ uri: this._skillContentUri }];
} catch (error) {
this.logService.error(`[AgentCustomizationSkillProvider] Error providing skills: ${error}`);
return [];
}
}
}
@@ -5,6 +5,7 @@
import * as vscode from 'vscode';
import { ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';
import { IExperimentationService } from '../../../platform/telemetry/common/nullExperimentationService';
import { Disposable } from '../../../util/vs/base/common/lifecycle';
import { SyncDescriptor } from '../../../util/vs/platform/instantiation/common/descriptors';
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
@@ -13,6 +14,7 @@ import { GitHubOrgCustomAgentProvider } from './githubOrgCustomAgentProvider';
import { GitHubOrgInstructionsProvider } from './githubOrgInstructionsProvider';
import { ImplementAgentProvider } from './implementAgentProvider';
import { PlanAgentProvider } from './planAgentProvider';
import { AgentCustomizationSkillProvider } from './agentCustomizationSkillProvider';
export class PromptFileContribution extends Disposable implements IExtensionContribution {
readonly id = 'PromptFiles';
@@ -20,6 +22,7 @@ export class PromptFileContribution extends Disposable implements IExtensionCont
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@IExperimentationService experimentationService: IExperimentationService,
) {
super();
@@ -48,5 +51,13 @@ export class PromptFileContribution extends Disposable implements IExtensionCont
this._register(vscode.chat.registerInstructionsProvider(githubOrgInstructionsProvider));
}
}
// Register skill provider for built-in agent customization skill
if ('registerSkillProvider' in vscode.chat) {
if (configurationService.getExperimentBasedConfig(ConfigKey.Advanced.AgentCustomizationSkillEnabled, experimentationService)) {
const agentCustomizationSkillProvider: vscode.ChatSkillProvider = instantiationService.createInstance(new SyncDescriptor(AgentCustomizationSkillProvider));
this._register(vscode.chat.registerSkillProvider(agentCustomizationSkillProvider));
}
}
}
}
@@ -15,7 +15,7 @@ import { CancellationToken } from '../../../util/vs/base/common/cancellation';
import { CancellationError } from '../../../util/vs/base/common/errors';
import { Schemas } from '../../../util/vs/base/common/network';
import { isAbsolute } from '../../../util/vs/base/common/path';
import { isEqual, normalizePath } from '../../../util/vs/base/common/resources';
import { extUriBiasedIgnorePathCase, isEqual, normalizePath } from '../../../util/vs/base/common/resources';
import { isString } from '../../../util/vs/base/common/types';
import { URI } from '../../../util/vs/base/common/uri';
import { IInstantiationService, ServicesAccessor } from '../../../util/vs/platform/instantiation/common/instantiation';
@@ -132,6 +132,12 @@ export async function assertFileOkForTool(accessor: ServicesAccessor, uri: URI,
if (instructionIndexFile.instructions.has(normalizedUri) || instructionIndexFile.skills.has(normalizedUri)) {
return;
}
// Check if the URI is under any skill folder (e.g., nested files like primitives/agents.md)
for (const skillFolderUri of instructionIndexFile.skillFolders) {
if (extUriBiasedIgnorePathCase.isEqualOrParent(normalizedUri, skillFolderUri)) {
return;
}
}
}
} else {
if (await customInstructionsService.isExternalInstructionsFile(normalizedUri)) {
@@ -716,6 +716,9 @@ export namespace ConfigKey {
clearInputs?: boolean;
thinkingKeepTurns?: number;
} | null>('chat.anthropic.contextEditing.config', ConfigType.Simple, null);
/** Enable the built-in agent customization skill provider */
export const AgentCustomizationSkillEnabled = defineSetting<boolean>('chat.agentCustomizationSkill.enabled', ConfigType.ExperimentBased, false);
}
/**