A DOCX editor for AI Agents: lightweight CLI toolkit for safe, reproducible .docx extraction and patching with python-docx.
- Extract DOCX into structured JSON (
docx-structure.v2) - Emit a read-only contextual review (
docx-review.v1) for tracked changes, comments, and anchors - Optionally emit RAG-friendly chunks (
docx-llm-chunks.v1) - Apply block-targeted patches with validation guards
- Support Markdown-aware writeback (inline + block-level)
- Convert Markdown to standalone or template-based DOCX files, including optional landscape tables
- Fill arbitrary table cells from JSON (
layout: "cell-map") for generic matrix/table use cases
- Python 3.10+
python-docx
Install:
py -3 -m pip install python-docxUpstream project (python-docx):
Extract structure:
py -3 scripts/extract_docx_for_llm.py --in input.docx --out structure.v2.jsonOptional RAG output:
py -3 scripts/extract_docx_for_llm.py --in input.docx --out structure.v2.json --rag-output rag.v1.jsonApply patch:
py -3 scripts/apply_docx_patch.py --in input.docx --out output.docx --patch patch.jsonInspect comments, revisions, and highlights:
py -3 scripts/docx_ops.py inspect --in input.docx [--json]With --json, the output keeps the legacy comments, revisions, and
highlights fields and adds a top-level review object (docx-review.v1);
the same object is added to the structured extraction below.
Fill generic table cells from JSON spec:
py -3 scripts/fill_docx_table_from_json.py --in input.docx --out output.docx --spec cell-map.jsonConvert Markdown to DOCX:
py -3 scripts/markdown_to_docx.py --in input.md --out output.docxBoth Markdown converters skip optional leading YAML front matter (--- … ---), so document metadata is not rendered as visible text. A standalone --- without a closing marker remains a normal Markdown horizontal rule.
Convert Markdown to DOCX using a styled Word template (preserves headers, footers, page size, margins, and styles):
py -3 scripts/markdown_to_template_docx.py --in input.md --out output.docx [--template my_template.docx] [--table-orientation portrait|landscape]With --table-orientation landscape, an H2/H3 heading immediately followed by a Markdown table (blank lines allowed) is placed with that table on the landscape page. Consecutive heading-table blocks do not create an empty portrait page between them; subsequent prose returns to the template's portrait layout.
Example spec (SWOT 2x2):
references/examples/table-cell-map-swot.jsonNotes:
table_index,row,colare 1-based.- Optional per-cell flags:
mode(replace|append) andclear_first(true|false).
Preview helpers:
py -3 scripts/docx_preview.py --helpscripts/docx_ops.py inspect --json, scripts/extract_docx_for_llm.py --out,
and the optional --rag-output payload all carry the same additive top-level
review object. It is read-only: the source DOCX is never opened for writing.
Legacy compatibility:
inspectkeeps itscomments,revisions, andhighlightsfields unchanged.- v2 keeps
docx-structure.v2, its block IDs, text, and stats unchanged. - v1 keeps
docx-llm-chunks.v1, its block IDs, chunks, and text unchanged. reviewnever replaces or reinterpretsp_*patch IDs; it uses XML locations.
Shape:
{
"schema": "docx-review.v1",
"paragraphs": [
{
"location": {"part": "word/document.xml", "path": "/document[1]/body[1]/p[1]"},
"text_original": "ADEFG",
"text_proposed": "ABCDG",
"text_markup": "ABCDEFG"
}
],
"revisions": [
{
"kind": "insert",
"id": "10",
"author": "Alice",
"date": "2026-10-01T10:00:00Z",
"text": "BC",
"location": {"part": "word/document.xml", "path": "/document[1]/body[1]/p[1]"},
"ranges": {"original": [1, 1], "proposed": [1, 3], "markup": [1, 3]},
"missing_metadata": []
}
],
"comments": [],
"limitations": [],
"coverage": {"supported_parts": ["word/document.xml"], "text_views_complete": true}
}Coordinates and views:
location.parthas no leading slash;location.pathuses 1-based sibling indexes, for example/document[1]/body[1]/tbl[1]/tr[1]/tc[1]/p[1]. Nested tables add anothertbl/tr/tc/pchain.- Text views are UTF-8 codepoint offsets, 0-based and end-exclusive.
w:tabbecomes a tab,w:br/w:crbecome a newline, and whitespace is preserved. originalomits insertions and keeps deletions;proposedkeeps insertions and omits deletions;markupkeeps both. Text omitted from a view keeps a collapsed[n, n]range there, wherenis the actual zero-width text boundary before the omitted content: a deletion ofEFafter proposedABCDreportsproposed: [4, 4](not[3, 3]), a deletion at the paragraph start reports[0, 0], and one at the end reports[len, len].- Only Word-namespace
w:ins/w:del/w:tare read as Word content. Foreign lookalikes in another namespace are reported as unsupported content and never enter a text view. - All paragraphs of the main document are included, including inner table
cells and paragraphs inside
w:customXmlwrappers. Text-box content is not merged into the surrounding paragraph.
Revisions, comments, and limitations:
- One record per text-carrying
w:ins/w:del; multiple runs inside one revision are merged. Missingauthor/datearenulland named inmissing_metadata; IDs stay strings. - Comments carry
resolved(true/false/null),parent_id,reply_ids, and ananchor. A missing done state is never reported as open. Anchors areresolved,unresolved(with diagnostics such asorphan_anchor,duplicate_anchor,reversed_anchor), orthread_onlyfor replies without their own range. Segments preserve cross-paragraph ranges in all three views. - Move, property, structural, and unknown revisions, text boxes, and review
markers in any other WordprocessingML part are reported as structured
limitations: headers, footers, footnotes, endnotes,word/comments.xml, the glossary, and every furtherword/*.xmlstory.unsupported_contentcovers body wrappers that are not traversed (for examplealtChunk) and foreign-namespace elements.coverage.text_views_completeisfalsewhen such a marker makes the text views incomplete; it is never a completeness guarantee for the whole package. - Comment and thread ambiguity is diagnosed instead of guessed, and every
diagnostic keeps the exact part, XML path, and comment/revision ID.
ambiguous_comment_idleaves all affected anchorsunresolved; duplicateparaIds yieldambiguous_thread_metadatawithout binding a done state or parent; self and cyclicparaIdParentrelations yieldself_parent_reference/cyclic_parent_referencewith no trustedparent_idorreply_ids.w15:doneacceptstrue/false/1/0, a missing value isnull, and an invalid value staysnullwithmalformed_thread_metadataat the offendingcommentExlocation. - Malformed ZIP/XML input fails with a clear error instead of returning an empty successful review.
- On Windows, piping CLI output can hit the legacy console code page (
cp1252) and fail on non-ASCII review text. SetPYTHONIOENCODING=utf-8for review commands, for example$env:PYTHONIOENCODING='utf-8'; py -3 scripts/docx_ops.py inspect --in input.docx --json. This feature does not change the existing CLI console-encoding behaviour.
Currently supported in scripts/apply_docx_patch.py:
replace_textset_paragraphdelete_paragraphreplace_paragraph_rangereplace_paragraph_range_markdown
Full schemas and contracts are documented in SKILL.md and references/python-docx-quickref.md.
- Fails on unexpected match counts (
expected_matches) - Refuses risky cross-run replacements
- Supports expectation guards (
expected_*) to avoid wrong target writes - Heading-protection for range replacements by default
py -3 scripts/selftest.py
py -3 -m unittest discover -s tests -p test_docx_review.py -vscripts/– executable toolsreferences/– quick reference notes and spec snippets:references/ooxml-numbering-notes.md(OOXML numbering model + restart behavior)references/python-docx-list-behavior-notes.md(official python-docx list/style behavior)
SKILL.md– canonical workflow/spec documentation
For numbering/list bugs, prefer Microsoft Learn OpenXML docs as the trusted external source before applying fixes.
This repository is the source of truth. After changes, sync tracked/non-ignored files into the agent skill folder (e.g. boku-martin/skills/python-docx-editor) so runtime uses the same version.
Example (PowerShell):
git -C C:\Users\dagobert-ai\.openclaw\workspace\projects\python-docx-editor `
ls-files --cached --others --exclude-standardCopy that file list to the target skill directory.
GNU General Public License v3.0 (GPL-3.0). See LICENSE.
Project is focused on practical CLI workflows and LLM-assisted editing pipelines.
Export DOCX to PDF via Microsoft Word on Windows:
powershell powershell -ExecutionPolicy Bypass -File scripts/export_docx_to_pdf.ps1 -InputPath input.docx -OutputPath output.pdf
Notes:
- This export path currently works only on Windows.
- It requires a local Microsoft Word installation because it uses the Word COM interface.