
Docx
- 261 installs
- 70 repo stars
- Updated July 26, 2026
- rysweet/amplihack
docx is an agent skill that creates, edits, and analyzes Microsoft Word .docx files with tracked changes, comments, and OOXML workflows for developers who must deliver branded Word reports instead of markdown.
About
docx is an agent skill in rysweet/amplihack for comprehensive Word document creation, editing, and analysis. It treats .docx files as ZIP archives of OOXML XML, routing new documents through docx-js JavaScript generation and edits through Python Document library scripts with unpack.py and pack.py helpers. Reading uses pandoc --track-changes=all for markdown extraction; the redlining workflow batches tracked changes in groups of three to ten edits, preserving RSIDs and minimal precise replacements in word/document.xml. Reference guides docx-js.md (~500 lines) and ooxml.md (~600 lines) define formatting rules agents must read fully before generation or OOXML edits. Developers reach for docx when stakeholders need .docx reports, specs, memos, or contract redlines from amplihack or agent outputs rather than plain markdown, especially for legal, academic, or business documents requiring tracked changes.
- Generates and edits structured .docx files
- Applies headings, tables, and house styles
- Merges technical content into report templates
- Supports stakeholder-ready spec and memo exports
- Round-trips edits without losing formatting
Docx by the numbers
- 261 all-time installs (skills.sh)
- +1 installs in the week ending Jul 26, 2026 (Skillselion tracking)
- Ranked #201 of 687 Office & Documents skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/rysweet/amplihack --skill docxAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 261 |
|---|---|
| repo stars | ★ 70 |
| Last updated | July 26, 2026 |
| Repository | rysweet/amplihack ↗ |
How do you create and edit Word docx files programmatically?
Create or edit Word deliverables—reports, specs, memos, templates—from amplihack outputs when stakeholders need branded .docx instead of markdown or plain text.
Who is it for?
Developers converting agent or markdown outputs into professional Word deliverables with tracked-change review workflows.
Skip if: Teams needing only PDF exports, Google Docs collaboration, or simple plain-text memos without OOXML complexity.
When should I use this skill?
User asks to create, edit, redline, or extract content from .docx Word files, reports, specs, or contract templates.
What you get
Generated or edited .docx files, pandoc markdown extractions, and redlined documents with tracked changes.
- Generated .docx files
- Redlined documents with tracked changes
- Markdown extractions from Word sources
By the numbers
- Redlining workflow batches tracked changes in groups of 3-10 edits
- Reference guides include docx-js.md (~500 lines) and ooxml.md (~600 lines)
Files
DOCX creation, editing, and analysis
Overview
A user may ask you to create, edit, or analyze the contents of a .docx file. A .docx file is essentially a ZIP archive containing XML files and other resources that you can read or edit. You have different tools and workflows available for different tasks.
Workflow Decision Tree
Reading/Analyzing Content
Use "Text extraction" or "Raw XML access" sections below
Creating New Document
Use "Creating a new Word document" workflow
Editing Existing Document
- Your own document + simple changes
Use "Basic OOXML editing" workflow
- Someone else's document
Use "Redlining workflow" (recommended default)
- Legal, academic, business, or government docs
Use "Redlining workflow" (required)
Reading and analyzing content
Text extraction
If you just need to read the text contents of a document, you should convert the document to markdown using pandoc. Pandoc provides excellent support for preserving document structure and can show tracked changes:
# Convert document to markdown with tracked changes
pandoc --track-changes=all path-to-file.docx -o output.md
# Options: --track-changes=accept/reject/allRaw XML access
You need raw XML access for: comments, complex formatting, document structure, embedded media, and metadata. For any of these features, you'll need to unpack a document and read its raw XML contents.
Unpacking a file
python ooxml/scripts/unpack.py <office_file> <output_directory>
Key file structures
word/document.xml- Main document contentsword/comments.xml- Comments referenced in document.xmlword/media/- Embedded images and media files- Tracked changes use
<w:ins>(insertions) and<w:del>(deletions) tags
Creating a new Word document
When creating a new Word document from scratch, use docx-js, which allows you to create Word documents using JavaScript/TypeScript.
Workflow
1. MANDATORY - READ ENTIRE FILE: Read `docx-js.md` (~500 lines) completely from start to finish. NEVER set any range limits when reading this file. Read the full file content for detailed syntax, critical formatting rules, and best practices before proceeding with document creation. 2. Create a JavaScript/TypeScript file using Document, Paragraph, TextRun components (You can assume all dependencies are installed, but if not, refer to the dependencies section below) 3. Export as .docx using Packer.toBuffer()
Editing an existing Word document
When editing an existing Word document, use the Document library (a Python library for OOXML manipulation). The library automatically handles infrastructure setup and provides methods for document manipulation. For complex scenarios, you can access the underlying DOM directly through the library.
Workflow
1. MANDATORY - READ ENTIRE FILE: Read `ooxml.md` (~600 lines) completely from start to finish. NEVER set any range limits when reading this file. Read the full file content for the Document library API and XML patterns for directly editing document files. 2. Unpack the document: python ooxml/scripts/unpack.py <office_file> <output_directory> 3. Create and run a Python script using the Document library (see "Document Library" section in ooxml.md) 4. Pack the final document: python ooxml/scripts/pack.py <input_directory> <office_file>
The Document library provides both high-level methods for common operations and direct DOM access for complex scenarios.
Redlining workflow for document review
This workflow allows you to plan comprehensive tracked changes using markdown before implementing them in OOXML. CRITICAL: For complete tracked changes, you must implement ALL changes systematically.
Batching Strategy: Group related changes into batches of 3-10 changes. This makes debugging manageable while maintaining efficiency. Test each batch before moving to the next.
Principle: Minimal, Precise Edits When implementing tracked changes, only mark text that actually changes. Repeating unchanged text makes edits harder to review and appears unprofessional. Break replacements into: [unchanged text] + [deletion] + [insertion] + [unchanged text]. Preserve the original run's RSID for unchanged text by extracting the <w:r> element from the original and reusing it.
Example - Changing "30 days" to "60 days" in a sentence:
# BAD - Replaces entire sentence
'<w:del><w:r><w:delText>The term is 30 days.</w:delText></w:r></w:del><w:ins><w:r><w:t>The term is 60 days.</w:t></w:r></w:ins>'
# GOOD - Only marks what changed, preserves original <w:r> for unchanged text
'<w:r w:rsidR="00AB12CD"><w:t>The term is </w:t></w:r><w:del><w:r><w:delText>30</w:delText></w:r></w:del><w:ins><w:r><w:t>60</w:t></w:r></w:ins><w:r w:rsidR="00AB12CD"><w:t> days.</w:t></w:r>'Tracked changes workflow
1. Get markdown representation: Convert document to markdown with tracked changes preserved:
pandoc --track-changes=all path-to-file.docx -o current.md2. Identify and group changes: Review the document and identify ALL changes needed, organizing them into logical batches:
Location methods (for finding changes in XML):
- Section/heading numbers (e.g., "Section 3.2", "Article IV")
- Paragraph identifiers if numbered
- Grep patterns with unique surrounding text
- Document structure (e.g., "first paragraph", "signature block")
- DO NOT use markdown line numbers - they don't map to XML structure
Batch organization (group 3-10 related changes per batch):
- By section: "Batch 1: Section 2 amendments", "Batch 2: Section 5 updates"
- By type: "Batch 1: Date corrections", "Batch 2: Party name changes"
- By complexity: Start with simple text replacements, then tackle complex structural changes
- Sequential: "Batch 1: Pages 1-3", "Batch 2: Pages 4-6"
3. Read documentation and unpack:
- MANDATORY - READ ENTIRE FILE: Read `ooxml.md` (~600 lines) completely from start to finish. NEVER set any range limits when reading this file. Pay special attention to the "Document Library" and "Tracked Change Patterns" sections.
- Unpack the document:
python ooxml/scripts/unpack.py <file.docx> <dir> - Note the suggested RSID: The unpack script will suggest an RSID to use for your tracked changes. Copy this RSID for use in step 4b.
4. Implement changes in batches: Group changes logically (by section, by type, or by proximity) and implement them together in a single script. This approach:
- Makes debugging easier (smaller batch = easier to isolate errors)
- Allows incremental progress
- Maintains efficiency (batch size of 3-10 changes works well)
Suggested batch groupings:
- By document section (e.g., "Section 3 changes", "Definitions", "Termination clause")
- By change type (e.g., "Date changes", "Party name updates", "Legal term replacements")
- By proximity (e.g., "Changes on pages 1-3", "Changes in first half of document")
For each batch of related changes:
a. Map text to XML: Grep for text in word/document.xml to verify how text is split across <w:r> elements.
b. Create and run script: Use get_node to find nodes, implement changes, then doc.save(). See "Document Library" section in ooxml.md for patterns.
Note: Always grep word/document.xml immediately before writing a script to get current line numbers and verify text content. Line numbers change after each script run.
5. Pack the document: After all batches are complete, convert the unpacked directory back to .docx:
python ooxml/scripts/pack.py unpacked reviewed-document.docx6. Final verification: Do a comprehensive check of the complete document:
- Convert final document to markdown:
pandoc --track-changes=all reviewed-document.docx -o verification.md- Verify ALL changes were applied correctly:
grep "original phrase" verification.md # Should NOT find it
grep "replacement phrase" verification.md # Should find it- Check that no unintended changes were introduced
Converting Documents to Images
To visually analyze Word documents, convert them to images using a two-step process:
1. Convert DOCX to PDF:
soffice --headless --convert-to pdf document.docx2. Convert PDF pages to JPEG images:
pdftoppm -jpeg -r 150 document.pdf pageThis creates files like page-1.jpg, page-2.jpg, etc.
Options:
-r 150: Sets resolution to 150 DPI (adjust for quality/size balance)-jpeg: Output JPEG format (use-pngfor PNG if preferred)-f N: First page to convert (e.g.,-f 2starts from page 2)-l N: Last page to convert (e.g.,-l 5stops at page 5)page: Prefix for output files
Example for specific range:
pdftoppm -jpeg -r 150 -f 2 -l 5 document.pdf page # Converts only pages 2-5Code Style Guidelines
IMPORTANT: When generating code for DOCX operations:
- Write concise code
- Avoid verbose variable names and redundant operations
- Avoid unnecessary print statements
Dependencies
Required dependencies (install if not available):
- pandoc:
sudo apt-get install pandoc(for text extraction) - docx:
npm install -g docx(for creating new documents) - LibreOffice:
sudo apt-get install libreoffice(for PDF conversion) - Poppler:
sudo apt-get install poppler-utils(for pdftoppm to convert PDF to images) - defusedxml:
pip install defusedxml(for secure XML parsing)
Dependencies for DOCX Skill
Overview
The DOCX skill requires Python packages for OOXML manipulation, Node.js packages for document creation, and system packages for text extraction and validation. This document provides complete installation instructions for all dependencies.
Dependency Categories
Required (Core Functionality)
These packages are required for basic DOCX skill functionality:
Python Packages:
defusedxml>=0.7.0- Secure XML parsing for OOXML operationspytest>=7.0.0- Testing framework for skill verification
System Packages:
pandoc- Document conversion and text extractionLibreOffice (soffice)- Document validation and PDF conversion
Optional (Enhanced Functionality)
These packages enable additional features but the skill works without them:
Node Packages:
docx- Creating new Word documents from JavaScript/TypeScript
System Packages:
poppler-utils- PDF to image conversion (pdftoppm)
Installation Instructions
Quick Install (Required Only)
Install core packages for basic DOCX functionality:
# Python packages
pip install defusedxml pytest
# System packages (Ubuntu/Debian)
sudo apt-get install pandoc libreoffice
# System packages (macOS)
brew install pandoc libreofficeComplete Install (All Features)
Install all packages for full functionality:
# Python packages
pip install defusedxml pytest
# Node packages
npm install -g docx
# System packages (see platform-specific instructions below)Platform-Specific Installation
macOS
# Install Homebrew if not already installed
# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Python packages
pip install defusedxml pytest
# System packages
brew install pandoc libreoffice poppler
# Node packages (optional)
npm install -g docxUbuntu/Debian Linux
# Python packages
pip install defusedxml pytest
# System packages
sudo apt-get update
sudo apt-get install -y pandoc libreoffice poppler-utils
# Node packages (optional)
# Install Node.js first if not available
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs
npm install -g docxFedora/RHEL/CentOS
# Python packages
pip install defusedxml pytest
# System packages
sudo dnf install -y pandoc libreoffice poppler-utils
# Node packages (optional)
# Install Node.js first if not available
curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash -
sudo dnf install -y nodejs
npm install -g docxWindows
# Python packages
pip install defusedxml pytest
# System packages via Chocolatey (recommended)
choco install pandoc libreoffice poppler
# Or download manually:
# - Pandoc: https://pandoc.org/installing.html
# - LibreOffice: https://www.libreoffice.org/download/
# - Poppler: https://github.com/oschwartz10612/poppler-windows/releases
# Node packages (optional)
npm install -g docx
# Add installation directories to PATH environment variableVerification
Verify installations with these commands:
Python Packages
# Check defusedxml
python -c "import defusedxml; print('defusedxml installed')"
# Check pytest
pytest --versionSystem Packages
# Check pandoc
pandoc --version
# Check LibreOffice
soffice --version
# Check poppler-utils (optional)
pdftoppm -vNode Packages
# Check Node.js
node --version
# Check docx package (optional)
npm list -g docxAutomated Verification
Use the pytest test suite to check all dependencies:
cd .claude/skills/docx
python tests/test_docx_skill.pyThis will run comprehensive dependency checks and display a detailed report showing which packages are installed and which are missing. Tests will skip gracefully if optional dependencies are unavailable.
Dependency Details
pytest
Purpose: Testing framework for verifying skill functionality
Capabilities:
- Unit testing for DOCX skill components
- Integration testing for workflows
- Dependency verification tests
- Automated test discovery and execution
- Flexible test fixtures and parametrization
Use in DOCX skill:
- Verify skill dependencies are installed
- Test OOXML manipulation functions
- Validate tracked changes workflows
- Ensure skill integration with Claude Code
Version Requirements: pytest>=7.0.0
License: MIT
Documentation: https://docs.pytest.org/
defusedxml
Purpose: Secure XML parsing for OOXML operations
Capabilities:
- Safe XML parsing that prevents XML bombs and entity expansion attacks
- Drop-in replacement for standard xml.dom.minidom
- Required for OOXML unpack/pack scripts
- Essential for all document manipulation
Security: Protects against XML-based attacks (XXE, billion laughs, quadratic blowup)
License: Python Software Foundation License
Documentation: https://github.com/tiran/defusedxml
pandoc
Purpose: Universal document converter and text extraction tool
Capabilities:
- Convert DOCX to markdown with structure preservation
- Support for tracked changes (--track-changes flag)
- Extract text while maintaining formatting information
- Convert between 40+ document formats
- Preserve document structure (headings, lists, tables)
Use in DOCX skill:
- Text extraction:
pandoc document.docx -o output.md - Tracked changes:
pandoc --track-changes=all document.docx -o output.md - Document analysis and verification
License: GPL
Documentation: https://pandoc.org/
LibreOffice (soffice)
Purpose: Office suite for document validation and conversion
Capabilities:
- Validate DOCX files (detect corruption)
- Convert DOCX to PDF for image export
- Headless mode for automated processing
- Support for all Office formats
Use in DOCX skill:
- Pack script validation: Verifies document integrity after OOXML edits
- PDF conversion:
soffice --headless --convert-to pdf document.docx - Ensures edited documents open correctly in Word
License: Mozilla Public License 2.0
Documentation: https://www.libreoffice.org/
docx (Node Package, Optional)
Purpose: Create Word documents programmatically using JavaScript/TypeScript
Capabilities:
- Create new DOCX files from scratch
- Rich formatting (bold, italic, colors, fonts)
- Tables, sections, headers, footers
- Images and embedded media
- Paragraph and document styling
Use in DOCX skill:
- Creating new documents from scratch
- JavaScript/TypeScript-based document generation
- Alternative to Python-based OOXML manipulation
License: MIT
Documentation: https://docx.js.org/
poppler-utils (Optional System Package)
Purpose: PDF manipulation and conversion tools
Capabilities:
pdftoppm: Convert PDF pages to images (JPEG, PNG)pdftotext: Extract text from PDFspdfinfo: Display PDF metadata
Use in DOCX skill:
- Visual analysis: Convert DOCX → PDF → Images for review
- Two-step workflow: soffice (DOCX→PDF) + pdftoppm (PDF→images)
License: GPL
Documentation: https://poppler.freedesktop.org/
Troubleshooting
ImportError: No module named 'defusedxml'
Solution: Install defusedxml
pip install defusedxmlImportError: No module named 'pytest'
Solution: Install pytest
pip install pytestCommand not found: pandoc
Solution: Install pandoc
# macOS
brew install pandoc
# Ubuntu/Debian
sudo apt-get install pandoc
# Windows
choco install pandocCommand not found: soffice
Solution: Install LibreOffice
# macOS
brew install --cask libreoffice
# Ubuntu/Debian
sudo apt-get install libreoffice
# Windows
choco install libreofficePack script validation fails
Solution: Either install LibreOffice or use --force flag
# Install LibreOffice (recommended)
brew install libreoffice # macOS
sudo apt-get install libreoffice # Ubuntu
# Or skip validation (not recommended)
python ooxml/scripts/pack.py unpacked/ output.docx --forcedocx package not found (npm)
Solution: Install Node.js and docx package
# Install Node.js first
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo bash -
sudo apt-get install nodejs
# Install docx package
npm install -g docxpdftoppm not found
Solution: Install poppler-utils
# macOS
brew install poppler
# Ubuntu/Debian
sudo apt-get install poppler-utils
# Windows
choco install popplerPermission denied errors
Solution: Use pip with --user flag or virtual environment
pip install --user defusedxmlVersion conflicts
Solution: Use virtual environment for isolation
python -m venv docx_skill_env
source docx_skill_env/bin/activate # Linux/macOS
# or
docx_skill_env\Scripts\activate # Windows
pip install defusedxmlMinimal Installation
For testing or minimal functionality:
# Absolute minimum (text extraction only)
brew install pandoc # macOS
sudo apt-get install pandoc # Ubuntu
# Recommended minimum (text extraction + OOXML editing + testing)
pip install defusedxml pytest
brew install pandoc libreoffice # macOS
sudo apt-get install pandoc libreoffice # UbuntuDocker Installation
For containerized environments:
FROM python:3.11-slim
# Install system dependencies
RUN apt-get update && apt-get install -y \
pandoc \
libreoffice \
poppler-utils \
nodejs \
npm \
&& rm -rf /var/lib/apt/lists/*
# Install Python packages
RUN pip install --no-cache-dir defusedxml pytest
# Install Node packages (optional)
RUN npm install -g docxCI/CD Considerations
For GitHub Actions or other CI environments:
# .github/workflows/test.yml
- name: Install DOCX skill dependencies
run: |
pip install defusedxml pytest
sudo apt-get update
sudo apt-get install -y pandoc libreoffice poppler-utils
npm install -g docxNote: Tests should skip gracefully if optional dependencies are missing.
Upgrading Dependencies
To upgrade to latest versions:
# Upgrade Python packages
pip install --upgrade defusedxml pytest
# Upgrade system packages
brew upgrade pandoc libreoffice poppler # macOS
sudo apt-get update && sudo apt-get upgrade pandoc libreoffice poppler-utils # Ubuntu
# Upgrade Node packages
npm update -g docxDependency Licenses Summary
| Package | License | Commercial Use |
|---|---|---|
| pytest | MIT | Yes |
| defusedxml | PSF | Yes |
| pandoc | GPL | Yes (linking allowed) |
| LibreOffice | MPL 2.0 | Yes |
| docx (npm) | MIT | Yes |
| poppler-utils | GPL | Yes (linking allowed) |
All dependencies are permissive licenses compatible with commercial use.
Shared Dependencies
The DOCX skill shares some dependencies with other Office skills:
Shared with PPTX:
- defusedxml (OOXML parsing)
- LibreOffice (validation)
Shared with PDF:
- poppler-utils (PDF processing)
See ~/.amplihack/.claude/skills/common/dependencies.txt for complete shared dependency information.
Advanced Installation
Custom LibreOffice Path
If LibreOffice is installed in a non-standard location:
# Set custom soffice path
export SOFFICE_PATH="/custom/path/to/soffice"
# Or edit pack.py to use custom pathSpecific Package Versions
For reproducible environments:
# Python packages with versions
pip install defusedxml==0.7.1 pytest==7.4.3
# Node packages with versions
npm install -g docx@8.5.0Offline Installation
For air-gapped environments:
# Download packages
pip download defusedxml pytest -d ./packages
# Install offline
pip install --no-index --find-links=./packages defusedxml pytest---
Last Updated: 2025-11-08 Maintained By: amplihack project
DOCX Skill Usage Examples
Overview
This document provides practical examples of using the DOCX skill in Claude Code conversations. These examples demonstrate common workflows including document creation, tracked changes (redlining), text extraction, and OOXML manipulation.
Example 1: Create a Business Proposal Document
Scenario
Create a professional business proposal with title page, executive summary, sections, and a pricing table.
Conversation
User: Create a business proposal document for a software development project with title page, executive summary, scope, timeline, and pricing table
Claude: I'll create a structured business proposal using docx-js.
import {
Document,
Paragraph,
TextRun,
Table,
TableCell,
TableRow,
Packer,
AlignmentType,
BorderStyle,
} from "docx";
import * as fs from "fs";
const doc = new Document({
sections: [
{
properties: {},
children: [
// Title Page
new Paragraph({
children: [
new TextRun({
text: "SOFTWARE DEVELOPMENT PROPOSAL",
bold: true,
size: 32,
}),
],
alignment: AlignmentType.CENTER,
spacing: { after: 400 },
}),
new Paragraph({
children: [new TextRun({ text: "Client: Acme Corporation", size: 24 })],
alignment: AlignmentType.CENTER,
spacing: { after: 200 },
}),
new Paragraph({
children: [new TextRun({ text: "Date: November 8, 2025", size: 20 })],
alignment: AlignmentType.CENTER,
spacing: { after: 800 },
}),
// Executive Summary
new Paragraph({
children: [new TextRun({ text: "Executive Summary", bold: true, size: 28 })],
spacing: { before: 400, after: 200 },
}),
new Paragraph({
children: [
new TextRun({
text: "This proposal outlines a comprehensive software development project to build a modern web application. The project will deliver a scalable, secure platform within 12 weeks.",
}),
],
spacing: { after: 200 },
}),
// Project Scope
new Paragraph({
children: [new TextRun({ text: "Project Scope", bold: true, size: 28 })],
spacing: { before: 400, after: 200 },
}),
new Paragraph({
children: [new TextRun({ text: "1. User Authentication System", bold: true })],
spacing: { after: 100 },
}),
new Paragraph({
children: [new TextRun({ text: " - OAuth 2.0 integration" })],
spacing: { after: 100 },
}),
new Paragraph({
children: [new TextRun({ text: " - Role-based access control" })],
spacing: { after: 100 },
}),
new Paragraph({
children: [new TextRun({ text: "2. Database Architecture", bold: true })],
spacing: { after: 100 },
}),
new Paragraph({
children: [new TextRun({ text: " - PostgreSQL backend" })],
spacing: { after: 100 },
}),
new Paragraph({
children: [new TextRun({ text: " - Optimized queries and indexing" })],
spacing: { after: 200 },
}),
// Timeline
new Paragraph({
children: [new TextRun({ text: "Project Timeline", bold: true, size: 28 })],
spacing: { before: 400, after: 200 },
}),
new Paragraph({
children: [new TextRun({ text: "Total Duration: 12 weeks" })],
spacing: { after: 200 },
}),
// Pricing Table
new Paragraph({
children: [new TextRun({ text: "Pricing Breakdown", bold: true, size: 28 })],
spacing: { before: 400, after: 200 },
}),
new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [
new Paragraph({ children: [new TextRun({ text: "Phase", bold: true })] }),
],
}),
new TableCell({
children: [
new Paragraph({ children: [new TextRun({ text: "Duration", bold: true })] }),
],
}),
new TableCell({
children: [
new Paragraph({ children: [new TextRun({ text: "Cost", bold: true })] }),
],
}),
],
}),
new TableRow({
children: [
new TableCell({ children: [new Paragraph("Discovery & Planning")] }),
new TableCell({ children: [new Paragraph("2 weeks")] }),
new TableCell({ children: [new Paragraph("$15,000")] }),
],
}),
new TableRow({
children: [
new TableCell({ children: [new Paragraph("Development")] }),
new TableCell({ children: [new Paragraph("8 weeks")] }),
new TableCell({ children: [new Paragraph("$60,000")] }),
],
}),
new TableRow({
children: [
new TableCell({ children: [new Paragraph("Testing & Deployment")] }),
new TableCell({ children: [new Paragraph("2 weeks")] }),
new TableCell({ children: [new Paragraph("$15,000")] }),
],
}),
new TableRow({
children: [
new TableCell({
children: [
new Paragraph({ children: [new TextRun({ text: "Total", bold: true })] }),
],
}),
new TableCell({
children: [
new Paragraph({ children: [new TextRun({ text: "12 weeks", bold: true })] }),
],
}),
new TableCell({
children: [
new Paragraph({ children: [new TextRun({ text: "$90,000", bold: true })] }),
],
}),
],
}),
],
}),
],
},
],
});
const buffer = await Packer.toBuffer(doc);
fs.writeFileSync("business_proposal.docx", buffer);
console.log("Created business_proposal.docx");Expected Output
- Professional
business_proposal.docxfile - Formatted title page with proper alignment
- Structured sections with headings
- Pricing table with borders and formatting
- Ready for review and customization
---
Example 2: Extract Text with Tracked Changes
Scenario
Review a contract that has tracked changes and extract all text including insertions and deletions.
Conversation
User: Extract text from contract.docx showing all tracked changes
Claude: I'll use pandoc to convert the document with all tracked changes preserved.
# Extract with all tracked changes visible
pandoc --track-changes=all contract.docx -o contract_with_changes.md
# View the markdown
cat contract_with_changes.mdAlternative formats:
# Accept all changes
pandoc --track-changes=accept contract.docx -o contract_clean.md
# Reject all changes
pandoc --track-changes=reject contract.docx -o contract_original.md
# Extract to plain text
pandoc contract.docx -o contract.txtExpected Output
- Markdown file with tracked changes formatted as strikethrough and underline
- Insertions shown as additions
- Deletions shown as strikethrough
- Structure preserved (headings, lists, tables)
---
Example 3: Implement Contract Redlines (Tracked Changes)
Scenario
Legal document needs systematic review with tracked changes. Change "30 days" to "60 days" throughout, update party names, and modify payment terms.
Conversation
User: Review contract.docx and implement these changes with tracking: 1) Change all "30 days" to "60 days", 2) Update "Vendor Corp" to "Supplier LLC", 3) Change payment terms from net-30 to net-60
Claude: I'll implement these changes using the redlining workflow with batching.
Step 1: Convert to markdown and analyze
pandoc --track-changes=all contract.docx -o current.md
cat current.mdStep 2: Identify changes and create batches
Batch 1: Timeline changes (30 → 60 days)
- Section 2.1: "delivered within 30 days"
- Section 5.3: "terminate with 30 days notice"
- Section 7.2: "respond within 30 days"
Batch 2: Party name updates
- Preamble: "Vendor Corp"
- Throughout document: "Vendor Corp" → "Supplier LLC"
Batch 3: Payment terms
- Section 4.1: "net-30" → "net-60"
- Section 4.2: "30 days from invoice" → "60 days from invoice"
Step 3: Unpack document
python ooxml/scripts/unpack.py contract.docx unpacked/
# Note the suggested RSID: 00AB12CDStep 4: Implement Batch 1 (Timeline changes)
# Find occurrences in XML
grep -n "30 days" unpacked/word/document.xml# batch1_timeline.py
from defusedxml import minidom
doc = minidom.parse("unpacked/word/document.xml")
RSID = "00AB12CD" # From unpack script
# Find and replace "30 days" with tracked changes
# Using minimal edits principle - only mark changed text
for text_node in doc.getElementsByTagName("w:t"):
if text_node.firstChild and "30 days" in text_node.firstChild.nodeValue:
parent = text_node.parentNode # <w:r>
paragraph = parent.parentNode # <w:p>
# Split: "delivered within 30 days" → "delivered within " + DEL"30" + INS"60" + " days"
original_text = text_node.firstChild.nodeValue
before = original_text.split("30 days")[0]
after = original_text.split("30 days")[1]
# Create replacement runs with minimal edits
# Unchanged text before
if before:
unchanged_before = doc.createElement("w:r")
unchanged_before.setAttribute("w:rsidR", parent.getAttribute("w:rsidR"))
t_before = doc.createElement("w:t")
t_before.appendChild(doc.createTextNode(before))
unchanged_before.appendChild(t_before)
# Deletion: "30"
del_run = doc.createElement("w:del")
del_run.setAttribute("w:id", "1")
del_run.setAttribute("w:author", "Claude")
del_r = doc.createElement("w:r")
del_t = doc.createElement("w:delText")
del_t.appendChild(doc.createTextNode("30"))
del_r.appendChild(del_t)
del_run.appendChild(del_r)
# Insertion: "60"
ins_run = doc.createElement("w:ins")
ins_run.setAttribute("w:id", "2")
ins_run.setAttribute("w:author", "Claude")
ins_r = doc.createElement("w:r")
ins_r.setAttribute("w:rsidR", RSID)
ins_t = doc.createElement("w:t")
ins_t.appendChild(doc.createTextNode("60"))
ins_r.appendChild(ins_t)
ins_run.appendChild(ins_r)
# Unchanged text after
if after or " days":
unchanged_after = doc.createElement("w:r")
unchanged_after.setAttribute("w:rsidR", parent.getAttribute("w:rsidR"))
t_after = doc.createElement("w:t")
t_after.appendChild(doc.createTextNode(" days" + after))
unchanged_after.appendChild(t_after)
# Replace in paragraph
paragraph.removeChild(parent)
if before:
paragraph.insertBefore(unchanged_before, None)
paragraph.insertBefore(del_run, None)
paragraph.insertBefore(ins_run, None)
paragraph.insertBefore(unchanged_after, None)
with open("unpacked/word/document.xml", "wb") as f:
f.write(doc.toxml(encoding="UTF-8"))
print("Batch 1 complete: Timeline changes")Step 5: Verify Batch 1
python ooxml/scripts/pack.py unpacked/ contract_batch1.docx
pandoc --track-changes=all contract_batch1.docx -o verify1.md
grep "60 days" verify1.md # Should find changesStep 6: Implement remaining batches
(Similar process for Batch 2 and Batch 3...)
Step 7: Final pack and verification
python ooxml/scripts/pack.py unpacked/ contract_reviewed.docx
pandoc --track-changes=all contract_reviewed.docx -o final_verification.md
# Verify all changes
grep "60 days" final_verification.md
grep "Supplier LLC" final_verification.md
grep "net-60" final_verification.mdExpected Output
contract_reviewed.docxwith systematic tracked changes- All changes batched logically for easier debugging
- Minimal edits (only changed text marked)
- Original formatting preserved for unchanged text
- Ready for legal review
---
Example 4: Extract Document Comments
Scenario
A reviewed document has comments that need to be extracted and analyzed.
Conversation
User: Extract all comments from reviewed_document.docx
Claude: I'll unpack the document and read the comments XML.
# Unpack document
python ooxml/scripts/unpack.py reviewed_document.docx unpacked/
# Read comments
cat unpacked/word/comments.xmlPython script for structured extraction:
from defusedxml import minidom
doc = minidom.parse("unpacked/word/comments.xml")
comments = []
for comment in doc.getElementsByTagName("w:comment"):
comment_id = comment.getAttribute("w:id")
author = comment.getAttribute("w:author")
date = comment.getAttribute("w:date")
# Extract comment text
text_parts = []
for t in comment.getElementsByTagName("w:t"):
if t.firstChild:
text_parts.append(t.firstChild.nodeValue)
text = "".join(text_parts)
comments.append({
"id": comment_id,
"author": author,
"date": date,
"text": text
})
# Print structured comments
for c in comments:
print(f"Comment {c['id']} by {c['author']} on {c['date']}:")
print(f" {c['text']}")
print()Expected Output
- List of all comments with metadata
- Comment ID, author, date, and text content
- Structured format for analysis
---
Example 5: Convert Document to Images for Visual Review
Scenario
Need to visually review a document as images for presentation or markup.
Conversation
User: Convert contract.docx to JPEG images for each page
Claude: I'll use a two-step process: DOCX → PDF → images.
# Step 1: Convert DOCX to PDF
soffice --headless --convert-to pdf contract.docx
# Step 2: Convert PDF to JPEG images (150 DPI)
pdftoppm -jpeg -r 150 contract.pdf page
# List generated images
ls -lh page-*.jpgFor specific pages or higher quality:
# High quality (300 DPI)
pdftoppm -jpeg -r 300 contract.pdf page
# Specific page range (pages 2-5)
pdftoppm -jpeg -r 150 -f 2 -l 5 contract.pdf page
# PNG format instead of JPEG
pdftoppm -png -r 150 contract.pdf pageExpected Output
page-1.jpg,page-2.jpg, etc. for each document page- JPEG images suitable for visual review and annotation
- Adjustable quality based on DPI setting
---
Example 6: Batch Process Multiple Documents
Scenario
Extract text from all Word documents in a directory for analysis.
Conversation
User: Extract text from all .docx files in the contracts/ directory
Claude: I'll batch process all documents with pandoc.
# Create output directory
mkdir -p extracted_text
# Process all DOCX files
for file in contracts/*.docx; do
filename=$(basename "$file" .docx)
echo "Processing $filename..."
pandoc "$file" -o "extracted_text/${filename}.txt"
done
# Create index
ls -1 extracted_text/*.txt > extracted_text/index.txt
echo "Extracted $(ls -1 extracted_text/*.txt | wc -l) documents"With tracked changes:
for file in contracts/*.docx; do
filename=$(basename "$file" .docx)
pandoc --track-changes=all "$file" -o "extracted_text/${filename}_with_changes.md"
doneExpected Output
- Text files for each Word document
- Organized in
extracted_text/directory - Index file listing all extracted documents
- Progress output for each file
---
Example 7: Add Header and Footer to Document
Scenario
Need to add page numbers and company logo to document headers/footers.
Conversation
User: Add page numbers to the footer and company name to the header of report.docx
Claude: I'll create a document with headers and footers using docx-js.
import {
Document,
Paragraph,
TextRun,
Header,
Footer,
PageNumber,
AlignmentType,
Packer,
} from "docx";
import * as fs from "fs";
const doc = new Document({
sections: [
{
headers: {
default: new Header({
children: [
new Paragraph({
children: [
new TextRun({
text: "Acme Corporation - Confidential",
bold: true,
}),
],
alignment: AlignmentType.CENTER,
}),
],
}),
},
footers: {
default: new Footer({
children: [
new Paragraph({
children: [
new TextRun({ text: "Page " }),
PageNumber.CURRENT,
new TextRun({ text: " of " }),
PageNumber.TOTAL_PAGES,
],
alignment: AlignmentType.CENTER,
}),
],
}),
},
children: [
new Paragraph({
children: [new TextRun({ text: "Document content here...", size: 24 })],
}),
],
},
],
});
const buffer = await Packer.toBuffer(doc);
fs.writeFileSync("report_with_header_footer.docx", buffer);Expected Output
- Document with header showing company name
- Footer with page numbers (Page X of Y)
- Headers/footers appear on all pages
---
Example 8: Extract and Analyze Document Structure
Scenario
Need to understand document structure (sections, headings, lists) for reorganization.
Conversation
User: Analyze the structure of proposal.docx and show me all headings
Claude: I'll convert to markdown which preserves document structure.
# Convert to markdown (preserves headings, lists, tables)
pandoc proposal.docx -o proposal_structure.md
# Extract just headings
grep "^#" proposal_structure.md
# Or more detailed with levels
echo "Document Structure:"
grep "^#" proposal_structure.md | while read -r line; do
level=$(echo "$line" | grep -o "^#*" | wc -c)
heading=$(echo "$line" | sed 's/^#* //')
indent=$(printf "%$((($level-1)*2))s")
echo "${indent}Level $level: $heading"
donePython script for detailed analysis:
# Extract structure from markdown
with open("proposal_structure.md") as f:
content = f.read()
headings = []
for line in content.split("\n"):
if line.startswith("#"):
level = line.count("#", 0, line.index(" "))
text = line.lstrip("#").strip()
headings.append({"level": level, "text": text})
# Print outline
for h in headings:
indent = " " * (h["level"] - 1)
print(f"{indent}{h['level']}. {h['text']}")Expected Output
- Document outline with heading levels
- Hierarchical structure visualization
- Easy to identify sections for reorganization
---
Example 9: Merge Multiple Documents
Scenario
Combine several Word documents into a single consolidated document.
Conversation
User: Merge section1.docx, section2.docx, and section3.docx into a single document
Claude: I'll use pandoc to merge the documents.
# Simple merge
pandoc section1.docx section2.docx section3.docx -o merged_document.docx
# Add page breaks between sections
pandoc section1.docx section2.docx section3.docx --reference-doc=template.docx -o merged_with_breaks.docx
# Convert all to markdown first for review
pandoc section1.docx -o section1.md
pandoc section2.docx -o section2.md
pandoc section3.docx -o section3.md
# Manually combine markdown and convert back
cat section1.md section2.md section3.md > combined.md
pandoc combined.md -o combined.docxExpected Output
- Single
merged_document.docxfile - All content from source documents preserved
- Optional page breaks between sections
- Original formatting maintained
---
Example 10: Create Document from Template with Data
Scenario
Generate multiple contract documents from a template by replacing placeholder variables.
Conversation
User: Create 3 contracts from template.docx by replacing {CLIENT_NAME}, {DATE}, and {AMOUNT} with data from data.json
Claude: I'll unpack the template, replace variables, and generate documents.
// data.json
[
{ "client": "Acme Corp", "date": "2025-11-08", "amount": "$50,000" },
{ "client": "Beta Inc", "date": "2025-11-10", "amount": "$75,000" },
{ "client": "Gamma LLC", "date": "2025-11-12", "amount": "$100,000" }
]import json
import shutil
from pathlib import Path
from defusedxml import minidom
# Load data
with open("data.json") as f:
clients = json.load(f)
# Process each client
for i, client in enumerate(clients, 1):
# Copy template
work_dir = Path(f"contract_{i}")
shutil.copytree("template_unpacked", work_dir)
# Load document XML
doc_path = work_dir / "word" / "document.xml"
doc = minidom.parse(str(doc_path))
# Replace placeholders
replacements = {
"{CLIENT_NAME}": client["client"],
"{DATE}": client["date"],
"{AMOUNT}": client["amount"]
}
for text_node in doc.getElementsByTagName("w:t"):
if text_node.firstChild:
text = text_node.firstChild.nodeValue
for placeholder, value in replacements.items():
text = text.replace(placeholder, value)
text_node.firstChild.nodeValue = text
# Save modified XML
with open(doc_path, "wb") as f:
f.write(doc.toxml(encoding="UTF-8"))
# Pack document
output_file = f"contract_{client['client'].replace(' ', '_')}.docx"
import subprocess
subprocess.run([
"python", "ooxml/scripts/pack.py",
str(work_dir), output_file
])
print(f"Generated {output_file}")Expected Output
- Three contract documents:
contract_Acme_Corp.docxcontract_Beta_Inc.docxcontract_Gamma_LLC.docx- Each with personalized client data
- Identical structure from template
---
Common Patterns
Error Handling for OOXML Operations
from defusedxml import minidom
from pathlib import Path
def safe_unpack_and_modify(docx_file, output_dir):
try:
# Unpack
import subprocess
result = subprocess.run(
["python", "ooxml/scripts/unpack.py", docx_file, output_dir],
capture_output=True,
text=True,
check=True
)
# Modify
doc_xml = Path(output_dir) / "word" / "document.xml"
if not doc_xml.exists():
raise FileNotFoundError(f"Document XML not found: {doc_xml}")
doc = minidom.parse(str(doc_xml))
# ... modifications ...
return True
except subprocess.CalledProcessError as e:
print(f"Unpack failed: {e.stderr}")
return False
except Exception as e:
print(f"Error: {e}")
return FalseVerify Changes Before Packing
# Always verify changes before final pack
grep -n "expected_text" unpacked/word/document.xml
# Pack with validation
python ooxml/scripts/pack.py unpacked/ output.docx
# If validation fails, use --force and manually check
python ooxml/scripts/pack.py unpacked/ output.docx --force
# Then verify document opens correctly
soffice output.docx # Manual verificationBatch Processing with Progress
from pathlib import Path
from tqdm import tqdm # pip install tqdm
docx_files = list(Path("documents").glob("*.docx"))
results = []
for docx_file in tqdm(docx_files, desc="Processing documents"):
try:
# Process document
output = process_document(docx_file)
results.append({"file": docx_file.name, "status": "success"})
except Exception as e:
results.append({"file": docx_file.name, "status": "failed", "error": str(e)})
# Summary
success_count = sum(1 for r in results if r["status"] == "success")
print(f"\nProcessed {len(results)} documents: {success_count} successful")---
Tips and Best Practices
1. Choose the right tool:
- pandoc: Text extraction, quick conversions
- docx-js: Creating new documents from scratch
- OOXML editing: Precise tracked changes, complex operations
2. Tracked changes workflow:
- Always batch changes (3-10 per batch)
- Use minimal edits principle (only mark changed text)
- Test each batch before proceeding
- Keep original RSID for unchanged text
3. Error prevention:
- Validate after packing (or use --force and manually verify)
- Always grep before writing scripts (line numbers change)
- Keep backup of original document
- Test on sample document first
4. Performance:
- Use pandoc for bulk text extraction
- Cache unpacked directories for multiple edits
- Batch process similar documents together
5. Quality assurance:
- Convert to markdown for verification
- Check document opens correctly in Word
- Verify tracked changes appear as expected
- Review comments and structure
---
Next Steps
- Review SKILL.md for complete workflow reference
- Check DEPENDENCIES.md for installation requirements
- See README.md for integration details
- Run tests:
pytest tests/ -v
---
Last Updated: 2025-11-08 Maintained By: amplihack project
DOCX Skill Integration
Overview
The DOCX skill provides comprehensive Word document manipulation capabilities for Claude Code, enabling document creation, editing with tracked changes (redlining), text extraction, and complex OOXML operations. This is the third Office skill integrated into amplihack, building on the common OOXML infrastructure established in PR #3.
Capabilities
- Create new Word documents from scratch using docx-js
- Edit existing documents with tracked changes (redlining)
- Extract text and structure with pandoc
- Access raw XML for complex formatting and metadata
- Batch edit documents with systematic tracked changes workflow
- Convert documents to images for visual analysis
- Handle comments, embedded media, and document structure
- Minimal, precise edits that preserve original formatting
Integration with amplihack
The DOCX skill follows amplihack's brick philosophy:
- Self-contained: All DOCX processing code and dependencies isolated in this directory
- Clear contract: Well-defined inputs (DOCX files) and outputs (modified DOCX, text, images)
- Regeneratable: Can be rebuilt from SKILL.md specification
- Zero-BS: No placeholders - all functionality works or gracefully degrades
- Shared infrastructure: Uses common OOXML scripts via symlink (single source of truth)
- Independent: Works without other Office skills, minimal cross-dependencies
Quick Start
1. Install dependencies (see DEPENDENCIES.md) 2. Verify installation: python ../common/verification/verify_skill.py docx 3. Use the skill in Claude Code conversations
Example conversation:
User: Create a Word document with a title, 3 sections, and a table
Claude: [Uses DOCX skill with docx-js to create structured document]Architecture
- SKILL.md: Official skill definition from Anthropic (copied verbatim)
- README.md: This file - amplihack-specific integration notes
- DEPENDENCIES.md: Complete dependency documentation with installation instructions
- ooxml/: Symlink to ../common/ooxml (shared OOXML infrastructure)
- examples/: Practical usage examples
- tests/: Verification tests that skip gracefully if dependencies missing
Key Workflows
1. Creating New Documents
Use docx-js (JavaScript/TypeScript) to create documents from scratch:
import { Document, Paragraph, TextRun, Packer } from "docx";
const doc = new Document({
sections: [
{
properties: {},
children: [
new Paragraph({
children: [new TextRun({ text: "Hello World", bold: true })],
}),
],
},
],
});
const buffer = await Packer.toBuffer(doc);
fs.writeFileSync("output.docx", buffer);2. Editing with Tracked Changes (Redlining)
The redlining workflow is the recommended default for editing any document, especially legal, academic, business, or government docs:
1. Convert to markdown: pandoc --track-changes=all document.docx -o current.md 2. Identify and group changes into batches (3-10 changes per batch) 3. Unpack document: python ooxml/scripts/unpack.py document.docx unpacked/ 4. Implement changes in batches using Python scripts 5. Pack document: python ooxml/scripts/pack.py unpacked/ reviewed.docx 6. Verify: pandoc --track-changes=all reviewed.docx -o verification.md
Key Principle: Minimal, precise edits - only mark text that actually changes, preserve original formatting for unchanged text.
3. Text Extraction
Use pandoc for quick text extraction with structure preservation:
pandoc document.docx -o output.md
pandoc --track-changes=all document.docx -o with-changes.md4. Raw XML Access
For complex operations (comments, advanced formatting, metadata):
# Unpack document
python ooxml/scripts/unpack.py document.docx unpacked/
# Read XML files
cat unpacked/word/document.xml # Main document content
cat unpacked/word/comments.xml # Comments
ls unpacked/word/media/ # Embedded images
# Pack when done
python ooxml/scripts/pack.py unpacked/ output.docxDependencies
The DOCX skill requires both Python and Node.js dependencies:
Required (Core functionality):
- defusedxml: Secure XML parsing
- pandoc: Text extraction and conversion
- LibreOffice: Document validation and PDF conversion
Optional (Enhanced functionality):
- docx (npm): Creating new documents
- poppler-utils: PDF to image conversion
See DEPENDENCIES.md for detailed installation instructions.
Testing
Run tests to verify the skill:
cd .claude/skills/docx
pytest tests/ -vTests will skip gracefully if dependencies are not installed, showing which features are available.
Usage Examples
See examples/example_usage.md for common workflows:
- Creating business documents from templates
- Implementing contract redlines with tracked changes
- Batch processing legal document reviews
- Extracting and analyzing document structure
- Converting documents to images for visual review
- Handling comments and embedded media
- Systematic editing workflows with batching
- Minimal, precise edits that preserve formatting
Known Limitations
1. Node.js required for creation: docx-js requires Node.js runtime for creating new documents 2. LibreOffice for validation: Pack script validation requires LibreOffice installation 3. Complex formatting: Advanced Word features (SmartArt, complex tables) may require manual intervention 4. Tracked changes complexity: Large documents with many changes should use batching (3-10 changes per batch) 5. Platform differences: Some tools may have different behavior on Windows vs Unix 6. RSID management: Tracked changes require careful RSID handling for proper Word display
Philosophy Compliance
This integration follows amplihack's core principles:
- Ruthless simplicity: Uses established tools (pandoc, docx-js, OOXML), no custom parsers
- Modular design: DOCX skill is a brick with clear studs (public API)
- Explicit dependencies: All requirements documented, no automatic installation
- Graceful degradation: Optional features skip cleanly if dependencies missing
- Shared infrastructure: OOXML scripts in common/ directory, symlinked for reuse
- Documentation-first: Complete docs before code execution
- Minimal, precise edits: Only mark changed text in tracked changes
Troubleshooting
Skill not recognized:
1. Verify SKILL.md exists in this directory 2. Check YAML frontmatter is valid 3. Verify symlink: ls -la ooxml/ should show link to ../common/ooxml 4. Restart Claude Code session
ImportError for defusedxml:
1. Run verification script: python ../common/verification/verify_skill.py docx 2. Install missing dependencies: pip install defusedxml 3. Re-run tests to confirm
Pandoc not found:
1. Install: sudo apt-get install pandoc (Ubuntu) or brew install pandoc (macOS) 2. Verify: pandoc --version
Pack script fails validation:
1. Check LibreOffice installed: soffice --version 2. Use --force flag to skip validation: python ooxml/scripts/pack.py unpacked/ output.docx --force 3. Manually verify document opens in Word
Tracked changes not appearing:
1. Verify RSID format (8 hex characters, e.g., "00AB12CD") 2. Check XML structure matches OOXML specification 3. Use minimal edits principle (only mark changed text) 4. Ensure proper <w:ins> and <w:del> tag structure
Symlink not working (Windows):
1. Check if symlinks are enabled (requires admin/developer mode) 2. Alternatively, copy common/ooxml/ to docx/ooxml/ (not recommended) 3. Update scripts to use absolute paths
Contributing
This skill is sourced from Anthropic's official skills repository. For issues:
1. amplihack integration issues: Open issue in amplihack repository 2. Skill functionality issues: Report to Anthropic skills repository 3. Documentation improvements: Submit PR to amplihack 4. OOXML script issues: Check common/ooxml/README.md first
References
- SKILL.md - Official skill documentation
- DEPENDENCIES.md - Complete dependency list
- examples/example_usage.md - Usage examples
- tests/test_docx_skill.py - Verification tests
- ../common/ooxml/README.md - OOXML scripts documentation
- Anthropic Skills Repository
License
The DOCX skill is provided by Anthropic under their proprietary license. See SKILL.md and Anthropic's LICENSE.txt for complete terms. The amplihack integration code (this README, DEPENDENCIES.md, tests, examples) follows amplihack's license.
---
Integration Status: Complete (PR #3) Last Updated: 2025-11-08 Maintained By: amplihack project
"""Basic verification tests for DOCX skill.
These tests verify the DOCX skill integration:
- Level 1: Skill file structure
- Level 2: Dependency availability
- Level 3: Basic functionality (if dependencies available)
- Level 4: Integration (future)
Tests skip gracefully if dependencies are missing.
"""
from pathlib import Path
import pytest
import yaml
# Define skill dependencies
PYTHON_PACKAGES_REQUIRED = ["defusedxml"]
PYTHON_PACKAGES_OPTIONAL = []
SYSTEM_COMMANDS_REQUIRED = ["pandoc", "soffice"]
SYSTEM_COMMANDS_OPTIONAL = ["pdftoppm", "node"]
# Level 1: Skill Load Tests
def test_skill_file_exists():
"""Verify SKILL.md exists."""
skill_file = Path(__file__).parent.parent / "SKILL.md"
assert skill_file.exists(), "SKILL.md not found"
def test_skill_yaml_valid():
"""Verify SKILL.md has valid YAML frontmatter."""
skill_file = Path(__file__).parent.parent / "SKILL.md"
content = skill_file.read_text()
assert content.startswith("---"), "SKILL.md missing YAML frontmatter"
# Extract and parse YAML
parts = content.split("---")
assert len(parts) >= 3, "Invalid YAML structure in SKILL.md"
metadata = yaml.safe_load(parts[1])
assert isinstance(metadata, dict), "YAML frontmatter is not a dictionary"
assert "name" in metadata, "YAML missing 'name' field"
assert metadata["name"] == "docx", "YAML name field should be 'docx'"
assert "description" in metadata, "YAML missing 'description' field"
def test_readme_exists():
"""Verify README.md exists with integration notes."""
readme = Path(__file__).parent.parent / "README.md"
assert readme.exists(), "README.md not found"
content = readme.read_text()
assert "amplihack" in content.lower(), "README missing amplihack context"
assert "docx" in content.lower(), "README should mention DOCX"
assert "tracked changes" in content.lower() or "redlining" in content.lower(), (
"README should mention tracked changes or redlining"
)
def test_dependencies_file_exists():
"""Verify DEPENDENCIES.md exists."""
deps_file = Path(__file__).parent.parent / "DEPENDENCIES.md"
assert deps_file.exists(), "DEPENDENCIES.md not found"
content = deps_file.read_text()
# Check for key dependencies mentioned
assert "defusedxml" in content.lower(), "DEPENDENCIES.md should mention defusedxml"
assert "pandoc" in content.lower(), "DEPENDENCIES.md should mention pandoc"
assert "libreoffice" in content.lower() or "soffice" in content.lower(), (
"DEPENDENCIES.md should mention LibreOffice"
)
def test_examples_exist():
"""Verify examples directory and content exist."""
examples_dir = Path(__file__).parent.parent / "examples"
assert examples_dir.exists(), "examples/ directory not found"
example_file = examples_dir / "example_usage.md"
assert example_file.exists(), "examples/example_usage.md not found"
content = example_file.read_text()
assert len(content) > 100, "example_usage.md appears to be empty or too short"
def test_ooxml_symlink_exists():
"""Verify ooxml symlink or directory exists."""
ooxml_path = Path(__file__).parent.parent / "ooxml"
# Should exist as symlink or directory
assert ooxml_path.exists(), "ooxml/ symlink or directory not found"
# Check if scripts directory exists via symlink
scripts_path = ooxml_path / "scripts"
assert scripts_path.exists(), "ooxml/scripts/ not found (symlink may be broken)"
# Verify unpack.py and pack.py exist
assert (scripts_path / "unpack.py").exists(), "unpack.py not found"
assert (scripts_path / "pack.py").exists(), "pack.py not found"
# Level 2: Dependency Tests
def check_python_package(package: str) -> bool:
"""Check if Python package is installed."""
try:
__import__(package)
return True
except ImportError:
return False
def check_system_command(command: str) -> bool:
"""Check if system command is available."""
import subprocess
try:
subprocess.run(
[command, "--version"],
capture_output=True,
check=True,
timeout=5,
)
return True
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
return False
def check_dependencies():
"""Check if required dependencies are available."""
python_ok = all(check_python_package(pkg) for pkg in PYTHON_PACKAGES_REQUIRED)
system_ok = all(check_system_command(cmd) for cmd in SYSTEM_COMMANDS_REQUIRED)
return python_ok and system_ok
@pytest.mark.skipif(
not all(check_python_package(pkg) for pkg in PYTHON_PACKAGES_REQUIRED),
reason="Required Python packages not installed (defusedxml)",
)
def test_required_python_dependencies():
"""Test that required Python packages are available."""
for package in PYTHON_PACKAGES_REQUIRED:
assert check_python_package(package), f"Required package {package} not installed"
@pytest.mark.skipif(
not all(check_system_command(cmd) for cmd in SYSTEM_COMMANDS_REQUIRED),
reason="Required system commands not installed (pandoc, soffice)",
)
def test_required_system_dependencies():
"""Test that required system commands are available."""
for command in SYSTEM_COMMANDS_REQUIRED:
assert check_system_command(command), f"Required command {command} not installed"
def test_optional_dependencies_status():
"""Report status of optional dependencies (does not fail)."""
print("\n\nOptional Python packages:")
for package in PYTHON_PACKAGES_OPTIONAL:
status = "✓ Installed" if check_python_package(package) else "✗ Not installed"
print(f" {package}: {status}")
print("\nOptional system commands:")
for command in SYSTEM_COMMANDS_OPTIONAL:
status = "✓ Available" if check_system_command(command) else "✗ Not available"
print(f" {command}: {status}")
# Level 3: Basic Functionality Tests
@pytest.mark.skipif(
not check_dependencies(),
reason="Required dependencies not installed",
)
def test_defusedxml_basic_functionality():
"""Test basic defusedxml functionality."""
from defusedxml import minidom
# Parse simple XML
xml_string = '<?xml version="1.0"?><root><item>Test</item></root>'
dom = minidom.parseString(xml_string)
# Verify parsing worked
root = dom.documentElement
assert root.tagName == "root", "Root element should be 'root'"
items = root.getElementsByTagName("item")
assert len(items) == 1, "Should have 1 item element"
assert items[0].firstChild.nodeValue == "Test", "Item text should be 'Test'"
@pytest.mark.skipif(
not check_dependencies(),
reason="Required dependencies not installed",
)
def test_ooxml_unpack_script_exists():
"""Test that OOXML unpack script is accessible."""
ooxml_path = Path(__file__).parent.parent / "ooxml"
unpack_script = ooxml_path / "scripts" / "unpack.py"
assert unpack_script.exists(), "unpack.py script not found"
# Verify script is executable or Python file
assert unpack_script.suffix == ".py", "unpack.py should be a Python file"
# Verify script has content
content = unpack_script.read_text()
assert len(content) > 100, "unpack.py appears to be empty or too short"
assert "defusedxml" in content, "unpack.py should use defusedxml"
@pytest.mark.skipif(
not check_dependencies(),
reason="Required dependencies not installed",
)
def test_ooxml_pack_script_exists():
"""Test that OOXML pack script is accessible."""
ooxml_path = Path(__file__).parent.parent / "ooxml"
pack_script = ooxml_path / "scripts" / "pack.py"
assert pack_script.exists(), "pack.py script not found"
# Verify script is Python file
assert pack_script.suffix == ".py", "pack.py should be a Python file"
# Verify script has content
content = pack_script.read_text()
assert len(content) > 100, "pack.py appears to be empty or too short"
assert "defusedxml" in content, "pack.py should use defusedxml"
@pytest.mark.skipif(
not check_dependencies(),
reason="Required dependencies not installed",
)
def test_pandoc_basic_functionality():
"""Test basic pandoc functionality."""
import subprocess
import tempfile
# Create temporary markdown file
with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as tmp:
tmp.write("# Test Document\n\nThis is a test paragraph.")
tmp_path = tmp.name
try:
# Convert markdown to DOCX
output_path = tmp_path.replace(".md", ".docx")
result = subprocess.run(
["pandoc", tmp_path, "-o", output_path], capture_output=True, text=True, timeout=10
)
assert result.returncode == 0, f"Pandoc conversion failed: {result.stderr}"
assert Path(output_path).exists(), "Output DOCX file not created"
# Verify output file is not empty
assert Path(output_path).stat().st_size > 0, "Output DOCX file is empty"
# Clean up
Path(output_path).unlink()
finally:
Path(tmp_path).unlink()
@pytest.mark.skipif(
not check_dependencies(),
reason="Required dependencies not installed",
)
def test_soffice_basic_functionality():
"""Test basic LibreOffice (soffice) functionality."""
import subprocess
# Just verify soffice can report version
result = subprocess.run(["soffice", "--version"], capture_output=True, text=True, timeout=5)
assert result.returncode == 0, "soffice --version failed"
assert "libreoffice" in result.stdout.lower(), "Output should mention LibreOffice"
@pytest.mark.skipif(
not check_dependencies(),
reason="Required dependencies not installed",
)
def test_ooxml_xml_manipulation():
"""Test XML manipulation capabilities."""
from defusedxml import minidom
# Create a simple OOXML-like structure
doc = minidom.parseString("""<?xml version="1.0"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p>
<w:r>
<w:t>Hello World</w:t>
</w:r>
</w:p>
</w:body>
</w:document>
""")
# Test XML traversal
text_nodes = doc.getElementsByTagName("w:t")
assert len(text_nodes) == 1, "Should have 1 text node"
assert text_nodes[0].firstChild.nodeValue == "Hello World", "Text should be 'Hello World'"
# Test XML modification
text_nodes[0].firstChild.nodeValue = "Modified Text"
assert text_nodes[0].firstChild.nodeValue == "Modified Text", "Text should be modified"
# Test XML serialization
xml_bytes = doc.toxml(encoding="UTF-8")
assert b"Modified Text" in xml_bytes, "Serialized XML should contain modified text"
@pytest.mark.skipif(
not check_dependencies(),
reason="Required dependencies not installed",
)
def test_tracked_changes_xml_structure():
"""Test understanding of tracked changes XML structure."""
from defusedxml import minidom
# Create a document with tracked changes
doc = minidom.parseString("""<?xml version="1.0"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p>
<w:r>
<w:t>The term is </w:t>
</w:r>
<w:del w:id="1" w:author="Reviewer">
<w:r>
<w:delText>30</w:delText>
</w:r>
</w:del>
<w:ins w:id="2" w:author="Reviewer">
<w:r>
<w:t>60</w:t>
</w:r>
</w:ins>
<w:r>
<w:t> days</w:t>
</w:r>
</w:p>
</w:body>
</w:document>
""")
# Verify deletion structure
deletions = doc.getElementsByTagName("w:del")
assert len(deletions) == 1, "Should have 1 deletion"
assert deletions[0].getAttribute("w:author") == "Reviewer", "Deletion should have author"
# Verify insertion structure
insertions = doc.getElementsByTagName("w:ins")
assert len(insertions) == 1, "Should have 1 insertion"
assert insertions[0].getAttribute("w:author") == "Reviewer", "Insertion should have author"
# Verify deleted text
del_text_nodes = doc.getElementsByTagName("w:delText")
assert len(del_text_nodes) == 1, "Should have 1 deleted text node"
assert del_text_nodes[0].firstChild.nodeValue == "30", "Deleted text should be '30'"
# Level 4: Integration Tests (Future)
@pytest.mark.skip(reason="Integration tests not yet implemented")
def test_skill_invocation():
"""Test that skill can be invoked in Claude Code."""
# Future: Test skill invocation through Claude Code API
@pytest.mark.skip(reason="Integration tests not yet implemented")
def test_full_unpack_pack_cycle():
"""Test complete unpack/modify/pack workflow."""
# Future: Test with actual DOCX file in fixtures
@pytest.mark.skip(reason="Integration tests not yet implemented")
def test_tracked_changes_workflow():
"""Test complete tracked changes workflow."""
# Future: Test redlining workflow end-to-end
# Utility function for manual testing
def print_dependency_report():
"""Print comprehensive dependency report."""
print("\n" + "=" * 60)
print("DOCX Skill Dependency Report")
print("=" * 60)
print("\nRequired Python Packages:")
for package in PYTHON_PACKAGES_REQUIRED:
status = "✓ Installed" if check_python_package(package) else "✗ MISSING"
print(f" {package:20s}: {status}")
print("\nRequired System Commands:")
for command in SYSTEM_COMMANDS_REQUIRED:
status = "✓ Available" if check_system_command(command) else "✗ MISSING"
print(f" {command:20s}: {status}")
print("\nOptional Python Packages:")
for package in PYTHON_PACKAGES_OPTIONAL:
status = "✓ Installed" if check_python_package(package) else "✗ Not installed"
print(f" {package:20s}: {status}")
print("\nOptional System Commands:")
for command in SYSTEM_COMMANDS_OPTIONAL:
status = "✓ Available" if check_system_command(command) else "✗ Not available"
print(f" {command:20s}: {status}")
print("\n" + "=" * 60)
all_required = all(check_python_package(pkg) for pkg in PYTHON_PACKAGES_REQUIRED) and all(
check_system_command(cmd) for cmd in SYSTEM_COMMANDS_REQUIRED
)
if all_required:
print("✓ DOCX skill is ready to use (core functionality)")
else:
print("✗ DOCX skill is missing required dependencies")
print("\nInstall with:")
print(" pip install defusedxml")
print(" sudo apt-get install pandoc libreoffice # Ubuntu")
print(" brew install pandoc libreoffice # macOS")
print("=" * 60 + "\n")
if __name__ == "__main__":
# When run directly, print dependency report
print_dependency_report()
# Run tests
import sys
sys.exit(pytest.main([__file__, "-v", "--tb=short"]))
Related skills
How it compares
Pick docx over markdown-only export skills when stakeholders require native .docx deliverables with tracked changes and OOXML-level edits.
FAQ
How does docx create new Word documents?
docx creates new documents with docx-js using Document, Paragraph, and TextRun components, exporting via Packer.toBuffer(). Agents must read docx-js.md completely before generation to follow critical Word formatting rules.
How does docx handle tracked changes in existing files?
docx uses a redlining workflow: pandoc extracts markdown, groups changes into batches of 3-10 edits, modifies word/document.xml via OOXML scripts, then packs the directory back to .docx with pack.py.