Skip to main content

Convert almost anything to Obsidian-flavored Markdown for a knowledge graph.

Project description

Any2MD

CI License: MIT Python 3.11+

Free, open-source CLI that converts almost anything — local files (PDF, DOCX, XLSX, images…) and online links (YouTube, Reddit, GitHub, arXiv, Wikipedia, Hacker News, Stack Overflow, Twitter/X, web articles) — into Obsidian-flavored Markdown for a knowledge graph. Every input is summarized. No external APIs, no API keys, ever.

Install

One command, anywhere (recommended — isolated, no venv to manage):

pipx install any2md-cli

Then just run it:

any2md

The first run asks one thing — where to save your .md files — and then gets out of the way. Summaries run locally: if Ollama is running it's used automatically, otherwise a built-in zero-setup extractive summarizer is used. Nothing else to configure.

From source (dev)
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

Usage

One-shot

any2md convert https://github.com/karpathy/nanoGPT
any2md convert ~/notes/paper.pdf -o ~/ObsidianVault/inbox
any2md convert https://arxiv.org/abs/1706.03762 --depth high --provider extractive
any2md convert --batch links.txt          # one target per line

Re-converting the same link refreshes the existing note instead of making a duplicate (tracking params like utm_* are stripped, so the same article always maps to one note). Pages that extract to nothing — paywalled or JavaScript-only — are skipped with a warning rather than written as empty notes.

Interactive REPL

any2md            # opens the REPL

Inside the REPL, paste a URL or file path to convert it. Commands:

Command Effect
/output <dir> set output folder
/provider <name> set summarizer: extractive (default) · ollama · none
/depth how much to keep: low · medium · high · raw (◀ ▶ live picker)
/batch <file> submit every line in a file
/jobs list jobs + status
/last path of the last written .md
/open [last] open the output folder (or the last note) in your file viewer
/rename <name> rename the file you just made (slug auto-cleaned)
/help · /quit help / exit

While a conversion runs you get a live spinner with an estimated time (it learns your real timings per source) and a rotating tip. Drag a file straight into the terminal to convert it.

Config

any2md config set output ~/ObsidianVault/inbox
any2md config set provider extractive
any2md config show

Precedence: CLI flag > env var (ANY2MD_OUTPUT_DIR, ANY2MD_PROVIDER, …) > ~/.any2md/config.toml > default.

Summarizers (all free, offline)

  • extractive (default): pure-Python TextRank-style. Zero setup, no network.
  • ollama: local model via OLLAMA_URL (default http://localhost:11434), OLLAMA_MODEL (default llama3.2). Unreachable → falls back to extraction-only.
  • none: extraction only, no summary.

Serve mode (HTTP)

any2md serve --port 8000

Routes:

# submit a conversion → returns {"id": "..."}
curl -X POST localhost:8000/convert -H 'Content-Type: application/json' \
     -d '{"target":"https://github.com/karpathy/nanoGPT"}'

curl localhost:8000/jobs/<id>            # status + progress
curl localhost:8000/jobs/<id>/download   # the rendered .md

Set ANY2MD_TOKEN to gate access — clients then send Authorization: Bearer <token>.

Deploy

Docker

docker build -t any2md .
docker run -p 8000:8000 -e ANY2MD_TOKEN=secret -v "$PWD/data:/data" any2md

Railway

Push the repo; Railway builds the Dockerfile and runs any2md serve on $PORT (see railway.toml). Set ANY2MD_TOKEN and ANY2MD_PROVIDER=extractive in the dashboard. No API keys required — the stack is fully free/offline.

Develop

pytest -q          # tests (no live network)
ruff check .       # lint

See CONTRIBUTING.md for the full workflow (TDD, fixtures, adding a source). CI runs the suite + lint on every push and PR.

Publish to PyPI (maintainer)

pipx install any2md-cli works once the package is on PyPI. To cut a release:

python -m build                 # builds dist/*.whl and dist/*.tar.gz
twine upload dist/*             # needs your PyPI account / API token

Bump __version__ in any2md/__init__.py first (pyproject.toml reads it dynamically).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

any2md_cli-0.1.2.tar.gz (72.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

any2md_cli-0.1.2-py3-none-any.whl (56.0 kB view details)

Uploaded Python 3

File details

Details for the file any2md_cli-0.1.2.tar.gz.

File metadata

  • Download URL: any2md_cli-0.1.2.tar.gz
  • Upload date:
  • Size: 72.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for any2md_cli-0.1.2.tar.gz
Algorithm Hash digest
SHA256 e74b7cab3fe2ff366cc3903bcf878e9e8666e21d2c3b50302d9d1236587074ca
MD5 8726231253bf6d407d211080e8dd6aae
BLAKE2b-256 7845a0e7f0cf1a359ef9ea665f6236f3824ed4a60c6457e6b5a2dd3c813f9e2d

See more details on using hashes here.

File details

Details for the file any2md_cli-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: any2md_cli-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 56.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for any2md_cli-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 fadb0ed10b1bab820dfe4309d1510e4ca914b929114f748e1618204791fe6141
MD5 a2adb47154cc14ca390d05fb0122212c
BLAKE2b-256 1241b311fb818f7eb90b72cecac31fc3c749a69185150cf0d4b97c2a37bbce20

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page