向 pi 贡献代码
最后更新: 2026年8月14日
$ pi –contribute
TL;DR。 pi 的贡献闸门是刻意设计的:每个新贡献者的议题和 PR 都会被默认自动关闭。在评论开头或结尾由维护者发出 lgtmi(仅议题)或 lgtm(议题 + PR)之后,你才能合入任何东西。一旦获批,在 push 之前于仓库根目录运行 npm run check 和 ./test.sh——两者必须全部通过。仓库遵循 lockstep 版本号(所有包共用一个版本号,没有 major 发布)、conventional commit 消息格式,以及一个把所有包一起 bump 的发布脚本。如果你使用 AI agent 来写 PR,请在 pi 仓库根目录启动它,让它自动加载 AGENTS.md——agent 必须遵守该文件。
本指南面向 earendil-works/pi 的贡献者。它把仓库里的 CONTRIBUTING.md 和 AGENTS.md 中的规则汇总到一处,便于一次性读完并看清它们之间的联动。如果上游文件有改动,本指南会滞后——在开议题或 PR 前,请始终以仓库中的规范版本为准。
贡献闸门
仓库会自动关闭每个新贡献者的每一条议题和每一个 PR。这不是 bug,而是设计。低信噪比的提交量很大,维护者按自己的节奏处理而不是被动响应。两个命令可以重新打开闸门,而且只有维护者才能下达:
lgtmi——你此后提交的议题将不再被自动关闭。lgtm——你此后提交的议题与 PR 都不再被自动关闭。只有lgtm授予提交 PR 的权利。
该命令必须出现在维护者回复的最开头或最末尾(可选地放在一个或多个 @username 提及之后)。放在回复中间不算数。如果你没看到位于边界的 lgtm,就不要开 PR——它会被自动关闭且不会进入评审。
周末提交的议题(周五至周日)不保证会被评审。如果事情紧急,请到 Discord 上说明:精简版本、可复现步骤和相关日志。
议题的质量门槛
如果你要开议题,必须使用两个 GitHub 议题模板之一,正文保持简短(一屏之内),并用自己的话写。要把 bug 或诉求讲清楚,说明为什么重要,并标明你是否想自己动手实现。AGENTS.md 明确写道:不要让 LLM 起草正文;如果非用不可,请在后续评论中明确标记为 AI 生成。
只有达到门槛,维护者才会重开议题或回复 lgtmi / lgtm。未达标的议题会被无评论地关闭——这种沉默是有意为之,「回复本身就是维护工作」。如果你两次忽略本文件,或大批量提交 agent 生成的议题,你的 GitHub 账号将被永久封禁。
对于较大的设计变更,Earendil 使用 RFC——并非所有 RFC 都公开,但公开的那些是开议题前与维护者沟通设计的好起点。
提交 PR 之前
除非已经获得维护者的 lgtm 批准,否则不要开 PR。拿到批准后,预检查是:
npm run check
./test.sh
两者必须全部通过。npm run check 要求输出完整(不要 tail)——它是 AGENTS.md 在每次代码改动后要求的类型/lint 闸门,不会跑测试。./test.sh 跑的是非 e2e 测试,几乎适用于所有贡献者。不要直接跑完整的 vitest 套件:它包含的 e2e 测试会在 endpoint 或 auth 环境变量存在时被激活,那需要另一套测试装置。
如果要在 packages/ai 中新增一个 provider,AGENTS.md 指定了必填的测试文件;写 provider 之前先看一遍那个文件的预期。所有 issue 专用的回归测试放在 packages/coding-agent/test/suite/regressions/ 下,命名 <issue-number>-<short-slug>.test.ts。
AGENTS.md 中关于 AI 生成内容的规则
用 AI 写代码可以。提交没读懂就交上来的 AI 生成 slop 不行。如果用 agent 起草 PR,请在 pi 仓库根目录启动它,让它自动加载 AGENTS.md——该文件是在本仓库工作的任何 agent 的强制规则集,你的 PR 将按它评审。agent 必须遵守的核心规则:
- 没有
any,除非绝对必要。TypeScript 配置使用 Node strip-only 模式(仅可擦除语法):不允许 parameter properties、enum、namespace/module、import =、export =。请用显式字段加构造函数赋值。 - 禁止内联 import。
await import()、import("pkg").Type、动态类型 import 都不行。只用顶层 import。 - 改动前先把文件读全。 对于大范围改动,搜索片段是不够的。
- 永远不要直接改
packages/ai/src/models.generated.ts。请改packages/ai/scripts/generate-models.ts中的生成器并重新生成;这样产生的models.generated.ts差异可以一并提交,即使其中夹带不相关的上游模型元数据变更。 - 单行帮助函数如果只有一个调用点,就内联。
- 去
node_modules查看外部 API 类型,而不是凭印象写签名。 - 永远不要为了应对过期依赖的类型错误而移除或降级代码——升级依赖即可。
- 永远不要硬编码按键。 应改为添加到
DEFAULT_EDITOR_KEYBINDINGS或DEFAULT_APP_KEYBINDINGS的默认值里。 - 看似有意为之的功能或代码,删除前先询问;不要保留向后兼容,除非明确要求。
这些规则来自 AGENTS.md,在 PR 评审中没有商量余地。如果你的 agent 违反了某条,修补的责任在你自己,而不是维护者。
Changelog 约定
不要编辑 CHANGELOG.md。Changelog 条目由维护者在 PR 合入后添加。Changelog 位于 packages/*/CHANGELOG.md(每个包一份),所有新条目都放在 ## [Unreleased] 之下——先读完这一节,再追加到已有小节里,避免重复。已发布版本的小节是不可变的。
Changelog 条目中的署名格式:
- 内部修复(来自议题):
Fixed foo bar ([#123](https://github.com/earendil-works/pi-mono/issues/123)) - 外部贡献:
Added feature X ([#456](https://github.com/earendil-works/pi-mono/pull/456) by [@username](https://github.com/username))
Commit 消息格式
仓库使用一套紧凑的 conventional-commit 子集。消息格式为:
{feat,fix,docs}[(ai,tui,agent,coding-agent)]: <commit message>
只有当改动确实只落在某个包内时才加 scope;否则不加。消息本身要信息密度高且简洁——不加 emoji、不写客套,只写技术散文。如果你用 AI agent 起草了 commit,请遵循 AGENTS.md 和贡献约定;「Thanks @user」可以,「Thanks so much @user!」不行。
可能同时有多个 pi 会话在同一个工作目录下运行。stage 时使用显式路径(git add <path1> <path2>);绝不要 git add -A 或 git add .。提交前跑 git status,确认你只 stage 了自己的文件。packages/ai/src/models.generated.ts 可以和你的文件一起 stage。
依赖与 lockfile 规则
把 npm 依赖和 lockfile 的变更当作已被评审过的代码看待。直接的外部依赖要钉到精确版本。本地 hydrate 用 npm install --ignore-scripts;CI 风格的安装用 npm ci --ignore-scripts。除非用户要求,否则不要跑 lifecycle 脚本。升级 undici 时,必须阅读其 changelog 并评估影响后再改版本号。
如果 packages/coding-agent/npm-shrinkwrap.json 需要重新生成,请跑 node scripts/generate-coding-agent-shrinkwrap.mjs(用 --check 或 npm run check 校验)。带有 lifecycle 脚本的新依赖需要在那个脚本里有显式的白名单条目——永远不要悄悄加。pre-commit hook 会拦截 lockfile 的提交,除非设置 PI_ALLOW_LOCKFILE_CHANGE=1。除非你真的想提交 lockfile 的变更,否则不要绕过它。
发布流程(面向维护者)
pi 使用 lockstep 版本号:所有包共用一个版本号,每次发布所有包一起升级。patch = 修复与新增,minor = breaking change。没有 major 发布。
流程:
-
审计 changelog。 询问用户是否对
main上最新一次提交运行过/cl提示词。如果没有,他们必须在发布前先运行/cl,更新每个包的[Unreleased]小节。 -
本地 smoke test。 把未发布的构建产物解压到
/tmp,用真实 prompt 跑一遍二进制:npm run release:local -- --out /tmp/pi-local-release --force /tmp/pi-local-release/node/pi --version /tmp/pi-local-release/node/pi --list-models /tmp/pi-local-release/node/pi -p "Say exactly: ok"对 Bun 二进制再重复一遍。启动、模型/账号列表、交互启动、以及至少一条用默认 provider 的真实 prompt 必须全部通过,才能继续。
-
运行发布脚本。 仅在 release 命令本身上使用
PI_ALLOW_LOCKFILE_CHANGE=1 npm_config_min_release_age=0:PI_ALLOW_LOCKFILE_CHANGE=1 npm_config_min_release_age=0 npm run release:patch # 修复与新增 PI_ALLOW_LOCKFILE_CHANGE=1 npm_config_min_release_age=0 npm run release:minor # breaking change该脚本会 bump 所有包版本、更新 changelog、重新生成 release 产物、运行
npm run check、提交Release vX.Y.Z、打 tagvX.Y.Z、添加全新的[Unreleased]小节,然后推送main和 tag。tag 已推送后不要再次运行该脚本。 -
CI 校验并广播。 推送 tag 会触发
.github/workflows/build-binaries.yml。publish-npmjob 通过 GitHub Actions OIDC(环境npm-publish)使用 npm Trusted Publishing——不需要本地的npm publish、OTP 或 WebAuthn。发布完成后,announce-pi-dev-release会校验每个公开 workspace 包都能解析到精确的发布版本,并把标记写入 R2;pi.dev/api/latest-version读取这个标记,所以只有该 job 成功完成后才会广播新版本。 -
如果 CI 失败。 publish 助手是幂等的——它会跳过 npm 上已存在的包版本——announce job 会在更新 R2 标记前再次校验可用性。修好 CI 或瞬态的 npm 问题后,重跑失败的 job 或 workflow;不要对同一版本重跑
npm run release:patch。
PR 提交前清单
在开 PR 之前:
- 维护者已经在边界位置用
lgtm批准过该改动。 -
npm run check完整输出通过(无 tail)。 -
./test.sh通过。 - issue 专用回归测试位于
packages/coding-agent/test/suite/regressions/<issue>-<slug>.test.ts。 - 没有改动
CHANGELOG.md——那是维护者的工作。 - commit 消息符合
{feat,fix,docs}[(scope)]: <message>,无 emoji、无客套。 -
git status只显示你的文件(没有git add -A或git add .)。 - 如果使用了 AI agent,请确认它是从仓库根目录启动的,已加载
AGENTS.md,并遵守了上述规则。 - lockfile 与
package.json依赖的变更是有意为之;pre-commit hook 没有被迫绕过。
延伸阅读
- CONTRIBUTING.md——贡献闸门与质量门槛的权威来源
- AGENTS.md——AI agent 与人类在仓库工作的强制规则
- Earendil RFCs——较大改动的公开设计讨论
- Discord——紧急议题与开议题前的设计交流
- Releases——
release:patch与release:minor产出的已发布工件