Settings, Permission Rules & Config
settings.json precedence and the rules that govern Claude
- Recite the settings precedence chain from CLI flags down to managed policy and predict which value wins when two files set the same key
- Write permission allow/ask/deny rules with correct pattern syntax and manage them with /permissions and /fewer-permission-prompts
- Identify the high-value settings keys — autoUpdatesChannel, minimumVersion, claudeMdExcludes, autoMemoryEnabled, apiKeyHelper, hooks — and the env block (DISABLE_AUTOUPDATER, CLAUDE_CODE_GIT_BASH_PATH)
- Use /config, /theme, and /color to set theme, editor mode, output style, and prompt suggestions interactively
- Install and scope plugins (claude plugin install|remove|list, /plugins, --plugin-dir) and lock down an org with managed policy
Claude Code's behavior is governed by a layered configuration system: settings.json files that merge along a strict precedence chain, permission rules that allow/ask/deny specific tools, and an env block plus interactive config for everything from themes to plugins. This lesson maps the whole stack — who wins when files disagree, the exact rule syntax, the keys that matter, and how to scope config per project, per user, and per org policy.
- 1The mental model: a stack of settings that merge
- 2The precedence chain (highest wins)
- 3Permission rules: allow, ask, deny
- 4Key settings and the env block
- 5Interactive config: /config, /theme, /color
- 6Plugins: marketplaces of skills, tools, MCP, and hooks
- 7Managed policy: org-wide control
The mental model: a stack of settings that merge
Everything Claude Code does is shaped by configuration — which model runs, whether it can push to git, what color the prompt is, which plugins load. That configuration does not live in one place. It lives in a stack of settings.json files plus a few command-line overrides, and they merge along a fixed precedence chain.
The key idea: this is not "one file wins." Each layer contributes keys, and when two layers set the same key, the higher-precedence layer wins for that key. Layers you didn't touch fall through to whatever the lower layers set. So your project's settings.json can pin a model while your user-level ~/.claude/settings.json still supplies your theme — they compose.
There are three reasons the layering exists, and they map onto who should control what:
- Per-user preferences (
~/.claude/settings.json) — your theme, your editor mode, defaults you want everywhere. - Per-project settings (
.claude/settings.json, checked into the repo) — conventions the whole team shares: allowed commands, hooks, model. - Per-machine / personal-to-this-repo (
.claude/settings.local.json, gitignored) — overrides just for you on this checkout, not shared with teammates. - Org policy (managed settings) — rules an administrator imposes that no lower layer can override.
Get the precedence chain in your head first; the rest of the lesson is detail hung on that spine.
Key insight
Merge, don't replace
A common mistake is assuming a project settings.json "resets" everything. It doesn't — it only overrides the specific keys it declares. Your user-level theme, env vars, and unrelated permission rules still apply unless a higher layer explicitly changes them. Configure the minimum at each layer and let the rest fall through.
The precedence chain (highest wins)
Here is the full chain, highest precedence at the top. When the same key is set in two places, the higher entry wins:
| Rank | Source | Scope / who controls it |
|---|---|---|
| 1 (wins) | CLI flags (--model, --permission-mode, --allowedTools, …) | This one invocation only |
| 2 | --settings ./file.json (file or inline) | This one invocation; overrides matching keys |
| 3 | project .claude/settings.local.json | You, on this checkout (gitignored) |
| 4 | project .claude/settings.json | The team (checked into the repo) |
| 5 | ~/.claude/settings.json | You, everywhere (per-user) |
| 6 (floor) | Managed policy / enterprise managed settings | Org admin — cannot be overridden |
There is a deliberate twist at the bottom. Normally "higher wins," but managed policy is special: although it sits at the bottom of the merge, an administrator's managed settings are designed so the layers above cannot relax them. A managed deny rule stays denied even if your project tries to allow it. Think of managed policy as a hard floor the rest of the stack builds on, not a value you can shout over.
Walking the chain with an example — suppose all of these set a model:
CLI: claude --model opus ← wins this run
--settings file: { "model": "sonnet" }
settings.local.json: { "model": "sonnet" }
settings.json: { "model": "haiku" }
~/.claude: { "model": "sonnet" }The session runs on opus — the CLI flag beats everything. Remove the flag and --settings wins; remove that and settings.local.json wins; and so on down. Each key resolves independently down this same ladder.
Tip
Where to put a setting
Team convention everyone needs → project .claude/settings.json (commit it). A tweak only you want on this repo → .claude/settings.local.json (gitignored). A preference you want in every project → ~/.claude/settings.json. A one-off for a single run → a CLI flag or --settings. Match the layer to the audience.
Permission rules: allow, ask, deny
The most consequential block in settings.json is permissions. It holds three lists — allow, ask, and deny — that govern what Claude may run, with pattern syntax targeting specific tools and commands.
{
"permissions": {
"allow": ["Bash(git status)", "Bash(git log *)", "Bash(npm test)", "Read"],
"ask": ["Bash(git push *)"],
"deny": ["Bash(rm -rf *)", "Read(./.env)", "Read(./secrets/**)"]
}
}What each list means:
allow— run without prompting. Use it for the safe, repetitive stuff (git status,git diff,npm test).ask— always prompt, even if a mode would otherwise auto-approve. Use it to force a human checkpoint on sensitive-but-allowed actions.deny— never run, no prompt.denywins over everything — it overrides the active permission mode and anyallowrule.
Pattern syntax. A rule is Tool(pattern):
| Pattern | Matches |
|---|---|
Read (bare tool) | Every use of the Read tool |
Bash(git status) | Exactly git status |
Bash(git log *) | git log followed by anything (the * is a wildcard) |
Read(./.env) | Reading that specific file |
Read(./secrets/**) | Reading anything under secrets/ (recursive glob) |
Manage all of this without hand-editing JSON using /permissions (alias /allowed-tools): it lists active rules and recent denials, and lets you turn a denial into a rule on the spot (press r to retry). And to generate a sensible allow-list automatically, run /fewer-permission-prompts — it scans your session transcripts for the safe, read-only commands you keep approving and proposes an allowlist to add to your project .claude/settings.json, cutting future prompts.
You can also allow-list for a single run from the CLI with the same rule syntax:
claude --allowedTools "Bash(git log *)" "Bash(git diff *)" "Read"Watch out
Keep allow rules narrow
A broad rule like Bash(git *) sweeps in git push and git reset --hard. Allow-list specific safe commands (Bash(git status), Bash(git log *)) rather than wildcarding a whole tool family. Narrow rules keep the dangerous variants prompting. And remember: anything that must never happen belongs in deny, not merely left out of allow.
Key settings and the env block
Beyond permissions, a handful of settings.json keys carry real weight. Here are the ones worth knowing by name:
| Key | What it controls |
|---|---|
autoUpdatesChannel | Release channel: latest (new features immediately) vs stable (~1 week delay, skips major regressions) |
minimumVersion | An update floor — refuse to run below this version (useful for org consistency) |
claudeMdExcludes | Glob(s) of CLAUDE.md files to skip — invaluable in monorepos. (Managed-policy CLAUDE.md can't be excluded.) |
autoMemoryEnabled | Toggle the auto-memory system (MEMORY.md); set false to disable |
apiKeyHelper | Path to a script that returns credentials dynamically (called every 5 min or on HTTP 401) |
hooks | Shell commands wired to lifecycle events for guaranteed behavior (lint on save, block patterns, run tests at Stop) |
permissions | The allow/ask/deny block from the previous section |
env | A block of environment variables applied to the session |
The env block is how you set environment variables declaratively in settings rather than exporting them in your shell:
{
"env": {
"DISABLE_AUTOUPDATER": "1",
"CLAUDE_CODE_GIT_BASH_PATH": "C:\\Program Files\\Git\\bin\\bash.exe"
}
}Two env vars you'll reach for often:
DISABLE_AUTOUPDATER=1— turns off only the background auto-update (useDISABLE_UPDATES=1to stop all update paths). Handy on locked-down or offline machines, and it pairs naturally withminimumVersionfor controlled rollouts.CLAUDE_CODE_GIT_BASH_PATH— on Windows, points Claude Code at your Git Bash executable so the Bash tool works (otherwise it falls back to the PowerShell tool).
hooks deserve a special mention. Unlike CLAUDE.md instructions — which are advisory (Claude may or may not follow them) — a hook is a shell command Claude Code runs at a lifecycle point (PreToolUse, PostToolUse, Stop, SessionStart, and more). Hooks are deterministic and guaranteed. When you need an action to always happen, configure a hook in settings; don't merely ask in conversation.
Note
apiKeyHelper for dynamic credentials
apiKeyHelper points to a script that prints a credential to stdout — useful when your token rotates (gateways, short-lived creds). Claude Code calls it every ~5 minutes or whenever it gets an HTTP 401, and warns if the script takes over 10 seconds. It does not require you to hard-code a secret into settings.json.
Interactive config: /config, /theme, /color
Not every setting is something you want to edit by hand. The interactive layer covers the everyday cosmetic and ergonomic choices.
/config (alias /settings) opens the settings UI. From there you set:
- Theme — light / dark / auto and accessibility variants.
- Editor mode — toggle vim keybindings vs the default editor mode. (The old
/vimcommand was removed; this lives in/config → Editor modenow.) - Output style — how Claude formats its responses.
- Prompt suggestions — turn the suggestion hints on or off.
- The release channel (
latestvsstable) and model can also be chosen here.
/theme jumps straight to theme selection — auto, light, dark, colorblind-friendly (daltonized) variants, ANSI, or a custom theme from ~/.claude/themes/.
/color [color] sets the accent color of your prompt — a quick way to visually distinguish multiple concurrent sessions or environments (e.g., a red prompt for a production-adjacent checkout).
These interactive choices are written back into your settings files, so they persist across sessions and still obey the same precedence chain — a theme set in /config lands in ~/.claude/settings.json unless a higher layer overrides it.
Tip
Color-code dangerous sessions
Running several Claude Code sessions across different repos or against different environments? Give each a distinct prompt color with /color so you never fire a command at the wrong one. A glanceable visual cue beats reading the working-directory path every time.
Plugins: marketplaces of skills, tools, MCP, and hooks
Plugins are bundles of capability you install into Claude Code. A single plugin can package any mix of skills, tools, MCP servers, and hooks, and plugins are distributed through marketplaces — curated collections you install from.
Manage them from the CLI:
claude plugin install code-review@claude-plugins-official # install from a marketplace
claude plugin list # list installed plugins
claude plugin remove code-review # uninstallInside a session, /plugins opens the plugin manager (browse, install, enable/disable) and /reload-plugins hot-reloads them after a change.
For local development or a one-off, load a plugin straight from a directory or .zip for the current session — no install required:
claude --plugin-dir ./my-plugin # repeatable; loads for this session onlyThis is the right tool for testing a plugin you're building, or pulling in a capability for a single run without committing to a global install. Because a plugin can carry MCP servers and hooks, installing one is a meaningful trust decision — it can wire commands into your lifecycle and connect external services. Install from marketplaces you trust, and review what a plugin bundles before enabling it.
Example
What lives in a plugin
A team's internal plugin might bundle: a /deploy-check skill, a custom MCP server for their issue tracker, a PreToolUse hook that blocks edits to infra/, and an output style. One claude plugin install brings all of it; claude plugin remove takes it all away — far cleaner than copying loose config files between machines.
Managed policy: org-wide control
The bottom of the precedence chain is reserved for administrators. Managed policy (also called enterprise managed settings) lets an organization impose configuration that the layers above cannot override — the one place where "lower in the chain" still means "final word."
Managed policy is delivered as a settings file (and a managed-policy CLAUDE.md) installed at OS-level system paths an ordinary user can't edit. Its purpose is governance: an admin can guarantee that across every developer's machine, certain commands are denied, a minimumVersion floor is enforced, auto-updates follow a chosen channel, or a particular CLAUDE.md is always loaded.
The critical properties:
- A managed
denyrule cannot be relaxed by a projectallowrule or a user setting. Org-level prohibitions hold everywhere. - A managed-policy CLAUDE.md cannot be excluded by
claudeMdExcludes— org instructions always load. - It composes with everything else: managed policy sets the floor, and the personal/project/CLI layers build on top of it for everything the policy leaves open.
This is how enterprises combine the flexibility developers need with the guarantees compliance requires: individuals still pick their theme, model, and project rules, while the organization keeps a non-negotiable safety and consistency baseline underneath.
Key insight
Two senses of "wins"
For ordinary keys, higher in the chain wins — a CLI flag beats a user setting. But managed policy inverts the intuition: it sits at the bottom of the merge yet is engineered so nothing above it can loosen it. Hold both ideas at once — higher layers win except against the guarantees an admin has locked in.
Try it: Layer and lock down config for a real repo
Take a project you work in and configure it through the full settings stack — proving you understand precedence, rules, and scope.
- See the chain in action. Set
"model"to one value in your~/.claude/settings.jsonand a different value in the project's.claude/settings.json. Start a session and run/config(or check the status line) to confirm the project value wins over the user value. Then launch once withclaude --model <a-third-value>and confirm the CLI flag now wins. - Build a permission profile. In project
.claude/settings.json, add apermissionsblock: anallowlist of safe commands (Bash(git status),Bash(git log *),Bash(npm test),Read), anaskentry (Bash(git push *)), and adenyblock (Bash(rm -rf *),Read(./.env)). Open/permissionsand verify the rules are active. Then ask Claude to push and confirm theask/denybehavior fires regardless of mode. - Auto-generate an allowlist. Run
/fewer-permission-prompts, review what it proposes from your transcripts, and accept a narrow set into the project settings. Note how it targets read-only commands. - Scope a personal override. Add a key (say a different
autoUpdatesChannelor anenvvar) to.claude/settings.local.jsonand confirm it overrides the committed project value for you — and that it's gitignored so teammates aren't affected. - Try the cosmetic layer. Use
/configto flip editor mode (vim) and output style,/themeto switch theme, and/colorto set a distinctive prompt color. Confirm the choices persist into a settings file. - Reflect. Write a short paragraph: for one rule you added, which layer did you choose and why — would it have been wrong to put it one layer higher or lower? If you have org/admin access, note what you would push into managed policy so no developer could relax it.
Key takeaways
- 1Precedence (highest first): CLI flags > --settings > project .claude/settings.local.json > project .claude/settings.json > ~/.claude/settings.json > managed policy. Layers merge per-key; unset keys fall through.
- 2Permission rules use Tool(pattern) syntax in allow/ask/deny lists (e.g. Bash(git log *), Read(./secrets/**)); deny always wins; manage with /permissions and auto-generate a safe allowlist with /fewer-permission-prompts.
- 3Know the high-value keys: autoUpdatesChannel, minimumVersion, claudeMdExcludes, autoMemoryEnabled, apiKeyHelper, hooks, and the env block (DISABLE_AUTOUPDATER, CLAUDE_CODE_GIT_BASH_PATH).
- 4Use /config for theme, editor (vim) mode, output style, and prompt suggestions; /theme for themes; /color for prompt accent color — all written back into settings.
- 5Plugins bundle skills/tools/MCP/hooks from marketplaces: claude plugin install|remove|list, /plugins and /reload-plugins in-session, and --plugin-dir for a local/session-only load.
- 6Managed policy / enterprise managed settings sits at the bottom of the chain but cannot be overridden by higher layers — the place for org-wide deny rules, a minimumVersion floor, and a non-excludable CLAUDE.md.
Quiz
Lock in what you learned
Check your understanding
0 / 4 answered
1.Your project's `.claude/settings.json` sets `"model": "haiku"`. You launch with `claude --model opus`, and your `~/.claude/settings.json` sets `"model": "sonnet"`. Which model runs, and why?
2.Which permissions setup guarantees Claude can never push to git but can run `git status` without a prompt?
3.An administrator wants every developer's Claude Code to refuse a particular command and always load an org CLAUDE.md, with no way for individuals to override it. Where does this belong?
4.You're building a plugin locally and want to try it in your current session without installing it globally. Which is correct?
Go deeper
Hand-picked sources to keep learning
Settings precedence, permission rules, hooks, and the deny-over-conversation principle.
Authoritative list including /config, /permissions, /fewer-permission-prompts, /theme, /color, and /plugins.
Exact syntax for --settings, --allowedTools, --plugin-dir, and --permission-mode.
autoUpdatesChannel (latest vs stable), DISABLE_AUTOUPDATER, and CLAUDE_CODE_GIT_BASH_PATH on Windows.
claudeMdExcludes and autoMemoryEnabled in context, plus how managed-policy CLAUDE.md behaves.
Changelog, issues, and release notes for tracking settings/plugin changes across versions.