跳到主内容
创建时间:最后更新:作者:白川

开发并加载第一个扩展

从一个命令扩展开始,理解注册、发现、加载和运行权限,建立可检查的开发回路。

开发并加载第一个扩展

智能体已经能读文件、调用工具,却还没有“检查这个仓库”的专用入口。往提示词里加一句要求,可以影响模型选择;如果要增加可调用的工具、确定执行的命令或执行前的规则,就需要把能力接到宿主里。

扩展是宿主加载的代码模块。本文先做一个命令,确认加载路径和运行上下文,再逐步加能力。第一次验证只检查扩展加载,不需要模型 API Key。

本文示例固定使用 @earendil-works/pi-coding-agent@1.5.0,源码位于仓库 columns/coding-agent/examples/extension-development/。示例是独立包,先在该目录安装依赖;不需要修改专栏站点依赖。下面的命令均从这个示例目录运行。

用命令确认加载成功

创建 extensions/hello.ts。默认导出的函数接收注册接口 pi,在这里声明命令;真正执行时,宿主才把当前上下文 ctx 交给 handler。

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

export default function (pi: ExtensionAPI) {
  pi.registerCommand("lesson-hello", {
    description: "确认扩展已经加载",
    handler: async (_args, ctx) => {
      if (ctx.hasUI) {
        ctx.ui.notify(`扩展已加载,工作目录:${ctx.cwd}`, "info")
      } else {
        pi.appendEntry("lesson.hello", { cwd: ctx.cwd })
      }
    },
  })
}

pi 适合注册能力,ctx 提供本次调用的工作目录、UI 和会话。两者生命周期不同:不要在初始化时记住某个会话上下文,之后一直复用。

bash
npm install
npx pi --extension ./extensions/hello.ts

在交互界面输入 /lesson-hello,应看到加载提示和工作目录。如果只有未知命令,先看启动日志中的扩展加载错误,确认路径指向文件,再核对导出函数。此时改模型配置不能解决加载问题。

加载位置决定影响范围

放置方式适用阶段需要检查的条件
命令行 --extension / -e
本地开发
本次启动的路径是否正确
项目的 .pi/extensions/
项目共享
项目信任及资源配置是否允许加载
用户的 ~/.pi/agent/extensions/
个人通用
是否影响了其他项目
资源包的 pi.extensions
分发复用
实际包内文件、依赖与入口是否一致

开发期先显式指定单个扩展。这个系列的多个样例会注册同名教学工具,应分别加载;同时加载全部样例会制造名称冲突。宿主支持加载 TypeScript,这不代表模块随处复制后都能找到依赖;依赖解析仍需要正确的包目录。

项目发现也不是任意脚本自动执行的许可。信任流程可能分阶段加载资源,项目扩展不能假设自己能参与信任之前的决定。需要实现信任策略时,应查对应宿主版本的启动顺序,不能拿普通 session_start 代替。

先区分三种扩展材料

Skill 主要给模型提供任务方法,扩展把代码接到智能体宿主,资源包负责把这些文件组织分发。一个包可以同时包含 Skill 和扩展,但安装了 Skill 不等于注册了新工具。

后面两篇使用 Chord 构建应用内插件。它需要自己创建 host、定义契约和管理引用,并不能把一个 facet 文件直接放进 .pi/extensions/ 当作命令行扩展。先确认扩展点属于谁,才能选择入口。

用加载器排除模型干扰

配套的 src/audit.ts 只发现并加载指定文件,列出工具、命令、事件,不创建 Agent 或发模型请求:

bash
npm run audit -- extensions/hello.ts

应看到 errors: []、命令 lesson-hello 和空工具集合。这个结果证明文件可以被加载并完成注册;输入命令后的 UI 展示,仍要在宿主交互模式单独检查。

扩展和宿主运行在同一个 Node 进程权限下,可能接触文件、网络和凭据。事件拦截也不构成操作系统沙箱。只加载自己能够审查和信任的代码,并把定时器、监听器等运行资源放到会话生命周期内启动和释放。下一篇先把只读检查能力做成工具和命令。

依据与版本

代码以已发布的 1.5.0 包导出、类型声明和加载器为准。在线主分支会继续变化,遇到接口差异时先核对版本。