首页
/ Superpowers 的 Pi 扩展实现指南:Pi 包清单、会话引导注入机制与 Drill 评估后端

Superpowers 的 Pi 扩展实现指南:Pi 包清单、会话引导注入机制与 Drill 评估后端

2026-09-06 12:36:57作者:邬祺芯Juliet

本文以 Superpowers 仓库中 Pi Extension and Evals Implementation Plan 这份实施计划为主体,完整拆解“为 Pi 编程 Agent 提供一等包支持 + 将 Pi 接入 Drill 评估后端”这一需求的设计与落地方式。读完本文,你将理解 Pi 包清单如何声明、.pi/extensions/superpowers.ts 如何在会话启动与压缩(compaction)后注入引导上下文、Pi 工具映射参考文档的约定,以及 Drill 评估框架中 Pi 后端的命令、会话日志过滤与归一化设计。

计划总览:目标、架构与技术栈

该计划是一份典型的“为 Agent 执行者编写”的 TDD 实施计划(文档头部明确要求使用 superpowers:subagent-driven-developmentsuperpowers:executing-plans 技能逐任务执行,步骤用 - [ ] 复选框跟踪)。其核心声明为:

  • Goal(目标):为 Superpowers 增加一等公民的 Pi 包支持,并将 Pi 加入 Drill 评估(eval)后端。
  • Architecture(架构):Pi 包在根目录 package.json 中声明,加载现有的 skills/ 目录加一个小型 Pi 扩展。该扩展在会话启动和压缩之后,将 using-superpowers 引导内容(bootstrap)以 user 角色消息注入 provider 上下文,并附带 Pi 专属的工具映射。Drill 侧则新增 pi 后端、Pi 会话日志归一化,以及对应测试。
  • Tech Stack(技术栈):Pi 的 TypeScript 扩展 API、Node 内置测试运行器(node:test)、Drill 的 Python 评估框架(pytest)。

计划分为四个任务:任务 1(Pi 包清单与扩展测试)、任务 2(Pi 工具映射参考文档)、任务 3(Drill Pi 后端与会话日志归一化)、任务 4(文档与全量验证)。下面逐任务展开,并结合仓库当前实际代码与测试说明实现细节。

任务 1:Pi 包清单与扩展测试

涉及文件

  • 修改:package.json
  • 新建:tests/pi/test-pi-extension.mjs

1.1 清单(Manifest)字段设计

计划要求更新根目录 package.json,在保留既有 nameversiontypemain 字段的前提下,新增 descriptionkeywordspi.extensionspi.skills。仓库当前 package.json 的实际内容印证了这一设计:

{
  "name": "superpowers",
  "version": "6.2.0",
  "description": "Superpowers skills and runtime bootstrap for coding agents",
  "type": "module",
  "main": ".opencode/plugins/superpowers.js",
  "keywords": [
    "pi-package",
    "skills",
    "tdd",
    "debugging",
    "collaboration",
    "workflow"
  ],
  "pi": {
    "extensions": [
      "./.pi/extensions/superpowers.ts"
    ],
    "skills": [
      "./skills"
    ]
  }
}

这里有三个值得注意的实现细节:

  1. keywords 中的 pi-package:这是 Pi 生态识别“可安装包”的约定标记,测试会显式断言它存在。
  2. pi.skills: ["./skills"]:直接复用仓库既有技能目录,Pi 拥有原生 skill 系统,因此无需任何兼容层 Skill 工具——这是计划架构里“no compatibility Skill tool is required”的落点。
  3. 路径演进:计划文本中最初写的是 pi.extensions: ["./extensions/superpowers.ts"],而当前代码是 ./.pi/extensions/superpowers.ts。从 git 历史(提交 chore: keep pi extension under .pi)可以看到扩展最终被移入 .pi/ 目录,使 Pi 专属资源与其他 harness 的插件目录(如 .opencode/.codex-plugin/)保持对称的目录布局。这类“计划路径 vs 落地路径”的偏差在实施计划中很常见,本文后文引用源码时均以当前仓库状态为准。

1.2 扩展实现解析:.pi/extensions/superpowers.ts

计划对扩展的行为规格(Step 4)是:

  • import.meta.url 定位包根目录;
  • 读取 skills/using-superpowers/SKILL.md
  • 剥离 YAML frontmatter;
  • 追加 Pi 专属工具映射;
  • 通过 resources_discover 暴露 skills 路径;
  • session_startsession_compact 时标记“bootstrap 待注入”;
  • context 钩子中注入 user 角色 bootstrap 消息,且插入位置在开头的 compactionSummary 消息之后;
  • agent_end 时清除待注入标记;
  • 不注册 session_before_compact(引导只在压缩“之后”生效,而不是在压缩前塞入上下文)。

当前 .pi/extensions/superpowers.ts 是一个零运行时依赖(仅用 node:fsnode:pathnode:url 内置模块)的 TypeScript 文件,逐项实现了上述规格。核心机制如下:

(1)生命周期状态机。 扩展用一个闭包内的布尔标志 injectBootstrap 控制注入开关:

let injectBootstrap = true;

pi.on("session_start", async () => { injectBootstrap = true; });
pi.on("session_compact", async () => { injectBootstrap = true; });
pi.on("agent_end", async () => { injectBootstrap = false; });

即“会话启动”与“发生压缩”都会重新开启注入,“一轮 Agent 执行结束”则关闭。

(2)去重与缓存。 bootstrap 内容通过模块级缓存 cachedBootstrap(三态:undefined 未加载、null 加载失败、字符串成功内容)只读一次文件;每次构造时包含一个固定标记 superpowers:using-superpowers bootstrap for piBOOTSTRAP_MARKER)。context 钩子在处理前先检查现有消息中是否已含该标记(messageContainsBootstrap 同时兼容字符串与 content-parts 两种消息形态),存在则直接返回 undefined 表示不修改上下文,从而保证重复的 provider 请求不会注入两份 bootstrap。

(3)压缩摘要之后的插入位置。 这是本扩展最有讲究的一点。压缩发生后,会话头部会出现若干 role: "compactionSummary" 的消息;bootstrap 必须插在这些摘要之后、其余消息之前,这样引导内容在时间线上晚于历史总结、早于用户最新输入:

const insertAt = firstNonCompactionSummaryIndex(event.messages);
return {
  messages: [
    ...event.messages.slice(0, insertAt),
    bootstrapMessage,
    ...event.messages.slice(insertAt),
  ],
};

其中 firstNonCompactionSummaryIndex 从索引 0 起跳过所有连续的 compactionSummary 消息。

(4)Bootstrap 内容的组装。 getBootstrapContent() 读取 skills/using-superpowers/SKILL.md,用正则 /^---\n[\s\S]*?\n---\n([\s\S]*)$/ 剥掉 frontmatter 后,包裹成如下结构:

<EXTREMELY_IMPORTANT>
superpowers:using-superpowers bootstrap for pi

You have superpowers.

The using-superpowers skill content is included below and is already loaded for this Pi session. Follow it now. Do not try to load using-superpowers again.

{SKILL.md 正文}

## Pi tool mapping
{内联的 Pi 工具映射文本}
</EXTREMELY_IMPORTANT>

值得注意的一个设计区分是:扩展内联了一份工具映射文本(piToolMapping() 函数,见 superpowers.ts 第 88–98 行),而不是在运行时读取任务 2 的参考文档。参考文档 pi-tools.md 是给 Agent 按需查阅的“规范文档”,内联文本是保证每轮上下文都携带的“硬约束”,两者内容互为镜像。

内联映射的核心内容与 skills/using-superpowers/references/pi-tools.md 一致:Pi 有原生 skills 但没有 Claude Code 的 Skill 工具;内建编码工具是小写的 readwriteeditbash,外加可选的 grepfindls;Pi 核心不带标准子代理(subagent)工具,若安装了 pi-subagents 提供 subagent 工具则使用它,否则不要虚构 Task 调用;Pi 核心也不带标准任务列表工具,旧的 TodoWrite 应视为“任务跟踪动作”,可用计划文件或仓库本地 TODO.md 跟踪。

(5)资源发现。 resources_discover 钩子返回 skillPaths: [skillsDir],把整个 skills/ 目录贡献给 Pi 的资源系统。

1.3 测试实现:tests/pi/test-pi-extension.mjs

计划 Step 1 要求先写“失败测试”:导入扩展、注册一组假的 Pi handler、断言清单与行为。当前 tests/pi/test-pi-extension.mjs 的写法是:

  • 构造一个假的 pi 对象,其 on(event, handler) 把 handler 存入 Map<event, handler[]>
  • 通过 import(pathToFileURL(extensionPath).href + '?cachebust=...') 动态导入 .pi/extensions/superpowers.ts,调用默认导出完成注册;
  • node --experimental-strip-types 让 Node 直接执行 TS 扩展文件,无需编译步骤。

六组测试分别锁定计划的每一条断言:

  1. 清单断言name === 'superpowers'keywordspi-packagepi.skills 深等于 ['./skills']pi.extensions 深等于 ['./.pi/extensions/superpowers.ts']
  2. 生命周期断言resources_discoversession_startsession_compactcontextagent_end 各恰好注册 1 个 handler,且 session_before_compact 注册数为 0;
  3. 资源发现断言resources_discover 返回的 skillPaths 恰为仓库 skills/ 的绝对路径;
  4. 启动注入断言session_start 后调用 context,结果首条消息为 user 角色、文本匹配 /You have superpowers//Pi tool mapping/,原用户消息原样保留;重复请求不重复注入;bootstrap 已在消息中时返回 undefinedagent_end 之后待注入被清除;
  5. 压缩注入断言session_compact 后,context 将 bootstrap 插入到 compactionSummary 消息之后、用户消息之前(结果消息数为 3,顺序为 summary → bootstrap → user);
  6. 参考文档断言pi-tools.md 存在,且仅对映射表格行| 开头的行)断言包含 subagent 与 todo/task 条目——测试注释明确说明这是为了防止“删掉表格只留下正文描述”造成的假通过。

运行方式(计划中 RED/GREEN 两步均使用同一条命令):

node --experimental-strip-types --test tests/pi/test-pi-extension.mjs

预期在实现前 FAIL(扩展与 pi 清单不存在),实现后 PASS。

任务 2:Pi 工具映射参考文档

涉及文件

  • 新建:skills/using-superpowers/references/pi-tools.md
  • 修改:tests/pi/test-pi-extension.mjs

计划的规格是:文档需覆盖 SkillTaskTodoWrite 与内建工具名的映射,并说明 Pi 的原生 skills、可选的 pi-subagents、以及“没有标准 todo/tasklist 插件”的事实。当前 skills/using-superpowers/references/pi-tools.md 的内容结构如下(此处完整给出):

  • 开头点明定位:技能以“动作”表述(“dispatch a subagent”“create a todo”“read a file”),在 Pi 上解析为下表的工具;
  • 映射表
技能请求的动作 Pi 等价物
派发子代理(Subagent (general-purpose): 模板) 若可用,使用已安装子代理工具,如 pi-subagents 提供的 subagent
任务跟踪(“create a todo”“mark complete”) 若可用,使用已安装的 todo/task 工具,否则在计划或 TODO.md 中跟踪任务
  • Subagents 一节:Pi 核心不附带标准子代理工具;pi-subagents 是强推荐的可选伴侣包,提供带单代理、链式、并行、异步、forked-context、resume/status 工作流的 subagent 工具;若无子代理工具,不要虚构 Task 调用,应在当前会话顺序执行或向用户说明该可选能力未安装。
  • Task lists 一节:Pi 核心不附带标准任务列表工具;若安装了 todo/task 扩展则用其文档工具,否则用 Superpowers 计划文件、Markdown 复选框或仓库本地 TODO.md;旧版 Superpowers 文档中的 TodoWrite 应视为上表的任务跟踪动作。

测试侧(任务 1 的第 6 组断言)对这份文档做了防退化校验:只解析表格行,要求至少一行匹配 /subagent/i、至少一行匹配 /todo|task/i。这体现了计划“用测试钉住文档契约”的一贯思路——参考文档不是散文,而是有可测试形态的规范。

任务 3:Drill Pi 评估后端与会话日志归一化

涉及文件

  • 新建:evals/backends/pi.yaml
  • 修改:evals/drill/backend.pyevals/drill/engine.pyevals/drill/normalizer.py
  • 修改:evals/tests/test_backend.pyevals/tests/test_normalizer.py

这一任务把 Pi 接入 Drill 评估框架。计划给出的测试规格(Step 1)明确了五个可验证点:

  1. load_backend("pi") 返回 family == "pi"
  2. Pi 后端的启动命令以 pi 开头,且包含 -e ${SUPERPOWERS_ROOT}
  3. _resolve_log_dir() 对 Pi 指向 ~/.pi/agent/sessions
  4. filter_pi_logs_by_cwd() 只保留头部 cwd 与场景工作目录一致的会话文件;
  5. normalize_pi_logs() 从 Pi 助手(assistant)会话条目中提取 toolCall 块,并把小写内建工具名映射为规范(canonical)工具名。

据此,后端配置 evals/backends/pi.yaml 的设计是:

  • 命令pi -e ${SUPERPOWERS_ROOT}——与 README 中“本地开发时以临时包方式加载本检出”的用法(pi -e /path/to/superpowers)同源,-e 参数把 Superpowers 仓库作为临时扩展包挂载进 Pi;
  • 就绪判定:宽松(permissive)的 TUI readiness 策略,容忍 TUI 界面启动时序;
  • 关停:发送 /quit 退出 Pi 会话;
  • 日志位置:Pi 会话日志目录(~/.pi/agent/sessions)。

引擎与归一化层的实现规格(Step 4):

  • Backend.family 增加 "pi" 家族;
  • Engine._resolve_log_dir 针对 Pi 返回 ~/.pi/agent/sessions
  • Engine._collect_tool_calls 适配 Pi 的日志形态;
  • normalizer.py 实现两个 Pi 专属函数:filter_pi_logs_by_cwd(按会话头部的 cwd 字段过滤,保证评估场景只采集自己工作目录的会话)与 normalize_pi_logs(解析 assistant 条目中的 toolCall 块,把 read/write/edit/bash 等小写内建工具名归一为 Drill 统一的规范名,供后续断言使用)。

对应测试用 pytest 运行:

uv run pytest evals/tests/test_backend.py evals/tests/test_normalizer.py -q

需要说明当前仓库状态的一个事实:evals/ 目录不在当前仓库快照中。从 git 历史可以确认,评估框架曾通过提交 Move eval harness to submodule (#1541) 移出主仓库成为独立子模块,Pi 后端相关工作以 Bump evals submodule for Pi backend 的提交形式在子模块中推进,后续版本(v6.0.2 起)不再随主仓库分发 evals 子模块。因此本文对任务 3 的描述以本计划文档为设计权威,evals/ 下的具体文件请以其所在评估仓库的实际检出为准。

任务 4:文档与全量验证

涉及文件

  • 修改:README.md
  • 修改:evals/README.md

计划要求在 README 的快速开始/安装列表中登记 Pi,并在 evals/README.md 中补充后端条目与用法。当前 README.md 的 “Pi” 小节(约第 180–194 行)完整落地了这一要求:

# 作为 Pi 包安装
pi install git:github.com/obra/superpowers

# 本地开发:把本检出作为临时包加载
pi -e /path/to/superpowers

并说明:Pi 包加载 Superpowers 技能与一个小型扩展,在会话启动及压缩后注入 using-superpowers bootstrap;由于 Pi 有原生 skills,不需要兼容 Skill 工具;子代理与任务列表工具仍是可选的 Pi 伴侣包。RELEASE-NOTES.md 也以 “Pi — a session-start extension that registers the skills and injects the using-superpowers bootstrap. Pi has native skills, so it needs no compatibility shim.” 概括了该能力。

最后一步是全量验证,计划给出的命令为:

node --experimental-strip-types --test tests/pi/test-pi-extension.mjs
uv run pytest evals/tests/test_backend.py evals/tests/test_setup.py evals/tests/test_normalizer.py -q

预期所有测试通过。

关键设计点回顾

回到计划的整体脉络,这份实施计划体现了 Superpowers 多 harness 适配的三条工程原则:

  1. 清单即适配:Pi 的一等支持本质上就是 package.jsonkeywordspi.skillspi.extensions 三组声明加一个约 120 行的零依赖 TS 扩展,复用既有 skills/ 内容,不复制、不分叉;
  2. 引导注入的时序正确性:bootstrap 只在“会话启动后”和“压缩后”注入(注册 session_start/session_compact 而不注册 session_before_compact),插入位置在所有 compactionSummary 之后,并用内容标记去重——保证 Agent 每轮都能“记得自己有 superpowers”,但绝不在压缩前污染待压缩上下文;
  3. 文档与测试同契约pi-tools.md 这类参考文档不是普通说明,其映射表被测试按行钉住;扩展行为则被一组“假 pi.on 注册表 + 事件序列重放”的测试完整锁定,任何行为回归都会让 node --test 失败。

对于维护该项目的 Agent 或开发者,建议的阅读与验证路径是:先读本计划了解任务拆分与验收标准,再对照 package.json.pi/extensions/superpowers.tstests/pi/test-pi-extension.mjsskills/using-superpowers/references/pi-tools.mdREADME.md 的 Pi 小节核对落地细节,最后运行任务 4 中的验证命令确认全绿。

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