Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

mq-task

mq-task is a task runner that executes code blocks in Markdown files, selected by section title. Your README.md (or any Markdown file) is the task file: the documentation and the automation are the same text.

It is built on mq, a jq-like tool for Markdown, which parses the document and extracts sections.

## build

```bash
cargo build --release
```

## ci

```meta
depends = ["lint", "test", "build"]
parallel = true
```

```bash
echo "all checks passed"
```
mq-task ci

Features

  • Run code blocks from any section, in any language with a configured runtime
  • Task dependencies, with parallel fan-out / fan-in
  • Conditions (if), retries, timeouts, and output passing between tasks
  • Import tasks from other Markdown files
  • Skip up-to-date tasks (sources / outputs) and --watch mode
  • Dependency graph (text and Mermaid) and shell completions
  • Interactive TUI with search, argument prompts and live progress

Warning

mq-task is under active development.

Head to Getting Started to install it and run your first task.

Getting Started

  1. Install mq-task
  2. Run your first task with the Quick Start
  3. Tune runtimes in Configuration

Install

Cargo

cargo install --git https://github.com/harehare/mq-task.git

Verify

mq-task --version

Quick Start

A task is a heading followed by one or more code blocks. Create README.md:

## hello

```bash
echo "Hello from mq-task"
```

List the tasks and run one:

mq-task            # list tasks in README.md
mq-task hello      # run the "hello" task
mq-task -f tasks.md hello   # use another file

mq-task hello is shorthand for mq-task run hello.

Passing arguments

Arguments after -- are exposed as MX_ARGS (space-joined) and MX_ARG_0, and so on

## greet

```bash
echo "Hello, $MX_ARG_0"
```
mq-task greet -- world

Dry run

--dry-run prints what would be executed without running anything.

Next

Configuration

mq-task init creates an mq-task.toml with default runtime settings. Use -c/--config to point at another file.

default_task = "build"

[runtimes]
bash = "bash"
sh = "sh"
python = "python3"
ruby = "ruby"
node = "node"
javascript = "node"
js = "node"
php = "php"
perl = "perl"

[runtimes.go]
command = "go run"
execution_mode = "file"

[runtimes.jq]
command = "jq"
execution_mode = "arg"

Execution modes

ModeBehaviour
file (default)Write the code to a temp file and pass it as an argument. stdin stays interactive.
stdinPipe the code into the command's stdin. The script itself cannot read the terminal. Use for psql, redis-cli and similar clients
argPass the code as a command-line argument (e.g. jq, mq).

default_task

Top-level key (outside [runtimes]). The task run when mq-task is invoked without a task name.

Overriding from the CLI

mq-task build -r python:python3.11 -e file
mq-task build --lang bash      # only run bash blocks

Guide

Each page covers one feature, from the basics to scheduling, caching and tooling. Task behaviour is configured with a meta code block (TOML) inside the task's section. See the Meta keys reference for the full list.

Writing Tasks

Every heading is a task. The task name is the heading text; the commands are the code blocks beneath it. By default ## headings are tasks.

## build

Compile the project.

```bash
cargo build
```
  • Several code blocks in one section run in order.
  • The prose under the heading is the task description (shown in list and the TUI).
  • The language of the fence selects the runtime from configuration.
  • A meta block configures the task; it is not executed.

Document-wide meta

A meta block before the first heading applies to every task:

```meta
env = ["REGION=staging"]
dir = "services/api"
```

Precedence, lowest to highest: document-wide meta → the task's own meta → CLI flags.

Dependencies

## format

```bash
cargo fmt
```

## lint

```meta
depends = ["format"]
```

```bash
cargo clippy
```

## test

```meta
depends = ["lint"]
```

```bash
cargo test
```

mq-task test runs format → lint → test.

  • Multiple dependencies: depends = ["format", "lint"]
  • A shared dependency runs only once
  • Circular dependencies are reported as an error

Parallel Execution

By default dependencies run one after another in declaration order. Set parallel = true to run them concurrently; the task starts after all of them finish (fan-out / fan-in).

## ci

```meta
depends = ["lint", "test", "build"]
parallel = true
```

```bash
echo "all checks passed"
```
  • Only real dependency edges constrain ordering: a task starts as soon as its own dependencies are done.
  • Output is prefixed with the task name (for example lint │ output); each task reports ✔/✘ with its duration.
  • The first failure aborts the run: running tasks (and the processes they started) are stopped. -k/--keep-going lets independent tasks finish; dependents of a failed task are not run.
  • --parallel forces concurrency regardless of the file; -j N/--jobs N caps concurrent tasks (-j 1 is sequential). Default: number of CPUs.
  • stdin is closed for tasks in parallel runs; keep interactive tasks sequential. Dry-run is always sequential.

Conditions

if skips a task when its condition is false. A skipped task does not block its dependents.

## deploy

```meta
if = 'flag("CI") && os() == "linux"'
```

```bash
./deploy.sh
```
ExpressionTrue when
flag("NAME")the variable is set and not empty, 0 or false
getenv("NAME") == "x", getenv("NAME") != "x"the variable equals / differs from x
os() == "linux"the OS is linux, macos or windows
exists("path")the path exists (relative to the task's working directory)
which("cmd")cmd is on PATH
!expr, a && b, a || bboolean combinations; parentheses group

Variables are looked up in --env, the task's meta/params, then the process environment.

A condition is an mq expression that must evaluate to true or false, so anything else mq offers (string functions, contains and so on) works too. Use getenv/flag rather than mq's built-in env(), which only sees the process environment.

Retries and Timeouts

## flaky-integration

```meta
retries = 3          # up to 4 attempts in total
retry_delay = "2s"   # default: 1s
timeout = "5m"       # per attempt; exit code 124 when exceeded
```

```bash
./run-integration-tests.sh
```
  • Durations accept ms, s, m, h; a bare number is seconds.
  • Only runtime failures are retried. Missing runtimes or parameters fail immediately.
  • A task with timeout runs in its own process group so the whole tree is stopped. It therefore cannot read from the terminal.

Passing Output Between Tasks

capture = true exposes a task's stdout (trailing newline removed) to dependents as MX_OUTPUT_<TASK> (the title upper-cased with other characters replaced by _). The output is still printed.

## version

```meta
capture = true
```

```bash
git describe --tags
```

## release

```meta
depends = ["version"]
```

```bash
echo "releasing $MX_OUTPUT_VERSION"
```

Captured output reaches direct and transitive dependents. Because stdout is piped, programs may stop colouring it.

Importing Tasks

List files in the document-wide meta block. Their tasks become available as <file name>:<task>.

```meta
imports = ["ci/checks.md", "deploy.md"]
```

## release

```meta
depends = ["checks:lint", "deploy:staging"]
```
  • Paths are relative to the importing file; imports nest (a:b:task); cycles are rejected.
  • Inside an imported file, depends use that file's own short names.
  • The imported file's document-wide env/dir apply to its tasks. Relative dir resolves against where mq-task was invoked.

Caching and Watch Mode

Declare the files a task reads with sources (globs). The task is skipped when sources, code, arguments and environment are unchanged since its last successful run. With outputs, it also runs if an output is missing.

## build

```meta
sources = ["src/**/*.rs", "Cargo.toml"]
outputs = ["target/release/app"]
```

```bash
cargo build --release
```

Fingerprints live in .mq-task/cache.json in the invocation directory. Consider git-ignoring .mq-task/. --no-cache ignores them.

Watch

mq-task test --watch

Re-runs whenever any sources of the task or its dependencies, or the task file, change.

Parameters, Env and Working Directory

Named parameters

A bare name is required; name=value sets a default.

## deploy

```meta
params = ["env=staging", "region"]
```

```bash
echo "deploying to $MX_PARAM_ENV / $MX_PARAM_REGION"
```
mq-task deploy -- region=eu           # env falls back to "staging"
mq-task deploy -- prod eu             # positional, in declaration order
mq-task deploy -- env=prod region=eu  # named

Parameters are exposed as MX_PARAM_<NAME>. A required parameter without a value is an error.

Environment and directory

mq-task deploy --env REGION=eu --env DEBUG=1
mq-task deploy --dir ../other-project

Defaults can be declared in meta:

## deploy

```meta
env = ["REGION=staging", "LOG_LEVEL=info"]
dir = "services/api"
```

CLI --env / --dir override the meta defaults for that run.

Aliases, Default and Private Tasks

Aliases

## build

```meta
alias = ["b"]
```

mq-task b runs build.

Default task

# mq-task.toml
default_task = "build"

mq-task with no task name runs build.

Private tasks

A title starting with _, or private = true in meta, hides the task from list/tui (list --all shows it). It can still be run and used as a dependency.

## _cleanup

```bash
rm -rf tmp/
```

Interactive TUI

mq-task tui

A searchable task list with a details pane (description, dependencies, params, options, code).

KeyAction
↑/↓, j/kmove
/search titles, aliases, descriptions
Enterrun inside the TUI (prompts when a param is required)
arun with arguments (name=value, positional; quote values containing spaces)
xleave the TUI and run in the terminal (interactive stdin works)
q/Escquit

While running, the left pane shows per-task progress (○ pending, spinner running, ✔, ✘, ⊘ skipped, ↻ retry) and the right pane the log. Tab toggles combined / selected-task log, PgUp/PgDn scroll, q/Ctrl+C aborts, r re-runs. --parallel, -j, -k and --no-cache apply too.

Dependency Graph

mq-task graph ci                  # tree for one task
mq-task graph --format mermaid    # Mermaid diagram of all public tasks
ci [parallel]
├── lint
└── test
    └── build

The Mermaid output can be pasted into GitHub Markdown or any Mermaid renderer.

Shell Completions

mq-task completions zsh  > ~/.zfunc/_mq-task
mq-task completions bash > /etc/bash_completion.d/mq-task

Supported shells are those of clap_complete: bash, zsh, fish, elvish, powershell. Task names are not completed dynamically yet.

Cookbook

Complete, copy-pasteable task files.

A CI pipeline in one file

## format

```bash
cargo fmt --check
```

## lint

```meta
sources = ["src/**/*.rs"]
```

```bash
cargo clippy -- -D warnings
```

## test

```meta
sources = ["src/**/*.rs", "tests/**/*.rs"]
retries = 1
```

```bash
cargo test
```

## ci

```meta
depends = ["format", "lint", "test"]
parallel = true
```

```bash
echo ok
```

Run it with mq-task ci -k to see every failure at once, or mq-task ci -j 2 to limit concurrency.

Release with captured version

## version

```meta
capture = true
```

```bash
git describe --tags --abbrev=0
```

## build

```meta
sources = ["src/**/*.rs"]
outputs = ["target/release/app"]
```

```bash
cargo build --release
```

## release

```meta
depends = ["version", "build"]
if = 'flag("GITHUB_ACTIONS")'
```

```bash
gh release upload "$MX_OUTPUT_VERSION" target/release/app
```

Multi-language tasks

Each fenced block uses the runtime for its language.

## report

```python
import json, sys
print(json.dumps({"ok": True}))
```

```bash
echo "done"
```

Add or change runtimes in mq-task.toml, or override one per run with -r python:python3.12.

Reference

Meta keys

A fenced block with the language meta (TOML) in a task section. Unknown or invalid values are an error.

KeyTypeDescription
dependsarray of stringsTasks to run first
parallelboolRun depends concurrently
ifstringCondition; task skipped when false
retriesintegerExtra attempts after a failure
retry_delaydurationWait between attempts (default 1s)
timeoutdurationPer-attempt limit; exit 124
captureboolExpose stdout as MX_OUTPUT_<TASK> to dependents
sourcesarray of globsInput files for up-to-date checks and --watch
outputsarray of pathsFiles that must exist for the task to be considered up to date
paramsarray of stringsNamed parameters (name required, name=value default)
envarray of KEY=VALUEDefault environment variables
dirstringWorking directory
aliasarray of stringsAlternate names
privateboolHide from listings
importsarray of pathsDocument-wide only: files to import

Environment variables set for tasks: MX_ARGS, MX_ARG_<N>, MX_PARAM_<NAME>, MX_OUTPUT_<TASK>.

CLI

mq-task [OPTIONS] [TASK] [-- <ARGS>...] [COMMAND]

Commands

CommandDescription
runRun a task
listList tasks (-a/--all includes private)
tuiSelect and run a task interactively
graph [TASK]Print the dependency graph (--format text|mermaid)
completions <SHELL>Generate shell completions
initWrite a sample mq-task.toml

Options

OptionDescription
-f, --file <FILE>Markdown file (default README.md)
-c, --config <CONFIG>Configuration file
-r, --runtime <LANG:COMMAND>Override a runtime
-e, --execution-mode <MODE>stdin, file or arg for overrides
--lang <LANG>Only run code blocks of this language
--dry-runShow what would run
--env <KEY=VALUE>Set an environment variable (repeatable)
-d, --dir <PATH>Working directory
-w, --watchRe-run when sources or the task file change
--parallelRun dependencies concurrently
-j, --jobs <N>Max concurrent tasks
-k, --keep-goingContinue independent tasks after a failure
--no-cacheIgnore up-to-date checks

Exit codes

mq-task exits with the code of the task's process, so it composes with &&, CI and git hooks.

  • A task killed by timeout exits with 124.
  • Under --keep-going, the first failure's code is used.