Skip to content

Your First Permission Rule

By default, Claude asks for your approval before editing files, running commands, or fetching URLs. Permission rules let you pre-approve (or permanently block) specific actions so Claude can work without interrupting you every time.


  1. Open (or create) .claude/settings.json in your project root.

  2. Add your allow rules:

    {
    "permissions": {
    "allow": [
    "Write(src/**)",
    "Bash(npm run *)"
    ]
    }
    }
  3. Claude now works without interrupting you.

    Any file under src/ can be edited silently. Any npm run script can be executed without a prompt.


Before (no rules): Every time Claude wants to edit a file, you see:

Claude wants to edit src/utils/format.ts
[Allow once] [Allow for this session] [Allow always] [Deny]

After (with Write(src/**) in allow): Claude edits the file silently. You stay in flow.


Rules are checked in this order — the first matching condition wins:

  1. Hard block? — Is this a hardcoded dangerous pattern (e.g. rm -rf /)? If yes → blocked with no override possible.
  2. Hook block? — Does a PreToolUse hook return exit 2? If yes → blocked by hook.
  3. Deny rule match? — Does a deny rule in settings match this tool call? If yes → denied.
  4. Allow rule match? — Does an allow rule match? If yes → allowed silently, no prompt shown.
  5. Default mode fallback (no rule matched):
ModeWhat happens
autoClassifier decides: safe actions auto-approved, risky ones prompt you
defaultAsk you every time
bypassPermissions or dontAskAllowed silently

Rules follow the pattern Tool(pattern). The pattern depends on the tool:

ToolPatternExample
WriteFile globWrite(src/**) — any file under src/
ReadFile globRead(~/.zshrc) — your zshrc file
EditFile globEdit(**/*.ts) — any TypeScript file
BashCommand prefixBash(npm *) — any npm command
BashExact commandBash(npm run test) — exactly this command
WebFetchDomainWebFetch(domain:anthropic.com) — this domain and subdomains
WebSearch(none)WebSearch — all web searches
src/** → any file anywhere under src/
src/*.ts → .ts files directly in src/ (not subdirectories)
**/*.test.ts → any .test.ts file anywhere
~/.zshrc → your home directory zshrc
Bash(npm *) → any npm command (npm install, npm run test, etc.)
Bash(npm run *) → any npm run script, but not npm install
Bash(git status) → exactly "git status", nothing else
Bash(* install) → any install command (npm install, pip install, etc.)

{
"permissions": {
"allow": [
"Write(src/**)"
],
"deny": [
"Write(src/secrets/**)",
"Bash(rm *)"
]
}
}
  • allow — Claude proceeds without asking
  • deny — Claude is blocked entirely (can’t even ask you)

Deny rules are evaluated first. In this example, Write(src/secrets/**) would be blocked even though Write(src/**) allows the parent directory.


For bigger changes to Claude’s default behavior, use defaultMode:

{
"permissions": {
"defaultMode": "auto"
}
}
ModeBehavior
defaultAsk for everything not explicitly allowed (safest)
autoUse heuristics to auto-approve “safe” actions; ask for risky ones
acceptEditsAuto-approve file edits; ask for shell commands
dontAskNever ask — approve everything not explicitly denied
bypassPermissionsSkip all permission checks (use only in trusted automation)
planClaude plans actions but never executes them

Recommendation for most junior developers: Start with default (the default). Add specific allow rules for things you approve of often. Only switch to auto once you’ve used Claude enough to trust its judgment in your project.


LocationScopeNotes
.claude/settings.jsonProject onlyChecked into git. Shared with your team.
~/.claude/settings.jsonAll your projectsPersonal preferences. Never checked in.
.claude/settings.local.jsonProject onlyPersonal overrides. Add to .gitignore.

For permission rules that apply to one project, use .claude/settings.json.


Claude still asks even with an allow rule

  • Check the rule syntax — Write(src/**) not Write(src/*) for recursive match
  • Make sure your file is at .claude/settings.json (not claude/settings.json)
  • Check that the JSON is valid (no trailing commas, correct quotes)

Claude was blocked by a deny rule I didn’t expect

  • Deny rules check the full path — if you deny Write(/tmp/*) and allow Write(src/**), a file in /tmp/ is still denied
  • Check for rules in ~/.claude/settings.json (your global settings might have deny rules)


← Back to GettingStarted/README.md