Claude Code has five permission modes that control how much autonomy it gets — from asking before every action, to executing everything without a prompt. The right mode depends on context: interactive coding sessions need guardrails, CI/CD pipelines need full autonomy, and code reviews need read-only access. Combined with tool-level allowlists and denylists, these modes form a layered permission system that determines exactly what Claude can and cannot do.
The Five Modes
Every claude session runs in exactly one permission mode. You set it with --permission-mode MODE or accept the default.
Permission Modes
| Mode | Flag | Behavior | Use Case |
|---|---|---|---|
default | (none needed) | Prompts for permission on first use of each tool | Interactive development |
acceptEdits | —permission-mode acceptEdits | Auto-approves file edits, prompts for shell commands | Trusted editing sessions |
plan | —permission-mode plan | Read-only — can analyze but NOT modify files or run commands | Code review, architecture analysis |
dontAsk | —permission-mode dontAsk | Auto-denies tools unless pre-approved via allow rules | Headless / automated workflows |
bypassPermissions | —permission-mode bypassPermissions | Skips all permission prompts — every tool call succeeds | CI/CD, containers, VMs only |
default is what you get when you launch claude with no flags. Every destructive tool — Bash, Edit, Write — requires one-time approval. Once approved, the rule persists for the session (file edits) or permanently per project (bash commands). Read-only tools like Grep and file reads never require permission.
acceptEdits trusts Claude with file modifications inside the project directory. Bash commands still require approval. This is the sweet spot for focused coding sessions where you want speed but not unlimited shell access.
plan is read-only. Claude can read files, search code, and reason — but cannot write files or execute commands. Use this for code review, architecture analysis, or when you want Claude to produce a plan without touching anything.
dontAsk is the inverse of default. Instead of prompting for unknown tools, it silently denies them. Only tools explicitly listed in permission allow rules will work. This is designed for headless or automated workflows where there is no human to answer prompts.
bypassPermissions disables all permission checks. Every tool call succeeds without prompting. This should only be used in isolated environments — CI/CD pipelines, containers, or virtual machines where the blast radius is contained.
Choosing a Mode
The decision comes down to two questions: is a human watching, and how much do you trust the environment?
Mode Selection Guide
| Scenario | Mode | Why |
|---|---|---|
| Interactive development, first time on a project | default | You want to see and approve each action until you build trust |
| Focused coding session, trusted codebase | acceptEdits | File edits flow freely; shell commands still require a nod |
| Code review or planning only | plan | Claude analyzes without modifying anything |
| Automated pipeline with explicit allow rules | dontAsk | Fails fast on unapproved tools instead of blocking on a prompt |
| CI/CD in a container or VM | bypassPermissions | No human present, isolated environment limits blast radius |
| Plan-then-execute workflow | plan then bypassPermissions | Plan in read-only, review output, resume with full access |
A common pattern is combining modes with sessions. Plan in plan mode, review the output, then --resume the same session in bypassPermissions to execute. The Session Chains chapter covers this workflow in detail.
—allowedTools / —disallowedTools
Beyond the five modes, you can filter at the individual tool level. These flags accept comma-separated tool names and support pattern matching.
# Only allow file reads and grep — nothing elseclaude -p "Analyze auth.py" --allowedTools "Read,Grep,Glob"
# Allow everything except shell accessclaude -p "Refactor this module" --disallowedTools "Bash"
# Allow specific bash commands with glob patternsclaude -p "Run the tests" --allowedTools "Bash(npm test *),Read,Edit"Tool specifiers use the same pattern syntax as permission rules:
Bash(git *)— matchesgit status,git commit, but NOTgitkEdit(/src/**/*.ts)— matches any TypeScript file under/src/mcp__puppeteer__*— matches all tools from the puppeteer MCP server
These flags are temporary and apply only to the current session. They combine with the permission mode — a tool must pass both the mode check and the allowlist/denylist check to execute.
Precedence Rules
Permission rules can be defined at multiple levels. When rules conflict, the higher-precedence level wins. Deny always beats allow at the same level.
Settings Precedence (Highest to Lowest)
| Level | Source | Scope |
|---|---|---|
| 1 | Managed settings (enterprise MDM) | Organization-wide, cannot be overridden |
| 2 | CLI arguments (—allowedTools, —disallowedTools) | Current session only |
| 3 | Local project settings (.claude/settings.local.json) | Per-developer, gitignored |
| 4 | Shared project settings (.claude/settings.json) | Team-wide, committed to repo |
| 5 | User settings (~/.claude/settings.json) | Global defaults for the user |
The critical rule: deny > ask > allow at every level. If a tool is denied at ANY level, no lower-level allow rule can override it. A team lead can deny Bash(rm -rf *) in shared project settings, and no individual developer’s local settings or CLI flags can re-enable it.
Modes in Action — Same Prompt, Three Outcomes
To see how modes affect behavior, here is the same prompt run under three different modes: “Write hello to /tmp/perm_test.txt” — a write to a path outside the project directory.
default mode blocks the write and reports one denial:
acceptEdits mode also blocks the write — but Claude retries once before giving up, producing two denials and an extra turn:
bypassPermissions mode succeeds immediately — zero denials, lowest cost:
The key insight from these payloads: acceptEdits does NOT auto-approve writes outside the project directory. The sandbox filesystem restriction applies regardless of permission mode. The retry behavior also means higher cost — check permission_denials.length to understand what happened.
—dangerously-skip-permissions is just an alias for —permission-mode bypassPermissions. They are identical in behavior. The long name exists to make scripts self-documenting about the risk involved.
plan mode works by blocking the ExitPlanMode tool — the internal tool Claude would call to transition from planning to execution. Since read-only tools (file reads, Grep, Glob) never require permission, they continue to work. Claude can analyze everything; it just cannot act on its analysis.
The permission_denials array in the JSON response tells you exactly which tools were blocked and what arguments Claude attempted. Parse this in your automation to detect when Claude tried to do something it was not allowed to do — and log it for auditing.
Permission rules in .claude/settings.json are strictly local — they do not walk up the directory tree. A parent directory’s permissions.deny: [“Write”] does not apply to subfolder sessions, even without a child settings.json. This is different from CLAUDE.md and MCP configs, which do inherit upward. For monorepo-wide permission enforcement, use managed settings (enterprise-level).