Permission prompts protect you, but approving git status for the fortieth time trains you to click "Allow" without reading. The fix is to auto-approve what's genuinely safe, hard-deny what must never happen, and keep prompts for the risky middle.
| Mode | What it does | Use it for |
|---|---|---|
default | Prompts the first time each tool or command is used | Everyday work |
acceptEdits | Auto-accepts file edits, still prompts for other actions | Iterating quickly in a clean git tree |
plan | Read-only: Claude researches and proposes a plan without changing anything | Big or unfamiliar changes |
bypassPermissions | Skips all permission prompts | Only isolated containers or VMs |
Press Shift+Tab to cycle modes during a session, or start with claude --permission-mode plan. Set a default with "defaultMode" inside permissions in settings.json.
About bypass mode (--dangerously-skip-permissions): with no prompts, one bad command can delete files, push code or leak credentials. Use it only somewhere disposable, with no production credentials and nothing you can't recreate.
Rules go in permissions.allow, permissions.ask or permissions.deny in a settings file:
Bash(npm test): exactly this command.Bash(npm run test:*): any command starting with npm run test.Read(./.env), Edit(./migrations/**): gitignore-style path patterns.WebFetch(domain:github.com): fetches from one domain.Deny beats ask, and ask beats allow. A deny rule can't be overridden by a broader allow rule.
{
"permissions": {
"allow": [
"Bash(git status)",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(git branch:*)",
"Bash(ls:*)",
"Bash(npm run lint)",
"Bash(npm run typecheck)",
"Bash(npm test:*)"
],
"ask": [
"Bash(git push:*)",
"Bash(npm install:*)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./**/*.pem)",
"Read(./secrets/**)"
]
}
}
Swap in your stack's commands, such as pytest, go test or cargo test. Keep allow rules narrow:
Bash(git:*), which also approves git reset --hard and git push --force.Bash(npm:*), which approves npm publish and arbitrary npm exec.Bash(python:*) and similar: an interpreter can run anything.A deny rule on Read(./.env) stops Claude opening the file with its Read tool. It doesn't stop cat .env through Bash unless Bash is also restricted. Treat deny rules as one layer, and keep real production secrets out of your development machine entirely.
Prefix rules can't express "allow rm -rf ./build but never rm -rf ~/". Hooks can, because they inspect the full command with real code and block it with exit code 2. A good setup uses both:
/permissions..claude/settings.json, and personal convenience allows in ~/.claude/settings.json. See settings.json explained.plan mode first for anything touching auth, payments or migrations. Read the plan, then let it edit.Keelwork bundles 10 workflow skills, 5 tested safety hooks (including a full guard-bash and a secret scanner), 3 subagents and 5 CLAUDE.md templates, with a one-command installer that safely merges into your settings.
Get Keelwork — $24 →