mx is a task runner that executes code blocks directly from Markdown files based on section titles.
What is mx?
mx
is a command-line task runner that treats your Markdown files as executable documentation. Instead of maintaining separate task scripts and documentation, you can keep everything in one place—typically your README.md
—and execute tasks directly from there.
Built on top of mq, a jq-like command-line tool for Markdown processing, mx parses your Markdown documents and executes the code blocks within specific sections.
Why mx?
Traditional task runners require you to maintain separate files from your documentation. With mx, your documentation is your task runner. This approach offers several benefits:
- Single source of truth: No synchronization issues between docs and scripts
- Self-documenting: Your tasks include their own documentation naturally
- Discoverable: New team members can find and understand tasks easily
- Multi-language support: Automatically detects and executes code in different languages
- Zero configuration: Works out of the box, with optional customization
Quick Start
Installation
The fastest way to install mx is using the installation script:
curl -sSL https://raw.githubusercontent.com/harehare/mx/refs/heads/main/bin/install.sh | bash
Alternatively, if you have Rust installed:
cargo install --git https://github.com/harehare/mx.git
Basic Usage
Create a README.md
with some tasks:
## Build
\`\`\`bash
cargo build --release
\`\`\`
## Test
\`\`\`bash
cargo test
\`\`\`
## Deploy
\`\`\`bash
./scripts/deploy.sh production
\`\`\`
List available tasks:
mx
Run a specific task:
mx Build
That's it! mx will execute the bash code block under the "Build" section.
Key Features
1. Multi-Language Runtime Support
One of mx's most powerful features is its ability to automatically detect and execute code blocks in different programming languages. Simply specify the language in your code block, and mx will use the appropriate runtime:
## Python Analysis
\`\`\`python
import pandas as pd
df = pd.read_csv('data.csv')
print(df.describe())
\`\`\`
## Node.js Build
\`\`\`javascript
const esbuild = require('esbuild');
esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/bundle.js',
});
\`\`\`
## Go Benchmark
\`\`\`go
package main
import (
"fmt"
"time"
)
func main() {
start := time.Now()
// Your code here
fmt.Printf("Execution time: %v\n", time.Since(start))
}
\`\`\`
## Ruby Script
\`\`\`ruby
require 'json'
data = JSON.parse(File.read('config.json'))
puts "Config loaded: #{data.keys.join(', ')}"
\`\`\`
Run any of these with:
mx "Python Analysis"
mx "Node.js Build"
mx "Go Benchmark"
mx "Ruby Script"
mx automatically uses the correct runtime for each language—no need to specify or configure anything!
2. Configurable Runtimes
While mx works out of the box with sensible defaults, you can customize runtime environments to match your needs:
mx init
This creates an mx.toml
configuration file where you can specify custom runtimes:
heading_level = 2
[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.mq]
command = "mq"
execution_mode = "arg"
Simple Runtime Configuration
For most languages, you can use a simple string format:
[runtimes]
python = "pypy3"
This uses the default execution mode (stdin
), where code is passed via standard input.
Advanced Runtime Configuration
Some languages require different execution modes. You can specify these using the detailed configuration format:
[runtimes.go]
command = "go run"
execution_mode = "file"
Available Execution Modes:
-
stdin
(default): Code is passed via standard input (works for Python, Ruby, Node.js, etc.) -
file
: Code is written to a temporary file and the file path is passed as an argument (required for Go) -
arg
: Code is passed directly as a command-line argument (used for specialized tools like mq)
This flexibility means you can:
- Use different Python versions (python3.11, pypy, etc.)
- Switch between Node.js and Deno
- Use custom interpreters or compilers
- Configure the appropriate execution mode for each language
- Add support for any language with a CLI
3. Executable Documentation
mx turns your documentation into a living, executable artifact. Instead of documenting commands that might become outdated, you document runnable tasks:
## Setup Development Environment
Install all dependencies and initialize the database.
\`\`\`bash
# Install npm dependencies
npm install
# Setup database
npx prisma migrate dev
# Generate API client
npm run generate:api
\`\`\`
## Start Development Server
Runs the application in development mode with hot reloading enabled.
\`\`\`bash
npm run dev
\`\`\`
## Run Tests with Coverage
Executes the full test suite and generates a coverage report.
\`\`\`bash
npm run test:coverage
open coverage/index.html
\`\`\`
Now your documentation is always in sync with what actually runs, because they're the same thing!
Real-World Use Case: Polyglot Projects
mx really shines in projects that use multiple programming languages. Here's an example from a typical web application:
## Setup
Install all dependencies across the stack.
\`\`\`bash
npm install
pip install -r requirements.txt
go mod download
\`\`\`
## Frontend Build
Bundle and optimize the React application.
\`\`\`javascript
const esbuild = require('esbuild');
esbuild.build({
entryPoints: ['frontend/src/index.tsx'],
bundle: true,
minify: true,
outfile: 'dist/app.js',
}).catch(() => process.exit(1));
\`\`\`
## Backend Test
Run the Python backend test suite.
\`\`\`python
import pytest
import sys
result = pytest.main(['-v', 'tests/'])
sys.exit(result)
\`\`\`
## Data Migration
Execute Go-based database migration.
\`\`\`go
package main
import (
"database/sql"
"log"
_ "github.com/lib/pq"
)
func main() {
db, err := sql.Open("postgres", "...")
if err != nil {
log.Fatal(err)
}
// Migration logic here
}
\`\`\`
## Generate API Docs
Create API documentation from OpenAPI spec.
\`\`\`ruby
require 'yaml'
require 'erb'
spec = YAML.load_file('openapi.yaml')
template = ERB.new(File.read('docs/template.erb'))
File.write('docs/api.html', template.result(binding))
\`\`\`
With mx, you can run all these tasks seamlessly:
mx Setup
mx "Frontend Build"
mx "Backend Test"
mx "Data Migration"
mx "Generate API Docs"
Each task automatically uses the appropriate runtime based on the code block language!
Advanced Features
Multiple Documentation Files
Organize complex projects with multiple task files:
mx -f docs/deployment.md "Deploy to Production"
mx -f docs/maintenance.md "Database Backup"
Custom Heading Levels
Prefer using ###
for tasks? Configure it in mx.toml
:
heading_level = 3
Task Discovery
New to a project? List all available tasks to see what you can do:
mx
# or
mx list --file docs/tasks.md
Use Cases
mx is perfect for:
- Open Source Projects: Make it easy for contributors to discover and run common tasks
- Polyglot Microservices: Manage tasks across services written in different languages
- Documentation Sites: Keep your documentation examples executable and tested
- Onboarding: New developers learn about and run tasks from one place
- CI/CD Workflows: Document deployment procedures that can be executed locally or in pipelines
- Data Science: Combine Python analysis, R scripts, and bash data processing in one place
Current Status
⚠️ Note: mx is currently under active development. While it's functional and usable, the API and features may change as the project evolves.
Contributing
mx is open source and welcomes contributions! Check out the GitHub repository to:
- Report issues
- Suggest features
- Submit pull requests
- Star the project ⭐
Conclusion
mx brings together documentation and automation in a natural way. By treating Markdown as executable documentation and providing seamless multi-language runtime support, it eliminates the gap between what you document and what you actually run.
If you're working on projects that use multiple programming languages and want your documentation to stay in sync with your actual workflows, give mx a try!
Links
- GitHub: https://github.com/harehare/mx
- mq (underlying parser): https://github.com/harehare/mq
- License: MIT
Top comments (0)