Every time Claude wants to read a file, edit one, run a shell command or fetch a URL, Claude Code checks the call before it happens. The answer is one of three things: run it, ask you first, or block it. Permissions are how you set that answer, so you can hand over the boring approvals and keep the risky ones.
The check is done by Claude Code, not by the model. An instruction in CLAUDE.md shapes what Claude tries; it doesn’t change what Claude Code allows.
Modes set the baseline
A permission mode decides what runs without asking. In the CLI, press Shift+Tab to cycle modes; the status bar shows the current one.
- Manual (config value
default): only reads run freely; edits, commands and network access ask. acceptEdits: file edits and common filesystem commands likemkdirandmvin your working directory run without asking.plan: Claude reads, explores and writes a plan, but edits stay blocked until you approve it. You can also prefix a single prompt with/plan.auto: no routine prompts; a separate classifier model reviews actions and blocks ones that go beyond your request. Recent versions start interactive sessions in this mode.
Two more are for scripts and sandboxes: dontAsk denies anything that would prompt, and bypassPermissions skips prompts entirely, which the docs reserve for isolated containers and VMs.
Rules carve out exceptions
Rules name a tool and, optionally, a pattern: Bash(npm run *), Read(./.env), WebFetch(domain:example.com). Each goes in one of three lists under permissions in a settings file: allow (no prompt), ask (always prompt) or deny (never). Run /permissions to see every rule and which file it came from. Answering a Bash prompt with “Yes, and don’t ask again” saves an allow rule to .claude/settings.local.json.
Rules are checked deny, then ask, then allow, and the first match wins. A narrow allow can’t punch a hole in a broad deny, and a deny set at any level, including your organization’s managed settings, can’t be overridden elsewhere. Deny rules hold in every mode, even bypassPermissions.
The catch
Patterns match the command as written. The docs’ own example: a deny on Bash(git push *) doesn’t catch git -C . push. Treat Bash rules as convenience, not containment, and for code that must stop no matter how it’s spelled, add a hook or the sandbox. If an edit you didn’t want gets through, checkpoints can rewind it.