(self, plugin: PluginSource, skill: SkillSource, result: EmitResult)
| 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}" |
no test coverage detected