Repository navigation
Stop Claude Code's settings rewrites from dirtying the tree - #6
Merged
Merged
Conversation
~/.claude/settings.json is stowed from this repo, but Claude Code owns it: it rewrites the whole file whenever a runtime option changes, so every /model switch left the working tree dirty with a key reorder and a one-line model change. There's no env var or setting to redirect where it persists — only the --settings flag, which is per-invocation and wouldn't cover GUI or IDE launches. Handle it in two halves, because neither is sufficient alone: .gitattributes routes the file through claude-settings-clean.py, which drops the keys Claude Code writes back as machine state (model, theme, sandbox, modelSettings) and sorts the rest so a rewrite in a different key order isn't a diff either. That keeps commits clean but not `git status` — git's index refresh hashes the worktree file raw and only the diff machinery applies the filter, so the file reads as modified even when it cleans to the committed blob. Verified against git 2.55.0; filter.<name>.required doesn't change it. skip-worktree is what actually silences the churn. It costs a toggle to commit a deliberate change to the file, which CLAUDE.md documents. Both are per-clone git state that .gitattributes can't carry, so bootstrap.sh sets them and doctor.sh reports a clone that's missing either — the same pattern as the Brewfiles. Also ignore **/.claude/.cc-writes/, which Claude Code creates in the repo. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AEPYeZgMsmFPVBbRvkFtM2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
~/.claude/settings.jsonis stowed from this repo, but Claude Code owns that file — it rewrites the whole thing whenever a runtime option changes. Every/modelswitch left the tree dirty with a key reorder plus a one-line model change.There's no env var or setting to redirect where Claude Code persists this; the only non-written settings source is the
--settingsflag, which is per-invocation and wouldn't cover desktop-app or IDE launches (so thepermissions.denyrules would silently not apply there).Fix, in two halves
Neither half is sufficient alone.
1. Clean filter — keeps commits clean.
.gitattributesroutes the file throughclaude-settings-clean.py, which drops the keys Claude Code writes back as machine state (model,theme,sandbox,modelSettings) and sorts the rest, so a rewrite in a different key order isn't a diff either. Invalid JSON round-trips untouched rather than wedging git.2.
skip-worktree— keepsgit statusclean. A clean filter alone does not quietgit status: git's index-refresh path hashes the worktree file raw, and only the diff machinery applies the filter. So the file reads as modified even when it cleans to exactly the committed blob:Reproduced in an isolated repo on git 2.55.0;
filter.<name>.requireddoes not change it.Wiring
Both are per-clone git state that
.gitattributescan't carry, sobootstrap.shsets them anddoctor.shreports a clone missing either — the same install/detect split as the Brewfiles.Cost
Committing a deliberate change to that settings file needs a toggle (
--no-skip-worktree, commit,--skip-worktree). Documented in CLAUDE.md, since while it's hidden git ignores edits you meant to keep.Verification
Simulated a
/modelswitch (model + theme changed, top-level keys reordered) against a clean tree:git statusstayed empty, and the live file kept every setting.shellcheck bootstrap.sh doctor.sh lib.sh macos/*.shclean../doctor.shgreen.Also ignores
**/.claude/.cc-writes/, which Claude Code creates inside the repo.🤖 Generated with Claude Code
https://claude.ai/code/session_01AEPYeZgMsmFPVBbRvkFtM2