@dylanrussell/chezmoi-guard
v1.1.2
Published
Redirect opencode agent edits to chezmoi source files, then sync. Intercepts edit/write/apply_patch so agents never edit chezmoi-managed targets out-of-band.
Downloads
343
Maintainers
Readme
chezmoi-guard
Stop opencode agents from editing chezmoi-managed files out-of-band.
Intercepts edit / write / apply_patch, redirects to the chezmoi source, then syncs.
What it does
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.
Install
// ~/.config/opencode/opencode.json
{
"plugin": [
"@dylanrussell/chezmoi-guard"
]
}Requires the chezmoi CLI on PATH. The plugin silently no-ops when chezmoi is missing.
Source-type handling
| 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 advisory
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.
Sync semantics
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.
Debug
CHEZMOI_GUARD_DEBUG=1 opencodeEmits [chezmoi-guard] ... lines to stderr for every redirect, skip, warn, and block.
Development
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 --noEmitLicense
MIT © Dylan Russell
