Claude Code permissions explained
Updated 24 October 2026
Claude Code decides whether to run a tool call, ask you, or refuse it from two things: the permission mode the session is in, and the allow, ask and deny rules in your settings files. This page explains both, the rule syntax that trips people up, which settings file wins, and the prompts no setting can turn off. It’s written against Claude Code 2.1.287; check yours with claude --version.
Quick reference
| You want to | Use |
|---|---|
| Change how much Claude asks, right now | Shift+Tab to cycle modes |
| Start every session in a mode | permissions.defaultMode in ~/.claude/settings.json |
| Stop prompts for one command | An allow rule, such as Bash(npm run *) |
| Always be asked before something | An ask rule, such as Bash(git push *) |
| Block something completely | A deny rule, such as Read(./.env) |
| See every rule and where it came from | /permissions |
| Let Claude work in another folder | --add-dir, /add-dir or additionalDirectories |
| Limit what shell commands can touch, whatever their text | The sandbox (/sandbox) |
1. Permission modes
The mode sets the baseline: what Claude can do without asking. The status bar under the prompt shows the current one.
| Mode | Status bar | Runs without asking |
|---|---|---|
default (labeled Manual) | ⏸ manual mode on | Reads, and a built-in set of read-only commands. Everything else asks. |
acceptEdits | ⏵⏵ accept edits on | Also file edits and mkdir, touch, rm, rmdir, mv, cp and sed, for paths inside your working directories |
plan | ⏸ plan mode on | Reads and exploring. Edits wait until you approve a plan. |
auto | ⏵⏵ auto mode on | Almost everything. A separate classifier model reviews shell commands and network requests instead of you. |
dontAsk | ⏵⏵ don't ask on | Reads, the read-only commands and whatever your allow rules cover. Anything that would prompt is denied. |
bypassPermissions | ⏵⏵ bypass permissions on | Everything except a short list of checks that always ask |
Two things surprise people here:
- Auto mode is now the default. From Claude Code v2.1.283, interactive terminal and VS Code sessions start in
autounless you set something else, as long as your account and model support it. If they don’t, or a settings file setsdisableAutoMode, you start in Manual.claude -pruns start in Manual and deny anything that needs approval. defaultandmanualare the same mode. The config value isdefault; the CLI and the apps call it Manual and acceptmanualas an alias.
Use dontAsk for CI, where nobody is there to answer. Use bypassPermissions only in a container or VM; Claude Code --dangerously-skip-permissions covers why.
2. Switching modes
During a session, press Shift+Tab. From auto, the first press goes to Manual, then the cycle runs Manual → acceptEdits → plan → auto (when it’s available) and round again. bypassPermissions only joins the cycle if you started the session with it enabled, and dontAsk never does. You can also prefix one prompt with /plan to plan just that task.
For one session, pass the mode at launch:
claude --permission-mode acceptEdits
The flag accepts manual, acceptEdits, plan, auto, dontAsk and bypassPermissions.
For every session, set defaultMode:
~/.claude/settings.json{
"permissions": {
"defaultMode": "acceptEdits"
}
}
The flag beats defaultMode, and defaultMode beats the built-in default. A project’s .claude/settings.json can set default, acceptEdits, plan or dontAsk, but auto and bypassPermissions only take effect from your user settings, --settings or managed settings. That stops a cloned repository from switching your prompts off.
In the VS Code extension, click the mode indicator under the prompt box. To pin the mode new conversations start in, set claudeCode.initialPermissionMode in your VS Code settings. The extension never reads the project’s settings files for the starting mode.
3. What asks by default
In Manual mode, this is what you get asked about, and what “Yes, and don’t ask again” remembers:
| Tool | Asks? | “Don’t ask again” lasts |
|---|---|---|
| File reads, Grep, Glob | No, inside your working directories | n/a |
| Bash | Yes, except built-in read-only commands | Permanently, per repository and command |
| Edit and Write | Yes | Until the session ends |
| WebFetch | Yes, except a built-in list of documentation domains | Permanently, per repository and domain |
| WebSearch | Yes | Permanently, per repository |
The read-only set includes ls, cat, echo, pwd, head, tail, grep, find, wc, which, diff, stat, du, cd and read-only forms of git. You can’t change the set. To be asked before one of them anyway, add an ask or deny rule for it.
A permanent approval is written to .claude/settings.local.json at the root of the git repository, so it covers subdirectories and worktrees too. Outside a git repository, and on Windows, Claude Code can save it somewhere else; /permissions shows which file each rule is in.
4. Allow, ask and deny rules
Rules sit in the permissions object of any settings file:
.claude/settings.json{
"permissions": {
"allow": ["Bash(npm run *)", "Bash(git commit *)"],
"ask": ["Bash(git push *)"],
"deny": ["Read(./.env)", "Read(./.env.*)", "Read(./secrets/**)"]
}
}
- allow runs the call without asking.
- ask always prompts, even in acceptEdits, auto and bypassPermissions.
- deny blocks the call in every mode.
Claude Code checks deny, then ask, then allow, and the first match decides. Specificity doesn’t matter: with Bash(aws *) in deny, an allow rule for Bash(aws s3 ls) never applies. The same goes for ask over allow.
A deny rule with just a tool name, such as "WebFetch", removes the tool, so Claude never sees it. A scoped rule, such as Bash(rm *), keeps the tool and blocks matching calls.
Type /permissions to see every rule and the file it came from, and to add or remove rules. Changes apply from Claude’s next tool call, even mid-turn. In auto mode it also has a Recently denied tab, where you can press r to retry a blocked action with a manual approval.
For one session, --allowedTools and --disallowedTools add allow and deny rules on the command line:
claude --allowedTools "Bash(npm test)" "Read" --disallowedTools "WebFetch"
5. Rule syntax
A rule is Tool or Tool(specifier). Use the tool’s canonical name from the tools reference, not the label you see in the transcript. A deny or ask rule naming a tool that doesn’t exist gets a startup warning.
Bash
A * matches any text, spaces included. Everything before the first * must match as written, so put the * after the subcommand:
| Rule | Matches | Doesn’t match |
|---|---|---|
Bash(npm run build) | npm run build | npm run build --watch |
Bash(npm run *) | npm run build, npm run test --watch, npm run | npm install |
Bash(git log *) | git log, git log --oneline | git push |
Bash(git *) | Every git command, including git push and git -c … | |
Bash(ls *) | ls, ls -la | lsof |
Bash(ls*) | ls -la, lsof |
- The space matters.
Bash(ls *)needs a space afterls;Bash(ls*)doesn’t. - The old
:*form still works, at the end only.Bash(ls:*)is the same asBash(ls *). One catch:Bash(npm run test:*)reads asnpm run test *, so it doesn’t matchnpm run test:unit. WriteBash(npm run test:unit)orBash(npm run test*)for that. - Compound commands are split. Claude Code splits on
&&,||,;,|,|&,&and newlines, and every part has to be allowed.Bash(npm test *)doesn’t approvenpm test && git push. A deny or ask rule applies if any part matches, including inside$(…)or aforloop. - Some wrappers are stripped first.
timeout,time,nice,nohup,stdbuf, barexargsand a few shell builtins, soBash(npm test *)also matchestimeout 30 npm test.npx,docker exec,direnv execandmise execaren’t stripped, andBash(npx *)would allow anything afternpx.
Bash rules match text, not programs. Bash(rm *) in deny stops rm -rf build/ but not /bin/rm -rf build/ or bash -c 'rm -rf build/'. Bash(git push *) doesn’t stop git -C . push. For a limit that holds whatever the command looks like, use the sandbox or a PreToolUse hook.
On Windows with the PowerShell tool on, rules use the same shape: PowerShell(Get-ChildItem *). Matching is case-insensitive and also covers common aliases, so that rule matches gci, ls and dir.
Read and Edit
Path rules use gitignore patterns. Edit rules cover every built-in tool that changes files; Read rules cover Read, Grep, Glob and @ mentions on a best-effort basis. Rules written as Write(…) or Glob(…) with a path are accepted but never match, so use Edit(…) and Read(…). The leading characters decide where a path starts:
| Pattern | Means | Example |
|---|---|---|
//path | Absolute path from the filesystem root | Read(//Users/alice/secrets/**) |
~/path | From your home directory | Read(~/.zshrc) |
/path | Relative to the settings source | Edit(/src/**/*.ts) |
path or ./path | Relative to the current directory | Read(./.env) |
- A single slash isn’t absolute. In a project’s settings,
/src/**means the project’ssrc. In~/.claude/settings.jsonit means~/.claude/src. For a rule in user settings that works in every project, use//or~/, or a bare pattern likeRead(**/.env). *stays in one folder,**crosses folders. A bare filename such asRead(.env)matches at any depth under the current directory.- A Read deny also blocks edits to the same path. It also applies to
cat,head,tail,sedand redirects in Bash, but not to a script that opens files itself or togrep -rover a whole folder. - Windows paths are converted to POSIX form first:
C:\Users\alicebecomes/c/Users/alice, soRead(//c/**/.env)matches every.envon drive C. .claudeignoredoes nothing. Move its entries into Read deny rules.
WebFetch
WebFetch(domain:example.com)matchesexample.comonly.WebFetch(domain:*.example.com)matches any subdomain, but notexample.comitself. Add both if you want both.- A bare
WebFetchin deny removes the tool. It doesn’t stopcurlin Bash, so pair it with a Bash deny or the sandbox.
MCP tools and subagents
mcp__github__*ormcp__githubmatches every tool from the server namedgithub.mcp__github__get_*matches itsget_tools, andmcp__github__create_issuematches one tool.mcp__*works in deny and ask, not in allow: an allow glob has to name a server.Agent(Explore)in deny stops Claude from using the Explore subagent.
6. Which settings file wins
| Level | File | Who it affects |
|---|---|---|
| 1. Managed | /Library/Application Support/ClaudeCode/managed-settings.json (macOS), /etc/claude-code/managed-settings.json (Linux, WSL), C:\Program Files\ClaudeCode\managed-settings.json (Windows), MDM or the claude.ai console | Everyone your organization deploys it to |
| 2. Command line | --settings, --allowedTools, --disallowedTools | This session |
| 3. Local project | .claude/settings.local.json | You, in this project. Kept out of git. |
| 4. Shared project | .claude/settings.json | Everyone who clones the repository |
| 5. User | ~/.claude/settings.json | You, in every project |
For a single value like defaultMode, the highest level wins. Rule lists don’t override each other: allow, ask and deny from every file are merged, then checked deny first. So a deny anywhere beats an allow anywhere, and a deny in your user settings blocks an allow in a project. Nothing below managed settings can undo a managed deny, and an administrator can set allowManagedPermissionRulesOnly to ignore everyone else’s rules.
One safety catch: allow rules and additionalDirectories in a project’s .claude/settings.json only apply after you accept the workspace trust dialog for that folder, which lists them first. Deny and ask rules apply straight away, since they only restrict.
7. Working directories and additionalDirectories
Claude can read and edit inside the folder you started it in. To add more:
- At launch:
claude --add-dir ../shared-lib - During a session:
/add-dir ../shared-lib - Every time: list it in settings
.claude/settings.json{
"permissions": {
"additionalDirectories": ["../shared-lib/"]
}
}
Files there follow the same rules as your working directory: reads don’t ask, and edits follow the mode. Adding a folder grants file access only. Claude Code doesn’t load that folder’s .claude/settings.json or hooks, and folders listed in additionalDirectories don’t load its skills or subagents either; --add-dir and /add-dir do.
To stop Claude reading outside these folders at all, set permissions.blockReadsOutsideWorkingDirectories to true. It applies in every mode, bypassPermissions included.
8. What always asks
Some prompts survive every mode, including bypassPermissions, and no allow rule removes them:
- Anything matching an ask rule.
AskUserQuestion, Claude’s multiple-choice questions, and MCP tools marked as needing user interaction.rmorrmdiron a critical path: the filesystem root, top-level folders like/usr, your home directory, or your working directory and its parents. In auto and bypass mode the terminal prompt counts down two minutes, then denies.- Connector tools your organization set to ask.
Writes to protected paths are never auto-approved outside bypassPermissions: Manual and acceptEdits ask, auto sends them to the classifier, and dontAsk denies. Protected paths include .git, .claude (except a few, such as .claude/worktrees/), .vscode, .idea, .husky, shell startup files such as .bashrc and .zshrc, .gitconfig, .npmrc, .mcp.json and .claude.json. An allow rule like Edit(.claude/**) doesn’t change this.
That’s on top of what a mode normally asks about. In acceptEdits, for example, edits to paths outside your working directories still ask, and so does any Bash command that isn’t a read-only command or one of the filesystem commands acceptEdits lets through.
9. Sandboxing
Rules decide whether a command may start. The sandbox limits what it can reach once it runs. It’s enforced by the operating system (Seatbelt on macOS, bubblewrap on Linux and WSL2), so it holds whether Claude writes rm or /bin/rm. It’s off by default. Run /sandbox to turn it on, or:
~/.claude/settings.json{
"sandbox": {
"enabled": true
}
}
Sandboxed commands can write to the working directory, a temp folder and any added directories, and reach only the network hosts you allow. In auto-allow mode, sandboxed commands run without a prompt; deny rules and command-specific ask rules such as Bash(git push *) still apply.
- It covers shell commands only. Claude’s Read and Edit tools, hooks and MCP servers run outside it.
- It doesn’t run on native Windows. Use WSL2.
- On Linux and WSL2, install
bubblewrapandsocatfirst (sudo apt-get install bubblewrap socat). - If the sandbox can’t start, Claude Code runs commands unsandboxed unless you set
sandbox.failIfUnavailabletotrue. - When a command fails in the sandbox, Claude can ask to retry it unsandboxed. Set
sandbox.allowUnsandboxedCommandstofalseto turn that off.
Answer the prompts that are left from your browser
However you tune modes and rules, some prompts are meant to reach you, and the session waits until you answer. Agent Pets lets you answer them without going back to the terminal:
- Each Claude Code, Codex, OpenCode and omp session becomes a pet walking along the bottom of every page in Chrome. When Claude asks to run a command or edit a file, its pet bounces; click it and answer Allow, Always or Deny.
- The capsule above each pet shows its state, such as Thinking, Your Turn or Allow Bash?, and the toolbar badge counts sessions waiting on you.
- A desktop notification names the pet that needs you, with Allow and Deny on it.
npx getagentpets init connects Claude Code. Everything runs on your computer, and the terminal prompt keeps working.
Get Agent Pets — was $25, now $6Get Agent Pets — $25
Questions
What are the Claude Code permission modes?
default (shown as Manual) asks before edits and most commands, acceptEdits lets file edits through, plan only reads and plans, auto has a classifier review actions instead of you, dontAsk denies anything that would prompt, and bypassPermissions skips almost every prompt.
If a command matches both an allow rule and a deny rule, which wins?
The deny rule. Claude Code checks deny rules first, then ask, then allow, and the first match decides. A more specific allow rule can’t carve an exception out of a broader deny rule, and a deny in any settings file beats an allow in any other.
Where does “Yes, and don’t ask again” save the rule?
For Bash commands and WebFetch domains, in .claude/settings.local.json at the root of the git repository, so it applies to future sessions anywhere in that repository. File edit approvals aren’t saved: they last until the session ends.
How do I let Claude Code work in a folder outside my project?
Start Claude Code with --add-dir and the path, run /add-dir during a session, or list the folder in permissions.additionalDirectories in a settings file. Files there follow the same rules as your working directory.
Can I control permissions from CLAUDE.md?
No. CLAUDE.md shapes what Claude tries to do, but Claude Code enforces permissions from settings files, /permissions, the permission mode and hooks. Put hard limits in deny rules.
Sources: Claude Code’s documentation on configuring permissions, permission modes, settings, the settings reference, managed settings and sandboxing; claude --help for Claude Code 2.1.287.
More guides
- How to stop Claude Code asking for permission: allow all commands safely
- Claude Code YOLO mode (--dangerously-skip-permissions): what it does and safer options
- Claude Code settings.json: file locations and reference
- Claude Code hooks: a practical guide
- Claude Code notifications: when done or waiting for input