Claude Code settings.json team git configuration CLAUDE.md 2026

Claude Code settings.json in Teams: How to Prevent Conflicts and Share Config Safely

The Prompt Shelf ·

At some point, a teammate pushes a settings.json change that overwrites your local permission rules. Or you push yours and break theirs. The merge conflict is trivial to resolve, but the underlying problem isn’t: you’re using the same file for two different jobs — shared team config and personal preferences — and git can’t tell the difference.

Here’s how to separate them cleanly.


The Problem: One File, Two Jobs

Claude Code stores its configuration in .claude/settings.json at the project root. When you use Claude Code locally, this file controls which tools Claude can use, which commands are pre-approved, what the default permission mode is, and more.

That’s fine for solo work. The moment a second engineer joins the project, you have a design conflict baked into the system:

  • Shared config (the tools Claude needs to do its job in this project) belongs in version control. Everyone on the team should be running with the same base.
  • Personal config (your preferred permission level, your local paths, your experimental settings) is yours. Committing it makes everyone else’s environment subtly wrong.

Claude Code solves this with a two-file system that most teams never learn about until they’ve already had the conflict.


The Two-File System: settings.json vs settings.local.json

Claude Code reads configuration from two files:

FilePurposeGit status
.claude/settings.jsonTeam-shared project configCommit this
.claude/settings.local.jsonIndividual overridesNever commit

settings.local.json values take precedence over settings.json. The merge happens at Claude Code startup — you never need to manually reconcile them.

The split is intentional. Anthropic designed it this way: shared settings live in settings.json (committed), personal overrides live in settings.local.json (gitignored). If your .gitignore doesn’t reflect this, fix it now.


Step 1: Update .gitignore

# .gitignore

# Claude Code personal settings — never commit
.claude/settings.local.json

# Claude Code auth and sensitive state
.claude/.credentials.json
.claude/store/

# Commit these:
# .claude/settings.json      <- team config
# .claude/CLAUDE.md          <- team context (optional, see below)

The .claude/store/ directory contains cached responses and internal state. It’s machine-specific and has no business being in your repository.


Step 2: What Goes in settings.json (Shared)

The shared settings file should contain everything that’s true about the project, not about you.

Tool allowlist for the project

{
  "permissions": {
    "allow": [
      "Bash(npm install)",
      "Bash(npm run build)",
      "Bash(npm run test)",
      "Bash(npm run test:*)",
      "Bash(npm run lint)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Edit(src/**)",
      "Edit(tests/**)",
      "Edit(*.json)",
      "Edit(*.md)",
      "Read(**)"
    ],
    "deny": [
      "Bash(git push *)",
      "Bash(git reset --hard *)",
      "Bash(rm -rf *)",
      "Bash(curl * | bash *)"
    ]
  }
}

This is project-specific knowledge. If your project uses pytest instead of npm run test, your settings should reflect that. The allowlist encodes what Claude Code needs to do its job in this codebase.

Why deny rules matter

The deny list isn’t paranoia — it’s documentation. When a new engineer looks at .claude/settings.json, the deny list tells them: “these are the commands Claude shouldn’t run unsupervised in this project.” It sets expectations before anyone opens an interactive session.

git push in the deny list means Claude won’t accidentally push to main during an interactive session. rm -rf * means Claude won’t nuke test fixtures when running cleanup. These are guardrails against mistakes, not against Claude being malicious.

Defaults that apply project-wide

{
  "permissions": {
    "allow": ["..."],
    "deny": ["..."],
    "defaultMode": "acceptEdits"
  },
  "env": {
    "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8000"
  }
}

acceptEdits mode auto-approves file edits without prompting — useful for teams where every engineer has reviewed and approved the allowlist. If your team is still figuring out what Claude should touch, leave defaultMode out and let the allow/deny rules handle it explicitly.


Step 3: What Goes in settings.local.json (Personal)

settings.local.json is where individual preferences live. It never gets committed.

Common personal overrides

{
  "permissions": {
    "allow": [
      "Bash(~/scripts/*)",
      "Bash(python3 ~/tools/*)"
    ],
    "defaultMode": "bypassPermissions"
  },
  "env": {
    "ANTHROPIC_MODEL": "claude-opus-4-8"
  }
}

This example shows someone who:

  • Has personal scripts they want Claude to be able to run
  • Prefers bypass mode locally (they’ve reviewed the project’s deny list and trust it)
  • Wants to use Opus for high-stakes local work while the team defaults to Sonnet

None of these belong in the shared config. The local override lets them have exactly this setup without touching anyone else’s environment.

bypassPermissions locally: when it’s reasonable

Running bypassPermissions in your personal settings is reasonable when:

  • You’ve reviewed the shared settings.json deny list and agree with it
  • You understand what Claude Code might do in your project
  • You’re working locally with limited blast radius

It’s not reasonable to push bypassPermissions to the shared settings. That’s an individual choice, not a team choice.


Step 4: CLAUDE.md and settings.json — What’s the Difference?

Teams often confuse these two files. They’re different tools for different jobs.

FileWhat it isWhat it controls
CLAUDE.mdA text prompt that Claude readsWhat Claude knows about the project
settings.jsonStructured configWhat Claude is allowed to do

CLAUDE.md is like an onboarding doc: it tells Claude your coding conventions, your preferred testing approach, what “done” means in this codebase. Claude reads it as context before every session.

settings.json is enforcement: it controls which tools Claude can invoke, which commands run without prompting, what the fallback behavior is when a tool isn’t explicitly listed.

Both belong in version control. Neither replaces the other.

For a deeper look at CLAUDE.md structure and what to put in it, see CLAUDE.md Grew to 2,000 Lines and Nobody Read It — Here’s How to Fix It.


Step 5: The .claude/ Directory — Full Picture

Here’s what a clean .claude/ directory looks like for a team project:

.claude/
├── settings.json          # Committed — team config
├── settings.local.json    # Gitignored — personal overrides
├── CLAUDE.md              # Optional committed — team context
└── commands/              # Optional committed — custom slash commands
    ├── review.md
    └── deploy-check.md

And the corresponding .gitignore additions:

.claude/settings.local.json
.claude/.credentials.json
.claude/store/
.claude/*.log

The commands/ directory contains custom slash commands that your team has written — these are project-specific prompts that make sense to share. If your team has a /review command that checks for security issues specific to your stack, everyone should have it.


Common Mistakes Teams Make

Mistake 1: Committing settings.local.json

It happens more than you’d think. Someone adds their personal config, doesn’t realize it’s settings.local.json not settings.json, and commits it. Everyone else on the team now has bypass mode or personal script paths in their environment.

Fix: add .claude/settings.local.json to .gitignore before the first commit. If it’s already been committed, remove it with git rm --cached .claude/settings.local.json and add it to .gitignore.

Mistake 2: An empty or missing settings.json

If there’s no settings.json in the repository, every engineer on the team has to configure their local permissions from scratch. Worse, there’s no shared baseline — one person’s Claude might have git push pre-approved while another’s doesn’t.

Fix: create a minimal settings.json early. Even a file with just an allow list and a deny list for the obvious cases is better than nothing.

Mistake 3: Hardcoding paths in shared settings

{
  "permissions": {
    "allow": [
      "Bash(/Users/alice/project/scripts/build.sh)"
    ]
  }
}

This works on Alice’s machine and breaks on everyone else’s. Use relative paths or project-root-relative paths in shared settings.

{
  "permissions": {
    "allow": [
      "Bash(./scripts/build.sh)",
      "Bash(scripts/*)"
    ]
  }
}

Mistake 4: Forgetting that deny takes precedence

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

If someone tries to add "allow": ["Bash(git push origin main)"] to their settings.local.json thinking it’ll override the deny rule — it won’t. Deny rules in shared settings cannot be overridden by allow rules in local settings. That’s by design.


Onboarding Checklist for New Engineers

When a new engineer joins a project that uses Claude Code, they need exactly three things:

  1. Clone the repositorysettings.json and CLAUDE.md come with it
  2. Verify .gitignore — confirm .claude/settings.local.json is listed
  3. Create personal overrides — create .claude/settings.local.json with their preferences (optional; the shared config works without it)

That’s it. The shared config handles the rest. No per-engineer setup calls, no “just copy the settings from the wiki,” no drift between team members’ environments.


CI/CD: A Special Case

For CI environments, the shared settings.json is usually not enough. CI jobs often need permission modes that would be invasive for interactive local use.

The common pattern is to set bypassPermissions in the CI environment via a CI-specific settings file or environment variable, not in the shared settings.json:

# .github/workflows/claude-ci.yml
- name: Run Claude Code
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
    CLAUDE_PERMISSION_MODE: bypass  # CI-specific override
  run: claude -p "run the test suite and fix any failures"

Or use a separate settings file that only exists in CI:

- name: Create CI settings
  run: |
    cat > .claude/settings.local.json << 'EOF'
    {
      "permissions": {
        "defaultMode": "bypassPermissions"
      }
    }
    EOF

The point is: CI permissions are a CI concern. They don’t belong in the shared config that every engineer on the team uses locally.

For a complete guide to running Claude Code in CI environments, see Claude Code in GitHub Actions: How to Fix Permission Errors.


Summary

The two-file system is the design. Use it:

  • .claude/settings.json — project defaults, tool allowlist, deny rules. Committed.
  • .claude/settings.local.json — personal overrides, local paths, bypass mode. Gitignored.
  • CLAUDE.md — project context for Claude to read. Committed.
  • .claude/store/, .claude/.credentials.json — machine state, secrets. Gitignored.

Most team conflicts around Claude Code config come from treating settings.json as personal config, or from not having a settings.local.json setup at all. The fix is a two-line .gitignore addition and a shared baseline config — neither takes more than ten minutes.

Related Articles

Explore the collection

Browse all AI coding rules — CLAUDE.md, .cursorrules, AGENTS.md, and more.

Browse Rules