相信不少人遇到过这种情况:让 Agent 自己改代码、跑测试,它偶尔会"顺手"把旁边的项目或目录一起改了,某些"聪明"的模型尤其喜欢这么干。更麻烦的是,这些变动不一定全是 Agent 直接改文件造成的——它运行的测试脚本,同样可能往项目外写东西。
比如下面这条帖子,讲的就是收尾清理阶段的一起误删事件。

来源:X @ThanksUTrump,2026 年 10 月 29 日。所述经过来自帖子及 Agent 自述,未见完整执行记录。
10 月 30 日,Claude Code 2.1.314 新增了一个限制工作目录外写入的开关,管的是它通过文件工具修改外部文件。[1] 而运行脚本、清理文件用到的 Shell 命令,还得靠 Bash 沙箱来限制。[4]
限定工作目录
假设订单服务、计费服务和公共脚本放在同一目录下:
work/
├── order-service/
├── billing-service/
└── shared-tools/
Claude Code 默认把启动时所在的目录作为主工作目录。这次只改 order-service,就从这个目录启动。如果从父目录 work 启动,另外两个目录也会进入工作范围,即使它们各有自己的 Git 仓库。[3]
先用 claude --version 确认版本达到 2.1.314 或更新,然后把下面的字段合并到订单项目的 .claude/settings.local.json 中:[2]
{
"permissions": {
"blockWritesOutsideWorkingDirectories": true
}
}
如果已有 permissions 对象,就在里面增加这个字段,保留原来的设置。希望团队统一启用这项配置的话,可以放到随仓库分享的 .claude/settings.json。[2]
开启后,Write、Edit、NotebookEdit 会拒绝修改工作目录外的文件。这个限制适用于所有权限模式,包括 bypassPermissions。[1]
不过,改订单服务的时候,我们可能还想让 Claude 参考一下计费服务的实现,于是这样启动:
claude --add-dir ../billing-service
这时,计费服务也算进工作范围了。新开关不再把它当作外部目录,能否编辑要看其他权限规则。通过会话中的 /add-dir 或配置文件添加目录,效果一样。把另一个仓库加进来参考,不等于只给了读取权限。[3]
如果只是想参考几处实现,可以先把具体文件给它,让它按现有读取权限去读,不用急着把整个仓库加进来。这个新开关只管写入,读取另有权限规则约束。[1][3]
管住脚本写入
今年 7 月,一位 Linux 用户在 Claude Code 的 GitHub 仓库报告过一起误删:Agent 做临时测试时,把 HOME 指向了临时目录;到了另一次 Bash 调用,HOME 恢复成真实主目录,清理命令直接删到了文档、下载等位置。[6]

来源:GitHub #75859,calstfrancis 的用户报告,原帖节选。与开头的 X 帖子是不同事件。[6]
回到订单项目,代码改完后还要跑 npm test。假设测试脚本会更新 ../shared-tools/build-info.json,旁边的文件还是可能被改到。
测试进程没有调用 Write、Edit 这些文件工具,新开关拦不住这类写入。这里需要 Bash 沙箱,由操作系统限制测试命令及其子进程的文件访问。[4]

图 01:两种写入方式,各自限制。[1][3][4]
在支持的平台上,可以把前面的配置补成这样:
{
"permissions": {
"blockWritesOutsideWorkingDirectories": true
},
"sandbox": {
"enabled": true,
"allowUnsandboxedCommands": false,
"failIfUnavailable": true
}
}
这样配以后,Claude 不能把失败的沙箱命令拿到沙箱外重试。启动时如果发现平台或依赖不支持沙箱,Claude Code 会直接退出。[4][5]
macOS 可以直接启用内置沙箱。Linux、WSL2 需要额外依赖,可以在启用这套配置前先运行 /sandbox:缺少的包会列在 Dependencies 页里,装好后重启 Claude Code。原生 Windows 尚不支持,使用上面的配置会因沙箱不可用而拒绝启动。[4]
验证写入边界
配好后,可以用两个并列的空目录试一下。把它们放在普通工作路径下,避开沙箱默认放行的用户临时目录:
claude-write-lab/
├── project/
└── outside/
把上面的配置放进 project/.claude/settings.local.json,从 project 启动新会话,不添加 outside。在 outside 中准备一个普通文本文件,再让 Claude 尝试下面几件事。新开关和文件系统隔离都生效、没有额外放行命令或目录时,结果应该是:
| 尝试 |
预期结果 |
| 用 Write 在项目内新建标记文件 |
新开关不拦截,其他权限照常检查 |
用 Write 在 ../outside 新建文件,再用 Edit 修改外部测试文本 |
两次写入都被新开关拒绝 |
用 Bash 向 ../outside 写入标记 |
命令在沙箱里运行,写入被拒绝 |
最后一项,可以让 Claude 通过 Bash 工具执行:
python3 -c 'from pathlib import Path; Path("../outside/bash-probe.txt").write_text("probe\n")'
这里要看 Claude 有没有真的调用工具。如果它只是回答"不能修改项目外文件",还不能说明写入被拦住了。自己用 ! Shell 模式输入命令也不能代替这次验证,因为手动命令在多数会话里运行于沙箱之外。[4]
工具调用之后,再去 outside 看标记文件有没有出现、原来的文本有没有变化。只看当前仓库的 git diff,发现不了旁边目录新增的文件。
排查越界原因
如果计费服务的源码还是被改了,先回看那次工具调用。是 Write、Edit 改的,就确认计费目录有没有被加进来;会话中如果用 /cd 换过目录,主工作目录和项目配置也会跟着变,不能只按启动位置判断。[3]
文件确实在工作范围外,再查配置。/status 的 Setting sources 能看到加载了哪些配置来源,但不显示开关的最终值,还得对照这些来源,看看项目设置有没有被更高优先级的配置覆盖。[2]
如果是跑完 npm test,旁边公共脚本目录里的文件才变了,就回头看测试的放行设置。比如,测试因为写不了用户缓存而报错,通过 sandbox.filesystem.allowWrite 加上缓存路径,测试仍在沙箱里,只是多了一个能写的目录。
但如果为了解决同一次报错,把测试命令放进了 sandbox.excludedCommands,一旦这次 Bash 调用符合排除规则,测试及其子进程就会在沙箱外运行。前面配置中禁止的是沙箱外重试,不会取消这些排除项。[4][5]

图 02:同一次缓存报错,两种放行方式的区别。[4][5]
打开 /sandbox 的 Config 页,可以对照这些放行设置,也看看文件系统隔离有没有被单独关闭。用户临时目录默认可写,已有的 Edit(...) 允许规则也可能增加可写路径。命令在沙箱里,不等于只能写订单项目。[4][5]
如果变动只出现在主仓库的 .git 里,还要看订单项目是不是 Git worktree。这种情况下,沙箱默认允许写主仓库共享的 .git 目录,供 Git 更新索引和引用;其中的 hooks/ 和 config 仍禁止写入。[4]
如果最后发现文件是 MCP 服务或 Hook 改的,就沿着对应服务和脚本去查。它们不受前面文件写入开关和 Bash 沙箱的约束。[4]
参考资料: