Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Never assume you are on `main` or in the primary checkout directory.
[configuration]
# /path/to/script.tmpl:END:section:12345678
```
- **Default new `modify_` scripts to a pure `chezmoi:modify-template`** (`{{- /* chezmoi:modify-template */ -}}` as the first line, e.g. `dot_codex/modify_config.toml`, `dot_claude/modify_settings.json`) rather than embedding another language (Python, shell) inside a rendered template string. Requires the source filename to have **no `.tmpl` suffix** and **no executable bit** — both confirmed empirically to break chezmoi's dispatch (it silently falls through to the legacy executable-script code path instead, and `.chezmoi.stdin` never gets populated). Parse the incoming file with `fromJson`/`fromToml`/`fromYaml` on `.chezmoi.stdin` (guard with `{{- if .chezmoi.stdin -}}` for a not-yet-existing target), transform with `mergeOverwrite`/`setValueAtPath`/`omit`, serialize back with `toPrettyJson`/`toToml`/`toYaml`. `toPrettyJson`'s return value always carries a trailing `\n` that `-}}` trim markers can't remove (trim only strips surrounding template *text*, not a pipeline's runtime value) — pipe through `trimSuffix "\n"` if the target shouldn't end in a blank line.
**Known exception, don't relitigate:** `modify_dot_claude.json.tmpl` stays Python. Go's `fromJson`/`toPrettyJson` always alphabetize map keys (`encoding/json` marshal behavior, no workaround via these functions) — fine for a script that owns its whole output, but that script's entire point is touching one key in a ~2000-line file Claude Code itself constantly rewrites with its own key order and literal UTF-8; a Go-template rewrite would reformat the whole file every apply. Reconsider only if chezmoi ever exposes an order-preserving JSON codec.

## Chezmoi externals & scripts

Expand Down
18 changes: 16 additions & 2 deletions src/chezmoi/modify_dot_claude.json.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@
# (startup counts, seen-tips history, feature flags, etc.). We only inject our
# managed preferences and leave everything else untouched.
# Deployment config (permissions, env, sandbox) lives in ~/.claude/settings.json;
# see dot_claude/modify_settings.json.tmpl.
# see dot_claude/modify_settings.json.
#
# Deliberately NOT a chezmoi:modify-template (see src/chezmoi/AGENTS.md for
# the default convention). fromJson/toPrettyJson always alphabetize keys
# (Go's encoding/json map behavior, no way around it with those functions) --
# fine for the two settings.json scripts, which already chose to fully own
# their output, but this file's whole point is to touch nothing but one key
# in a ~2000-line file Claude Code itself constantly rewrites with its own
# key order and literal UTF-8. A Go-template rewrite would reformat that
# entire file on every apply, exactly the diff-noise bug this script fixes.
import json
import sys

Expand All @@ -27,4 +36,9 @@ def deep_merge(d1, d2):

merged = deep_merge(existing.copy(), preferences)

print(json.dumps(merged, indent=2, sort_keys=True), end="")
# No sort_keys, no default ensure_ascii: Claude Code writes this file with
# its own key order and literal UTF-8. Matching that means an apply that
# only touches editorMode produces a single-line diff instead of
# reformatting the whole ~2000-line file (key resort + unicode escaping)
# every time, which made real changes impossible to spot in the noise.
print(json.dumps(merged, indent=2, ensure_ascii=False), end="")
Loading