ailiteracynepal 🇳🇵
Text size

Chapter 01 · Section V · 12 min read

1.5 Permissions: modes, tool approval, working dirs

The permission surface controls what runs without asking, what prompts, and what is denied outright. The right settings save you from clicking; the wrong ones save you from disasters.

Every tool call goes through the permission system before it runs. Learning where the knobs are — modes, allowlists, denylists, working-directory scope — is what turns “constant approval popups” into “auto-approve the safe stuff, prompt for the interesting stuff.”

Permission modes

Set globally via --permission-mode, or per-session via /config:

  • default — the “prompt on tool use” behaviour, minus what your allowlist auto-approves. Sane starting point.
  • acceptEdits — auto-approve Read/Write/Edit; still prompt for Bash and any tool with side effects.
  • plan — Claude produces a plan; nothing executes. Great for planning.
  • bypassPermissions — no prompts. Only use in a hermetic container.

The allow / deny lists

In settings.json:

{
  "permissions": {
    "allow": [
      "Read",
      "Grep",
      "Glob",
      "Bash(npm run test:*)",
      "Bash(git status)",
      "Bash(git diff*)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force*)",
      "Bash(git reset --hard*)"
    ]
  }
}

Precedence: deny beats allow beats mode. A denied pattern will not run even if the mode is bypassPermissions. That is the property to lean on: your denylist is the last-line guardrail.

Bash allowlist patterns are shell globs, matched against the exact command Claude proposes. Bash(npm run test:*) matches npm run test:unit and npm run test:e2e but not npm test.

Working directories

The session’s workspace is scoped to the launch directory plus any --add-dir you passed. Every file tool resolves against that scope; shell commands run inside it.

Adding a directory expands what Claude can see and act on. Removing one requires a session restart. If you find yourself constantly typing --add-dir, it might be worth configuring in ~/.claude/settings.json:

{
  "additionalDirectories": ["/Users/you/shared-utils"]
}

Interaction with tool approvals

When Claude proposes a tool call, three things determine what happens:

  1. Denylist check. If the exact call matches a deny pattern → refused. No prompt.
  2. Allowlist check. If it matches an allow pattern → auto-approved. No prompt.
  3. Permission mode. Otherwise, the mode decides: prompt in default, auto-approve in acceptEdits (for file tools), etc.

This composability is what lets you set a permissive mode for productivity without losing the guardrails.

Check your understanding

Quick check

Your settings.json has `permissions.deny: ['Bash(rm -rf *)']` and you launch with `--permission-mode bypassPermissions`. Claude proposes `rm -rf ./dist`. What happens?