From dac3d77abc4f41741d447f3fc48cc3004a8caa84 Mon Sep 17 00:00:00 2001 From: Harald Kirschner Date: Tue, 27 Jan 2026 10:53:43 -0800 Subject: [PATCH] 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> --- .../skills/agent-customization/SKILL.md | 70 +++++ .../agent-customization/primitives/agents.md | 150 +++++++++++ .../primitives/instructions.md | 176 ++++++++++++ .../agent-customization/primitives/prompts.md | 119 +++++++++ .../agent-customization/primitives/skills.md | 182 +++++++++++++ .../primitives/workspace-instructions.md | 129 +++++++++ extensions/copilot/package.json | 12 +- extensions/copilot/package.nls.json | 3 +- .../agentCustomizationSkillProvider.ts | 250 ++++++++++++++++++ .../agents/vscode-node/promptFileContrib.ts | 11 + .../src/extension/tools/node/toolUtils.ts | 8 +- .../common/configurationService.ts | 3 + 12 files changed, 1110 insertions(+), 3 deletions(-) create mode 100644 extensions/copilot/assets/prompts/skills/agent-customization/SKILL.md create mode 100644 extensions/copilot/assets/prompts/skills/agent-customization/primitives/agents.md create mode 100644 extensions/copilot/assets/prompts/skills/agent-customization/primitives/instructions.md create mode 100644 extensions/copilot/assets/prompts/skills/agent-customization/primitives/prompts.md create mode 100644 extensions/copilot/assets/prompts/skills/agent-customization/primitives/skills.md create mode 100644 extensions/copilot/assets/prompts/skills/agent-customization/primitives/workspace-instructions.md create mode 100644 extensions/copilot/src/extension/agents/vscode-node/agentCustomizationSkillProvider.ts diff --git a/extensions/copilot/assets/prompts/skills/agent-customization/SKILL.md b/extensions/copilot/assets/prompts/skills/agent-customization/SKILL.md new file mode 100644 index 00000000000..cb3553e4d3d --- /dev/null +++ b/extensions/copilot/assets/prompts/skills/agent-customization/SKILL.md @@ -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//` | `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. diff --git a/extensions/copilot/assets/prompts/skills/agent-customization/primitives/agents.md b/extensions/copilot/assets/prompts/skills/agent-customization/primitives/agents.md new file mode 100644 index 00000000000..1a545463074 --- /dev/null +++ b/extensions/copilot/assets/prompts/skills/agent-customization/primitives/agents.md @@ -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 | +| `/agents/*.agent.md` | User profile | + +## Frontmatter + +```yaml +--- +description: "" # For agent picker and subagent discovery +name: "Agent Name" # Optional, defaults to filename +tools: ["search", "fetch"] # Optional: built-in, MCP (/*), 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 (`/*`), 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:` + +### 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/.agent.md` | +| User profile | `/agents/.agent.md` | + +### 3. Create the File + +```markdown +--- +description: "" +tools: [""] +--- +You are a . Your responsibilities: +- +- + + +``` + +### 4. Configure Handoffs (optional) + +If this agent is part of a workflow: +```yaml +handoffs: + - agent: "next-stage-agent" + condition: "When " +``` + +## 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 \ No newline at end of file diff --git a/extensions/copilot/assets/prompts/skills/agent-customization/primitives/instructions.md b/extensions/copilot/assets/prompts/skills/agent-customization/primitives/instructions.md new file mode 100644 index 00000000000..8585e0c8c75 --- /dev/null +++ b/extensions/copilot/assets/prompts/skills/agent-customization/primitives/instructions.md @@ -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 | +| `/instructions/*.instructions.md` | User profile (cross-workspace) | + +## Frontmatter + +```yaml +--- +description: "" # 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: 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/.instructions.md` | +| User profile | `/instructions/.instructions.md` | + +### 3. Create the File + +```markdown +--- +description: "" +applyTo: "" # Optional—omit for task-based instructions +--- +# + +<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 \ No newline at end of file diff --git a/extensions/copilot/assets/prompts/skills/agent-customization/primitives/prompts.md b/extensions/copilot/assets/prompts/skills/agent-customization/primitives/prompts.md new file mode 100644 index 00000000000..16fdb7edf1b --- /dev/null +++ b/extensions/copilot/assets/prompts/skills/agent-customization/primitives/prompts.md @@ -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 \ No newline at end of file diff --git a/extensions/copilot/assets/prompts/skills/agent-customization/primitives/skills.md b/extensions/copilot/assets/prompts/skills/agent-customization/primitives/skills.md new file mode 100644 index 00000000000..fb00e50aba5 --- /dev/null +++ b/extensions/copilot/assets/prompts/skills/agent-customization/primitives/skills.md @@ -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 \ No newline at end of file diff --git a/extensions/copilot/assets/prompts/skills/agent-customization/primitives/workspace-instructions.md b/extensions/copilot/assets/prompts/skills/agent-customization/primitives/workspace-instructions.md new file mode 100644 index 00000000000..51c3c2c71c0 --- /dev/null +++ b/extensions/copilot/assets/prompts/skills/agent-customization/primitives/workspace-instructions.md @@ -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 \ No newline at end of file diff --git a/extensions/copilot/package.json b/extensions/copilot/package.json index 56005739c61..49506e95855 100644 --- a/extensions/copilot/package.json +++ b/extensions/copilot/package.json @@ -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" } -} +} \ No newline at end of file diff --git a/extensions/copilot/package.nls.json b/extensions/copilot/package.nls.json index 6c9ef6c822f..13e5e14b1a5 100644 --- a/extensions/copilot/package.nls.json +++ b/extensions/copilot/package.nls.json @@ -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." } diff --git a/extensions/copilot/src/extension/agents/vscode-node/agentCustomizationSkillProvider.ts b/extensions/copilot/src/extension/agents/vscode-node/agentCustomizationSkillProvider.ts new file mode 100644 index 00000000000..5b92879da17 --- /dev/null +++ b/extensions/copilot/src/extension/agents/vscode-node/agentCustomizationSkillProvider.ts @@ -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 []; + } + } +} diff --git a/extensions/copilot/src/extension/agents/vscode-node/promptFileContrib.ts b/extensions/copilot/src/extension/agents/vscode-node/promptFileContrib.ts index 569af0adcdc..179b49f8ad4 100644 --- a/extensions/copilot/src/extension/agents/vscode-node/promptFileContrib.ts +++ b/extensions/copilot/src/extension/agents/vscode-node/promptFileContrib.ts @@ -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)); + } + } } } diff --git a/extensions/copilot/src/extension/tools/node/toolUtils.ts b/extensions/copilot/src/extension/tools/node/toolUtils.ts index d1647d26caf..c7e37aee619 100644 --- a/extensions/copilot/src/extension/tools/node/toolUtils.ts +++ b/extensions/copilot/src/extension/tools/node/toolUtils.ts @@ -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)) { diff --git a/extensions/copilot/src/platform/configuration/common/configurationService.ts b/extensions/copilot/src/platform/configuration/common/configurationService.ts index 74b233a3223..9f109b9a7eb 100644 --- a/extensions/copilot/src/platform/configuration/common/configurationService.ts +++ b/extensions/copilot/src/platform/configuration/common/configurationService.ts @@ -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); } /**