跳转到内容

第 31 课:发布与插件生态

设计并发布自己的 agent CLI,理解 npm 包、CLI 发布、配置迁移、extension API、skills marketplace 和企业配置。

发布一个 coding agent 不是把 dist/cli.js 丢到 npm 就结束。真正要交付的是一个可安装、可升级、可扩展、可回滚、能被团队配置约束的生态入口。

Pi 的发布和插件生态给了清晰参考:@earendil-works/pi-coding-agent 通过 package.json 暴露 binexportsfilesprepublishOnly;package manager 支持 npm、git、本地路径和项目级 .pi/settings.jsonDefaultResourceLoader 把 packages、extensions、skills、prompts、themes 统一解析;extension API 提供工具、事件、命令、UI、provider 和 session state 扩展点。你的 forge-code alpha 版也应该沿着这几层设计。

阶段五前半部分已经有评测、trace replay 和 prompt 工程。现在要把原型交给团队试用,会遇到新的问题:

  • CLI 怎样安装?是 npm i -g forge-code,还是内部 registry,还是 bun binary?
  • 用户升级后旧配置怎么办?settings.json 字段重命名如何迁移?
  • 企业团队怎样统一 provider、skills、权限策略和 npm wrapper?
  • 插件是直接复制 .ts 文件,还是发布为 npm package?
  • skills marketplace 如何筛选、启用、禁用和版本锁定?
  • 扩展 API 给多大能力?工具、命令、事件、UI、配置都开放,还是只开放一部分?

这节课的目标是从“我能本地运行”走到“团队能安装、审计、升级和扩展”。

发布与插件生态可以拆成四层:

第一层是发行物。它回答“用户拿到什么”:npm 包、二进制、Docker 镜像、内部包、源码 checkout。对 Node CLI 来说,最小发行物是 package.jsonbinexportsfiles、build 脚本和 prepublishOnly

第二层是配置生命周期。它回答“旧版本如何升级”:默认值、settings schema、字段迁移、项目级配置、用户级配置、企业固定配置。配置迁移必须可重复运行,不能依赖“用户手动删文件”。

第三层是资源生态。它回答“能力怎么扩展”:extensions、skills、prompts、themes、packages。资源可以来自 npm、git、本地路径,也可以是项目 .pi/ 自动发现。

第四层是治理边界。它回答“团队怎么安全使用”:允许哪些 package source、锁定哪些版本、用哪个 npmCommand、是否禁用三方 extension、哪些 skills 进入 marketplace、企业默认 provider 和权限模式是什么。

打开 /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/package.json。重点看这些字段:

  • name / version:包身份和版本。
  • bin:把 pi 命令映射到 dist/cli.js
  • main / types / exports:SDK 和 extension 作者从哪里 import。
  • files:npm tarball 里包含哪些目录。
  • scripts.build:编译 TypeScript 并复制主题、assets、HTML export 模板。
  • prepublishOnly:发布前强制 clean、build、shrinkwrap。
  • engines.node:运行时 Node 版本边界。

forge-code 来说,alpha 版至少要做到:安装后命令可执行、SDK 类型可 import、插件 API 可 import、包里不漏源代码秘密、不缺 runtime assets。

课程 lab starter /Users/ryanchen/codespace/pi-agent-course-lab/src/21-release-checklist.ts 已经列出四项:

typecheck -> package smoke -> config migration -> extension compatibility

这四项代表发布前的最低门槛。typecheck 保证类型层不坏;npm pack --dry-run 检查 tarball 内容;配置迁移保证旧用户能启动;extension compatibility 保证生态不被破坏。

Pi 的 package manager 支持三种 source:

  • npm:@scope/pkg@1.2.3
  • git:github.com/user/repo@v1
  • /absolute/path/to/package./relative/path

这些 source 进入 settings 后,DefaultPackageManager.resolve() 会解析 installed package,再收集 extensions、skills、prompts、themes。版本化 npm spec 和 pinned git ref 是团队治理的关键:企业环境不应该默认追随最新 package。

DefaultResourceLoader.reload() 会先 reload settings,再通过 package manager resolve resources,随后加载 extensions、skills、prompts、themes、AGENTS/CLAUDE context files 和 system prompt。

extension API 是生态能力的主边界。extensions.mdextensions/types.ts 显示 extension 可以:

  • 监听 agent、session、tool、model、resource lifecycle event。
  • 注册 LLM-callable tool。
  • 注册 slash command、shortcut、flag。
  • 使用 UI dialog、status、widget、custom component。
  • 修改 tool call、message end、compaction、provider 等行为。

这很强,也意味着 extension 必须被当作代码执行权限来治理,而不是普通配置。

skills marketplace 不只是“有一个列表”。至少要有:

  • skill 元数据:name、description、version、source、适用场景。
  • 安装来源:npm、git、本地、企业镜像。
  • 启用范围:user-level 或 project-level。
  • 冲突策略:同名 skill 谁优先。
  • 审核状态:官方、团队批准、实验性、禁用。
  • 兼容性:支持的 forge-code 版本和所需工具权限。

Pi 的 packages.md 提供了 package gallery 和 pi-package keyword 的思路;你的内部 marketplace 可以先用一个 JSON index 实现,再逐步接入 UI。

企业配置要避免“每个开发者自己配一遍”。最小方案是:

  • 公司内部 npm registry 或 git mirror。
  • 项目级 .pi/settings.jsonforge-code.settings.json
  • 固定 packagesskillsprompts、provider、model allowlist。
  • 设置 npmCommand,确保 package install 走公司指定 Node/npm wrapper。
  • 禁用或审批未知 extension source。
  • 发布前用 npm pack --dry-run 和 smoke project 验证真实安装路径。

本课 lab starter 是 /Users/ryanchen/codespace/pi-agent-course-lab/src/21-release-checklist.ts。它目前只返回 required checks。课堂上要把它升级成 release gate:每个 check 有命令、证据、失败处理和是否阻断发布。

发布 forge-code 内部 alpha 版可以先定义这样一条流水线:

npm run typecheck
npm run test
npm pack --dry-run
node dist/cli.js --version
node dist/cli.js task create --help
run config migration fixture
run extension compatibility fixture

每一步都要产生 evidence。没有 evidence 的发布只是“我觉得可以发”。

从 starter 的 ReleaseCheck 开始,增加执行状态:

type ReleaseCheck = {
name: string;
required: boolean;
command?: string;
evidence?: string;
};
type ReleaseResult = {
publishable: boolean;
blockers: string[];
warnings: string[];
};
function evaluateRelease(checks: ReleaseCheck[]): ReleaseResult {
const blockers: string[] = [];
const warnings: string[] = [];
for (const check of checks) {
const passed = Boolean(check.evidence);
if (!passed && check.required) {
blockers.push(check.name);
continue;
}
if (!passed) {
warnings.push(check.name);
}
}
return {
publishable: blockers.length === 0,
blockers,
warnings,
};
}

这段代码的教学重点不是数组遍历,而是发布决策的变量流:

  1. required 决定失败是否阻断发布。
  2. command 是 evidence 的来源,但不是所有检查都有命令;配置迁移可能来自 fixture 结果。
  3. evidence 是发布审计的材料。npm pack --dry-run 的输出、migration fixture 的旧配置输入和新配置输出都应该被保存。
  4. publishable 只由 blockers 决定,warnings 可以进入 release note 或 alpha 限制说明。

再把 package 生态放进 checklist:

const alphaChecks: ReleaseCheck[] = [
{ name: "typecheck", required: true, command: "npm run typecheck", evidence: "0 errors" },
{ name: "package smoke", required: true, command: "npm pack --dry-run", evidence: "dist/cli.js included" },
{ name: "config migration", required: true, evidence: "settings-v1 -> settings-v2 fixture passed" },
{ name: "extension compatibility", required: false },
];

课堂上会让学生解释:为什么 extension compatibility 在 alpha 可以是 warning,但到了企业 beta 应该升级为 required。答案是生态承诺变了:alpha 允许破坏扩展,beta 开始要求稳定 API。

  • /Users/ryanchen/codespace/pi-agent-course-lab/src/21-release-checklist.ts:本课 lab starter,用发布检查表建立 release gate。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/package.json at 61babc2:CLI npm 包的 binexportsfiles、build、binary build、prepublishOnly、runtime dependencies。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/package-manager-cli.ts at 61babc2pi installpi removepi listpi update 的 CLI 入口。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/package-manager.ts at 61babc2:npm/git/local package source、settings 持久化、安装路径、manifest、resource filtering、pinned ref 处理。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/resource-loader.ts at 61babc2:把 packages、extensions、skills、prompts、themes 和 context files 装载进 runtime。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/extensions/types.ts at 61babc2:extension API 的主要类型边界。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/docs/packages.md at 61babc2:Pi package 的安装来源、package manifest、gallery metadata、resource filtering、scope 和 deduplication 说明。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/docs/extensions.md at 61babc2:extension 能力、加载位置、runtime dependency、事件和 API 说明。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/docs/skills.md at 61babc2:skills 规范、加载、冲突和校验规则。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/test/package-manager.test.ts at 61babc2:package manager 行为测试锚点。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/test/extensions-runner.test.ts at 61babc2:extension runner 行为测试锚点。

学完本课后,你应该能做到:

  • 解释一个 agent CLI npm 包的最小发布面:binexportsfiles、build、publish gate。
  • 设计 npm pack --dry-run、CLI smoke、typecheck、migration fixture、extension compatibility 组成的 release checklist。
  • 说明 Pi packages 如何从 npm、git、本地路径进入 settings,再被 resource loader 加载。
  • 区分 extension、skill、prompt template、theme 的职责和风险等级。
  • 为内部 skills marketplace 设计元数据、版本锁定、启用范围、审核状态和冲突策略。
  • 为企业配置设计 user-level、project-level、registry、npmCommand、provider allowlist 和未知 extension 审批策略。
  1. 实践题:扩展 /Users/ryanchen/codespace/pi-agent-course-lab/src/21-release-checklist.ts,实现 evaluateRelease(),要求输出 publishableblockerswarnings,并为 npm pack --dry-run 增加一条 evidence 示例。
  2. 思考题:为什么 extension package 应该被当成“可执行代码”治理,而 skill marketplace 也不能只看 Markdown 内容是否友好?