markdown-agent
v2.16.0
Published
```bash review.claude.md # Run with Claude commit.gemini.md "fix auth bug" # Run with Gemini git diff | explain.claude.md # Pipe through any command ```
Readme
markdown-agent
review.claude.md # Run with Claude
commit.gemini.md "fix auth bug" # Run with Gemini
git diff | explain.claude.md # Pipe through any commandYour 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/**/*.tsreview.claude.md # Runs: claude --model opus <prompt>
review.claude.md --verbose # Pass extra flagsHow 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
markdown-agent 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 | ma review.claude.md
# Chain agents
ma plan.claude.md | ma implement.codex.mdInstallation
npm install -g markdown-agent
# or
bun install && bun linkQuick Start
# Run with filename-inferred command
ma task.claude.md
ma task.gemini.md
# Override command via --command flag
ma task.md --command claude
ma task.md -c gemini
# Pass additional flags to the command
ma task.claude.md --verbose --debugNote: Both
maandmarkdown-agentcommands 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 markdown-agent—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
ma task.md --command claude
ma task.md -c gemini
# Override the filename-inferred command
ma task.claude.md --command gemini # Runs gemini, not claude$varname Fields
Frontmatter fields starting with $ (except $1, $2...) hijack their corresponding CLI flags:
---
$feature_name: Authentication # Default value
$target_dir: src/features # Default value
---
Build {{ feature_name }} in {{ target_dir }}.# Use defaults
ma create.claude.md
# Override with CLI flags (hijacked, not passed to command)
ma create.claude.md --feature_name "Payments" --target_dir "src/billing"The --feature_name and --target_dir flags are consumed by markdown-agent for template substitution—they won't be passed to the command.
Frontmatter Reference
System Keys (handled by markdown-agent)
| Field | Type | Description |
|-------|------|-------------|
| args | string[] | Named positional arguments for template variables |
| env | object | Set process environment variables |
| env | string[] | Pass as --env flags to command |
| $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 |
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:
ma task.claude.md --_interactive # Enable interactive mode
ma task.claude.md -_i # Short formGlobal Configuration
Set default frontmatter per command in ~/.markdown-agent/config.yaml:
commands:
claude:
model: sonnet # Default model for claude
copilot:
silent: true # Always use --silent for copilotBuilt-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 with Args
# create-feature.claude.md
---
args: [feature_name, target_dir]
model: sonnet
---
Create a new feature called "{{ feature_name }}" in {{ target_dir }}.ma create-feature.claude.md "Auth" "src/features"Environment Variables
# api-test.claude.md
---
env:
API_URL: https://api.example.com
DEBUG: "true"
---
Test the API at !`echo $API_URL`Imports & Command Inlines
Inline content from other files or command output directly in your prompts.
File Imports
Use @ followed by a path to inline file contents:
---
model: claude
---
Follow these coding standards:
@~/.config/coding-standards.md
Now review this code:
@./src/api.ts@~/path- Expands~to home directory@./path- Relative to current markdown file@/path- Absolute path
Imports are recursive—imported files can have their own @ imports.
Glob Imports
Use glob patterns to include multiple files at once:
Review all TypeScript files in src:
@./src/**/*.tsGlob imports:
- Respect
.gitignoreautomatically - Include common exclusions (
node_modules,.git, etc.) - Are limited to ~100,000 tokens by default
- Set
MA_FORCE_CONTEXT=1to override the token limit
Files are formatted as XML with path attributes:
<api path="src/api.ts">
...file content...
</api>
<utils path="src/utils.ts">
...file content...
</utils>Line Range Imports
Extract specific lines from a file:
@./src/api.ts:10-50This imports only lines 10-50 from the file.
Symbol Extraction
Extract specific TypeScript/JavaScript symbols (interfaces, types, functions, classes, etc.):
@./src/types.ts#UserInterface
@./src/api.ts#fetchUserSupported symbols:
interface Name { ... }type Name = ...function Name(...) { ... }class Name { ... }const/let/var Name = ...enum Name { ... }
Command Inlines
Use !`command` to execute a shell command and inline its output:
Current branch: !`git branch --show-current`
Recent commits:
!`git log --oneline -5`
Based on the above, suggest what to work on next.URL Imports
Fetch content from URLs (markdown and JSON only):
@https://raw.githubusercontent.com/user/repo/main/README.mdEnvironment Variables
markdown-agent automatically loads .env files from the markdown file's directory.
Loading Order
Files are loaded in order (later files override earlier):
.env- Base environment.env.local- Local overrides (not committed).env.development/.env.production- Environment-specific.env.development.local/.env.production.local- Environment-specific local
Example
my-agents/
├── .env # API_KEY=default
├── .env.local # API_KEY=my-secret (gitignored)
└── review.claude.mdEnvironment variables are available:
- In command inlines:
!`echo $API_KEY` - In the spawned command's environment
CLI Options
Usage: ma <file.md> [any flags for the command]
ma <file.md> --command <cmd>
ma --setup
ma --logs
ma --help
Command resolution:
1. --command flag (e.g., ma task.md --command claude)
2. Filename pattern (e.g., task.claude.md → claude)
All frontmatter keys are passed as CLI flags to the command.
Global defaults can be set in ~/.markdown-agent/config.yaml
ma-specific flags (consumed, not passed to command):
--command, -c Specify command to run
--dry-run Preview without executing
--_interactive, -_i Enable interactive mode
Examples:
ma task.claude.md -p "print mode"
ma task.claude.md --model opus --verbose
ma commit.gemini.md
ma task.md --command claude
ma task.md -c gemini
ma task.claude.md -_i # Run in interactive mode
Without a file:
ma --setup Configure shell to run .md files directly
ma --logs Show log directory
ma --help Show this helpEnvironment Variables
| Variable | Description |
|----------|-------------|
| MA_FORCE_CONTEXT | Set to 1 to disable the 100k token limit for glob imports |
| NODE_ENV | Controls which .env.[NODE_ENV] file is loaded (default: development) |
Shell Setup
Make .md files directly executable:
ma --setup # One-time setupThen run agents directly:
task.claude.md # Just type the filename
task.claude.md --verbose # With passthrough argsManual Setup (zsh)
Add to ~/.zshrc:
alias -s md='ma'
export PATH="$HOME/agents:$PATH" # Your agent libraryBuilding Your Agent Library
Create a directory of agents and add it to PATH:
~/agents/
├── review.claude.md # Code review
├── commit.gemini.md # Commit messages
├── explain.claude.md # Code explainer
├── test.codex.md # Test generator
└── debug.claude.md # Debugging helperexport PATH="$HOME/agents:$PATH"Now use them from anywhere:
review.claude.md # Review current directory
commit.gemini.md "add auth" # Generate commit message
git diff | review.claude.md # Review staged changesNotes
- If no frontmatter is present, the file is printed as-is (unless command inferred from filename)
- Template system uses LiquidJS - supports conditionals, loops, and filters
- Logs are always written to
~/.markdown-agent/logs/<agent-name>/for debugging - Use
--logsto show the log directory - Stdin is wrapped in
<stdin>tags and prepended to the prompt
