@dylanrussell/chezmoi-guardRedirect opencode agent edits to chezmoi source files, then sync. Intercepts edit/write/apply_patch so agents never edit chezmoi-managed targets out-of-band.
0
54
29.2
Multi-signal model
1 month ago
2026-07-17
Install and configure
opencode.jsonWrites to this project's opencode.json — applies to this repository only.
opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@dylanrussell/chezmoi-guard@1.1.2"]
}Writes to ~/.config/opencode/opencode.json — applies to every project.
~/.config/opencode/opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@dylanrussell/chezmoi-guard@1.1.2"]
}If you want to modify the plugin locally, install it into the project and reference the local path.
shell
pnpm add -D @dylanrussell/chezmoi-guardopencode loads npm dependencies through its embedded runtime on startup and caches them locally — no manual global install needed.
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 opencode
Emits [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 --noEmit
License
MIT © Dylan Russell