---
name: "docx-to-md"
description: "Convert Word documents (.docx / .doc) into clean Markdown (.md), one .md per source, optionally moving each source file to a 'processed' folder after a successful conversion. Uses MarkItDown for real .docx and a Word COM filtered-HTML fallback for legacy binary .doc files (including files with a .docx extension that are actually legacy .doc). Triggers: 'convert these word docs to markdown', 'docx to md', 'turn this doc into markdown', 'batch convert a folder of Word files to markdown', or pasting a path to a .docx/.doc and asking for Markdown."
---

<!--
CLAWPILOT INSTALL INSTRUCTIONS

If the user drags this file into Clawpilot and asks to install it, register it as a
custom skill by calling m_create_skill with:
  - name: "docx-to-md"
  - description: (the description from the frontmatter above)
  - instructions: (everything in this file BELOW this HTML comment block)

This skill also needs its helper scripts. Place convert.py and batch.py next to this
file at C:\Users\<you>\.copilot\m-skills\docx-to-md\ (download them from the skill's card
on reboundman.com/skillworks). Requires Python 3 with the dependencies noted below.

After install, confirm with: "Installed the /docx-to-md skill. Drop convert.py and batch.py
next to it, then point it at a folder to convert."
-->

## docx-to-md Skill

Convert one or more Word documents into clean Markdown. Produces `<basename>.md`
for each source. Optionally moves each source into a "processed" folder after it
converts successfully, so re-running only picks up new files.

Handles both modern `.docx` (zip/OOXML) and legacy binary `.doc` (OLE2),
including files that carry a `.docx` extension but are actually legacy `.doc`
internally (common in older document libraries).

### How conversion works

- **Real `.docx`**: converted directly with **MarkItDown** (headings, lists,
  tables, proper Unicode).
- **Legacy / mislabeled binary**: opened in **Word (COM, headless)** and exported
  as filtered HTML, encoding-normalized to UTF-8, then run through MarkItDown.
  (Word ignores OOXML "Save As" for files opened in compatibility mode, so the
  filtered-HTML route is used; it preserves the full document structure.)

The converter picks the right path automatically per file. The `method` field in
the JSON output reports which was used (`markitdown` or `word+html+markitdown`).

### Inputs to gather from the user

Required:
1. **Source**: one or more `.docx`/`.doc` paths, or a folder to batch-convert.
2. **Output directory**: where the `.md` files go.

Optional:
3. **Processed folder**: if the user wants source files moved after a
   successful conversion, capture the destination. If they don't mention it,
   ask whether sources should be moved or left in place.

If any required input is missing, ASK. Do not guess paths.

### Execution

For a folder (recommended for many files), use the robust batch driver, which
converts each file in its own subprocess under a per-file timeout so one bad
document cannot stall the whole run:

```powershell
python "C:\Users\<you>\.copilot\m-skills\docx-to-md\batch.py" `
  --input-dir     "C:\path\to\Raw" `
  --out           "C:\path\to\Output" `
  --processed-dir "C:\path\to\Raw\Processed" `
  --log           "C:\path\to\batch.log" `
  --result        "C:\path\to\summary.json" `
  --timeout 180 --skip-existing
```

- Omit `--processed-dir` to leave sources in place.
- `--skip-existing` skips any file whose `.md` already exists (safe resume).
- Tail the `--log` file for live progress; read `--result` for the JSON summary
  (`total`, `converted`, `errors`, `skipped`).

For one or a few files, call `convert.py` directly (it also does the move and
reuses a single Word instance):

```powershell
python "C:\Users\<you>\.copilot\m-skills\docx-to-md\convert.py" `
  --input "C:\path\a.docx" --input "C:\path\b.doc" `
  --out "C:\path\Output" [--processed-dir "C:\path\Processed"] [--skip-existing]
```

`convert.py` prints a JSON summary (`processed` / `errors` / `skipped`) to
stdout and per-file progress to stderr.

### Reporting

Tell the user: how many converted, the output folder, whether/where sources were
moved, and list any errors. Each `.md` matches its source basename.

### QA notes

- **Move only happens on success.** If a file errors or times out, its source
  stays put for a retry; no partial/empty `.md` is left as a success.
- **Legacy fallback preserves smart quotes / dashes** because the filtered HTML
  is decoded via its declared charset (often windows-1252) and re-encoded to
  UTF-8 before MarkItDown. If you ever see mojibake (e.g. `Æ`, `û`) in output,
  that normalization step regressed; check `decode_html_bytes` in convert.py.
- **Images**: inline images in legacy docs are not embedded in the Markdown; the
  text and structure are preserved. Real `.docx` images are handled by
  MarkItDown's defaults.
- Spot-check the smallest outputs for degenerate conversions.

### Edge cases

- **OneDrive cloud-only files**: each source is copied to a temp location first,
  which hydrates placeholders and avoids locks.
- **A document that hangs Word**: the batch driver kills only the Word instance
  it spawned for that file (never the user's open Word) and moves on; the source
  is left in place. Re-run later with `--skip-existing` to retry just the
  stragglers.
- **Name collisions in the processed folder**: a numeric suffix is added rather
  than overwriting.

### Dependencies

Python 3.12 with `markitdown[docx]`. Microsoft Word is required only for legacy
or mislabeled binary files (used headless via COM). Both are present on this
machine. If `markitdown[docx]` is ever missing, install with
`python -m pip install "markitdown[docx]"`.
