Skip to main content

Profile-based file merging

Some assistant tools rewrite their config files at runtime, so chezmoi does not treat the deployed target as the source of truth. The repo keeps explicit profile sources, and run_onchange scripts render the selected profile into the live target only when content differs.

This avoids complex templates and comment filters. Work/personal differences live in .work.* and .personal.* files, while mixed-ownership targets pass through typed reconcilers that know which runtime fields may survive.

Mental model

StepWhat happens
1The merge script checks the .isWork template variable.
2It picks the correct source file.
3Mixed-ownership targets pass through a typed ownership-aware reconciler.
4It writes the final destination only when content differs and updates both the generic checksum manifest and the AI effective-state ledger.
5Tool-specific formats stay decoupled.

All merge scripts live under home/.chezmoiscripts/ and source scripts/chezmoi_lib.sh.

Reference matrix

ToolSource filesTargetMerge script
Claude Code settingshome/dot_claude/settings.{work,personal}.json~/.claude/settings.jsonrun_onchange_after_07-merge-claude-code-settings.sh.tmpl
Gemini settings+MCPhome/dot_gemini/settings.json + mcp_servers.yaml~/.gemini/settings.jsonrun_onchange_after_07-merge-gemini-settings.sh.tmpl
OpenCode config+MCPhome/dot_config/opencode/readonly_opencode.{work,personal}.jsonc~/.config/opencode/opencode.jsoncrun_onchange_after_07-merge-opencode-config.sh.tmpl
Codex config+MCPhome/dot_codex/private_config.{work,personal}.toml~/.codex/config.tomlrun_onchange_after_07-merge-codex-config.sh.tmpl
Pi settings/modelshome/dot_pi/agent/readonly_settings.{work,personal}.json + readonly_models.json / readonly_models.personal.json~/.pi/agent/{settings,models}.jsonrun_onchange_after_07-merge-pi-config.sh.tmpl
Copilot settings+MCP+extensionhome/private_dot_copilot/settings.json + exact_extensions/exact_agent-memory/readonly_extension.mjs + mcp_servers.yaml~/.copilot/{settings.json,mcp-config.json,extensions/agent-memory/extension.mjs}run_onchange_after_07-merge-copilot-config.sh.tmpl

Using it

Pi targets are installed readonly.

Codex rebuilds from its profile base and reattaches only MCP approvals, hook trust, valid project trust, and valid TUI counters.

Copilot recursively preserves undeclared runtime settings, lets declared policy win, and replaces only subagents.agents exactly so stale agents and per-agent overrides cannot survive.

MCP-server injection for each tool is covered in MCP servers.

Internals (for maintainers)

Effective-state trace

Each successful 07-hook write records one schema-v1 artifact row under ~/.local/state/chezmoi/generated_artifacts.v1.json.

The row carries:

  • producer
  • selected profile
  • complete repo-local input/transform hashes
  • target
  • ownership adapter
  • expected owned semantic hash
  • consumer
  • local version probe

Copilot MCP rendering is apply-time only: run_onchange_after_07-merge-copilot-config.sh.tmpl owns the target and its copilot-mcp ledger row. Runtime ,copilot does not render config or change the ledger; it only replaces bare --resume with a locally selected --session-id=<id> to avoid Copilot 1.0.73's MCP startup race. Hosted authentication rotates inside the per-request stdio bridges.

,doctor ai

,doctor ai evaluates generated artifact rows without changing anything.

CheckRule
whole-file outputscompare exact bytes
Claude MCPcompare only mcpServers
Copilot settingsfollow the declared baseline shape and require subagents.agents exactness
Codexignore only its four explicit runtime-owned buckets
source/transform changesreport stale state until the matching hook runs again

Default output is static. ,doctor ai --live adds deduplicated local harness probes; it does not apply chezmoi, refresh credentials, or use the network.