Imported from okou-ai/vm0-skills (
extract-template/docx/SKILL.md). Install upstream withnpx skills add okou-ai/vm0-skills --skill docx. Copyright stays with the author.
Extract a template package from a docx
Input: one .docx. Output: a loadable skill package whose instructions and
resources match the selected reuse scope.
Run every command below from extract-template/docx/.
If the original has not been rendered, render it before choosing the reuse scope:
node ../scripts/render-document.mjs --input <source.docx> --out source-render
Inspect its page images. Use a new output directory for each later render.
Choose the reuse scope
Read ../document-reuse.md, choose from representative
rendered pages and the user's purpose, then use the matching section below.
For style extraction, continue at Style extraction.
Slot filling
Copy the source to package/template.docx. Write package/SKILL.md with the
slot map and filling instructions from the shared guide. Locate each slot in
the document's XML and record enough context to distinguish repeated blanks.
Tell the generated skill to edit only those slots in a copy of template.docx.
Replace their text inside <w:t> while preserving run and paragraph
properties. Slots may use non-text XML such as an underlined <w:tab/>.
Record their blank area and how to insert a value while preserving fixed
labels, alignment and underline extent. Replacing a tab outright can move
the text that follows it; verify the filled rendering.
Patch the affected XML entries in the ZIP; preserve all other entries as they are. Opening and saving through python-docx or LibreOffice can rewrite parts outside the slots. Render the filled copy and verify the values fit and the rest of the document is unchanged. Continue at Publish the source package.
Structure and expression reuse
Copy the source to package/example.docx. Write package/SKILL.md with the
structure and writing rules from the shared guide, grounded in the source's
actual wording. Include where each kind of new content belongs, what may vary
in length or number, and which passages remain fixed.
Instruct the skill to draft content using those rules and edit a copy of
example.docx. Preserve the formatting of each kind of block. Add or remove
repeating items by copying or deleting the matching paragraph, table row or
section as a whole, keeping its formatting and required XML relationships.
Include separator paragraphs in the copied group when they provide its spacing.
Preserve run boundaries; clear unused fragments, including superscript text.
Regenerate identifiers that must be unique in cloned blocks.
Let text reflow and items grow within the recorded layout rules. Render the
result to check the layout, content organization and expression. Continue at
Publish the source package.
Locate editable text when needed
After choosing the scope, locate text across the document, headers and footers:
python3 scripts/inspect_docx.py <source.docx> --slots
python3 scripts/inspect_docx.py <source.docx> --slots --json
It does not classify the document or identify which text may change. It lists
runs containing <w:t>, so check the rendered pages and XML for gaps without
text, such as underlined tabs. Do not treat a field's cached result as editable
text; Word recomputes it.
Address edits by the part and node path, checking the old text before replacing it. Use the paragraph text to group split runs; retain their individual formatting and whitespace. Inspect linked placeholders and empty separator paragraphs.
For a filled content control, remove w:showingPlcHdr so its value is treated
as content. Preserve its placeholder definition for future empty values.
If it has w:dataBinding, update the referenced custom XML value as well;
do not leave the displayed value and its binding inconsistent.
Publish the source package
Before publishing, follow the package's own recipe on a disposable copy with sample content. Verify repeated values in the body, header and footer; exercise an added and removed item only where the selected scope permits it. Render all pages and compare the formatting, artwork, spacing and content with the original. Fix the recipe and repeat this check if it changes.
node ../scripts/cover-page.mjs --input <the original .docx> --out cover.png
npx --yes --package="${CLI_PKG_URL}" okou user-template publish \
--title "<user-visible template name>" \
--kind document \
--source <the original .docx> \
--cover cover.png \
--page-count <pageCount from cover-page.mjs> \
--package package
--source and the copy in package/ are both needed: the first is what a
reader opens, the second is the only one a later run can open. Publish without
--cover and --page-count only when cover-page.mjs failed; report what it
said.
Say the template exists only after the command succeeds, then stop.
Style extraction
Prerequisites
python3 -m pip install --break-system-packages pymupdf
python3 scripts/ensure_pandoc.py --dir ./vendor # then run the export PATH line it prints
1. Inspect
python3 scripts/inspect_docx.py <source.docx>
Note the missing required styles, numbered sections, body candidates and every
REVIEW line. Check the body style and section against a body page. Map a
heading by its role there; a style named Heading1 may belong to the cover.
[styles in use] decides how to extract the visual styles:
- the document uses its own style names: run step 2 with the
--mapit prints, after checking which pandoc style each name plays the part of; - the document is formatted by hand (
Normalwith direct formatting): its styles carry nothing. Render it as the inspector says and follow the Style extraction steps in../pdf/SKILL.mdon the render instead of continuing here. Ignore the exit code.
2. Build
python3 scripts/build_reference.py <source.docx> reference.docx \
[--map 'Memo Title=Title,Section Head=Heading1,__body__=Body Copy'] \
[--section <1-based body section number>]
Use __body__ to choose an existing source style for ordinary paragraphs;
this does not modify the source. Check the reported section selection. If
selection is ambiguous, pass the section and body style read from the source.
Resolve each ACTION REQUIRED before continuing. Do not strip a cover's frame
or change its colours to force it into a body-heading role.
3. Adjust
Required when step 1 or 2 asked for it; otherwise optional.
# paper size, and the header values step 1 flagged (left = literal text in the source)
python3 scripts/set_header_footer.py reference.docx --paper A4 \
--replace "DOC-2026-001=[DOC ID]" --replace "Jane Doe=[OWNER]"
# style values
python3 scripts/set_style.py reference.docx --list
python3 scripts/set_style.py reference.docx "Block Text" --font Georgia --size 10.5 --color 6C757D
python3 scripts/set_style.py reference.docx "Source Code" --create --font Consolas --size 9
# only to change the layout; a multi-column source is already multi-column
python3 scripts/set_header_footer.py reference.docx --columns 2 --column-gap 20
# only for a plain-text header/footer, or to add one; rebuilds the part
python3 scripts/set_header_footer.py reference.docx --footer "Confidential - page " --page-number
Style names are the w:name, case-insensitive. set_style.py options:
--font --east-asia-font --size --color --bold/--no-bold --italic/--no-italic --before --after --line --indent --left-indent --align --keep-next.
4. Verify
python3 scripts/verify_reference.py reference.docx --render-dir verified-probe
Exit code must be 0. Inspect every probe page for the correct heading roles,
body placement and required visual assets before packaging. Dangling or framed
styles → step 2. No paper size → step 3 with --paper. After a correction,
verify again into a new directory.
5. Package
python3 scripts/make_package.py <source.docx> reference.docx <out dir>
The output directory name is the skill name; --name overrides it. Add
anything the scripts could not read to the package's Limits.
6. Publish
node ../scripts/cover-page.mjs --input <source.docx> --out cover.png
npx --yes --package="${CLI_PKG_URL}" okou user-template publish \
--title "<user-visible template name>" \
--kind document \
--source <source.docx> \
--cover cover.png \
--page-count <pageCount from cover-page.mjs> \
--package <out dir>
Publish without --cover and --page-count only when cover-page.mjs
failed; report what it said. Say the template exists only after the command
succeeds.
Rules
- Reproduce the source. Do not correct an inverted hierarchy or a style left at a default; the package records it.
- Replace the source's own title, document number, version, owner and date in
the header and footer with
--replace; they are copied into every output. - Change the column layout only when asked.
- Do not edit
w:styleId. Do not add style names outside pandoc's set. - Step 4 is mandatory.
Troubleshooting
| Symptom | Action |
|---|---|
| Headings render like body text | Step 4 names the missing style |
| CJK text falls back to a serif font | The source names no East Asian font. set_style.py Normal --east-asia-font NAME fills that slot and leaves the Latin font alone |
| Code blocks will not restyle | set_style.py reference.docx "Source Code" --create |
| Output carries the source's number or owner | set_header_footer.py --replace |
| Header text sits outside the text area | Step 1 reports the tab stop; rebuild the header with --header 'left\tright', which places it from the margins |
| A docx saved by WPS fails to parse | Re-save it from Word, restart at step 1 |
| A slot-filling template comes back redrawn in a similar style | Its package carries no copy of the source, so there was nothing to edit. Add the file and republish |
| Content-control text disappears or loses its font, colour or rules | Render with ../scripts/render-document.mjs; use its flattened PDF and proportional page images |