Skip to main content

Architecture

This setup is a chezmoi source directory. chezmoi renders templates and copies files into your $HOME.

Repository Layoutโ€‹

Documentation Hygieneโ€‹

This repo treats docs/ as part of the configuration:

  • If you change dotfiles behavior (anything under home/ that affects commands or workflows), update docs/ in the same change.
  • If a change truly has no user-facing impact, record that in the PR/commit context so the docs/code divergence is explicit.

Repo-Only Assets (Not Installed Into $HOME)โ€‹

Some directories in home/ are intentionally ignored by chezmoi and are used as "repo-local data" for scripts.

Ignore rules live in:

Examples in this setup:

  • home/app_icons/ is used by the ,apply-app-icons script, but it is not installed into $HOME.

Chezmoi Naming Conventions (How Source Maps To Installed Files)โ€‹

Chezmoi uses filename conventions to decide where things land.

Common patterns in this setup:

  • Paths under home/dot_config/ map to ~/.config/...
  • home/exact_lib/... -> ~/lib/... for deployed command internals
  • home/dot_* maps to ~/.<name> (for example: home/dot_zsh/ -> ~/.zsh/)
  • Paths under home/private_dot_ssh/ map to ~/.ssh/... (and are treated as private by chezmoi)
  • executable_foo -> installs as an executable file named foo
  • readonly_foo -> installs as foo with 0444 permissions (read-only); used for config files that should not be modified by external tools at runtime
  • exact_ prefix in a directory name means "exact directory" (chezmoi does not merge it with existing contents)

This is why you may see paths like:

The Data Flowโ€‹

This setup is intentionally declarative:

Chezmoi data flow: repo source, data/templates, apply, hash-gated hooks, derived home files, and runtime consumers

  1. You answer prompts in home/.chezmoi.toml.tmpl.
  2. Templates in home/ render differently depending on those values.
  3. Hooks in home/.chezmoiscripts/ install / update tools based on the rendered config.
  4. Re-running chezmoi apply converges you back to the intended state.

Dynamic AI Context Mergingโ€‹

Because AI tools (like OpenCode and Pi) often rewrite their config files during runtime, rendering templates directly into those files causes conflicts. Instead, this architecture uses Source-Based Merging:

  • MCP server definitions share a single canonical registry at home/.chezmoidata/mcp_servers.yaml.
  • During chezmoi apply, the unified script run_onchange_after_07-generate-mcp-configs.sh.tmpl calls scripts/generate_mcp_configs.py once and writes the result to Claude Code, Pi, and any other tool that consumes the standard mcpServers JSON shape.
  • Tools with different MCP schemas (OpenCode, Codex) still derive from the same registry via small inject scripts in scripts/ that transform the canonical registry into the tool-specific config shape.

Models are set directly in each harness config. For the full picture see MCP servers and Tool configs.

Shared Library (scripts/chezmoi_lib.sh)โ€‹

All run_onchange_after_07-merge-* scripts source a shared shell library at scripts/chezmoi_lib.sh for common operations:

FunctionPurpose
chezmoi_write_if_changedAtomic string write, skip if content unchanged
chezmoi_install_if_changedFile copy via install(1), skip if content unchanged
chezmoi_record_checksumRecord one literal target and sha256 in the manifest
chezmoi_record_artifactRecord one ownership-aware generated AI artifact

Two independent runtime ledgers serve different questions:

  • ~/.local/state/chezmoi/managed_configs.tsv is the generic literal whole-file checksum manifest. scripts/managed_config_manifest.py collapses exact duplicate rows and powers the ,doctor Config Drift section, which joins each row with the artifact ledger below: rows with no artifact and no remaining producer in source retire silently, while artifact-covered rows compare ownership-projected state so app-owned churn stays silent.
  • ~/.local/state/chezmoi/generated_artifacts.v1.json is the AI-specific effective-state ledger. scripts/generated_artifact_ledger.py records source/transform hashes, ownership projection, target, consumer, and local probe metadata. ,doctor ai and the Config Drift section read it, so runtime-owned Codex fields do not create false drift.

Both ledgers are atomic and mode 0600. The AI ledger stores paths and hashes only, never generated config contents or resolved secret values.

To add a new AI tool config, create its source file and a merge script that sources the library โ€” typically 5โ€“10 lines of tool-specific logic.

Hooks (Automation)โ€‹

The most important concept for understanding "what happens" is the hook naming:

  • run_once_before_* runs once before apply work.
  • run_once_after_* runs once after apply work.
  • run_onchange_after_* runs after apply when the tracked inputs change.

Examples in this repo:

HookPurpose
home/.chezmoiscripts/run_once_before_00-install-xcode.shXcode CLT
home/.chezmoiscripts/run_once_after_01-install-brew.shHomebrew install
home/.chezmoiscripts/run_once_after_02-install-fish.shFish install
home/.chezmoiscripts/run_onchange_after_03-install-brew-packages.fish.tmplBrew bundle
home/.chezmoiscripts/run_onchange_after_05-install-mise-runtimes.sh.tmplmise runtimes + shims
home/.chezmoiscripts/run_onchange_after_05-install-uv-versions.sh.tmplUV Python versions

Many hooks embed sha256sum comments that reference template content. The AI/config 07 hooks include every directly or transitively executed local transform, so changing a shared parser, reconciler, or shell helper schedules the affected projection again.

The Reference map lists every hook and helper script and the file each one drives.

External Assetsโ€‹

home/.chezmoiexternal.toml is used for things you want updated regularly but don't want to vendor into your dotfiles repo.

Current externals:

ExternalPurpose
tpmtmux plugin manager
EmmyLua.spoonHammerspoon Lua annotations
lowfi data filesBackground music tracklists
bat themesSyntax highlighting themes