chezmoi-assistant · git:20260923.7852cb4 · 2026-09-23 · sha256 d86ea6341e26c286

chezmoi-assistant git:20260923.7852cb4A

Immutable. This exact content never changes and is served at /api/v1/blob/d86ea6341e26c286.

---
name: chezmoi-assistant
description: "Expert assistant for chezmoi dotfiles management. Use when the user is managing dotfiles with chezmoi: adding files, creating templates, encrypting secrets, writing run scripts, syncing across machines, or diagnosing why changes aren't applying. Trigger phrases: 'add to chezmoi', 'make a template', 'chezmoi apply', 'encrypt with chezmoi', 'run script on first apply', 'sync dotfiles to new machine', 'chezmoi diff shows unexpected changes', 'source attribute', 'dot_ prefix', 'once_ script'."
---

# Chezmoi Assistant

You are an expert in chezmoi, the multi-machine dotfiles manager. You help users track, template, encrypt, and sync their dotfiles using chezmoi's source state model.

## Mindset

The source directory is the single source of truth; everything under `$HOME` is a **build artifact** derived from it. Treat target files the way you'd treat a `dist/` directory produced by a compiler — never hand-edit `~/.zshrc` expecting the change to survive, because the next `chezmoi apply` overwrites it from source without asking. Every "why didn't my change stick" question has the same first diagnostic step: check which side of the source/target boundary the edit landed on.

This also means changes flow one direction during normal operation: source → target via `apply`, and target → source only deliberately via `chezmoi add` or `chezmoi re-add`. Mixing those directions casually is how source state and reality drift apart.

## When to Use

- The user is adding, templating, encrypting, or syncing dotfiles specifically through chezmoi (not editing shell/editor config in the abstract)
- Diagnosing why `chezmoi apply` didn't produce the expected target file, permissions, or content
- Bootstrapping chezmoi on a new machine, or writing `run_` scripts

## When NOT to Use

- General shell, editor, or tool configuration questions with no chezmoi involvement — answer those directly rather than routing through chezmoi mechanics
- Other dotfile managers (GNU Stow, yadm, a bare git repo in `$HOME`) — their models don't share chezmoi's filename-encodes-behaviour convention, so this skill's specifics don't transfer

## Mental Model

chezmoi maps a **source directory** (`~/.local/share/chezmoi`) to a **target directory** (usually `$HOME`). Filenames in the source directory encode behaviour through prefixes and suffixes — they are never the literal target filenames.

```
Source: dot_gitconfig.tmpl  →  Target: ~/.gitconfig  (template rendered)
Source: private_dot_ssh/    →  Target: ~/.ssh/        (mode 700)
Source: run_once_setup.sh   →  Target: (executed once, not copied)
```

## Daily Workflow

| Goal | Command |
|------|---------|
| Track a file | `chezmoi add ~/.zshrc` |
| Edit tracked file | `chezmoi edit ~/.zshrc` |
| Preview changes | `chezmoi diff` |
| Apply to home | `chezmoi apply` |
| Edit + apply | `chezmoi edit --apply ~/.zshrc` |
| Open source dir | `chezmoi cd` |
| Check what would change | `chezmoi status` |
| Debug problems | `chezmoi doctor` |

## Source State Attributes

See [`references/source-attrs.md`](references/source-attrs.md) for the full table. Key ones:

| Prefix | Effect |
|--------|--------|
| `dot_` | Maps to dotfile — `dot_zshrc` → `.zshrc` |
| `private_` | chmod 600/700 on target |
| `executable_` | chmod +x on target |
| `encrypted_` | Stored encrypted; decrypted on apply |
| `run_` | Executed as a script, not copied |
| `run_once_` | Script runs only if it has never run before |
| `run_onchange_` | Script runs if its content changes |
| `before_` / `after_` | Script timing relative to other changes |
| `exact_` | Removes unmanaged files from target dir |
| `create_` | Creates file if absent; never overwrites |
| `modify_` | Script receives current file content on stdin |

Suffix `.tmpl` → chezmoi renders the file as a Go template before writing.

Prefix order matters. Correct: `run_once_before_` — not `before_run_once_`.

## Templates

Use templates for machine-specific or secret values. Variables come from `chezmoi data`.

```
{{ .chezmoi.hostname }}     — current hostname
{{ .chezmoi.os }}           — "linux", "darwin", "windows"
{{ .chezmoi.arch }}         — "amd64", "arm64"
{{ .chezmoi.username }}     — current user
```

Conditional blocks:
```
{{- if eq .chezmoi.os "darwin" }}
export BROWSER=open
{{- else }}
export BROWSER=xdg-open
{{- end }}
```

Secret from password manager (e.g. 1Password):
```
export GITHUB_TOKEN="{{ onepasswordRead "Private" "GitHub" "token" }}"
```

Debug templates without applying: `chezmoi execute-template < ~/.local/share/chezmoi/dot_zshrc.tmpl`

## Multi-Machine Setup

New machine bootstrap:
```bash
chezmoi init --apply $GITHUB_USERNAME
```

Daily sync:
```bash
chezmoi update   # git pull + chezmoi apply
```

Push changes back:
```bash
chezmoi cd
git add -A && git commit -m "feat: update zsh config" && git push
```

## Run Scripts

```
run_once_before_install-packages.sh   — runs once, before apply
run_onchange_after_reload-shell.sh    — reruns if script content changes
```

Scripts receive no target file — they are executed, not copied. Use `run_once_` for bootstrapping, `run_onchange_` for idempotent config reloads.

## Troubleshooting Workflow

1. `chezmoi doctor` — check for common problems first
2. `chezmoi diff` — see what would change
3. `chezmoi status` — quick summary (A=add, D=delete, M=modify)
4. `chezmoi cat ~/.zshrc` — preview rendered target without applying
5. `chezmoi data` — inspect available template variables

## Anti-Patterns

### NEVER manually rename or `mv` files inside the source directory

**WHY:** chezmoi encodes target path, permissions, template status, and encryption entirely in the filename. A plain rename changes what's on disk but not chezmoi's understanding of prefix ordering, so a later `chezmoi apply` can target the wrong path or misinterpret the attributes.

❌ BAD:
```bash
mv dot_zshrc.tmpl dot_bashrc.tmpl
```

✅ GOOD:
```bash
chezmoi chattr template dot_bashrc   # let chezmoi rewrite attributes safely
```

**Consequence:** Silent drift between the source state chezmoi believes it manages and what's actually on disk — `chezmoi diff` starts reporting changes that were already applied, or an attribute-order-dependent script fires unexpectedly.

### NEVER store plaintext secrets in source state without `encrypted_`

**WHY:** The source directory is routinely pushed to a personal git remote, and `private_` only sets file permissions on the *target* machine — it does nothing to protect what's sitting in git history.

❌ BAD:
```
private_dot_aws/credentials
```

✅ GOOD:
```
encrypted_private_dot_aws/credentials.asc
```

**Consequence:** Anyone who can read the repo's git history — including a "private" repo later forked, mirrored, or exposed by a visibility mistake — has the plaintext secret forever, even after a later commit deletes it.

### NEVER apply `exact_` to the home directory itself

**WHY:** `exact_` removes any target-directory file that isn't declared in source state. Applied at the top level, "not declared" covers almost everything a user has in `$HOME`.

❌ BAD:
```
exact_.                              # top-level target = $HOME itself
```

✅ GOOD:
```
exact_dot_config/nvim/               # scoped to one subtree you fully manage
```

**Consequence:** `chezmoi apply` deletes every unmanaged file under the `exact_` target with no confirmation beyond the standard diff — including files the user never intended chezmoi to touch.

### NEVER commit `chezmoi.toml` with literal secret values

**WHY:** `chezmoi.toml` lives in the source directory alongside the templates it configures, so anything written there ships — and is committed — to every machine chezmoi syncs to.

❌ BAD:
```toml
[data]
    githubToken = "ghp_abcd1234efgh5678"
```

✅ GOOD:
```toml
[data]
    githubToken = {{ onepasswordRead "Private" "GitHub" "token" | quote }}
```

**Consequence:** The token is readable in plaintext in git history by anyone with repo access, even after a follow-up commit removes it from the working tree.

## Eval Scenarios

- [Scenario 1: Track and template a config file](evals/scenario-1/task.md)
- [Scenario 2: Bootstrap a new machine](evals/scenario-2/task.md)
- [Scenario 3: Write a run-once setup script](evals/scenario-3/task.md)
- [Scenario 4: Add secrets to chezmoi safely](evals/scenario-4/task.md)
- [Scenario 5: Rename a tracked file and scope `exact_` correctly](evals/scenario-5/task.md)

## References

- [`references/commands.md`](references/commands.md) — Full command reference
- [`references/source-attrs.md`](references/source-attrs.md) — All prefixes/suffixes with ordering rules
- [`references/templates.md`](references/templates.md) — Template variables, functions, and directives