Docx logo

Docx

CommunityPopular
mateaix
docx

Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx files). For CREATING new documents (report, memo, letter, résumé, contract from scratch), call the built-in tool `renderDocx` instead — it renders Markdown to .docx in milliseconds without forking Node.js. This skill remains authoritative for EDITING existing .docx (unpack/edit XML/pack), tracked changes, comments, image manipulation, find-and-replace, and conversions. Triggers include: "Word doc", "word document", ".docx", or requests to edit/extract/restructure existing Word files. Do NOT use for PDFs, spreadsheets, or general coding tasks unrelated to document generation.

Overview

Publishermateaix
Repositorymateclaw
Skill namedocx
Stars
1.1K
Forks
337
Bundled files
59
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 59 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by mateaix on GitHub. Read the source before you install it.

Installation

Install the Docx AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/mateaix/mateclaw/tree/dev/mateclaw-server/src/main/resources/skills/docx
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/mateaix/mateclaw.git /tmp/mateclaw
mkdir -p .claude/skills
cp -r /tmp/mateclaw/mateclaw-server/src/main/resources/skills/docx .claude/skills/docx
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Docx in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Docx on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Docx is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Important: All scripts/ paths are relative to this skill directory. Use run_skill_script tool to execute scripts, or run with: cd {this_skill_dir} && python scripts/...

DOCX creation, editing, and analysis

Quick Start — Pick the Right Tool

TaskRecommended Tool
Create a new document (report / résumé / contract / memo)renderDocx() — millisecond render, no subprocess
Edit an existing .docx (content / formatting)unpack → edit XML → pack workflow below
Add tracked changes / commentsunpack → edit XML → pack workflow below
GB/T 9704 official documentwriteGongwen() (BmacClaw only)

Create a new document (recommended path)

Call the in-process Java tool — no Node.js install, no fork, no disk round-trip:

renderDocx(
  markdown="# Title\n\nBody paragraph...",
  filename="monthly-report",
  pageSize="A4"
)

Returns a clickable link of the form [monthly-report.docx](/api/v1/files/generated/<uuid>) valid for 10 minutes. The user clicks it to download — no follow-up Agent step needed.

renderDocx supports headings (# ## ###), bold (**text**), bullet lists (- item), numbered lists (1. item), pipe-style tables, and plain paragraphs. For images, headers/footers, or precise OOXML control, fall back to the docx-js workflow below.

Prerequisites

  • python-docx (pip install python-docx): direct structure reading and light editing (paragraphs, styles, tables)
  • docx (npm install -g docx): new document creation
  • LibreOffice (soffice): .doc -> .docx conversion, tracked-changes acceptance, and PDF export
  • pandoc: text extraction
  • pdftoppm (poppler-utils): document-to-image workflows
  • If pdftoppm is unavailable, a Python fallback path may use pdf2image.
  • On Windows, dependencies must be installed and available in PATH; if missing, report the dependency issue and stop (do not keep retrying).

Overview

A .docx file is a ZIP archive containing XML files.

Quick Reference

TaskApproach
Read/analyze contentpandoc or unpack for raw XML
Create new documentUse docx-js - see Creating New Documents below
Edit existing documentUnpack → edit XML → repack - see Editing Existing Documents below

Converting .doc to .docx

Legacy .doc files must be converted before editing:

bash
python scripts/office/soffice.py --headless --convert-to docx document.doc

Reading Content

Option A: python-docx (recommended for structured access)

Install: pip install python-docx. Gives direct access to paragraphs, styles, tables, and metadata without unpacking ZIP.

python
from docx import Document

doc = Document("document.docx")

# Paragraphs with styles
for para in doc.paragraphs:
    print(f"[{para.style.name}] {para.text}")

# Tables
for i, table in enumerate(doc.tables):
    print(f"Table {i+1}:")
    for row in table.rows:
        print([cell.text for cell in row.cells])

# Inline styles within a paragraph
for para in doc.paragraphs:
    for run in para.runs:
        print(f"  run: bold={run.bold} italic={run.italic} text={run.text!r}")

Use python-docx when you need to read or lightly modify content. Fall back to the unpack/XML workflow for complex structural changes.

Option B: pandoc (plain text extraction)

bash
# Text extraction with tracked changes
pandoc --track-changes=all document.docx -o output.md

# Raw XML access
python scripts/office/unpack.py document.docx unpacked/

Converting to Images

bash
python scripts/office/soffice.py --headless --convert-to pdf document.docx
pdftoppm -jpeg -r 150 document.pdf page

Accepting Tracked Changes

To produce a clean document with all tracked changes accepted (requires LibreOffice):

bash
python scripts/accept_changes.py input.docx output.docx

Creating New Documents

Generate .docx files with JavaScript, then validate. Install: npm install -g docx

Setup

javascript
const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell, ImageRun,
        Header, Footer, AlignmentType, PageOrientation, LevelFormat, ExternalHyperlink,
        TableOfContents, HeadingLevel, BorderStyle, WidthType, ShadingType,
        VerticalAlign, PageNumber, PageBreak } = require('docx');

const doc = new Document({ sections: [{ children: [/* content */] }] });
Packer.toBuffer(doc).then(buffer => fs.writeFileSync("doc.docx", buffer));

Validation

After creating the file, validate it. If validation fails, unpack, fix the XML, and repack.

bash
python scripts/office/validate.py doc.docx

Page Size

javascript
// CRITICAL: docx-js defaults to A4, not US Letter
// Always set page size explicitly for consistent results
sections: [{
  properties: {
    page: {
      size: {
        width: 12240,   // 8.5 inches in DXA
        height: 15840   // 11 inches in DXA
      },
      margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } // 1 inch margins
    }
  },
  children: [/* content */]
}]

Common page sizes (DXA units, 1440 DXA = 1 inch):

PaperWidthHeightContent Width (1" margins)
US Letter12,24015,8409,360
A4 (default)11,90616,8389,026

Landscape orientation: docx-js swaps width/height internally, so pass portrait dimensions and let it handle the swap:

javascript
size: {
  width: 12240,   // Pass SHORT edge as width
  height: 15840,  // Pass LONG edge as height
  orientation: PageOrientation.LANDSCAPE  // docx-js swaps them in the XML
},
// Content width = 15840 - left margin - right margin (uses the long edge)

Styles (Override Built-in Headings)

Use Arial as the default font (universally supported). Keep titles black for readability.

javascript
const doc = new Document({
  styles: {
    default: { document: { run: { font: "Arial", size: 24 } } }, // 12pt default
    paragraphStyles: [
      // IMPORTANT: Use exact IDs to override built-in styles
      { id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal", quickFormat: true,
        run: { size: 32, bold: true, font: "Arial" },
        paragraph: { spacing: { before: 240, after: 240 }, outlineLevel: 0 } }, // outlineLevel required for TOC
      { id: "Heading2", name: "Heading 2", basedOn: "Normal", next: "Normal", quickFormat: true,
        run: { size: 28, bold: true, font: "Arial" },
        paragraph: { spacing: { before: 180, after: 180 }, outlineLevel: 1 } },
    ]
  },
  sections: [{
    children: [
      new Paragraph({ heading: HeadingLevel.HEADING_1, children: [new TextRun("Title")] }),
    ]
  }]
});

Lists (NEVER use unicode bullets)

javascript
// CORRECT - use numbering config with LevelFormat.BULLET
const doc = new Document({
  numbering: {
    config: [
      { reference: "bullets",
        levels: [{ level: 0, format: LevelFormat.BULLET, text: "\u2022", alignment: AlignmentType.LEFT,
          style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] },
      { reference: "numbers",
        levels: [{ level: 0, format: LevelFormat.DECIMAL, text: "%1.", alignment: AlignmentType.LEFT,
          style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] },
    ]
  },
  sections: [{
    children: [
      new Paragraph({ numbering: { reference: "bullets", level: 0 },
        children: [new TextRun("Bullet item")] }),
      new Paragraph({ numbering: { reference: "numbers", level: 0 },
        children: [new TextRun("Numbered item")] }),
    ]
  }]
});

// Each reference creates INDEPENDENT numbering
// Same reference = continues (1,2,3 then 4,5,6)
// Different reference = restarts (1,2,3 then 1,2,3)

Tables

CRITICAL: Tables need dual widths - set both columnWidths on the table AND width on each cell.

javascript
// CRITICAL: Use ShadingType.CLEAR (not SOLID) to prevent black backgrounds
const border = { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" };
const borders = { top: border, bottom: border, left: border, right: border };

new Table({
  width: { size: 9360, type: WidthType.DXA }, // Always use DXA
  columnWidths: [4680, 4680], // Must sum to table width (DXA: 1440 = 1 inch)
  rows: [
    new TableRow({
      children: [
        new TableCell({
          borders,
          width: { size: 4680, type: WidthType.DXA }, // Also set on each cell
          shading: { fill: "D5E8F0", type: ShadingType.CLEAR }, // CLEAR not SOLID
          margins: { top: 80, bottom: 80, left: 120, right: 120 },
          children: [new Paragraph({ children: [new TextRun("Cell")] })]
        })
      ]
    })
  ]
})

Width rules:

  • Always use WidthType.DXA - never WidthType.PERCENTAGE
  • Table width must equal the sum of columnWidths
  • Cell width must match corresponding columnWidth

Images

javascript
// CRITICAL: type parameter is REQUIRED
new Paragraph({
  children: [new ImageRun({
    type: "png", // Required: png, jpg, jpeg, gif, bmp, svg
    data: fs.readFileSync("image.png"),
    transformation: { width: 200, height: 150 },
    altText: { title: "Title", description: "Desc", name: "Name" } // All three required
  })]
})

Page Breaks

javascript
// PageBreak must be inside a Paragraph
new Paragraph({ children: [new PageBreak()] })

Table of Contents

javascript
// Headings must use HeadingLevel ONLY - no custom styles
new TableOfContents("Table of Contents", { hyperlink: true, headingStyleRange: "1-3" })

Headers/Footers

javascript
sections: [{
  properties: {
    page: { margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } }
  },
  headers: {
    default: new Header({ children: [new Paragraph({ children: [new TextRun("Header")] })] })
  },
  footers: {
    default: new Footer({ children: [new Paragraph({
      children: [new TextRun("Page "), new TextRun({ children: [PageNumber.CURRENT] })]
    })] })
  },
  children: [/* content */]
}]

Critical Rules for docx-js

  • Set page size explicitly - docx-js defaults to A4
  • Landscape: pass portrait dimensions - docx-js swaps width/height internally
  • Never use \n - use separate Paragraph elements
  • Never use unicode bullets - use LevelFormat.BULLET with numbering config
  • PageBreak must be in Paragraph
  • ImageRun requires type
  • Always set table width with DXA - never use WidthType.PERCENTAGE
  • Tables need dual widths - columnWidths array AND cell width
  • Use ShadingType.CLEAR - never SOLID for table shading
  • TOC requires HeadingLevel only
  • Override built-in styles - use exact IDs: "Heading1", "Heading2", etc.
  • Include outlineLevel - required for TOC (0 for H1, 1 for H2, etc.)

Editing Existing Documents

Follow all 3 steps in order.

Step 1: Unpack

bash
python scripts/office/unpack.py document.docx unpacked/

Extracts XML, pretty-prints, merges adjacent runs, and converts smart quotes to XML entities. Use --merge-runs false to skip run merging.

Step 2: Edit XML

Edit files in unpacked/word/. See XML Reference below for patterns.

Use "MateClaw" as the author for tracked changes and comments, unless the user explicitly requests a different name.

CRITICAL: Use smart quotes for new content:

xml
<w:t>Here&#x2019;s a quote: &#x201C;Hello&#x201D;</w:t>
EntityCharacter
&#x2018;' (left single)
&#x2019;' (right single / apostrophe)
&#x201C;" (left double)
&#x201D;" (right double)

Adding comments: Use comment.py to handle boilerplate:

bash
python scripts/comment.py unpacked/ 0 "Comment text with &amp; and &#x2019;"
python scripts/comment.py unpacked/ 1 "Reply text" --parent 0  # reply to comment 0
python scripts/comment.py unpacked/ 0 "Text" --author "Custom Author"

Then add markers to document.xml (see Comments in XML Reference).

Step 3: Pack

bash
python scripts/office/pack.py unpacked/ output.docx --original document.docx

Validates with auto-repair, condenses XML, and creates DOCX. Use --validate false to skip.

Auto-repair will fix:

  • durableId >= 0x7FFFFFFF (regenerates valid ID)
  • Missing xml:space="preserve" on <w:t> with whitespace

Common Pitfalls

  • Replace entire <w:r> elements: When adding tracked changes, replace the whole <w:r>...</w:r> block.
  • Preserve <w:rPr> formatting: Copy the original run's <w:rPr> block into your tracked change runs.

XML Reference

Schema Compliance

  • Element order in <w:pPr>: <w:pStyle>, <w:numPr>, <w:spacing>, <w:ind>, <w:jc>, <w:rPr> last
  • Whitespace: Add xml:space="preserve" to <w:t> with leading/trailing spaces
  • RSIDs: Must be 8-digit hex (e.g., 00AB1234)

Tracked Changes

Insertion:

xml
<w:ins w:id="1" w:author="MateClaw" w:date="2025-01-01T00:00:00Z">
  <w:r><w:t>inserted text</w:t></w:r>
</w:ins>

Deletion:

xml
<w:del w:id="2" w:author="MateClaw" w:date="2025-01-01T00:00:00Z">
  <w:r><w:delText>deleted text</w:delText></w:r>
</w:del>

Inside <w:del>: Use <w:delText> instead of <w:t>, and <w:delInstrText> instead of <w:instrText>.

Minimal edits - only mark what changes:

xml
<!-- Change "30 days" to "60 days" -->
<w:r><w:t>The term is </w:t></w:r>
<w:del w:id="1" w:author="MateClaw" w:date="...">
  <w:r><w:delText>30</w:delText></w:r>
</w:del>
<w:ins w:id="2" w:author="MateClaw" w:date="...">
  <w:r><w:t>60</w:t></w:r>
</w:ins>
<w:r><w:t> days.</w:t></w:r>

Deleting entire paragraphs - mark the paragraph mark as deleted:

xml
<w:p>
  <w:pPr>
    <w:rPr>
      <w:del w:id="1" w:author="MateClaw" w:date="2025-01-01T00:00:00Z"/>
    </w:rPr>
  </w:pPr>
  <w:del w:id="2" w:author="MateClaw" w:date="2025-01-01T00:00:00Z">
    <w:r><w:delText>Entire paragraph content being deleted...</w:delText></w:r>
  </w:del>
</w:p>

Rejecting another author's insertion:

xml
<w:ins w:author="Jane" w:id="5">
  <w:del w:author="MateClaw" w:id="10">
    <w:r><w:delText>their inserted text</w:delText></w:r>
  </w:del>
</w:ins>

Restoring another author's deletion:

xml
<w:del w:author="Jane" w:id="5">
  <w:r><w:delText>deleted text</w:delText></w:r>
</w:del>
<w:ins w:author="MateClaw" w:id="10">
  <w:r><w:t>deleted text</w:t></w:r>
</w:ins>

Comments

After running comment.py, add markers to document.xml:

CRITICAL: <w:commentRangeStart> and <w:commentRangeEnd> are siblings of <w:r>, never inside <w:r>.

xml
<w:commentRangeStart w:id="0"/>
<w:r><w:t>commented text</w:t></w:r>
<w:commentRangeEnd w:id="0"/>
<w:r><w:rPr><w:rStyle w:val="CommentReference"/></w:rPr><w:commentReference w:id="0"/></w:r>

Images

  1. Add image file to word/media/
  2. Add relationship to word/_rels/document.xml.rels:
xml
<Relationship Id="rId5" Type=".../image" Target="media/image1.png"/>
  1. Add content type to [Content_Types].xml:
xml
<Default Extension="png" ContentType="image/png"/>
  1. Reference in document.xml:
xml
<w:drawing>
  <wp:inline>
    <wp:extent cx="914400" cy="914400"/>  <!-- EMUs: 914400 = 1 inch -->
    <a:graphic>
      <a:graphicData uri=".../picture">
        <pic:pic>
          <pic:blipFill><a:blip r:embed="rId5"/></pic:blipFill>
        </pic:pic>
      </a:graphicData>
    </a:graphic>
  </wp:inline>
</w:drawing>

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Docx AI skill do?

Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx files). For CREATING new documents (report, memo, letter, résumé, contract from scratch), call the built-in tool `renderDocx` instead — it renders Markdown to .docx in milliseconds without forking Node.js. This skill remains authoritative for EDITING existing .docx (unpack/edit XML/pack), tracked changes, comments, image manipulation, find-and-replace, and conversions. Triggers include: "Word doc", "word document", ".docx", or requests to edit/extract/restructure existing Word files. Do NOT use...

Why use Docx on TypingMind?

Because you install it once and use it with any model. Docx is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Docx in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/mateaix/mateclaw/tree/dev/mateclaw-server/src/main/resources/skills/docx. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Docx?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Docx?

As many as you like. As long as a model supports skills, you can use Docx with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Docx AI skill free?

Yes. It is published on GitHub by mateaix under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇