CLI Coding Agents 三国志:Claude Code vs Pì vs OpenCode
三款终端编程 Agent 在理念、架构与自治策略上的深度源码对比
三款终端编程 Agent 在理念、架构与自治策略上的深度源码对比
它们是谁
2025 年是 CLI Coding Agent 爆发的一年。从 Anthropic 官方出品的 Claude Code,到 libgdx 作者 Mario Zechner 主导的开源项目 Pì(原名 Ampcode),再到 SST 团队打造的 OpenCode——三个项目都在回答同一个问题:如何让 LLM 在你的终端里真正成为一个写代码的搭档?
但它们给出的答案截然不同。
一、核心理念:三种世界观
Claude Code——“结构化的工作流引擎”
Claude Code 的设计哲学是流程驱动。它把编程任务拆解为严格的阶段式工作流。以其 feature-dev 插件为例,一个新功能的开发被分为七个阶段:
Discovery → Exploration → Clarification → Architecture → Implementation → Review → Summary
每个阶段都有明确的输入输出,关键节点(如进入 Implementation)需要用户明确批准才能继续。这不是一个随叫随到的助手,更像是一个带着方法论的技术顾问——它会先理解你的代码库,再提出方案,等你点头后才动手。
Claude Code 内置了丰富的多 Agent 协作机制:code-explorer 并行探索代码库、code-architect 设计架构方案、code-reviewer 用置信度评分过滤误报。这些 Agent 可以并行运行,最后由主 Agent 汇总结果。
一句话:Claude Code 相信结构产生质量。
Pì——“极致可扩展的最小内核”
Pì 的哲学写在 README 第一行:
“Pi is aggressively extensible so it doesn’t have to dictate your workflow.”
Pì 的核心极度克制。看看它主动不做的事情:
| 功能 | Pì 的态度 |
|---|---|
| MCP 支持 | ”Build CLI tools with READMEs, or build an extension” |
| Sub-agents | ”Spawn pi instances via tmux, or build your own with extensions” |
| 权限弹窗 | ”Run in a container, or build your own confirmation flow” |
| Plan Mode | ”Write plans to files, or build it with extensions” |
| TODOs | ”They confuse models. Use a TODO.md file” |
| 后台 Bash | ”Use tmux. Full observability, direct interaction” |
这不是懒,是一种激进的设计选择:核心只提供原语,一切高层功能由扩展实现。内核只有 7 个内置工具(read、bash、edit、write、grep、find、ls),但通过 Extension API 可以拦截整个 Agent 生命周期的 20+ 个事件,注册自定义工具、命令、快捷键,甚至注入 UI 组件。
一句话:Pì 相信用户比框架更懂自己的工作流。
OpenCode——“开放标准的全栈产品”
OpenCode 走的是产品化路线。它的核心关键词是:开源、多端、协议优先。
架构上,OpenCode 采用 Client/Server 分离:核心引擎作为 Server 运行(opencode serve),TUI、Web UI、Desktop App(Tauri)都是 Client。这意味着你可以远程连接一个运行在服务器上的 OpenCode 实例,用本地浏览器操作。
它积极拥抱标准协议:原生支持 MCP(Model Context Protocol)用于外部工具集成,支持 ACP(Agent Client Protocol)使自己可以作为其他系统的 Agent 被调用,还有开箱即用的 LSP 支持。
在 LLM 适配上,OpenCode 是三者中最开放的——通过 Vercel AI SDK 对接 20+ 家模型提供商,包括 Anthropic、OpenAI、Google、Mistral、Groq 等,甚至用 Tree-sitter 解析 bash 命令来适配不同模型的行为差异。
一句话:OpenCode 相信开放标准是通往生产级工具的正确路径。
二、核心架构:三种工程选择
技术栈一览
| Claude Code | Pì | OpenCode | |
|---|---|---|---|
| 语言 | TypeScript | TypeScript | TypeScript |
| 运行时 | Node.js | Node.js / Bun | Bun |
| 包管理 | npm | npm workspaces | Bun |
| UI 框架 | Ink (React) | 自研 pi-tui | SolidJS + OpenTUI |
| LLM 接入 | Anthropic API | 自研 pi-ai(20+ providers) | Vercel AI SDK |
| 数据持久化 | 文件系统 | 文件系统(Session JSON) | SQLite (Drizzle ORM) |
| 扩展机制 | Hooks + Plugins | Extensions + Skills + Packages | Plugins + Custom Tools |
Claude Code:Plugin 即一切
Claude Code 的扩展单元是 Plugin——一个包含 hooks、commands、agents、skills 的目录。其 Marketplace 机制内置了 12+ 个官方插件:
claude-code/
├── plugins/
│ ├── feature-dev/ # 7 阶段功能开发流程
│ ├── code-review/ # 多 Agent 并行代码审查
│ ├── ralph-wiggum/ # 自引用循环(自治 Agent)
│ ├── hookify/ # 从对话模式自动生成 hooks
│ ├── security-guidance/ # 安全模式检测(9 种漏洞模式)
│ └── plugin-dev/ # 插件开发工具链
Hook 系统是 Claude Code 的安全基石。Hooks 用 Python/Shell 脚本在工具执行前后拦截,可以 warn(警告)或 block(阻断):
# security_reminder_hook.py - PreToolUse 拦截
patterns = [
("command injection", r"child_process\.exec"),
("XSS", r"dangerouslySetInnerHTML"),
("eval injection", r"\beval\s*\("),
("pickle deserialize", r"pickle\.load"),
]
# 匹配到 → exit code 2 → 阻断工具执行
这种”声明式安全”思路很有意思:你不需要写代码来理解安全规则,Hookify 插件甚至能从你和 Claude 的对话中自动提取模式并生成 hook。
Pì:Extension 即操作系统
Pì 的 Extension 系统更像一个操作系统级 API。一个 Extension 是一个 TypeScript 模块,通过 ExtensionAPI 可以做几乎任何事:
export default function (pi: ExtensionAPI) {
// 拦截工具调用
pi.on("tool_call", async (event, ctx) => {
if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) {
const ok = await ctx.ui.confirm("Dangerous!", "Allow rm -rf?");
if (!ok) return { block: true, reason: "Blocked by user" };
}
});
// 注册自定义工具
pi.registerTool({
name: "deploy",
description: "Deploy to production",
parameters: Type.Object({ version: Type.String() }),
async execute(toolCallId, params, signal, onUpdate, ctx) {
return { content: [{ type: "text", text: "Deployed!" }], details: {} };
},
});
// 注册命令、快捷键、UI 组件...
}
值得注意的是 Pì 的 operations 抽象。每个内置工具的底层操作(文件读写、bash 执行)都是可替换的接口:
interface BashOperations {
exec(command: string, cwd: string, options: {
onData: (data: Buffer) => void;
signal?: AbortSignal;
timeout?: number;
}): Promise<{ exitCode: number | null }>;
}
这意味着你可以把 bash 工具的后端替换成 SSH 远程执行、Docker 容器执行、甚至 WebAssembly 沙箱——而 LLM 完全无感知。
Example 目录里有 30+ 个扩展示例,从权限门控(permission-gate)、受保护路径(protected-paths)、Git 检查点(git-checkpoint),到——没开玩笑——在终端里玩贪吃蛇和太空侵略者(snake.ts、space-invaders.ts)。还有一个 doom-overlay 可以在 Agent 执行工具时叠加 DOOM 游戏画面。这就是 Pì 的气质:核心严肃,扩展疯狂。
OpenCode:前后端分离的 Agent 产品
OpenCode 的架构最接近一个传统软件产品:
opencode/packages/
├── opencode/ # 核心引擎(Server)
│ ├── src/
│ │ ├── session/ # 会话管理(SQLite)
│ │ ├── permission/ # 权限系统
│ │ ├── tool/ # 工具注册
│ │ ├── agent/ # Agent 定义
│ │ ├── mcp/ # MCP 集成
│ │ └── cli/cmd/tui/ # TUI(SolidJS)
├── app/ # Web UI(SolidJS)
├── desktop/ # 桌面应用(Tauri)
├── plugin/ # 插件 SDK
└── sdk/js/ # JS SDK
TUI 用 SolidJS 响应式框架配合自研 OpenTUI 渲染引擎构建,支持主题切换、Dialog 系统、快捷键绑定。Agent 切换用 Tab 键,在 build(全权限)和 plan(只读)之间自由切换。
SQLite 做持久化这个选择值得一提——相比 Claude Code 和 Pì 的文件系统方案,SQLite 在会话查询、分支管理、状态回溯上有天然优势,代价是部署复杂度稍高。
三、YOLO 哲学:三种信任模型
这是三个项目分歧最大的地方。所谓”YOLO”(You Only Live Once),在 Coding Agent 语境下指的是:Agent 执行操作时,需要多少人类确认?
Claude Code:安全默认,显式跳过
Claude Code 的默认立场是保守的。每个工具调用都有权限分类:
// settings-strict.json
{
"permissions": {
"disableBypassPermissionsMode": "disable",
"ask": ["Bash"],
"deny": ["WebSearch", "WebFetch"]
}
}
allow: 自动执行ask: 需要用户确认deny: 永远拒绝
想要”放飞自我”?有一个明确的、名字就在警告你的 flag:
claude --dangerously-skip-permissions
这个设计精妙在于命名本身就是安全机制——你在团队 Slack 里很难理直气壮地说”我把 dangerously-skip-permissions 打开了然后出事了”。而且这个 flag 可以在企业级 managed settings 中被彻底禁用。
此外,Claude Code 还有一个分层的安全网:
- 工具级权限:命令可以声明自己只需要哪些工具(如
allowed-tools: Bash(git commit:*)) - Hook 级拦截:PreToolUse hooks 可以阻断任何危险操作
- 沙箱模式:
sandbox.autoAllowBashIfSandboxed在沙箱环境下自动放行
Claude Code 的信任模型:不信任是默认值,信任需要显式声明。
Pì:天生 YOLO,安全自己搭
Pì 的做法最为激进:核心没有任何权限系统。
没有 --yolo flag,因为不需要——默认就是全部放行。LLM 说执行 rm -rf /,Pì 就去执行 rm -rf /。README 对此的态度坦诚而直接:
“No permission popups. Run in a container, or build your own confirmation flow with extensions inline with your environment and security requirements.”
如果你需要安全门控,那是你的事。项目提供了 permission-gate.ts 示例扩展作为参考:
// 示例,不是内置功能
pi.on("tool_call", async (event, ctx) => {
if (event.toolName !== "bash") return;
const command = event.input.command as string;
const isDangerous = [/\brm\s+(-rf?|--recursive)/i, /\bsudo\b/i]
.some(p => p.test(command));
if (isDangerous) {
if (!ctx.hasUI)
return { block: true, reason: "Dangerous command blocked (no UI)" };
const choice = await ctx.ui.select("⚠️ Dangerous command. Allow?", ["Yes", "No"]);
if (choice !== "Yes") return { block: true, reason: "Blocked by user" };
}
});
或者你可以更简单地限制工具集:
pi --tools read,grep,find,ls -p "Review this code" # 只读模式
pi --no-tools # 禁用所有工具
这种设计背后的逻辑是:权限模型因人因场景而异。一个在 Docker 容器里跑 CI 的场景和一个在本地开发的场景,需要的安全策略完全不同。与其提供一个”差不多够用”的内置方案,不如让用户精确构建适合自己的方案。
Pì 的信任模型:信任是默认值,安全需要用户主动搭建。
OpenCode:声明式规则,智能默认
OpenCode 走的是中间路线,但实现最为精细。权限系统基于声明式规则集,支持通配符匹配:
// permission/next.ts
export function evaluate(permission: string, pattern: string, ...rulesets: Ruleset[]): Rule {
const merged = merge(...rulesets)
const match = merged.findLast(
(rule) => Wildcard.match(permission, rule.permission)
&& Wildcard.match(pattern, rule.pattern)
)
return match ?? { action: "ask", permission, pattern: "*" }
// 没有匹配规则 → 默认 ask
}
默认权限配置体现了实用主义哲学:
const defaults = PermissionNext.fromConfig({
"*": "allow", // 大部分操作:允许
doom_loop: "ask", // 死循环检测:询问
external_directory: { "*": "ask" }, // 项目外目录:询问
read: {
"*": "allow", // 读文件:允许
"*.env": "ask", // 但 .env 文件:询问
"*.env.*": "ask", // .env.local 等:询问
},
})
而且 OpenCode 的权限是按 Agent 分层的。build Agent 有全权限,plan Agent 被硬编码禁止编辑:
// plan agent
edit: { "*": "deny" } // 不能改文件
plan_exit: "allow" // 可以退出计划模式
用户确认有三种响应方式:
once: 这次允许always: 以后同类操作都允许(本次会话内)reject: 拒绝
Bash 工具还用 Tree-sitter 解析命令语法,提取出访问的目录路径,对项目外目录触发 external_directory 权限检查。这比简单的正则匹配精确得多。
OpenCode 的信任模型:智能默认 + 精细可配 + 按角色隔离。
四、功能侧重:三种产品定位
Claude Code:企业级工作流
Claude Code 的功能设计围绕团队和企业场景:
- 结构化开发流程(feature-dev 七阶段)
- 多 Agent 并行审查(code-review 用 6 个专项 Agent 并行扫描,置信度 ≥80 才报告)
- 企业管控(managed settings、marketplace 白名单、权限分级)
- 安全合规(security-guidance 检测 9 种常见漏洞模式)
独特亮点是 ralph-wiggum 插件:一个自引用循环机制,让 Claude 在同一个 prompt 上反复迭代直到任务完成。Stop hook 拦截退出行为并重新注入 prompt,配合 max-iterations 安全限制。据说在 Y Combinator 测试中,一夜自主生成了 6 个仓库。
Pì:开发者的瑞士军刀
Pì 的功能设计围绕单个开发者的灵活性:
- 跨 Provider 无缝切换(对话中途换模型,thinking block 自动转换为文本)
- Session 树状分支(Git 式的会话管理,可以 fork、navigate、回溯)
- 消息队列(Agent 执行工具时可以插入引导消息,执行完后自动追加 follow-up)
- SDK 模式(嵌入 Node.js 应用,programmatic 控制)
- Pi Packages(npm/git 安装扩展包,一行配置激活)
独特亮点是 operations 抽象——每个工具的底层实现可替换,让你能把 Agent 的”手”伸到 SSH 远程服务器、Docker 容器、甚至自定义沙箱中,而上层逻辑完全不变。
OpenCode:用户体验优先的全栈产品
OpenCode 的功能设计围绕产品体验:
- 精致的 TUI(SolidJS 响应式渲染、主题系统、Dialog 系统)
- 多端统一(TUI + Web + Desktop 共享同一个 Server 和 UI 组件)
- Agent 快速切换(Tab 键在 build/plan 模式间跳转)
- 协议优先(MCP、ACP、LSP 开箱即用)
- 配置优先级链(远程 .well-known → 全局 → 环境变量 → 项目 → managed,7 级覆盖)
独特亮点是 bash 命令的语义理解——用 Tree-sitter 解析命令 AST,识别出 cp source dest 中的 source 和 dest 路径,而不是简单的字符串匹配。这让权限系统能理解”这条命令在访问项目外的目录”这种语义级信息。
五、一张表看全貌
| 维度 | Claude Code | Pì | OpenCode |
|---|---|---|---|
| 核心理念 | 结构化工作流 | 极简内核 + 极致扩展 | 开放标准的全栈产品 |
| 目标用户 | 企业/团队 | 高级开发者 | 广泛开发者 |
| 默认安全性 | 高(需确认) | 低(全部放行) | 中(智能默认) |
| YOLO 机制 | --dangerously-skip-permissions | 默认即 YOLO | 声明式规则配置 |
| 扩展深度 | 中(Hooks + Plugins) | 极深(OS 级 API) | 中(Plugin + Custom Tools) |
| 内置 Agent 数 | 多(explorer/architect/reviewer) | 0(扩展实现) | 4(build/plan/general/explore) |
| MCP 支持 | 有 | 无(扩展实现) | 有(原生) |
| 多端 | 仅 CLI | CLI + SDK + RPC | CLI + Web + Desktop |
| LLM 锁定 | Anthropic 优先 | 完全无锁定 | 无锁定(Vercel AI SDK) |
| Session 管理 | 线性 | 树状分支 | Fork/Revert/Compact |
| 数据存储 | 文件系统 | 文件系统 | SQLite |
写在最后
三个项目代表了 CLI Coding Agent 设计空间中三个清晰的方向:
Claude Code 是”Rails”——有观点、有结构、有最佳实践,适合需要可预测性和安全保障的团队。代价是灵活性受限于框架设计。
Pì 是”Arch Linux”——给你最小的内核和最大的自由,适合知道自己想要什么的高手。代价是一切都需要自己搭建。
OpenCode 是”VS Code”——用产品化的方式提供良好的默认体验,同时保持足够的开放性。代价是在任何单一维度上都不是最极致的。
选哪个?取决于你的答案:
- 想要开箱即用的企业级工作流 → Claude Code
- 想要完全掌控每一个细节 → Pì
- 想要多端、多模型、协议优先的开放平台 → OpenCode
或者,像所有好的工程决策一样:三个都试试,然后选最适合你当下需求的那个。