Yoda
参考Agent 设计指南Runtime 生命周期

启动

CC 单二进制多模式(交互 Ink / -p 打印 / daemon / bridge,入口快速路径分发)vs Codex 多子命令(tui / exec / app-server);harness 统一用 PTY + 注册表声明式拼装启动参数

启动

结论

CC 和 Codex 都是「一个二进制、多种入口」,但分发方式相反:CC 在入口文件里用参数嗅探的快速路径分发(--version 零依赖、mcp servedaemonremote-controlps/logs/attach/kill--bare 等先于主 CLI 加载短路),运行身份再通过 CLAUDE_CODE_ENTRYPOINT 环境变量贯穿全程(cli/sdk-cli/sdk-ts/sdk-py/mcp/claude-vscode/claude-desktop/remote);Codex 则是 clap 子命令树(裸 codex [PROMPT] 进 TUI,codex exec 非交互,codex app-server 给 GUI 嵌入,codex mcp-server 给 MCP 宿主),交互/非交互共享一套 SharedCliOptions-m/-s/-C/--add-dir/--yolo)。对 harness 的关键事实:交互模式两家都必须 PTY(CC 是 Ink、Codex 是 ratatui 备用屏),headless 模式两家都可纯管道(CC -p --output-format stream-json,Codex exec --json + stdin prompt -)。Yoda 的做法是注册表声明每个 runtime 的 flag 语义(autoApprove/resume/session-id/initial-prompt),buildAgentCommand 统一拼装后 spawn node-pty,没有 prompt flag 的 runtime 降级为键击注入。

研究问题

  • 各 CLI 的关键启动 flag(自动批准、resume、session-id、初始 prompt)对照?
  • 入口/运行模式如何检测与切换?PTY 还是 pipe?
  • 环境变量注入与认证继承的差异?
  • harness 如何统一 spawn 两家 CLI?

各 Agent 设计与实现

Claude Code

入口分发entrypoints/cli.tsx 是 bootstrap,所有 import 都是动态的,按 argv 依次短路 [一手源码]:

  • --version/-v/-V:零模块加载直接打印(cli.tsx:36-42);
  • --claude-in-chrome-mcp / --chrome-native-host / --computer-use-mcp:以 MCP server/native host 身份运行(cli.tsx:72-93);
  • remote-control|rc|remote|sync|bridge:bridge 模式,先查 OAuth token 再查组织策略 allow_remote_controlcli.tsx:112-162);
  • daemon / --daemon-worker:后台 supervisor 与 worker(cli.tsx:100-180);
  • ps|logs|attach|kill|--bg:针对 ~/.claude/sessions/ 注册表的后台会话管理(cli.tsx:185-209);
  • --worktree --tmux:加载完整 CLI 前直接 exec 进 tmux(cli.tsx:248-274);
  • --update/--upgrade 重写为 update 子命令;--bare 提前设 CLAUDE_CODE_SIMPLE=1cli.tsx:276-285);
  • 全部不命中才加载 main.js 进入完整 CLI(cli.tsx:287-296)。

运行身份(entrypoint)main.tsx:519-539 在启动时写 process.env.CLAUDE_CODE_ENTRYPOINT——已有值(SDK/IDE 注入)则尊重;mcp servemcp;GitHub Action 设 claude-code-github-action;否则按是否非交互设 sdk-clicli [一手源码]。下游消费方包括会话存储(utils/sessionStorage.ts:423-425getEntrypoint() 直接读该变量)、内置 agent 可用性、REPL 工具开关等;统计侧映射出 sdk-typescript/sdk-python/claude-vscode/claude-desktop/remote 等取值(main.tsx:820-829)[一手源码]。这意味着 harness 可以(也应该)通过设置 CLAUDE_CODE_ENTRYPOINT 声明自己的接入身份 [推断:变量是公开读取的事实接口,但未在 docs 列为稳定 API]。

交互 vs 打印模式:交互模式是 Ink(React)TUI,需要 TTY raw mode;-p/--print 进入打印模式,支持 cat file | claude -p "query" 管道、--output-format text|json|stream-json--input-format stream-json(双向 NDJSON)、--include-partial-messages--max-turns--max-budget-usd--no-session-persistence(docs cli-reference.md CLI flags 表)[一手文档]。

会话与权限 flag-c/--continue(当前目录最近会话)、-r/--resume <id|name>--session-id <uuid>(指定会话 ID,Yoda 用它做会话隔离)、--fork-session--dangerously-skip-permissions(等价 --permission-mode bypassPermissions)、--permission-mode default|acceptEdits|plan|auto|...--model--settings <file|json>--setting-sources--bare(跳过 hooks/skills/plugins/MCP/CLAUDE.md 自发现,加速脚本化调用)(docs cli-reference.md)[一手文档]。

Codex CLI

子命令树:根 CLI 是 MultitoolCli,「不带子命令时所有 option 转发给交互 TUI」(cli/src/main.rs:88-118 及注释、override_usage = "codex [OPTIONS] [PROMPT]")[一手源码]。主要子命令(main.rs:120-205):exec(别名 e,非交互)、reviewlogin/logoutmcpmcp-server(stdio MCP server)、app-server(实验性,GUI 嵌入用 JSON-RPC server,含 daemon 子命令)、remote-controlapp(桌面 App 启动器)、completionupdatedoctorsandboxapply(别名 a)、resume/fork/archive/unarchivecloud [一手源码]。

TUI flagtui/src/cli.rs:10-75):位置参数 prompt;resume 走子命令(picker 默认 / --last / 直接给 session id);-a/--ask-for-approval 审批策略;--search 开 web 搜索;--no-alt-screen 不用备用屏(嵌入友好);--strict-config 配置错误即退出 [一手源码]。

共享 flag(交互/非交互同源,utils/cli/src/shared_options.rs:9-63):-i/--image-m/--model--oss/--local-provider-p/--profile(注意:Codex 的 -p 是 profile,CC 的 -p 是 print——harness 易踩)、-s/--sandbox--dangerously-bypass-approvals-and-sandbox(别名 --yolo)、--dangerously-bypass-hook-trust-C/--cd--add-dir [一手源码]。

exec 模式exec/src/cli.rs:14-103):prompt 可为位置参数,为空或 - 时从 stdin 读;--json 输出 JSONL 事件流;--output-last-message <FILE> 把最终回复落盘;--output-schema <FILE> 结构化输出;--ephemeral 不留会话痕迹;--skip-git-repo-check 允许在非 git 目录跑;--color;子命令 exec resume <id>|--last [一手源码]。非交互模式默认 RUST_LOG=error,日志直接打在输出里(codex/docs/install.md "Tracing")[一手文档]。

环境变量CODEX_HOME(配置/会话/版本缓存根);CODEX_MANAGED_BY_NPM/BUN + CODEX_MANAGED_PACKAGE_ROOT(npm shim 注入,见安装章);认证可经 codex login --with-api-key 持久化或环境变量 access token(main.rs:80 引入的 read_codex_access_token_from_env)[一手源码];RUST_LOG 控制日志 [一手文档]。

PTY 与终端约束

  • CC 交互模式是 Ink(React for CLIs),依赖 TTY raw mode;-p 打印模式完全可在管道里跑(docs cli-reference.mdcat file | claude -p 即官方示例)[一手文档]。
  • Codex TUI 是 ratatui,默认进入备用屏(alternate screen),提供 --no-alt-screen 关闭以便嵌入式/录屏场景(tui/src/cli.rs:71-72);codex exec 在无 TTY 时正常工作,且会用 stdin().is_terminal() 判断是否从 stdin 读 prompt(exec/src/lib.rs:1844-1858)[一手源码]。
  • 终端尺寸:Codex doctor 把 <80x24 列为 warning(cli/src/doctor.rs:142-143NARROW_TERMINAL_COLUMNS/ROWS);Yoda spawn PTY 的兜底尺寸正是 80x24(core/terminals/impl/local-terminal-provider.ts:20-21),实际尺寸跟随前端 xterm 并随窗口 resize 同步 [一手源码]。

差异矩阵

维度Claude CodeCodex CLI
入口形态单命令 + argv 嗅探快速路径clap 子命令树
交互模式claude ["prompt"](Ink/React,需 TTY)codex ["prompt"](ratatui 备用屏,--no-alt-screen 可关)
非交互claude -p "q"--output-format text|json|stream-jsoncodex exec "q"--json JSONL,prompt - 读 stdin
stdin 喂 promptcat x | claude -p "q"codex exec --json -(stdin 即 prompt)
继续/恢复-c/--continue-r/--resume <id|name>codex resume [id]/--last(picker 默认)、codex exec resume
指定会话 ID--session-id <uuid>(新会话可指定)不支持预指定;resume 用既有 id [一手源码:tui/src/cli.rs 无对应 flag]
Fork--fork-session(resume 时换新 id)codex fork(子命令)
自动批准--dangerously-skip-permissions / --permission-mode--dangerously-bypass-approvals-and-sandbox--yolo)/ -a + -s
工作目录进程 cwd(--add-dir 扩权)-C/--cd <DIR> 显式指定 + --add-dir
-p 含义print 模式profile(配置档)
嵌入用 server--input-format stream-json(NDJSON 双向);SDKcodex app-server(JSON-RPC,实验性)+ daemon
运行身份标识CLAUDE_CODE_ENTRYPOINT 环境变量无对等物(按子命令区分)

最小复现

# headless 一发(两家管道均可,无需 PTY)
echo "say hi" | claude -p --output-format text
codex exec --skip-git-repo-check --color never --json - <<< "say hi"

# 会话可控启动(Yoda 实际拼出来的命令形态)
claude --session-id 8f14e45f-... --dangerously-skip-permissions "initial prompt"
codex --dangerously-bypass-approvals-and-sandbox "initial prompt"
# resume:
claude --resume 8f14e45f-...
codex resume --cd /path/to/worktree 8f14e45f-...

(命令形态由 yoda/src/main/core/conversations/impl/agent-command.ts:94-147 + src/shared/runtime-registry.ts:189-236 推出 [一手源码]。)

Harness 接入建议(Yoda 实践)

Yoda 已上线的启动管线(全部 [一手源码]):

  1. 声明式 flag 注册表:每个 runtime 声明 autoApproveFlag / initialPromptFlag / resumeFlag / sessionIdFlag / defaultArgs / useKeystrokeInjection 等(src/shared/runtime-registry.ts:33-97)。CC:--dangerously-skip-permissions + --session-id + --resume;Codex:--dangerously-bypass-approvals-and-sandbox + resume 子命令带位置 session id,resume 时额外补 --cd <worktree>agent-command.ts:122-134 有 codex 专门分支)。
  2. 统一拼装buildAgentCommand() 按「defaultArgs → session/resume → autoApprove → initialPrompt → extraArgs」顺序拼接;自定义 CLI 前缀经 parseShellWords 解析且拒绝 shell 元字符与 builtin$|;source 等),防注入(agent-command.ts:9-92)。
  3. PTY spawn:交互会话一律 node-pty,初始 cols/rows 来自前端 xterm 实际尺寸(local-conversation.ts:97-204);env 由 buildAgentEnv 合成(provider API keys + hook 回调端口/token + task env)。可选 tmux 包一层实现会话持久化。
  4. prompt 投递降级链:有 initialPromptFlag 走 CLI 参数;没有(Amp/OpenCode/Jules 等)走键击注入;图片走剪贴板粘贴(CC 标记 clipboardImagePaste: true,prompt 必须改为注入投递,否则回合会在图片落地前开始——local-conversation.ts:141-150 注释)。
  5. headless 复用:AI 小功能(任务命名、会话摘要)不走 API client,而是 runAgentCli() 直接 spawn claude --print --model {m} --output-format text --no-session-persistencecodex exec --ephemeral --sandbox read-only --json -,对 Codex 在收到最终 agent_message JSONL 事件时提前 kill 省时(core/agent-cli/run-agent-cli.ts:11-49, 68-96)。

改进方向:

  • Yoda 未设置 CLAUDE_CODE_ENTRYPOINT,CC 会把 Yoda 会话记为 cli;设为自有标识可让转录/统计区分来源 [推断];
  • Codex 嵌入可逐步从「PTY + TUI」迁到 codex app-server(JSON-RPC 协议有 app-server-protocol crate 定义),换取结构化事件而非终端流 [推断,app-server 仍标记 experimental]。

失效条件

  • CC 入口快速路径集合变化(cli.tsx 为 v2.1.88 重构源;docs 的 flag 表为 2026-06,新 flag 需回归)
  • CC --session-id/--resume/--dangerously-skip-permissions 语义变化(Yoda 注册表直接依赖)
  • CLAUDE_CODE_ENTRYPOINT 取值集合或读取点变化
  • Codex TUI/exec flag 重构(tui/src/cli.rsexec/src/cli.rsshared_options.rs 任一变更)
  • Codex app-server 脱离 experimental(届时本章嵌入建议需升级为主路径)
  • Codex resume 子命令形态(picker/--last/位置 id)调整

参考资料

  • CC docs:claude-code-docs/docs/cli-reference.md(CLI commands / CLI flags)、setup.md
  • CC 重构源:src_2026-03-31/entrypoints/cli.tsxmain.tsx:519-539, 820-829utils/sessionStorage.ts:423-425
  • Codex 源(b89ce9a):codex-rs/cli/src/main.rs(MultitoolCli/Subcommand)、tui/src/cli.rsexec/src/cli.rsutils/cli/src/shared_options.rscodex/docs/exec.mdcodex/docs/install.md
  • Yoda 源:src/main/core/conversations/impl/{agent-command,local-conversation}.tssrc/main/core/agent-cli/run-agent-cli.tssrc/shared/runtime-registry.ts

On this page