Skip to content

Skills · converters

file-to-markdown

Convert a Word document, PowerPoint deck, Excel workbook, web page, CSV, JSON, text file, email (.eml or Outlook .msg) or PDF into clean Markdown. It keeps headings, lists, tables, links, bold and italic, slide titles and speaker notes. It saves embedded images beside the Markdown and links them, shows tracked changes as pending rather than accepted, and reports everything else it dropped (charts, comments, formulas), so the content can be read, reviewed or fed to another skill. Use when the user hands over a .docx, .pptx, .xlsx, .html, .csv, .json, .eml, .msg or .pdf and says "read this", "turn this into markdown", "extract the text", or wants it critiqued or briefed.

Pack convertersTier lightJob Get a draft ready to shipv0.4.0

npx skilldrop-cli install file-to-markdown
Other ways to install

With the sibling skills it hands off to:

npx skilldrop-cli install file-to-markdown --with-related

The whole pack it belongs to:

npx skilldrop-cli install --pack converters

By hand, from a clone of the repo:

cp -R packs/converters/skills/file-to-markdown ~/.claude/skills/

In Claude Code, with the converters plugin installed, invoke it as:

/converters:file-to-markdown

Try it

A realistic prompt from the skill's acceptance evals:

Northwind sent us their Q3 roadmap as northwind-q3.pptx (14 slides, a few charts, speaker notes on most slides). Turn it into markdown so we can critique it.

What a good result looks like

Example output

Example: a client's files converted for review · the skill's own worked example, source

Worked example

What the user said: "Acme sent their migration brief, last quarter's roadmap deck and the budget workbook, plus a link to their status page. Turn them into something we can review."

The Word, PowerPoint and Excel files below are small test files built for this example (not shipped, because the repo keeps examples as text). The web page is inputs/fabrikam-status.html; a JSON export is inputs/accounts.json. Every block of output is what the script actually printed.

acme-brief.docx

A Word brief with Title and Heading styles, bulleted and numbered lists, a hyperlink, a table, an image, a comment and a tracked change. It is in inputs/.

python3 scripts/to_markdown.py acme-brief.docx -o acme-brief.md

stderr:

converted acme-brief.docx -> acme-brief.md: 4 headings, 5 list items, 1 images saved, 1 tables, 1 tracked deletions shown as {-- --}, 1 tracked insertions shown as {++ ++}
dropped (not in the Markdown): 1 comments
note: tracked changes are pending, not accepted: {++added++} and {--removed--} mark them. Accept or reject them in Word to settle the text

The image is saved as acme-brief-images/image1.png and linked where it sat. The tracked change stays pending.

Markdown:

# Acme migration brief

## Background

We are moving **three services** to the new platform, *starting in November*. See the [project page](https://acme.example/migration).

### Goals

- Cut hosting cost
- Retire the 2014 cluster
   - Including the batch jobs

### Steps

1. Inventory services
2. Pilot with billing

Architecture sketch:

![Architecture sketch](acme-brief-images/image1.png)

| **Risk** | **Owner** | **Due** |
|---|---|---|
| Vendor lock-in | Priya | 2026-11-01 |
| Data residency \| EU | Tom | 2026-12-15 |

Open question: who signs off the cutover?

Kept text {--old wording--}{++new wording++}

northwind-roadmap.pptx

A three-slide deck with a title slide, bullets at two levels, a picture and speaker notes on slide 2. It is in inputs/.

python3 scripts/to_markdown.py northwind-roadmap.pptx -o northwind-roadmap.md

stderr:

converted northwind-roadmap.pptx -> northwind-roadmap.md: 3 slides, 1 images saved, 1 slides with speaker notes
dropped: nothing detected

Markdown:

## Slide 1: Northwind Q3 roadmap

Product review, 12 October

## Slide 2: What we shipped

- **Self-serve onboarding**
  - Cut setup time from 3 days to 4 hours
- Usage-based billing

![Shipped features](northwind-roadmap-images/image1.png)

> **Speaker notes:** Mention the two pilot customers by segment, not by name.
>
> Pause for questions here.

## Slide 3: Adoption

Source: product analytics, Sept

acme-budget.xlsx

A two-sheet workbook with shared strings, a rich-text cell, two date-formatted cells, two SUM formulas and a chart.

python3 scripts/to_markdown.py acme-budget.xlsx -o acme-budget.md

stderr:

converted acme-budget.xlsx -> acme-budget.md: 2 sheets
dropped (not in the Markdown): 1 charts, 2 formulas (cached values kept)

Markdown:

## Budget

| Line item | Q3 | Q4 | Start date |
|---|---|---|---|
| Hosting | 12500 | 13250.5 | 2026-10-01 |
| Support contract | 4000 | 4000 | 2026-11-01 |
| Total | 16500 | 17250.5 |  |

## Notes

| Figures are placeholders. |
|---|

contoso.docx

An HTML page saved as .docx by macOS textutil, which writes no heading styles and types bullets as characters.

python3 scripts/to_markdown.py contoso.docx -o contoso.md

stderr:

converted contoso.docx -> contoso.md: 2 headings, 4 list items
dropped: nothing detected
note: no heading styles in the document; 2 heading(s) inferred from font size and bold, so check the levels
note: list items written as typed bullets or numbers were turned into a Markdown list

Markdown:

# Contoso onboarding guide

Welcome. Read **this first** and then *the checklist*. Questions go to the help page.

## Checklist

- Get a laptop
- Join the team channel
1. Book IT setup
2. Meet your buddy

**Day**

**What**

Mon

Setup

Tue

Shadowing

The table and the link in contoso.docx were already gone before this script saw the file: textutil wrote the table cells as separate paragraphs and the link as blue underlined text. That is why the reply must say "check the levels", and why a .docx exported from Word itself converts better than one produced by textutil.

fabrikam-status.html

python3 scripts/to_markdown.py examples/inputs/fabrikam-status.html -o out.md

stderr:

converted fabrikam-status.html -> out.md: 3 headings, 1 tables, 4 list items, 1 code blocks, 1 image links
dropped (not in the Markdown): 3 scripts/styles, 1 navigation menus, 1 images (embedded data)

Markdown:

# Fabrikam service status

All systems **operational** except *search*. Details on the [incident page](https://status.fabrikam.example/search).

## Open incidents

| ID | Service | Since |
|---|---|---|
| INC-7 | Search \| EU | 09:40 UTC |
| INC-8 | Exports | 11:05 UTC |

## What to do

1. Retry the search
2. If it fails, use filters:
   - by date
   - by owner

> We will post the next update by 14:00 UTC.

curl -s https://status.fabrikam.example/api | jq .


![Uptime chart](uptime.png)

accounts.json

python3 scripts/to_markdown.py examples/inputs/accounts.json -o out.md

stderr:

converted accounts.json -> out.md: 2 rows
dropped: nothing detected

Markdown:

| id | name | tags | active | region |
|---|---|---|---|---|
| 1 | Acme | \["a", "b"\] | true |  |
| 2 | Northwind |  |  | EU |

ops-review.pdf

A two-page PDF printed from Chrome, from an HTML page of the same report, so it has a title, a nested list, a table, a chart image, a Mermaid diagram kept as text, and a code block.

python3 scripts/to_markdown.py ops-review.pdf -o ops-review.md

stderr:

converted ops-review.pdf -> ops-review.md: 6 pdf headings guessed, 1 pdf tables, 2 pdf code blocks, 2 pages
dropped: nothing detected
note: PDF gives no structure, so it is inferred from the layout: tables from aligned columns, lists from indentation, and short standalone lines as ### headings. Check them against the PDF; images and charts are not recovered

Page 1 of ops-review.md, unedited:

<!-- page 1 -->

### Northwind Health: Q3 platform review

Prepared for the operations committee. Figures are placeholders for this example.

### Summary

- Uptime held at the target for the quarter
- Two incidents, both under 30 minutes
  - INC-101: login latency
  - INC-102: delayed reminder emails
- Migrate reminders to the new queue
- Retire the old scheduler

Decision needed: approve the scheduler retirement for October.

### Numbers

| Metric | Q2 | Q3 | Change |
|---|---|---|---|
| Uptime | 99.90% | 99.95% | up |
| Incidents | 4 | 2 | down |
| p95 login (ms) | 410 | 380 | down |

### Weekly active members, Q1 to Q4 (illustrative)

What to tell the user: the headings were guessed from short standalone lines, and the chart image is gone (a PDF's text layer has no images). The table and the list came through. A page with two columns of prose side by side is read in column order instead, with nothing inferred, and the script says which pages those were. A table that starts at the bottom of one page and continues on the next loses its header row to the previous page.

What to tell the user (for the first three files)

I converted all three. Before we review, here is what is not in the Markdown:

  • Brief: one reviewer comment. Its image is saved beside the Markdown and linked. One tracked change is still pending ("old wording" deleted, "new wording" inserted), so it is marked, not accepted: say which way it should go.
  • Deck: nothing. Its picture is saved and linked, and slide 2's speaker notes are kept.
  • Workbook: the chart, and the two SUM formulas. Their last calculated values (16500 and 17250.5) are in the table, but the formulas themselves are not.

Want me to run a critique of the brief next?

How it's checked

Measured against plain Claude Code (2026-10-23): it met 66% of its checks, against 39% without it, and a blind judge preferred the agent without it. How skills are checked

39 assertions across 6 acceptance evals, and 9 trigger queries. Every skill's checks

What the output must do
  • Runs to_markdown.py on northwind-q3.pptx and writes a .md file
  • Each slide is a '## Slide N: title' section, in slide order
  • Speaker notes appear under their own slide, not collected at the end
  • States at the top of the reply how many charts and images were dropped, using the script's counts
  • Says that any critique cannot cover the chart contents because they were not converted
  • Does not invent slide content, numbers or chart values that are not in the converted text
  • Runs to_markdown.py on the PDF
  • If pdftotext is missing, passes on the install step (brew install poppler / apt install poppler-utils) and does not attempt to read the PDF bytes
  • If it converts, says the PDF gives text only: no headings or tables recovered
  • Does not summarise or paraphrase in place of the converted text
  • Writes harbourline-shift-policy.md, with the document's title and section headings as Markdown headings and the overtime rates as a Markdown table
  • On the notice period, it reports that the document has an unaccepted tracked change from 48 to 24 hours and a comment saying legal have not signed off the change; it does not give 24 hours (or 48) as the settled answer without mentioning this
  • It says the night-sailing rates are not in the file (the document only refers to an Appendix B that is not included) and does not supply any night rates
  • It points out that the catering assistant row does not add up (13.10 + 1.90 is 15.00, not 15.20), and does not silently change the figure in the Markdown
  • Its list of what did not survive names the image (Figure 1) and the reviewer's comment, rather than claiming nothing was lost
  • The crew portal link is kept as a working Markdown link to its address, and the bold and italic in the closing note are kept
  • It does not invent text, rates, approvals or appendix content that are not in the .docx
  • Gives a Markdown table with all six seed varieties and, for each, the type, number of packets, donor plot and notes as in the file
  • Values that contain commas ("Tomato, cherry 'Sungold'", "Squash, butternut" and the quoted notes) stay in one cell each, and no columns are shifted
  • The blank line and the empty row in the file do not appear as empty rows in the table
  • If the Total row is kept, it notes briefly that the packets add up to 36, not 38; it does not silently change any figure
  • It does not invent varieties, numbers, donors or notes that are not in the file
  • The reply is the table plus at most a couple of short lines, with no long explanation, and it delivers without asking questions first
  • Writes a Markdown file with 'Master Supply Agreement' as the top heading and the five numbered sections ('1. Payment terms' through '5. Price schedule') as subheadings. The three supplier obligations are a bulleted list, 'EUR 250,000' is bold and 'gross negligence' is italic.
  • Renders the price schedule as a Markdown table with the header Part number | Unit price | Minimum order quantity and the rows VP-110 / EUR 42.00 / 500 and VP-220 / EUR 57.50 / 250. Keeps 'supplier portal' as a Markdown link to https://supplier.example.com/terms.
  • Shows the payment-terms change as pending rather than accepted: the deleted '30' and the inserted '60' are both visible and marked as a proposed change by J. Okafor. It does not present the clause as simply 'payable within 60 days' as if it were agreed.
  • Shows the deleted sentence 'No termination fee applies.' (M. Lindqvist) as a pending deletion that is still visible and marked, not silently dropped. Shows the ISO 9001 obligation bullet as a pending insertion by J. Okafor, not as an agreed obligation.
  • Surfaces R. Achterberg's comment on the EUR 250,000 liability cap (asking to raise it to EUR 500,000, marked 'Not agreed yet') as an open item, either inline or in a notes or dropped-items list.
  • Answers the question directly with a short list of what is not yet agreed: exactly three tracked changes (30 to 60 days; the inserted ISO 9001 obligation; the deleted termination-fee sentence) and one open comment on the liability cap. It accepts or rejects none of them and leaves supplier_agreement_v3.docx unchanged.
  • Saves the embedded logo image as an image file next to the Markdown (for example in a sibling folder) and links it from the Markdown with a relative image link.
  • Invents nothing that is not in the document: no extra clauses, parties, prices, dates, authors or comments. Names, figures and the portal URL match the file exactly.
  • Produces Markdown covering both files. The three slide titles 'Q3 Offsite: Where We Landed', 'Revenue by region' and 'APAC launch' appear in that order as headings, each with its bullets, and each slide's speaker notes are kept and clearly labelled as notes under the right slide.
  • Converts both worksheets, each identified by its name: Regions as a Markdown table (EMEA 120 / 110, North America 140 / 130, LATAM 160 / 150, Total row) and Assumptions (FX rate GBP/EUR 1.17; Source: Finance close 10 Oct 2026).
  • Reports that the bar chart on slide 2 is not reproduced as a chart, either as a dropped item or as a text summary. Any figures it gives for the chart are exactly the values cached in it (EMEA 120, North America 140, LATAM 160), and no chart values are made up.
  • Flags that the Regions Total in B5 is the formula =SUM(B2:B4) with a stored value of 410, while the three rows sum to 420 (120+140+160). It treats the total as stale or inconsistent rather than silently showing 410 or quietly correcting it to 420. The Plan total of 390 does match its rows.
  • Warns the user, beyond just copying the notes, that the slide 3 notes say the £250k APAC first-year target must not go into the board pack (finance hasn't signed it off; say 'target under review'). It neither leaves £250k in board-pack-ready output without a warning nor deletes it silently.
  • Relays the 'real story' from the slide 2 notes, since the user pointed to them: North America beat plan only because of the one-off Kestrel Sports order (£35k), and without it NA is at 105 against a plan of 130.
  • Invents nothing that is not in the files: no extra slides, regions, APAC actuals, prior-quarter LATAM figures, chart values or names beyond those present.
  • Is the right size for a rushed request. It delivers the Markdown file(s) straight away with a brief summary of the flagged issues, not a long essay or a rewritten board pack, and does not hold up the work with clarifying questions.
When it should and shouldn't activate

Should activate:

  • turn this Outlook .msg email into markdown so I can paste it into the ticket
  • Convert this Word doc to markdown
  • Extract the text and tables from budget.xlsx so you can read it
  • Here's the deck as a pptx, pull out the slide text and speaker notes
  • Turn this saved HTML page into clean markdown

Should leave to another skill:

  • Convert my markdown notes into a .docx for the client
  • Make an HTML version of README.md I can email
  • Here's a messy Slack thread, turn it into a brief for the design doc
  • Review this design doc markdown and tell me what to fix

Hands off to

Related skills

md-to-docx, md-to-xlsx

Source

SKILL.md · the whole folder on GitHub.