Claude Code Permission Modes Explained

Written by Michael Lip · Solo founder of Zovo · $400K+ on Upwork · 100% JSS Join 50+ builders · More at zovo.one

TL;DR: Claude Code has three permission modes — default (prompts for everything), allowEdits (auto-approves file edits, prompts for commands), and bypassPermissions (auto-approves everything). Configure via CLI flag, settings.json, or PreToolUse hooks.

The Problem

Claude Code keeps asking for permission on every tool call, slowing down your workflow:

Permission rule Bash requires confirmation for this command:
  cat package.json

  Allow? [y/n]

Or conversely, you want to restrict Claude Code from running certain commands but the permission system does not seem configurable enough.

Why This Happens

Claude Code defaults to the safest permission mode — prompting for confirmation on every potentially impactful tool call (file writes, shell commands, etc.). This is intentional for security, but becomes friction for experienced users who want more autonomy.

The Fix

Step 1 — Choose Your Permission Mode

Mode File Edits Shell Commands Use Case
default Prompt Prompt New users, shared machines
allowEdits Auto-allow Prompt Trusted projects, edit-heavy workflows
bypassPermissions Auto-allow Auto-allow CI/CD, sandboxed environments, power users

Step 2 — Configure via CLI Flag

# Allow file edits without prompts
claude --permission-mode allowEdits

# Bypass all permissions (use with caution)
claude --dangerously-skip-permissions

Step 3 — Configure via Settings File

For persistent configuration, edit ~/.claude/settings.json:

{
  "defaultMode": "bypassPermissions",
  "skipDangerousModePermissionPrompt": true,
  "permissions": {
    "allow": [
      "Bash(npm test)",
      "Bash(npm run *)",
      "Bash(git *)",
      "Read(*)",
      "Write(src/*)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(sudo *)"
    ]
  }
}

Step 4 — Fine-Grained Control with PreToolUse Hooks

For the most control, use a PreToolUse hook that programmatically decides permissions:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/permission-hook.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

permission-hook.sh:

#!/bin/bash
# Read the tool input from stdin
INPUT=$(cat)

# Extract the command being run
COMMAND=$(echo "$INPUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('input',{}).get('command',''))" 2>/dev/null)

# Allow safe commands, deny dangerous ones
case "$COMMAND" in
  rm\ -rf*|sudo*|chmod\ 777*)
    echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","reason":"Blocked by safety hook"}}'
    ;;
  *)
    echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'
    ;;
esac
chmod +x /path/to/permission-hook.sh

Step 5 — Verify Your Configuration

# Start Claude Code and check the active mode
claude --permission-mode allowEdits

# In the session, run a test command
> Read the file package.json
# Should auto-approve without prompting

> Run: echo "hello world"
# In allowEdits mode, this should still prompt

Common Variations

Scenario Cause Quick Fix
Permissions reset after background task Known bug (issue #47810) Use hook-based permissions as workaround
bypassPermissions still prompts Settings not loaded Check file path, restart Claude Code
Hook not being called Wrong matcher pattern Use "matcher": ".*" to match all tools
Want to allow git but deny rm Need fine-grained rules Use permissions.allow and permissions.deny arrays
Sandbox mode overrides settings Sandbox has its own rules Set sandbox.autoAllowBashIfSandboxed: true

Prevention


Last verified: 2026-04-14. Found an issue? Open a GitHub issue.