outlook-to-md
Skill2 starsChanged 50 days agoMITView on GitHub
Turn Outlook mail into organised markdown archives - from a PST export, or from live mail exported by the outlook skill. Use when the user wants to convert or extract a PST or Outlook export to markdown, build a searchable email archive, or keep an existing email archive current.
- Installs packages
1 other project uses a version of this.
Adopted by
Most recently taken up by
- dbhq-uk/outlook-skillword for word, first seen Sep 25, 2026
Other versions1 project uses a version of this.
Word for word · 1
- dbhq-uk/outlook-skill★ 2100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 4 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 59
Add only new emails (skips already-extracted messages by Message-ID):
- Distinctive: line 95
If emails were already extracted with readpst elsewhere, point at the directory:
- Distinctive: line 130
index.csv: All emails with date, sender, recipient, subject, folder, attachment count
- Very specific: line 160
If you run readpst yourself and point this tool at the output, pass -j 0 too.
- Distinctive: line 164
Every extraction produces a verifiable chain of custody:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Outlook Email to Markdown
Turn Outlook mail into an organised, integrity-verified archive of markdown files, raw email backups, and attachments. Reads a PST export, or a directory of .eml files - which is how live mail arrives, via the outlook skill's export verb. Supports full extraction and incremental append mode, so one archive can span both.
Prerequisites
- Python virtual environment set up (run setup.sh if not done)
readpst(system tool from pst-utils), to read a.pst. A directory of.emlfiles does not need it
First-Time Setup
# Set up Python environment (one-time)
${CLAUDE_SKILL_DIR}/setup.shSystem Dependency for PST Files
readpst is the only PST reader. If setup.sh reports it missing, ask the user to install it:
# Ubuntu/Debian
sudo apt install pst-utils
# macOS
brew install libpstExtraction Operations
Full Extraction
Extract all emails from a PST file into markdown:
# Basic extraction
${CLAUDE_SKILL_DIR}/.venv/bin/python ${CLAUDE_SKILL_DIR}/scripts/outlook_to_md.py /path/to/file.pst /path/to/output/
# Verbose output with progress
${CLAUDE_SKILL_DIR}/.venv/bin/python ${CLAUDE_SKILL_DIR}/scripts/outlook_to_md.py /path/to/file.pst /path/to/output/ --verbose
# Include deleted items
${CLAUDE_SKILL_DIR}/.venv/bin/python ${CLAUDE_SKILL_DIR}/scripts/outlook_to_md.py /path/to/file.pst /path/to/output/ --include-deleted --verbose
# Render every date in one zone (default: the offset each message was sent with)
${CLAUDE_SKILL_DIR}/.venv/bin/python ${CLAUDE_SKILL_DIR}/scripts/outlook_to_md.py /path/to/file.pst /path/to/output/ --timezone "Europe/London"
# Fix MAILER-DAEMON sent items (provide the PST owner's email)
${CLAUDE_SKILL_DIR}/.venv/bin/python ${CLAUDE_SKILL_DIR}/scripts/outlook_to_md.py /path/to/file.pst /path/to/output/ --owner-email "user@example.com"Incremental Extraction (Append Mode)
Add only new emails (skips already-extracted messages by Message-ID):
${CLAUDE_SKILL_DIR}/.venv/bin/python ${CLAUDE_SKILL_DIR}/scripts/outlook_to_md.py /path/to/file.pst /path/to/output/ --append --verboseKeeping an Archive Current from Live Mail
A PST is a snapshot. To carry an archive forward, export new mail with the
outlook skill and append it: the two produce the same shape.
- Export the folder as
.emlwith the outlook skill'sexportverb, for exampleexport "Inbox/Clients" ./staging/ --since 2026-07-01. Its own instructions give the command.--count Ncaps how many messages it writes, newest first (default 1000). If the outlook skill is not installed or not configured, say so and stop: this skill cannot read live mail. - Append the staging directory to the existing archive:
${CLAUDE_SKILL_DIR}/.venv/bin/python ${CLAUDE_SKILL_DIR}/scripts/outlook_to_md.py \
./staging/ ./archive/ --appendDeduplication is by Message-ID, so a --since window that overlaps what is
already archived costs bandwidth and nothing else. Graph-sourced mail is
recorded under the pst_folder index column like any other; the column means
"the folder this message came from".
This guarantee depends on the message actually carrying a Message-ID
header. Received mail always has one, but a message with none (some drafts,
some malformed mail) has no key to dedupe on and is re-archived as a fresh
entry on every overlapping run. Narrow in practice, but real: there is no
content-hash fallback.
Extract from Pre-Extracted .eml Directory
If emails were already extracted with readpst elsewhere, point at the directory:
${CLAUDE_SKILL_DIR}/.venv/bin/python ${CLAUDE_SKILL_DIR}/scripts/outlook_to_md.py /path/to/eml-directory/ /path/to/output/Output Structure
output/
├── emails/
│ ├── FolderName/
│ │ ├── 2023-01-15_093042_from-john.smith_to-jane.doe_RE-Subject/
│ │ │ ├── email.md # Formatted markdown with YAML frontmatter
│ │ │ ├── email.eml # Raw original email (RFC 822)
│ │ │ ├── attachment_001_doc.pdf # Extracted attachments
│ │ │ └── checksums.sha256 # Per-email integrity hashes
│ │ └── .../
│ └── .../
├── index.csv # Machine-readable master index
├── index.md # Human-readable index with timeline
├── extraction_log.txt # Processing log with statistics
└── manifest.sha256 # Master integrity manifestEmail Markdown Format
Each email.md contains:
- YAML frontmatter: message_id, date, from, to, cc, subject, attachments with SHA256 hashes
- Formatted body: HTML converted to markdown, or plain text preserved
- Attachment links: Relative links to extracted files with sizes
- Original headers: Full RFC 822 headers in code block
Index Files
- index.csv: All emails with date, sender, recipient, subject, folder, attachment count
- index.md: Timeline view grouped by year/month with links to each email
CLI Reference
outlook_to_md.py [-h] [--include-deleted] [--timezone TZ] [--verbose] [--append | --overwrite] [--owner-email EMAIL] pst_file output_dir| Argument | Description |
pst_file | Path to PST file, or directory of pre-extracted .eml files |
output_dir | Output directory (created if needed) |
--include-deleted | Include deleted items from PST (passes -D to readpst) |
--timezone TZ | Render every date in this IANA zone, e.g. Europe/London. An unknown name is refused. Default: the offset each message was sent with |
--verbose, -v | Verbose output with per-email logging |
--append | Skip emails already in archive (by Message-ID) |
--overwrite | Replace an existing archive: deletes its emails/ folder and index files first |
--owner-email EMAIL | PST owner's email (fixes MAILER-DAEMON in sent items) |
With neither flag, an output directory that already holds an archive is refused. Use
--append to add to it. Use --overwrite only when the user has asked to rebuild it.
Extraction Backends
A directory input (pre-extracted .eml files) is handled directly and needs
nothing else. A PST file is read with readpst (from pst-utils), run as
readpst -j 0 -e -8 -o <tmp> <pst> with -D added by --include-deleted. Its
.eml output then goes through the same path as a directory input. -j 0
turns off readpst's parallel jobs, which can drop messages without an error.
If you run readpst yourself and point this tool at the output, pass -j 0 too.
Integrity Verification
Every extraction produces a verifiable chain of custody:
- Each email folder has
checksums.sha256(SHA256 of all its files) manifest.sha256hashes all checksum files plus the index- Source PST SHA256 is recorded in the manifest
To verify: sha256sum -c manifest.sha256
Workflow: Extract and Search
Extract, then search the markdown with ripgrep. There is no semantic index - the output is plain files on disk, so use whatever search you already trust.
# Step 1: Extract
${CLAUDE_SKILL_DIR}/.venv/bin/python ${CLAUDE_SKILL_DIR}/scripts/outlook_to_md.py archive.pst ./email-output/ --verbose
# Step 2: Search the output
rg -i "settlement agreement" ./email-output/ -lGrep is exact, so search on names, addresses and distinctive phrases rather than concepts.
Error Handling
- "readpst is not installed": Install pst-utils (
sudo apt install pst-utils, orbrew install libpston macOS) - Corrupt emails: Logged to extraction_log.txt, processing continues
- Encoding issues: Decoded with the declared charset, or as UTF-8 if Python cannot use that charset; bytes that will not decode are replaced with U+FFFD
- Duplicate timestamps: Appended with -001, -002 suffixes
- Path too long: Subject truncated, uniqueness preserved
Performance
| Scenario | Approximate Speed |
| Emails without attachments | ~5,000/hour |
| Emails with attachments | ~2,000/hour |
A typical 300MB PST (~1,000-3,000 emails) processes in 5-15 minutes.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

