写一个 Tool,再写一个 Extension
用当前 Pi API 注册类型安全的 hello tool,并扩展为带生命周期观察的项目能力。
ANALYSIS SNAPSHOTPi v0.82.1
5bc1c2c0文件位置
把项目扩展放在 .pi/extensions/hello.ts。启动时 Pi 会要求确认 project trust;也可以用 pi -e ./hello.ts 临时加载。
最小类型安全工具
import { Type } from "@earendil-works/pi-ai";
import {
defineTool,
type ExtensionAPI,
} from "@earendil-works/pi-coding-agent";
const helloTool = defineTool({
name: "hello",
label: "Hello",
description: "Greet a person by name",
parameters: Type.Object({
name: Type.String({ description: "Name to greet" }),
}),
async execute(_id, params) {
return {
content: [{ type: "text", text: `Hello, ${params.name}!` }],
details: { greeted: params.name },
};
},
});
export default function setup(pi: ExtensionAPI) {
pi.registerTool(helloTool);
}
packages/coding-agent/examples/extensions/hello.ts:5-26defineTool() · registerTool()
加入观察性
export default function setup(pi: ExtensionAPI) {
pi.registerTool(helloTool);
pi.on("tool_call", async (event, ctx) => {
if (event.toolName === "hello") {
ctx.ui.notify(`Calling ${event.toolName}`, "info");
}
});
}
如果目标是安全确认,应在可阻断的 tool-call hook 中返回明确 decision,而不是只显示通知。事件名、字段与返回契约应以固定版本的 docs/extensions.md 和 core/extensions/types.ts 为准。
状态如何持久化
pi.appendEntry("hello-stats", { count: 1 });
这会产生 custom session entry,不进入模型 context。恢复时遍历当前 branch 的 entries,找到最近一条同 customType 的数据。需要让模型知道时,使用 custom message,而不是滥用 state entry。
从 Tool 到产品能力
一个成熟 extension 往往包含:
- 一个窄而清晰的 tool contract。
- before/after hooks 做政策和归一化。
- custom entry 保存不进 context 的状态。
- command 或 UI 让人类配置与检查。
- session start/tree handlers 恢复当前分支状态。
- 可在 faux provider 下运行的确定性测试。
这正是 Pi 的 extension-first 哲学:产品能力长在边界上,而不是不断改 core loop。