Edit content in any format, faithfully
Goal: edit the text inside a file without touching its structure (a Word document, a JSON catalog, a Markdown guide, or any other format kapi reads). The result is the same file (same keys, same styles, same inline codes) with only the prose changed. You (or your assistant) do the rewriting; kapi parses the format, enforces a faithful round-trip, and writes the result back. kapi sends no content to a model for the rewrite, and you need no project, provider or credential.
A short walkthrough inspecting a file's blocks, previewing an edit as a diff, applying it in place, and verifying the result with a check, showing how only the leaf text changes while the format round-trips.
- Desktop
- Agent (Claude)
- CLI
- Read the blocks. Open the file in your project: Blocks lists each text block as the engine reads it; Preview shows the rendered document with the source ↔ target toggle.
- Edit a block in place. In Preview, click a block and choose Edit. Formatting and placeholders stay as chips you type around, and saving writes the file through its format, leaving every other byte as it was.
- Edit translated text in Review. For target-language blocks you can also edit in the Review queue, then approve; the write goes through the same round-trip.
- Rewrite many blocks at once with
apply. Drive the inspect →applyloop from the CLI tab or a connected assistant. Then reopen Preview to see that headings, styles and inline codes are intact, and click Run on everything on the project's Map to confirm the gate.
With kapi wired as an MCP server or through the Agent Skill, ask:
Simplify the setup section of
guide.md: shorter sentences, same meaning, keep every link.
The assistant drives the loop through structured calls: read_blocks to read blocks, apply_edits to write the change set back, check_file to confirm the gate. The assistant is the writer; kapi enforces the round-trip, refuses an edit to a block that changed since the assistant read it, and rejects an edit that would corrupt inline markup. No model is called for the write itself.
Read the blocks. kapi inspect returns one record per content block: its reference (ref), the revision of its text (rev), the text, the inline codes it holds, the operations it accepts, and its structural role:
kapi inspect guide.md --jsonl # one JSON object per line
{"ref":{"doc":"guide.md","block":"setup/p"},"rev":"r:5e10a2b3c4d5e6f7","text":"See the <x id=\"1\"/>dashboard<x id=\"/1\"/>.","codes":{"1":{"kind":"paired","type":"link:hyperlink","attrs":{"href":"/dashboard"}}},"ops":["set_content","replace_text"]}
Three fields anchor an edit. ref names the block; copy it into the edit. rev is the revision you read; send it back as if_match so kapi can tell the block is still what you read. text renders inline codes as <x id="…"/> tokens; keep every token in your edited text (a placeholder is <x id="1/"/>, a paired span opens with <x id="1"/> and closes with <x id="/1"/>). A character reference the source spells out, such as & in HTML, shows as its character; keep each one where it is, and it keeps its spelling. inspect reads a file with the same reader apply writes it back through, so every ref and rev it prints is one apply resolves.
Everything else in text is text, and kapi encodes it for the file's format: in HTML a < or & you type is written as a character reference. Markdown, MDX and AsciiDoc keep some of their own syntax in text, such as a backslash escape, an MDX {expression} or an AsciiDoc +++ passthrough. Syntax the block already holds stays as you keep it, and syntax you add (a tag, a link, an expression, an import line, a macro) is escaped so the page shows the characters you typed.
Edit, then apply. Write a change set with one operation per changed block: set_content with the new text, or replace_text to change words inside it and keep everything around them:
{"ops": [
{"op": "set_content", "at": {"doc": "guide.md", "block": "setup/p"}, "if_match": "r:5e10a2b3c4d5e6f7",
"text": "Open the <x id=\"1\"/>dashboard<x id=\"/1\"/>."},
{"op": "replace_text", "at": {"doc": "guide.md", "block": "setup/p#2"}, "if_match": "r:0d71f30c75e4a087",
"edits": [{"find": "utilize", "text": "use"}]}
]}
kapi inspect guide.md --jsonl > blocks.jsonl
# write the operations for the blocks you change (keeping every <x id="…"/>
# token) to edits.json (you do this; no command)
kapi apply edits.json --dry-run # check it and print a diff per document, write nothing
kapi apply edits.json # apply in place
kapi apply edits.json --in-place=.bak # apply, keeping a .bak
Verify. kapi check confirms the result meets your content rules: deterministic, read-only, exit 0 pass and 3 on gate failure:
kapi check guide.md --max-chars 500 --forbid "utilize"
What refuses an edit
apply writes a change set only when every operation in it holds. When one is refused, nothing in the change set is written, the refused operation names why, and the others report not_applied:
stale: the block's revision differs from theif_matchyou sent; it changed since you read it. The result carries the current revision and text to rebase on.guard: an editedtextthat drops, invents, duplicates, or unbalances an<x id="…"/>token, or flat text in place of a block holding a plural or select. Edit one branch of a plural with thepaththatinspectlists understructures.not_found: the reference names no document or block, or afindmatches nothing; the block may have been removed or renamed since you read it.unsupported: the block takes no edit, such as a Markdown code block, whichinspectlists with noops.
A refusal exits with code 3, distinct from an operational error: read the affected blocks again and resend with fresh revisions. A change set that does not decode exits 2 before anything is read. Resending a change set that landed writes nothing: the revisions it names have moved, so it is refused as stale with each block's current text. An operation whose revision still holds and whose text the block already has reports unchanged. The same change set can carry a term or a content memory pair (Create content) or a review decision (The kapi loop), applied after the content; kapi apply --schema prints every operation.
What "faithfully" means
Only the extracted prose changes; everything outside it (.docx character styles and table borders, JSON keys and non-string values, Markdown headings and fences) is reproduced byte for byte. So you can edit binary office formats (.docx, .pptx) safely; kapi formats marks such formats faithful in its Edit column.
In a project
In a kapi project, kapi inspect and kapi check run against the recipe's declared content with no file argument. inspect names each document by its project-relative path and lists each translation among a block's editions, and kapi apply resolves those references in the same project.
Next
- Checks: rule families, profiles, scoring, and the gate exit codes.
- Create content: the author → parse → check loop and the mixed change-set.
- Feed content to your AI: the same anchored blocks as an ingestion stream.