pi-map_
← ~/guides

扩展入门 101

最后更新: 2026年7月27日

$ pi –extend

Pi 的设计哲学是保持内核小巧,把工作流相关的行为交给扩展。Pi 的扩展不是沙箱市场里那种插件——它是一个以你的完整系统权限运行在 Pi 进程内的 TypeScript 模块,可以订阅生命周期事件、注册 LLM 可调用的工具、添加斜杠命令、渲染自定义 UI、拦截工具调用。本指南依据仓库中的 docs/extensions.md 介绍真实机制。

扩展是什么

扩展是一个 .ts 文件,其默认导出是一个工厂函数,接收一个 ExtensionAPI 对象(惯例命名为 pi)。Pi 使用 jiti 加载扩展,因此 TypeScript 无需编译即可运行:

import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";

export default function (pi: ExtensionAPI) {
  // 订阅事件,注册工具/命令/快捷键/CLI 标志
}

工厂函数可以是 async;Pi 会等待它完成后才继续启动(先于 session_start,也先于提供商注册的冲刷)。不要在工厂函数里启动长期存活的后台资源——把它们推迟到 session_start,并在 session_shutdown 处理器中关闭。

扩展放在哪里

自动发现的位置(这些位置的扩展可用 /reload 热重载):

位置 作用域
~/.pi/agent/extensions/*.ts 全局
~/.pi/agent/extensions/*/index.ts 全局(目录形式)
.pi/extensions/*.ts 项目级
.pi/extensions/*/index.ts 项目级(目录形式)

项目级扩展只在项目被信任后加载(见信任提示或 /trust)。快速测试时用 pi -e ./path.ts——可重复,并可与 --no-extensions 组合以隔离环境:

pi --no-extensions -e ./my-extension.ts

扩展也可以作为 pi 包从 npm 或 git 分享(pi install <source>),额外路径可以列在 settings.jsonpackages / extensions 字段中。如果扩展需要 npm 依赖,在旁边放一个 package.json,运行 npm install,即可自动解析 node_modules/ 中的导入。

安全提示: 扩展以你的权限执行任意代码。只安装你信任的来源。

扩展能做什么

  • pi.on(event, handler) —— 订阅生命周期事件;许多处理器可以阻断、修改或注入内容
  • pi.registerTool({...}) —— 添加 LLM 可调用的工具(注册与内置工具同名即覆盖)
  • pi.registerCommand("name", {...}) —— 添加 /name 斜杠命令
  • pi.registerShortcut(...)pi.registerFlag(...) —— 键位绑定与 CLI 标志
  • pi.registerProvider(...) —— 添加自定义模型提供商
  • pi.appendEntry(...) —— 将状态持久化到会话文件
  • ctx.ui —— selectconfirminputnotify、用 custom() 构建完整 TUI 组件、setStatussetWidget
  • 工具调用/结果与消息的自定义渲染器

事件生命周期

事件与智能体循环一一对应。核心序列:

pi 启动
  ├─► project_trust          (仅用户/全局扩展参与)
  ├─► session_start          { reason: "startup" }
  └─► resources_discover
用户发送提示
  ├─► input                  (可拦截/转换)
  ├─► before_agent_start     (可注入消息、修改系统提示词)
  ├─► agent_start
  │     每个 turn:
  │       turn_start → context(可修改消息)
  │       → before_provider_request → after_provider_response
  │       → tool_execution_start → tool_call(可阻断)
  │       → tool_result(可修改)→ tool_execution_end → turn_end
  ├─► agent_end
  └─► agent_settled

会话操作有自己的事件:/new/resumesession_before_switchsession_shutdownsession_start/compactsession_before_compactsession_compact;模型切换 → model_selectthinking_level_select;退出 → session_shutdown

最小可运行示例

改编自官方 quick start。保存为 ~/.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("Extension loaded!", "info");
  });

  // 拦截危险工具调用
  pi.on("tool_call", async (event, ctx) => {
    if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) {
      const ok = await ctx.ui.confirm("Dangerous!", "Allow rm -rf?");
      if (!ok) return { block: true, reason: "Blocked by user" };
    }
  });

  // 注册一个 LLM 可调用的自定义工具
  pi.registerTool({
    name: "greet",
    label: "Greet",
    description: "Greet someone by name",
    parameters: Type.Object({
      name: Type.String({ description: "Name to greet" }),
    }),
    async execute(toolCallId, params, signal, onUpdate, ctx) {
      return {
        content: [{ type: "text", text: `Hello, ${params.name}!` }],
        details: {},
      };
    },
  });

  // 注册 /hello 命令
  pi.registerCommand("hello", {
    description: "Say hello",
    handler: async (args, ctx) => {
      ctx.ui.notify(`Hello ${args || "world"}!`, "info");
    },
  });
}

重启 Pi(或 /reload)后:模型可以调用 greet 工具,/hello world 可用,任何 rm -rf 尝试都会触发确认对话框。

扩展中可用的导入:@earendil-works/pi-coding-agent(类型)、typebox(参数 schema)、@earendil-works/pi-ai@earendil-works/pi-tui(自定义 UI)、Node.js 内置模块,以及本地 node_modules/ 中的任何包。

值得研读的真实示例

仓库在 packages/coding-agent/examples/extensions/ 中附带了数十个可运行的扩展。推荐入口:

  • hello.ts —— 最小工具注册
  • confirm-destructive.ts —— 权限门模式
  • git-checkpoint.ts —— 每个 turn 做 stash,分支切换时恢复
  • question.ts / questionnaire.ts —— 通过 ui.select / ui.custom 构建交互式工具
  • todo.ts —— 带会话持久化的有状态工具
  • custom-compaction.ts —— 替换压缩摘要逻辑
  • summarize.ts —— 会话总结命令
  • snake.ts —— 在 TUI 里做游戏,何乐而不为

按照官方文档的说法,写扩展最快的方式是:pi 自己会写扩展——直接让它为你的场景构建一个。

延伸阅读