Permission Modes
Why permissions matter
Claude Code has real access to your filesystem and shell. It can read files, write files, run commands, and make network requests. Permissions are how you control the blast radius.
The permission system has two layers:
- Modes - preset permission levels you can switch between
- Settings - granular per-tool allow/ask/deny lists
The five permission modes
| Mode | File reads | File writes | Shell commands | Best for |
|---|---|---|---|---|
| default | Allowed | Ask each time | Ask each time | Learning, exploration |
| acceptEdits | Allowed | Auto-accept | Ask each time | Trusted edit sessions |
| plan | Allowed | Blocked | Blocked | Code review, planning |
| dontAsk | Allowed | Auto-accept | Auto-accept | Experienced users, automation |
| bypassPermissions | Allowed | Auto-accept | Auto-accept | CI/CD pipelines only |
default
The starting mode. Claude reads freely but asks permission before writing files or running commands. You see a prompt like:
Claude wants to edit src/auth.ts
[Allow] [Deny] [Always allow]
Good for: learning Claude Code, working in unfamiliar codebases, pair programming.
acceptEdits
File edits are auto-accepted. Shell commands still require approval. This is the sweet spot for most development work - you trust Claude's code changes but want to approve any rm, git push, or npm install.
plan
Read-only. Claude can read your codebase and propose a plan, but cannot modify anything. Use this when you want Claude to analyze and suggest without touching anything.
claude --permission-mode plan> How would you refactor the auth module?
Claude will read files, analyze the code, and propose a detailed plan
with specific file changes - but won't execute any of them.
dontAsk
Everything is auto-approved. Claude reads, writes, and runs commands without asking. Use this when you trust the task and want Claude to work autonomously.
bypassPermissions
Like dontAsk but also bypasses system-level restrictions. Only available via CLI flag:
claude --dangerously-skip-permissionsThis mode is named dangerously for a reason. It skips all safety checks. Only use it in sandboxed environments like CI/CD pipelines or Docker containers where the blast radius is contained. Never use it on your local machine with important data.
Switching modes
In interactive mode: press Shift+Tab to cycle through modes. The current mode shows in the prompt indicator.
Via CLI flag:
claude --permission-mode plan
claude --permission-mode acceptEdits
claude --dangerously-skip-permissionsThe "Always allow" shortcut: when Claude asks for permission in default mode, choosing "Always allow" adds that specific tool to your allow list for the session.
Settings-based permissions
For finer control, configure permissions in .claude/settings.json (shared with team, committed to git) or .claude/settings.local.json (personal, gitignored):
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"Bash(npm test)",
"Bash(npm run lint)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force)"
]
}
}The three arrays:
- allow - always permitted, no prompt
- ask - prompt before each use (default for most tools)
- deny - always blocked, even if the mode would allow it
You can be specific with shell commands. Bash(npm test) allows only that exact command. Bash(npm run *) allows any npm run script.
Start restrictive, open up as you build trust. Begin in default mode, use "Always allow" for commands you approve repeatedly, and graduate to acceptEdits or dontAsk as you get comfortable. The settings file lets you codify team-wide safety rails.
Recommended setup for teams
Share safety rails in .claude/settings.json (committed to git):
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"Bash(npm test)",
"Bash(npm run lint)",
"Bash(npm run build)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force)",
"Bash(curl * | sh)"
]
}
}Individual developers can add personal overrides in .claude/settings.local.json (gitignored).
Explore permission modes
- Start Claude Code in
defaultmode:claude - Ask Claude to create a new file. Notice the permission prompt.
- Press
Shift+Tabto switch toplanmode - Ask Claude to refactor something. Notice it only plans, doesn't execute.
- Press
Shift+Tabto switch toacceptEditsmode - Ask Claude to make a small edit. Notice it applies without asking.
- Run
/costto see your usage
In default mode, you'll see prompts like "Claude wants to write to file.ts - Allow / Deny". In plan mode, Claude will describe what it would do but make no changes. In acceptEdits mode, file changes apply immediately but shell commands still ask.
The mode indicator in the prompt shows: > (default), >> (acceptEdits), ? (plan), >>> (dontAsk).
Create a settings file
Create a .claude/settings.json for one of your projects:
mkdir -p .claude- Create the settings file with allow/deny rules appropriate for your project
- Start Claude Code and verify the rules work
Think about: what commands should always be safe? What commands should never run?
A solid starting point:
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"Bash(npm test)",
"Bash(npm run lint)",
"Bash(npm run build)",
"Bash(npx tsc --noEmit)"
],
"deny": [
"Bash(git push --force*)",
"Bash(rm -rf *)"
]
}
}Demo the Shift+Tab switching live. Start in default mode, make an edit (show the prompt), switch to plan mode (show the blocked execution), switch to acceptEdits (show auto-approved edits). The visual difference between modes lands better as a live demo than as text.