The `docx-edit` skill: how the firm reads and edits Word documents with AI, self-contained via a vendored OOXML engine (`assets/vendor/docx-engine.mjs`) — the same engine behind the in-app `.docx` viewer — so it never hand-parses XML with python. Defines the `docx-agent.mjs` tool surface (`inspect` to get a 0-based paragraph map covering body text and table cells, `apply` a comments + tracked-change plan, `accept-all`/`reject-all`), the plan schema where every edit is anchored by paragraph `index` and a VERBATIM `search` (the safety net against editing the wrong clause), and a verb-driven workflow that distinguishes "edit/redline" (propose new changes, fanning out the `docx-redliner` subagent) from "adopt/reject" (act on existing tracked changes). Output defaults to a non-destructive `<base>.redlined.docx`. When implementing or invoking Word document review/editing — the tool contract and the verb-to-action rules.
Word redline: inspect → plan → apply
Click any node to read its source — a doc section or the code itself.
name: docx-edit description: >- Firm-owned Word (.docx) reading + editing. Use whenever the user wants to work with a Word document: read/answer questions about a contract ("what's the salary / term / governing law"), check a value against a threshold, redline or comment on a clause, or accept/adopt or reject tracked changes. Reads the FULL document — including clauses laid out in tables — and writes tracked-change redlines + comments as reviewable suggestions anywhere in the document. Self-contained; runs on the firm's own model and infrastructure; pairs with the in-app .docx viewer.
Word (.docx) reading + editing
This skill is how this firm reads and edits Word documents with AI. It is self-contained:
assets/docx-agent.mjs imports a vendored copy of the OOXML engine
(assets/vendor/docx-engine.mjs) — the same engine behind the in-app .docx viewer — so it
runs in any workspace with no install. Do not hand-parse the document's XML with python —
use this tool; it reads tables, tracked changes, and comments correctly, and edits as
reviewable tracked changes the lawyer accepts in the viewer.
The tool
# Read the document (JSON). `paragraphs` is EVERY paragraph in order — body text AND
# table cells — each with a 0-based `index` and a `location` ("body" or "table, row R,
# col C"). `changes`/`comments` summarize existing tracked changes & comments.
node .opencode/skills/docx-edit/assets/docx-agent.mjs inspect "<file.docx>"
# Apply comments + tracked-change redlines (plan JSON on stdin). Writes <base>.redlined.docx.
echo '<plan-json>' | node .opencode/skills/docx-edit/assets/docx-agent.mjs apply "<file.docx>" --plan -
# Accept ("adopt") or reject ALL tracked changes, then write the clean result.
node .opencode/skills/docx-edit/assets/docx-agent.mjs accept-all "<file.docx>" --out "<file>.adopted.docx"
node .opencode/skills/docx-edit/assets/docx-agent.mjs reject-all "<file.docx>"
Writes <base>.redlined.docx (or .adopted.docx) next to the source by default — the original
is never touched. --in-place overwrites it; --out sets a path; --author "Name" stamps
the reviewer.
The edit plan
apply takes a plan that maps directly onto the engine's batch API. Anchor every edit by
the index from inspect (the same index works for body paragraphs and table cells) and a
verbatim search copied exactly from that paragraph's text:
{
"author": "Eigenwelt Reviewer",
"comments": [ { "paragraphIndex": 181, "text": "Cap is low for this deal — propose €75k.", "search": "EUR 55,000.00" } ],
"proposals": [ { "paragraphIndex": 181, "search": "EUR 55,000.00", "replaceWith": "EUR 75,000.00" } ]
}
commentsare margin notes (the why); they don't change text.searchoptional.proposalsare tracked-change edits.replaceWith:""deletes;search:""inserts at the end of the paragraph; both non-empty is a replacement.searchmust be VERBATIM (an exact substring of that paragraph) or the op is dropped and listed inerrors. This is also the safety net: a wrongindexcan't quietly edit the wrong clause — the search won't match, so it errors instead.
Workflow
-
Resolve the target
.docx(attached,@path, named, or in a folder). The engine handles OOXML Word (.docx,.docm,.dotx). For legacy.doc/.odt/.rtf, ask the user to save as.docx. -
inspectand read the JSON. Most legal contracts lay every numbered clause inside a table — those cells are inparagraphswithlocation: "table, row R, col C". Find the clause you need (e.g. the Remuneration clause for the salary) and note itsindex. Check existingchanges/comments. -
Do what was asked. Read the verb first:
- "edit / change / revise / redline / amend / rewrite / mark up / comment / draft" →
this means PROPOSE NEW edits. Find the relevant clause in
paragraphsandapplya redline/comment anchored by itsindex+ a verbatimsearch. Do NOT start by looking at existing tracked changes — a document having no tracked changes is NOT a reason to stop; you are there to create them. If the user said "edit" but didn't say what to change, ask what change they want (or read the doc and propose a specific one) — never reply "there are no tracked changes." For a full review pass, fan out thedocx-redlinersubagent (Task tool) withFILE, theINSTRUCTION, and theparagraphslist; it returns the plan JSON. - "adopt / accept / finalize / reject the changes / clean up the markup" → ONLY here do
existing tracked changes matter.
accept-all(orreject-all). Ifinspectshows zero changes, say so plainly — there's nothing to adopt; don't pretend. - A question / threshold check → answer from the inspected text and quote the clause.
The existing-changes count in
inspectis only relevant for the accept/adopt/reject case. Never treat "no existing tracked changes" as the answer to an edit request. - "edit / change / revise / redline / amend / rewrite / mark up / comment / draft" →
this means PROPOSE NEW edits. Find the relevant clause in
-
Hand back: state the output file by the
namethe tool returns (a plain, space-free filename — this is what makes it appear as a clickable Word artifact in the panel, exactly like a.md/.csv/.htmlthe firm produces; a name with spaces/parens will NOT surface). Tell the user to open it in the in-app.docxviewer to review — tracked changes + comments render inline; they accept/reject there. Don't pass a--outwith spaces/parens. Summarize what you changed and flag anyerrors(ops whosesearchdidn't match verbatim) and retry those with corrected text rather than dropping them.
Guardrails
- Read with the tool, not python. The vendored engine sees tables, tracked changes, and
comments that hand-rolled XML parsing misses, and gives you the exact
indexto edit by. - Non-destructive. Edits are tracked-change suggestions; the human accepts/rejects in the viewer. Default to a copy; overwrite in place only on request.
- Never fabricate a value, party, number, or a "done." If
searchwon't match or there are no changes to adopt, say so. - Open models, firm-owned. Don't hardcode a model; the
docx-redlinersubagent inherits the firm's configured model. - Distribution. Bundled-core: seeded into every workspace via
core-skills.ts(scripts/gen-core-skills.mjs), alongside the in-app viewer it feeds.