三个开源 Agent 项目的跨工具复用对照:运行时扩展、配置资产、插件包

2026-07-29

本文基于三个仓库的以下版本梳理:pi commit 027a584、ECC commit 591ab5c、superpowers commit 44c9b2d(均为 2026-07 下旬)。三个项目都在持续迭代,具体行为以各自仓库最新代码与文档为准。

跨工具复用 Agent 能力,难的不是把内容写一遍,而是你决定把「变化」放在哪一层。 放进运行时,能力就是代码,拦得住、改得动,但只在那一个工具里成立;放进共享资产,能力可以搬走,但每个工具的执行力度不一样;放进各平台的插件包,内容能一字不改地跑遍所有平台,代价是每接一个新平台都要重做一遍集成。pi、ECC、superpowers 这三个 MIT 许可的开源项目,恰好各走了一条,而且都把自己的取舍写进了文档。

一、三条路各自在解决什么问题

你在某个工具里花两周调出一套顺手的流程:某几个命令要先确认再执行、写死的项目约定要每次带上、一个内部检查脚本要在每轮工具调用后跑一遍。然后团队换工具,或者你自己想在另一个 CLI 里干同样的活,这套东西一点都搬不过去。

三个项目对这件事的答案不同。pi(https://github.com/earendil-works/pi )提供的是一套运行时扩展 API,你写 TypeScript 模块挂到事件上。ECC(https://github.com/affaan-m/ECC )把技能、规则、hook、MCP 配置这些当成共享源放在一个仓库里,各个执行工具在边缘做适配。superpowers(https://github.com/obra/superpowers )把技能正文当成不可改的真源,为每个平台单独写一层引导与工具名映射。

站内的 Agent 协议与生态对照MCP 与扩展机制的框架关系 讲的是通用方法论层面该怎么分层、什么时候该用协议什么时候该用扩展;本篇不重复那些结论,只看这三个具体项目是怎么把它落到仓库文件里的——目录叫什么、事件叫什么、哪一步会静默失败。

对照维度piECCsuperpowers依据文件
复用的最小单位一个 TypeScript 扩展模块一份 SKILL.md(配 rules/hooks/mcp-configs/skills/ 目录下的技能正文,逐字共享三方各自的架构文档
能力载体代码:事件订阅、自定义工具、命令文本资产:指令、约束、工作流形状文本资产 + 每平台一个引导注入器packages/coding-agent/docs/extensions.mddocs/architecture/cross-harness.mddocs/porting-to-a-new-harness.md
跨到别的工具怎么办文档不承诺跨工具,扩展是 pi 运行时内的能力各执行工具写薄适配层,只适配加载方式、事件形状、命令名每个平台一个 manifest + 一个引导机制,技能正文不动同上
触发方式订阅生命周期事件,如 session_starttool_callcontext由各工具自己的加载约定决定,hook 支持程度不一会话开始强制注入 using-superpowers 全文,包在 <EXTREMELY_IMPORTANT> 标签里同上
记忆/状态pi.appendEntry() 做会话级持久化Memory Vault 三个 scope,走 ecc memory CLI 或可选的 MCP 服务文档未把记忆纳入移植范围docs/architecture/cross-harness.md
分发自动发现目录,或 settings.jsonpackages / extensions 字段install.sh --profile minimal --target <harness> 等安装入口各平台自己的安装机制:marketplace、git URL、package.json 字段docs/architecture/harness-adapter-compliance.mddocs/porting-to-a-new-harness.md

二、pi:能力就是运行时里的一段代码

pi 的扩展是 TypeScript 模块,默认导出一个工厂函数,参数是 ExtensionAPI。扩展通过 jiti 加载,所以 TypeScript 不需要预先编译。放置位置决定它能不能被自动发现和热重载:~/.pi/agent/extensions/*.ts 与同目录下的 */index.ts 是全局范围,.pi/extensions/*.ts*/index.ts 是项目范围;pi -e ./path.ts 只适合临时试跑,文档明确说自动发现位置的扩展才能用 /reload 热重载。另外可以在 settings.json 里用 packages 声明 npm 或 git 来源的 pi 包,用 extensions 指向额外的本地路径。

真正撑起这条路的是事件面。pi 文档里给了一张完整的生命周期图,从 project_trustsession_startresources_discover 一路到 before_agent_startturn_startcontextbefore_provider_requesttool_calltool_resultturn_endagent_endagent_settled。其中几个是有实权的:tool_call 可以返回 { block: true, reason } 直接拦下这次工具调用,而且 event.input 是可变的,你原地改写参数就会影响真正执行的那次调用——文档同时提醒,改完之后不会重新做 schema 校验。tool_result 像中间件一样串起来,后一个处理器看到的是前一个改过的结果,返回部分字段就是局部打补丁。context 事件拿到的是消息数组的深拷贝,改完返回即可。

注册面同样是代码:pi.registerTool() 注册模型可调用的工具,pi.registerCommand() 注册 /mycommand 这类命令,还有 pi.registerShortcut()pi.registerFlag()pi.registerProvider()registerTool 在启动后也能调用,新工具当场生效,不必 /reload。技能、提示词、主题的搜索路径则通过 resources_discover 事件返回 skillPathspromptPathsthemePaths 三个数组来贡献。

这条路对你意味着什么:你能做的事情上限很高——权限闸门、路径保护、自定义压缩策略、把 ! 执行的 bash 换成 SSH 后端(user_bash 事件支持返回自定义 operations)。代价是这套 API 名字、事件名、返回约定全是 pi 自己的,换个工具就得重写。文档在扩展位置那一节直接挂了安全提示:扩展以你的完整系统权限运行、能执行任意代码,只装可信来源;项目级的 .pi/extensions 要等项目被信任之后才加载。这跟 最小权限设计 的思路是一致的——能力越强的插口,装之前越要看清来源。

三、ECC:durable 的部分放共享源,harness 只做边缘适配

ECC 的架构文档开头一句就是它的全部立场:ECC 是可复用的工作流层,各个 harness 是执行面。它想把技能、规则与指令、hook、MCP 配置、安装清单、会话与编排模式、以及与 harness 无关的记忆文档,全部留在一个仓库里。

文档给了一张 Portability Model 表,逐行写清每类资产的共享源、适配方式和当前状态。技能的共享源是 skills/*/SKILL.md,适配形态包括 Claude 插件、Codex 插件、.agents/skills、Cursor 的技能副本、OpenCode 的插件/配置。规则与指令的共享源是 rules/AGENTS.md 和翻译过的文档,状态标注是「支持,但各 harness 并不完全一致」。Hook 的共享源是 hooks/hooks.jsonscripts/hooks/,状态写得更直白:在 Claude、OpenCode、Cursor 上是 hook 背书的,在 Codex 上是指令背书的。会话那一行状态是 Alpha。

最可移植的单元是 SKILL.md。文档给了一份好技能的判据:YAML frontmatter 带 namedescriptionorigin;写清什么时候该用;说明需要哪些工具或连接器但不嵌入密钥;示例保持仓库相对路径或通用写法;除非那一节明确标注,否则不要假设某个 harness 独有的命令。

记忆这块是 ECC 特有的一块。Memory Vault 存的是可移植的 ecc.memory.v1 Markdown 文档,分三个 scope:project 在 <repo>/.ecc/memory/project/,team 在 <repo>/.ecc/memory/team/,user 在 ~/.ecc/memory/。基线接口是确定性的 ecc memory CLI;支持 MCP 的 harness 可以改用 ecc-memory-mcp,工具是 memory_savememory_searchmemory_readmemory_doctor。这个 MCP 服务是 opt-in 的,参考条目放在 mcp-configs/mcp-servers.json,故意不放进默认的 .mcp.json,理由文档写了两条:避免安装后静默多出一个可写的上下文面,以及不必为它的工具 schema 付出成本。信任边界也写死了:首个版本的所有条目都是 create-only 且状态永远是 unreviewed;召回的记忆是数据,不是可执行指令;疑似密钥的写入会被尽力拦掉,读取不跟随符号链接;如果 vault 的保护性 .gitignore 被改动,project scope 的写入就停。关于记忆该怎么分层,可以对照 Agent 记忆分层 一起看。

ECC 还有一条很实用的判据:如果一个改动需要你去编辑三份 harness 副本里的同一段工作流,那说明共享源放错了地方——把工作流放回 skills/,只在边缘适配加载、事件形状、命令名映射和平台限制。配套的 Harness Adapter Compliance Matrix 把状态分成四档:Native、Adapter-backed、Instruction-backed、Reference-only。这张矩阵由 scripts/lib/harness-adapter-compliance.js 渲染,并由 npm run harness:adapters -- --check 校验,避免文档和数据漂移。Reference-only 这一档的含义要看清楚:它表示这个工具在设计上有参考价值,但 ECC 今天没有为它提供安装器或直接适配。

四、superpowers:内容一字不改,每个平台补一层引导

superpowers 把跨平台拆成三个组件。第一是技能,skills/ 是唯一真源,所有平台逐字共享;技能正文只描述动作——「调用一个技能」「读一个文件」「派发一个 subagent」「建一条 todo」——从不写具体工具名,这正是同一份正文能在多个平台跑的原因。第二是工具映射,把动作词汇翻译成该平台真实的工具名,放在 skills/using-superpowers/references/<harness>-tools.md,或内联在引导注入器里。第三是引导:每次会话开始,把 skills/using-superpowers/SKILL.md 全文注入模型上下文,包在 <EXTREMELY_IMPORTANT> 标签里并附上工具映射。文档对第三点的措辞很重:引导就是整个集成本身,没有它,技能文件躺在磁盘上但永远不会被调用。

由此推出两条硬规则。其一,不许为了适配某个平台去改技能正文——移植只加工具映射和引导注入器,不伸手进 skills/*/SKILL.md 换工具名。其二,一切都通过该平台自己的安装机制交付,绝不去改用户的文件;不许往用户的全局配置里塞东西,哪怕是 ~/.gemini/config/AGENTS.md 这种看起来「就是给你写的」的地方也不行。

接下来是三种集成形状,按引导怎么送到模型面前来分。Shape A 是 shell hook:平台在会话开始跑一个命令并读它的 stdout,参考实现是 hooks/session-start 和 polyglot 包装 hooks/run-hook.cmd。Shape B 是进程内插件:平台加载一个 JS/TS 模块,你在生命周期回调里改消息数组,参考实现是 .opencode/plugins/superpowers.js.pi/extensions/superpowers.ts。Shape C 是指令文件:平台既没有 shell hook 也没有代码插件,只会加载一个由你安装的扩展自己携带、并由 manifest 声明的上下文文件,参考实现是 gemini-extension.jsonGEMINI.md

Shape A 最容易翻车的地方是 JSON 输出形状。同一个脚本要按环境变量分支打印三种不同结构:Cursor 是 { "additional_context": … },Claude Code 是 { "hookSpecificOutput": { "hookEventName": "SessionStart", "additionalContext": … } },Copilot CLI 与 SDK 标准是 { "additionalContext": … }。文档点名这是个陷阱:字段写错就静默不注入,而 Claude Code 会同时读 additional_contexthookSpecificOutput 且不去重,两个都发就变成双重注入。event matcher 字符串也不一样,Claude Code 用 startup|clear|compact,Cursor 用 sessionStart,写错了 hook 就默默不触发。

Shape B 有三件必须复刻的事:注入成 user 角色消息而不是 system 消息(文档给了两个 issue 编号作为理由,#750 是每轮重复的 system 消息会撑爆 token,#894 是多条 system 消息会破坏部分模型);要有去重守卫,因为回调可能反复触发;如果平台会压缩历史,压缩之后要重新注入。而消息对象的形状每个平台都不同——pi 用 { role, content: [{ type, text }], timestamp },OpenCode 操作的是 message.info.rolemessage.parts[],照抄另一个平台的对象字面量会静默失败。

这里有个很值得看的交叉点:superpowers 对 pi 的集成,用的正是第二节讲的那套 pi 扩展 API。.pi/extensions/superpowers.ts 里是这样注册技能目录的:

pi.on("resources_discover", async () => ({
	skillPaths: [skillsDir],
}));

引导注入则挂在 context 事件上,靠一个 injectBootstrap 标志控制时机——session_startsession_compact 时置真,agent_end 时置假,并且在插入前检查消息里是否已经存在引导标记。也就是说,第一条路(运行时扩展)提供插口,第三条路(各平台插件包)消费插口,两者不是互斥关系——这里的「第一条」「第三条」指本文开头那三条复用路线,跟上一段 superpowers 自己的 Shape A/B/C 不是一回事。

验收这块 superpowers 定得很死。移植完成的判据里有一条实测:在干净会话里发出「Let’s make a react todo list」这句话,brainstorming 技能必须在写任何代码之前自动触发,并且要把完整 transcript 贴进 PR,没有这份证据的 PR 会被关掉。更前置的一条硬门槛是:如果唯一能让内容进入模型上下文的办法是每次会话让人手动 opt-in(粘一段提示词、跑一个命令、开一个模式),那这个平台就没法被正经支持。

五、边界与代价

三条路各自放弃了什么,文档里都写着,值得逐条抄下来。

pi 这条路放弃的是可移植性。扩展 API 是 pi 自己的,文档从头到尾没有承诺跨工具复用。它还有几个具体的行为边界:默认的并行工具执行模式下,tool_call 不保证在 ctx.sessionManager 里看到同一条 assistant 消息里其它兄弟工具的结果;会话被替换(/new/resume/fork)之后,你之前捕获的旧 pi、旧命令 ctx、旧 SessionManager 对象都会失效,继续用会抛错;await ctx.reload() 之后,当前这个处理器仍然跑在旧版本的调用帧里,文档建议把 reload 当成该处理器的终点。分发的坑也有:以 pi 包安装时默认走 npm install --omit=devdevDependencies 在运行时不可用。文档还专门交代,不要在工厂函数里启动进程、socket、文件监视器或定时器,因为工厂可能在一个根本不会开会话的调用里执行,后台资源要推迟到 session_start 再起,并注册幂等的 session_shutdown 收尾。

ECC 这条路放弃的是执行力度的一致性。它自己列的「仍在成熟中」的清单包括:各 harness 之间精确的 hook 对等、Hermes 的自动技能同步、ecc2/ 的发布打包、跨 harness 的会话恢复语义。Codex 那一行状态是 instruction-backed,意思就是 hook 逻辑对 Codex 而言只是策略文本,不是运行时强制。记忆那块也有明确的不管:所有首发条目永远是 unreviewed,人的接受动作把知识提升为受治理的仓库产物,但不会把记忆的 frontmatter 变成自我认证的批准;活跃的执行状态应该留在 GitHub 或 Linear 里,不要只存在记忆中。Hermes 那一节列了明确的不许 ship 清单:OAuth token 和 API key、~/.hermes 原始导出、个人工作区记忆、私有数据集、未经评审的本地自动化包。

superpowers 这条路放弃的是灵活性和轻量。技能正文不许改,哪怕改了能让某个平台跑得更顺;用户配置不许碰,如果安装机制真的带不动引导,那就把它作为限制上报,而不是当成动手改用户文件的许可。项目是零依赖插件,加平台是贡献规则里唯一的豁免口,即便如此也只能加集成严格必需的东西,能编译掉的类型导入可以,运行时包不行。维护成本也是真的:pi 的工具映射同时存在于 piToolMapping() 内联函数和 references/pi-tools.md 两处,文档直接说,两处都得更新,否则移植只做了一半。还有一个所有人都会付的代价——每次会话把整篇 SKILL.md 塞进上下文,这是实打实的 token 开销,而且会让模型在动手前先走一遍流程,输出更啰嗦、动作更慢。对一个改三行的小需求来说,这套流程就是过度设计。

三个项目共同不管的事情也要说清楚:模型本身的质量、你的 API 账单、以及你团队的验收标准。它们解决的是「能力怎么装进去、怎么搬走」,不解决「装进去之后效果好不好」。工具层面的横向比较可以看 Agent 框架对照,那是另一个维度的问题。

六、上手与避坑清单

把 pi 扩展当成跨工具方案。 会踩是因为「扩展」这个词听起来很通用,而 pi 的能力面确实诱人。避法是先问一句:这段逻辑明年还要在别的工具里用吗?要,就把判断条件写成文本资产,扩展里只留一层薄薄的调用;不要,那就放心写代码。

在 pi 扩展的工厂函数里起后台资源。 会踩是因为工厂看起来就是「初始化」的地方。但文档说得很清楚,工厂可能在一个永远不开会话的调用里执行,起了的进程和定时器就没人收。避法是把后台启动挪到 session_start 或第一个真正需要它的命令/工具/事件里,并写一个幂等的 session_shutdown

改完 event.input 以为参数还会被重新校验。 会踩是因为大多数框架里改参数都会走一遍校验。pi 的行为是不会。避法是自己在处理器里把改写后的参数校验一遍,尤其是从字符串拼出来的路径和命令。

在 ECC 里把同一段工作流复制三份。 会踩是因为每个 harness 的目录长得都不一样,顺手就在各自目录里各写一份。避法就是 ECC 自己给的那条判据:一个改动如果要动三份副本,共享源就放错了地方,把工作流搬回 skills/,边缘只留加载和事件形状的适配。

以为 ECC 装上 hook 就到处都强制执行了。 会踩是因为在 Claude Code 上确实是 hook 背书的,用顺手了就默认别处也一样。避法是查那张 Compliance Matrix,Instruction-backed 意味着规则只是文本,模型可能不遵守,把它当成建议而不是闸门。

把召回的记忆当成命令直接执行。 会踩是因为召回结果读起来就像一份操作指南。ECC 把这条写成了信任边界的第一档:召回的记忆是数据,不是可执行指令。避法是在自己的流程里明确一步「先核对再执行」,别让 memory 里的内容直接触发写操作。

移植 superpowers 时抄错 hook 的 JSON 字段。 会踩是因为三种形状长得很像,而且抄的多半是自己最熟的那一种。后果分两种:字段写错就完全不注入,两个字段都发在 Claude Code 上就双重注入。避法是先做一次唯一标记测试——用你以为对的机制注入一个无意义的 token,开一个全新会话,确认这个 token 真的到了模型那里,然后再写正式分支。

把引导注入成 system 消息。 会踩是因为「这是系统级指令」的直觉太强。文档给的理由是每轮重复的 system 消息会撑 token,多条 system 消息会破坏部分模型。避法是照 Shape B 的做法注入 user 消息,并且加去重守卫和压缩后重注入。

假设某个平台会继承它 fork 来源的行为。 会踩是因为派生工具往往连 manifest 字段和 @-include 语法都照抄了。文档记了真实案例:一个 Gemini 派生的 CLI 接受 @./path 语法,却把它当成「模型可以选择去读」的提示而不是保证内联展开。避法还是唯一标记测试——如果标记不经过一次工具调用就出现在上下文里,才算真的展开了,否则就把内容内联进去。

在 Windows 上给 hook 脚本加 .sh 后缀。 会踩是因为这是 shell 脚本的常规命名。但 Claude Code 的 Windows 处理会给包含 .sh 的命令前面加 bash,结果是双重调用。避法是 hook 脚本一律不带扩展名,交给 polyglot 包装去分发。

用「让人每次手动粘一段提示词」凑合过关。 会踩是因为它确实能演示成功。但 superpowers 把「不需要每次会话手动 opt-in」列为唯一不可谈判的能力要求,靠手动触发的移植会在验收测试上直接失败。避法是先确认平台有没有会话开始的自动注入面,没有就先别写代码。

收尾

一句话自检:你要复用的这段能力,如果明年换工具,你是打算重写代码、还是打算重写适配层、还是打算重写引导包?答案决定你该走哪条路,而这三个仓库把三条路的账都算给你看了。

接下来读哪个文件,取决于你卡在哪一步。想给某个工具加拦截和自定义工具,读 pi 的 packages/coding-agent/docs/extensions.md,特别是事件那一章和最后关于会话替换的踩坑说明。想让一套工作流在多个工具间共存,读 ECC 的 docs/architecture/cross-harness.md,再顺手翻同目录的 harness-adapter-compliance.md,那张矩阵会告诉你每个工具今天到底是什么状态。想把一套技能推到一个新平台上,读 superpowers 的 docs/porting-to-a-new-harness.md,从 Part 2 的能力清单开始,先确认那条硬门槛能不能过,再决定要不要动手。这三份文档都不长,而且都诚实地写了自己还没做到的部分——这一点比任何架构图都有参考价值。

文中三个项目各有中文专题:pi 开源编程 AgentECC 增强套件superpowers 方法论

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。