Claude CodeClaude Code 2.1 · macOS, Linux, Windows
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
31 small wins to finish your pathNext lesson

settings.json: allow, ask, deny

A mode decides how much you are asked in general. Rules decide the specific answers, in a file, once.

Every time you choose allow this kind of thing from now on at a prompt, a rule gets written. Writing them yourself is the same thing without the clicking, and it can be committed so the whole team gets it.

json
{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git commit *)"
    ],
    "deny": [
      "Bash(git push *)"
    ]
  }
}

Three lists exist: allow never asks, ask always asks even in a permissive mode, and deny refuses outright. The docs give the order plainly: rules are evaluated deny, then ask, then allow, and the first match decides. A more specific rule does not jump the queue, which is what makes a deny rule worth writing.

How a rule matches

The pattern is the tool name and, in brackets, what it may do.

RuleMatchesDoes not match
Bash(npm run build)Exactly that commandnpm run build --watch
Bash(npm run *)npm run build, npm run testnpm install
Read(./.env)Reading that file hereA .env in a parent directory
WebFetch(domain:example.com)Fetches to that domainAny other host

The gap worth knowing about is that these are patterns over the command text, not a boundary the operating system enforces, which is the next lesson. Bash(git push *) in the deny list stops git push origin main. It does not stop git -C . push origin main, because that is a different string. The documentation says so plainly, and the lesson to take from it is that rules are a good fence and not a wall.

Where the file goes

FileScope
.claude/settings.jsonThe project. Committed, so everyone gets it
.claude/settings.local.jsonYou, in this project. Gitignored
~/.claude/settings.jsonYou, everywhere
Managed settingsSet by an organisation, and not overridable

A sensible project file is short: allow the commands your team runs a hundred times a day, deny the two or three that should never happen without a person, and leave everything else to the mode.

Start with deny
Deny rules are the ones worth writing first, and they are worth writing even in a team where nobody would do it on purpose. Bash(git push *) and Read(**/.env) cost nothing and remove a whole class of bad afternoon.
Try it yourself
  • Add an allow rule for your test command and notice the prompts stop.
  • Add a deny rule for something, then ask Claude to do it and read what happens.

Little by little, you're building something great.