第十章:Pi 包管理与分发
本章深入讲解 Pi 的包生态系统:从一个包由什么组成、如何安装和管理(
pi install/pi list/pi remove/pi update),到如何创建、发布和共享你自己的 Pi 包。读完本章,你将不仅能消费社区 4600+ 包,还能把自己积累的 Extensions、Skills、Prompt Templates 和 Themes 打包分发给团队或社区。
10.1 Pi 包系统概述
第一章提到 Pi 有 4600+ 社区包——这不是夸张的数字,而是截至 2026 年 6 月 npm 上以 pi- 开头的包的实际统计。Pi 包生态之所以如此繁荣,根本原因在于 Mario 把包定义得极其简单:一个 Pi 包就是一个普通的 npm 包或 Git 仓库,其中声明了 Pi 应当从中加载哪些资源。
10.1.1 包的组成:四种资源类型
一个 Pi 包可以包含以下四种资源中的任意组合:
| 资源类型 | 文件格式 | 在包中的作用 | 对应章节 |
|---|---|---|---|
| Extensions(扩展) | .ts / .js |
注册新工具、命令、生命周期钩子、TUI 组件 | 第九章 |
| Skills(技能) | SKILL.md |
告诉模型”如何完成某类特定任务”的渐进式文档 | 第八章 |
| Prompt Templates(提示模板) | .md |
追加到系统提示末尾的行为约束或风格指南 | 5.6.4 |
| Themes(主题) | .json |
定义 TUI 各组件的颜色方案 | 5.6.5 |
一个包不需要包含全部四种——它可以只提供一个 Skill,只注册一个 Extension,甚至只带一套主题。包的设计原则是”按需组合”:一个解决特定问题的包,只带解决该问题所需的资源。
10.1.2 安装源:npm 和 Git
Pi 支持两种包安装源:
| 源类型 | 格式 | 适用场景 |
|---|---|---|
| npm | npm:@scope/package-name 或直接 package-name |
公开发布的包,社区分享 |
| Git | git:github.com/user/repo |
私有包、未发布到 npm 的包、开发中的包 |
两种源的包在功能上完全对等——无论来源是 npm 还是 Git,Pi 都用同一套机制加载其中的 Extension、Skill、Prompt 和 Theme。
10.1.3 配置位置
安装的包会被记录在 settings.json 的 packages 数组中。第五章(5.6.1)已简要介绍过这个字段,这里复习并深化:
{
"packages": [
"pi-tool-react@0.2.0",
"npm:@scope/pi-utils@1.0.0",
"git:github.com/user/pi-private-tools.git"
]
}
packages 数组中的每个条目是一个包引用字符串,Pi 在启动时(或在执行 pi install 后)解析这些引用,下载并加载包中声明的所有资源。
10.1.4 全局 vs 项目级包
和配置的两层体系一致,包也分为全局和项目两级:
| 层级 | 配置位置 | 加载时机 | 作用范围 |
|---|---|---|---|
| 全局包 | ~/.pi/agent/settings.json 的 packages |
每次启动 Pi | 所有项目 |
| 项目包 | .pi/settings.json 的 packages |
项目被信任后 | 当前项目 |
数组合并规则:项目 packages 追加到全局 packages 之后(与 extensions、skills、prompts、themes 的合并规则一致)。这意味着你在所有项目中都会加载全局包,而在特定项目中可以额外加载项目专属包。
推荐分发策略:
- 全局包:放通用工具——如
pi-tool-shell-safety(Shell 安全检查)、pi-theme-nord(Nord 配色主题)、pi-skill-git-workflow(Git 工作流技能)。 - 项目包:放项目专属工具——如为某个微服务仓库定制的数据库迁移 Skill、为团队内部框架编写的代码生成 Extension。
10.2 安装包
10.2.1 pi install 基本语法
pi install <包引用>
<包引用> 是一个字符串,支持以下四种格式:
| 格式 | 示例 | 说明 |
|---|---|---|
| npm 包(简写) | pi-tool-react |
等同于 npm:pi-tool-react,从 npm registry 安装 |
| npm 包(完整) | npm:@scope/pi-utils |
显式指定 npm 源,支持 scoped package |
| npm 包(指定版本) | npm:@scope/pi-utils@1.0.0 |
锁定特定版本 |
| Git 仓库 | git:github.com/user/repo |
从 Git 仓库克隆安装 |
10.2.2 npm 包格式详解
npm 是 Pi 包的主要分发渠道。包引用格式遵循标准的 npm 包标识规范:
# 方式 A:简写(无 scope 的包)
pi install pi-tool-react
# 方式 B:完整 npm: 前缀(推荐,语义明确)
pi install npm:pi-tool-react
# 方式 C:带 scope 的包
pi install npm:@myteam/pi-custom-tools
# 方式 D:带 scope + 版本号
pi install npm:@myteam/pi-custom-tools@2.1.0
# 方式 E:版本范围(semver)
pi install npm:pi-tool-react@^1.0.0
pi install npm:pi-tool-react@~1.2.3
pi install npm:pi-tool-react@latest
关于版本号的建议:强烈推荐在包引用中标注版本号。不标版本意味着 Pi 每次解析时都会取 npm 上的最新版——如果包作者发布了 breaking change,你的 Pi 可能在下一次启动时突然行为异常。在生产环境中,锁定精确版本(如 @2.1.0)是最稳妥的做法;在个人实验环境中,可以使用 @latest 或 ^ 范围来自动获取补丁更新。
10.2.3 Git 包格式详解
Git 格式让你可以直接从 Git 仓库安装 Pi 包,无需通过 npm 发布。适用于私有仓库、开发中的包、或不想走 npm publish 流程的场景:
# 基本格式:从默认分支安装
pi install git:github.com/user/pi-tools
# 指定分支
pi install git:github.com/user/pi-tools#develop
# 指定标签
pi install git:github.com/user/pi-tools#v1.0.0
# 指定 commit
pi install git:github.com/user/pi-tools#a1b2c3d
# GitLab
pi install git:gitlab.com/user/pi-tools
# Bitbucket
pi install git:bitbucket.org/user/pi-tools
# 自托管 Git(Gitea / Gogs 等)
pi install git:git.mycompany.com/team/pi-tools
Git 包引用的 URL 部分直接传给 git clone,因此任何 git clone 能识别的 URL 格式都可以用(包括 https://、git@、ssh:// 等)。上一节示例省略了协议前缀——Pi 会自动补全 https://。如果需要显式指定协议:
pi install git:https://github.com/user/pi-tools.git
pi install git:git@github.com:user/pi-tools.git
分支/标签/commit 的 # 语法在所有 Git 平台上通用,Pi 内部将其映射为 git clone --branch 或 git checkout。
10.2.4 安装流程详解
当你执行 pi install npm:@scope/pi-tools@1.0.0 时,Pi 内部依次执行以下步骤:
- 解析包引用:将字符串解析为
{ type: "npm", name: "@scope/pi-tools", version: "1.0.0" }结构。 - 下载包:
- npm 包:通过
npm pack或直接npm install到 Pi 的内部缓存目录(~/.pi/packages/)。 - Git 包:通过
git clone到同一缓存目录。
- npm 包:通过
- 读取
package.json中的pi字段:这是 Pi 包的核心声明——它告诉 Pi 这个包里有哪些 Skills、Extensions、Prompts、Themes。详见 10.4。 - 注册资源:将 Skills 路径加入 Skills 索引(Pi 启动时加载),将 Extensions 路径加入 Extension 加载列表,将 Prompt 和 Theme 路径加入对应的资源池。
- 更新配置:在
settings.json的packages数组中追加该包引用字符串(如果使用--no-save参数则跳过)。 - 输出安装摘要:在 TUI 中显示安装了哪些资源(如 “✓ 已加载 2 个 Skills,1 个 Extension”)。
安装过程中的 package.json 查找逻辑:Pi 会在包的根目录下寻找 package.json,如果找不到,或者找到了但其中没有 pi 字段,Pi 会给出警告并跳过该包——因为没有 pi 字段的声明,Pi 不知道该包提供了什么资源。
10.2.5 安装参数
# 保存到全局配置(默认行为)
pi install npm:pi-tool-react
# 保存到项目配置
pi install npm:pi-tool-react --project
pi install npm:pi-tool-react -p
# 仅临时使用,不写入 settings.json
pi install npm:pi-tool-react --no-save
# 强制重新下载(即使缓存中已有相同版本)
pi install npm:pi-tool-react --force
# 安装但不自动启用(仅下载,不加载资源)
pi install npm:pi-tool-react --no-enable
--no-save 适合测试新包——不想因为试用就把包永久写进配置。--force 适合包的本地开发迭代——你改了包的代码,想确认 Pi 加载的是最新版本而非缓存。
10.3 管理包
10.3.1 pi list:查看已安装包
pi list
输出示例:
已安装的 Pi 包:
全局包(~/.pi/agent/settings.json):
┌──────────────────────────────────┬──────────┬──────────────────────────────────┐
│ 包名 │ 版本 │ 源 │
├──────────────────────────────────┼──────────┼──────────────────────────────────┤
│ pi-tool-react │ 0.2.0 │ npm registry │
│ npm:@scope/pi-utils │ 1.0.0 │ npm registry │
│ git:github.com/user/pi-private │ a1b2c3d │ GitHub (main) │
└──────────────────────────────────┴──────────┴──────────────────────────────────┘
项目包(./.pi/settings.json):
┌──────────────────────────────────┬──────────┬──────────────────────────────────┐
│ 包名 │ 版本 │ 源 │
├──────────────────────────────────┼──────────┼──────────────────────────────────┤
│ pi-skill-db-migration │ 1.2.0 │ npm registry │
└──────────────────────────────────┴──────────┴──────────────────────────────────┘
合计:4 个包
对于 Git 包,”版本”列显示的是当前 checkout 的 commit 短哈希和分支名。
10.3.2 pi remove:卸载包
# 卸载包
pi remove npm:pi-tool-react
# 同时清理缓存
pi remove npm:pi-tool-react --purge
pi remove 做了三件事:
- 从
settings.json的packages数组中删除该包引用。 - 从 Pi 的资源索引中注销该包提供的所有 Skills、Extensions、Prompts、Themes。
- 默认保留缓存(
~/.pi/packages/中的下载文件),以便将来重新安装时无需再次下载。--purge参数会额外删除缓存。
10.3.3 pi update:更新包
# 更新 Pi 本身
pi update
# 更新 Pi 和所有已安装包到最新版本
pi update --all
# 仅更新已安装的包(不更新 Pi 本身)
pi update --packages
# 更新指定包
pi update npm:pi-tool-react
pi update --all 是推荐的日常维护命令——每周运行一次即可保持整个 Pi 生态处于最新状态。更新时的具体行为:
| 更新对象 | 检查内容 | 更新方式 |
|---|---|---|
| Pi 本身 | npm registry 上 @earendil-works/pi-coding-agent 的最新版本 |
npm install -g @earendil-works/pi-coding-agent@latest |
| npm 包 | 每个包在 npm registry 上的最新版本(或满足 semver 范围的最新版本) | 重新下载并替换缓存 |
| Git 包 | 远程仓库的最新 commit(当前 checkout 分支的 HEAD) | git pull(若本地有未提交修改则警告并跳过) |
Git 包的更新策略:Pi 不会强制覆盖本地修改——如果你在 ~/.pi/packages/ 中对 Git 包做了手动修改(例如调试),pi update 会跳过该包并给出警告,提示先处理本地修改后再更新。
版本锁定与更新:如果你在包引用中指定了精确版本(@1.0.0),pi update --all 不会将其升级——它会保持你锁定的版本。只有当你使用范围版本(@^1.0.0)、@latest 或未指定版本时,才会拉取最新匹配版本。
10.4 创建可分发包
创建一个 Pi 包的本质是:创建一个包含 package.json(其中有 pi 字段)的 npm 包或 Git 仓库。如果包中还有 .ts/.js 扩展文件、SKILL.md 技能文件、.md 提示模板或 .json 主题文件,在 pi 字段中声明它们的路径即可。
10.4.1 package.json 中的 pi 字段
pi 字段是 Pi 识别一个 npm 包是否为 Pi 包的标志。如果 package.json 中没有这个字段,Pi 会认为这个包不提供任何 Pi 资源(即使它恰好包含技能文件),从而跳过加载。
pi 字段的结构:
{
"name": "pi-tool-my-awesome-pack",
"version": "1.0.0",
"description": "一个描述",
"pi": {
"extensions": ["./extensions/hello.ts"],
"skills": ["./skills/code-review/SKILL.md"],
"prompts": ["./prompts/coding-style.md"],
"themes": ["./themes/ocean.json"]
}
}
10.4.2 pi.extensions:扩展路径
一个字符串数组,指向包内的 TypeScript 或 JavaScript 扩展文件。Pi 在加载包时会按顺序将这些文件作为 Extension 加载——与你在 settings.json 的 extensions 数组中声明本地扩展的行为完全一致。
"pi": {
"extensions": [
"./extensions/danger-detector.ts",
"./extensions/git-utils.ts"
]
}
路径是相对于包根目录的。每个 .ts / .js 文件必须导出一个默认函数,接收 ExtensionAPI 对象并在其上注册工具、命令或事件监听器。具体 API 参见第九章:Extensions 扩展开发。
10.4.3 pi.skills:技能路径
一个字符串数组,指向包内的 SKILL.md 技能文件。每个路径通常指向一个目录——Pi 会加载该目录下的 SKILL.md 文件(目录名即为技能名)。
"pi": {
"skills": [
"./skills/git-workflow",
"./skills/api-design"
]
}
上述配置意味着包内存在以下文件结构:
pi-tool-my-awesome-pack/
├── skills/
│ ├── git-workflow/
│ │ └── SKILL.md
│ └── api-design/
│ └── SKILL.md
├── ...
└── package.json
技能文件遵循 agentskills.io 标准。Pi 在启动时只加载技能名称和一句话摘要,完整内容在 Agent 首次使用时才按需加载(渐进式披露)。详细技能编写规范见第八章:Skills 技能系统。
10.4.4 pi.prompts:提示模板路径
一个字符串数组,指向包内的 Markdown 提示模板文件。这些文件的内容会被追加到系统提示的末尾(在所有 Skills 之后、对话历史之前)。
"pi": {
"prompts": [
"./prompts/react-patterns.md",
"./prompts/testing-guidelines.md"
]
}
提示模板用于注入”始终在线”的行为约束或风格指南,而不是像 Skills 那样按需加载。例如:
react-patterns.md:”> 始终使用函数组件和 Hooks。避免 class 组件。状态管理优先使用 useState 和 useReducer。”testing-guidelines.md:”> 为所有导出的工具函数编写单元测试。测试文件与源文件同目录,后缀.test.ts。”
10.4.5 pi.themes:主题路径
一个字符串数组,指向包内的 JSON 主题文件。
"pi": {
"themes": [
"./themes/ocean.json",
"./themes/forest.json"
]
}
加载后,用户可以在 settings.json 的 theme 字段中通过主题名引用这些主题。例如,如果 ocean.json 中定义了 "name": "ocean",用户就可以设置 "theme": "ocean"。
10.4.6 完整 package.json 示例
以下是一个真实可用的 Pi 包 package.json 完整示例。这个假想的包名为 pi-tool-frontend-kit,集成了 React 开发中常用的扩展、技能、提示模板和主题:
{
"name": "pi-tool-frontend-kit",
"version": "1.2.0",
"description": "Pi package with tools, skills, prompts and theme for React frontend development",
"keywords": ["pi", "pi-package", "react", "frontend"],
"author": "Your Name <you@example.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/your-org/pi-tool-frontend-kit"
},
"pi": {
"extensions": [
"./extensions/component-scaffolder.ts",
"./extensions/css-linter.ts"
],
"skills": [
"./skills/react-component",
"./skills/state-management",
"./skills/api-integration"
],
"prompts": [
"./prompts/react-conventions.md",
"./prompts/a11y-checklist.md"
],
"themes": [
"./themes/ocean-breeze.json"
]
},
"dependencies": {
"typebox": "^0.32.0"
},
"peerDependencies": {
"@earendil-works/pi-coding-agent": ">=0.70.0"
},
"files": [
"extensions/",
"skills/",
"prompts/",
"themes/",
"package.json",
"README.md"
]
}
几个关键点:
keywords中包含"pi"和"pi-package":方便在 npm 上被搜索到。社区约定所有 Pi 相关的包都在 keywords 中带上这两个标签。peerDependencies声明 Pi 版本要求:如果你的包依赖了特定版本的 Extension API,通过此字段声明兼容的 Pi 版本范围,npm 会在安装时给出提示。dependencies中声明typebox:如果你的 Extension 注册了自定义工具,通常需要使用TypeBox来定义工具参数 schema。第九章会详细讲解。files字段精确控制发布内容:npm publish 默认会包含.gitignore忽略之外的所有文件。显式声明files可以避免意外将测试文件、开发配置等发布到 npm。
10.5 包资源加载机制
理解 Pi 如何发现和加载包中的资源,对于调试包相关问题(”装了但没生效”)至关重要。
10.5.1 加载顺序和优先级
Pi 的资源加载严格按照以下顺序进行:
1. 内置工具和命令(read / write / edit / bash / grep / find / ls)
2. 全局 settings.json 中声明的 Extensions / Skills / Prompts / Themes
3. 全局 settings.json 中 packages 数组的每个包
4. 项目 .pi/settings.json 中声明的 Extensions / Skills / Prompts / Themes
5. 项目 .pi/settings.json 中 packages 数组的每个包
6. CLI -e 参数临时加载的 Extensions
这个顺序的意义:后加载的可以覆盖先加载的。例如,你可以在全局安装一个 pi-theme-dark 包,然后在项目中通过本地 themes 数组覆盖其中某个主题。同样,项目包中的 Skill 可以覆盖全局包中同名的 Skill(通过 Skill 名称匹配)。
10.5.2 冲突解决
当两个包声明了同名的资源时,Pi 使用以下规则解决冲突:
| 资源类型 | 冲突检测依据 | 解决规则 |
|---|---|---|
| Extension | 文件名(不含路径前缀) | 后加载的覆盖先加载的 |
| Skill | 技能目录名(skills/<name>/SKILL.md 中的 <name>) |
后加载的覆盖先加载的 |
| Prompt | 文件名 | 全部加载(追加,不覆盖) |
| Theme | 主题 JSON 中的 name 字段 |
后加载的覆盖先加载的 |
Skill 覆盖是特性,不是 Bug:如果一个项目包声明了一个与全局包同名的 Skill,项目包的版本会”阴影”(shadow)全局包的版本。这允许团队在特定项目中定制一个通用 Skill 的行为,而不影响其他项目。Pi 在启动日志中会明确标注被覆盖的 Skill(⚠ skill "code-review" 已被 /path/to/project-skill/SKILL.md 覆盖)。
Prompt 不覆盖:因为 Prompt Templates 是追加到系统提示末尾的文本块,多个来源的 Prompts 应该全部生效——全局和项目的行为约束同时起作用。如果两个包有同名的 Prompt 文件,两者的内容都会被注入。
10.5.3 资源发现机制
Pi 在启动时会触发一个内部的 resources_discover 事件,该事件的流程如下:
- 扫描 settings.json 的
packages数组,解析每个包引用。 - 对于每个包:
- 检查缓存目录中是否已有该包。没有则下载(npm install 或 git clone)。
- 读取包的
package.json,提取pi字段。 - 验证
pi字段中声明的每个路径是否存在。不存在的路径会给出警告但不阻塞启动。 - 将资源路径登记到对应索引中。
- 对于本地声明的 Extensions / Skills / Prompts / Themes:解析路径(展开
~、解析相对路径),验证文件存在性,登记到索引。 - 构建最终的资源表:合并所有来源,按优先级解决冲突。
- 按需加载 Skills:Skills 在启动时只加载名称和摘要(从
SKILL.md的 front matter 或首段提取),完整内容在 Agent 首次使用时才按需加载(渐进式披露)。 - 执行 Extensions:导入每个 Extension 的默认导出函数,传入
ExtensionAPI对象,完成工具注册和事件监听。
整个加载过程会输出详细日志(可通过 --debug 或 /debug 命令查看)。如果你安装了包但 Skills 或 Extensions 没有生效,建议先用 pi --debug 启动,查看日志中是否有路径解析错误或冲突覆盖警告。
10.6 发布到 npm
npm 是 Pi 包生态的主要分发渠道。将你的 Pi 包发布到 npm 上,全世界的 Pi 用户都可以通过 pi install npm:your-package 安装它。
10.6.1 发布前的检查清单
在 npm publish 之前,请逐项确认:
package.json的name字段以pi-开头(社区约定,见 10.9.1)。package.json中已正确设置version(初始版本建议0.1.0,遵循 semver)。pi字段存在且所有路径指向实际存在的文件。keywords中包含"pi"和"pi-package"。files字段(或.npmignore)正确配置,排除测试、开发配置等不需要发布的内容。peerDependencies中声明了对@earendil-works/pi-coding-agent的版本要求。- 在本地用
pi install ./path/to/your-package --no-save或npm link测试过,确认包能正常加载。 README.md中包含:包的用途、安装方式(pi install npm:your-package)、提供的资源列表(Skills / Extensions / Prompts / Themes)。
10.6.2 npm publish 流程
# 1. 确保你在包根目录
cd /path/to/pi-tool-my-pack
# 2. 登录 npm(如果尚未登录)
npm login
# 3. 检查哪些文件会被发布
npm pack --dry-run
# 4. 发布
npm publish
# 5. 验证
npm view pi-tool-my-pack
scope 包的发布:如果你的包使用了 scope(如 @myteam/pi-tools),首次发布时需要加上 --access public(scope 包默认是私有的):
npm publish --access public
10.6.3 版本管理
遵循语义化版本(Semantic Versioning / semver):
| 版本变化 | 示例 | 何时使用 |
|---|---|---|
| 补丁版本(Patch) | 1.0.0 → 1.0.1 |
Bug 修复,不影响 API |
| 次版本(Minor) | 1.0.0 → 1.1.0 |
新增 Skill / Extension / Theme,向后兼容 |
| 主版本(Major) | 1.0.0 → 2.0.0 |
不兼容的 API 变更,重命名 Skill,改变工具参数 |
便捷命令:
npm version patch # 1.0.0 → 1.0.1,自动 git tag
npm version minor # 1.0.0 → 1.1.0
npm version major # 1.0.0 → 2.0.0
npm version 命令会自动更新 package.json 中的版本号并创建 git tag,但不会自动 push。需要手动执行 git push --follow-tags。
10.6.4 发布注意事项
- 不要发布包含 API Key 或凭证的文件。检查
files字段和.npmignore,确保auth.json、.env、credentials.json等敏感文件不会被打包。 - Extension 中不要硬编码绝对路径。使用 Extension API 提供的路径解析函数获取配置文件、数据库等持久化资源的位置。
- 测试你的包在”干净环境”中是否可用。在另一个目录中执行
pi install npm:your-package --no-save,确认能正常加载和使用。 - 发布后更新 CHANGELOG。在 GitHub Release 中简述该版本的变更内容,让用户知道升级能得到什么。
- 遵循 npm 的最佳实践:
main字段指向入口文件(如果你的包也是可编程使用的库),types字段指向类型声明文件(如果你的包提供了 TypeScript 类型)。
10.7 从 Git 仓库安装
Git 源为 Pi 包的私有分发和开发中迭代提供了灵活性——你不需要发布到 npm 就能让团队成员使用你的包。
10.7.1 GitHub / GitLab / Bitbucket 支持
Pi 对三大 Git 托管平台提供原生支持:
# GitHub
pi install git:github.com/user/pi-tools
pi install git:github.com/user/pi-tools#develop
pi install git:github.com/user/pi-tools#v1.0.0
# GitLab
pi install git:gitlab.com/user/pi-tools
# Bitbucket
pi install git:bitbucket.org/user/pi-tools
Pi 内部会为每个平台自动补全正确的 git clone URL。对于 GitHub,完整的克隆命令等价于:
git clone https://github.com/user/pi-tools.git ~/.pi/packages/git-github.com-user-pi-tools/
10.7.2 私有仓库配置
安装私有仓库的 Pi 包需要认证。根据你的 Git 托管平台和认证方式,有以下几种方案:
方案 A:SSH 密钥(推荐)
为你的机器配置 SSH 密钥并将其添加到 GitHub / GitLab 账户中:
# 1. 生成 SSH 密钥(如果还没有)
ssh-keygen -t ed25519 -C "your-email@example.com"
# 2. 将公钥添加到 GitHub / GitLab
cat ~/.ssh/id_ed25519.pub
# 3. 测试 SSH 连接
ssh -T git@github.com
# 4. 使用 SSH 格式的 URL 安装
pi install git:git@github.com:your-org/pi-private-tools.git
Pi 执行 git clone 时会自动使用系统的 SSH 配置。
方案 B:HTTPS + Personal Access Token
如果你无法使用 SSH,可以通过 Personal Access Token 进行 HTTPS 认证:
# GitHub: 在 https://github.com/settings/tokens 生成 token
pi install git:https://<your-token>@github.com/your-org/pi-private-tools.git
# GitLab: 在 https://gitlab.com/-/profile/personal_access_tokens 生成 token
pi install git:https://oauth2:<your-token>@gitlab.com/your-org/pi-private-tools.git
安全警告:将 Token 直接写入包引用字符串意味着它会被明文记录在 settings.json 中。更安全的做法是使用 Git 的 credential helper:
# 配置 Git credential helper
git config --global credential.helper store
# 首次克隆时手动输入用户名和 Token
git clone https://github.com/your-org/pi-private-tools.git
# 输入用户名和 Token,Git 会记住
# 之后 Pi 的 git clone 也会复用这个凭证
pi install git:https://github.com/your-org/pi-private-tools.git
方案 C:自托管 Git 的 SSH 配置
如果你的组织使用自托管的 Git 服务(如 Gitea、Gogs、GitLab CE),在 ~/.ssh/config 中配置好主机别名和密钥:
# ~/.ssh/config
Host git.mycompany.com
HostName git.mycompany.com
User git
IdentityFile ~/.ssh/mycompany_rsa
然后使用对应的 URL 安装:
pi install git:git@git.mycompany.com:team/pi-tools.git
10.7.3 SSH 认证故障排查
如果 pi install git:... 报错 Permission denied (publickey):
- 确认 SSH 密钥已添加到 Git 平台:登录 GitHub → Settings → SSH and GPG Keys,确认密钥在列表中。
- 确认 SSH agent 正在运行且密钥已加载:
eval $(ssh-agent -s) ssh-add ~/.ssh/id_ed25519 - 测试 SSH 连接:
ssh -T git@github.com。正常应输出 “Hi! You've successfully authenticated..." - 确认 Git URL 格式正确:SSH URL 应以
git@开头(git@github.com:user/repo.git),而非https://。
10.8 Pi 配置中的包管理
10.8.1 settings.json 中 packages 数组
packages 字段已在第五章(5.6.1)中介绍过。这里从”包管理”的角度补充一些细节:
{
"packages": [
"pi-tool-react@0.2.0",
"npm:@scope/pi-utils@1.0.0",
"git:github.com/your-org/pi-private-tools.git#v2.0.0"
]
}
手动编辑 vs pi install 命令:你可以直接手动编辑 settings.json 在 packages 数组中添加包引用——Pi 在下次启动时会自动检测到新增条目并下载。但推荐使用 pi install 命令,因为它会:
- 立即解析包引用并下载
- 验证包格式和内容
- 给出即时的加载结果反馈
- 避免手动写错格式
数组的维护:当你添加或删除包时,注意数组中条目的 JSON 语法要求——每个字符串后要有逗号(除了最后一项),字符串要双引号包裹。
10.8.2 项目级 .pi/settings.json 配置
项目级包配置与全局配置的格式完全一致,但语义不同——它只对当前项目生效:
// 项目根目录/.pi/settings.json
{
"packages": [
"pi-skill-project-conventions@0.1.0",
"git:github.com/my-team/pi-internal-tools.git"
]
}
项目信任机制的作用:如第五章所述,Pi 默认不会自动加载项目级配置——你需要在使用该项目的 Pi 时确认信任。一旦信任,项目级 packages 会被追加到全局 packages 之后。
10.8.3 全局 ~/.pi/agent/settings.json 配置
全局 settings.json 中的 packages 定义了所有项目中都可用的包:
{
"packages": [
"pi-tool-shell-safety@1.0.0",
"pi-theme-nord@0.5.0",
"pi-skill-git-workflow@1.2.0"
]
}
全局包的管理原则:
- 保持全局包列表精简——只装你在每个项目中都会用到的通用包。
- 项目专有工具放在项目级
packages中。 - 定期执行
pi list检查全局包列表,移除不再使用的包。
10.9 最佳实践
10.9.1 包命名规范
社区约定 Pi 包的命名遵循以下规范:
| 类别 | 命名格式 | 示例 |
|---|---|---|
| 通用工具 | pi-tool-<功能> 或 pi-<功能> |
pi-tool-shell-safety、pi-git-helper |
| 技能 | pi-skill-<技能名> |
pi-skill-git-workflow、pi-skill-api-design |
| 主题 | pi-theme-<主题名> |
pi-theme-nord、pi-theme-solarized |
| 语言/框架专用 | pi-<语言>-<功能> |
pi-react-component-gen、pi-python-linter |
| 组织内部 | @<组织>/pi-<功能> |
@mycompany/pi-internal-tools |
为什么命名重要:npm 是一个扁平的命名空间——所有无 scope 的包共享 package-name。以 pi- 开头能:
- 让用户在 npm 上搜索
pi-时找到你的包(Pi 生态的社区 4600+ 包几乎全部以pi-开头)。 - 在
pi list输出中自然地排列在一起。 - 向潜在用户传达”这是一个 Pi 生态的包”。
10.9.2 包版本管理
在生产环境中,对包版本的管理建议如下:
- 全局包使用精确版本:
"pi-tool-shell-safety@1.0.0"而非"pi-tool-shell-safety"。这样pi update --all不会意外升级全局包——升级全局包可能影响你所有项目的 Pi 行为。 - 项目包可以适当宽松:如果项目包的 API 稳定、作者有良好的向后兼容记录,可以使用
@^1.0.0自动获取补丁和次版本更新。 - Git 包使用 tag 锁定:
git:github.com/user/repo#v1.0.0而非git:github.com/user/repo#main。主分支随时可能合并 breaking change。 - 在团队中统一版本:在团队的
AGENTS.md或项目文档中明确写出当前使用的各个包及其版本。新成员加入时,执行pi install即可获得一致的包环境。
10.9.3 团队共享
将 Pi 包作为团队知识资产共享的方式:
- 发布到 npm(公开或组织 scope):适合已经稳定、被多个项目使用的包。
- 通过私有 Git 仓库共享:适合仍在快速迭代的包、或包含组织敏感信息的包(如内部工具集、数据库 schema 变更 Skill)。
- 在项目仓库中内嵌
.pi/目录:如果某个包的资源量很小(就一个 Skill 文件和一个 Extension),直接放在项目.pi/目录下比发布成一个独立的包更简单。
推荐的分发策略:
| 资源量 | 稳定性 | 建议 |
|---|---|---|
| 1-2 个 Skill 或 1 个 Extension | 项目专属 | 放在项目 .pi/ 目录 |
| 3+ 个资源、多个项目共用 | 稳定 | 发布为 npm 包 |
| 3+ 个资源、仍在迭代 | 不稳定 | 通过私有 Git 仓库共享,用 tag 或分支分流 |
| 大型工具集(10+ 个资源) | 稳定 | 发布为组织 scope 下的 npm 包:@your-org/pi-kit |
10.9.4 安全注意事项
包系统引入了新的安全考量——你安装的包中的 Extension 代码会在你的机器上以 Pi 进程的权限运行。以下是几条关键的防护措施:
- 审查包的来源:
- 安装前查看包的 npm 页面:下载量、最后发布时间、GitHub 仓库的 stars 和活跃度。
- 阅读包的
README.md了解其功能范围。如果包声称只提供一个 Skill 但其package.json声明了多个 Extensions,保持警惕。 - 查看包的 GitHub Issues 区,了解是否有安全问题被报告。
- 审查 Extensions 代码:
- Extension 是 TypeScript/JavaScript 代码,拥有和 Pi 进程同等的文件系统权限。安装包后,尽快阅读其 Extensions 源码(
~/.pi/packages/<package>/extensions/)。 - 重点关注:Extension 是否调用了
bash工具、是否监听和修改了tool_call事件、是否注册了网络请求相关的工具。
- Extension 是 TypeScript/JavaScript 代码,拥有和 Pi 进程同等的文件系统权限。安装包后,尽快阅读其 Extensions 源码(
- 使用 Git 的 commit/tag 而非动态分支:
git:github.com/user/repo#v1.0.0指向一个不可变的 tag——你审查过一次代码后,这个 tag 的代码不会变。git:github.com/user/repo#main是可变的——你审查过的代码可能在下一次pi update后被替换为包含恶意代码的新版本。
- 最小权限原则:
- 只安装你真正需要的包。不要因为”看起来有用”而安装一个你实际上不会用到的包。
- 对于不需要读写文件的包(如纯主题包),Pi 默认的 YOLO 模式风险较低——主题没有可执行代码。
- 对于包含 Extensions 的包,这些 Extensions 可以执行任意 shell 命令、读写任意文件——在”完全可信”的项目之外使用它们时,请通过 Docker 容器或 Gondolin 微 VM 运行 Pi(见第十二章:容器化与安全沙箱)。
- CI/CD 中的包管理:
- 在 CI 流水线中使用的包,尽量锁定精确版本。
- 在 CI 中运行
pi list并对比期望的包列表,确保没有意外引入或遗漏的包。 - 考虑在 CI 流水线中加入
npm audit步骤,检测包依赖中的已知漏洞(Pi 包本身是 npm 包,其依赖链也受 npm audit 覆盖)。
10.10 本章小结
本章完整覆盖了 Pi 包生态系统的方方面面:
- 包系统概述:一个 Pi 包是在
package.json中声明了pi字段的普通 npm 包或 Git 仓库,可包含 Extensions、Skills、Prompts、Themes 四种资源。安装源为 npm 和 Git,配置在settings.json的packages数组中,分全局和项目两级。 - 安装包:
pi install <引用>支持 npm 包(含 scope、版本号、semver 范围)和 Git 仓库(含分支/tag/commit)两种格式。安装流程依次为下载、读取pi字段、注册资源、更新配置。 - 管理包:
pi list列出已安装包,pi remove卸载并注销资源,pi update更新 Pi 本体(pi update)、同时更新所有包(pi update --all)、或只更新指定包。 - 创建可分发包:核心是在
package.json中添加pi字段,声明extensions、skills、prompts、themes的路径。本章提供了一个包含全部四种资源的完整package.json示例。 - 资源加载机制:资源按固定的优先级顺序加载——内置工具 → 全局资源 → 全局包 → 项目资源 → 项目包 → CLI
-e扩展。同名 Skills 和 Extensions 后加载的覆盖先加载的,Prompt Templates 则全部追加不覆盖。Skills 采用渐进式披露——启动时仅加载名称和摘要。 - 发布到 npm:遵循
npm publish标准流程,推荐版本号遵循 semver,keywords中包含"pi"和"pi-package"。 - 从 Git 仓库安装:支持 GitHub / GitLab / Bitbucket 和自托管 Git。私有仓库可通过 SSH 密钥、HTTPS Personal Access Token 或 Git credential helper 认证。
- 配置中的包管理:全局
packages放通用工具,项目packages放专属工具。手动编辑和pi install命令两种方式均可,推荐后者。 - 最佳实践:包命名以
pi-开头、锁定精确版本、按资源量和稳定性选择分发策略(项目内嵌 / npm 发布 / 私有 Git)、安装前审查包来源和 Extensions 代码、在 CI 中锁定版本。
下一章:第十一章:SDK 嵌入与 RPC 模式 将讲解如何通过 SDK 将 Pi 的 Agent 能力嵌入到自己的 Node.js 应用中,以及通过 RPC 协议与非 Node.js 程序集成。
提示:如果你在包管理中遇到问题——包安装了但不生效、Skill 没有出现、Extension 报错——先用
pi --debug启动 Pi 并检查启动日志中与packages相关的输出。九成问题可以通过日志中的路径解析错误或冲突覆盖警告定位。