Codex CLI 用 `--add-dir` 让 agent 同时读写两个目录
有个需求出现的频率高得离谱:代码仓库在一个目录,但你想让 agent 把生成的东西写到仓库外面另一个目录去——比如构建产物目录、一个专门放草稿的目录、或者同一台机器上另一个需要同步改动的目录。默认情况下它会被拦住,因为 Codex(OpenAI Codex)的默认沙箱模式就是把可写范围圈在工作区里的。
Codex CLI 给了一个专门解决这件事的选项:--add-dir。本机在 codex-cli 0.147.0(Windows 11)上执行 codex --help,这一项的说明是「主工作区之外额外可写目录」。下面按能直接抄的命令、产出物、验收、不适用场景四段来讲。
一、先把 -C 和 --add-dir 分清楚
这两个选项经常被搞混,混了之后表现会很怪。在 codex-cli 0.147.0 的 help 里:
| 选项 | 官方说明 |
|---|---|
-C, --cd <DIR> | 指定 agent 的工作根目录 |
--add-dir <DIR> | 主工作区之外额外可写目录 |
区别是:-C 是换——把主工作区挪到别处,你原来的目录反而不再是工作区了;--add-dir 是加——主工作区不动,在它之外再挂一个可写目录。
所以「同时读写两个目录」的正确姿势是:用 -C 指定主的那个(或者干脆在那个目录里启动 Codex),再用 --add-dir 把第二个挂上去。拿 -C 去挂第二个目录,只会把第一个目录甩掉。
还有一个细节值得留意:help 里 -i, --image <FILE>... 这类可以给多个值的选项,参数占位符后面是带 ... 的,--enable <FEATURE> 的说明里也明写了「可重复」;而 --add-dir <DIR> 两样都没有。我们没有实测过给两次 --add-dir 会怎样,所以如果你要挂的额外目录不止一个,我更建议直接走配置里的数组形式(见第二节第三段),别赌命令行能重复。
二、命令与配置怎么写
1. 交互式会话临时挂一个目录
codex -C /path/to/main-repo --add-dir /path/to/build-output
Windows 侧(Git Bash 或 PowerShell 里)路径换成本地写法:
codex -C D:\work\main-repo --add-dir D:\work\build-output
每个选项为什么在这:-C 明确主工作区,省得你在哪个目录敲命令就变成哪里是工作区,脚本化之后这一点尤其重要;--add-dir 把第二个目录纳进可写范围。
需要注意,--add-dir 解决的是「沙箱边界让不让写」,不解决「要不要问你」。要不要弹审批是另一个维度,由 -a, --ask-for-approval 控制,三档的官方释义分别是:untrusted(只有 ls、cat、sed 这类受信任命令免审批,其余升级给用户)、on-request(由模型决定何时请求审批)、never(从不询问,执行失败直接回传给模型)。两个维度是正交的,别指望调审批策略能替代 --add-dir。
2. 非交互跑一次
如果这是个要塞进脚本的活儿,用 codex exec:
codex exec -C /path/to/main-repo --add-dir /path/to/build-output \
--json -o last-message.txt "把 main-repo 的变更同步整理一份说明到 build-output"
三个选项各自的用处:--json 让事件以 JSONL 打到 stdout,方便后面接管道;-o, --output-last-message <FILE> 把 agent 的最后一条消息写到指定文件;prompt 作为位置参数直接带进去。补充两条 help 里的行为,脚本里用得上:不给位置参数或者给 - 时会从 stdin 读;如果 stdin 是管道并且你同时给了 prompt,stdin 的内容会作为 <stdin> 块追加到 prompt 后面,而不是二选一。
如果第二个目录不是 Git 仓库,codex exec 还有个 --skip-git-repo-check,允许在非 Git 仓库里运行。
3. 把它固化到配置里
天天都要挂同一个目录,就别每次敲命令行了。官方《Configuration Reference》里对应的键是 sandbox_workspace_write.writable_roots(数组,说明是「workspace-write 下额外的可写根」),配合 sandbox_mode:
sandbox_mode = "workspace-write"
[sandbox_workspace_write]
writable_roots = ["D:/work/build-output"]
network_access = false
network_access 那一行是顺手写清楚的:workspace-write 下是否出网由这个布尔键单独控制,跟可写目录没有继承关系,两件事别混着记。TOML 的双引号字符串里反斜杠是转义字符,Windows 路径写成正斜杠或者把反斜杠双写都行,写单反斜杠迟早出事。
以上为按官方文档键位组合的示例,未逐项实测,以官方文档为准。
不想改文件、只想临时覆盖一次,可以用顶层的 -c, --config <key=value>。它用点号路径表示嵌套,value 按 TOML 解析,解析失败会按字面字符串处理:
codex -c 'sandbox_workspace_write.writable_roots=["D:/work/build-output"]' -C D:/work/main-repo
「解析失败按字面字符串处理」这条要记牢——引号少打一个,它不一定报错,可能默默把一串文本当成了值。
再往上一层,如果你有好几套固定的目录组合,官方还提供了权限档:permissions.<name>.extends 可以继承 :workspace,permissions.<name>.workspace_roots.<path> 用布尔值把某个路径纳入该档的工作区根,permissions.<name>.filesystem.<path-or-glob> 取 read / write / deny,命令行侧用 -P, --permission-profile <NAME> 套用。这套东西比 --add-dir 表达力强得多,但也更容易配错,日常临时用还是 --add-dir 划算。
三、产出物长什么样
先说别抱错期待的部分:--add-dir 本身没有产出物,它只是把一个目录纳进可写边界。真正落盘的是 agent 执行的命令,落在哪、叫什么名字,取决于你 prompt 里怎么要求。
有依据可讲的是 codex exec 这一侧的输出形态:--json 是事件流,格式是 JSONL,一行一个事件打到 stdout(具体有哪些字段我们没有采集,所以这里不给字段名,你自己跑一次接住第一行看看就清楚了);-o, --output-last-message <FILE> 只写最后一条消息到文件——注意是最后一条,不是全过程,想留全程得靠 --json 那一路。这两个可以同时用,一个给机器接管道,一个给人看结论。
另外,会话本身默认是会落盘的:本机 ~/.codex/ 下有 sessions/ 目录按年份分子目录存 rollout。不想留痕的一次性任务,codex exec 有 --ephemeral,不把会话文件落盘。
四、怎么验收,最容易错在哪
按顺序检查这几处,基本能覆盖八成翻车:
第一处,配置到底加载成功了没。 这是本机在 codex-cli 0.147.0(Windows 11)上实测出来的一条很实用的结论:故意给 -c 传一段语法不合法的 TOML(codex -c 'features=[unclosed' doctor --summary),命令没有崩溃退出,doctor 照常跑完,但输出里出现了一行:
✗ config config could not be loaded - Fix the reported config error, then rerun codex doctor.
也就是说,配置写坏了不一定有醒目的报错,它可能只是「没生效」。所以改完 writable_roots 之后第一件事就是跑一次 codex doctor --summary,盯住 config 那一行是不是 loaded。
第二处,别指望 --strict-config 能兜住拼写错误。 本机实测 codex -c model_reasoning_effortt=high --strict-config exec --help(注意那个多打的 t),结果是正常打印 help,没有报未知字段错误。说明这个校验发生在真正加载配置去跑会话的时候,--help 这类不进入会话的路径根本不触发。所以「我加了 --strict-config,没报错就说明键名没写错」是错的。
第三处,doctor 的 sandbox 行能看什么、不能看什么。 本机 codex doctor --summary 的 Configuration 分组里,sandbox 那一行显示的是 restricted fs + restricted network · approval OnRequest。它告诉你的是沙箱大类状态和当前审批策略,不会列出你挂了哪些可写根,别拿它来核对目录清单。
第四处,路径本身。 目录必须真实存在、大小写和分隔符要对。这一步出错的表现往往不是报错,而是 agent 写了个别的地方,或者干脆说没权限。
顺带一个反例,省得你走弯路:codex sandbox 这个子命令看起来像是能拿来验证沙箱边界的——本机确实实测过,在它的默认状态下执行 cmd /c "echo hi > <仓库路径>\__sbtest.txt",命令返回后目标文件并不存在,写入没落盘。但它的 --sandbox-state-* 系列选项是绑在一起的:只给 --sandbox-state-disable-network 而不给 --sandbox-state-json,会直接报参数缺失,提示 --sandbox-state-json <JSON> 是必需参数。它是一个独立的子命令,我们没有验证过它的状态和你会话里 --add-dir 的边界是同一份,所以别把它当成 --add-dir 的验收工具。
Windows 侧还有两条要单独盯:一是官方文档提到,如果目录是「writable by Everyone」(对所有人可写),Codex 会告警,提示 Windows 权限过宽——你挂的那个额外目录如果是从别处拷来的、或者放在公共盘上,很容易撞上这条;二是 Windows 的沙箱是在 PowerShell 中原生运行的一套机制,跟 Linux 那套完全不同,Linux/WSL2 下沙箱起不来要先查 bubblewrap(bwrap)装没装,这条排查步骤不要往 Windows 上套。Windows 侧真出问题,官方给的顺序是重启 Codex、重试 elevated 初始化、必要时回退到 unelevated、然后发送诊断,日志在 CODEX_HOME/.sandbox/sandbox.log。
五、什么情况别这么干
- 想让它写系统目录、或者你自己都说不清边界的目录,就不要用
--add-dir硬开。这个选项的价值恰恰在于边界是明确的一个目录;一旦你开始一个个往上加,说明你要的其实是别的模式,那就该正面评估danger-full-access(官方原文:移除文件系统与网络边界,只在你确实要它拥有完全访问权时使用)的代价,而不是用--add-dir打补丁。顺带说一句,-a never也不是「放开权限」,它只改变「要不要问你」,沙箱边界还在——这两个概念混淆是最常见的误解。 - 第二个目录里有别人的东西(同事的工作区、共享盘、生产配置目录),别挂。沙箱边界是「能不能写」,不是「写得对不对」,写错了它不会替你回滚。
- 只需要读、不需要写的场景,
--add-dir是「额外可写目录」,语义上就重了。真要严格控制,走permissions.<name>.filesystem.<path-or-glob>显式给read。 - 拿不到管理员批准的 Windows 机器上要谨慎推进。官方把
elevated标为首选模式,但它需要管理员批准的初始化设置;回退的unelevated用从当前用户派生的受限 token 运行,官方自己写明「保护更弱」。也就是说,同样一条--add-dir命令,在两种模式下的实际隔离强度并不一样,跨机器复制命令前先确认对面是哪种模式。 - CI 或者无人值守的环境里,别只靠
--add-dir当唯一防线。真要在这种环境跑,codex exec那套--ephemeral、--ignore-user-config(不加载$CODEX_HOME/config.toml,但 auth 仍然使用CODEX_HOME)配合起来把变量收敛掉,比多挂几个目录重要得多。
最后提醒一句版本的事:本机采集当天,开头执行 codex --version 得到 codex-cli 0.131.0,十几分钟后再执行同一命令得到 codex-cli 0.147.0。选项名、默认值、特性阶段都会随版本变,排查任何问题前,先跑一次 codex --version 看当次的实时输出,别用记忆里的版本号。
相关阅读
- 用
-i把截图和设计稿带进 Codex 首轮指令 - 用
notify让 Codex 跑完主动叫你:配置写法、通知脚本与验收清单 - Codex shell 环境变量策略:哪些变量会被带进子进程
- Codex 的六个使用面:一张图看懂该用哪个
本文依据 Codex 官方文档(learn.chatgpt.com/docs/ 的《Codex CLI》《Sandbox》《Windows sandbox》《Configuration Reference》页面)整理,核对日 2026-08-09;文中标注「本机实测」的部分基于 codex-cli 0.147.0 / Windows 11 环境下的只读命令输出。产品功能、模型与价格以官方最新说明为准。