Add Copilot as the 6th converter target, transforming Claude Code plugins
into Copilot's native format: custom agents (.agent.md), agent skills
(SKILL.md), and MCP server configuration JSON.
Component mapping:
- Agents → .github/agents/{name}.agent.md (with Copilot frontmatter)
- Commands → .github/skills/{name}/SKILL.md
- Skills → .github/skills/{name}/ (copied as-is)
- MCP servers → .github/copilot-mcp-config.json
- Hooks → skipped with warning
Also adds `compound sync copilot` support and fixes YAML quoting for
the `*` character in frontmatter serialization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
11 KiB
title, type, date, status
| title | type | date | status |
|---|---|---|---|
| feat: Add GitHub Copilot converter target | feat | 2026-02-14 | complete |
feat: Add GitHub Copilot Converter Target
Overview
Add GitHub Copilot as a converter target following the established TargetHandler pattern. This converts the compound-engineering Claude Code plugin into Copilot's native format: custom agents (.agent.md), agent skills (SKILL.md), and MCP server configuration JSON.
Brainstorm: docs/brainstorms/2026-02-14-copilot-converter-target-brainstorm.md
Problem Statement
The CLI tool (compound) already supports converting Claude Code plugins to 5 target formats (OpenCode, Codex, Droid, Cursor, Pi). GitHub Copilot is a widely-used AI coding assistant that now supports custom agents, skills, and MCP servers — but there's no converter target for it.
Proposed Solution
Follow the existing converter pattern exactly:
- Define types (
src/types/copilot.ts) - Implement converter (
src/converters/claude-to-copilot.ts) - Implement writer (
src/targets/copilot.ts) - Register target (
src/targets/index.ts) - Add sync support (
src/sync/copilot.ts,src/commands/sync.ts) - Write tests and documentation
Component Mapping
| Claude Code | Copilot | Output Path |
|---|---|---|
Agents (.md) |
Custom Agents (.agent.md) |
.github/agents/{name}.agent.md |
Commands (.md) |
Agent Skills (SKILL.md) |
.github/skills/{name}/SKILL.md |
Skills (SKILL.md) |
Agent Skills (SKILL.md) |
.github/skills/{name}/SKILL.md |
| MCP Servers | Config JSON | .github/copilot-mcp-config.json |
| Hooks | Skipped | Warning to stderr |
Technical Approach
Phase 1: Types
File: src/types/copilot.ts
export type CopilotAgent = {
name: string
content: string // Full .agent.md content with frontmatter
}
export type CopilotGeneratedSkill = {
name: string
content: string // SKILL.md content with frontmatter
}
export type CopilotSkillDir = {
name: string
sourceDir: string
}
export type CopilotMcpServer = {
type: string
command?: string
args?: string[]
url?: string
tools: string[]
env?: Record<string, string>
headers?: Record<string, string>
}
export type CopilotBundle = {
agents: CopilotAgent[]
generatedSkills: CopilotGeneratedSkill[]
skillDirs: CopilotSkillDir[]
mcpConfig?: Record<string, CopilotMcpServer>
}
Phase 2: Converter
File: src/converters/claude-to-copilot.ts
Agent conversion:
- Frontmatter:
description(required, fallback to"Converted from Claude agent {name}"),tools: ["*"],infer: true - Pass through
modelif present - Fold
capabilitiesinto body as## Capabilitiessection (same as Cursor) - Use
formatFrontmatter()utility - Warn if body exceeds 30,000 characters (
.length)
Command → Skill conversion:
- Convert to SKILL.md format with frontmatter:
name,description - Flatten namespaced names:
workflows:plan→plan - Drop
allowed-tools,model,disable-model-invocationsilently - Include
argument-hintas## Argumentssection in body
Skill pass-through:
- Map to
CopilotSkillDiras-is (same as Cursor)
MCP server conversion:
- Transform env var names:
API_KEY→COPILOT_MCP_API_KEY - Skip vars already prefixed with
COPILOT_MCP_ - Add
type: "local"for command-based servers,type: "sse"for URL-based - Set
tools: ["*"]for all servers
Content transformation (transformContentForCopilot):
| Pattern | Input | Output |
|---|---|---|
| Task calls | Task repo-research-analyst(desc) |
Use the repo-research-analyst skill to: desc |
| Slash commands | /workflows:plan |
/plan |
| Path rewriting | .claude/ |
.github/ |
| Home path rewriting | ~/.claude/ |
~/.copilot/ |
| Agent references | @security-sentinel |
the security-sentinel agent |
Hooks: Warn to stderr if present, skip.
Phase 3: Writer
File: src/targets/copilot.ts
Path resolution:
- If
outputRootbasename is.github, write directly into it (avoid.github/.github/double-nesting) - Otherwise, nest under
.github/
Write operations:
- Agents →
.github/agents/{name}.agent.md(note:.agent.mdextension) - Generated skills (from commands) →
.github/skills/{name}/SKILL.md - Skill dirs →
.github/skills/{name}/(copy viacopyDir) - MCP config →
.github/copilot-mcp-config.json(backup existing withbackupFile)
Phase 4: Target Registration
File: src/targets/index.ts
Add import and register:
import { convertClaudeToCopilot } from "../converters/claude-to-copilot"
import { writeCopilotBundle } from "./copilot"
// In targets record:
copilot: {
name: "copilot",
implemented: true,
convert: convertClaudeToCopilot as TargetHandler<CopilotBundle>["convert"],
write: writeCopilotBundle as TargetHandler<CopilotBundle>["write"],
},
Phase 5: Sync Support
File: src/sync/copilot.ts
Follow the Cursor sync pattern (src/sync/cursor.ts):
- Symlink skills to
.github/skills/usingforceSymlink - Validate skill names with
isValidSkillName - Convert MCP servers with
COPILOT_MCP_prefix transformation - Merge MCP config into existing
.github/copilot-mcp-config.json
File: src/commands/sync.ts
- Add
"copilot"tovalidTargetsarray - Add case in
resolveOutputRoot():case "copilot": return path.join(process.cwd(), ".github") - Add import and switch case for
syncToCopilot - Update meta description to include "Copilot"
Phase 6: Tests
File: tests/copilot-converter.test.ts
Test cases (following tests/cursor-converter.test.ts pattern):
describe("convertClaudeToCopilot")
✓ converts agents to .agent.md with Copilot frontmatter
✓ agent description is required, fallback generated if missing
✓ agent with empty body gets default body
✓ agent capabilities are prepended to body
✓ agent model field is passed through
✓ agent tools defaults to ["*"]
✓ agent infer defaults to true
✓ warns when agent body exceeds 30k characters
✓ converts commands to skills with SKILL.md format
✓ flattens namespaced command names
✓ command name collision after flattening is deduplicated
✓ command allowedTools is silently dropped
✓ command with argument-hint gets Arguments section
✓ passes through skill directories
✓ skill and generated skill name collision is deduplicated
✓ converts MCP servers with COPILOT_MCP_ prefix
✓ MCP env vars already prefixed are not double-prefixed
✓ MCP servers get type field (local vs sse)
✓ warns when hooks are present
✓ no warning when hooks are absent
✓ plugin with zero agents produces empty agents array
✓ plugin with only skills works
describe("transformContentForCopilot")
✓ rewrites .claude/ paths to .github/
✓ rewrites ~/.claude/ paths to ~/.copilot/
✓ transforms Task agent calls to skill references
✓ flattens slash commands
✓ transforms @agent references to agent references
File: tests/copilot-writer.test.ts
Test cases (following tests/cursor-writer.test.ts pattern):
describe("writeCopilotBundle")
✓ writes agents, generated skills, copied skills, and MCP config
✓ agents use .agent.md file extension
✓ writes directly into .github output root without double-nesting
✓ handles empty bundles gracefully
✓ writes multiple agents as separate .agent.md files
✓ backs up existing copilot-mcp-config.json before overwriting
✓ creates skill directories with SKILL.md
File: tests/sync-copilot.test.ts
Test cases (following tests/sync-cursor.test.ts pattern):
describe("syncToCopilot")
✓ symlinks skills to .github/skills/
✓ skips skills with invalid names
✓ merges MCP config with existing file
✓ transforms MCP env var names to COPILOT_MCP_ prefix
✓ writes MCP config with restricted permissions (0o600)
Phase 7: Documentation
File: docs/specs/copilot.md
Follow docs/specs/cursor.md format:
- Last verified date
- Primary sources (GitHub Docs URLs)
- Config locations table
- Agents section (
.agent.mdformat, frontmatter fields) - Skills section (
SKILL.mdformat) - MCP section (config structure, env var prefix requirement)
- Character limits (30k agent body)
File: README.md
- Add "copilot" to the list of supported targets
- Add usage example:
compound convert --to copilot ./plugins/compound-engineering - Add sync example:
compound sync copilot
Acceptance Criteria
Converter
- Agents convert to
.agent.mdwithdescription,tools: ["*"],infer: true - Agent
modelpasses through when present - Agent
capabilitiesfold into body as## Capabilities - Missing description generates fallback
- Empty body generates fallback
- Body exceeding 30k chars triggers stderr warning
- Commands convert to SKILL.md format
- Command names flatten (
workflows:plan→plan) - Name collisions deduplicated with
-2,-3suffix - Command
allowed-toolsdropped silently - Skills pass through as
CopilotSkillDir - MCP env vars prefixed with
COPILOT_MCP_ - Already-prefixed env vars not double-prefixed
- MCP servers get
typefield (localorsse) - Hooks trigger warning, skip conversion
- Content transformation: Task calls, slash commands, paths, @agent refs
Writer
- Agents written to
.github/agents/{name}.agent.md - Generated skills written to
.github/skills/{name}/SKILL.md - Skill dirs copied to
.github/skills/{name}/ - MCP config written to
.github/copilot-mcp-config.json - Existing MCP config backed up before overwrite
- No double-nesting when outputRoot is
.github - Empty bundles handled gracefully
CLI Integration
compound convert --to copilotworkscompound sync copilotworks- Copilot registered in
src/targets/index.ts - Sync resolves output to
.github/in current directory
Tests
tests/copilot-converter.test.ts— all converter tests passtests/copilot-writer.test.ts— all writer tests passtests/sync-copilot.test.ts— all sync tests pass
Documentation
docs/specs/copilot.md— format specificationREADME.md— updated with copilot target
Files to Create
| File | Purpose |
|---|---|
src/types/copilot.ts |
Type definitions |
src/converters/claude-to-copilot.ts |
Converter logic |
src/targets/copilot.ts |
Writer logic |
src/sync/copilot.ts |
Sync handler |
tests/copilot-converter.test.ts |
Converter tests |
tests/copilot-writer.test.ts |
Writer tests |
tests/sync-copilot.test.ts |
Sync tests |
docs/specs/copilot.md |
Format specification |
Files to Modify
| File | Change |
|---|---|
src/targets/index.ts |
Register copilot target |
src/commands/sync.ts |
Add copilot to valid targets, output root, switch case |
README.md |
Add copilot to supported targets |
References
- Custom agents configuration - GitHub Docs
- About Agent Skills - GitHub Docs
- MCP and coding agent - GitHub Docs
- Existing converter:
src/converters/claude-to-cursor.ts - Existing writer:
src/targets/cursor.ts - Existing sync:
src/sync/cursor.ts - Existing tests:
tests/cursor-converter.test.ts,tests/cursor-writer.test.ts