现在位置: 首页 > Pi Agent > 正文

Pi Agent 扩展入门

Extensions(扩展)是 Pi Agent 最强大的定制方式——你可以用 TypeScript 编写自定义工具、命令和事件监听器。


什么是 Extension

Extension 是一个 TypeScript 模块,它可以做下面这些事。

能力说明典型用途
注册自定义工具为 AI 增加可调用的新功能查询内部系统、读写数据库、封装第三方 API
注册自定义命令用户通过 /命令名 调用封装常用提示词、一键执行固定流程
监听生命周期事件在 AI 工作的各个阶段插入逻辑拦截危险命令、注入上下文、记录审计日志
注册键盘快捷键在终端界面中绑定按键快速切换面板、触发高频操作
展示自定义 UI在终端中渲染交互界面进度条、列表、确认弹窗等组件

第一个 Extension:Hello World

下面这个扩展一次性演示了事件监听、工具注册和命令注册三种能力。

实例

// 文件路径:~/.pi/agent/extensions/my-extension.ts
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";

export default function (pi: ExtensionAPI) {
  // 在会话启动时触发
  pi.on("session_start", async (_event, ctx) => {
    ctx.ui.notify("我的扩展已加载!", "info");
  });

  // 拦截危险命令
  pi.on("tool_call", async (event, ctx) => {
    if (event.toolName === "bash"
        && event.input.command?.includes("rm -rf")) {
      const ok = await ctx.ui.confirm("危险操作!", "确认执行 rm -rf?");
      if (!ok) return { block: true, reason: "用户阻止" };
    }
  });

  // 注册一个自定义工具
  pi.registerTool({
    name: "greet",
    label: "打招呼",
    description: "向指定的人打招呼",
    parameters: Type.Object({
      name: Type.String({ description: "要打招呼的人名" }),
    }),
    async execute(toolCallId, params) {
      return {
        content: [{ type: "text", text: `你好,${params.name}!` }],
        details: {},
      };
    },
  });

  // 注册一个自定义命令
  pi.registerCommand("hello", {
    description: "向世界问好",
    handler: async (args, ctx) => {
      ctx.ui.notify(`你好 ${args || "世界"}!`, "info");
    },
  });
}

第 13 行的事件判断用了最简单的 event.toolName 写法,也可以用类型守卫把 event 收窄成具体事件类型,见《事件系统》一章。

用 pi -e 参数启动 Pi Agent,即可加载这个扩展进行测试:

$ pi -e ~/.pi/agent/extensions/my-extension.ts
Loading extension: /Users/runoob/.pi/agent/extensions/my-extension.ts
my-extension 加载成功(1 个工具、1 个命令)
我的扩展已加载!

最后一行就是 session_start 事件里 ctx.ui.notify 弹出的通知,说明扩展已经生效。


扩展的加载位置

Pi Agent 会在固定的几个目录中自动发现扩展,也可以显式指定路径。

位置作用范围说明
~/.pi/agent/extensions/*.ts全局对所有项目生效,自动发现
~/.pi/agent/extensions/*/index.ts全局子目录中有 index.ts 的扩展
.pi/extensions/*.ts项目仅当前项目,需项目信任
.pi/extensions/*/index.ts项目项目子目录扩展

除了命令行参数,也可以在 settings.json 的 extensions 字段中写入路径数组,目录路径会按扩展目录规则展开。

实例

{
  "extensions": [
    "/Users/runoob/.pi/agent/extensions/quick-notify.ts",
    "/Users/runoob/.pi/agent/extensions/team-tools"
  ]
}

第一个元素是单文件扩展,第二个是目录扩展,实际使用时替换成你自己的扩展位置即可。


Extension 文件结构

Extension 可以是单个 .ts 文件,也可以是一个带入口文件的目录。

单文件扩展

最简单的形式,适合小型扩展:

~/.pi/agent/extensions/
└── my-extension.ts

目录扩展(含 index.ts)

适合多文件扩展:

~/.pi/agent/extensions/
└── my-extension/
    ├── index.ts        # 入口文件(导出默认函数)
    ├── tools.ts        # 辅助模块
    └── utils.ts        # 工具函数

带依赖的扩展

适合需要 npm 包的大型扩展:

~/.pi/agent/extensions/
└── my-extension/
    ├── package.json    # 声明依赖和入口
    ├── node_modules/   # npm install 后生成
    └── src/
        └── index.ts

扩展以你的系统权限运行,可以执行任意代码。

只安装来自可信来源的扩展


扩展的加载机制

扩展通过 jiti 加载,TypeScript 代码无需编译即可直接运行。

如果你的工厂函数返回 Promise,Pi Agent 会在继续启动流程之前等待它完成。

这意味着你可以在扩展初始化时做异步操作,比如获取远程配置。


可用 Import 包

这些包由 Pi Agent 内置提供,扩展中可以直接 import。

包名用途
@earendil-works/pi-coding-agent扩展类型(ExtensionAPI、ExtensionContext 和各种事件类型)
typebox工具参数的 Schema 定义
@earendil-works/pi-aiAI 工具(如 StringEnum,用于 Google 兼容的枚举)
@earendil-works/pi-tuiTUI 组件,用于自定义渲染

这些内置核心包由 Pi 自身提供,列为 peerDependencies 即可,不要打包进你的扩展。

自有运行时依赖则必须写进 package.json 的 dependencies 字段。

Pi 安装扩展包时默认执行生产安装(npm install --omit=dev),devDependencies 在运行时不可用。

其他 npm 包也可以使用——在扩展目录中添加 package.json 并运行 npm install 即可。


开发提示

这几条经验能帮你少走弯路,尤其是加载方式的选择。

做法原因
把扩展放在 ~/.pi/agent/extensions/ 目录中支持 /reload 热重载,修改后无需重启会话
pi -e ./path.ts 仅用于临时测试长期使用应把扩展放入 ~/.pi/agent/extensions/ 目录,由 Pi Agent 自动发现
不要在扩展工厂函数中启动后台资源进程、socket、文件监听器、定时器等应在 session_start 事件中启动
在 session_shutdown 事件中清理会话级资源避免切换会话后残留句柄与监听器

进一步集成

除了编写扩展,官方还提供两条编程接入路线。

Node 或 TypeScript 项目可以直接使用 @earendil-works/pi-coding-agent 导出的 SDK:用 createAgentSession 创建会话、subscribe 订阅事件、prompt 发送消息。

这种方式与 Pi 同进程运行,类型定义完整。

跨语言调用或需要进程隔离时,改用 pi --mode rpc,通过 JSONL 协议与 Pi 进程通信。

两条路线与扩展共用同一套事件模型,选哪条主要看你是否需要独立进程。