MCPcopy
hub / github.com/wshobson/agents / OpenCodeAdapter

Class OpenCodeAdapter

tools/adapters/opencode.py:168–265  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

166
167
168class OpenCodeAdapter(HarnessAdapter):
169 harness_id = "opencode"
170
171 def __init__(self, output_root: Path | None = None) -> None:
172 super().__init__(output_root=output_root)
173 self._seen_skill_ids: dict[str, str] = {}
174
175 def emit_plugin(self, plugin: PluginSource) -> EmitResult:
176 result = EmitResult()
177 for skill in plugin.skills:
178 self._emit_skill(plugin, skill, result)
179 for agent in plugin.agents:
180 self._emit_agent(plugin, agent, result)
181 for cmd in plugin.commands:
182 self._emit_command(plugin, cmd, result)
183 return result
184
185 def emit_global(self, plugins: list[PluginSource]) -> EmitResult:
186 result = EmitResult()
187 # Minimal opencode.json pointing at .opencode/
188 # NOTE: only `$schema` is accepted as an extension key — OpenCode rejects others.
189 config = {
190 "$schema": "https://opencode.ai/config.json",
191 }
192 result.written.append(self.write("opencode.json", json.dumps(config, indent=2) + "\n"))
193 return result
194
195 # ── Internals ──────────────────────────────────────────────────────────
196
197 def _emit_skill(self, plugin: PluginSource, skill: SkillSource, result: EmitResult) -> None:
198 skill_id = _opencode_skill_id(plugin, skill)
199 source_id = f"{plugin.name}/{skill.name}"
200 existing_source = self._seen_skill_ids.get(skill_id)
201 if existing_source and existing_source != source_id:
202 raise ValueError(
203 f"OpenCode skill id collision for `{skill_id}`: {existing_source} and {source_id}"
204 )
205 self._seen_skill_ids[skill_id] = source_id
206
207 skill_dir = Path(".opencode") / "skills" / skill_id
208
209 fm = dict(skill.frontmatter)
210 fm["name"] = skill_id
211
212 body = _rewrite_body_lowercase_tools(skill.body).rstrip() + "\n"
213 content = _opencode_frontmatter(fm) + "\n\n" + body
214 result.written.append(self.write(skill_dir / "SKILL.md", content))
215
216 # Mirror all support files (references/, assets/, scripts/, examples/, etc.)
217 # without decoding so binary assets keep working.
218 for src in sorted(skill.dir.rglob("*")):
219 if not src.is_file() or src.name == "SKILL.md":
220 continue
221 rel = src.relative_to(skill.dir)
222 result.written.append(self.mirror_file(src, skill_dir / rel))
223
224 def _emit_agent(self, plugin: PluginSource, agent: AgentSource, result: EmitResult) -> None:
225 agent_id = f"{plugin.name}__{agent.name}"

Calls

no outgoing calls