Markdown to docx
Skill eugenelim/agent-ready-repo/packs/converters/.apm/skills/markdown-to-docx
Fill a branded Word template from a Markdown artifact — turn this Markdown into a Word doc, produce a report, a memo, a statement of work, or a branded .docx. The deterministic script fills the Jinja fill-points a designer placed in the template (front-matter, headings, lists, tables map onto them), so the cover page, styles, headers, and placed logo survive. Use when the user wants a Word document, report, memo, or .docx out of Markdown. Tier-1 on docxtpl (the user installs it; the skill detects it and stops if absent).From its SKILL.md
npx -y skills add eugenelim/agent-ready-repo --skill markdown-to-docxAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 15 stars15 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.3 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it
Markdown to Word
A thin wrapper around scripts/render.py. The script is the renderer; you
assemble nothing by hand. It fills a user-provided, pre-tagged .docx
template at the Jinja fill-points the designer placed, via
docxtpl — rather than building a document
from scratch — so the cover page, paragraph styles, headers/footers, and any
placed logo survive.
Prerequisites
This skill is Tier-1 on docxtpl (the exact canonical PyPI package — a real
but lower-profile single-maintainer package, so install the exact name). Install
it once:
python -m pip install 'docxtpl>=0.16.0'
The >=0.16.0 floor clears a version where
get_undeclared_template_variables() was broken. docxtpl installs into
your environment, outside the repo's SCA (pip-audit / CodeQL never
scan it), so you own keeping it current. The skill never installs it for you.
Verify before rendering:
python scripts/render.py --check
Exit 0 → proceed. Exit 2 → it's not installed; run the pip install above
and stop.
The deterministic-renderer contract
You drive three verbs; the script does the rendering:
| Verb | What it does | Stdout markers |
|---|---|---|
--check | Import-probe docxtpl; exit 0/2. | — |
inspect <template> | List the template's declared Jinja variables. | FILLPOINTS: <variable> (or GUIDANCE: if untagged) |
render <markdown> --template <tpl> [--output <path>] | Fill the template and write a .docx. | OUTPUT: <path>, FILLED: <n>, WARNING: <msg>, GUIDANCE: <msg> |
The mapping: front-matter key: value → {{ key }} scalars; the first list →
an items list for a {%p for it in items %}…{%p endfor %} paragraph loop; the
first Markdown table → rows (a list of {column: value} dicts) for a
{%tr for r in rows %} row loop. Detail and how to add tags in Word are in
references/fill-points.md.
Your job is to assemble the Markdown content and invoke the script. Do not
hand-write the .docx or its XML.
Template flow
- Detect — look for a
.docxtemplate on disk in the working directory. - Confirm or elicit — confirm the found one, or ask the user for theirs.
- No fill-points — if the template carries no Jinja tags, the script emits
GUIDANCE:explaining how to add them (insert{{ … }},{%p … %},{%tr … %}tags in Word) rather than silently converting. Relay it; don't convert by hand. - Opt-out — only if the user explicitly declines a template, render
template-less with the
docxtpl/python-docxdefault document. Say so up front: the result carries no brand. Never invent a brand or ship a default template asset.
Authoring tags in Word — run fragmentation
Word silently splits text into multiple "runs" when formatting or autocorrect
changes mid-word, and a Jinja tag split across runs ({ + { title } + })
won't render. Author each tag inside one uniform run: type the whole tag in
one go with consistent formatting, or paste it as plain text. If a tag isn't
filling, this is the first thing to check.
Trust model
A user-supplied template is trusted-author input, consistent with the
converters pack's local-files-trusted stance. Because a .docx template carries
Jinja2 source, a malicious template author could embed server-side template
injection (SSTI) — this is an accepted, out-of-scope risk for a
trusted-author template, as are XXE / zip-bomb on a deliberately crafted
archive. Two cheap controls hold regardless and are enforced:
autoescape=Trueat thedocxtplrender()call (it is off by default), so a user-content value containing{{or XML metacharacters is escaped, not interpolated as live markup.- Output-path confinement — the script resolves
--output/--templateand refuses any path escaping the working directory (a model-influenced output path is a local control, independent of template trust).
Don't
- Don't hand-write the
.docxor its XML — the script renders. - Don't convert an untagged document (Pandoc/Quarto) — relay the
GUIDANCE:instead. Template-fill only. - Don't ship or invent a default template — an absent template is the user's explicit choice.
- Don't auto-install
docxtpl— print the install line and stop.
Edge cases
| Situation | Behavior |
|---|---|
docxtpl not installed | --check exits 2 with the install line; stop. |
| Template has no Jinja tags | GUIDANCE: explains how to add them; no file written. |
| A tag isn't filling | Likely run fragmentation — re-author the tag in one uniform run. |
| Template declares a variable with no Markdown source | The script emits WARNING: naming it; the rest still fills. |
What ships with it: 6 files
30.5 KB alongside SKILL.md, 2 of them executable
evals/
- eval_queries.json1.6 KB
- evals.json4.0 KB
- files/sample.md136 B
references/
- fill-points.md3.1 KB
scripts/
- render.pyruns13.2 KB
- test_render.pyruns8.4 KB