mcpField
P
pi-coding-agent

pi-coding-agent

github.com/mizchiAgent 工作流416130 次安装1 周前更新
代码仓库

关于 pi-coding-agent

pi-coding-agent 是用于在 TypeScript/Node 脚本和服务中创建 Agent 会话的 SDK,适合构建专用 Agent 或嵌入自动化流程。它支持流式输出、自定义工具及按需限制工具集。

主要功能

  1. 01从 Node 脚本创建 Agent 会话
  2. 02流式接收 Agent 输出
  3. 03选择或限制内置工具
  4. 04定义可调用的自定义工具
  5. 05通过资源加载器配置扩展和提示词

适用场景

  1. 01构建专用 TypeScript 命令行 Agent
  2. 02将 Agent 嵌入 Node 服务或任务运行器
  3. 03在脚本流程中执行一次性 Agent 任务
  4. 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。预打包的工具集:

工具集内容
codingToolsread、bash、edit、write(默认)
readOnlyToolsread、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 工具集绑定到 cwdcreateCodingTools(cwd)
将 readOnlyTools 工具集绑定到 cwdcreateReadOnlyTools(cwd)
只将一个工具绑定到 cwdcreateReadTool(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() 等——每次替换后都要重新订阅
使用另一种语言驱动 Agentpi --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 协议

评论

支持 Markdown