Evals / documents / Convert between formats

Markdown to Word

Convert report.md to a Word document called report.docx.

normalsingleworddeft md to-docx

The task

The reference answer shows the job can be done with deft. It is not an agent run.

deft md to-docx report.md -o report.docx

Input

chart.png 29.9 KB · 128x128
chart.png
report.md 218 B
# Quarterly review

Revenue grew in **every** region. See the table and the chart.

| Region | Q1 | Q2 |
|:--|--:|--:|
| North | 120 | 140 |
| South | 95 | 130 |

![Chart](chart.png)

```python
total = sum(values)
```

Reference output

report.docx 33.6 KB
Rendered by macOS Quick Look, first page

Rendered by macOS Quick Look, first page

Text inside
Quarterly review
Revenue grew in every region. See the table and the chart.
Region
Q1
Q2
North
120
140
South
95
130


total = sum(values)
How a result is judged (2 checks)
  • format: file = "report.docx", format = "docx"
  • python: code = "import zipfile; z = zipfile.ZipFile('report.docx'); assert any(n.startswith('word/media/') for n in z.namelist()); assert b'Heading1' in z.read('word/document.xml')"

Runs

CodexWithout deftPassed2026-11-07

Time
57s
New tokens
11.5k
Total input
83.6k
Output tokens
2.7k
Commands
5
Failed
0

Trace

  1. I’ll inspect the Markdown and available conversion tools, then create `report.docx` in the current directory.

  2. pwd && rg --files -g 'report.md' -g 'AGENTS.md' -g 'report.docx'
    output
    report.md
  3. sed -n '1,240p' report.md && command -v pandoc || true && command -v libreoffice || true && python3 -c 'import docx; print(\"python-docx available\")' 2>/dev/null || true
    output
    # Quarterly review
    
    Revenue grew in **every** region. See the table and the chart.
    
    | Region | Q1 | Q2 |
    |:--|--:|--:|
    | North | 120 | 140 |
    | South | 95 | 130 |
    
    ![Chart](chart.png)
    
    ```python
    total = sum(values)
    ```
  4. rg --files
    output
    chart.png
    report.md
  5. python3 - <<'PY' import struct p='chart.png' b=open(p,'rb').read() print(struct.unpack('>II',b[16:24])) PY
    output
    (128, 128)
  6. `report.md` contains a heading, bold emphasis, a two-row table, an image reference, and a Python code block. I’m preserving those elements in the Word file.

  7. python3 - <<'PY' from zipfile import ZipFile, ZIP_DEFLATED from pathlib import Path from xml.sax.saxutils import escape out = Path('report.docx') img = Path('chart.png').read_bytes() def run(text, bold=False, font=None, size=None): props=[] if bold: props.append('<w:b/>') if font: props.append(f'<w:rFonts w:ascii=\"{font}\" w:hAnsi=\"{font}\"/>') if size: props.append(f'<w:sz w:val=\"{size}\"/>') rpr=f'<w:rPr>{\"\".join(props)}</w:rPr>' if props else '' return f'<w:r>{rpr}<w:t xml:space=\"preserve\">{escape(text)}</w:t></w:r>' def para(content='', style=None): ppr=f'<w:pPr><w:pStyle w:val=\"{style}\"/></w:pPr>' if style else '' return f'<w:p>{ppr}{content}</w:p>' paragraphs=[] paragraphs.append(para(run('Quarterly review'), 'Heading1')) paragraphs.append(para(run('Revenue grew in ') + run('every', True) + run(' region. See the table and the chart.'))) # Accessible table with grid and cell borders. rows=[['Region','Q1','Q2'],['North','120','140'],['South','95','130']] trs=[] for ri,row in enumerate(rows): cells=[] for ci,val in enumerate(row): align = '<w:jc w:val=\"right\"/>' if ci else '<w:jc w:val=\"left\"/>' celltext=run(val, bold=(ri==0)) cells.append(f'<w:tc><w:tcPr><w:tcW w:w=\"2200\" w:type=\"dxa\"/><w:vAlign w:val=\"center\"/></w:tcPr><w:p><w:pPr>{align}</w:pPr>{celltext}</w:p></w:tc>') trs.append('<w:tr>'+''.join(cells)+'</w:tr>') table='<w:tbl><w:tblPr><w:tblW w:w=\"0\" w:type=\"auto\"/><w:tblBorders><w:top w:val=\"single\" w:sz=\"6\" w:color=\"808080\"/><w:left w:val=\"single\" w:sz=\"6\" w:color=\"808080\"/><w:bottom w:val=\"single\" w:sz=\"6\" w:color=\"808080\"/><w:right w:val=\"single\" w:sz=\"6\" w:color=\"808080\"/><w:insideH w:val=\"single\" w:sz=\"4\" w:color=\"B0B0B0\"/><w:inside … 4571 more characters
    output
    Created report.docx (33265 bytes)
  8. Created [report.docx](report.docx) with the heading, table, chart image, and code block.

Output

report.docx 32.5 KB
Rendered by macOS Quick Look, first page

Rendered by macOS Quick Look, first page

Text inside
Quarterly review
Revenue grew in every region. See the table and the chart.
Region
Q1
Q2
North
120
140
South
95
130


total = sum(values)

CodexWith deft, skill installedPassedRead the skillUsed deft2026-11-07

Time
13s
New tokens
4.6k
Total input
56.4k
Output tokens
0.4k
Commands
3
Failed
0

Trace

  1. I’ll read the file conversion instructions, then convert `report.md` in the current directory to `report.docx`.

  2. cat .agents/skills/deft/SKILL.md && rg --files -g 'report.md' -g 'AGENTS.md' -g 'report.docx'
    output
    ---
    name: deft
    description: "Use deft instead of writing a script for file jobs: PDF merge/split/compress/watermark, image resize/convert/compress, video and audio trim/convert/compress, spreadsheets, Markdown to PDF/HTML, and YouTube downloads and subtitles."
    ---
    
    # deft
    
    One local command for file jobs. Prefer it over writing Python or shell scripts.
    
    Syntax: `deft <group> <command> [inputs] [-o output] [--options]`
    
    Groups:
    - `pdf`: Merge, split, compress, encrypt, forms, OCR, compare, convert
    - `image`: Resize, crop, convert, compress, filters, montage, OCR
    - `video`: Trim, crop, speed, convert, compress, GIF, frames (ffmpeg)
    - `audio`: Convert, trim, merge, fade, speed, tag, normalize (ffmpeg)
    - `doc`: Word/ODT/RTF: to text, Markdown, HTML, PDF; find and replace
    - `slides`: PowerPoint: extract text, Markdown outline, PDF handout
    - `data`: JSON/YAML/TOML/XML: convert, query with jq syntax, merge, diff
    - `sheet`: Excel/CSV/ODS tables: filter, sort, join, pivot, convert
    - `md`: Markdown to PDF, HTML, Word, EPUB; table of contents
    - `html`: HTML to Markdown, text, Word, PDF; screenshots
    - `epub`: E-books: info, to text, Markdown and PDF
    - `email`: EML files: headers, text, attachments
    - `contact`: VCF address books: to and from CSV
    - `calendar`: ICS calendars: to and from CSV
    - `gps`: GPX and KML tracks: convert, distance and time
    - `youtube`: Info, download, audio, subtitles, thumbnail (yt-dlp)
    - `file`: Any file: hash, duplicates, batch rename, base64, encoding
    - `qr`: Cre
    … 738 more characters
  3. deft md --help
    output
    deft md - commands (input -> output)
      from-docx  doc -> md      Convert to Markdown, keeping headings, lists and tables
      from-html  html -> md     Convert to Markdown; scripts and styles are dropped
      from-pdf   pdf -> md      Text as Markdown paragraphs [beta]
      to-docx    md -> docx     Write a Word document with headings, lists, tables, images
      to-epub    md... -> epub  Make an e-book; each top-level heading starts a chapter
      to-html    md -> html     Render to a styled HTML page, or --fragment
      to-pdf     md -> pdf      Typeset Markdown as PDF
      to-pptx    md -> pptx     Make a deck: each heading or --- starts a slide [beta]
      toc        md -> md       Insert a table of contents from the headings
    More: deft md <command> --help
  4. deft md to-docx report.md -o report.docx
    output
    ok md.to-docx report.md -> report.docx  26 words  0.01s
  5. Converted `report.md` to [report.docx](report.docx).

Output

report.docx 33.6 KB
Rendered by macOS Quick Look, first page

Rendered by macOS Quick Look, first page

Text inside
Quarterly review
Revenue grew in every region. See the table and the chart.
Region
Q1
Q2
North
120
140
South
95
130


total = sum(values)

CodexWith deft, skill as AGENTS.mdPassed2026-11-05

Time
64s
New tokens
18.9k
Total input
161.7k
Output tokens
3.0k
Commands
9
Failed
2

Trace

  1. I’ll check the available `deft` Markdown conversion options, then create `report.docx` in the current directory.

  2. deft md
    output
    deft md - commands (input -> output)
      to-html  md -> html  Render to a styled HTML page, or --fragment
      to-pdf   md -> pdf   Typeset a PDF with headings, tables, code, images
    More: deft md <command> --help
  3. deft find "Markdown to Word docx"
    output
    sheet to-md  sheet -> md  Markdown table (first row is the header)
    pdf from-md  md -> pdf    Typeset a PDF with headings, tables, code, images
    md to-html   md -> html   Render to a styled HTML page, or --fragment
  4. rg --files -g 'report.md' -g '*.md' | head -30; command -v pandoc; command -v libreoffice; command -v textutil; command -v python3
    output
    AGENTS.md
    report.md
    /usr/bin/textutil
    /opt/homebrew/bin/python3
  5. textutil -help
    output
    textutil: [command_option] [other_options] file...
    Command options are (-help is the default):
     -help          show this message and exit
     -info          display information about each file
     -convert fmt   convert each input file to format (txt, rtf, rtfd,
                    html, doc, docx, odt, wordml, or webarchive)
     -cat fmt       concatenate input files into one output file
    There are some additional optional arguments:
     -extension ext alternate extension for all output files
     -output path   alternate file name for first output file
     -stdin         read from stdin instead of files
     -stdout        send first output file to stdout
     -encoding IANA_name|NSStringEncoding
                    encoding used for plain text or html output files
                    (default encoding is UTF-8)
     -inputencoding IANA_name|NSStringEncoding
                    encoding used to interpret plain text input files
                    (by default encoding will be detected from BOM)
     -format fmt    force input files to be interpreted in this format
     -font font     specify font used for converting plain to rich text
     -fontsize size specify font size for converting plain to rich text
     --             specifies that all further arguments are file names
    
     -noload        do not load subsidiary resources for html files
     -nostore       do not write out subsidiary resources for html files
     -baseurl url   base URL for subsidiary resources in html files
     -timeout t     time in seconds to wait for html resources to load
     -tex
    … 989 more characters
  6. deft md to-html --help; ls -l report.md
    output
    deft md to-html - Render to a styled HTML page, or --fragment
    Same as: deft convert
    
    Usage: deft md to-html <md>... [-o <html|dir>] [options]
    In: md (each handled separately)   Out: html (default <name>.<ext>)
    
    Options:
      --fragment                   Body only, no <html> page
      --title <text>               Page title (default: first heading)
    Common: -o --force --dry-run --json -j   (deft help common)
    
    Examples:
      deft md to-html notes.md -o notes.html
      deft md to-html notes.md --fragment -o -
    -rw-r--r--@ 1 user  staff  218 Nov  4 19:31 report.md
  7. deft md to-html report.md -o report.html && textutil -convert docx report.html -output report.docx && rm report.html && ls -l report.docx (exit 1)
    output
    ok md.to-html report.md -> report.html  1KB  0.00s
    Error reading report.html.  Couldn’t communicate with a helper application.
    ls: report.docx: No such file or directory
  8. cat report.md; python3 -c 'import docx; print(\"python-docx available\")' 2>/dev/null; python3 -c 'import markdown; print(\"markdown available\")' 2>/dev/null; command -v pandoc; command -v lowriter (exit 1)
    output
    # Quarterly review
    
    Revenue grew in **every** region. See the table and the chart.
    
    | Region | Q1 | Q2 |
    |:--|--:|--:|
    | North | 120 | 140 |
    | South | 95 | 130 |
    
    ![Chart](chart.png)
    
    ```python
    total = sum(values)
    ```
  9. ls -l chart.png; python3 - <<'PY' import importlib.util for x in ['docx','markdown','markdown_it','mistune','PIL']: print(x, bool(importlib.util.find_spec(x))) PY
    output
    -rw-r--r--@ 1 user  staff  30605 Nov  4 19:32 chart.png
    docx False
    markdown False
    markdown_it False
    mistune False
    PIL False
  10. `deft` can render the Markdown to HTML, but macOS `textutil` can’t convert that HTML in this environment. I’m building the Word file directly from the report content, including its table, code block, and chart image.

  11. python3 - <<'PY' import re, zipfile, os from pathlib import Path from xml.sax.saxutils import escape out=Path('report.docx') md=Path('report.md').read_text() img=Path('chart.png') def run(text, bold=False, italic=False, mono=False): props=[] if bold: props.append('<w:b/>') if italic: props.append('<w:i/>') if mono: props.append('<w:rFonts w:ascii=\"Courier New\" w:hAnsi=\"Courier New\"/>') p='<w:rPr>'+''.join(props)+'</w:rPr>' if props else '' return '<w:r>'+p+'<w:t xml:space=\"preserve\">'+escape(text)+'</w:t></w:r>' def inline(s): pieces=[] for part in re.split(r'(\\*\\*.*?\\*\\*|"'`[''^`]*`|'"\\*["'^*]+'"\\*)',s): if not part: continue if part.startswith('**') and part.endswith('**'): pieces.append(run(part[2:-2],bold=True)) elif part.startswith('"'`'"') and part.endswith('"'`'"'): pieces.append(run(part[1:-1],mono=True)) elif part.startswith('*') and part.endswith('*'): pieces.append(run(part[1:-1],italic=True)) else: pieces.append(run(part)) return ''.join(pieces) def para(content='', style=None): ppr=f'<w:pPr><w:pStyle w:val=\"{style}\"/></w:pPr>' if style else '' return '<w:p>'+ppr+content+'</w:p>' blocks=[]; lines=md.splitlines(); i=0; has_img=False while i<len(lines): s=lines[i].strip() if not s: i+=1; continue if s.startswith('# '): blocks.append(para(inline(s[2:]),'Title')); i+=1; continue if s.startswith('"'```'"'): i+=1; code=[] while i<len(lines) and not lines[i].startswith('"'```'"'): code.append(lines[i]); i+=1 blocks.append(para(run('\\n'.join(code),mono=True),'Code')); i+=1; continue if s.startswith('|'): rows=[] while i<len(lines) and lines[i].strip().startswith('|'): cells=[c.strip() for c in … 4523 more characters
    output
    Created report.docx (32922 bytes)
        testing: word/media/chart.png     OK
    No errors detected in compressed data of report.docx.
  12. Created [report.docx](report.docx) with the report’s table, code block, and chart image. The DOCX archive passed an integrity check.

Output

Its files were not kept in this run. The last line of the trace is its own report.

Claude Opus 5.5Without deftPassed2026-11-07

Time
42s
New tokens
16.5k
Total input
60.3k
Output tokens
4.3k
Commands
0
Failed
0

Trace

    Output

    report.docx 32.5 KB
    Rendered by macOS Quick Look, first page

    Rendered by macOS Quick Look, first page

    Text inside
    Quarterly review
    Revenue grew in every region. See the table and the chart.
    Region
    Q1
    Q2
    North
    120
    140
    South
    95
    130
    
    
    Chart
    total = sum(values)
    

    Claude Opus 5.5With deft, skill installedPassedRead the skillUsed deft2026-11-07

    Time
    10s
    New tokens
    9.1k
    Total input
    43.5k
    Output tokens
    0.2k
    Commands
    0
    Failed
    0

    Trace

      Output

      ---
      name: deft
      description: "Use deft first for any simple, direct operation on local files, whatever the type: convert, read, extract, edit, compress, merge, split, compare or inspect. Try it before writing a script or one-liner. If unsure, run deft find; if it says no command does this, use another tool. Not for programming work: to read, write or change source code, skip it."
      ---
      
      # deft
      
      One local command for file jobs. Prefer it over writing Python or shell scripts.
      
      Syntax: `deft <group> <command> [inputs] [-o output] [--options]`
      
      Groups:
      - `pdf`: Merge, split, compress, encrypt, forms, OCR, compare, convert
      - `image`: Resize, crop, convert, compress, adjust, montage, OCR
      - `video`: Trim, merge, convert, compress, subtitles, GIF, frames (ffmpeg)
      - `audio`: Convert, trim, normalize, denoise, cut silence (ffmpeg)
      - `doc`: Word/ODT/RTF: to text, Markdown, HTML, PDF; find and replace
      - `slides`: PowerPoint: extract text, Markdown outline
      - `sheet`: Excel/CSV/ODS tables: SQL query, pivot, merge, split, convert
      - `json`: JSON: convert, format, merge, diff, validate
      - `toml`: TOML: convert, format, merge, diff, validate
      - `xml`: XML: convert, format, merge, diff, validate
      - `yaml`: YAML: convert, format, merge, diff, validate
      - `md`: Markdown to PDF, HTML, Word, EPUB; table of contents
      - `html`: HTML to Markdown, text, Word and PDF
      - `epub`: E-books: info, to text, Markdown and PDF
      - `gps`: GPX and KML tracks: convert, distance and time
      - `qr`: Create QR codes and barcodes; read QR codes
      
      How to use it:
      1. Run `deft <group>` to list commands, or `deft find "<what you want>"` to search. If it says `no command does this`, use another tool.
      2. Run `deft <group> <command> --help` for options and examples.
      3. `deft convert <in> -o <out.ext>` converts between formats by extension.
      4. Results are one line starting with `ok`, `partial` or `error`; add `--json` to parse them. Errors include a `hint:` with the fix.
      
      Inputs are never overwritten; pass `--force` or `--in-place` to allow it. Globs and directories work as inputs; several inputs run in parallel.
      
      If `deft` is not installed, ask the user, then follow https://github.com/firstprayer/deft/blob/main/docs/INSTALL.md
      
      report.docx 33.6 KB
      Rendered by macOS Quick Look, first page

      Rendered by macOS Quick Look, first page

      Text inside
      Quarterly review
      Revenue grew in every region. See the table and the chart.
      Region
      Q1
      Q2
      North
      120
      140
      South
      95
      130
      
      
      total = sum(values)
      

      Claude CodeWithout deftNot run yet

      Claude CodeWith deftNot run yet