Stop opencode agents from editing chezmoi-managed files out-of-band.
Intercepts edit / write / apply_patch, redirects to the chezmoi source, then syncs.
When an opencode agent edits a file that lives under your chezmoi source, the edit lands on the rendered target — not the source. The next chezmoi apply silently reverts it. This plugin fixes that transparently:
- Intercepts
edit,write, andapply_patchtool calls. - Asks
chezmoi managedwhether the target is chezmoi-managed. - If it is, rewrites the tool args to operate on the source file.
- After the tool runs, executes
chezmoi apply --no-tty <target>to sync the target from the freshly-edited source. - Advises
readcalls: reading a target whose source is a template,modify_script, or encrypted entry prepends guidance pointing at the editable source — without rewriting the read or hiding the real on-disk content. - Emits a TUI toast on every redirect, warning, or block.
Requires the chezmoi CLI on PATH. The plugin silently no-ops when chezmoi is missing.
| Source prefix / suffix | Behaviour |
|---|---|
dot_ / private_ / executable_ / empty_ (normal) |
Redirect to source, chezmoi apply after |
exact_ (directory attribute) |
Files inside an exact_ dir redirect normally — exact_ only prunes absent target entries, it does not make files structural. Directories are excluded upstream by chezmoi managed --include=files,symlinks. |
.tmpl (template) |
edit → redirect to source + guidance; write → warn (hits rendered target, lost on apply) |
symlink_ |
Read the link target, redirect to the actual file, chezmoi apply after |
modify_ |
Passthrough with warning (partial file manager; target edits may be overwritten) |
encrypted_ / .age / .asc |
BLOCKED with guidance (use chezmoi edit instead) |
run_ / directories |
Skipped (scripts / structural markers) |
read is never redirected — the agent always sees the real on-disk target bytes. But for three source kinds, a guidance block is prepended to the read output because a naive follow-up edit would misfire:
| Source kind | Why reads need an advisory |
|---|---|
.tmpl (template) |
Rendered target bytes ≠ source bytes. An edit oldString built from the rendered content will not match the source file the guard redirects to. The advisory says: read the source first. |
modify_ |
The target is script-managed state; persistent changes belong in the modify script, so the advisory points at it. |
encrypted_ / .age / .asc |
Reading the plaintext target is fine, but edits are blocked — the advisory pre-empts a doomed edit plan with chezmoi edit. |
Reads of normal/symlink targets stay silent: their edits are transparently redirected anyway, and a banner on every dotfile read would be pure context noise.
The guard edits the source before the target is touched, so the normal chezmoi apply is non-interactive (the target is clean). The --no-tty flag is added so that if the target has drifted out-of-band (MM status — a prior manual edit, or a template/modify passthrough), chezmoi apply fails safe: it exits non-zero and leaves the user's out-of-band edit intact, instead of hanging on a TTY prompt or silently discarding changes with --force.
CHEZMOI_GUARD_DEBUG=1 opencodeEmits [chezmoi-guard] ... lines to stderr for every redirect, skip, warn, and block.
npm install
npm run build # tsup → dist/plugin.js + dist/plugin.d.ts
npm test # vitest run --coverage
npm run lint # biome check
npm run typecheck # tsc --noEmitMIT © Dylan Russell