Mirror a folder tree into Claude-optimal Markdown — single-file bundle by default, ghost-tree mode optional.
Project description
mdmirror
Turn any folder into Claude-optimal Markdown context with one command.
mdmirror repo # → ./repo.claude.md
mdmirror repo --stdout | pbcopy # paste-ready clipboard
What it does
- Recursively walks a folder.
- Converts office docs / PDFs / HTML via markitdown.
- Source code (
.py,.ts,.go, …) is fenced with the right language tag — no mangling. - Each file gets a
source:frontmatter so the LLM always knows where a chunk came from. - Output is sorted by path: deterministic, diffable across runs.
- Token count is exact (via
tiktoken), not a heuristic. - Aggressive default ignore list:
.git,node_modules,dist, lockfiles, fonts, images, archives. Honors.gitignore.
Install
pipx install mdmirror # recommended — isolated env
# or
uv tool install mdmirror
# or
pip install mdmirror
Office formats (docx, xlsx, pptx, pdf, outlook) ship in the base install. For audio transcription via whisper:
pipx install '.[audio]' # adds whisper, youtube-transcription
pipx install '.[all]' # everything
Modes
Default — Claude bundle
mdmirror repo # → ./repo.claude.md
mdmirror repo bundle.md # → ./bundle.md
mdmirror repo --stdout # stream to stdout
The bundle is a single .md with a header, a complete file tree, then every file in alphabetical order:
# Context bundle: repo
Source root: `/path/to/repo`
## File tree
```
README.md
src/lib/foo.ts
src/main.py
```
---
source: README.md
bytes: 312
---
# Hello
…
<!-- ────────────────────────────── -->
---
source: src/lib/foo.ts
bytes: 412
---
```ts
// file contents
```
Tree mirror
For when you want one .md per file on disk:
mdmirror repo --tree # → ./repo_md/
mdmirror repo out --tree # → ./out/
mdmirror repo out --tree --bundle # tree AND ./repo.claude.md
Flags
| Flag | Purpose |
|---|---|
--tree |
Mirror as a folder of .md files instead of one bundle |
--bundle |
With --tree, also emit a bundle at ./<input>.claude.md |
--stdout |
Stream the bundle to stdout (great for | pbcopy) |
--overwrite |
Replace existing output files |
--dry-run |
Report planned actions, write nothing |
--workers N |
Parallel worker processes |
--skip-pattern PAT |
Extra gitignore-style pattern (repeatable) |
--include-hidden |
Don't skip dotfiles / dotdirs |
--follow-symlinks |
Follow symlinks (cycle-safe) |
--no-default-ignore |
Disable the built-in skip list |
--no-gitignore |
Don't honor a root .gitignore |
--no-frontmatter |
Strip the source: YAML header |
--no-fence |
Disable code fencing (everything goes through markitdown) |
-q, -v |
Quiet / verbose logging |
Behaviors worth knowing
- Naming (tree mode):
report.docxbecomesreport.docx.mdso a siblingreport.pdfdoesn't collide.*.mdfiles pass through unchanged. - Output safety: refuses to run if the output dir is inside the input dir.
- Failures don't crash the run. Each failed file is logged and counted; everything else still gets converted.
- Logs go to stderr. Stdout stays clean for
--stdoutpiping.
Develop
uv pip install -e '.[dev]'
.venv/bin/pytest -q -m "not integration" # fast (~0.5s)
.venv/bin/pytest -q # incl. real markitdown roundtrip
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mdmirror-0.2.0.tar.gz.
File metadata
- Download URL: mdmirror-0.2.0.tar.gz
- Upload date:
- Size: 52.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b888ab7b5e6c40f62d25be4ee482c6476ee1f0e62b78b39e97a0013991ab8efc
|
|
| MD5 |
a54c7eca095a976f98b8cf2e06b12b62
|
|
| BLAKE2b-256 |
34ed40289b1fcd5d1f8465da948fe2f56fd3d7155bfe642d580ce06273c7fcbf
|
Provenance
The following attestation bundles were made for mdmirror-0.2.0.tar.gz:
Publisher:
release.yml on f3r21/mdmirror
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mdmirror-0.2.0.tar.gz -
Subject digest:
b888ab7b5e6c40f62d25be4ee482c6476ee1f0e62b78b39e97a0013991ab8efc - Sigstore transparency entry: 1390667134
- Sigstore integration time:
-
Permalink:
f3r21/mdmirror@39dff7c47333d2acf263375f04aebdfd38ff27e7 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/f3r21
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@39dff7c47333d2acf263375f04aebdfd38ff27e7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mdmirror-0.2.0-py3-none-any.whl.
File metadata
- Download URL: mdmirror-0.2.0-py3-none-any.whl
- Upload date:
- Size: 16.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba32e1fe03932bdf9bd79de14f2f52bfab6fce505a96996df6ddc02bb269c5a0
|
|
| MD5 |
f5f0447f968e0aed43ba22ff05980dd8
|
|
| BLAKE2b-256 |
821c2a36adf93330f3fa8e6e717ee32b5e74c646df564cc19dfb6c8d63ff3b6a
|
Provenance
The following attestation bundles were made for mdmirror-0.2.0-py3-none-any.whl:
Publisher:
release.yml on f3r21/mdmirror
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mdmirror-0.2.0-py3-none-any.whl -
Subject digest:
ba32e1fe03932bdf9bd79de14f2f52bfab6fce505a96996df6ddc02bb269c5a0 - Sigstore transparency entry: 1390667164
- Sigstore integration time:
-
Permalink:
f3r21/mdmirror@39dff7c47333d2acf263375f04aebdfd38ff27e7 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/f3r21
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@39dff7c47333d2acf263375f04aebdfd38ff27e7 -
Trigger Event:
push
-
Statement type: