启动
CC 单二进制多模式(交互 Ink / -p 打印 / daemon / bridge,入口快速路径分发)vs Codex 多子命令(tui / exec / app-server);harness 统一用 PTY + 注册表声明式拼装启动参数
启动
结论
CC 和 Codex 都是「一个二进制、多种入口」,但分发方式相反:CC 在入口文件里用参数嗅探的快速路径分发(--version 零依赖、mcp serve、daemon、remote-control、ps/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_control(cli.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=1(cli.tsx:276-285);- 全部不命中才加载
main.js进入完整 CLI(cli.tsx:287-296)。
运行身份(entrypoint):main.tsx:519-539 在启动时写 process.env.CLAUDE_CODE_ENTRYPOINT——已有值(SDK/IDE 注入)则尊重;mcp serve 设 mcp;GitHub Action 设 claude-code-github-action;否则按是否非交互设 sdk-cli 或 cli [一手源码]。下游消费方包括会话存储(utils/sessionStorage.ts:423-425 的 getEntrypoint() 直接读该变量)、内置 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,非交互)、review、login/logout、mcp、mcp-server(stdio MCP server)、app-server(实验性,GUI 嵌入用 JSON-RPC server,含 daemon 子命令)、remote-control、app(桌面 App 启动器)、completion、update、doctor、sandbox、apply(别名 a)、resume/fork/archive/unarchive、cloud [一手源码]。
TUI flag(tui/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打印模式完全可在管道里跑(docscli-reference.md的cat 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-143的NARROW_TERMINAL_COLUMNS/ROWS);Yoda spawn PTY 的兜底尺寸正是 80x24(core/terminals/impl/local-terminal-provider.ts:20-21),实际尺寸跟随前端 xterm 并随窗口 resize 同步 [一手源码]。
差异矩阵
| 维度 | Claude Code | Codex CLI |
|---|---|---|
| 入口形态 | 单命令 + argv 嗅探快速路径 | clap 子命令树 |
| 交互模式 | claude ["prompt"](Ink/React,需 TTY) | codex ["prompt"](ratatui 备用屏,--no-alt-screen 可关) |
| 非交互 | claude -p "q",--output-format text|json|stream-json | codex exec "q",--json JSONL,prompt - 读 stdin |
| stdin 喂 prompt | cat 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 双向);SDK | codex 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 已上线的启动管线(全部 [一手源码]):
- 声明式 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 专门分支)。 - 统一拼装:
buildAgentCommand()按「defaultArgs → session/resume → autoApprove → initialPrompt → extraArgs」顺序拼接;自定义 CLI 前缀经parseShellWords解析且拒绝 shell 元字符与 builtin($、|、;、source等),防注入(agent-command.ts:9-92)。 - PTY spawn:交互会话一律 node-pty,初始
cols/rows来自前端 xterm 实际尺寸(local-conversation.ts:97-204);env 由buildAgentEnv合成(provider API keys + hook 回调端口/token + task env)。可选 tmux 包一层实现会话持久化。 - prompt 投递降级链:有
initialPromptFlag走 CLI 参数;没有(Amp/OpenCode/Jules 等)走键击注入;图片走剪贴板粘贴(CC 标记clipboardImagePaste: true,prompt 必须改为注入投递,否则回合会在图片落地前开始——local-conversation.ts:141-150注释)。 - headless 复用:AI 小功能(任务命名、会话摘要)不走 API client,而是
runAgentCli()直接 spawnclaude --print --model {m} --output-format text --no-session-persistence或codex exec --ephemeral --sandbox read-only --json -,对 Codex 在收到最终agent_messageJSONL 事件时提前 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-protocolcrate 定义),换取结构化事件而非终端流 [推断,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.rs、exec/src/cli.rs、shared_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.tsx、main.tsx:519-539, 820-829、utils/sessionStorage.ts:423-425 - Codex 源(b89ce9a):
codex-rs/cli/src/main.rs(MultitoolCli/Subcommand)、tui/src/cli.rs、exec/src/cli.rs、utils/cli/src/shared_options.rs、codex/docs/exec.md、codex/docs/install.md - Yoda 源:
src/main/core/conversations/impl/{agent-command,local-conversation}.ts、src/main/core/agent-cli/run-agent-cli.ts、src/shared/runtime-registry.ts