Allow npm test, Gate Destructive Git: Claude Code Permissions
▶ Watch on YouTube & subscribe to The Stack Underflow
You are ten minutes into a session with Claude Code, and it stops to ask permission before every single npm test call. You approve it. It asks again two minutes later. You approve it again. By the twentieth prompt you are tempted to just turn permissions off entirely — except you also want it to keep asking before it runs git push --force or rm -rf. Those two goals feel like they’re in tension. They aren’t, and the reason they aren’t is the entire answer to this question.
The one-sentence version: Permissions in Claude Code are not a single on/off switch — they are scoped rules per tool pattern, so you can allow the safe, repetitive command and still gate the dangerous, irreversible one.
The question
You want npm test to run without prompting, but destructive git to still ask. Best config?
- A) Turn off all permissions
- B) Scoped permission rules — allow
Bash(npm test:*), keep git behind ask/deny - C) Approve everything once
- D)
bypassPermissionsmode
Pause here and pick an answer before you scroll — the mechanism matters more than the letter.
The answer
B. Scoped permission rules — allow Bash(npm test:*), keep git behind ask/deny.
Claude Code’s permission system is not a global toggle. It is a set of rules, each scoped to a specific tool-and-pattern combination, evaluated per tool call. Every time the agent wants to invoke a tool, that specific invocation gets checked against the rule set and resolves to exactly one of three outcomes: allow (run silently), ask (prompt you), or deny (refuse outright). That means “permissive for tests, cautious for destructive git” isn’t a workaround — it’s the system working exactly as designed. You write one rule that matches the npm test invocation pattern and puts it in the allow list, and you leave (or explicitly place) destructive git patterns in ask or deny. The two rules don’t conflict because they match different things.
Claude Code permission evaluation — per tool call
Claude wants to run: Bash(npm test)
│
▼
┌─────────────────────────────────────────────┐
│ permissions.allow: ["Bash(npm test:*)"] ✓ │ ← pattern matches → ALLOW
│ permissions.ask: ["Bash(git push:*)"] │ (runs silently, no prompt)
│ permissions.deny: ["Bash(rm -rf *)"] │
└─────────────────────────────────────────────┘
Claude wants to run: Bash(git push --force)
│
▼
┌─────────────────────────────────────────────┐
│ permissions.allow: ["Bash(npm test:*)"] │
│ permissions.ask: ["Bash(git push:*)"] ✓ │ ← pattern matches → ASK
│ permissions.deny: ["Bash(rm -rf *)"] │ (you get prompted)
└─────────────────────────────────────────────┘
Claude wants to run: Bash(rm -rf node_modules)
│
▼
┌─────────────────────────────────────────────┐
│ permissions.allow: ["Bash(npm test:*)"] │
│ permissions.ask: ["Bash(git push:*)"] │
│ permissions.deny: ["Bash(rm -rf *)"] ✓ │ ← pattern matches → DENY
└─────────────────────────────────────────────┘ (refused, no execution)
In settings.json, this is a permissions object with allow, ask, and deny arrays of tool-pattern strings — the pattern is the tool name plus an argument matcher, for example Bash(npm test:*) matches any npm test invocation, and Bash(git push:*) matches any git push invocation regardless of flags. Wildcards let you scope narrowly (Bash(npm test:*) only) or broadly (Bash(git *) catches every git subcommand). The safe, repetitive, side-effect-free command gets an allow rule so it never interrupts your flow. The irreversible command — force-push, hard reset, rm -rf — stays behind ask (or gets moved to deny if you never want it run at all, even with confirmation). Exact flag names and file paths shift release to release, so treat the strings above as illustrative and confirm current syntax in Anthropic’s official Claude Code docs before you write your own config.
Why the other options fail
- A) Turn off all permissions. This removes the gate for everything, not just
npm test. Destructive git now runs silently too — you’ve solved the annoyance and reintroduced the exact risk the question is asking you to avoid. - C) Approve everything once. A one-time approval is not a durable rule. It either applies to a single invocation (so the prompting comes right back next time) or, if it’s a broad “always allow this session” click, it isn’t scoped to
npm testspecifically — it quietly covers whatever else gets approved alongside it, including things you didn’t mean to allow. - D)
bypassPermissionsmode. This is a global mode that skips confirmation for every tool call across the board. It’s the same failure as option A wearing a different name: you get silence onnpm test, but you also get silence on the git command that deletes a branch nobody meant to delete. A global bypass cannot express “allow this, ask for that” — only scoped rules can.
The concept behind it
The pattern here generalizes well past this one question, and it’s worth internalizing because Domain 3 leans on it repeatedly: Claude Code’s permission model is rule-based and additive, not a single switch. Every tool invocation — a bash command, a file write, a web fetch — gets matched against your configured rules and resolves to allow, ask, or deny. There is no fourth state and no implicit default beyond “ask” for anything not otherwise matched.
That gives you a design axis you can use deliberately: put commands on a spectrum from safe and repetitive to rare and irreversible, and write rules that match that spectrum.
| Command pattern | Risk profile | Typical rule |
|---|---|---|
Bash(npm test:*) | Repeatable, no side effects outside the sandbox | allow |
Bash(npm run lint:*) | Repeatable, read-only | allow |
Bash(git commit:*) | Reversible, low blast radius | allow or ask |
Bash(git push:*) | Affects shared state, hard to fully undo | ask |
Bash(git push --force:*) | Can destroy others’ history | ask or deny |
Bash(rm -rf *) | Irreversible, destructive | deny |
A few things worth knowing about how the rules actually behave:
- Patterns are specific, not vague categories.
Bash(npm test:*)matches annpm testinvocation and its arguments — it does not implicitly covernpm run buildornpm install. If you want a broader allow, you write a broader pattern; the system won’t guess your intent for you. - The three-way split (allow/ask/deny) exists for every tool, not just Bash. File writes, MCP tool calls, and web fetches go through the same evaluation. The
npm testexample is the clearest teaching case because everyone recognizes the annoyance, but the underlying mechanism is tool-agnostic. - Scoped rules compose with organizational policy. Your local allow/ask/deny rules sit inside a larger precedence hierarchy — managed enterprise policy can override what you set locally, which is a different (and equally testable) mechanism covered in the permission-precedence tutorial linked below.
- Deny beats ask, and ask beats silence. When a command could match more than one rule, the more restrictive outcome should win — this is why
denyis the right home for genuinely irreversible actions rather than relying onaskand hoping you’re paying attention every time.
The exam-relevant takeaway is not “memorize the exact bracket syntax,” because that syntax is exactly the kind of live-product detail Anthropic revises between releases. The durable fact is the shape of the system: permissions are evaluated per tool call against scoped patterns, with three possible outcomes, and you author rules to match your own risk tolerance per command category rather than accepting one blanket setting for the whole session.
FAQ
Does allowing npm test also allow other npm commands?
No. A rule like Bash(npm test:*) matches that specific command pattern. npm install, npm run build, and other subcommands are unaffected unless you write separate rules for them (or a broader pattern that intentionally covers more).
Where do these permission rules actually live?
In Claude Code’s settings files, as a permissions object with allow, ask, and deny arrays of tool-pattern strings. Which settings file you put them in — shared project settings versus your own local, gitignored settings — determines whether the rule applies to your whole team or just to you; that split is covered in the personal-vs-team settings tutorial below. Confirm the exact current file paths and key names in Anthropic’s docs, since this is one of the fastest-moving corners of Claude Code.
Is bypassPermissions mode ever the right call?
Sometimes, deliberately — for example, a fully sandboxed CI job where nothing in the environment matters if it gets destroyed. But it is a global override, not a scoping tool, so it’s the wrong answer any time you want different behavior for different commands, which is most day-to-day development work.
What happens if a command doesn’t match any rule? It falls back to a prompt — you get asked. Scoped allow rules exist specifically to remove that prompt for the commands you’ve decided are safe to run unattended.
Where this fits in the CCA series
This question sits in Domain 3 — Claude Code Configuration & Workflows (20% of the exam), the domain that covers how you actually configure and govern day-to-day Claude Code usage. Permission scoping is one mechanism among several in this domain worth knowing cold:
- Enterprise Denies, You Allow — Who Wins? Claude Code Permission Precedence — the ladder that decides which rule set wins when your local settings disagree with organizational policy.
- The Setting You Must Not Commit: settings.local.json in Claude Code — where personal permission tweaks live so they don’t leak into the shared repo.
- A Rule for You, Not Your Team: Local vs Shared Claude Code Settings — the broader personal-vs-team split that permission rules are one instance of.
- A Scoped ‘Test-Writer’ Agent: Custom Subagents in Claude Code — scoping applied to an entire agent’s tool access, not just individual commands.
For the full picture of how all five exam domains fit together, start with the Claude Certified Architect (CCA) Exam Guide.
Not affiliated with, authorized, or endorsed by Anthropic. “Claude” and “Claude Certified Architect” are trademarks of Anthropic. These are original practice questions for study, not real exam content — verify current exam details on Anthropic’s official pages.
Exam logistics as of mid-2026: Pearson VUE, $125, retakes available, 60 questions in 120 minutes, passing score 720/1000, five domains — verify current details on Anthropic’s official pages before you register. Browse all tutorials for the rest of the series.
Found this useful? The deep version lives on YouTube — new breakdowns of how AI dev tools actually work, weekly.
Subscribe on YouTube →