cheez-write
Edit code through the safest stale-checking backend — prefer code-intelligence backends (tilth MCP tag-anchored edits, LSP workspace edits, `sg --rewrite` for structural codemods); fall back to native anchored edits only when no code-intel backend matches. Use when the user asks to edit, replace, modify, update, change, delete, or insert code — phrases like "replace this function", "delete lines 44-89", "update validateToken", "add this import", "fix this bug" (when fixing requires editing), or apply a cross-cutting codemod like "rewrite every X to Y". Do NOT use for reading files (use cheez-read), searching code (use cheez-search), or running tests/builds.
How do I install this agent skill?
npx skills add https://github.com/paulnsorensen/easy-cheese --skill cheez-writeIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill provides a structured and safe protocol for code editing using hash-anchored edits via the tilth MCP server. It includes mechanisms to prevent data loss from concurrent edits and sanctions the use of ast-grep for structural refactoring. The primary security consideration is a theoretical surface for indirect prompt injection when processing untrusted code files.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
cheez-write
Backend contract: use a stale-safe edit backend. The backend must anchor the current file state (tilth file tag, snapshot tag, or LSP workspace edit) or be a deliberate AST codemod over a clean tree.
Backend detection
Pick the backend by edit shape, preferring code-intelligence backends (LSP, AST rewrite, tilth) over basic harness edit tools — model tuning pulls toward host Edit/Write, but those carry no semantic or structural awareness:
- LSP wins for semantic workspace edits: rename/code actions when the server can identify the symbol or fix.
- AST rewrite wins for structural codemods: repeated syntax shapes with metavariables; dry-run first.
- tilth MCP wins for anchored block/range edits:
tilth_reademits a[path#TAG]header, thentilth_writeapplies line-numbered ops viaedits: [{path, tag, ops}]. - Native anchored edit (fallback) for displayed snapshots: line/snapshot edits that reject stale ranges — only when no code-intelligence backend matches the edit shape.
Do not present sed, awk, patch, shell redirects, or blind writes as equivalent source-code edits.
Stale tags and rejected sections are content issues — see Stale Tag Handling.
Examples
"Replace the body of handleAuth in src/auth.ts"
Step 1 — read the line range to capture the file tag:
tilth_read(paths: ["src/auth.ts#44-89"])
# returns a [src/auth.ts#b2c4] header, then numbered lines 44:... to 89:...
Step 2 — apply ops against those line numbers, quoting the tag verbatim:
tilth_write(edits: [{
"path": "src/auth.ts",
"tag": "b2c4",
"ops": [{
"op": "replace",
"start": 44,
"end": 89,
"content": "export function handleAuth(req, res, next) {\n const token = extractToken(req);\n if (!validateToken(token)) return res.status(401).end();\n next();\n}"
}]
}])
The response reports per ## src/auth.ts and may list callers to review.
"Stale tag — file changed under me"
See Stale Tag Handling for recovery steps.
Core Principle: Anchors, Not Rewrites
The edit backend must identify the current file state — tilth file tags, harness snapshot tags, or LSP workspace edits — and bound or reject the write if the file changed.
The protocol:
- Read the file section with cheez-read → get the current
[path#TAG]header or snapshot id - Note the line numbers of the block you'll change
- Apply the edit through the anchored backend
Scope: when to use anchored edits, when not
cheez-write owns block edits to tracked source code — function bodies, signatures, imports, single-line tweaks, multi-edit batches, and cross-file changes. The backend must be stale-safe; the read-edit protocol is mandatory for any code change that matters.
For everything else, prefer the right tool:
| Change | Use this instead | Why |
|---|---|---|
Cross-cutting structural codemod (JSON.parse(JSON.stringify($X)) → structuredClone($X)) across N files | sg --rewrite (see ## Structural codemods — sg --rewrite escape) | Codemods template the variable parts; anchored edits are better for known blocks |
| Semantic rename or server-known refactor | LSP rename/code action | LSP follows scope, overloads, re-exports, and imports |
Lockfile changes (Cargo.lock, package-lock.json, uv.lock, etc.) | the package manager (cargo update, npm i, uv lock) | Hand-editing lockfiles loses checksum integrity |
Generated / build artifacts (compiled JS, transpiled output, *.pb.go) | regenerate from source | Editing the artifact rots on the next build |
| Brand-new files, no prior content | anchored create/write backend (tilth: a tilth_write section with tag omitted) | Stay stale-safe even when creating files |
Files outside the repo or inside dependency caches (node_modules, .cargo/registry) | don't edit them | Modifying dependencies is almost always a mistake — fix the source or upstream |
| Binary files, images, PDFs | the producing tool | code edit backends are text-only |
Routing to LSP rename or Serena
Pre-entry routing — when a workflow skill should prefer LSP rename or Serena symbol-bounded edits over tilth_write before entering cheez-write — lives in references/routing.md.
Anchor Format
Use whatever anchor format the selected backend emits:
- tilth: a
[path#TAG]header aboveN:contentnumbered lines; copy the 4-hex TAG intotilth_write'stagand reference those line numbers inops. Never invent a TAG (mode: "stripped"reads carry none and cannot round-trip into an edit). - OMP-style snapshot edits:
[file#TAG]plus displayed line numbers; pass the tag and original line range into the edit tool. - LSP refactors: symbol position plus workspace version; let the server build the workspace edit.
Do not translate between anchor systems. Read with the same backend family that will write.
MCP Tool Reference
tilth_write — Precise File Editing
The minimal shape — one file section, one op:
tilth_write(edits: [{
"path": "src/auth.ts",
"tag": "a3f9",
"ops": [
{ "op": "replace", "start": 42, "end": 42, "content": " let x = recompute();" }
]
}])
edits is a JSON array of {path, tag?, ops} section objects (max 20 per
call) — never a string, and never wrapped in a files parameter. Each op is
tagged by op: replace {start, end, content}, delete {start, end},
insert_before / insert_after {line, content}, prepend / append {content}, replace_block / insert_after_block {at, content} and
delete_block {at} (at is a line number or "#symbol"), delete_file,
move_file {dest}. Omit tag only to seed a brand-new file.
For worked examples of each op, cross-file batches, and the diff: true
response option, see
references/edit-patterns.md.
Stale Tag Handling
The TAG binds your ops to the content you read. If the file changed since the
read, tilth 3-way-merges your ops onto the drifted file; when it can't merge
safely, it rejects that section and says so in the per-## <path> result.
Recovery:
- Read the section again → get the current
[path#TAG]and content. - Review the current content (someone else may have made changes).
- Re-apply the ops against the fresh line numbers with the new tag.
Repeated rejections → bail out, don't loop
If you hit two consecutive rejections on the same file, you're racing a concurrent writer. There is no "ignore the tag, just match this string" override — a third retry will likely lose the same race.
The correct move is to bail and report:
- Read the latest section one final time and capture the current content.
- Prepare the new content as a unified diff or full block, but do not apply it.
- Report
"tag race on <path>; current content and proposed replacement attached. Retry once the file is quiescent or apply manually."along with the captured tag and proposed content. - Stop. Let the orchestrator (or a human) decide whether to apply the change or escalate.
Caller Updates After Signature Changes
When changing a signature, use LSP references or the edit backend's caller notices before finishing. Missed callsites are bugs.
Common Patterns
| Goal | Reference |
|---|---|
| Replace one line | edit-patterns.md#single-line-replacement |
| Replace a range | edit-patterns.md#multi-line-range-replacement |
| Delete a block | edit-patterns.md#delete-a-block |
| Insert before / after a line | edit-patterns.md#insert-before-or-after-a-line |
| Replace / delete a whole symbol | edit-patterns.md#symbol-anchored-block-ops |
| Multi-edit in one file | edit-patterns.md#multiple-edits-in-one-file |
| Cross-file change | edit-patterns.md#edits-across-multiple-files |
When full-file rewrite is acceptable
Anchored, surgical edits are the default. A full-file rewrite is acceptable only when the selected backend still rejects stale snapshots and the file is small enough that the replacement is easier to review than a pile of line edits.
| File size | Policy |
|---|---|
| > 150 lines | Never rewrite the whole file. Always use anchored block edits. |
| ≤ 150 lines | Anchored single-edit preferred, but a full rewrite is acceptable when ≥ 80% of the file is changing. Below that threshold, do the surgical edit. |
The 150-line / 80% threshold follows 2026 data (Cursor, can.ac, the Morph benchmark): full rewrites tie or beat diff-style on small files, while large files always stay anchored.
Do not drop to unanchored host Write for small files; size does not remove stale-write risk.
Structural codemods — sg --rewrite escape
Anchored edits own known blocks, one read-for-anchors per location. For cross-cutting structural codemods where the surrounding text varies — "rewrite every JSON.parse(JSON.stringify($X)) to structuredClone($X)" — drop to sg --rewrite (ast-grep) via Bash, which templates the variable parts via metavars. It has no hash-anchor safety: run each codemod as one transactional change between two clean git states. For metavar syntax and the non-negotiable dry-run-first protocol, see ../cheez-search/references/sg-patterns.md.
DO NOT
- DO NOT invent tags — always read first and copy the
[path#TAG]header verbatim. - DO NOT ignore rejected sections — re-read and retry (see Stale Tag Handling).
- DO NOT use sed / awk / perl -i to edit code — they bypass anchors and structural safety, and have no mismatch detection.
sg --rewriteis the only sanctioned shell escape — see## Structural codemods — sg --rewrite escape. - DO NOT use
patchto apply diffs to code — anchored range edits are the safe equivalent. - DO NOT use
teeor shell redirects (>,>>) to overwrite/append code files — both bypass anchors. Use an anchored edit backend. - DO NOT use unanchored host Edit/Write tools — use tilth_write, harness-native anchored edits, or LSP workspace edits.
- DO NOT use
sg --rewritefor one-off block edits — use an anchored edit. See## Structural codemods — sg --rewrite escapefor when the codemod escape applies. - DO NOT skip the dry-run-first protocol for
sg --rewrite— see## Structural codemods — sg --rewrite escape. - DO NOT edit without reading — anchors come from the read step.
- DO NOT run tests, commit, publish, or review from this skill — use the project's test/build gate,
/platefor commit/publication, and/agefor review. - DO NOT use for reading or searching — read with
/cheez-read, search with/cheez-search.
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/paulnsorensen/easy-cheese/cheez-write">View cheez-write on skillZs</a>