Summary
Make all Claude global configuration live in and stay synced with chezmoi,
with a low-friction, greenlit capture of the changes Claude Code (and
plugins/RTK) write to their own settings — rather than one PR per change.
Design agreed in a planning session; implementation deferred until the
in-flight Claude↔chezmoi work (generated settings.json / permissions) lands,
to avoid collisions. Related: #37, #36, #32.
Decisions
- Greenlight, batched per branch. Each sync = one descriptively-named
branch → PR → merge to main, so all machines converge via chezmoi update.
(Per-change PRs are overkill; a session's batch per branch is the unit.)
SessionStart drift-detection hook. Scoped chezmoi diff over the
tracked ~/.claude set; if drift exists, surface a reminder so Claude
proactively offers to sync at session start.
/chezmoi-sync command — broad and bidirectional. Not Claude-specific:
- live newer than source → offer
chezmoi add (capture up)
- source newer than live → offer
chezmoi apply (push down)
- per-element: list each drifted path, show its diff, choose
capture / apply / skip
- must degrade gracefully around template-render failures —
chezmoi status currently aborts on the Bitwarden-templated ~/.kube/homelab.yaml;
the command should fall back to per-path diffing rather than fail the scan.
Track-allowlist (everything else under ~/.claude ignored)
settings.json (see caveat below), CLAUDE.md, RTK.md, agents/,
commands/, skills/, hooks/*.py.
- Never tracked:
~/.claude.json (≈84 KB, ~200 secret-ish keys — already
in .chezmoiignore, keep it there), settings.local.json (machine-local),
*cache*.json, daemon.status.json, mcp-needs-auth-cache.json,
hooks/__pycache__/ (a .pyc is currently tracked — drop it).
Caveat — settings.json is a GENERATED target
Per the new permissions convention, settings.json's Bash allowlist is
generated from .chezmoidata/permissions.toml + a template. So the sync must
not chezmoi add settings.json (that would clobber the template with
rendered output). Reconcile its drift instead by:
This is the main point to align with the in-flight work before implementing.
CLAUDE.md templating — pattern, not a task
Do not template CLAUDE.md wholesale (it's the most-loaded instruction
file; a template error has a huge blast radius). When a real per-machine
difference appears (YAGNI until then), isolate it into an @-imported fragment:
- templated fragment —
dot_claude/CLAUDE.machine.md.tmpl on
{{ .chezmoi.hostname }} / OS, imported via @CLAUDE.machine.md; or
- host-gated fragments — plain per-host files applied via a templated
.chezmoiignore (the existing per-host pattern).
chezmoi skill updates needed
- Add a "Claude config sync" section (allowlist + the greenlight/
/chezmoi-sync
workflow).
- Fix the stale guidance: commit bodies use
Assisted-by: AI, never
Co-Authored-By.
Safety
Public repo: secrets never tracked; betterleaks pre-commit + CI is the backstop.
Summary
Make all Claude global configuration live in and stay synced with chezmoi,
with a low-friction, greenlit capture of the changes Claude Code (and
plugins/RTK) write to their own settings — rather than one PR per change.
Design agreed in a planning session; implementation deferred until the
in-flight Claude↔chezmoi work (generated
settings.json/ permissions) lands,to avoid collisions. Related: #37, #36, #32.
Decisions
branch → PR → merge to
main, so all machines converge viachezmoi update.(Per-change PRs are overkill; a session's batch per branch is the unit.)
SessionStartdrift-detection hook. Scopedchezmoi diffover thetracked
~/.claudeset; if drift exists, surface a reminder so Claudeproactively offers to sync at session start.
/chezmoi-synccommand — broad and bidirectional. Not Claude-specific:chezmoi add(capture up)chezmoi apply(push down)capture / apply / skip
chezmoi statuscurrently aborts on the Bitwarden-templated~/.kube/homelab.yaml;the command should fall back to per-path diffing rather than fail the scan.
Track-allowlist (everything else under
~/.claudeignored)settings.json(see caveat below),CLAUDE.md,RTK.md,agents/,commands/,skills/,hooks/*.py.~/.claude.json(≈84 KB, ~200 secret-ish keys — alreadyin
.chezmoiignore, keep it there),settings.local.json(machine-local),*cache*.json,daemon.status.json,mcp-needs-auth-cache.json,hooks/__pycache__/(a.pycis currently tracked — drop it).Caveat —
settings.jsonis a GENERATED targetPer the new permissions convention,
settings.json's Bash allowlist isgenerated from
.chezmoidata/permissions.toml+ a template. So the sync mustnot
chezmoi addsettings.json(that would clobber the template withrendered output). Reconcile its drift instead by:
.chezmoidata/permissions.toml(the existingworkflow; Allowlist sync: expand one entry across equivalent command prefixes (bare ↔ rtk ↔ chezmoi) #37/Extend read-only allowlist coverage to the non-rtk-wrapped toolchain #36/DRY up git permission rules: chezmoi-template one verb list across git / git -C / rtk git / chezmoi git #32);
enabledPlugins/ other settings → need a defined path into the template.This is the main point to align with the in-flight work before implementing.
CLAUDE.md templating — pattern, not a task
Do not template
CLAUDE.mdwholesale (it's the most-loaded instructionfile; a template error has a huge blast radius). When a real per-machine
difference appears (YAGNI until then), isolate it into an
@-imported fragment:dot_claude/CLAUDE.machine.md.tmplon{{ .chezmoi.hostname }}/ OS, imported via@CLAUDE.machine.md; or.chezmoiignore(the existing per-host pattern).chezmoi skill updates needed
/chezmoi-syncworkflow).
Assisted-by: AI, neverCo-Authored-By.Safety
Public repo: secrets never tracked; betterleaks pre-commit + CI is the backstop.