---
title: "settings.json, Field by Field"
description: "The settings file that earns its place in a real repo — grouped by what each field changes, with the ones worth touching separated from the ones that are not."
source: "https://claudeflow.so/guides/settings-json/"
---

There are two ways to write about `settings.json`. One is to list every field in
alphabetical order — which the [official reference](https://code.claude.com/docs/en/settings)
already does, exhaustively and correctly, and it will always be more current than anything
we publish.

This is the other way: **grouped by what it changes for you**, with a clear line between the
fields that pay for themselves in the first week and the ones you should leave alone until
you have a reason.

  A perfect settings file in the wrong location does nothing. If you are here because an
  edit had no effect, start with [Your config isn't applying](/guides/config-not-applying/)
  — this guide assumes you already know which file wins.

## Pick the file before you write a line

Three questions, one answer. This is the decision people get wrong, and no amount of
correct JSON survives it.

| Who is this for? | File | In git? |
| --- | --- | --- |
| Everyone who clones this repo | `.claude/settings.json` | Yes — commit it |
| Only you, only this repo | `.claude/settings.local.json` | No — gitignored |
| Only you, every repo | `~/.claude/settings.json` | No — lives in your home |

  Anything resembling a credential, a machine-specific path, or a personal preference does
  not belong in the committed file. Once it is in git history it is in everyone's clone, and
  removing it later does not remove it from history.

## What the fields actually change

Rather than the full field list, here is the grouping that matters when you are deciding
what to put in a real project.

    What Claude may do without asking, and what it must never do. This is the group with the
    highest payoff and the highest blast radius — worth your attention first.

    Shell commands fired on lifecycle events. This is how a kit installs itself, how
    formatting gets enforced, how context gets loaded on every session.

    Variables handed to the session and the commands it runs. Useful for pointing tooling at
    the right place; dangerous as a home for secrets in a committed file.

    Which model runs, and how the tool presents itself. Easy to change, easy to over-tune —
    leave defaults until you have a measured reason.

## The permissions block

If you only configure one thing, configure this. It is the difference between approving
every `ls` for a year and approving nothing that matters.

The shape is three lists — what is allowed silently, what is refused outright, and what
should always stop and ask:

```json title=".claude/settings.json"
{
  "permissions": {
    "allow": [
      "Bash(git status)",
      "Bash(git diff:*)",
      "Bash(npm run test:*)"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Read(./.env)",
      "Read(./secrets/**)"
    ]
  }
}
```

Three things worth understanding before you copy that:

1. **`deny` is the load-bearing list.**

   `allow` saves you keystrokes. `deny` saves you from a bad afternoon. Write the deny rules
   first, and write them for the things that are unrecoverable rather than merely annoying —
   destructive commands and files that hold credentials.

2. **Allow the read-only commands, not the categories.**

   `Bash(git status)` is safe because it cannot change anything. A blanket allow on `Bash`
   is not a shortcut, it is the removal of the feature. Grant the specific commands you find
   yourself approving over and over.

3. **Denying a file is not the same as securing it.**

   A `deny` on `./.env` stops the tool from reading it through the normal path. It is a
   guardrail, not a security boundary — do not treat it as a reason to keep secrets somewhere
   they would otherwise not belong.

  Do not try to predict which commands you will approve. Work for a few days, notice which
  prompts you approve reflexively, and promote exactly those. An allowlist written from
  guesswork is always both too broad and too narrow.

## The hooks block

Hooks run shell commands when something happens in the session. They are the mechanism
behind almost every "how do I make it always do X" question.

```json title=".claude/settings.json"
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          { "type": "command", "command": "./.claude/scripts/load-context.sh" }
        ]
      }
    ]
  }
}
```

  A hook in a committed settings file runs for everyone who clones the repo. Review hooks in
  pull requests the way you would review a CI change, because that is what they are.

Hooks earn a guide of their own — the failure modes are specific enough that a section here
would mislead. That one is coming in this same wave.

## A starting file

This is a defensible baseline for a project: no secrets, no machine-specific paths, nothing
that assumes your setup.

    ```json title=".claude/settings.json"
    {
      "permissions": {
        "allow": [
          "Bash(git status)",
          "Bash(git diff:*)",
          "Bash(git log:*)"
        ],
        "deny": [
          "Bash(rm -rf:*)",
          "Read(./.env)",
          "Read(./.env.*)"
        ]
      }
    }
    ```

    ```json title=".claude/settings.local.json"
    {
      "permissions": {
        "allow": [
          "Bash(npm run dev:*)"
        ]
      }
    }
    ```

Note what is *not* there. No model pin, no environment block, no hooks. Every one of those
is a decision you should make deliberately later, not inherit from a template today.

## Common mistakes

**Copying a maximal settings file from the internet.** You inherit someone else's risk
tolerance and someone else's machine. Every allow rule you did not write yourself is a rule
you cannot justify in review.

**Putting secrets in the committed file.** Environment blocks look like a convenient home
for tokens. They are in git forever the moment you push.

**Allowing broad command patterns to stop the prompts.** It works, briefly, and it removes
the only thing standing between an agent and a destructive command.

**Pinning a model and forgetting.** A pin that made sense six months ago quietly costs you
every improvement since. If you pin, write down why, and revisit it.

**Editing while a session is open.** Start a fresh session before you decide a change did
nothing. This one wastes more time than every other mistake on this list.

## Checklist

- [ ] The file matches its audience: team, local, or user
- [ ] Nothing resembling a secret or a machine path is in the committed file
- [ ] `deny` covers destructive commands and credential files
- [ ] Every `allow` entry is a command you actually approved repeatedly
- [ ] No blanket allow on an entire tool
- [ ] Hooks, if any, were reviewed as carefully as CI config
- [ ] The JSON validates
- [ ] A fresh session was started before judging the result

## Where to go next
