你刚给一个 Bash(npm test) 点过 allow,两分钟后 Claude 再跑这条命令同样的提示又跳出来。你往 allowlist 加了一条,下一个 Bash(npm run build) 还是在问。每次 Edit、每次 Read、每次 shell 命令都要点一下——你以为的”agent”实际上是个要点一百次确认的钥匙保管员。原因基本都是这三种:settings.json 范围搞错、allowlist pattern 和实际命令对不上、或者有个 hook 设计上就要重复确认。把范围修对、pattern 精准放宽,一个 session 100 次提示能降到 5 次。
常见原因
1. allowlist 比实际命令更具体
settings.json 里写的是 "Bash(npm test)",但 Claude 跑的是 Bash(npm test --watch) 或者 Bash(cd packages/web && npm test)。匹配是工具调用层面的精确字符串,长一点的形态命中不了 allow 规则。
如何判断:弹提示时看清完整工具调用文本,和你 allowlist 里的写法对比。哪怕只是末尾多了个 flag——pattern 就是太窄。
2. allowlist 放错 settings 范围
.claude/settings.local.json(每用户每项目)、.claude/settings.json(仓库共享)、~/.claude/settings.json(全局用户)是三个独立文件。规则放错文件就被忽略。
如何判断:看清你的规则在哪个范围。cat .claude/settings.json、cat .claude/settings.local.json、cat ~/.claude/settings.json 三个都看一遍——必须放在 Claude 真的在这个项目里读的那个范围里。
3. 有 pre-tool hook 强制重新确认
PreToolUse 下的 hook 能编程式要求确认。如果你有一个对每个 Bash 都问的 hook、再厉害的 allowlist 也压不住。
如何判断:打开 ~/.claude/settings.json 和项目的 .claude/settings.json。找 hooks.PreToolUse 里 matcher 是 Bash / Edit / Read 的条目。有的话就盖过 allowlist。
4. 规则写在 deny 不在 allow
常见笔误是把规则放到 "deny" 而不是 "allow"。扫一眼 JSON 看着对、实际效果反了。
如何判断:打开 settings.json 顺着 key 看:permissions.allow 是放行规则、permissions.deny 是阻断。放错位置要么没效果要么主动 block。
5. settings 中途改了、没重新加载
有些版本启动时缓存权限集——中途改了 settings.json、正在跑的 Claude 用的还是老版本。规则要是限定到了项目、当前工作目录还可能对不上。
如何判断:改完 settings.json 同样的提示还跳。重启 Claude Code;规则限定到项目的话再确认 pwd 是不是规则所属的项目。
动手前先确认
- 确定到底哪些工具调用在循环(什么命令的
Bash?哪个文件的Edit?)。要拿到精确字符串。 - 看清你在编辑的是哪一个 settings 文件——三层不会透明合并。
- 改之前先备份 settings 文件,JSON 写坏了 Claude Code 启动不了。
需要收集的信息
- 最近一次权限弹窗的完整工具调用字符串。
~/.claude/settings.json、项目.claude/settings.json、.claude/settings.local.json三个文件的内容。- 有没有自定义 hook 在
hooks.PreToolUse里。 - Claude Code 版本(
claude --version或应用内构建号)。 - session 的工作目录(
pwd)。 - 是否点过 “Always allow”——可能是误点了 “Just this time”。
最短修复路径
Step 1:抓住完整工具调用文本
下次弹提示别急着点,先看清完整文本。注意工具名和参数字符串。形如:
Bash(cd packages/web && pnpm run build)
Edit(/Users/you/proj/src/api/auth.ts)
Read(/Users/you/proj/CLAUDE.md)
工具名后面的整段就是你 allowlist pattern 必须匹配的东西。
Step 2:选对 settings 范围
按这张表选要改的文件:
| 规则适用范围 | 改哪个文件 |
|---|---|
| 跨所有项目的个人默认 | ~/.claude/settings.json |
| 团队通过 git 共享 | 仓库根的 .claude/settings.json |
| 这个项目里个人用、不进 git | .claude/settings.local.json |
npm test 这种项目特定但个人用的命令、放 settings.local.json 不污染同事的环境。
Step 3:写一条覆盖同族调用的 pattern
用 shell glob 风格覆盖同一意图的多种变体:
{
"permissions": {
"allow": [
"Bash(npm test*)",
"Bash(pnpm run *)",
"Bash(git status)",
"Bash(git log*)",
"Bash(cd * && npm *)",
"Edit(/Users/you/proj/src/**)",
"Read(/Users/you/proj/**)"
]
}
}
* 匹配任意——用它吸收末尾 flag 和参数。要有意识——宽规则方便、但也会放过你没打算放过的命令。
Step 4:验证规则真的被读到
保存 settings 后重启 Claude Code,再故意触发循环命令:
让 Claude:"跑 npm test"
预期:不弹提示
实际:还在弹 → 规则没匹配上
还在弹就检查:
- JSON 合法(
python -m json.tool < settings.json跑一遍)。 - 文件在 Claude 真的读的范围(
claude --debug打印加载的 settings)。 - 没有 deny 规则盖过你的 allow(deny 优先于 allow)。
Step 5:检查和禁用有问题的 hook
有 hook 在强制确认就找出来:grep -A 5 "PreToolUse" ~/.claude/settings.json .claude/settings.json。matcher 是 Bash 又跑交互确认的 hook 会让每个 Bash 重新提示、allowlist 也压不住。不需要就删;需要的话把 matcher 收窄、只对真正破坏性的命令生效。
Step 6:信任的项目里宽 allow
自己的项目里、信任 Claude 判断的话,可以激进 allowlist:
{
"permissions": {
"allow": [
"Bash(*)",
"Edit(*)",
"Read(*)",
"Grep(*)",
"Write(*)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(* >/dev/null *)",
"Bash(git push --force*)",
"Bash(curl * | bash*)"
]
}
}
“全放行 + 危险点 deny”让 agent 流畅运行、没有点确认的疲劳。
怎么确认已经修好
- 跑一个 10 分钟探索性 session,数权限提示数。修完应该 <5 次(理想 0-2 次)。
- 把原本循环的每条命令各手动触发一次,确认不再弹提示。
- 跑一条明显破坏性的命令(
rm -rf /tmp/test-fixture)确认会弹——deny 规则在生效。 - 重启 Claude Code 后同样的放行命令还在静默跑——settings 持久化了。
长期预防
- 每个项目定 allowlist 策略:信任的项目宽 allow + 定点 deny;安全敏感的项目只窄 allow。
- 破坏性 deny 放
~/.claude/settings.json全局生效。 - 个人机器的规则放
.claude/settings.local.json、别把开发者特定的模式漏进团队 settings。 - 团队工作流加新工具时把 allowlist 条目加到共享的
.claude/settings.json、同事不用每人重做一遍。 - 每次改完 settings.json 用
python -m json.tool校验——一个逗号缺失某些版本会静默整个文件失效。 - 每季度审查 hook、把已经不值得它造成摩擦的删掉。
常见坑
- pattern 用
?或者正则语法、以为能用——Claude Code 是简单 glob 只有*、不是完整正则。 - 写了
Bash(*)但一条 deny 都没——agent 早晚会在某个瞬间跑出风险操作。 - 编辑了错的范围文件、然后断言”settings 不生效”——是生效的、你编辑错了。
- 改完全局 settings 没重启 Claude Code——旧 session 还在用旧 allowlist。
- 反复点 “Just this time”、然后惊讶提示一直回来。“Just this time” 设计上就不持久化。
FAQ
Q:“Always allow”、“This session”、“Just this time” 有啥区别? A:Always = 写进 settings.json;session = 退出前都记住;once = 下次还问。这里的误点是循环最常见的来源。
Q:allowlist pattern 能用完整正则吗?
A:不能、只能用 * 的简单 glob。需要类似正则的能力就拆成多条。
Q:我的 deny 规则为啥没拦住该拦的命令?
A:deny 匹配也得用对 pattern 形态。Bash(rm -rf /) 这种 deny 不匹配 Bash(rm -rf /tmp/x)——同族用 Bash(rm -rf *)。
Q:MCP 服务端的工具走同一套权限系统吗?
A:走——MCP 工具调用名字形如 mcp__server__tool、可以用一样的方式 allowlist。
相关阅读
标签: #Claude Code #agent #排查