Skip to main content

docx-generator

Production-grade DOCX generator that creates valid Microsoft Word (.docx) files from Markdown or structured AST using a safe streaming ZIP engine and minimal but strict OOXML baseline.

Source facts

Repository
guyoung/boxagnts
Last source activity
July 24, 2026 at 13:19
Detected SKILL.md language
English
Stars
11
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
docx-generator
description
Production-grade DOCX generator that creates valid Microsoft Word (.docx) files from Markdown or structured AST using a safe streaming ZIP engine and minimal but strict OOXML baseline.
when_to_use
Use when generating Word (.docx) documents from Markdown or structured AST. Suitable for AI-generated reports, documentation, summaries, or structured outputs that must reliably open in Microsoft Word.
tools
jsexec, read, write, bash
args
[{"name":"input","description":"Markdown string or structured AST used to generate DOCX","required":true},{"name":"output","description":"Output file path for the generated .docx file (default: /generated_document.docx)","required":false},{"name":"mode","description":"Input mode: markdown | ast (default: markdown)","required":false}]
> **CODE SKILL — VERBATIM REQUIRED** > This skill targets a formal specification (OOXML). > Adaptation is prohibited per C2-A Rule 3. ## CRITICAL: Execution Constraints The code below is the **SOLE IMPLEMENTATION** — it MUST NOT be replaced, simplified, or reimplemented. **Prohibited deviations:** - Do NOT write your own `crc32()` — use ONLY the one in Runnable Entry Point - Do NOT write your own `ZipStream` class — use ONLY the one in Runnable Entry Point - Do NOT skip any OOXML file from the 10-file baseline - Do NOT use compression (method 8 / DEFLATE) — STORE mode only - Do NOT assemble document body XML manually outside `markdownToDocxBody()` or `astToDocxBody()` ## Correct Invocation Pattern 1. Copy the **complete script** from `## Runnable Entry Point` below 2. Replace ONLY `{{input}}`, `{{output}}`, `{{mode}}` with actual values 3. Execute as a single `jsexec` call with **NO modifications** ## Argument Details | Arg | Type | Required | Default | Description | |-----|------|----------|---------|-------------| | `input` | `string` | yes | — | Markdown string or structured AST | | `output` | `string` | no | `/generated_document.docx` | Output .docx file path | | `mode` | `string` | no | `markdown` | Input mode: `markdown` | `ast` | ## Runnable Entry Point (COMPLETE IIFE, DO NOT MODIFY) ```js // ===== ⛔ Runnable Entry Point (IIFE) — DO NOT MODIFY ANY CODE BELOW ⛔ ===== (function() { const fs = require("fs"); const path = require("path"); const CRC32_TABLE = new Int32Array(256); (function(){for(let n=0;n<256;n++){let c=n;for(let k=0;k<8;k++)c=(c&1)?(0xEDB88320^(c>>>1)):(c>>>1);CRC32_TABLE[n]=c;}})(); function crc32(buf) { let crc = 0xFFFFFFFF; for (let i = 0; i < buf.length; i++) crc = (crc >>> 8) ^ CRC32_TABLE[(crc ^ buf[i]) & 0xFF]; return (crc ^ 0xFFFFFFFF) >>> 0; } function validateDocx(buf) { let eocdOff = -1; for (let i = buf.length - 22; i >= 0; i--) { if (buf[i]===0x50 && buf[i+1]===0x4B && buf[i+2]===0x05 && buf[i+3]===0x06) { eocdOff = i; break; } } if (eocdOff === -1) throw new Error("DOCX validation FAILED: no EOCD found"); const total = buf.readUInt16LE(eocdOff + 8); const cdOff = buf.readUInt32LE(eocdOff + 16); let pos = cdOff; const errors = []; for (let k = 0; k < total; k++) { if (buf.readUInt32LE(pos) !== 0x02014B50) { errors.push("Bad CD sig at "+k); break; } const compMethod = buf.readUInt16LE(pos + 10); const crcStored = buf.readUInt32LE(pos + 16); const uncompSize = buf.readUInt32LE(pos + 24); const nameLen = buf.readUInt16LE(pos + 28); const extraLen = buf.readUInt16LE(pos + 30); const localOff = buf.readUInt32LE(pos + 42); const fname = buf.slice(pos + 46, pos + 46 + nameLen).toString('utf8'); if (compMethod !== 0) errors.push(`COMPRESSION: "${fname}" method=${compMethod}, MUST be 0`); try { if (!fname.startsWith('word/media/')) { const localNameLen = buf.readUInt16LE(localOff + 26); const localExtraLen = buf.readUInt16LE(localOff + 28); const dataStart = localOff + 30 + localNameLen + localExtraLen; const storedData = buf.slice(dataStart, dataStart + uncompSize); const actualCrc = crc32(storedData); if (actualCrc !== crcStored) errors.push(`CRC MISMATCH: "${fname}" stored=0x${crcStored.toString(16)} actual=0x${actualCrc.toString(16)}`); } } catch (e) { errors.push(`VALIDATION: "${fname}" - ${e.message}`); } pos += 46 + nameLen + extraLen; } if (errors.length > 0) throw new Error("DOCX validation FAILED before writing:\n" + errors.map(e=>" - "+e).join('\n')); console.log("OK DOCX validation: " + total + " entries, CRC OK, STORE mode"); } class ZipStream { constructor() { this.entries = []; this.chunks = []; this.offset = 0; } writeFile(name, data) { const raw = Buffer.isBuffer(data) ? data : Buffer.from(data, 'utf8'); const nameBuf = Buffer.from(name, 'utf8'); const fileCrc = crc32(raw); const header = Buffer.alloc(30); header.writeUInt32LE(0x04034b50, 0); header.writeUInt16LE(20, 4); header.writeUInt16LE(0, 6); header.writeUInt16LE(0, 8); header.writeUInt32LE(fileCrc, 14); header.writeUInt32LE(raw.length, 18); header.writeUInt32LE(raw.length, 22); header.writeUInt16LE(nameBuf.length, 26); const local = Buffer.concat([header, nameBuf]); this.chunks.push(local, raw); this.entries.push({ name, crc: fileCrc, size: raw.length, offset: this.offset }); this.offset += local.length + raw.length; } finalize() { const central = []; for (const e of this.entries) { const nameBuf = Buffer.from(e.name, 'utf8'); const c = Buffer.alloc(46); c.writeUInt32LE(0x02014B50, 0); c.writeUInt16LE(20, 4); c.writeUInt16LE(20, 6); c.writeUInt16LE(0, 8); c.writeUInt16LE(0, 10); c.writeUInt32LE(e.crc, 16); c.writeUInt32LE(e.size, 20); c.writeUInt32LE(e.size, 24); c.writeUInt16LE(nameBuf.length, 28); c.writeUInt16LE(0, 30); c.writeUInt16LE(0, 32); c.writeUInt16LE(0, 34); c.writeUInt16LE(0, 36); c.writeUInt32LE(0, 38); c.writeUInt32LE(e.offset, 42); central.push(c, nameBuf); } const centralBuf = Buffer.concat(central); const eocd = Buffer.alloc(22); eocd.writeUInt32LE(0x06054b50, 0); eocd.writeUInt16LE(this.entries.length, 8); eocd.writeUInt16LE(this.entries.length, 10); eocd.writeUInt32LE(centralBuf.length, 12); eocd.writeUInt32LE(this.offset, 16); return Buffer.concat([...this.chunks, centralBuf, eocd]); } } // ── OOXML Generators ── function contentTypes(images) { const imgOverrides = images.map(img => ` <Override PartName="/word/media/${img.name}" ContentType="${img.mime}"/>`).join('\n'); return `<?xml version="1.0" encoding="UTF-8"?> <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"> <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/> <Default Extension="xml" ContentType="application/xml"/> <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/> <Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/> <Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/> <Override PartName="/word/webSettings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"/> <Override PartName="/word/fontTable.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"/> <Override PartName="/word/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/> <Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/> ${imgOverrides} </Types>`; } function rootRels() { return `<?xml version="1.0" encoding="UTF-8"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/> </Relationships>`; } function docRels(images) { const imgRels = images.map(img => ` <Relationship Id="${img.rId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/${img.name}"/>`).join('\n'); return `<?xml version="1.0" encoding="UTF-8"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/> <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/> <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" Target="webSettings.xml"/> <Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/> <Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/> <Relationship Id="rId6" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Target="numbering.xml"/> ${imgRels} </Relationships>`; } function documentXml(body, imageCount) { const imgNs = imageCount > 0 ? '\n xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"\n xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"\n xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"' : ''; return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"${imgNs}> <w:body>${body}<w:sectPr/></w:body> </w:document>`; } function stylesXml() { return `<?xml version="1.0" encoding="UTF-8"?> <w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"> <w:style w:type="paragraph" w:styleId="Normal" w:default="1"><w:name w:val="Normal"/><w:rPr><w:rFonts w:ascii="Calibri" w:hAnsi="Calibri"/><w:sz w:val="22"/></w:rPr></w:style> <w:style w:type="paragraph" w:styleId="Heading1"><w:name w:val="heading 1"/><w:basedOn w:val="Normal"/><w:rPr><w:rFonts w:ascii="Calibri" w:hAnsi="Calibri"/><w:b/><w:color w:val="1F3864"/><w:sz w:val="36"/></w:rPr></w:style> <w:style w:type="paragraph" w:styleId="Heading2"><w:name w:val="heading 2"/><w:basedOn w:val="Normal"/><w:rPr><w:rFonts w:ascii="Calibri" w:hAnsi="Calibri"/><w:b/><w:color w:val="2E75B6"/><w:sz w:val="28"/></w:rPr></w:style>
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub