# Documentation Bc Md To Docx Converter

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-fernandoartalf-al-copilot-skills-collection-documentation-bc-md-to-docx-converter`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [fernandoartalf](https://agentstack.voostack.com/s/fernandoartalf)
- **Installs:** 0
- **Category:** [Developer Tools](https://agentstack.voostack.com/c/developer-tools)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [fernandoartalf](https://github.com/fernandoartalf)
- **Source:** https://github.com/fernandoartalf/AL-Copilot-Skills-Collection/tree/main/skills/documentation-bc-md-to-docx-converter
- **Website:** https://alcopilotskills.com/

## Install

```sh
agentstack add skill-fernandoartalf-al-copilot-skills-collection-documentation-bc-md-to-docx-converter
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Markdown to DOCX Converter with Template (Python)

## Overview

Converts markdown (.md) files to professionally formatted Word documents (.docx) using **Python** and the **python-docx** library. The conversion pipeline works in three stages:

1. **Parse** the markdown file into named sections (metadata fields + H2 body sections)
2. **Generate a JSON file** containing all extracted sections as key-value pairs
3. **Substitute `{{Placeholder}}` tokens** in the Word template with the corresponding JSON values

**The conversion is only considered complete when the custom template is applied and all placeholders are substituted.** The template ensures consistent branding, styles, headers, footers, and formatting across all generated documents.

## Agent Instructions

When asked to convert a markdown file to a Word document, follow this checklist in order:

1. **Validate** — Confirm `python-docx` is installed and the source `.md` file exists.
2. **List templates** — Run `--list-templates` to show available templates. If only one template exists, it is auto-selected (skip step 3).
3. **Ask user** — Present the available templates and ask the user to select one.
4. **Run** — Execute `convert_md_to_docx.py  --template `.
5. **Verify** — Confirm the `.docx` file was created and is at least 20 KB.
6. **Report** — Tell the user the output file path and confirm all placeholders were substituted.

> For CLI flags, JSON schema, field maps, examples, and error handling, see the reference sections below.

## Prerequisites

- **Python 3.10+** installed and available in the system PATH
- **python-docx** library — install via:
  ```bash
  pip install -r .github\skills\documentation-bc-md-to-docx-converter\scripts\requirements.txt
  ```
  or directly:
  ```bash
  pip install python-docx
  ```
- **Template files**: bundled in the skill's `templates/` folder (`1_UserStory_Template.docx`, `2_Spec_Template.docx`, `3_Analysis_Template.docx`, `4_Architecture_Template.docx`, `5_CCN_Template.docx`, `6_ReleaseNote_Template.docx`)
  - The template must contain `{{Placeholder}}` tokens in paragraphs, tables, headers, or footers that will be replaced with values from the parsed markdown
- **(Optional) Mermaid CLI** — only required when the source markdown contains `` markers (emitted by the `documentation-bc-ccn-generator` skill) and you want them rendered as inline images. Install once globally:
  ```bash
  npm install -g @mermaid-js/mermaid-cli
  ```
  After installation, `mmdc` must be on PATH. When `mmdc` is missing the converter still succeeds but each diagram is inserted as its plain-text fallback with a `[Diagram:  ()]` label and a warning is printed.

## Quick Start

### Step 0: Install Python Dependencies

```bash
pip install -r .github\skills\documentation-bc-md-to-docx-converter\scripts\requirements.txt
```

### Step 1: List Available Templates

```bash
python .github\skills\documentation-bc-md-to-docx-converter\scripts\convert_md_to_docx.py --list-templates
```

This will display all available templates in the `templates/` folder with their index numbers.

### Step 2: Run the Converter with a Selected Template

```bash
python .github\skills\documentation-bc-md-to-docx-converter\scripts\convert_md_to_docx.py "docs\releasenotesmd\Release_Note_DSD-1234_Test_Customer_123456.md" --template 1
```

> **Note (macOS/Linux):** Replace backslashes with forward slashes in paths and line-continuation characters (`^`).

This will:
1. Parse the markdown into sections
2. Create a `.json` file alongside the output with all section values
3. Open the selected template, replace all `{{Placeholder}}` tokens, and save the `.docx`

## How the Pipeline Works

### Field Map Configuration

The converter uses a **unified field map registry** shared across every
`documentation-bc-*` skill at:

```
.github/skills/shared/unified-field-map.json
```

This JSON file is the **single source of truth** for all documentation
artefacts (USERSTORY, SPEC, ANALYSIS, ARCHITECTURE, CCN, PLAN,
RELEASENOTE). Each artefact entry contains pointers to:

- A reference markdown template under
  `.github/skills/documentation-bc-*-generator/references/*-template.md`
- A schema describing `frontmatterFields`, `metadataFields`, `sections`,
  plus `labelLocales` / `displayHeadingLocales` for multi-language output
- A canonical Word template (`templateHint` → filename in `templates/`)
- Detection metadata (`idPattern`, `filePattern`, `headingPattern`,
  `headingKey`, `extraMetadataKeys`)

**Detection order for `parse_markdown()`:**
1. Frontmatter `template:` value matched to a registered template filename.
2. Frontmatter `id:` matched against the artefact's `idPattern`.
3. Filename matched against the artefact's `filePattern`.

**Language resolution:** the converter selects the locale used to fill
`{{Label_}}` and `{{Label_Section_}}` placeholders in this
order: `--language` CLI flag → frontmatter `language:` field → `"en"`.

**To add new fields, sections, or languages:** edit the artefact's
schema entry in `unified-field-map.json` and add the matching
`{{Placeholder}}` in the Word template. No code changes required.

### Stage 1: Parse Markdown → Sections Dict

The script reads the markdown and extracts fields based on the resolved
artefact schema:

| Source | JSON Key | Driven By |
|--------|----------|----------|
| YAML frontmatter fields | `frontmatterFields[].key` (via `yamlKey`) | `unified-field-map.json` |
| H1 heading capture group | `headingKey` | `unified-field-map.json` (`headingPattern`) |
| Metadata table rows `\| **Label** \| Value \|` | `metadataFields[].key` / `extraMetadataKeys` entries | `unified-field-map.json` |
| `## Heading` sections via `` anchors | `` from the anchor (PascalCase) | Reference template anchors + `sections[]` |
| `Label_` / `Label_Section_` localised labels | `resolve_label()` using `labelLocales[lang]` | `unified-field-map.json` + `--language` |

Any `## Heading` without a section-key anchor is still captured using a
PascalCase key derived from the heading text.

### Stage 2: Write JSON File

A JSON file is created next to the output `.docx` with the same base name:

```json
{
  "CustomerName": "Test Customer 123456",
  "CCNNr": "DSD-1234",
  "Version": "1.0.0.0",
  "ChangeRequestDetails": "Implementation of the BC Dataverse...",
  "TestingSteps": "1. Open the **CDS Countries** list page..."
}
```

If any `...` marker block was found inside an H2 section body, the body text keeps a `{{DIAGRAM:}}` token where the marker was and a top-level `_diagrams` entry is added to the JSON:

```json
{
  "ArchitectureSolutionFromARCHNNN": "... prose ... {{DIAGRAM:component-view}} ... more prose ...",
  "_diagrams": {
    "component-view": {
      "type": "flowchart",
      "name": "component-view",
      "mermaid": "flowchart TD\n  A --> B\n  ...",
      "fallback": ""
    }
  }
}
```

The `_diagrams` key is reserved — never use it as a placeholder name. The JSON file is the single source of truth: editing `mermaid` source there and re-running the substitution stage is enough to update the diagram in the docx (no markdown changes needed).

### Stage 3: Substitute Placeholders in Template

The script opens the `.dotx/.docx` template and searches for `{{Key}}` placeholders in:

- **Body paragraphs** (all runs are joined, substituted, then rewritten)
- **Body tables** (every cell in every row, including nested tables — in cells with rich formatting, all runs are joined into a single run using the first run's formatting before substitution; rich formatting within the placeholder span is not preserved)
- **Headers and footers** (all section headers/footers including first-page and even-page variants)

Each `{{Key}}` token is replaced with the value from the JSON sections dict. If a key is not found in the JSON, the placeholder is left unchanged.

After `{{Key}}` substitution, any `{{DIAGRAM:}}` tokens that landed inside the resulting text are processed in a second pass per-paragraph:

1. The corresponding entry is looked up in `_diagrams`.
2. Its `mermaid` source is written to a temp file and rendered to PNG via `mmdc -i .mmd -o .png -b white --quiet`.
3. The token is replaced **in place** with an inline picture run sized to 6.0 inches wide (Inches(6)); surrounding text on the same line is preserved.
4. Rendered PNGs are cached per-run keyed by the mermaid source, so repeating a diagram costs nothing extra.
5. If `mmdc` is not installed, the mermaid source is empty, rendering fails, or `mmdc` times out (30-second limit), the token is replaced with a `[Diagram:  ()]` label followed by the diagram's `fallback` text and a warning is printed (`[WARNING] Mermaid render timed out for ` on timeout). The conversion does **not** fail.

#### Bare ` ```mermaid ``` ` fences

In addition to the explicit `` marker pair, any **bare** triple-backtick ` ```mermaid ``` ` fence found inside an H2 section body is auto-promoted to a synthetic diagram entry. The fence is extracted, given a generated name (`auto-mermaid-1`, `auto-mermaid-2`, …), its first non-empty source line is used to derive the diagram `type` (`flowchart`, `erDiagram`, `sequenceDiagram`, …), and a `{{DIAGRAM:auto-mermaid-N}}` token is left in the body. From there it follows the exact same render-or-fallback path as a marker-delimited diagram. This lets generator skills emit plain Mermaid code blocks without forcing markers, while preserving deterministic JSON keys.

#### Inline `` images

Inline markdown images of the form `` found in any H2 section body are extracted into `sections["_images"]` and replaced with a `{{IMAGE:}}` token. During substitution the token is rendered as an inline picture sized to 6.0 inches wide. Path resolution order:

1. Relative to the markdown file's parent directory.
2. Relative to the repo root (the workspace folder containing the converter).

If neither resolution succeeds, the original `` markdown is left in place untouched and a `[WARNING] Image not found, leaving as text` line is printed. URLs (`http://`, `https://`) and `data:` URIs are not downloaded and are also left as text. Like `_diagrams`, the `_images` key is reserved — never use it as a placeholder name.

#### Placeholder Format

Use double curly braces with the exact PascalCase key: `{{CustomerName}}`, `{{CCNNr}}`, `{{ChangeRequestDetails}}`, etc. Keys map directly to the JSON output from Stage 2. Unmatched placeholders are left unchanged.

#### Markdown-aware section body rendering

When a placeholder's substituted value contains block-level markdown (detected by the presence of fenced code blocks, GFM pipe tables, ordered/unordered list items, ATX headings, or a blank-line separator), the converter does **not** flatten the text into a single paragraph. Instead it tokenises the value into a small block model and emits the corresponding native Word constructs as siblings inserted **before** the placeholder paragraph; the original placeholder paragraph is then removed so no empty residual paragraph remains.

Supported subset:

- **Inline**: `**bold**`, `*italic*` / `_italic_`, `` `code` `` (rendered with `Consolas` font), `[label](url)` (rendered as a blue-underlined run using color `#0563C1`; no real hyperlink relationship is attached — visual fidelity only).
- **Paragraphs**: split on blank lines; inline markdown applied per paragraph.
- **Headings**: `#` … `######` produce a paragraph carrying the resolved value (no heading style is applied — styling continues to follow the template's placeholder paragraph formatting).
- **Lists**: unordered (`-`, `*`, `+`) and ordered (`1.`, `2.`, …) lists. Each item becomes its own paragraph, prefixed literally with `"• "` or `"N. "`. Literal prefixes are used (instead of `List Bullet` / `List Number` Word styles) so output is consistent regardless of which styles the template defines.
- **Tables**: GFM pipe tables (header row + `--- | ---` separator) are emitted as raw `` XML built via `OxmlElement`, with `tblW=auto` and single 4px borders on all six edges. The header row's cell runs are set to bold. Cells render their inline markdown.
- **Fenced code**: ```` ```lang ```` blocks render one paragraph per source line, each in `Consolas`. The language hint is currently informational only (no syntax highlighting).

Mechanics:

- Insertion uses lxml `paragraph._element.addprevious(new_elem)` followed by `parent.remove(p_elem)`. This is parent-agnostic and works whether the placeholder paragraph lives in the document body, a table cell (including nested tables), a header, a footer, or a text-box content region.
- Every rendered paragraph clones the template paragraph's `` and every new run inherits the template run's font/size/color via `copy_run_format`. This keeps localised typography intact.
- `{{DIAGRAM:}}` and `{{IMAGE:}}` tokens are preserved through tokenisation and rendered at the inline level via the same `_embed_diagram` / `_embed_image` path used by Stage 3, so mermaid charts and inline images embedded in a section body appear as native pictures in the resulting docx (not as raw tokens or markdown source).
- If a value does not look like block markdown, the legacy single-paragraph path (`apply_formatted_text_to_paragraph`) is used unchanged, preserving prior behaviour for short scalar values like `{{CustomerName}}` or `{{Version}}`.

### Stage 4: TOC / Field Auto-Update

After saving the `.docx`, the converter patches `word/settings.xml` inside the ZIP archive to inject ``. This instructs Word (and LibreOffice) to refresh all document fields — including the Table of Contents — the first time the file is opened. No external dependencies (Word COM, LibreOffice macro) are required; the patch uses only Python's `zipfile` and `xml.etree.ElementTree` modules.

The patch is applied automatically on every conversion. If it fails for any reason, a `[WARNING]` is printed but the `.docx` is still usable — the user simply needs to manually update the TOC (`Right-click TOC → Update Field`).

## Markdown Structure Requirements

The markdown must follow this structure for proper conversion:

```markdown
# Release Note – [Customer Name]

| Field | Value |
|-------|-------|
| **CCN Nr.** | DSD-XXXX |
| **Issue Nr.** | IXXXX |
| **Version** | X.X.X.X |
| **Date** | DD/MM/YYYY |
| **Title** | Description |
| **Released By** | Author |

---

## Change Request Details

Content here...

---

## Testing Setup

Content here...

---

## Testing Steps

Content here...
```

## Core Workflow

The conversion follows these steps:

### Step 1: Validate Inputs

- Verify `python-docx` is installed; if not, instruct the user to `pip install python-docx` and do not proceed.
- Confirm the source markdown file exists.
- Run `--list-templates`; auto-select if only one template exists, otherwise ask the user to choose.

### Step 2: Parse Markdown and Generate JSON

- Parse the markdown file extracting frontmatter fields, H1 heading, metadata table fields, and H2 section bodies (located via `` anchors).
- Resolve the artefact type via the unified field map. The converter raises a `ValueError` if no artefact type can be determined (the markdown must carry a `template:` frontmatter entry, a matching `id:`, or a filename pattern declared in the registry).
- Emit `{{Label_}}` / `{{Label_Section_}}` placeholders for the resolved language (`--language` → frontmatter `language:` → `"en"`).
- If a required metadata field is missing, the converter aborts with an error listing the missing fields. Optional missing fields use their `default` value or leave the `{{Placeholder}}` unchanged.
- Write the sections dict to a `.json` file alongside the output.

### Step 3: Substitute Placeholders in Template

- Open the template with `python-docx`
- Walk all paragraphs, tables, headers, and footers
- Replace every `{{Key}}` placeholder with the corresponding value from the JSON
- Save the result as `.docx`

### Step 4: Verify Output

- Confirm the `.docx` file was created
- Confirm the output `.docx` file size is at l

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [fernandoartalf](https://github.com/fernandoartalf)
- **Source:** [fernandoartalf/AL-Copilot-Skills-Collection](https://github.com/fernandoartalf/AL-Copilot-Skills-Collection)
- **License:** MIT
- **Homepage:** https://alcopilotskills.com/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-fernandoartalf-al-copilot-skills-collection-documentation-bc-md-to-docx-converter
- Seller: https://agentstack.voostack.com/s/fernandoartalf
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
