第 05 模块 · 2 节

用 Extension 注册自定义命令并跑通

《Pi 进阶实战》05 贯穿进阶项目 · 本节时长 46 分钟

贯穿项目最后一击:用 M04 学的扩展能力,把 md-tools 注册成 Pi 的 /md 斜杠命令和一个可被模型调用的 md_tools 工具,并跑通完整链路。至此,入门课的脚本被彻底「升级」成 Pi 的一等公民。

目标:扩展把 md-tools 接进 Pi

我们要写一份扩展,做三件事:

  1. pi.registerTool 注册 md_tools 工具,模型可直接调用
  2. pi.registerCommand 注册 /md 斜杠命令,人可直接用
  3. tool_call 事件给 rename 加确认(安全)

这份扩展调用你上一节打包的脚本,也就是把 md-tools 脚本「暴露」给 Pi。


第一步:完整扩展骨架

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

export default function (pi: ExtensionAPI) {
  // 1) 工具:模型可直接调用
  pi.registerTool({
    name: "md_tools",
    label: "MD Tools",
    description: "处理 markdown 文件:stats 统计、convert 转纯文本、rename 改名(先预览)",
    parameters: Type.Object({
      action: StringEnum(["stats", "convert", "rename"] as const),
      path: Type.String({ description: "目标 markdown 文件" }),
    }),
    async execute(toolCallId, params) {
      const result = await pi.exec("node", ["./scripts/index.js", params.action, params.path]);
      return {
        content: [{ type: "text", text: result.stdout }],
        details: { code: result.code },
      };
    },
  });

  // 2) 命令:人用 /md
  pi.registerCommand("md", {
    description: "用 md-tools 处理 markdown 文件",
    handler: async (args, ctx) => {
      ctx.ui.notify(`md-tools: ${args}`, "info");
    },
  });

  // 3) 安全:rename 前确认
  pi.on("tool_call", async (event, ctx) => {
    if (event.toolName === "md_tools" && event.input.action === "rename") {
      const ok = await ctx.ui.confirm("改名?", `确认对 ${event.input.path} 改名吗?`);
      if (!ok) return { block: true, reason: "用户取消改名" };
    }
  });
}

注意 pi.exec 是执行 shell 命令的 API(返回 stdout / stderr / code),用它把脚本的输出接回来。


第二步:放到自动发现目录

官方提醒过:放自动发现目录才能 /reload 热重载。所以别只做 pi -e ./x.ts 的临时测试,要正式放到:

~/.pi/agent/extensions/my-md-tools.ts   # 全局,所有项目生效

或项目级 .pi/extensions/(需信任)。放好启动,扩展就自动加载。


第三步:跑通整条链路

启动后,验证三件事:

# 1) 模型能否调用工具:直接问它
"用 md_tools 检查 README.md 的统计信息"

# 2) 斜杠命令是否可用:输入
/md stats README.md

# 3) 改名是否走确认:让模型执行 rename
"用 md_tools 把 a.md 改名为 b.md"   # 应弹出确认

一条条验证,别一起跑。工具、命令、安全拦截,三条链路各自确认通了,才算真的跑通。


落地练习:完整验收 md-tools 扩展

  1. 把上面扩展放进 ~/.pi/agent/extensions/,重启 Pi
  2. 让模型调用 md_tools 工具,确认统计输出正确
  3. 输入 /md stats 确认命令可用
  4. 让模型执行 rename,确认会先弹确认框
  5. 修改扩展代码,用 /reload 验证热重载生效

怎么判断做对了?——工具、命令、安全拦截三条都通,/reload 后新逻辑生效。

卡住了怎么办? 工具不通 → 检查 parameters schema、脚本路径。命令不通 → 检查 registerCommand。安全拦截不触发 → 检查 event.toolName 是不是 md_toolsevent.input.action 取值。


常见坑:pi -e 测完就完事,忘了放正式位置

很多人在 pi -e ./x.ts 里测通了就收工,结果下次启动(不带 -e)扩展没了——因为没放进自动发现目录。

官方明确:-e 只适合快速测试;要自动加载、要能 /reload,就放进 ~/.pi/agent/extensions/(全局)或 .pi/extensions/(项目)。 测试和落地是两个动作,别混为一谈。


小结(也是整门课小结)

这一节跑通了 md-tools 的完整进阶闭环。回顾整门课,你已掌握:

  1. 上下文与系统提示词:AGENTS.md 深化约定、SYSTEM.md / APPEND_SYSTEM.md 定制、Project Trust 信任机制
  2. 自定义技能:把 md-tools 打包成可复用、可跨 Harness 的 Skill
  3. 提示词模板:用 $1 / $@ 固化常用命令
  4. 扩展开发:registerTool / registerCommand / 事件系统 / 生命周期
  5. 贯穿项目:md-tools 从脚本升级为 Skill + Extension 的一等公民

「封装一次、处处复用」——这就是 Pi 进阶的核心回报。用它去打磨属于你自己的工具箱吧。