关于 pi-coding-agent
pi-coding-agent 是用于在 TypeScript/Node 脚本和服务中创建 Agent 会话的 SDK,适合构建专用 Agent 或嵌入自动化流程。它支持流式输出、自定义工具及按需限制工具集。
主要功能
- 01从 Node 脚本创建 Agent 会话
- 02流式接收 Agent 输出
- 03选择或限制内置工具
- 04定义可调用的自定义工具
- 05通过资源加载器配置扩展和提示词
适用场景
- 01构建专用 TypeScript 命令行 Agent
- 02将 Agent 嵌入 Node 服务或任务运行器
- 03在脚本流程中执行一次性 Agent 任务
- 04限制 Agent 仅使用只读或特定领域工具
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mizchi/ skills --skill pi-coding-agent安装后在 Claude Code 中输入/pi-coding-agent确认已加载。
附带文件README.md
详细介绍
pi-coding-agent
@mariozechner/pi-coding-agent(上游)同时提供 pi 交互式 TUI 和一流的 SDK。使用 SDK,你可以从任何 Node 20.6+ 脚本中启动 Agent 会话、流式传输其输出、限定其工具范围,并添加自己的工具,而无需分叉 CLI。
适用场景
- 使用 TypeScript / Node 构建单一用途的 Agent CLI(而非聊天产品)
- 将 pi 封装在更大的 Node 服务或作业运行器中
- 将 Agent 推理嵌入脚本流水线(单次运行,完成后退出)
- 需要只读或受领域限制的工具集
- 想添加 Agent 可调用的自定义工具(
deploy、query_db等)
以下情况请跳过此技能:
- 只想交互式使用 pi → 在 CLI 中运行
pi,无需 SDK - 主机不是 Node → 改用
pi --mode rpc(通过子进程使用 JSON-RPC) - 想要 Anthropic 官方 SDK 的语义 → 那是另一个包
@anthropic-ai/claude-agent-sdk
注意事项(请先阅读)
| 现象 | 原因 | 解决方法 |
|---|---|---|
require() 报错 ERR_PACKAGE_PATH_NOT_EXPORTED | 包仅支持 ESM("type": "module",exports 映射不包含 CJS) | 使用 ESM(import),或通过 tsx / --experimental-strip-types 运行 |
| 工具针对错误的目录解析路径 | 预构建的 readTool / bashTool 会在导入时捕获 process.cwd() | 传入自定义 cwd 和显式 tools 时,使用工厂形式 createCodingTools(cwd) / createReadTool(cwd) |
session.prompt(...) 抛出“stream in progress” | 上一个流仍在运行时再次调用 prompt() | 传入 { streamingBehavior: "steer" } 或 "followUp",或使用 session.steer() / session.followUp() |
设置了 ANTHROPIC_API_KEY 仍出现身份验证错误 | AuthStorage 按运行时覆盖项 → auth.json → 环境变量的顺序读取;过期的 ~/.pi/agent/auth.json 可能覆盖环境变量 | 清除已存储的凭据,或在调用 createAgentSession() 前调用 authStorage.setRuntimeApiKey("anthropic", key) |
| 流式传输期间没有任何输出 | 忘记订阅,或只处理 text_delta,而模型仅输出思考内容 | 订阅 message_update,并处理 text_delta 和(可选的)thinking_delta |
扩展只能通过 pi -e ./ext.ts 加载,常规运行时无法加载 | -e 仅用于单次运行;文件不在自动发现目录中 | 将其移至 ~/.pi/agent/extensions/<name>.ts(全局)或 <project>/.pi/extensions/<name>.ts(项目),或在 settings.json 的 extensions: [...] 中注册 |
已安装的 pi 包在运行时出现 Cannot find module '@mariozechner/pi-coding-agent' | 将其列在 dependencies 而非 peerDependencies 中 | 五个核心包(@mariozechner/pi-{ai,agent-core,coding-agent,tui}、typebox)必须列在 peerDependencies 中,版本范围为 "*",且不能打包——pi 会在加载时提供它们 |
pi install 成功,但缺少传递依赖 | 默认使用 npm install --omit=dev,因此运行时不会包含 devDependencies | 将导入项移至 dependencies,或者在需要自定义 npm 封装器时,在 settings.json 中设置 npmCommand |
| 扩展更改未生效 | TUI 缓存了之前的加载结果 | 在 pi 中运行 /reload(适用于自动发现位置中的任何扩展) |
安装
pnpm add @mariozechner/pi-coding-agent
# Or one-off scripts:
npx tsx my-agent.ts
需要 Node ≥ 20.6 和 API 密钥(环境变量或 ~/.pi/agent/auth.json)。
最小会话
#!/usr/bin/env -S npx tsx
import {
AuthStorage,
ModelRegistry,
SessionManager,
createAgentSession,
} from "@mariozechner/pi-coding-agent";
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(), // no .jsonl on disk
authStorage,
modelRegistry,
});
session.subscribe((event) => {
if (
event.type === "message_update" &&
event.assistantMessageEvent.type === "text_delta"
) {
process.stdout.write(event.assistantMessageEvent.delta);
}
});
await session.prompt(process.argv.slice(2).join(" ") || "Hello");
运行:
export ANTHROPIC_API_KEY=sk-ant-...
npx tsx my-agent.ts "Explain the layout of src/"
不带任何选项的 createAgentSession() 也可以正常工作——它会从 ~/.pi/agent/ 和当前工作目录中发现工具、技能、扩展及默认模型。只有需要偏离这些默认值时才传入选项。
限制工具集
SDK 同时提供内置工具数组和按字符串名称选择工具的方式。选择更易读的方式即可。
import { createAgentSession, readOnlyTools } from "@mariozechner/pi-coding-agent";
// Array of tool objects (use process.cwd())
await createAgentSession({ tools: readOnlyTools });
// Or by name — equivalent, more concise
await createAgentSession({ tools: ["read", "grep", "find", "ls"] });
工具名称目录:read、bash、edit、write、grep、find、ls。预打包的工具集:
| 工具集 | 内容 |
|---|---|
codingTools | read、bash、edit、write(默认) |
readOnlyTools | read、grep、find、ls |
cwd × 工具陷阱
预构建的工具实例(readOnlyTools、codingTools、readTool、bashTool 等)会在导入时捕获 process.cwd(),之后不会重新绑定。将它们与自定义 cwd 组合使用时,Agent 会悄悄读取错误的目录。
请记住以下三种组合:
| 形式 | 使用自定义 cwd 时 | 原因 |
|---|---|---|
tools: readOnlyTools(常量) | 不行 | 在导入时捕获了 process.cwd() |
tools: ["read", "grep", ...](字符串名称) | 可以 | SDK 会根据提供的 cwd,为每个会话解析名称 |
tools: createReadOnlyTools(cwd)(工厂函数) | 可以 | 显式绑定 cwd |
省略 tools | 可以 | SDK 会选择默认工具集,并为你绑定 cwd |
工厂函数系列与每个常量/实例一一对应,按需选择:
| 你需要…… | 工厂函数 |
|---|---|
将 codingTools 工具集绑定到 cwd | createCodingTools(cwd) |
将 readOnlyTools 工具集绑定到 cwd | createReadOnlyTools(cwd) |
| 只将一个工具绑定到 cwd | createReadTool(cwd)、createBashTool(cwd)、createEditTool(cwd)、createWriteTool(cwd)、createGrepTool(cwd)、createFindTool(cwd)、createLsTool(cwd) |
import {
createAgentSession,
createCodingTools,
createReadOnlyTools,
createGrepTool,
createReadTool,
} from "@mariozechner/pi-coding-agent";
const cwd = "/path/to/project";
// Whole set, bound to cwd
await createAgentSession({ cwd, tools: createCodingTools(cwd) });
await createAgentSession({ cwd, tools: createReadOnlyTools(cwd) });
// Or hand-picked, bound to cwd
await createAgentSession({ cwd, tools: [createReadTool(cwd), createGrepTool(cwd)] });
// Or — equivalent and shorter — name-based, which the SDK rebinds per session
await createAgentSession({ cwd, tools: ["read", "grep", "find", "ls"] });
只有在同时传入非默认的 cwd 和显式的 tools 值时,才需要考虑这个问题。完全省略 tools,SDK 就会为你构建绑定到 cwd 的默认工具集。
自定义工具
import { Type } from "typebox";
import { createAgentSession, defineTool } from "@mariozechner/pi-coding-agent";
const deployTool = defineTool({
name: "deploy",
label: "Deploy",
description: "Deploy the current branch to staging",
parameters: Type.Object({
target: Type.Optional(Type.String({ description: "Target env (default: staging)" })),
}),
execute: async (_toolCallId, params) => {
// Your logic. Must return { content: [...], details: {...} }.
const target = params.target ?? "staging";
return {
content: [{ type: "text", text: `Deployed to ${target}` }],
details: { target },
};
},
});
const { session } = await createAgentSession({
customTools: [deployTool],
});
customTools 会附加到你同时请求的其他 tools。参数使用 TypeBox——Agent 会获得 JSON Schema 视图,而你的 execute 会接收类型化的 params。
自定义系统提示词
系统提示词由 ResourceLoader 管理,不直接由 createAgentSession() 管理:
import { DefaultResourceLoader, createAgentSession } from "@mariozechner/pi-coding-agent";
const resourceLoader = new DefaultResourceLoader({
systemPromptOverride: () => "You are a senior SRE. Answer tersely.",
});
await resourceLoader.reload();
const { session } = await createAgentSession({ resourceLoader });
同一个加载器也是添加技能、提示词模板、扩展和虚拟 AGENTS.md 上下文文件的入口。完整选项列表请参阅 docs/sdk.md。
扩展(插件系统)
扩展是 pi 在启动时加载的 TypeScript 模块。与通过 SDK 传入的 customTools 相比,扩展:
- 可在每次发现它的
pi调用中重复使用 - 可注册工具和命令(
/foo)、快捷键、页脚小组件、提供方和自定义渲染 - 可拦截生命周期事件(阻止工具调用、修改上下文、自定义压缩)
- 可通过
ctx.ui向用户显示提示(选择/确认/输入/通知)
Pi 通过 jiti 直接加载 .ts 文件——无需编译步骤。
**安全性:**扩展拥有你的完整系统权限,并且可以执行任意代码。只从你信任的来源安装扩展。
自动发现位置
| 路径 | 范围 |
|---|---|
~/.pi/agent/extensions/<name>.ts | 全局,单文件 |
~/.pi/agent/extensions/<name>/index.ts | 全局,多文件 |
.pi/extensions/<name>.ts | 项目本地 |
.pi/extensions/<name>/index.ts | 项目本地,多文件 |
此外,还包括 settings.json 中声明的路径(见下方安装部分)。在任何自动发现位置中的扩展,都可以在 TUI 中使用 /reload 热重载。
最小扩展示例
保存为 ~/.pi/agent/extensions/greet.ts:
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { Type } from "typebox";
export default function (pi: ExtensionAPI) {
pi.on("session_start", async (_event, ctx) => {
ctx.ui.notify("greet extension loaded", "info");
});
// Block dangerous bash commands
pi.on("tool_call", async (event, ctx) => {
if (event.toolName !== "bash") return;
const cmd = event.input.command as string;
if (/\brm\s+-rf?\b|\bsudo\b/.test(cmd)) {
const ok = ctx.hasUI ? await ctx.ui.confirm("Dangerous", `Allow:\n${cmd}`) : false;
if (!ok) return { block: true, reason: "blocked by greet extension" };
}
});
pi.registerTool({
name: "greet",
label: "Greet",
description: "Greet someone by name",
parameters: Type.Object({ name: Type.String() }),
async execute(_id, params) {
return { content: [{ type: "text", text: `Hello, ${params.name}!` }], details: {} };
},
});
pi.registerCommand("hello", {
description: "Say hello",
handler: async (args, ctx) => ctx.ui.notify(`Hello ${args || "world"}!`, "info"),
});
}
无需在任何位置安装即可试用:
pi -e ./greet.ts
异步工厂函数(用于启动时执行任务)
异步默认导出会在 session_start 触发前等待执行完成。可用它获取动态配置(例如本地大模型的模型列表):
export default async function (pi: ExtensionAPI) {
const res = await fetch("http://localhost:1234/v1/models");
const { data } = (await res.json()) as { data: Array<{ id: string; context_window?: number; max_tokens?: number }> };
pi.registerProvider("local-openai", {
baseUrl: "http://localhost:1234/v1",
apiKey: "LOCAL_OPENAI_API_KEY",
api: "openai-completions",
models: data.map((m) => ({
id: m.id, name: m.id, reasoning: false, input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: m.context_window ?? 128000,
maxTokens: m.max_tokens ?? 4096,
})),
});
}
从 SDK 加载扩展
在 SDK 代码中,扩展仍由 ResourceLoader 管理。DefaultResourceLoader 会发现上述相同的自动发现目录;对于一次性路径,请传入 additionalExtensionPaths;对于内联工厂,请传入 extensionFactories:
import { DefaultResourceLoader, createAgentSession } from "@mariozechner/pi-coding-agent";
const resourceLoader = new DefaultResourceLoader({
additionalExtensionPaths: ["/abs/path/to/my-ext.ts"],
extensionFactories: [
(pi) => pi.on("agent_start", () => console.log("[inline] agent starting")),
],
});
await resourceLoader.reload();
const { session } = await createAgentSession({ resourceLoader });
带有 npm 依赖项的多文件扩展
对于需要引入 npm 包的扩展,请在其旁边放置一个 package.json:
~/.pi/agent/extensions/my-ext/
├── package.json
├── package-lock.json
├── node_modules/
└── src/
└── index.ts
// package.json
{
"name": "my-ext",
"dependencies": { "zod": "^3.0.0" },
"pi": { "extensions": ["./src/index.ts"] }
}
在该目录中运行一次 npm install。pi 会通过本地 node_modules/ 解析导入。
安装第三方包
pi 包是分发单元:它包含一个或多个扩展、技能、提示词模板和主题。pi install 会写入 settings.json;pi 下次启动时,会自动加载包中声明的所有内容。
pi install npm:@foo/[email protected] # npm, version-pinned
pi install npm:@foo/bar # npm, latest (re-pulled by `pi update`)
pi install git:github.com/user/repo@v1 # git, ref-pinned
pi install https://github.com/user/repo # raw URL also works
pi install ./local/package # local path, not copied
pi list # show installed
pi update # pull all non-pinned packages
pi remove npm:@foo/bar
默认情况下,它会修改 ~/.pi/agent/settings.json(全局设置)。使用 -l 可改为写入 <project>/.pi/settings.json——提交该文件后,pi 会在启动时为团队中的所有人自动安装缺失的包。
若要试用某个包而不保留任何设置:
pi -e npm:@foo/bar
pi -e git:github.com/user/repo
-e 会将包安装到临时目录,仅在当前运行期间有效。
生成的 settings.json 如下:
{
"packages": [
"npm:@foo/[email protected]",
"git:github.com/user/repo@v1"
],
"extensions": [
"/absolute/path/to/local/extension.ts",
"/absolute/path/to/local/extension/dir"
]
}
packages(数组):按来源指定的 pi 包。extensions(数组):未打包的扩展路径。两个数组都支持对象形式,以筛选包内内容:{ source: "npm:my-pkg", extensions: ["extensions/*.ts", "!extensions/legacy.ts"], skills: [], prompts: [...] }。
编写可发布的 pi 包
在 package.json 中添加 pi 清单,并添加 pi-package 关键字,以便在 pi.dev/packages 中发现:
{
"name": "my-pi-pack",
"version": "1.0.0",
"keywords": ["pi-package"],
"pi": {
"extensions": ["./extensions"],
"skills": ["./skills"],
"prompts": ["./prompts"],
"themes": ["./themes"]
},
"peerDependencies": {
"@mariozechner/pi-coding-agent": "*",
"@mariozechner/pi-ai": "*",
"@mariozechner/pi-agent-core": "*",
"@mariozechner/pi-tui": "*",
"typebox": "*"
},
"dependencies": {
"zod": "^3.0.0"
}
}
如果完全省略 pi 字段,pi 会从约定目录自动发现内容:extensions/、skills/、prompts/、themes/。路径数组支持 glob 和 !negation。
依赖项规则——如果处理不当,包会在安装时出错:
- 五个核心包——
@mariozechner/pi-coding-agent、@mariozechner/pi-ai、@mariozechner/pi-agent-core、@mariozechner/pi-tui、typebox——必须放在peerDependencies中,并设为"*"。pi 会在运行时提供它们;将它们打包会导致模块标识错误(你的defineTool不是 pi 的defineTool)。 - 其他所有内容(zod、chalk 等)都放在
dependencies中——pi 在获取包后会运行npm install --omit=dev,因此运行时无法使用devDependencies。 - 若要嵌入另一个 pi 包,请同时将其列入
dependencies和bundledDependencies,然后通过node_modules/<name>/...引用其中的资源。
打印模式(单次运行流水线)
runPrintMode 与 pi -p 的行为一致:发送提示词、流式输出结果,然后退出。适用于 shell 流水线或 CI 任务。
import {
type CreateAgentSessionRuntimeFactory,
SessionManager,
createAgentSessionFromServices,
createAgentSessionRuntime,
createAgentSessionServices,
getAgentDir,
runPrintMode,
} from "@mariozechner/pi-coding-agent";
const createRuntime: CreateAgentSessionRuntimeFactory = async (
{ cwd, sessionManager, sessionStartEvent },
) => {
const services = await createAgentSessionServices({ cwd });
return {
...(await createAgentSessionFromServices({ services, sessionManager, sessionStartEvent })),
services,
diagnostics: services.diagnostics,
};
};
const runtime = await createAgentSessionRuntime(createRuntime, {
cwd: process.cwd(),
agentDir: getAgentDir(),
sessionManager: SessionManager.inMemory(),
});
await runPrintMode(runtime, {
mode: "text", // "text" | "json"
initialMessage: process.argv.slice(2).join(" "),
});
mode: "json" 会将输出切换为每轮一个结构化 JSON 封装;将输出管道传入另一个程序时,通常应使用此模式。
引导进行中的流
默认情况下,session.prompt() 按顺序执行。若要在某轮运行期间注入内容:
await session.steer("Stop and read README.md first"); // delivered after current tool calls
await session.followUp("Then summarise it"); // delivered after the agent finishes
两者都会展开基于文件的提示词模板,但会拒绝扩展命令(/...)。如果在流式输出期间调用 session.prompt(text),则必须明确选择:{ streamingBehavior: "steer" | "followUp" }。
选择合适的入口点
| 使用场景 | API |
|---|---|
| 完全由你控制的一次性 CLI 工具 | createAgentSession() + SessionManager.inMemory() |
| 需要将会话持久化到磁盘 | SessionManager.create(cwd)(或 .continueRecent、.open) |
| 构建适合管道调用的脚本 | runPrintMode(runtime, { mode: "json" }) |
| 根据你的领域定制工具(单个进程、代码内定义) | defineTool() + customTools: [...] |
在这台机器上的每次 pi 运行中共享工具 / 命令 | 在 ~/.pi/agent/extensions/<name>.ts 中创建扩展,使用 pi.registerTool / pi.registerCommand |
| 仅限单个项目的工具 / 命令(提交到版本控制) | 在 <project>/.pi/extensions/<name>.ts 中创建扩展,并添加 <project>/.pi/settings.json(使用 -l) |
| 打包并分享扩展 / 技能 / 提示词 | Pi 包:在 package.json 中添加 pi 字段和 pi-package 关键字 |
| 安装他人的包 | pi install npm:<spec> 或 pi install git:<spec>(项目级安装使用 -l) |
在运行时替换当前会话(/new、/fork、/resume) | 先调用 createAgentSessionRuntime(),然后调用 runtime.newSession() 等——每次替换后都要重新订阅 |
| 使用另一种语言驱动 Agent | pi --mode rpc --no-session(通过 stdio 使用 JSON-RPC),无需 SDK |
SDK 与 pi --mode rpc
如果你需要类型安全、进程内控制,以及注册使用 TypeScript 编写的工具或扩展,请使用 SDK。如果宿主语言是 Python、Go、Rust 等,或者需要进程隔离,请使用 RPC 模式。两者提供相同的 Agent;SDK 还允许你直接访问内部状态和事件流。
参考资料
- 上游 SDK 指南 — 权威 SDK 参考(技能、设置、扩展选项、运行时替换)
- 扩展指南 — 完整事件列表、
ExtensionAPI/ExtensionContext接口、自定义 UI 组件 - 包指南 — 打包、
pi install来源、筛选、作用域 / 去重规则 - 技能指南 — Agent 如何使用
SKILL.md(与此技能仓库分开) - 设置参考 —
~/.pi/agent/settings.json架构(上下文压缩、重试、包、扩展) examples/sdk/— 13 个 SDK 示例,也会安装到node_modules/@mariozechner/pi-coding-agent/examples/sdk/下examples/extensions/— 扩展示例集:permission-gate.ts、protected-paths.ts、git-checkpoint.ts、自定义提供方、自定义 UI 等- RPC 模式 — 从非 Node 宿主调用 pi 时使用的 JSON 协议