Skip to main content

mkd

Contents:

What is mkd?

mkd is a local-first CLI oriented around Markdown documents that:

  • gathers an essential family of Markdown chores under a single executable, run with a path-explicit command
  • holds no opinion about file layout in your project
  • keeps heavyweight tools (Pandoc, Poppler, and RapidOCR) outside the Python package

What can mkd do?

  • Transcribe images and PDFs into page-sectioned Markdown, with a content-addressed local cache for large jobs

  • Convert between DOCX/HTML/Markdown via Pandoc, with a tweakable reference DOCX letting you easily specify your desired house style

  • Split/join Markdown files, backed by manifests to survive edit cycles without losing order

  • Reflow (e.g. to one sentence per line) for diff-clean prose review

  • Word counts (including tags allowing multiple custom section-based counts at once)

  • Proofing via deterministic OCR cleanup & zero external LLM calls

    Only safe fixes auto-apply; everything uncertain is surfaced for review.

Why can I trust mkd?

[!TIP] mkd's local-first trust model is intentionally boring about trust boundaries.

In particular, mkd will never:

  • fetch remote URLs
  • check remote ilnks
  • log into services
  • talk to a server
  • crawl repos
  • watch directories
  • The only automatic side effect is the transcription cache, which is placed at ${XDG_CACHE_HOME:-~/.cache}/mkd by default.

  • Commands read the input paths you pass and write to either:

    • stdout
    • explicit output paths
    • documented paths derived from those inputs
  • Conversion and OCR commands may create and use temporary files under the system temp directory (i.e. /tmp), then clean them up after processing completes.

Who is mkd for?

  • Who it is for: an engineer or writer who wants these operations boring, scriptable, and reproducible.
  • What it is not: a document server, a network client, or a policy engine.

Getting started

Prerequisites

Global/mandatory

Ensure the following are available (including in the $PATH, if applicable) before proceeding:

  • Python >= 3.11
  • uv

Subcommand-specific

The following dependencies are only required by certain mkd subcommands:

Dependency Subcommands requiring it
Pandoc count, from-docx, from-html, to-docx, reference
Poppler transcribe pdf

mkd doctor checks these external tools. A required minimum is a compatibility floor; a recommended minimum is the maintained support baseline and does not mean older versions cannot work.

[!TIP] On macOS, both can be installed easily via Homebrew:

brew install pandoc poppler

Installation

Use the checkout directly while developing:

uv sync
uv run mkd --help

To install mkd machine-wide (based on a local checkout of the repo), run this in your checkout's root dir:

uv tool install -e . --force
mkd --help

[!TIP]

  • Examples below use the bare mkd.
  • From an unmanaged checkout, prefix them with uv run.
  • For project-pinned invocation (via wrapper scripts), see docs/operations.md.

Core commands

[!IMPORTANT] Run mkd doctor first to confirm the environment before working on live documents (especially after installing or upgrading mkd or an external tool).

  • Check the environment, then preflight and count a draft:

    mkd doctor --smoke                      # probe pandoc, Poppler, and RapidOCR
    mkd check paper.md --fail-on warning    # fail the command on warnings, not just errors
    mkd count paper.md
    
  • Format prose for line-oriented review:

    mkd sentences draft.md formatted.md     # write to a new file
    mkd sentences draft.md --in-place       # rewrite the input (explicit opt-in)
    
  • Transcribe source material into page-sectioned Markdown:

    mkd transcribe pdf reading.pdf --output reading.md
    mkd transcribe pdf reading.pdf --output reading.md --confidence-report reading-confidence.json
    mkd transcribe pdf reading.pdf --page-range 12 18 --mode auto --output reading-p12-p18.md
    mkd transcribe images pages/ --output pages.md
    
  • Proof OCR output, then split it by page and join it back after editing:

    mkd proof transcript.md proofed.md --report proof-report.json
    mkd split proofed.md parts --by page --manifest
    mkd join --manifest parts/_mkd-split.json --output edited.md
    
  • Convert across document formats:

    mkd from-docx authoritative.docx draft.md --keep-comments
    mkd from-html export.html export.md --selector '.WordSection1'
    mkd to-docx draft.md final.docx --reference-doc reference.docx
    

Testing

  • Run the full test suite:

    uv run pytest
    
    • Tests target module boundaries first, then CLI wiring.
    • tests/test_cli.py drives the app through Typer's CliRunner.
    • The test suite runs fully offline, matching the runtime contract.
    • Pull requests run the locked suite across every supported Python minor.

    See docs/testing.md for suite structure, CI design rationale, mocks, and smoke checks.

  • Lint:

    # checks:
    # - line length 100
    # - rule sets E, F, I, N, W
    uv run ruff check .
    

Documentation map

Document Contains
docs/commands.md Full CLI reference covering options, outputs, & failure semantics
docs/workflows.md End-to-end recipes for transcription, proofing, checks, conversion, & split/join
docs/operations.md Install modes, environment checks, & stdout/file behavior
docs/architecture.md Module boundaries, external-tool edges, cache model, & invariants
docs/cache.md Cache location, invalidation, refresh, & cleanup
docs/troubleshooting.md Common failures & how to narrow them
docs/extension-guide.md & docs/development.md Adding commands & following repo conventions

Other features

Transcription cache

To make large jobs cheap to rerun, transcribe pdf and transcribe images cache successful pages by default.

CLI flags

  • Default runs reuse valid successful entries and retry misses or prior failures.
  • --refresh ignores cached successes and rewrites them.
  • --no-cache disables reads and writes for a single run.
  • --cache-dir PATH uses PATH as the app cache root for a single run.
  • --confidence-report PATH writes OCR confidence as separate JSON without changing transcript Markdown.

Confidence reports preserve ordered target, line, and fragment evidence for review; they do not annotate or otherwise alter transcript Markdown.

Cache location

By default, the cache lives at ${XDG_CACHE_HOME:-~/.cache}/mkd.

mkd cache path
mkd cache prune --older-than 30d --dry-run
mkd cache clean --force

Lifecycle

  • Cache entries key on:

    • the source bytes
    • the mkd version
    • the processing options
    • the signatures of the OCR and Poppler tools involved
  • This is so that changes in the user's choice of tools or options invalidate stale entries automatically.

[!TIP]

  • Cache data can contain extracted document text and diagnostic source paths.
  • Use --no-cache for a sensitive one-off run.
  • Full layout and invalidation rules can be found at docs/cache.md.

Word-count markers

  • By default, mkd count counts the whole rendered document.

  • To count only selected regions, wrap them:

    <!-- word-count-begin -->
    This counts.
    <!-- word-count-end -->
    
    • Short forms (<!-- wcb --> and <!-- wce -->) are also accepted.
    • Active markers are recognized only outside fenced code blocks.
    • Marker-looking comments inside fenced code blocks stay inert examples and mkd check reports them as visible informational findings that do not fail --fail-on warning.

[!TIP] Run mkd check before trusting marked counts in automation.

Design and safety

The core is deliberately small and boring: every capability is a thin CLI shell over a Typer-free module that can be tested in isolation.

  • Local-first and path-based: commands take explicit input/output paths, write local files or stdout, and perform no network I/O.
  • Thin CLI over tested modules: src/mkd/cli.py owns only Typer wiring, option validation, and exit codes; domain logic lives in modules that return plain values, dataclasses, or typed exceptions.
  • Heavyweight tools at narrow edges: Pandoc, Poppler, and RapidOCR are resolved with shutil.which, invoked through a bounded shared subprocess boundary, and translated into mkd objects or typed errors immediately, so foreign formats never leak inward; operations.md documents the supported timeout tuning contract.
  • Failures are contract, not surprise: expected problems raise typed MkdError subclasses, which the CLI renders as mkd <command>: <message> rather than as a stack trace.

Guaranteed invariants

mkd's only persistent side effects are documented and local: explicit output files, explicit in-place rewrites, the app cache at ${XDG_CACHE_HOME:-~/.cache}/mkd unless disabled or overridden, and temporary files under the system temp directory during conversion or OCR.

  • No network, ever: no command fetches a remote URL, and check validates structure and DOCX hazards statically, without invoking Pandoc.
  • No silent overwrites: sentences and proof require --in-place to touch the input; split, join, and reference refuse risky overwrites without --force; from-html refuses an identical input/output path.
  • Atomic user outputs: file outputs are completed in a private sibling temporary path, given their final permission bits, and file-fsynced before replacement; existing output symlinks update their regular-file targets without replacing the links.
  • Explicit durability limits: split output is atomic per file, not transactional across its directory; newly created ancestor directories are not fsynced; and a rejected best-effort parent-directory fsync can leave a successfully visible file with weaker crash durability.
  • Clean stdout: content-producing commands keep stdout to content and send diagnostics to stderr or explicit report files, so mkd composes in pipelines.
  • Stable transcript headings: every transcript section is headed ### Page <label>; this is a cross-module contract, so proof preserves it, page-based split recognizes it, and the cache keys on the label that produced it.

mkd doctor returns a stable scheme: 0 (healthy), 1 (a failed check, or a warning under --strict), and 2 (a usage or orchestration error). Other commands exit non-zero on a handled failure.

For the full module map, external-tool contracts, and the complete invariant list, see docs/architecture.md.

External dependencies

  • mkd ships thin and defers document-format interpretation to established tools.

  • mkd doctor reports which are present, their versions, and whether they satisfy any required compatibility floor or recommended support baseline (and, with --smoke, whether they actually run);

    • Missing tools surface as concise, actionable CLI errors.
    • Required minimums reject incompatible versions; recommended baselines warn without claiming older versions cannot work.
Tool Powers Notes
Pandoc word counts, DOCX and HTML conversion, reference DOCX parses Markdown to JSON; owns format interpretation
Poppler (pdfinfo, pdftotext, pdftoppm) PDF page counts, text extraction, rasterization text-first, with raster fallback for OCR
RapidOCR OCR of images and PDF fallback pages installed into the environment automatically by uv

Download files

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

Source Distribution

mkd-0.4.0.tar.gz (135.0 kB view details)

Uploaded Source

Built Distribution

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

mkd-0.4.0-py3-none-any.whl (93.0 kB view details)

Uploaded Python 3

File details

Details for the file mkd-0.4.0.tar.gz.

File metadata

  • Download URL: mkd-0.4.0.tar.gz
  • Upload date:
  • Size: 135.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for mkd-0.4.0.tar.gz
Algorithm Hash digest
SHA256 5418ac42830e57c57c28a2014dc3d6eb00f30c17321e7d5ee7d25785ae54cabb
MD5 123e6c8c86310752ee8ab9ffca237d14
BLAKE2b-256 ff97cef081f960a2d3db89d7b9175f4cc6c5f55b3d0161de66f52ba3c65ee116

See more details on using hashes here.

File details

Details for the file mkd-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: mkd-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 93.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for mkd-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 96735f754a4c8acc25072c2a93039ce01e887c7752e808be35a41a38e006b694
MD5 ec04ed1dbad07e2090c7acbeb5a26b12
BLAKE2b-256 b372a496eb6c53d95d0910799305be19b225c078dcd0194fd834c00a7328034c

See more details on using hashes here.

Release history Release notifications | RSS feed

0.5.0

2 files

0.4.1

2 files

This release

0.4.0 This release

2 files

0.3.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page