Pi Extensions 进阶:从安装社区扩展到自己定制

前一篇笔记讲了 extension 是什么、有什么示例、怎么写。这篇记录实际使用——怎么从官方和社区安装、以及怎么根据需求自己改造。

一、先自己写个简单的:show-system-prompt

动机

Agent 的系统提示词由多个来源拼合而成——角色定位、工具列表、AGENTS.md、项目上下文、技能清单。Claude/Openclaw 这样的 Agent 内置系统提示词就能吃掉几千个 token。但是像 Pi 这种我们可以轻松写一个 Extention 调试提示词效果,看一眼"LLM 当前实际收到的 prompt 到底是什么"。一个 /system-prompt 命令,把完整内容打印到终端。写

主要就两步:

捕获提示词。 通过 before_agent_start 事件捕获 Pi 发给 LLM 的完整 system prompt,存到变量里。这是"post-chained"的值——即所有 extension 修改之后、实际发送之前的版本。如果命令被调用时还没有 turn 跑过,就降级用 ctx.getSystemPrompt()。

全屏覆盖层显示。 用 ctx.ui.custom() 打开一个覆盖层,渲染 prompt 全文。handleInput 监听 Escape 或 q 关闭,render 用 wrapTextWithAnsi 处理终端宽度换行。加了宽度缓存,只在终端尺寸变化时重新计算。

示例代码

import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Key, matchesKey, wrapTextWithAnsi } from "@earendil-works/pi-tui";

export default function (pi: ExtensionAPI) {
  let lastPrompt: string | undefined;

  pi.on("before_agent_start", (event) => {
    lastPrompt = event.systemPrompt;
  });

  pi.registerCommand("system-prompt", {
    description: "Print the current session's system prompt",
    handler: async (_args, ctx) => {
      const prompt = lastPrompt ?? ctx.getSystemPrompt();

      if (ctx.mode !== "tui") {
        ctx.ui.notify(prompt, "info");
        return;
      }

      await ctx.ui.custom<void>((_tui, _theme, _kb, done) => {
        let cachedWidth = 0;
        let cachedLines: string[] = [];

        return {
          render(width: number): string[] {
            if (width !== cachedWidth) {
              cachedWidth = width;
              cachedLines = wrapTextWithAnsi(prompt, width);
            }
            return cachedLines;
          },
          handleInput(data: string): void {
            if (matchesKey(data, Key.escape) || data === "q") done();
          },
          invalidate(): void { cachedWidth = 0; },
        };
      });
    },
  });
}

结果

输入 /system-prompt 后,终端全屏显示 Pi 当前 session 的完整系统提示词:

You are an expert coding assistant operating inside pi,
a coding agent harness. You help users by reading files,
executing commands, editing code, and writing new files.

Available tools:
- read: Read file contents
- bash: Execute bash commands
- edit: Make precise file edits
- write: Create or overwrite files

<project_context>
  <project_instructions path="/path/to/AGENTS.md">
    ## Behavioral Guidelines
    1. Think Before Coding
    2. Simplicity First
    ...
  </project_instructions>
</project_context>

The following skills provide specialized instructions:

<available_skills>
  <skill>
    <name>librarian</name>
    <description>Research open-source libraries...</description>
    <location>.../pi-web-access/skills/librarian/SKILL.md</location>
  </skill>
</available_skills>

Current date: 2026-07-02
Current working directory: /path/to/myproject

按 Esc 或 q 关闭。

二、最值得装的官方示例扩展:permission-gate

为什么装它

Pi 默认不拦截任何工具调用。LLM 理论上可以执行 sudo rm -rf /,或者用 find / 扫描整个文件系统。这在日常使用中很少发生(现在模型足够智能),但是万一发生了。。。安全底线应该自己做,而不是依赖模型的判断。

permission-gate.ts 是官方示例之一,专门做这件事:拦截危险操作,弹窗确认。

安装

# 从官方示例目录复制
cp ~/.nvm/versions/node/v24.6.0/lib/node_modules/@earendil-works/pi-coding-agent/examples/extensions/permission-gate.ts \
   ~/.pi/agent/extensions/

# /reload 生效

它做了什么

const rules: DangerousRule[] = [
  { pattern: /\bfind\s+\/(?![a-zA-Z0-9_./])/i, label: "find-root", reason: "..." },
];

监听 tool_call 事件,检查 bash 命令是否匹配危险模式,以及 find 工具的 path 参数是否为 /。两种场景分开处理:

简单改造

原始规则只写了几条示例命令规则,可以根据自己实际需求加了规则:

const rules: DangerousRule[] = [
  // 全盘扫描
  { pattern: /\bfind\s+\/(?![a-zA-Z0-9_./])/i, label: "find-root", reason: "Full-filesystem scan" },
  // 删除命令
  { pattern: /\brm\s+-rf\b/, label: "rm-rf", reason: "Recursive force delete" },
  // sudo
  { pattern: /\bsudo\b/, label: "sudo", reason: "Privilege escalation" },
  // 写入系统目录
  { pattern: /\b(write|cp|mv|tee)\s+.*\/(etc|usr|opt|var)\//, label: "sys-write", reason: "Writing to system path" },
  // curl/wget 到管道执行的模式
  { pattern: /\bcurl\s+\S+\s*\|/, label: "curl-pipe", reason: "curl piped to shell" },
];

纯数据改动,不需要改逻辑。这个扩展设计得很好——规则是声明式的列表,加新规则就增加一行对象。不过纯基于正则表达式过于简单了,现在聪明一点模型其实很容易绕过。

还可以怎么改

三、其他建议装的官方示例插件

官方 70 多个示例不要都装。有的是演示特定 API 的教学代码,有的功能已被主线集成,有的对日常工作价值不大。

已被主线集成,没必要装

示例 被什么替代
shutdown-command.ts Pi 内置 /quit
session-name.ts Pi 内置 /name
bookmark.ts /tree 中 Shift+L 已支持标签

这三份源码仍然有学习价值(看底层 API setSessionName、setLabel 的用法),但安装是多余的。

必装(安全底线)

permission-gate.ts — 上面讲过了。这是唯一一个我建议所有用户都装的 extension。Pi 不替你做安全判断,你得自己做。

强烈推荐(日常高频使用)

question.ts — LLM 可调用的选择/填空工具。不需要 LLM 猜测你的意图,也用不着一轮对话来回确认,直接弹窗问。支持选项列表 + 自定义输入,按 Enter 选择,Esc 取消,上下键导航。结果是结构化的 { answer, wasCustom },LLM 能可靠地解析。

安装方式一样:从官方 examples 目录 cp 到 ~/.pi/agent/extensions/。

plan-mode/ — Claude Code 风格的 plan mode。/plan 切换只读探索模式,禁用编辑/写入工具,bash 只放行 ls、grep、cat 等安全命令。Agent 输出 Plan: 开头的编号步骤后,可以选择进入执行模式。执行过程中用 [DONE:n] 标记完成进度。

安装时注意是目录,需要整个复制:

cp -r ~/.nvm/versions/.../examples/extensions/plan-mode ~/.pi/agent/extensions/

todo.ts — LLM 可调用的 todo 工具。Agent 可以 add / list / toggle / clear,用户用 /todos 命令查看列表。状态持久化在 session entry 的 details 里,fork 时自动正确。

handoff.ts — 会话交接。当你一个 session 聊得太多上下文太长,/handoff <目标> 会调用一个小模型(默认)把当前上下文总结成一个精炼的新 prompt,创建新 session 并在编辑器里预填。比直接 /new 再口头复述高效得多。

安装:

cp ~/.nvm/versions/.../examples/extensions/handoff.ts ~/.pi/agent/extensions/

按需选择

场景 推荐
用 Git 且切换 session 频繁 dirty-repo-guard.ts + git-checkpoint.ts
想换一种压缩策略 custom-compaction.ts
有自定义 Provider custom-provider-anthropic/ 参考
想在终端玩贪吃蛇 snake.ts
研究 extension 怎么写 UI message-renderer.ts、question.ts、custom-footer.ts

四、从社区安装 extention: 以 pi-md-export 为例

它是什么

我最常用的一个社区 extension,源自 npm 的 pi-md-export)。它提供一个 /md 命令,把当前 session 的对话导出为 Markdown 文件,我改造了一下,让它放到我的 Obsidian vault 的 pi/ 目录里。

文件名格式:<YYYY-MM-DD>-<项目名>-<slug>.md

安装

pi install npm:pi-md-export
# /reload

它能做什么

/md 命令支持多种模式:

执行后弹出选择:保存为 .md 文件还是复制到剪贴板。

我的改造

原始代码默认输出到一个目录,我改成写到我的 Obsidian vault 的目录。

  1. 支持 PI_MD_VAULT_DIR 环境变量 — 不改代码,通过环境变量覆盖输出路径
    export PI_MD_VAULT_DIR="/path/to/obsidian/ingest/pi"
    
  2. 文件名改进 — 加上 YYYYMMDD- 前缀
  3. slug 自动建议 — 从对话第一句用户消息提取关键词,作为 slug 的默认值,用户可以直接按 Enter 接受

装它解决了什么

我在 Obsidian 里的工作流是:Pi 聊完一段内容后,/md 导出 → 自动保存到 obsidian/pi/ → Obsidian 自动索引 → 可以在笔记库里查看对话。这个过程从"手动复制粘贴"变成了"一个命令+按 Enter"。

五、当前的扩展清单

~/.pi/agent/extensions/
├── show-system-prompt.ts   # 自写:查看系统提示词
├── permission-gate.ts      # 官方:危险操作拦截(已改造)
├── question.ts             # 官方:LLM 可调用的选择工具
├── todo.ts                 # 官方:Todo 管理
├── handoff.ts              # 官方:会话交接
├── plan-mode/              # 官方:只读探索模式
│   ├── index.ts
│   ├── utils.ts
│   └── README.md
└── pi-markdown-export.ts   # 社区: 可以导出到 Obsidian vault