首页
/ 从 Codex 移植到 OpenCode 原生插件:superpowers 的跨平台技能架构设计与落地实现

从 Codex 移植到 OpenCode 原生插件:superpowers 的跨平台技能架构设计与落地实现

2026-09-06 18:09:50作者:吴年前Myrtle

导读

本文以 superpowers 仓库中的 OpenCode 支持设计文档 为主体,剖析"如何把一套以技能(Skills)为核心的 Agent 方法论,从仅支持 Claude Code / Codex 移植到 OpenCode.ai"。文档给出了"抽取共享核心模块 + 平台原生插件"的架构决策,读者可从中掌握跨 Coding Agent 平台做技能发现、解析与引导注入(bootstrap)的通用设计方法,并对照仓库当前实现,了解设计落地后的演进与取舍。


一、背景:为什么需要一个"原生 OpenCode 插件"

superpowers 是一个"Agent 技能框架与软件开发方法论"仓库,其核心资产是一组以 SKILL.md 形式组织的技能目录,每个技能通过 YAML frontmatter 声明自己的 namedescription,从而被 Agent 在合适的时机自动发现与调用。它的三个目标平台差异很大:

平台 能力形态 superpowers 接入方式
Claude Code Anthropic 原生插件系统 + 基于文件的技能 原生插件
Codex 无插件系统 引导 Markdown + CLI 脚本
OpenCode.ai JavaScript/TypeScript 插件,提供事件钩子(event hooks)与自定义工具 API 原生 JS/TS 插件

设计文档明确指出,此前为 OpenCode 做的移植尝试(PR #93、PR #116)都采用"文件复制"方式,难以维护。而 OpenCode 具备真正的插件系统,因此本设计(2025-11-22,状态标注为 Design Complete, Awaiting Implementation)的路线是:使用 OpenCode 的原生 JS/TS 插件系统,同时与 Codex 实现共享核心代码

设计文档还梳理了 OpenCode 的 Agent 体系:主 Agent 分为 Build(默认、全权限)与 Plan(受限、只读);子 Agent(Subagents)面向研究、检索与多步任务;子 Agent 既可由主 Agent 自动派发,也可通过 @mention 语法手动召唤;自定义 Agent 可写在 opencode.json~/.config/opencode/agent/ 中。这些能力决定了技能如何在 OpenCode 中被装载和复用。

二、架构设计:共享核心 + 平台包装器

设计文档给出了三层结构:

  1. 共享核心模块 lib/skills-core.js——技能发现(discovery)与解析(parsing)的公共逻辑,Codex 与 OpenCode 两端共用;
  2. 平台专属包装器——Codex 侧为 CLI 脚本 .codex/superpowers-codex,OpenCode 侧为插件模块 .opencode/plugin/superpowers.js
  3. 技能目录——核心技能位于 ~/.config/opencode/superpowers/skills/(或安装位置),个人技能位于 ~/.config/opencode/skills/个人技能通过同名遮蔽(shadowing)覆盖核心技能

2.1 代码复用策略

设计文档的核心诉求是"单一事实来源"(single source of truth)。它计划把 .codex/superpowers-codex 中散落的逻辑抽取为一个 CommonJS 模块:

// lib/skills-core.js
module.exports = {
  extractFrontmatter(filePath),      // Parse name + description from YAML
  findSkillsInDir(dir, maxDepth),    // Recursive SKILL.md discovery
  findAllSkills(dirs),                // Scan multiple directories
  resolveSkillPath(skillName, dirs), // Handle shadowing (personal > core)
  checkForUpdates(repoDir)           // Git fetch/status check
};

五个函数的职责边界非常清晰:

  • extractFrontmatter:读取 SKILL.md,逐行解析 --- 包裹的 YAML 头,仅提取 namedescription 两个字段;
  • findSkillsInDir:递归扫描目录树,对每个含 SKILL.md 的子目录收集技能信息,返回包含 pathnamedescriptionsourceType 的结构化数组;
  • resolveSkillPath:实现遮蔽语义——先查个人目录,除非技能名显式带 superpowers: 前缀强制指向核心目录;
  • checkForUpdates:用 git fetch origin && git status --porcelain=v1 --branch 检查远端是否有新提交,并设置 3 秒超时,网络不可用时静默返回 false,避免阻塞引导流程。

2.2 技能 Frontmatter 格式

设计文档明确了当前仓库的技能头格式——只有 namedescription,没有 when_to_use 字段(抽取核心逻辑时需保持兼容):

---
name: skill-name
description: Use when [condition] - [what it does]; [additional context]
---

这一点可以用仓库中的真实技能验证,例如 using-superpowers 技能brainstorming 技能 的 frontmatter 都遵循 name + description 两字段约定,其中 description 往往用"Use when …"条件句描述触发时机,这是后续 Agent 自动匹配技能的依据。

三、OpenCode 插件实现设计

设计文档用可执行的伪代码/代码草图描述了插件形态,共包含两类自定义工具与一个会话级钩子。

3.1 自定义工具 use_skill

等价于 Claude Code 的 Skill 工具,把指定技能的正文注入当前对话。设计草图使用 zod 做参数校验,skill_name 形如 "superpowers:brainstorming",执行时经共享核心解析路径、剥离 frontmatter 后,按统一模板返回技能标题 + 描述 + 支撑文件目录 + 正文:

{
  name: 'use_skill',
  description: 'Load and read a specific skill to guide your work',
  schema: z.object({
    skill_name: z.string().describe('Name of skill (e.g., "superpowers:brainstorming")')
  }),
  execute: async ({ skill_name }) => {
    const { skillPath, content, frontmatter } = resolveAndReadSkill(skill_name);
    const skillDir = path.dirname(skillPath);

    return `# ${frontmatter.name}
# ${frontmatter.description}
# Supporting tools and docs are in ${skillDir}
# ============================================

${content}`;
  }
}

返回模板刻意附带 skillDir 目录,因为技能目录里往往还躺着配套脚本、附加文档与专用工具——Agent 必须知道去哪里找它们。

3.2 自定义工具 find_skills

列出所有可用技能及其元数据,输出格式为 namespace:name + 描述 + 目录:

{
  name: 'find_skills',
  description: 'List all available skills',
  schema: z.object({}),
  execute: async () => {
    const skills = discoverAllSkills();
    return skills.map(s =>
      `${s.namespace}:${s.name}
  ${s.description}
  Directory: ${s.directory}
`).join('\n');
  }
}

3.3 会话启动钩子(session.started)

设计文档规划了在 session.started 事件触发时执行四步引导:

  1. 注入 using-superpowers 全文——建立"必须先调用技能再作答"的强制性工作流;
  2. 自动执行 find_skills——开场即把全部可用技能列表连同目录展示给 Agent;
  3. 注入工具映射指令——把 superpowers 技能中引用的 Claude Code 风格工具名翻译成 OpenCode 等价物:TodoWriteupdate_planTask(带子 Agent)→ OpenCode 子 Agent 体系(@mention)、Skill 工具 → use_skill 自定义工具、Read/Write/Edit/Bash → OpenCode 原生工具;
  4. 非阻塞更新检查——带超时的快速 git fetch,有新版本时提示用户。

设计文档还给出了插件入口的结构草图,导出形如 SuperpowersPlugin 的工厂函数,从插件对象中读取会话上下文、注册 session.started 钩子与两个自定义工具,路径基址分别指向 ~/.config/opencode/superpowers~/.config/opencode/skills

3.4 目标文件结构

superpowers/
├── lib/
│   └── skills-core.js           # NEW: Shared skill logic
├── .codex/
│   ├── superpowers-codex        # UPDATED: Use skills-core
│   ├── superpowers-bootstrap.md
│   └── INSTALL.md
├── .opencode/
│   ├── plugin/
│   │   └── superpowers.js       # NEW: OpenCode plugin
│   └── INSTALL.md               # NEW: Installation guide
└── skills/                       # Unchanged

四、实施计划:分三阶段推进

设计文档把落地拆成三个渐进阶段,每阶段都带验证关口(这是仓库方法论一贯的"先验证再前进"风格):

  • Phase 1 重构共享核心:创建 lib/skills-core.js,从 .codex/superpowers-codex 中逐个抽出 frontmatter 解析、技能发现与路径解析(含遮蔽),并把格式收敛到仅 name/description;随后改造 .codex/superpowers-codex 改为 require('../lib/skills-core')、删除重复代码;回归验证 bootstrap、use-skill、find-skills 三条命令。
  • Phase 2 构建 OpenCode 插件:创建 .opencode/plugin/superpowers.js,实现自定义工具与 session.started 钩子;编写 .opencode/INSTALL.md;验证会话引导、两个工具与技能目录可达性。
  • Phase 3 文档收尾:更新 README、主文档与 RELEASE-NOTES,双平台并行测试。

配套的 OpenCode 支持实施计划文档 进一步把它细化为 18 个可执行任务,包含每个函数的完整代码、node -c 语法校验命令、逐任务 git commit 规范,以及需要真实 OpenCode 环境的 7 项手动测试清单与成功标准。Next Steps 部分还强调:用 git worktrees 创建隔离工作区(分支 feature/opencode-support)、共享核心函数走 TDD、增量实现、双平台并排回归、最后 PR 合入主分支。

五、设计带来的收益

设计文档结尾归纳了这套"共享核心 + 平台包装"路线的五个收益,可看作方案评审的标准:

  • 代码复用:技能发现与解析只有一份实现;
  • 可维护性:一处修复,双平台受益;
  • 可扩展性:未来接入 Cursor、Windsurf 等新平台只需新增薄包装;
  • 原生集成:充分使用 OpenCode 的插件系统而非文件复制;
  • 体验一致:所有平台上技能使用体验统一。

六、仓库现状对照:设计落地后的真实演进

设计文档标注为 2025-11-22"待实现",但仓库当前快照已包含完成的 OpenCode 插件。把设计与现状对照,可以看到实际实现沿原架构方向落地,但 API 选择上发生了明显演进。以下均以仓库现有文件为据。

6.1 插件入口与钩子的变化

当前插件位于 .opencode/plugins/superpowers.js(注意目录为 plugins 而非设计稿的 plugin),且 package.jsonmain 字段直接指向 .opencode/plugins/superpowers.js,说明 OpenCode 插件包体系已经成为标准安装通道。

插件函数 SuperpowersPlugin 的签名保留了 client/directory,但没有采用设计稿的 session.started,而是注册了两个钩子:

  1. config 钩子:把 path.resolve(__dirname, '../../skills')(即安装包内自带技能目录)写入 config.skills.paths,让 OpenCode 免去 symlink 或手改配置即可自动发现全部 superpowers 技能。注释特别解释:Config.get() 返回缓存单例,这里就地修改会在技能被懒加载发现时生效。
  2. experimental.chat.messages.transform 钩子:负责把 bootstrap 上下文注入每轮会话第一条用户消息之前,而不是以 system 消息注入。注释给出了两条动机:避免每条 system 消息每轮都重复计入 token 开销(#750);多条 system 消息会破坏 Qwen 等模型的解析(#894)。

钩子内部还有防止重复注入的守卫(检测首条用户消息是否已含 EXTREMELY_IMPORTANT 标记),并对注入文本用 <EXTREMELY_IMPORTANT> 块包裹,顶部声明"你已经拥有 superpowers",并提示 using-superpowers 技能"已加载,不要重复加载"。

6.2 自定义工具让位于 OpenCode 原生 skill 工具

设计稿规划的自定义工具 use_skill/find_skills 在当前插件中并未出现——实现演进为直接利用 OpenCode 的原生 skill 工具,因为新版本 OpenCode CLI 本身已具备"列出并加载技能"的能力。这一点在 .opencode/INSTALL.mddocs/README.opencode.md 中得到印证:使用方式简化为 use skill tool to list skillsuse skill tool to load brainstorming。README.opencode 还注明其中的工具映射"已对照已安装 OpenCode CLI 的工具清单验证过"。

相应地,工具映射表从"桥接缺失工具"变成了"动作语义到工具清单的翻译"。技能用动作描述需求(create a todo、dispatch a subagent、read a file),映射如下:

  • 创建/完成待办 → todowrite
  • 派发通用子 Agent → task 工具 + subagent_type: "general"(代码库探索用 "explore"
  • 调用技能 → OpenCode 原生 skill 工具
  • 读写删文件 → read / apply_patch
  • 执行命令 → bash
  • 检索文件内容与文件名 → grepglob
  • 抓取 URL → webfetch

6.3 技能目录与优先级的变化

设计稿假设核心技能装在 ~/.config/opencode/superpowers/skills/;当前实现则把技能与插件一起作为 npm/git 包发布,技能目录按插件文件相对位置 ../../skills 解析(测试环境的标准布局可参考 tests/opencode/setup.sh 的注释:$OPENCODE_CONFIG_DIR/superpowers/skills/ 即包内 ../../skills)。技能优先级规则在真实使用中按 Project > Personal > Superpowers 排列,其中项目技能放在项目内 .opencode/skills/,个人技能放在 ~/.config/opencode/skills/。需要留意的是,设计稿所设想的"同名个人技能遮蔽核心技能"在当前 OpenCode 版本中未必成立——见 6.5 中测试脚本的注释。

6.4 版本引导与启动缓存优化

针对设计稿中"会话启动注入"的需求,实现上额外引入了一个模块级缓存:由于 experimental.chat.messages.transform 在每一步都会触发(OpenCode 的 prompt 组装每步都从数据库重载消息,fresh 的消息数组可能需要重复注入),而 SKILL.md 在会话期间不会变化,插件首次读取并解析一次后即缓存(.opencode/plugins/superpowers.js 注释引用 #1202 的完整分析),缓存未命中与缺失以 undefined/null 区分。配套的 tests/opencode/test-bootstrap-caching.mjs 通过 monkey-patch fs.existsSync/fs.readFileSync 并多次调用 transform 钩子,断言磁盘访问次数不会随 agent 步数线性增长。

6.5 测试体系:用隔离环境验证插件行为

仓库为 OpenCode 支持建立了专门的测试套件(tests/opencode/run-tests.sh 为统一入口,支持 --integration / --verbose / --test 等参数):

  • setup.sh:用 mktemp -d 构建隔离 HOME,铺出 opencode 配置目录 + superpowers 包 + plugins symlink 的标准安装布局,并预置 personal-test / project-test 等测试技能夹具(内含 PERSONAL_SKILL_MARKER_12345 等唯一标记);
  • test-plugin-loading.sh:校验插件 symlink 存在且目标有效、技能目录非空、using-superpowers(bootstrap 关键技能)存在、插件 JS 语法通过 node --check,并回归防止插件继续引用旧的误导性技能路径;
  • test-tools.sh:通过 opencode run --format json 真实驱动 CLI,断言原生 skill 工具能加载 personal、project 与内置 brainstorming 技能,并在输出中匹配 "tool":"skill" 与技能正文标记;
  • test-priority.sh:在 superpowers / personal / project 三处创建同名 priority-test,如实记录当前 OpenCode 对重名技能的实际行为——注意其注释说明,测试脚本并不假装 local shadowing 已经生效,而是"记录现状、保持测试套件诚实",将期望行为单独跟踪。

这些测试文件同时展示了"设计文档中提到的双平台回归、引导内容可见性、技能目录可达性"等验收标准,在真实 OpenCode 环境里如何被机械地检查。

七、安装、使用与排障(基于当前仓库)

结合 .opencode/INSTALL.mddocs/README.opencode.md,当前版本的推荐接入方式如下。

安装:在全局或项目级 opencode.jsonplugin 数组中,以 git 包规范声明 superpowers(即 superpowers@git+<仓库地址> 形式),重启 OpenCode 后由插件管理器完成安装并注册全部技能,可用提问 "Tell me about your superpowers" 验证。若你同时使用 Claude Code / Codex 等其它 harness,需要在每个 harness 中分别安装。

个人技能:在 ~/.config/opencode/skills/<skill-name>/SKILL.md 放置带 frontmatter 的 Markdown 文件即可;项目技能则放在项目内 .opencode/skills/

更新与版本锁定:OpenCode 与 Bun 的部分版本会把 git 依赖 pin 进 lockfile 或缓存,导致重启后拿不到最新提交;如更新不生效,需清理 OpenCode 包缓存或重装插件。需要锁定时可改用分支或 tag 规范,如 superpowers@git+<仓库地址>#v5.0.3

已知坑位(Windows):部分 Windows 版 OpenCode 对 git 源插件规范存在上游安装问题(含 git+https URL 的缓存路径问题、以及 Bun 在正常终端可用却找不到 git.exe 的情况)。规避方式是改用系统 npm 安装到 OpenCode 配置目录,再把 opencode.jsonplugin 指向本地包路径,例如把包装到 ~/.config/opencode/node_modules/superpowers 后声明 "plugin": ["~/.config/opencode/node_modules/superpowers"]

排障要点:插件未加载可执行 opencode run --print-logs "hello" 2>&1 | grep -i superpowers 查看日志并核对 opencode.json 插件声明;技能找不到时先用 skill 工具查看发现结果,并确认每个技能都含合法 YAML frontmatter 的 SKILL.md

结语

回看这份 OpenCode 支持设计文档,它的价值并不止于"让 superpowers 多支持一个平台"。extractFrontmatter → findSkillsInDir → resolveSkillPath → checkForUpdates 这套共享核心的切分、个人技能遮蔽语义、会话启动引导注入与工具映射表的构思,构成了一个"如何让同一套 Agent 技能在不同 harness 上以原生方式复现"的可复用模板。而仓库当前实现中"config 钩子注册技能路径 + messages.transform 注入 bootstrap + 原生 skill 工具 + 模块级缓存防重读"的组合,则是这份设计在真实 OpenCode API 约束下经过验证的最终答案——设计与实现之间的落差,恰好是读者理解跨平台插件移植时最有价值的参考。

登录后查看全文
热门项目推荐
相关项目推荐