Docx Skill: Create, Edit, and Review Word Documents with Hermes Agent
Create, read, edit, template, and review Word .docx files.
Written by Neura Market from the official Hermes Agent documentation for Docx. Commands, paths, and version numbers are reproduced from the source unchanged.
Read the official documentationThe Docx skill turns Hermes Agent into a Word document workshop. You hand it a JSON spec and it produces a .docx; you point it at an existing file and it reads, edits, templates, and reviews it. This is the tool to reach for when a user asks for a report, letter, or contract, or when you need to pull text, styles, or images out of a .docx without opening Word.
What it does
The skill wraps python-docx in a set of small command-line scripts. Each script does one job and prints JSON to stdout, so you can chain them together or inspect results programmatically. You can create documents from a structured spec, read back their content and structure, edit text and formatting, fill templates with data, review tracked changes, manage comments, and check the health of a .docx package.
It handles the common building blocks of a Word document: paragraphs, headings, lists, tables, images, headers and footers, and custom styles. It also understands the parts of a document that usually require manual Word work: tracked changes (insertions and deletions), comments, table of contents fields, and "Page X of Y" footers. The skill does not render documents to PDF, and it does not touch legacy .doc files.
Before you start
You need Python 3.10 or newer with python-docx installed. The import name is docx, and lxml comes along with it. Install it with:
pip install python-docx
For adding comments, the skill uses the native API on python-docx 1.2 and newer, and falls back to an XML approach on older versions. Both paths are automatic, so you do not need to configure anything.
If you plan to embed images, the image files must already exist locally and be in PNG or JPEG format. The skill does not fetch or convert images for you.
All helper scripts live in a scripts/ directory next to the skill file. You run them with the terminal tool. Every script supports --help, and each prints JSON to stdout.
How to run the scripts
The skill ships with seven scripts. Here is the full set of entry points:
python scripts/docx_create.py spec.json out.docx
python scripts/docx_read.py out.docx --text
python scripts/docx_edit.py replace out.docx --find old --replace new
python scripts/docx_template.py tpl.docx values.json filled.docx
python scripts/docx_revisions.py list out.docx
python scripts/docx_comments.py list out.docx
python scripts/docx_validate.py out.docx
Each script has a specific purpose. docx_create.py builds a new document from a JSON spec. docx_read.py extracts information from an existing document. docx_edit.py modifies a document in place or writes a new one. docx_template.py fills placeholders. docx_revisions.py lists and resolves tracked changes. docx_comments.py manages comments. docx_validate.py checks the package health.
Quick reference
The table below summarizes the most common operations. You will use these commands constantly, so keep it handy.
| Task | Command |
|---|---|
| Create from JSON spec | docx_create.py spec.json out.docx |
| Full text (body+tables+headers/footers) | docx_read.py f.docx --text |
| Heading outline + table shapes | docx_read.py f.docx --structure |
| Styles actually used | docx_read.py f.docx --styles |
| Extract embedded images | docx_read.py f.docx --images outdir/ |
| Detect tracked changes/comments | docx_read.py f.docx --revisions |
| Find/replace (formatting kept) | docx_edit.py replace f.docx --find A --replace B -o out.docx |
| Set a table cell | docx_edit.py set-cell f.docx --table 0 --row 1 --col 2 --text X |
| Insert paragraph before index N | docx_edit.py insert f.docx --index N --text X --style Normal |
| Delete paragraph N | docx_edit.py delete f.docx --index N |
| Apply style to paragraph N | docx_edit.py style f.docx --index N --style "Heading 1" |
| Merge equal-format adjacent runs | docx_edit.py normalize f.docx -o out.docx |
| Insert TOC field before para N | docx_edit.py toc f.docx --index N -o out.docx |
| "Page X of Y" footer fields | docx_edit.py page-numbers f.docx |
Fill {{tokens}} | docx_template.py tpl.docx values.json out.docx --strict |
| List revisions (id/author/date/text) | docx_revisions.py list f.docx |
| Accept / reject all revisions | docx_revisions.py accept-all f.docx -o out.docx (or reject-all) |
| Accept / reject one revision | docx_revisions.py accept f.docx --id 3 -o out.docx |
| List comments (+anchored text) | docx_comments.py list f.docx |
| Add comment anchored to text | docx_comments.py add f.docx --target "phrase" --text "note" --author You |
| Delete comment by id | docx_comments.py delete f.docx --id 0 |
| Health-check the package | docx_validate.py f.docx (exit 1 on errors) |
Creating a document
To create a document, you first write a JSON spec file using write_file, then run docx_create.py with that spec and an output path. The spec is the blueprint for the entire document.
The spec supports these top-level keys:
page: page size and margins in millimeters.headerandfooter: strings that appear on every page.footer_page_numbers: adds a "Page X of Y" field footer.styles: custom paragraph styles with font, size, bold/italic, and hexcolor.blocks: the content of the document, in order.
Each block can be one of several types:
heading: with a level from 1 to 9.paragraph: either a plaintextstring or arunslist where each run can setbold,italic, orunderline.bullet_listandnumbered_list: for lists.table: with aheaderrow (rendered bold),rows, and an optional built-in tablestylesuch asTable Grid.image: with apathand optionalwidth_mm.toc: a Table of Contents field.page_break: forces a page break.
The full spec format is documented at the top of scripts/docx_create.py. When you are unsure about a key or value, read that header comment first.
Reading a document
docx_read.py extracts information from an existing .docx. You must pass exactly one mode flag. The modes are:
--text: returns body paragraphs, all table cell text, and header/footer text as JSON.--structure: returns the heading outline plus paragraph, table, and section counts.--styles: lists the styles actually used in the document.--images DIR: copies every file underword/media/out of the package into the given directory.--revisions: detects tracked changes and comments.
Use --text when you need the full content, --structure when you need an outline, and --styles when you need to confirm which styles are applied. The --images mode is handy when you need to extract embedded pictures.
Editing an existing document
docx_edit.py is the workhorse for modifications. It supports several subcommands:
replace: walks the body, tables (including nested ones), headers, and footers, and replaces text while preserving run formatting. Add--body-onlyto skip headers and footers. Pass-o out.docxto keep the original file; omit it to edit in place.set-cell: sets the text of a specific table cell. The command takes--table,--row,--col, and--text.insert: inserts a new paragraph before a given index. The--styleoption sets the paragraph style.delete: removes the paragraph at the given index.style: applies a style to the paragraph at the given index.normalize: merges adjacent runs with identical formatting. Run this first on documents that came out of heavy Word editing, so later find-replace matches reliably.toc: inserts a Table of Contents field before a given paragraph index.page-numbers: adds "Page X of Y" footer fields.
Paragraph indices for insert, delete, style, and toc refer to the body order shown by --structure or --text. Count from zero.
Reviewing tracked changes
docx_revisions.py handles tracked changes. The list subcommand reports every insertion (w:ins) and deletion (w:del) anywhere in the body, tables, headers, or footers. Each entry includes the id, author, date, and affected text.
To resolve changes in bulk, use accept-all or reject-all. To handle a single revision, use accept --id N or reject --id N. Accepting keeps insertions and drops deleted text; rejecting does the reverse. Always write the result to a new file with -o out.docx so you can compare.
Managing comments
docx_comments.py works with comments. The list subcommand returns each comment's id, author, date, body text, and the document text it is anchored to.
To add a comment, use add --target "some phrase" --text "note" --author You. The comment anchors to the first occurrence of that phrase. The script splits runs as needed and preserves formatting. To remove a comment, use delete --id N, which removes the comment and its markers without touching the document text.
Filling templates
Templating is straightforward. Put {{name}}-style tokens in the document, then run docx_template.py with a JSON object of values. The script replaces each token with its corresponding value.
Use --strict to fail when tokens remain unfilled. The JSON output lists filled counts and unfilled_tokens either way, so you can check the result programmatically.
Verifying your work
Always re-read the output after any operation. Run docx_read.py out.docx --text and check that the expected strings appear and old strings are gone. After accept/reject, docx_revisions.py list should return [] (or only the ids you intentionally left). After comment surgery, docx_comments.py list should reflect the change, and --text output must be unchanged.
Run docx_validate.py out.docx on anything you produced via revision or comment surgery. It exits 0 with "ok": true on a healthy package. For templates run with --strict, or check unfilled_tokens == []. Use --structure to confirm the heading outline and table shapes, and --styles to confirm custom styles applied.
Converting to PDF
The skill does not render PDFs. When LibreOffice is installed, you can convert headlessly with:
soffice --headless --convert-to pdf --outdir outdir/ file.docx
Check availability first with command -v soffice || command -v libreoffice. If neither exists, tell the user PDF conversion is unavailable in this environment rather than improvising. python-docx cannot render PDFs, and layout fidelity requires a real renderer.
When not to use it
This skill is not for legacy .doc files, .odt files, or WYSIWYG layout work. If the user needs precise visual positioning or pixel-perfect design, a different tool is required. For PDF output, you need LibreOffice installed separately.
Limits and gotchas
Several pitfalls can trip you up. Knowing them in advance saves debugging time.
- Tokens split across runs. Word often fragments text into several runs. The replace helpers collapse matched runs, and the replacement inherits the first run's formatting. Running
docx_edit.py normalizefirst reduces fragmentation for all later edits. - Revision coverage.
docx_revisions.pyresolves run-level insertions and deletions, which are the overwhelming majority. Paragraph-mark and table-row revisions, format-change records, and moves are detected by--revisionsbut not auto-resolved. Seereferences/revisions-and-comments.mdand hand those to Word. - Comment threading. Replies and "resolved" status live in
commentsExtended.xml, which this skill ignores. Comments it adds are plain top-level comments. - Field results are computed by Word.
toc,page-numbers, and thetoc/footer_page_numbersspec options write field codes. Word or LibreOffice populates the actual entries and numbers when the file is opened. Word may prompt to update fields. python-docx never computes them, so placeholder text shows until then. - Validation is a health check, not schema validation.
docx_validate.pyverifies the zip, required parts, relationship targets, image magic bytes, and referenced styles. It is NOT XSD validation. A file can pass and still contain XML Word dislikes. - Style names must exist. Applying a style that isn't defined in the document raises
KeyError. Built-ins likeHeading 1,List Bullet,List Number, andTable Gridexist in the default template. Custom styles must be declared in the create spec first. - Numbered lists restart.
List Numberrelies on Word's default numbering. Separate lists in one document may continue numbering instead of restarting. Warn users needing precise multi-list numbering. - Cell writes replace formatting.
set-cellusescell.text = ..., which resets runs in that cell to plain formatting. - Encoding. All JSON specs and values files are read as UTF-8 explicitly. Never rely on locale defaults when writing your own glue code.
- Don't unzip-and-sed the XML. Edit through the scripts or python-docx. Raw text substitution in
document.xmlcorrupts files easily. Usepatchorwrite_fileonly for the JSON inputs, never on the.docxitself.
What pairs with this
The Docx skill sits alongside other productivity skills in the Hermes Agent bundle. The related skills are pdf, xlsx, and powerpoint. Use them together to handle a full suite of office documents: generate a report in Word, convert it to PDF, or pull data from a spreadsheet to fill a template.