AgentStack
SKILL verified Apache-2.0 Self-run

Docx

skill-matrixfounder-universal-skills-docx · by MatrixFounder

Use when the user asks to create, edit, convert, validate, preview, or password-protect Microsoft Word .docx documents. Triggers include "markdown to docx", "docx to markdown", "fill Word template", "accept tracked changes", "validate docx", "preview docx as image", "encrypt/decrypt docx", and related .docx round-trip or template-fill tasks.

No reviews yet
0 installs
3 views
0.0% view→install

Install

$ agentstack add skill-matrixfounder-universal-skills-docx

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution Used
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Docx? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

docx skill

Purpose: Give the agent a deterministic, script-first way to create, edit, and convert Microsoft Word .docx files so it does not have to re-derive low-level OOXML constructs (page sizes, tables, tracked changes, comment markers, image embeddings, template substitution) on every task. Writing .docx by hand through docx-js or python-docx calls is feasible but error-prone; the scripts here encode the practical knowledge and make the common operations a single command.

1. Red Flags (Anti-Rationalization)

STOP and READ THIS if you are thinking:

  • "I'll just assemble the docx inline with docx-js calls, the scripts are overkill." → WRONG. md2docx.js already handles page size, numbering, dual-width tables, and image alt-text — regressions from hand-rolled code usually show as silently broken tables or images on Word for Windows.
  • "I can skip docx_fill_template.py's run-merge pass and just regex-replace {{placeholder}}." → WRONG. Word and Google Docs fracture a single placeholder across multiple `` siblings after spell-check or autocorrect; a naïve regex won't match and the placeholder will ship unfilled.
  • "For tracked changes I'll just delete every ` block." → **WRONG**. That loses author attribution and breaks paragraph-level delete markers. Use docxacceptchanges.py` which drives LibreOffice's own accept dispatcher.
  • "The smart-quote entity rewrite in office/unpack.py is ugly, I'll turn it off." → WRONG. Different Word builds round-trip UTF-8 quotes inconsistently. Keeping them as ’ etc. on unpack and reversing on pack makes edits deterministic.
  • "I'll just regex-replace \| in OOXML to extract or modify text." → WRONG. Raw XML regex operates on serialised bytes and breaks namespace prefixes, entity encoding, and split-run anchors. Use docx_replace.py (anchor-and-action surgical edit) which works at the lxml element level after run canonicalisation.
  • "I need to edit a .docx and the docx2md → md2docx round-trip loses styling." → Use docx_replace.py --replace instead. It operates at run level, preserving bold/italic/colour/numbering. Round-trip through markdown is lossy: inline formatting, list numbering schemas, and table borders all flatten.

2. Capabilities

  • Surgical anchor-and-action edit of a live .docx (no template required) via docx_replace.py — find a text anchor and --replace TEXT / --insert-after PATH / --delete-paragraph without a lossy docx2md → md2docx round-trip. Run-level formatting (bold, italic, colour, numbering) is preserved. --insert-after automatically relocates images, charts, OLE objects, SmartArt diagrams, and numbered/bulleted lists from the MD source into the base document (docx-6.5 + docx-6.6 v2 relocators, 2026-05-12). Honest scope: --replace requires the anchor to fit within a single ` after run-merge (cross-run anchors not supported, R10.a); --insert-after and --delete-paragraph work at paragraph granularity. Anchor not found → exit 2 AnchorNotFound; last-paragraph delete refused → exit 2 LastParagraphCannotBeDeleted`.
  • Convert Markdown to .docx with headings, lists, tables, images, and optional mermaid diagrams via md2docx.js.
  • Convert HTML / web archives to .docx via html2docx.js — accepts .html/.htm, Chrome .mhtml/.mht, and Safari .webarchive (sub-resources extracted to a temp dir). Confluence, GitLab wiki, and CMS chrome (ARIA-role-tagged headers/sidebars/footers, namespaced ` macros, tablesorter wrappers) is stripped automatically before walking. Inline SVG diagrams (drawio/mermaid/PlantUML) are rastered to PNG via a two-tier renderer: **Tier 1** = headless Chrome / Chromium / Edge / Brave (auto-detected from conventional install paths or HTML2DOCX_BROWSER) gives pixel-perfect CSS layout including foreignObject labels and word-wrap; **Tier 2** = @resvg/resvg-js fallback for hosts without a browser, with foreignObject → SVG conversion that preserves drawio's centring math, synthesises a viewBox on canvas-clipped diagrams (5% expansion absorbs drawio's edge-overshoot artifact), and word-wraps long Cyrillic/Latin labels at the wrapper's width:Npx so text fits inside its box. --reader-mode opt-in: stripped CMS chrome via a curated candidate list (#main-content, .entry, .post-content, article) with per-candidate min-text filter — useful for browser-saved news / blog pages where Confluence-priority selectors fall through to `.
  • Extract .docx content back to Markdown preserving tables, lists, and embedded images via docx2md.js. Comments and tracked changes (which mammoth strips) are pulled into a JSON sidecar (.docx2md.json) — useful for contract audits where the audit trail must accompany the converted markdown. Footnotes/endnotes are converted to pandoc-style [^fn-N] / [^en-N] markers with definitions appended at the end. Schema versioned (v: 1); the sidecar's unsupported field reports counts of revision types not yet captured (rPrChange, pPrChange, moveFrom, moveTo, cellIns, cellDel) so callers know what was lost. Opt-out via --no-metadata (skip sidecar) and --no-footnotes (skip pandoc conversion). Sidecar is not written when the source has no comments, no revisions, and zero unsupported counts — clean docs stay clean.
  • Fill .docx templates containing {{placeholder}} or {{nested.key}} markers from a JSON payload with safe run-merging.
  • Accept all tracked changes in a .docx via headless LibreOffice without leaving artefacts in the user's profile.
  • Unpack and repack .docx archives for raw OOXML editing, with smart-quote entity round-tripping and run canonicalisation.
  • Structurally validate a .docx: relationships, content types, tracked-change/` integrity, comment marker pairing, **package-layout allow-list** (every ZIP entry must live under [ContentTypes].xml, rels/, word/, docProps/, or customXml/ per ECMA-376 §11.3.10 — catches scratch-file leaks that Word refuses to open while LibreOffice silently tolerates), and optional XSD binding. With --strict`, the package-layout warning is promoted to exit 1.
  • Reject password-protected and legacy .doc (CFB-container) inputs early with a clear remediation message (exit 3) instead of a BadZipFile traceback.
  • Detect macro-enabled inputs (.docm, with vbaProject.bin) and warn when the chosen output extension would silently drop the macros (docmdocx).
  • Render any .docx/.docm/.pdf (or peer-skill .xlsx/.pptx) into a single PNG-grid preview via preview.py (LibreOffice + Poppler).
  • Emit failures as machine-readable JSON to stderr with --json-errors (uniform across all four office skills).
  • Set or remove a password on a .docx/.xlsx/.pptx (MS-OFB Agile, Office 2010+) via office_passwd.py — three modes: --encrypt PASSWORD, --decrypt PASSWORD, --check (exit 0 encrypted / 10 clean / 11 missing).
  • Insert a Word review comment anchored on a text substring via docx_add_comment.py — wires `// markers, appends to word/comments.xml, and patches [Content_Types].xml + relationships. Supports threaded **replies** (--parent N) via in commentsExtended.xml; reply-to-reply chains are flattened to the conversation root to match Word's review-pane render. Multi-paragraph bodies via \n in --comment are split into separate per ECMA-376 §17.13.4.2. Opt-in **library mode** (--unpacked-dir DIR) operates in-place on an already-unpacked tree (skips unpack/pack/encryption-check) — **not reentrant**: do not run two processes against the same tree concurrently, no file locking. Malformed OOXML side-parts surface as MalformedOOXML envelope (not a traceback). See [references/add-comment-howto.md`](references/add-comment-howto.md) for verification steps and §6 troubleshooting for failure modes.
  • Merge N .docx files into one via docx_merge.py (VDD iter-2 real-world hardened). Appends body content + styles + numbering + media + relationships into the first input (base), with full reference relocation: image rIds renumbered, r:embed/r:link/r:id in body remapped, ` bumped past base's max, / shifted with body refs rewritten, missing Default Extension entries pulled into [Content_Types].xml. Strips paragraph-level from extras (their header/footer references would dangle). Inserts new BEFORE first ` per ECMA-376 §17.9.20 schema-order. Honest scope (still not merged, warned when extras have content): footnotes / endnotes / headers / footers / comments.

3. Execution Mode

  • Mode: script-first.
  • Why this mode: Each operation is a small, deterministic CLI wrapping a specific OOXML-aware library. Inline assembly in the agent's text is slower and regresses on details (page size, DXA units, namespace ordering). Scripts also make the skill reusable from any shell environment.

4. Script Contract

  • Commands:
  • node scripts/md2docx.js INPUT.md OUTPUT.docx [--header "TEXT"] [--footer "TEXT"] [--page-size A4|Letter] [--landscape] [--margins T,R,B,L]
  • node scripts/docx2md.js INPUT.docx OUTPUT.md [--metadata-json PATH] [--no-metadata] [--no-footnotes] [--json-errors]
  • node scripts/html2docx.js INPUT OUTPUT.docx [--header "TEXT"] [--footer "TEXT"] [--reader-mode] [--json-errors] — INPUT may be .html/.htm, .mhtml/.mht, or .webarchive; sub-resources in archives are extracted to a temp dir automatically (cleaned up on exit incl. SIGINT/SIGTERM). --reader-mode swaps the article-root candidate list for a CMS/blog-specific one (#main-content/.entry/.post-content/article with per-candidate min-text filter; bare ` deliberately omitted as it wraps whole-site chrome on news sites). Override SVG renderer with HTML2DOCXBROWSER=/path/to/chrome or set it to a non-existent path to force the resvg-js fallback for CI determinism. Set HTML2DOCXALLOWNOSANDBOX=1` only inside a trusted CI container — default leaves Chrome's sandbox enabled.
  • python3 scripts/docx_fill_template.py TEMPLATE.docx DATA.json OUTPUT.docx [--strict]
  • python3 scripts/docx_accept_changes.py INPUT.docx OUTPUT.docx [--timeout 120]
  • python3 scripts/office/unpack.py INPUT.docx OUTDIR/ [--no-pretty] [--no-escape-quotes] [--no-merge-runs]
  • python3 scripts/office/pack.py INDIR/ OUTPUT.docx [--no-unescape-quotes] [--no-condense]
  • python3 scripts/office/validate.py INPUT.docx [--strict] [--json] [--schemas-dir PATH] [--compare-to ORIGINAL.docx]
  • python3 scripts/preview.py INPUT OUTPUT.jpg [--cols 3] [--dpi 110] [--gap 12] [--padding 24] [--label-font-size 14] [--soffice-timeout 240] [--pdftoppm-timeout 60]
  • python3 scripts/office_passwd.py INPUT [OUTPUT] (--encrypt PASSWORD | --decrypt PASSWORD | --check) — pass - as PASSWORD to read it from stdin.
  • python3 scripts/docx_add_comment.py INPUT.docx OUTPUT.docx --anchor-text TEXT --comment BODY [--author NAME] [--initials AB] [--date ISO] [--all]
  • python3 scripts/docx_add_comment.py INPUT.docx OUTPUT.docx --parent N --comment BODY [--author NAME] — reply to comment N (inherits its anchor range; threads via commentsExtended.xml).
  • python3 scripts/docx_add_comment.py --unpacked-dir DIR --anchor-text TEXT --comment BODY [...] — library mode: edit an already-unpacked tree in-place (combine with --parent to add a reply over the same tree).
  • python3 scripts/docx_replace.py INPUT.docx OUTPUT.docx --anchor TEXT (--replace TEXT | --insert-after PATH_OR_DASH | --delete-paragraph) [--all] [--unpacked-dir DIR] [--scope=LIST] [--json-errors] — Tier B (script-first); positional INPUT OUTPUT; --anchor TEXT is always required; exactly one of --replace/--insert-after/--delete-paragraph is required (mutex). --all replaces/acts on every match instead of first. --unpacked-dir DIR operates on an already-unpacked tree in-place (library mode — skips unpack/pack/encryption-check). --insert-after - reads the markdown body from stdin. --scope=LIST (docx-6.7) restricts anchor-search to a subset of OOXML parts: comma-separated body, headers, footers, footnotes, endnotes, all (default: all). Example: --scope=body limits edits to word/document.xml, leaving header/footer boilerplate untouched. Order within the requested set is deterministic (document → headers → footers → footnotes → endnotes). Exit codes: 0 success, 1 I/O or OOXML error, 2 anchor-not-found / last-paragraph-delete refused / invalid --scope value, 3 encrypted/password-protected input, 6 same-path self-overwrite refused, 7 post-validate failure. --json-errors emits failures as {v:1, error, code, type, details} JSON on stderr (cross-5 envelope parity). Honest scope: --replace anchor must fit within a single ` after run-merge; --insert-after converts markdown to OOXML via md2docx.js` (requires Node.js in PATH).
  • python3 scripts/docx_merge.py OUTPUT.docx INPUT1.docx INPUT2.docx [...] [--page-break-between] [--no-merge-styles]
  • All scripts above accept --json-errors to emit failures as a single line of JSON on stderr ({v, error, code, type?, details?}). The schema version v is currently 1; argparse usage errors are routed through the same envelope (type:"UsageError").
  • Inputs: positional paths only; optional flags per command.
  • Outputs: a single file at the named output path; office/unpack.py produces a directory tree; office/validate.py prints a report (or JSON with --json). docx2md.js additionally creates _images/ next to the Markdown output when the document has embedded images.
  • Failure semantics: non-zero exit on missing input, invalid JSON, unresolved placeholders (with --strict), unreadable ZIP, or soffice errors. Error detail goes to stderr.
  • Idempotency: repeated runs with the same inputs produce equivalent output files (byte-exact for the XML parts after pretty-print and entity normalisation). docx_accept_changes.py is idempotent on an already-accepted document. Exception: office_passwd.py --encrypt is intentionally non-deterministic — Office encryption uses a fresh random salt per run, so the encrypted bytes differ each time even with the same password. The decrypted output, however, is byte-equal to the pre-encryption input (lossless round-trip).
  • Dry-run support: not applicable; operations are file-to-file and reversible by re-running.

5. Safety Boundaries

  • Allowed scope: only the input/output paths named on the command line and, for Python scripts, modules under scripts/. Never modify files the user did not name.
  • Default exclusions: never overwrite the input in place unless the same path is also passed as output; never fetch remote images silently; never install npm/pip packages globally.
  • Destructive actions: docx_accept_changes.py rewrites the destination; always use a distinct output path unless the user explicitly asks for in-place replacement. office/pack.py overwrites the destination archive.
  • Optional artifacts: the office/schemas/ directory is optional; office/validate.py runs structural checks without it and only warns about missing XSDs unless --strict.

6. Validation Evidence

  • Local verification:
  • cd skills/docx/scripts && npm install — installs docx, marked, mammoth, turndown, image-size, turndown-plugin-gfm into scripts/node_modules/.
  • python3 -m venv .venv && source .venv/bin/activate && pip install -r scripts/requirements.txt — installs python-docx, lxml, defusedxml.
  • node scripts/md2docx.js examples/fixture-simple.md /tmp/out.docx && unzip -l /tmp/out.docx | grep word/document.xml — exits 0 and lists the document part.
  • node scripts/docx2md.js /tmp/out.docx /tmp/back.md && test -s /tmp/back.md — round-trips non-empty Markdown. On a docx with comments / tracked changes, also writes /tmp/back.docx2md.json (schema v:1); on a docx with footnotes/endnotes, the markdown gets pandoc-style [^fn-N] markers

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.