Mdflow
Multi-backend CLI for executable markdown prompts. Run .md files against Claude, Codex, Gemini, or Copilot.
Install / Use
/learn @johnlindquist/MdflowQuality Score
Category
Development & EngineeringSupported Platforms
README
mdflow
review.claude.md # Run with Claude
commit.gemini.md "fix auth bug" # Run with Gemini
git diff | explain.claude.md # Pipe through any command
Your markdown files are now executable AI agents.
What Is This?
Markdown files become first-class CLI commands. Write a prompt in markdown, run it like a script. The command is inferred from the filename.
# review.claude.md
---
model: opus
---
Review this code for bugs and suggest improvements.
@./src/**/*.ts
review.claude.md # Runs: claude --model opus <prompt>
review.claude.md --verbose # Pass extra flags
How It Works
1. Filename → Command
Name your file task.COMMAND.md and the command is inferred:
task.claude.md # Runs claude
task.gemini.md # Runs gemini
task.codex.md # Runs codex
task.copilot.md # Runs copilot (print mode by default)
2. Frontmatter → CLI Flags
Every YAML key becomes a CLI flag passed to the command:
---
model: opus # → --model opus
dangerously-skip-permissions: true # → --dangerously-skip-permissions
mcp-config: ./mcp.json # → --mcp-config ./mcp.json
add-dir: # → --add-dir ./src --add-dir ./tests
- ./src
- ./tests
---
3. Body → Prompt
The markdown body is passed as the final argument to the command.
Unix Philosophy
mdflow embraces the Unix philosophy:
- No magic mapping - Frontmatter keys pass directly to the command
- Stdin/stdout - Pipe data in and out
- Composable - Chain agents together
- Transparent - See what runs in logs
# Pipe input
git diff | mdflow review.claude.md
# Chain agents
mdflow plan.claude.md | mdflow implement.codex.md
Installation
npm install -g mdflow
# or
bun install && bun link
Quick Start
# Run with filename-inferred command
mdflow task.claude.md
mdflow task.gemini.md
# Override command via --_command flag
mdflow task.md --_command claude
mdflow task.md -_c gemini
# Pass additional flags to the command
mdflow task.claude.md --verbose --debug
Note: Both
mdflowandmdcommands are available.
Command Resolution
Commands are resolved in this priority order:
- CLI flag:
--_command claudeor-_c claude - Filename pattern:
task.claude.md→claude
If no command can be resolved, you'll get an error with instructions.
Flag Hijacking
Some CLI flags are "hijacked" by mdflow—they're consumed and never passed to the underlying command. This allows generic markdown files without command names to be executed.
--_command / -_c
Override the command for any markdown file:
# Run a generic .md file with any command
mdflow task.md --_command claude
mdflow task.md -_c gemini
# Override the filename-inferred command
mdflow task.claude.md --_command gemini # Runs gemini, not claude
_varname Template Variables
Frontmatter fields starting with _ (except internal keys like _interactive, _cwd, _subcommand) define template variables:
---
_feature_name: Authentication # Default value
_target_dir: src/features # Default value
---
Build {{ _feature_name }} in {{ _target_dir }}.
# Use defaults
mdflow create.claude.md
# Override with CLI flags (consumed by mdflow, not passed to command)
mdflow create.claude.md --_feature_name "Payments" --_target_dir "src/billing"
The --_feature_name and --_target_dir flags are consumed by mdflow for template substitution—they won't be passed to the command.
No frontmatter declaration required: You can pass --_varname flags without declaring them in frontmatter. If the variable is used in the body but not provided, you'll be prompted for it:
---
print: true
---
{% if _verbose == "yes" %}Detailed analysis:{% endif %}
Review this code: {{ _target }}
mdflow review.claude.md --_verbose yes --_target "./src"
Positional Arguments as Template Variables
CLI positional arguments are available as {{ _1 }}, {{ _2 }}, etc.:
---
print: true
---
Translate "{{ _1 }}" to {{ _2 }}.
mdflow translate.claude.md "hello world" "French"
# → Translate "hello world" to French.
Use {{ _args }} to get all positional args as a numbered list:
---
print: true
---
Process these items:
{{ _args }}
mdflow process.claude.md "apple" "banana" "cherry"
# → Process these items:
# → 1. apple
# → 2. banana
# → 3. cherry
_stdin - Piped Input
When you pipe content to mdflow, it's available as the _stdin template variable:
---
model: haiku
---
Summarize this: {{ _stdin }}
cat README.md | md summarize.claude.md
Interactive Form Inputs
Use _inputs to define typed interactive prompts with validation:
---
model: sonnet
_inputs:
_name:
type: text
description: "Enter your name"
default: "World"
_env:
type: select
options: [dev, staging, prod]
_count:
type: number
description: "How many items?"
_confirm:
type: confirm
description: "Are you sure?"
_secret:
type: password
description: "API key"
---
Hello {{ _name }}! Deploying to {{ _env }} with {{ _count }} items.
Input types:
text- Free text input (default if no type specified)select- Choose from a list of optionsnumber- Numeric inputconfirm- Yes/no booleanpassword- Hidden input for secrets
Legacy format: _inputs: [_name, _value] (array of variable names) still works.
Frontmatter Reference
System Keys (handled by md)
| Field | Type | Description |
|-------|------|-------------|
| _varname | string | Template variable with default value (use {{ _varname }} in body) |
| _inputs | object/array | Interactive form inputs (see above) |
| _env | object | Set process environment variables |
| $1, $2... | string | Map positional args to flags (e.g., $1: prompt) |
| _interactive / _i | boolean | Enable interactive mode (overrides print-mode defaults) |
| _subcommand | string/string[] | Prepend subcommand(s) to CLI args |
| _cwd | string | Override working directory for inline commands |
| context_window | number | Override token limit for context (default: model-based) |
Auto-Injected Template Variables
| Variable | Description |
|----------|-------------|
| {{ _stdin }} | Content piped to mdflow |
| {{ _1 }}, {{ _2 }}... | Positional CLI arguments |
| {{ _args }} | All positional args as numbered list (1. arg1, 2. arg2, ...) |
All Other Keys → CLI Flags
Every other frontmatter key is passed directly to the command:
---
model: opus # → --model opus
dangerously-skip-permissions: true # → --dangerously-skip-permissions
mcp-config: ./mcp.json # → --mcp-config ./mcp.json
p: true # → -p (single char = short flag)
---
Value conversion:
key: "value"→--key valuekey: true→--keykey: false→ (omitted)key: [a, b]→--key a --key b
Print vs Interactive Mode
All commands run in print mode by default (non-interactive, exit after completion). Use the .i. filename marker, _interactive frontmatter, or CLI flags to enable interactive mode.
Print Mode (Default)
task.claude.md # Runs: claude --print "..."
task.copilot.md # Runs: copilot --silent --prompt "..."
task.codex.md # Runs: codex exec "..."
task.gemini.md # Runs: gemini "..." (one-shot)
Interactive Mode
Add .i. before the command name in the filename:
task.i.claude.md # Runs: claude "..." (interactive session)
task.i.copilot.md # Runs: copilot --silent --interactive "..."
task.i.codex.md # Runs: codex "..." (interactive session)
task.i.gemini.md # Runs: gemini --prompt-interactive "..."
Or use _interactive (or _i) in frontmatter:
---
_interactive: true # or _interactive: (empty), or _i:
model: opus
---
Review this code with me interactively.
Or use CLI flags:
mdflow task.claude.md --_interactive # Enable interactive mode
mdflow task.claude.md -_i # Short form
Global Configuration
Set default frontmatter per command in ~/.mdflow/config.yaml:
commands:
claude:
model: sonnet # Default model for claude
copilot:
silent: true # Always use --silent for copilot
Built-in defaults: All commands default to print mode with appropriate flags per CLI tool.
Examples
Claude with MCP Server
# db.claude.md
---
model: opus
mcp-config: ./postgres-mcp.json
dangerously-skip-permissions: true
---
Analyze the database schema and suggest optimizations.
Gemini YOLO Mode
# refactor.gemini.md
---
model: gemini-3-pro-preview
yolo: true
---
Refactor the authentication module to use async/await.
Codex with Sandbox
# analyze.codex.md
---
model: o3
sandbox: workspace-write
full-auto: true
---
Analyze this codebase and suggest improvements.
Copilot (no frontmatter needed!)
# task.copilot.md
Explain this code.
This runs: copilot --silent --prompt "Explain this code." (print mode)
For interactive mode, use .i. in the filename:
# task.i.copilot.md
Explain this code.
This runs: copilot --silent --interactive "Explain this code."
Template Variables
# create-feature.claude.md
---
_feature_name: ""
_target_dir: src/features
model: sonnet
---
Create a new feature called "{{ _feature_name }}" in {{ _target_dir }}.
mdflow create-feature.claude.md --_feature_name "Auth"
Environment Variables
Use _env (underscore prefix) to set environment variables for the command:
# api-test.claude.md
---
_env:
API_URL: https://api.example.com
DEBUG: "true"
---
Related Skills
node-connect
342.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
85.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
342.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
342.5kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
