Emit Copilot agent profiles and skills (commands mapped to runnable skills). Agents go to ``.copilot/agents/ __ .agent.md``, skills to ``.copilot/skills/ __ /SKILL.md``. Plugin commands are emitted as runnable skills at ``.copilot/skills/ - /SKIL
| 65 | |
| 66 | |
| 67 | class CopilotAdapter(HarnessAdapter): |
| 68 | """Emit Copilot agent profiles and skills (commands mapped to runnable skills). |
| 69 | |
| 70 | Agents go to ``.copilot/agents/<plugin>__<agent>.agent.md``, skills to |
| 71 | ``.copilot/skills/<plugin>__<skill>/SKILL.md``. Plugin commands are emitted as |
| 72 | runnable skills at ``.copilot/skills/<plugin>-<command>/SKILL.md`` with |
| 73 | ``user-invocable: true`` and ``disable-model-invocation: true`` so they appear |
| 74 | in the VS Code ``/`` menu but are not auto-loaded by the agent. |
| 75 | Tool names are rewritten from Claude Code CamelCase to Copilot lowercase. |
| 76 | Model aliases are mapped to the GPT-5 family (same as Codex CLI). |
| 77 | |
| 78 | Run ``make install-copilot`` to symlink artifacts to ``~/.copilot/`` |
| 79 | for user-level discovery. |
| 80 | """ |
| 81 | |
| 82 | harness_id = "copilot" |
| 83 | |
| 84 | def __init__(self, output_root: Path | None = None, repo_root: Path | None = None) -> None: |
| 85 | """Set output root (defaults to WORKTREE) and optional repo root.""" |
| 86 | super().__init__(output_root=output_root) |
| 87 | if repo_root is not None: |
| 88 | self.repo_root = repo_root |
| 89 | |
| 90 | def emit_plugin(self, plugin: PluginSource) -> EmitResult: |
| 91 | """Emit agent profiles, skills, and command-as-skill files for one plugin.""" |
| 92 | result = EmitResult() |
| 93 | for agent in plugin.agents: |
| 94 | self._emit_agent(plugin, agent, result) |
| 95 | for skill in plugin.skills: |
| 96 | self._emit_skill(plugin, skill, result) |
| 97 | for command in plugin.commands: |
| 98 | self._emit_command_as_skill(plugin, command, result) |
| 99 | # Legacy: also emit as .copilot/commands/ for backward compat |
| 100 | self._emit_command_index(plugin, result) |
| 101 | for command in plugin.commands: |
| 102 | self._emit_command(plugin, command, result) |
| 103 | return result |
| 104 | |
| 105 | def emit_global(self, plugins: list[PluginSource]) -> EmitResult: |
| 106 | """No cross-plugin artifacts needed for Copilot.""" |
| 107 | return EmitResult() |
| 108 | |
| 109 | def _emit_agent(self, plugin: PluginSource, agent: AgentSource, result: EmitResult) -> None: |
| 110 | """Emit one .agent.md profile into the agents/ directory. |
| 111 | |
| 112 | Builds frontmatter (name, description, model, tools), rewrites tool |
| 113 | names, and resolves model aliases before writing. |
| 114 | """ |
| 115 | agent_id = f"{plugin.name}__{agent.name}" |
| 116 | rel = Path(".copilot") / "agents" / f"{agent_id}.agent.md" |
| 117 | |
| 118 | model, warning = resolve_model("copilot", agent.model) |
| 119 | if warning: |
| 120 | result.warnings.append(f"agent `{agent_id}`: {warning}") |
| 121 | |
| 122 | fm: dict = { |
| 123 | "name": agent_id, |
| 124 | "description": agent.description or f"{agent.name} (from {plugin.name})", |
no outgoing calls