Skip to content

Instantly share code, notes, and snippets.

@amiller68
Last active May 26, 2026 11:47
Show Gist options
  • Select an option

  • Save amiller68/5d19561e02a6077bd87cd0b881691795 to your computer and use it in GitHub Desktop.

Select an option

Save amiller68/5d19561e02a6077bd87cd0b881691795 to your computer and use it in GitHub Desktop.
Claude Code Configuration

Generic Guide: Configuring Claude Code Settings

Based on my research, here's comprehensive advice for configuring Claude Code for any project:

Configuration File Strategy

Choose the Right Location

  1. Project-Level (.claude/settings.json) - RECOMMENDED for teams
  • Shared with entire team via git
  • Enforces consistent behavior across all developers
  • Best for: CI/CD workflows, team standards, security policies
  • Commit this file to version control
  1. User-Level (~/.claude/settings.json) - For personal defaults
  • Applies globally to all your projects
  • Good for: Personal preferences, frequently-used tools
  • Never share credentials here
  • Use sparingly to avoid conflicts with project settings
  1. Local-Level (.claude/settings.local.json) - For personal overrides
  • Git-ignored, personal only
  • Overrides project settings for your machine
  • Best for: Personal API keys, machine-specific paths, experimental settings
  • Add to .gitignore

Precedence Order

Enterprise Policy > User Settings > Project Settings > Local Settings (deny always wins)

Permission Configuration Pattern

The Three-Tier Approach

Use a traffic light mental model:

{ "permissions": { "allow": [], // 🟒 Green zone - safe, autonomous "ask": [], // 🟑 Yellow zone - needs confirmation "deny": [] // πŸ”΄ Red zone - blocked completely } }

  1. Green Zone (Allow) - Safe Operations

Philosophy: Read-only operations, reversible changes, standard dev workflow

Always safe to allow: "allow": [ // Git read operations "Bash(git status)", "Bash(git diff*)", "Bash(git log*)", "Bash(git show*)", "Bash(git branch*)",

// Build & test (non-destructive)
"Bash(npm run test*)",
"Bash(npm run build*)",
"Bash(npm run lint*)",
"Bash(make test)",
"Bash(make build)",

// File inspection
"Bash(cat *)",
"Bash(ls *)",
"Bash(pwd)",
"Bash(head *)",
"Bash(tail *)",
"Bash(find *)",
"Bash(grep *)",
"Bash(which *)",

// File operations (project files only)
"Read(./src/**)",
"Read(./tests/**)",
"Read(./docs/**)",
"Write(./src/**)",
"Write(./tests/**)",
"Edit",

// Web operations
"WebSearch"

]

  1. Red Zone (Deny) - Dangerous Operations

Philosophy: Destructive, irreversible, or security-sensitive

Always deny: "deny": [ // Destructive commands "Bash(rm -rf *)", "Bash(rm -r *)", "Bash(sudo *)", "Bash(chmod 777 *)",

// Credential files
"Read(./.env*)",
"Read(./**/.env*)",
"Read(./secrets/**)",
"Read(**/.aws/**)",
"Read(**/.ssh/**)",
"Read(*.pem)",
"Read(*.key)",
"Read(*.crt)",
"Read(id_rsa*)",

// Lock files (prevent corruption)
"Write(package-lock.json)",
"Write(pnpm-lock.yaml)",
"Write(yarn.lock)",
"Write(Cargo.lock)",
"Write(Gemfile.lock)",
"Write(composer.lock)",
"Write(poetry.lock)",
"Write(uv.lock)",

// Publishing/deployment
"Bash(npm publish*)",
"Bash(docker push*)",
"Bash(kubectl delete*)",

// Network operations (unless needed)
"Bash(curl *)",
"Bash(wget *)"

]

  1. Yellow Zone (Ask) - Requires Judgment

Philosophy: Impactful but sometimes necessary

Typically ask for: "ask": [ // Package management "Bash(npm install*)", "Bash(npm uninstall*)", "Bash(pnpm add*)", "Bash(pip install*)",

// Git write operations
"Bash(git push*)",
"Bash(git merge*)",
"Bash(git rebase*)",
"Bash(git reset --hard*)",

// PR/deployment operations
"Bash(gh pr create*)",
"Bash(gh pr merge*)",
"Bash(make deploy*)",

// External data
"WebFetch"

]

Bash Permission Syntax

Critical Rules

⚠️ Bash rules use PREFIX MATCHING, NOT REGEX

// βœ… Correct patterns "Bash(git status)" // Exact match only "Bash(git diff*)" // Matches: git diff, git diff HEAD, git diff --cached "Bash(npm run *)" // Matches: npm run test, npm run build, etc. "Bash(make *)" // Matches all make commands

// ❌ Common mistakes "Bash(git.*)" // Doesn't work - not regex "Bash(git (status|diff))" // Doesn't work - no alternation "Bash(test)" // Too broad - matches "delete test"

Pattern Examples

// Specific command "Bash(git status)"

// Command family with space "Bash(git diff *)" // git diff anything

// Command prefix (no space) "Bash(git diff*)" // git diff, git difftool, git diff-tree

// Tool with all subcommands "Bash(docker compose *)"

// Glob-style (works for simple cases) "Bash(npm run test*)" // test, test:unit, test:integration

Workflow-Specific Configurations

Configuration 1: Autonomous Development

Use case: Full-featured development with minimal interruption

{ "permissions": { "allow": [ "Bash(git status)", "Bash(git diff*)", "Bash(git log*)", "Bash(git add*)", "Bash(git commit*)", "Bash(npm run *)", "Bash(make *)", "Bash(cat *)", "Bash(ls *)", "Bash(grep )", "Read(./src/)", "Read(./tests/)", "Write(./src/)", "Write(./tests/)", "Edit", "WebSearch" ], "deny": [ "Bash(rm -rf )", "Bash(sudo )", "Read(./.env)", "Write(lock)" ], "ask": [ "Bash(git push)", "Bash(npm install)" ], "defaultMode": "acceptEdits" } }

Configuration 2: Safe Exploration/Research

Use case: Understanding codebases, read-only analysis

{ "permissions": { "allow": [ "Bash(git status)", "Bash(git diff*)", "Bash(git log*)", "Bash(cat *)", "Bash(ls )", "Bash(grep )", "Bash(find )", "Read", "WebSearch" ], "deny": [ "Write", "Edit", "Bash(git commit)", "Bash(git push)", "Read(./.env)" ], "defaultMode": "plan" } }

Configuration 3: CI/CD Pipeline

Use case: Automated testing and deployment

{ "permissions": { "allow": [ "Bash(npm ci)", "Bash(npm run test*)", "Bash(npm run build)", "Bash(npm run lint)", "Bash(git status)", "Bash(git diff*)", "Read", "Write(./dist/)", "Write(./coverage/)" ], "deny": [ "Bash(npm install)", // Use npm ci instead "Bash(git push*)", "Write(./.env*)", "Write(lock)" ], "defaultMode": "acceptEdits" } }

Configuration 4: PR Review Bot

Use case: Automated code review

{ "permissions": { "allow": [ "Bash(gh pr view*)", "Bash(gh pr diff*)", "Bash(git diff*)", "Bash(npm run test*)", "Bash(npm run lint)", "Read", "WebSearch" ], "deny": [ "Write", "Edit", "Bash(git push*)", "Bash(gh pr merge*)" ], "ask": [ "Bash(gh pr comment*)" ] } }

Tool-Specific Patterns

Node.js Projects

"allow": [ "Bash(npm run *)", "Bash(pnpm run )", "Bash(yarn run )", "Bash(npx )" ], "deny": [ "Write(package-lock.json)", "Write(pnpm-lock.yaml)", "Write(yarn.lock)" ], "ask": [ "Bash(npm install)", "Bash(pnpm add)", "Bash(yarn add)" ]

Python Projects

"allow": [ "Bash(python -m pytest*)", "Bash(python -m unittest*)", "Bash(python -m mypy*)", "Bash(python -m black*)", "Bash(uv run )", "Bash(poetry run )" ], "deny": [ "Write(poetry.lock)", "Write(Pipfile.lock)", "Write(uv.lock)" ], "ask": [ "Bash(pip install)", "Bash(poetry add)", "Bash(uv add*)" ]

Docker Projects

"allow": [ "Bash(docker ps*)", "Bash(docker logs*)", "Bash(docker inspect*)", "Bash(docker compose up*)", "Bash(docker compose down*)" ], "deny": [ "Bash(docker rm -f*)", "Bash(docker system prune*)", "Bash(docker push*)" ], "ask": [ "Bash(docker build*)" ]

Git Operations

"allow": [ // Safe read operations "Bash(git status)", "Bash(git diff*)", "Bash(git log*)", "Bash(git show*)", "Bash(git branch*)",

// Local write operations
"Bash(git add*)",
"Bash(git commit*)",
"Bash(git checkout*)",
"Bash(git stash*)",
"Bash(git pull*)"

], "ask": [ // Remote operations "Bash(git push*)",

// History rewriting
"Bash(git merge*)",
"Bash(git rebase*)",
"Bash(git reset --hard*)",
"Bash(git cherry-pick*)"

]

File Operation Patterns

Granular File Access

{ "permissions": { "allow": [ // Source code "Read(./src/)", "Write(./src/)",

    // Tests
    "Read(./tests/**)",
    "Write(./tests/**)",

    // Documentation
    "Read(./docs/**)",
    "Write(./docs/**)",
    "Write(*.md)",

    // Config (read-only)
    "Read(*.json)",
    "Read(*.yaml)",
    "Read(*.toml)"
  ],
  "deny": [
    // Credentials
    "Read(./.env*)",
    "Read(./config/secrets*)",

    // Build artifacts
    "Write(./dist/**)",
    "Write(./build/**)",

    // Dependencies
    "Write(./node_modules/**)",
    "Write(./.venv/**)"
  ]
}

}

Broad Access (Less Secure)

{ "permissions": { "allow": [ "Read", // Read EVERYTHING "Write", // Write ANYWHERE "Edit" // Edit ANYTHING ], "deny": [ // Still protect credentials "Read(./.env*)", "Read(/.aws/)", "Write(lock)" ] } }

Security Best Practices

  1. Defense in Depth

Don't rely solely on Claude Code permissions:

  • Run Claude Code in containers or VMs
  • Use OS-level permissions (read-only mounts)
  • Employ git hooks for validation
  • Use dedicated service accounts with limited privileges

Example: Run in Docker with limited access

docker run -v $(pwd)/src:/workspace/src:ro
-v $(pwd)/.claude:/workspace/.claude:ro
claude-code

  1. Principle of Least Privilege

Start restrictive, expand as needed:

// Week 1: Minimal "allow": ["Bash(git status)", "Read(./src/**)", "WebSearch"]

// Week 2: Add testing "allow": ["Bash(git status)", "Bash(npm test)", "Read(./src/**)", "WebSearch"]

// Week 3: Add commits "allow": ["Bash(git status)", "Bash(git commit*)", "Bash(npm test)", "Read(./src/)", "Write(./src/)", "WebSearch"]

  1. Credential Protection Strategy

Multi-layer approach:

{ "permissions": { "deny": [ // File patterns "Read(./.env*)", "Read(.//.env*)", "Read(./secrets/)", "Read(./config/credentials*)",

    // File extensions
    "Read(*.pem)",
    "Read(*.key)",
    "Read(*.crt)",
    "Read(*.p12)",
    "Read(*.pfx)",

    // Directory patterns
    "Read(**/.aws/**)",
    "Read(**/.ssh/**)",
    "Read(**/.gnupg/**)",
    "Read(*/secrets/**)",

    // Specific files
    "Read(./.npmrc)",
    "Read(./.pypirc)",
    "Read(./credentials.json)"
  ]
}

}

Plus use .gitignore: .env* secrets/ *.pem .key credentials

Plus environment variables (never in files): export API_KEY="secret" # Good echo "API_KEY=secret" > .env # Bad

  1. Lock File Protection

Why it matters: Lock files ensure reproducible builds

"deny": [ // Node.js "Write(package-lock.json)", "Write(pnpm-lock.yaml)", "Write(yarn.lock)",

// Python
"Write(poetry.lock)",
"Write(Pipfile.lock)",
"Write(uv.lock)",

// Rust
"Write(Cargo.lock)",

// Ruby
"Write(Gemfile.lock)",

// PHP
"Write(composer.lock)",

// Go
"Write(go.sum)"

]

Advanced Configuration Options

Default Modes

{ "permissions": { "defaultMode": "acceptEdits" // or "plan" or "default" } }

Modes:

  • "default" - Prompts first time for each tool
  • "acceptEdits" - Auto-approves file modifications
  • "plan" - Read-only, planning mode only
  • "bypassPermissions" - DANGEROUS - skips all checks

Environment Variables

{ "env": { "NODE_ENV": "development", "DEBUG": "true", "CI": "false" } }

Hooks (Advanced)

{ "hooks": { "pre-commit": "npm run lint && npm test", "post-edit": "prettier --write ${FILE}", "pre-bash": "echo 'Running: ${COMMAND}'" } }

Testing Your Configuration

  1. Validation Checklist

Test allowed commands (should not prompt)

claude-code "Show me git status" claude-code "Run the tests" claude-code "Read src/main.js"

Test ask commands (should prompt)

claude-code "Install a new package" claude-code "Push to remote"

Test denied commands (should be blocked)

claude-code "Delete everything with rm -rf" claude-code "Show me the .env file"

  1. JSON Validation

Validate JSON syntax

jq . .claude/settings.json

Or use jsonschema

jsonschema -i .claude/settings.json
https://json.schemastore.org/claude-code-settings.json

  1. Permission Audit

Create a test script:

#!/bin/bash

test-permissions.sh

echo "Testing allowed commands..." claude-code "Run git status" || echo "FAIL: git status" claude-code "Run make test" || echo "FAIL: make test"

echo "Testing denied commands..." claude-code "Read .env" && echo "FAIL: .env readable" || echo "PASS: .env denied"

echo "Testing file operations..." claude-code "Read src/main.js" || echo "FAIL: can't read source" claude-code "Write to src/test.txt" || echo "FAIL: can't write source"

Common Pitfalls & Solutions

Pitfall 1: Too Permissive Initially

❌ Bad: "allow": ["Bash(*)", "Read", "Write"]

βœ… Good: "allow": [ "Bash(git status)", "Bash(npm run test)", "Read(./src/)", "Write(./src/)" ]

Pitfall 2: Forgetting Lock Files

❌ Bad: "allow": ["Write"] // Will corrupt package-lock.json

βœ… Good: "allow": ["Write(./src/)", "Write(./tests/)", "Write(*.md)"], "deny": ["Write(lock)"]

Pitfall 3: Credentials in Project Settings

❌ Bad: // .claude/settings.json (committed to git) { "env": { "API_KEY": "sk-secret123" } }

βœ… Good: // .claude/settings.local.json (git-ignored) { "env": { "API_KEY": "sk-secret123" } }

Pitfall 4: Overly Broad Patterns

❌ Bad: "allow": ["Bash(rm)"] // Matches "rm -rf" too!

βœ… Good: "allow": [ "Bash(npm run *)", "Bash(yarn run *)" ], "deny": ["Bash(rm *)"]

Pitfall 5: Not Testing Patterns

❌ Assumption: "Bash(git diff*)" // Does this match "git diff HEAD"?

βœ… Test it:

Create test command

claude-code "Run git diff HEAD"

Verify: did it prompt or not?

Migration Strategy

From No Config β†’ Full Config

Phase 1: Observation (Week 1) { "permissions": { "ask": ["*"] // Everything requires approval } } Monitor what commands Claude Code needs.

Phase 2: Safe Operations (Week 2) { "permissions": { "allow": [ "Bash(git status)", "Bash(git diff*)", "Read(./src/**)" ], "ask": ["*"] } }

Phase 3: Development Workflow (Week 3) { "permissions": { "allow": [ "Bash(git status)", "Bash(git diff*)", "Bash(git add*)", "Bash(git commit*)", "Bash(npm run test)", "Read(./src/)", "Write(./src/)" ], "deny": [ "Bash(rm -rf )", "Read(./.env)" ] } }

Phase 4: Full Automation (Week 4+) { "permissions": { "allow": [/* comprehensive list /], "deny": [/ security boundaries /], "ask": [/ impactful operations */], "defaultMode": "acceptEdits" } }

Project Type Templates

Minimal Web App

{ "permissions": { "allow": [ "Bash(npm run )", "Bash(git status)", "Bash(git diff)", "Bash(git add*)", "Bash(git commit*)", "Read(./src/)", "Write(./src/)", "WebSearch" ], "deny": [ "Read(./.env*)", "Write(package-lock.json)" ], "ask": ["Bash(git push*)"] } }

Monorepo (Multi-language)

{ "permissions": { "allow": [ "Bash(pnpm run )", "Bash(make )", "Bash(uv run )", "Bash(turbo )", "Bash(git status)", "Bash(git diff)", "Bash(git add)", "Bash(git commit)", "Read(./apps/)", "Read(./packages/)", "Write(./apps/)", "Write(./packages/)", "WebSearch" ], "deny": [ "Read(./.env)", "Write(lock)", "Bash(rm -rf )" ], "ask": [ "Bash(pnpm install)", "Bash(git push*)" ], "defaultMode": "acceptEdits" } }

Data Science / Jupyter

{ "permissions": { "allow": [ "Bash(jupyter *)", "Bash(python -m )", "Bash(pip list)", "Bash(conda list)", "Read(./notebooks/)", "Read(./data/)", "Write(./notebooks/)", "Write(./output/)", "WebSearch" ], "deny": [ "Write(./data/**)", // Prevent data corruption "Bash(rm -rf )" ], "ask": [ "Bash(pip install)", "Bash(conda install)" ] } }

Troubleshooting

Issue: Still Getting Prompts

Check:

  1. Exact pattern match: "Bash(npm run test)" vs "Bash(npm run test*)"
  2. Spacing: "Bash(git diff )" vs "Bash(git diff)"
  3. Tool name capitalization: "Bash" not "bash"
  4. Conflicting user settings
  5. Enterprise policies overriding

Debug:

Check active config

claude-code --show-config

Test specific command

claude-code --test-permission "Bash(npm run test)"

Issue: Permissions Too Broad

Symptom: Claude Code can do things it shouldn't

Fix: Add explicit denies "allow": ["Bash(npm )"], // Too broad "deny": ["Bash(npm publish)"] // Block specific case

Issue: Can't Access Needed Files

Symptom: "Permission denied" errors

Fix: Check file patterns // Wrong "Read(./src/*)" // Only top-level files

// Right "Read(./src/**)" // Recursive

Final Recommendations

  1. Start Simple

{ "permissions": { "allow": [ "Bash(git status)", "Read(./src/**)", "WebSearch" ] } }

  1. Add Security Denies

{ "permissions": { "allow": ["..."], "deny": [ "Bash(rm -rf )", "Read(./.env)", "Write(lock)" ] } }

  1. Enable Workflow

{ "permissions": { "allow": ["...", "Bash(npm run *)", "Write(./src/**)"], "deny": ["..."] } }

  1. Add Confirmations

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

  1. Set Default Mode

{ "permissions": { "allow": ["..."], "deny": ["..."], "ask": ["..."], "defaultMode": "acceptEdits" } }

  1. Document & Share

{ "$schema": "https://json.schemastore.org/claude-code-settings.json", "_comment": "Team config for autonomous development", "permissions": { // ... your config } }

Key Takeaways

  1. Permissions use prefix matching, not regex
  2. Start restrictive, expand gradually
  3. Always deny credentials and destructive operations
  4. Use "ask" for impactful but necessary operations
  5. Commit project settings, ignore local settings
  6. Test your configuration before deploying
  7. Don't rely on permissions alone for security
  8. Document why specific permissions are needed

This configuration approach enables autonomous Claude Code operation while maintaining security boundaries appropriate for your project's risk tolerance.

@ianymu

ianymu commented May 26, 2026

Copy link
Copy Markdown

I liked your notes because they focus on what Claude is allowed to do, not just how to prompt it.

One thing I am trying to sanity-check: after permissions are set, do you still want a receipt that proves what checks actually ran? Like changed files, risky areas, commands, and any final "tests passed" claim that has no command behind it.

Would that help in a team repo, or is it overkill?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment