piagent_
← ~/guides

向 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.mdAGENTS.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、enumnamespace/moduleimport =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_KEYBINDINGSDEFAULT_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 -Agit 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(用 --checknpm run check 校验)。带有 lifecycle 脚本的新依赖需要在那个脚本里有显式的白名单条目——永远不要悄悄加。pre-commit hook 会拦截 lockfile 的提交,除非设置 PI_ALLOW_LOCKFILE_CHANGE=1。除非你真的想提交 lockfile 的变更,否则不要绕过它。

发布流程(面向维护者)

pi 使用 lockstep 版本号:所有包共用一个版本号,每次发布所有包一起升级。patch = 修复与新增,minor = breaking change。没有 major 发布。

流程:

  1. 审计 changelog。 询问用户是否对 main 上最新一次提交运行过 /cl 提示词。如果没有,他们必须在发布前先运行 /cl,更新每个包的 [Unreleased] 小节。

  2. 本地 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 必须全部通过,才能继续。

  3. 运行发布脚本。 仅在 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、打 tag vX.Y.Z、添加全新的 [Unreleased] 小节,然后推送 main 和 tag。tag 已推送后不要再次运行该脚本。

  4. CI 校验并广播。 推送 tag 会触发 .github/workflows/build-binaries.ymlpublish-npm job 通过 GitHub Actions OIDC(环境 npm-publish)使用 npm Trusted Publishing——不需要本地的 npm publish、OTP 或 WebAuthn。发布完成后,announce-pi-dev-release 会校验每个公开 workspace 包都能解析到精确的发布版本,并把标记写入 R2;pi.dev/api/latest-version 读取这个标记,所以只有该 job 成功完成后才会广播新版本。

  5. 如果 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 -Agit add .)。
  • 如果使用了 AI agent,请确认它是从仓库根目录启动的,已加载 AGENTS.md,并遵守了上述规则。
  • lockfile 与 package.json 依赖的变更是有意为之;pre-commit hook 没有被迫绕过。

延伸阅读

  • CONTRIBUTING.md——贡献闸门与质量门槛的权威来源
  • AGENTS.md——AI agent 与人类在仓库工作的强制规则
  • Earendil RFCs——较大改动的公开设计讨论
  • Discord——紧急议题与开议题前的设计交流
  • Releases——release:patchrelease:minor 产出的已发布工件