Run tasks and workflows from Markdown.
upmd turns Markdown code blocks into runnable tasks and dependency-aware workflows. Run them interactively in a real terminal, with instructions, commands, and output kept together.
- Treats fenced code blocks as named tasks with dependencies.
- Runs one task or a workflow from the TUI, lightweight CLI, or CI.
- Gives each task a real pseudo-terminal, including prompts, colors, editors, and pagers.
- Shows output beside the source and can pass shell environment and working-directory changes to later tasks.
Shell installer (macOS and Linux)
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/rezigned/upmd/releases/latest/download/upmd-installer.sh | shHomebrew (macOS)
brew install rezigned/tap/upmdWindows and archives: Download a prebuilt archive and checksum from the latest release. Builds are available for macOS, Linux, and Windows on supported x86_64 and ARM64 platforms.
From source (requires Rust 1.82 or newer):
cargo install --git https://github.com/rezigned/upmd.git# Open up.md or UP.md when present, otherwise browse the current directory
upmd
# Open a document in the TUI
upmd README.md
# Run one named task and its dependencies
upmd DEMO.md --block lint --yes
# Run every task without prompts
upmd --cli --all --yes README.mdTip
With no path, upmd opens up.md or UP.md from the current directory when either is present. Otherwise, it opens the current-directory file picker. Directories are searched recursively, while piped stdin is read as Markdown. A failed task or startup error produces a non-zero exit status.
Run upmd --help for all command-line options.
The workflow in DEMO.md uses name and deps attributes after the language token:
```bash [name:build]
cargo build
```
```bash [name:lint, deps:build]
cargo clippy
```
```bash [name:test, deps:build]
cargo test
```
```bash [name:verify, deps:"lint | test"]
echo "verified ✅"
```name makes a task addressable from --block and the goto menu. Numeric IDs also work:
upmd DEMO.md --block verify· build ──┬─→ · lint ──┬─→ · verify
│ │
└─→ · test ──┘
lint and test each use deps:build, so they start together after build succeeds. verify uses deps:"lint | test" and waits for both. Within deps, | puts tasks in the same stage and , starts the next stage.
If a task fails, later stages are skipped. Under --all, unrelated tasks continue and the command exits non-zero.
Override a task runner with an attribute such as [bin:zsh]. Runner settings resolve in this order: block attribute, user configuration, then built-in defaults.
| Key | Action |
|---|---|
up / k, down / j |
Move between tasks |
Enter |
Run the selected task |
/ |
Search document text |
ctrl-g |
Find and jump to a task |
o |
Open the full terminal output |
i |
Send input to the selected running task |
? |
Search all shortcuts |
q / ctrl-c |
Quit |
Press o for the full terminal screen and scrollback. Running programs receive keyboard, paste, and SGR mouse input directly. Press ctrl-o to return home. Default bindings are listed in src/apps/config.toml.
Successful shell tasks pass exported variables and working-directory changes to later tasks:
```bash [name:prepare]
export API_URL="http://127.0.0.1:8080"
cd /tmp
```
```bash [name:inspect, deps:prepare]
printf 'API_URL=%s\n' "$API_URL"
pwd
```Python, Go, Rust, and TypeScript can opt into experimental state capture with --capture-state. Press e to inspect or edit the environment before running the next task.
Built-in runners cover Bash, POSIX shell, Zsh, Fish, Cmd, PowerShell, Python, JavaScript, TypeScript, Ruby, PHP, C, Go, Rust, and Zig.
TypeScript tries Node.js native strip-types, npx tsx, then ts-node. Rust uses rustc or cargo rustc --. Override a binary with [bin:...] on a task or binaries.<language> in the configuration. Compiled runners use isolated temporary workspaces.
Persistent settings live at ~/.config/upmd/config.toml:
theme = "catppuccin-mocha"
transparent = true
[tui]
inline_max_lines = 20Runner binaries and key bindings can also be overridden. Print every available setting and keymap section with:
upmd --dump-default-configSet RUST_LOG to write tracing output to the platform cache directory:
RUST_LOG=upmd=debug upmd README.mdMIT

