Markdown is the lingua franca of documentation, but it’s static. You write ./deploy.sh, and six months later someone runs it in a terminal with different state and gets a different result. The code rots. The docs drift.
Three tools try to fix this by making Markdown executable, but they take very different approaches. Let me break them down.
Runme — 10.6K★ (TypeScript, MIT)
“Jupyter notebooks, but for your ops runbooks.”
Runme is a VS Code extension (and CLI) that treats your Markdown file as an interactive notebook. Code fences become runnable cells. Shell state persists between cells — export ENV=staging in cell 1 means $ENV is set in cell 2.
Links & Stats 👉 https://github.com/stateful/runme
![]()
![]()
![]()
This is huge for incident response runbooks. You step through a page-in procedure cell-by-cell, and each command builds on the last. Runme also supports Python, JavaScript/TypeScript, Go — not just shell.
My take: Runme is for the human in the loop — SREs running a playbook during an outage, or onboarding docs where someone needs to follow along interactively. The VS Code UI means you’re tied to an editor, but that’s also its superpower: you see the file, the output, and the next step all at once.
xc — 460★ (Go, MIT)
“A Makefile that reads like documentation.”
xc is a pure CLI task runner. You define named tasks as ## <name> headings in a Markdown file, with Inputs: for parameters and Requires: for dependencies. Run xc deploy ENV=staging and it resolves prerequisites, executes the block, and streams output to terminal.
Links & Stats 👉 https://github.com/joerdav/xc
![]()
![]()
![]()
Unlike Runme, each task executes in a fresh subshell — stateless by design. This is the right choice when you’re codifying CI/CD steps that should be reproducible regardless of what the developer did before.
My take: xc is a Makefile replacement with better DX — discoverable tasks (the Markdown table of contents is your task list), named parameters instead of cryptic $@ and $<, and a clean syntax for dependencies. If your team keeps writing README.md instructions that nobody follows because they just run make deploy from muscle memory, xc bridges the gap: the README is the task definition.
mdsh — 123★ (Go, MIT)
“Live command output baked into your README. On every build.”
mdsh is a Markdown preprocessor. You write code fences with a special marker (>$ ), and running mdsh executes the commands and rewrites the .md file in place with the output appended. Commit the result, and your README always reflects reality.
Links & Stats 👉 https://github.com/zimbatm/mdsh
![]()
![]()
![]()
The --frozen flag is the killer feature: if any command output differs from what’s in the file, mdsh exits non-zero. This means your CI pipeline can fail the build when the docs drift from reality. Your README becomes a tested artifact.
My take: mdsh is the smallest idea here but possibly the most impactful for open-source projects. A --frozen CI check means you can’t merge a PR that updates a CLI flag without also updating the README examples. That’s the kind of hard constraint that keeps docs honest.
How They Stack Up
| Layer | Tool | Best For |
|---|---|---|
| Interactive | Runme | Incident response, playbooks, onboarding |
| CI/CD tasks | xc | Makefile replacement, named pipelines |
| Doc integrity | mdsh | READMEs that always match reality |
They’re not competitors — they operate at different layers. A sensible pipeline:
- Author a procedure in Runme (interactive, stateful)
- Harden into an xc task (stateless, named, parameterized)
- Embed the canonical invocation in your README via mdsh (auto-updating, CI-verified)
The common thread: stop treating Markdown as a write-once-read-never artifact. Make it the live source of truth.