Stop chezmoi clobbering app-written settings keys - #17
Merged
Conversation
~/.claude/settings.json is owned by two writers. chezmoi treated it as a plain managed file, so every apply deleted the keys Claude Code writes at runtime -- model and effortLevel -- which is why it sat at MM in chezmoi status and kept coming back after each apply. Convert it to a modify_ source. chezmoi pipes the current target in on stdin and takes stdout as the new target, so the managed block is merged over the existing file with jq '. * $managed': managed keys win, app-written keys survive. Falls back to the managed block when stdin is empty, not valid JSON, or jq is missing, so a fresh machine is fine. The merge is idempotent, which matters because chezmoi runs the script for status and diff too -- a non-converging script would report drift forever. Also fold the settings.local.json rule into dot_gitignore_global. It had been added to the target directly, so apply kept trying to delete it, and document the pattern plus the two files still unconverted: zed's settings.json and .gitconfig.
Zed rewrites ~/.config/zed/settings.json whenever a setting changes in the UI -- theme, font size, panel docks -- so it would hit the same clobber as ~/.claude/settings.json the first time the app wrote to it. Converting now rather than after it bites. settings.json is strict JSON, so the jq merge carries over unchanged. First apply reformats arrays to jq's style; verified the script is idempotent from there (pass 1 == pass 2), so this converges instead of reporting drift forever. Note the attribute order: modify_ precedes private_ in the source filename, and the target keeps its 0600 mode.
6 tasks
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.
~/.claude/settings.jsonhas two writers. chezmoi treated it as a plain managedfile, so it was authoritative and every
chezmoi applydeleted whatever ClaudeCode had written at runtime:
That is the recurring
MMinchezmoi status-- the file diverges in bothdirections, so apply clobbers the app and re-add clobbers the repo.
Fix
Convert the source to a
modify_script. chezmoi pipes the current targetto it on stdin and takes stdout as the new target, so it can merge instead of
overwrite:
jq's*merges objects recursively with the right-hand side winning, somanaged keys stay authoritative while anything the app added survives -- today
modelandeffortLevel, tomorrow whatever/configgains.Two properties worth calling out:
statusanddiff, not justapply. Iff(current) != currentonce converged, the file would reportdrift forever. Verified byte-identical output against the live file.
jqall fall back to emitting the managed block. Emitting nothing wouldtruncate
settings.json.The trade-off: a merge only ever adds, so deleting a key from the source no
longer deletes it from the target. Documented.
Also
**/.claude/settings.local.jsonhad been added straight to~/.gitignore_global,so apply kept trying to delete it. Folded into the chezmoi source, with a comment
explaining what that file is.
CLAUDE.mdgains the general pattern, the two constraints above, and the twofiles still unconverted that are known to rewrite themselves:
~/.config/zed/settings.json(Zed writes UI changes; strict JSON, so the same recipe applies) and
~/.gitconfig(
gh auth setup-gitrewrites its credential blocks with tabs and a trailingspace -- INI, so
jqdoes not apply).Manual testing
Six cases against the rendered script:
modelandeffortLevelpreservedchezmoi apply: runtime keys survived, managed keys correct,MMgonechezmoi statusclean afterwards; onlyRscript entries remainpre-commitpasses🤖 Generated with Claude Code