第 02 模块 · 2 节

package.json 的 pi 键与目录约定

《Pi 生产级工程》02 Pi Packages 分发 · 本节时长 28 分钟

两种声明方式

一个 Pi Package 的资源,可以二选一声明:

  1. package.json 里写一个 pi,显式列出各资源路径
  2. 约定目录,pi 自动发现

两者可以混用,但记住:显式 pi 键是更可控的生产级选择,约定目录是偷懒时的兜底。

贯穿项目md-tools 的包结构,从这一节开始真正成型。你要决定它用 pi 键还是约定目录、把哪类资源放哪。**建议直接上手 pi 键——因为它能精确控制哪些文件进包、哪些不进,这对分发干净很关键。** 现在就把 md-tools 的资源分类想清楚:扩展放哪、技能放哪、主题放哪。

方式一:package.json 的 pi 键

package.json 里加一个 pi 字段,声明四类资源的路径。路径相对包根目录,数组支持 glob 和 ! 排除:

{
  "name": "my-package",
  "keywords": ["pi-package"],
  "pi": {
    "extensions": ["./extensions"],
    "skills": ["./skills"],
    "prompts": ["./prompts"],
    "themes": ["./themes"]
  }
}

pi-package 这个 keyword 很重要——包画廊(gallery)靠它来收录和展示你的包。


方式二:约定目录

如果 package.json 里没有 pi 键,pi 会自动从这些约定目录发现资源:

目录 加载什么
extensions/ .ts.js 文件
skills/ 递归找 SKILL.md 文件夹,顶层 .md 文件当技能
prompts/ .md 文件
themes/ .json 文件

一个极简包的目录结构大概长这样:

md-tools/
├── package.json
├── extensions/
│   └── index.ts
├── skills/
│   └── md-convert/SKILL.md
├── prompts/
│   └── normalize.md
└── themes/
    └── md-tools.json

画廊元数据:让别人一眼看懂

想让包在画廊里更好看,加 videoimage 字段显示预览:

{
  "name": "my-package",
  "keywords": ["pi-package"],
  "pi": {
    "extensions": ["./extensions"],
    "video": "https://example.com/demo.mp4",
    "image": "https://example.com/screenshot.png"
  }
}
  • video 只支持 MP4,桌面端悬停自动播放
  • image 支持 PNG / JPEG / GIF / WebP,作静态预览
  • 两个都设时,video 优先

依赖声明:peerDependencies vs bundledDependencies

这一节最容易踩坑的就是依赖。三句关键话:

  1. 第三方运行时依赖dependencies,pi 装包时自动跑 npm install
  2. @earendil-works/pi-* 这套核心包不要打包,改成 peerDependencies"*" 范围——pi 已经内置了它们
  3. 其他 pi 包必须打进你的 tarball,用 bundledDependencies
{
  "dependencies": {
    "shitty-extensions": "^1.0.1"
  },
  "bundledDependencies": ["shitty-extensions"],
  "pi": {
    "extensions": ["extensions", "node_modules/shitty-extensions/extensions"],
    "skills": ["skills", "node_modules/shitty-extensions/skills"]
  }
}

核心包清单(用 peerDependencies,别打包):@earendil-works/pi-ai@earendil-works/pi-agent-core@earendil-works/pi-coding-agent@earendil-works/pi-tuitypebox


落地练习:给 md-tools 写 package.json

动手写 md-tools 的包骨架:

  1. md-tools/ 目录,初始化 package.jsonname: "md-tools",加 keywords: ["pi-package"]
  2. 写上 pi 键,声明 extensionsskillspromptsthemes 四个路径
  3. 在里面放好约定目录(可以先放占位文件),确认路径和目录对得上

怎么判断做对了?——pi install ./md-tools 能装上,pi list 能看到四类资源各就各位;如果哪类没加载,多半是路径写错或目录命名不对。

卡住了怎么办? 资源没加载?先用约定目录(不写 pi 键)跑通一遍,再改成显式 pi 键。依赖报错?回想上面的「核心包放 peerDependencies」规则。


常见坑:把 pi 核心包打进 dependencies

最常见的依赖错误,是把 @earendil-works/pi-coding-agent 这类核心包写进 dependencies,自己打包一份。

后果是:版本冲突、重复加载、行为诡异。正确做法是让它们当 peerDependencies(范围 "*"),因为 pi 运行时已经内置了。你能打包的是「你自己的代码」,不是 pi 自己。 这个边界理不清,包会越做越乱。


小结

  1. 资源声明两种方式:package.jsonpi 键,或约定目录
  2. pi-package keyword 才会进画廊
  3. 画廊可用 video / image 显示预览
  4. 第三方依赖进 dependencies,pi 核心包进 peerDependencies
  5. 其他 pi 包用 bundledDependencies 打进 tarball

下一节,我们讲怎么安装、管理和最终发布这个包。