Install and run

Two programs: mdsh-gui, the desktop app, and mdsh, the command line with its terminal UI, mdsh -i. They share one engine, so a file runs the same in both.

Homebrew, on macOS and Linux

brew install --cask gdey/tap/mdsh   # the desktop app, macOS
brew install gdey/tap/mdsh          # mdsh and mdsh -i

The macOS app isn’t signed with an Apple Developer ID yet, so the first time you open it macOS says it can’t check it. Open it once, then choose Open Anyway in System Settings › Privacy & Security. Or add --no-quarantine to the brew install --cask line.

Linux packages

Each release has a .deb and an .rpm for mdsh, and the desktop app as an AppImage that carries its own GTK and WebKit:

sudo apt install ./mdsh_*_linux_amd64.deb     # Debian, Ubuntu
sudo dnf install ./mdsh_*_linux_amd64.rpm     # Fedora, RHEL
chmod +x mdsh-gui_*_linux_x86_64.AppImage
./mdsh-gui_*_linux_x86_64.AppImage

Anything else

Archives for Linux, macOS and Windows on amd64 and arm64 are on the releases page, with a checksums.txt. Or build it with Go 1.27 or newer:

go install github.com/gdey/markdownshell/cmd/mdsh@latest

Blocks run in /bin/sh unless the front matter names another shell. The Windows builds are not well tested yet.

Run something

ToRun
Open a folder in the desktop appmdsh-gui ~/runbooks
Open a file in the desktop appmdsh-gui deploy.md
Open it in the terminal UImdsh -i deploy.md (? lists the keys)
Run it and print the resultmdsh deploy.md
Run it and write the output backmdsh -w deploy.md
Run it unattended, in CImdsh --yes -w deploy.md
Make a reportmdsh --pdf run.pdf deploy.md, or --html run.html
Keep a log of what ranmdsh --audit run.jsonl deploy.md
Let it read $DEBUGmdsh --expose DEBUG deploy.md
Run it like a scriptchmod +x deploy.md && ./deploy.md

Your first document

Save this as hello.md:

#!/usr/bin/env mdsh
# Hello

```sh {#greet}
echo "hello from $(uname -s)"
```

Open it with mdsh-gui hello.md and press play, or run mdsh -w hello.md. Either way the file gets an output fence under the block:

```output {for=greet exit=0}
hello from Darwin
```

Run it again and the output is replaced, not added to. From here, take the examples tour, or read Writing documents.