Evals / documents / Read a document
Word to Markdown
Convert demo.docx to demo.md, keeping headings and tables.
The task
The reference answer shows the job can be done with deft. It is not an agent run.
deft doc to-md demo.docx -o demo.md
The files are not kept here, because demo.docx is under GPL-3.0. Its source is linked.
Input
No input file.
Reference output
No output yet.
How a result is judged (1 check)
- text: file = "demo.md", contains = ["# Tables", "| ---"]
Runs
CodexWithout deftPassed2026-11-07
- Time
- 30s
- New tokens
- 10.4k
- Total input
- 73.7k
- Output tokens
- 1.2k
- Commands
- 4
- Failed
- 1
Trace
I’ll inspect the document structure and convert its text, headings, and tables into Markdown in the current directory.
rg --filesoutput
demo.docx
python - <<'PY' from docx import Document p='demo.docx' d=Document(p) for x in d.element.body: tag=x.tag.rsplit('}',1)[-1] if tag=='p': from docx.text.paragraph import Paragraph para=Paragraph(x,d) print('P',repr(para.text), 'STYLE',para.style.name) elif tag=='tbl': from docx.table import Table t=Table(x,d) print('TABLE') for row in t.rows: print([c.text for c in row.cells]) PY (exit 1)output
Traceback (most recent call last): File "<stdin>", line 1, in <module> ModuleNotFoundError: No module named 'docx'
python - <<'PY' import zipfile, xml.etree.ElementTree as ET z=zipfile.ZipFile('demo.docx') r=ET.fromstring(z.read('word/document.xml')) ns={'w':'http://schemas.openxmlformats.org/wordprocessingml/2006/main'} body=r.find('w:body',ns) for el in body: tag=el.tag.split('}')[-1] if tag=='p': s=''.join(t.text or '' for t in el.findall('.//w:t',ns)) ppr=el.find('w:pPr/w:pStyle',ns) print('P',repr(s),'STYLE', ppr.get('{%s}val'%ns['w']) if ppr is not None else '') elif tag=='tbl': print('TABLE') for tr in el.findall('w:tr',ns): print([ ''.join(t.text or '' for t in tc.findall('.//w:t',ns)) for tc in tr.findall('w:tc',ns)]) PYoutput
P 'Demonstration of DOCX support in calibre' STYLE Title P 'This document demonstrates the ability of the calibre DOCX Input plugin to convert the various typographic features in a Microsoft Word (2007 and newer) document. Convert this document to a modern ebook format, such as AZW3 for Kindles or EPUB for other ebook readers, to see it in action.' STYLE P 'There is support for images, tables, lists, footnotes, endnotes, links, dropcaps and various types of text and paragraph level formatting.' STYLE P 'To see the DOCX conversion in action, simply add this file to calibre using the “Add Books” button and then click “Convert”. Set the output format in the top right corner of the conversion dialog to EPUB or AZW3 and click “OK”.' STYLE P '' STYLE P 'Text Formatting' STYLE Heading1 P 'Inline formatting' STYLE Heading2 P 'Here, we demonstrate various types of inline text formatting and the use of embedded fonts.' STYLE P 'Here is some bold, italic, bold-italic, underlined and struck out text. Then, we have a superscript and a subscript. Now we see some red, green and blue text. Some text with a yellow highlight. Some text in a box. Some text in inverse video.' STYLE P 'A paragraph with styled text: subtle emphasis followed by strong text and intense emphasis. This paragraph uses document wide styles for styling rather than inline text properties as demonstrated in the previous paragraph — calibre can handle both with equal ease.' STYLE P 'Fun with fonts' STYLE Heading2 … 9659 more characters
python - <<'PY' import zipfile, xml.etree.ElementTree as ET, re z=zipfile.ZipFile('demo.docx') root=ET.fromstring(z.read('word/document.xml')) ns={'w':'http://schemas.openxmlformats.org/wordprocessingml/2006/main'} body=root.find('w:body',ns) out=[] def celltext(tc): return ''.join(t.text or '' for t in tc.findall('.//w:t',ns)).replace('|','\\\\|').replace('\\n','<br>') for el in body: tag=el.tag.split('}')[-1] if tag=='p': text=''.join(t.text or '' for t in el.findall('.//w:t',ns)).strip() if not text: continue style=el.find('w:pPr/w:pStyle',ns) sty=style.get('{%s}val'%ns['w'],'') if style is not None else '' if sty=='Title': out.extend(['# '+text,'']) elif sty.startswith('Heading'): level=re.search(r'\\d+',sty) out.extend([('#'*int(level.group()))+' '+text,'']) elif sty.startswith('TOC'): continue elif sty=='ListParagraph': out.extend(['- '+text,'']) else: out.extend([text,'']) elif tag=='tbl': rows=[] for tr in el.findall('w:tr',ns): rows.append([celltext(tc) for tc in tr.findall('w:tc',ns)]) if not rows: continue width=max(map(len,rows)) rows=[r+['']*(width-len(r)) for r in rows] out.append('| '+' | '.join(rows[0])+' |') out.append('| '+' | '.join(['---']*width)+' |') for row in rows[1:]: out.append('| '+' | '.join(row)+' |') out.append('') open('demo.md','w',encoding='utf-8').write('\\n'.join(out).rstrip()+'\\n') PYConverted [demo.docx](<demo.docx>) to [demo.md](<demo.md>), preserving the headings and tables.
Output
Files not kept here, for licence reasons.
CodexWith deft, skill installedPassedRead the skillUsed deft2026-11-07
- Time
- 16s
- New tokens
- 8.6k
- Total input
- 71.1k
- Output tokens
- 0.5k
- Commands
- 4
- Failed
- 1
Trace
I’ll use the `deft` file conversion workflow to convert the Word document while retaining its structure.
cat .agents/skills/deft/SKILL.md && deft find (exit 2)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; r … 768 more characters
deft find "Word to Markdown" && lsoutput
doc to-md doc -> md Convert to Markdown, keeping headings, lists and tables md to-docx md -> docx Write a Word document with headings, lists, tables, images demo.docx
deft doc to-md --helpoutput
deft doc to-md - Convert to Markdown, keeping headings, lists and tables Same as: deft md from-docx · deft convert Usage: deft doc to-md <doc>... [-o <md|dir>] [options] In: doc (each handled separately) Out: md (default <name>.<ext>) Options: --images <path> Folder for embedded pictures Common: -o --force --dry-run --json -j (deft help common) Examples: deft doc to-md letter.docx -o letter.md deft doc to-md letter.docx --images media -o with-pictures.md Result fields: words, images
deft doc to-md demo.docx -o demo.mdoutput
ok doc.to-md demo.docx -> demo.md 1631 words 0.00s warning: 3 images were not saved; add --images <dir>
Converted [demo.docx](demo.docx) to [demo.md](demo.md), preserving headings and tables. The document contains 3 images, which weren’t saved in this conversion.
Output
Files not kept here, for licence reasons.
Claude Opus 5.5Without deftFailed2026-11-07
demo.md does not contain "| ---"
- Time
- 51s
- New tokens
- 24.1k
- Total input
- 135.9k
- Output tokens
- 5.8k
- Commands
- 0
- Failed
- 0
Trace
Output
Files not kept here, for licence reasons.
Claude Opus 5.5With deft, skill installedPassedRead the skillUsed deft2026-11-07
- Time
- 12s
- New tokens
- 13.9k
- Total input
- 63.0k
- Output tokens
- 0.7k
- Commands
- 0
- Failed
- 0
Trace
Output
Files not kept here, for licence reasons.
Claude CodeWithout deftNot run yet
Claude CodeWith deftNot run yet