pi Pi 包完全指南:pi install 安装管理、npm/git 源、pi manifest 与资源过滤实战
本文围绕 pi 的包(Pi Package)机制展开:讲解如何用 pi install / pi remove / pi list / pi update 管理来自 npm、git 和本地的包,如何编写 package.json 中的 pi manifest 来发布自己的包,以及如何使用 settings 中的过滤语法精确控制包内资源的加载。读完本文,你可以独立制作、安装、过滤和升级 pi 包,并理解 pi 包管理器在源码层面的安装路径约定、去重规则与更新语义。
安全提示:Pi 包拥有完整的系统访问权限。扩展(extensions)会执行任意代码,而技能(skills)可以指示模型执行包括运行可执行程序在内的任意操作。安装第三方包之前,务必审查其源代码。
一、安装与管理:install、remove、list、update 四个核心命令
pi 包通过四个命令完成全生命周期管理。默认情况下,install 和 remove 会写入用户级设置文件 ~/.pi/agent/settings.json;加上 -l 参数则写入项目级设置 .pi/settings.json。项目级设置可以随仓库共享给团队,pi 在项目被信任(trust)后启动时会自动安装 settings 中声明但本地缺失的包——这一点在源码 package-manager.ts 的 resolve() 中得到印证:解析阶段发现 npm/git 源本地不存在时会回调 onMissing 并触发安装。
pi install npm:@foo/bar@1.0.0
pi install git:github.com/user/repo@v1
pi install https://github.com/user/repo # raw URLs work too
pi install /absolute/path/to/package
pi install ./relative/path/to/package
pi remove npm:@foo/bar
pi list # show installed packages from settings
pi update # update pi only
pi update --all # update pi, update packages, and reconcile pinned git refs
pi update --extensions # update packages and reconcile pinned git refs only
pi update --models # refresh model catalogs only
pi update --self # update pi only
pi update --self --force # reinstall pi even if current
pi update npm:@foo/bar # update one package
pi update --extension npm:@foo/bar
这些命令负责 pi 包的管理,其中 pi update 还可以顺带更新 pi CLI 本身。从 package-manager-cli.ts 的实现可以看到几个关键行为:
- 更新目标解析:
update命令的参数解析器(parsePackageCommand)会校验目标冲突,例如--all不能与--self、--extensions、--models、--extension或位置参数同时使用,pi/self可作为别名表示"只更新 pi 自身"。 - 托管安装(experimental installer-managed)的更新语义:对由官方安装器托管的安装,
pi update会把精确校验过的版本安装到一个带 lockfile 的 staging 目录中,验证通过后才激活;如果更新失败,当前版本保持完好。托管安装不支持--force——源码中遇到该组合会直接报错并提示重新运行安装器来修复(runManagedSelfUpdate与verifyManagedRelease会先执行npm ci、再运行--version冒烟校验)。 - 非 npm 包更新被跳过时的提示:直接运行
pi update(不指定目标)只更新 pi 自身,此时会提示 "Extensions are skipped. Run pi update --extensions to update extensions." --models的实现:pi update --models通过ModelRuntime.refresh({ allowNetwork: true, force: true })强制刷新模型目录,内置 15 秒超时保护。
卸载 pi 本身的方式请参考 Quickstart 文档。
不落地安装:-e / --extension 临时试用
想在不安装的情况下试用一个包,可以用 --extension(或 -e)。它会把包装载到临时目录,仅在当前这一次运行中生效:
pi -e npm:@foo/bar
pi -e git:github.com/user/repo
从源码看,临时安装的路径由 getExtensionTempFolder 与 getTemporaryDir 生成,位于 ~/.pi/agent/tmp/extensions/ 下,并用源标识的 SHA-256 哈希前 8 位命名,权限固定为 0700——即每次运行互不污染,也永远不会写入 settings。
交互式开关资源:pi config
使用 pi config 可以从图形化 TUI 中启用/禁用已安装包与本地目录中的扩展、技能、提示模板和主题:
pi config从全局设置(~/.pi/agent/settings.json)开始,在 TUI 中按 Tab 键可在全局与项目本地模式之间切换;pi config -l则直接以项目覆盖配置.pi/settings.json为起点启动,全局继承来的资源会以暗淡(dimed)样式显示。
该命令的实现在 package-manager-cli.ts 的 handleConfigCommand 中:它会分别解析全局与项目两套 ResolvedPaths 交给 selectConfig 组件,且当项目未被信任时会拒绝 -l 模式,要求使用 --approve 显式批准。
二、包来源(Package Sources):npm、git 与本地路径
pi 在 settings 与 pi install 中接受三种来源类型,解析逻辑集中在 DefaultPackageManager.parseSource()(见 package-manager.ts):先尝试 npm: 前缀,再判断是否为本地路径,最后回退到 git URL 解析。
npm 来源
npm:@scope/pkg@1.2.3
npm:pkg
- 精确版本会被钉住(pinned),包更新时跳过。源码中
pinned的判定是semver.valid(version) !== null,即只有完全版本号(如1.0.0)算 pinned,^1.0.0、latest等范围不算;updateConfiguredSources中if (!parsed.pinned)才进入 npm 更新候选。 - 用户级安装位于
~/.pi/agent/npm/,项目级安装位于.pi/npm/。对应源码getNpmInstallRoot:project作用域为<cwd>/.pi/npm,用户作用域为<agentDir>/npm(agentDir 即~/.pi/agent)。 - 在
settings.json中设置npmCommand可以把 npm 包的查询与安装操作固定到某个包装命令(如mise或asdf)。这是settings-manager.ts中定义的全局设置项(argv 形式),源码的getNpmCommand()会把数组第一个元素作为可执行程序、其余作为参数拼接执行,例如:
{
"npmCommand": ["mise", "exec", "node@20", "--", "npm"]
}
一个值得注意的细节:源码 getNpmInstallArgs 会根据 npmCommand 解析出的实际包管理器名称(npm/pnpm/bun)选择不同的安装参数——npm 用 --legacy-peer-deps,pnpm 关闭 auto-install-peers 与严格 peer 检查,bun 用 --omit=peer。原因是 pi 包运行在 pi 进程内,pi 核心 API 通过 loader 别名/虚拟模块注入,不允许包管理器自行解决 @earendil-works/pi-* 的 peer 依赖。
git 来源
git:github.com/user/repo@v1
git:git@github.com:user/repo@v1
https://github.com/user/repo@v1
ssh://git@github.com/user/repo@v1
- 不带
git:前缀时,只接受带协议的 URL(https://、http://、ssh://、git://); - 带
git:前缀时接受简写形式,包括github.com/user/repo和git@github.com:user/repo; - HTTPS 与 SSH 均支持;SSH URL 自动使用你配置的 SSH 密钥(遵循
~/.ssh/config); - 非交互运行(如 CI)可以设置
GIT_TERMINAL_PROMPT=0禁用凭据提示,并设置GIT_SSH_COMMAND(例如ssh -o BatchMode=yes -o ConnectTimeout=5)以快速失败。源码中所有 git 远端命令(runGitRemoteCommand)都会默认带上GIT_TERMINAL_PROMPT=0; - ref 是钉住的 tag 或 commit。
pi update --extensions/--all不会把它移动到更新的 ref,但会把已有克隆对齐(reconcile)到配置的 ref。因此移动 ref 需要显式执行pi install git:host/user/repo@new-ref,这会同时更新 settings 并让现有包迁到新的 pinned ref; - 克隆目录:全局为
~/.pi/agent/git/<host>/<path>,项目为.pi/git/<host>/<path>(对应getGitInstallRoot/getGitInstallPath); - 当 reconcile 改变了 checkout 时,pi 会 reset 并清理该克隆,若存在
package.json则执行npm install(源码getGitDependencyInstallArgs:未配置npmCommand时为install --omit=dev,配置了 wrapper 时退化为纯install以兼容)。
SSH 示例:
# git@host:path shorthand (requires git: prefix)
pi install git:git@github.com:user/repo
# ssh:// protocol format
pi install ssh://git@github.com/user/repo
# With version ref
pi install git:git@github.com:user/repo@v1.0.0
本地路径来源
/absolute/path/to/package
./relative/path/to/package
本地路径指向磁盘上的文件或目录,写入 settings 时不复制,只是登记。相对路径会相对于它所在的那个 settings 文件解析(源码 normalizePackageSourceForSettings 甚至会把绝对路径规约成相对 baseDir 的相对形式再写入)。如果路径是文件,它会被当作单个扩展加载;如果是目录,pi 按包规则(见下文"包结构")加载其中的资源。
三、创建自己的 Pi 包
在 package.json 中添加 pi manifest,或直接使用约定目录。为了可被检索,建议包含 pi-package 关键字:
{
"name": "my-package",
"keywords": ["pi-package"],
"pi": {
"extensions": ["./extensions"],
"skills": ["./skills"],
"prompts": ["./prompts"],
"themes": ["./themes"]
}
}
manifest 的解析实现在 pi-manifest.ts:readPiManifest() 只会接受 extensions、skills、prompts、themes 四个字段,且每个字段必须是纯字符串数组,非法字段会被静默忽略(整个 manifest 解析失败则返回 null,回退到约定目录发现)。
关于路径的几个规则:
- 路径相对于包根目录;
- 数组支持 glob 模式与
!exclusions; - 正向 manifest glob 只会发现可见路径,按字典序排列(源码
expandPackageGlob使用globSync,且显式过滤掉任何路径段以.开头的结果); - 需要包含点前缀(dot-prefixed)路径时,直接以精确路径列出;
- 如果某个 glob 需要"穿过"一个符号链接才能到达资源,直接列出该符号链接的资源根目录(符号链接目录不会被自动穿透)。
画廊元数据(Gallery Metadata)
pi 的包画廊会展示打了 pi-package 标签的包。为包添加 video 或 image 字段可以展示预览:
{
"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 优先。
(注意:video / image 是供画廊展示使用的字段,不属于 readPiManifest 识别的资源字段,不影响本地加载。)
四、包结构:约定目录自动发现
如果包里没有 pi manifest,pi 从以下约定目录自动发现资源:
| 目录 | 加载规则 |
|---|---|
extensions/ |
加载 .ts 与 .js 文件 |
skills/ |
递归查找 SKILL.md 文件夹,并加载顶层 .md 文件作为技能 |
prompts/ |
加载 .md 文件 |
themes/ |
加载 .json 文件 |
源码 package-manager.ts 中的 FILE_PATTERNS 常量与上表完全一致(extensions: /\.(ts|js)$/、skills/prompts: /\.md$/、themes: /\.json$/)。技能发现由 collectSkillEntries 实现:目录中直接存在 SKILL.md 时该目录即构成一个技能(不再向下递归);否则顶层的 .md 文件会被收集为技能,子目录继续递归查找 SKILL.md。
自动发现时还有两条隐含规则,来自目录扫描代码:
- 点前缀目录/文件与
node_modules一律跳过(除非通过 manifest 精确列出); - 各目录内的
.gitignore、.ignore、.fdignore规则会被读取并应用于资源收集(addIgnoreRules),所以你可以用 ignore 文件排除生成物。
扩展目录的发现比其它三类更"智能":collectAutoExtensionEntries 会先检查目录自身——若其 package.json 的 pi.extensions 非空,或存在 index.ts / index.js,则以该入口为准;否则才递归扫描目录下的 .ts / .js 文件(子目录若自带 index.ts 也会作为独立扩展入口)。
五、依赖(Dependencies)管理
第三方运行时依赖应写在 package.json 的 dependencies 中;不注册扩展/技能/提示/主题的依赖同样属于 dependencies。因为 pi 从 npm 或 git 安装包时会执行 npm install,这些依赖会被自动安装。git 包的默认安装是生产依赖安装(npm install --omit=dev),所以**devDependencies 在运行期不可用**——只有配置了 npmCommand 时 git 包才退化为普通 install。
pi 自身为扩展与技能内置捆绑(core bundled)了一组基础包。如果你 import 了下列任何一项,应在 peerDependencies 中以 "*" 范围声明,并且不要打包进自己的 tarball:
@earendil-works/pi-ai@earendil-works/pi-agent-core@earendil-works/pi-coding-agent@earendil-works/pi-tuitypebox
其他 pi 包则必须打进你的 tarball:加入 dependencies 与 bundledDependencies,然后通过 node_modules/ 路径引用其资源。因为 pi 以相互独立的模块根(module roots)加载各个包,不同包的安装之间不会发生模块碰撞或共享。
示例:
{
"dependencies": {
"shitty-extensions": "^1.0.1"
},
"bundledDependencies": ["shitty-extensions"],
"pi": {
"extensions": ["extensions", "node_modules/shitty-extensions/extensions"],
"skills": ["skills", "node_modules/shitty-extensions/skills"]
}
}
六、包过滤(Package Filtering):精确控制加载范围
在 settings 中使用对象形式的包条目,可以为每个资源类型指定过滤模式:
{
"packages": [
"npm:simple-pkg",
{
"source": "npm:my-package",
"extensions": ["extensions/*.ts", "!extensions/legacy.ts"],
"skills": [],
"prompts": ["prompts/review.md"],
"themes": ["+themes/legacy.json"]
}
]
}
其中 +path 与 -path 是相对包根目录的精确路径。过滤语义汇总:
| 写法 | 含义 |
|---|---|
| 省略某个 key | 加载该类型的全部资源 |
[] |
一个都不加载 |
!pattern |
排除匹配项(glob) |
+path |
强制包含一个精确路径(可覆盖排除) |
-path |
强制排除一个精确路径(优先级最高) |
| 普通 glob / 精确路径 | 包含匹配项 |
autoload: false |
该条目改为"从零开始、只应用显式模式"的增量模式(见下文) |
过滤叠加在 manifest 之上:它们只能收窄 manifest 已允许的范围,不能引入 manifest 未声明的资源。这一流程在源码 applyPatterns() 中分四步执行:①应用 include 模式(无 include 则全量)→ ②应用 ! 排除 → ③+ 强制包含(从全量中捞回)→ ④- 强制排除(最后生效)。精确匹配(matchesAnyExactPattern)会剥掉 ./ 前缀后比较,对技能资源还支持以 SKILL.md 的父目录路径/名称作为匹配目标,因此你可以按"技能文件夹"粒度过滤技能。
七、作用域与去重(Scope and Deduplication)
包可以同时出现在全局(用户)设置与项目设置中。去重与身份(identity)规则在 dedupePackages() 与 getPackageIdentity() 中实现,规则如下:
身份判定(用于识别"同一个包"):
- npm:包名(
npm:<name>); - git:去掉 ref 的仓库 URL,即
git:<host>/<path>——因此同一仓库的 SSH 与 HTTPS 两种写法被视为同一包; - local:解析后的绝对路径。
合并规则:
- 同一包同时出现在全局与项目设置时,项目条目胜出;
- 例外:当项目条目带有
autoload: false时,它不是替换全局条目,而是作为增量(delta)叠加在全局条目之上——全局条目继续加载,项目条目只贡献其显式声明的资源过滤。源码中该逻辑由findAutoloadDeltaBase实现:project 作用域、对象形式、autoload === false三者同时满足时,会定位到同身份的全局条目作为 delta 基座。
这一机制适合"团队共享一份基础包配置,个别项目在本地追加或收窄"的场景:项目里写 { "source": "npm:xxx", "autoload": false, "extensions": ["extensions/review.ts"] } 即可在全局加载之上精准追加一个扩展,而不必把其余资源全部排除。
八、源码导读:包管理器的关键调用链
如果你需要进一步深入,以下路径串联了 pi 包机制的完整实现:
- 命令行入口与参数解析:package-manager-cli.ts 中的
handlePackageCommand(install/remove/list/update)与handleConfigCommand(pi config); - 核心包管理器:package-manager.ts 中的
DefaultPackageManager——parseSource()(三种来源解析)、installAndPersist/removeAndPersist(安装并写入 settings)、updateConfiguredSources()(npm 跳过 pinned 精确版本、git 对齐 pinned ref)、dedupePackages()(作用域去重)、applyPatterns()(过滤四步法); - manifest 解析:pi-manifest.ts 中的
readPiManifest(); - 全局设置项(如
npmCommand):settings-manager.ts; - 扩展/技能/主题等资源的类型定义与加载约定可继续参考 extensions 文档 与 skills 文档。
补充一个运维技巧:设置环境变量 PI_OFFLINE=1(或 true/yes)可进入离线模式——updateConfiguredSources、checkForAvailableUpdates 与自动安装逻辑都会短路跳过网络操作,适合在断网环境中使用已缓存的包。
小结
pi 包机制 = 四种资源(extensions / skills / prompts / themes)+ 三种来源(npm / git / local)+ 两级作用域(user / project)。manifest 声明资源、settings 过滤收窄、pinned 版本/ref 保证可复现、项目级 autoload: false 提供增量叠加——理解这四层之后,pi install、pi update、pi config 的所有行为都可以从 package-manager.ts 的源码中找到一一对应的实现。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00