{"id":36954013,"url":"https://github.com/johnlindquist/mdflow","last_synced_at":"2026-07-05T02:01:45.522Z","repository":{"id":327920817,"uuid":"1111357463","full_name":"johnlindquist/mdflow","owner":"johnlindquist","description":"Multi-backend CLI for executable markdown prompts. Run .md files against Claude, Codex, Gemini, or Copilot.","archived":false,"fork":false,"pushed_at":"2025-12-19T19:33:15.000Z","size":1150,"stargazers_count":582,"open_issues_count":20,"forks_count":39,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-05-06T00:11:59.537Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/johnlindquist.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-06T19:22:12.000Z","updated_at":"2026-05-04T19:30:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/johnlindquist/mdflow","commit_stats":null,"previous_names":["johnlindquist/mdflow","johnlindquist/markdown-agent"],"tags_count":58,"template":false,"template_full_name":null,"purl":"pkg:github/johnlindquist/mdflow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnlindquist%2Fmdflow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnlindquist%2Fmdflow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnlindquist%2Fmdflow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnlindquist%2Fmdflow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/johnlindquist","download_url":"https://codeload.github.com/johnlindquist/mdflow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnlindquist%2Fmdflow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35141083,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-13T12:54:55.911Z","updated_at":"2026-07-05T02:01:45.479Z","avatar_url":"https://github.com/johnlindquist.png","language":"TypeScript","funding_links":[],"categories":["General"],"sub_categories":[],"readme":"# mdflow\n\n```bash\nreview.claude.md                 # Run with Claude\ncommit.gemini.md \"fix auth bug\"  # Run with Gemini\ngit diff | explain.claude.md     # Pipe through any command\n```\n\n**Your markdown files are now executable AI agents.**\n\n---\n\n## What Is This?\n\nMarkdown files become first-class CLI commands. Write a prompt in markdown, run it like a script. The command is inferred from the filename.\n\n```markdown\n# review.claude.md\n---\nmodel: opus\n---\nReview this code for bugs and suggest improvements.\n\n@./src/**/*.ts\n```\n\n```bash\nreview.claude.md                 # Runs: claude --model opus \u003cprompt\u003e\nreview.claude.md --verbose       # Pass extra flags\n```\n\n---\n\n## How It Works\n\n### 1. Filename → Command\n\nName your file `task.COMMAND.md` and the command is inferred:\n\n```bash\ntask.claude.md    # Runs claude\ntask.gemini.md    # Runs gemini\ntask.codex.md     # Runs codex\ntask.copilot.md   # Runs copilot (print mode by default)\n```\n\n### 2. Frontmatter → CLI Flags\n\nEvery YAML key becomes a CLI flag passed to the command:\n\n```yaml\n---\nmodel: opus              # → --model opus\ndangerously-skip-permissions: true  # → --dangerously-skip-permissions\nmcp-config: ./mcp.json   # → --mcp-config ./mcp.json\nadd-dir:                 # → --add-dir ./src --add-dir ./tests\n  - ./src\n  - ./tests\n---\n```\n\n### 3. Body → Prompt\n\nThe markdown body is passed as the final argument to the command.\n\n---\n\n## Unix Philosophy\n\nmdflow embraces the Unix philosophy:\n\n- **No magic mapping** - Frontmatter keys pass directly to the command\n- **Stdin/stdout** - Pipe data in and out\n- **Composable** - Chain agents together\n- **Transparent** - See what runs in logs\n\n```bash\n# Pipe input\ngit diff | mdflow review.claude.md\n\n# Chain agents\nmdflow plan.claude.md | mdflow implement.codex.md\n```\n\n---\n\n## Installation\n\n```bash\nnpm install -g mdflow\n# or\nbun install \u0026\u0026 bun link\n```\n\n## Quick Start\n\n```bash\n# Run with filename-inferred command\nmdflow task.claude.md\nmdflow task.gemini.md\n\n# Override command via --_command flag\nmdflow task.md --_command claude\nmdflow task.md -_c gemini\n\n# Pass additional flags to the command\nmdflow task.claude.md --verbose --debug\n```\n\n\u003e **Note:** Both `mdflow` and `md` commands are available.\n\n---\n\n## Command Resolution\n\nCommands are resolved in this priority order:\n\n1. **CLI flag**: `--_command claude` or `-_c claude`\n2. **Filename pattern**: `task.claude.md` → `claude`\n\nIf no command can be resolved, you'll get an error with instructions.\n\n---\n\n## Flag Hijacking\n\nSome 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.\n\n### `--_command` / `-_c`\n\nOverride the command for any markdown file:\n\n```bash\n# Run a generic .md file with any command\nmdflow task.md --_command claude\nmdflow task.md -_c gemini\n\n# Override the filename-inferred command\nmdflow task.claude.md --_command gemini  # Runs gemini, not claude\n```\n\n### `_varname` Template Variables\n\nFrontmatter fields starting with `_` (except internal keys like `_interactive`, `_cwd`, `_subcommand`) define template variables:\n\n```yaml\n---\n_feature_name: Authentication   # Default value\n_target_dir: src/features       # Default value\n---\nBuild {{ _feature_name }} in {{ _target_dir }}.\n```\n\n```bash\n# Use defaults\nmdflow create.claude.md\n\n# Override with CLI flags (consumed by mdflow, not passed to command)\nmdflow create.claude.md --_feature_name \"Payments\" --_target_dir \"src/billing\"\n```\n\nThe `--_feature_name` and `--_target_dir` flags are consumed by mdflow for template substitution—they won't be passed to the command.\n\n**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:\n\n```yaml\n---\nprint: true\n---\n{% if _verbose == \"yes\" %}Detailed analysis:{% endif %}\nReview this code: {{ _target }}\n```\n\n```bash\nmdflow review.claude.md --_verbose yes --_target \"./src\"\n```\n\n### Positional Arguments as Template Variables\n\nCLI positional arguments are available as `{{ _1 }}`, `{{ _2 }}`, etc.:\n\n```yaml\n---\nprint: true\n---\nTranslate \"{{ _1 }}\" to {{ _2 }}.\n```\n\n```bash\nmdflow translate.claude.md \"hello world\" \"French\"\n# → Translate \"hello world\" to French.\n```\n\nUse `{{ _args }}` to get all positional args as a numbered list:\n\n```yaml\n---\nprint: true\n---\nProcess these items:\n{{ _args }}\n```\n\n```bash\nmdflow process.claude.md \"apple\" \"banana\" \"cherry\"\n# → Process these items:\n# → 1. apple\n# → 2. banana\n# → 3. cherry\n```\n\n### `_stdin` - Piped Input\n\nWhen you pipe content to mdflow, it's available as the `_stdin` template variable:\n\n```yaml\n---\nmodel: haiku\n---\nSummarize this: {{ _stdin }}\n```\n\n```bash\ncat README.md | md summarize.claude.md\n```\n\n### Interactive Form Inputs\n\nUse `_inputs` to define typed interactive prompts with validation:\n\n```yaml\n---\nmodel: sonnet\n_inputs:\n  _name:\n    type: text\n    description: \"Enter your name\"\n    default: \"World\"\n  _env:\n    type: select\n    options: [dev, staging, prod]\n  _count:\n    type: number\n    description: \"How many items?\"\n  _confirm:\n    type: confirm\n    description: \"Are you sure?\"\n  _secret:\n    type: password\n    description: \"API key\"\n---\nHello {{ _name }}! Deploying to {{ _env }} with {{ _count }} items.\n```\n\n**Input types:**\n- `text` - Free text input (default if no type specified)\n- `select` - Choose from a list of options\n- `number` - Numeric input\n- `confirm` - Yes/no boolean\n- `password` - Hidden input for secrets\n\n**Legacy format:** `_inputs: [_name, _value]` (array of variable names) still works.\n\n---\n\n## Frontmatter Reference\n\n### System Keys (handled by md)\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `_varname` | string | Template variable with default value (use `{{ _varname }}` in body) |\n| `_inputs` | object/array | Interactive form inputs (see above) |\n| `_env` | object | Set process environment variables |\n| `$1`, `$2`... | string | Map positional args to flags (e.g., `$1: prompt`) |\n| `_interactive` / `_i` | boolean | Enable interactive mode (overrides print-mode defaults) |\n| `_subcommand` | string/string[] | Prepend subcommand(s) to CLI args |\n| `_cwd` | string | Override working directory for inline commands |\n| `context_window` | number | Override token limit for context (default: model-based) |\n\n### Auto-Injected Template Variables\n\n| Variable | Description |\n|----------|-------------|\n| `{{ _stdin }}` | Content piped to mdflow |\n| `{{ _1 }}`, `{{ _2 }}`... | Positional CLI arguments |\n| `{{ _args }}` | All positional args as numbered list (1. arg1, 2. arg2, ...) |\n\n### All Other Keys → CLI Flags\n\nEvery other frontmatter key is passed directly to the command:\n\n```yaml\n---\nmodel: opus                           # → --model opus\ndangerously-skip-permissions: true    # → --dangerously-skip-permissions\nmcp-config: ./mcp.json                # → --mcp-config ./mcp.json\np: true                               # → -p (single char = short flag)\n---\n```\n\n**Value conversion:**\n- `key: \"value\"` → `--key value`\n- `key: true` → `--key`\n- `key: false` → (omitted)\n- `key: [a, b]` → `--key a --key b`\n\n---\n\n## Print vs Interactive Mode\n\nAll 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.\n\n### Print Mode (Default)\n\n```bash\ntask.claude.md      # Runs: claude --print \"...\"\ntask.copilot.md     # Runs: copilot --silent --prompt \"...\"\ntask.codex.md       # Runs: codex exec \"...\"\ntask.gemini.md      # Runs: gemini \"...\" (one-shot)\n```\n\n### Interactive Mode\n\nAdd `.i.` before the command name in the filename:\n\n```bash\ntask.i.claude.md    # Runs: claude \"...\" (interactive session)\ntask.i.copilot.md   # Runs: copilot --silent --interactive \"...\"\ntask.i.codex.md     # Runs: codex \"...\" (interactive session)\ntask.i.gemini.md    # Runs: gemini --prompt-interactive \"...\"\n```\n\nOr use `_interactive` (or `_i`) in frontmatter:\n\n```yaml\n---\n_interactive: true   # or _interactive: (empty), or _i:\nmodel: opus\n---\nReview this code with me interactively.\n```\n\nOr use CLI flags:\n\n```bash\nmdflow task.claude.md --_interactive  # Enable interactive mode\nmdflow task.claude.md -_i             # Short form\n```\n\n---\n\n## Global Configuration\n\nSet default frontmatter per command in `~/.mdflow/config.yaml`:\n\n```yaml\ncommands:\n  claude:\n    model: sonnet # Default model for claude\n  copilot:\n    silent: true  # Always use --silent for copilot\n```\n\n**Built-in defaults:** All commands default to print mode with appropriate flags per CLI tool.\n\n---\n\n## Examples\n\n### Claude with MCP Server\n\n```markdown\n# db.claude.md\n---\nmodel: opus\nmcp-config: ./postgres-mcp.json\ndangerously-skip-permissions: true\n---\nAnalyze the database schema and suggest optimizations.\n```\n\n### Gemini YOLO Mode\n\n```markdown\n# refactor.gemini.md\n---\nmodel: gemini-3-pro-preview\nyolo: true\n---\nRefactor the authentication module to use async/await.\n```\n\n### Codex with Sandbox\n\n```markdown\n# analyze.codex.md\n---\nmodel: o3\nsandbox: workspace-write\nfull-auto: true\n---\nAnalyze this codebase and suggest improvements.\n```\n\n### Copilot (no frontmatter needed!)\n\n```markdown\n# task.copilot.md\nExplain this code.\n```\n\nThis runs: `copilot --silent --prompt \"Explain this code.\"` (print mode)\n\nFor interactive mode, use `.i.` in the filename:\n\n```markdown\n# task.i.copilot.md\nExplain this code.\n```\n\nThis runs: `copilot --silent --interactive \"Explain this code.\"`\n\n### Template Variables\n\n```markdown\n# create-feature.claude.md\n---\n_feature_name: \"\"\n_target_dir: src/features\nmodel: sonnet\n---\nCreate a new feature called \"{{ _feature_name }}\" in {{ _target_dir }}.\n```\n\n```bash\nmdflow create-feature.claude.md --_feature_name \"Auth\"\n```\n\n### Environment Variables\n\nUse `_env` (underscore prefix) to set environment variables for the command:\n\n```markdown\n# api-test.claude.md\n---\n_env:\n  API_URL: https://api.example.com\n  DEBUG: \"true\"\n---\nTest the API at !`echo $API_URL`\n```\n\n---\n\n## Imports \u0026 Command Inlines\n\nInline content from other files or command output directly in your prompts.\n\n### File Imports\n\nUse `@` followed by a path to inline file contents:\n\n```markdown\n---\nmodel: claude\n---\nFollow these coding standards:\n@~/.config/coding-standards.md\n\nNow review this code:\n@./src/api.ts\n```\n\n- `@~/path` - Expands `~` to home directory\n- `@./path` - Relative to current markdown file\n- `@/path` - Absolute path\n\nImports are recursive—imported files can have their own `@` imports.\n\n### Glob Imports\n\nUse glob patterns to include multiple files at once:\n\n```markdown\nReview all TypeScript files in src:\n@./src/**/*.ts\n```\n\nGlob imports:\n- Respect `.gitignore` automatically\n- Include common exclusions (`node_modules`, `.git`, etc.)\n- Are limited to ~100,000 tokens by default\n- Set `MDFLOW_FORCE_CONTEXT=1` to override the token limit\n\nFiles are formatted as XML with path attributes:\n\n```xml\n\u003capi path=\"src/api.ts\"\u003e\n...file content...\n\u003c/api\u003e\n\n\u003cutils path=\"src/utils.ts\"\u003e\n...file content...\n\u003c/utils\u003e\n```\n\n### Line Range Imports\n\nExtract specific lines from a file:\n\n```markdown\n@./src/api.ts:10-50\n```\n\nThis imports only lines 10-50 from the file.\n\n### Symbol Extraction\n\nExtract specific TypeScript/JavaScript symbols (interfaces, types, functions, classes, etc.):\n\n```markdown\n@./src/types.ts#UserInterface\n@./src/api.ts#fetchUser\n```\n\nSupported symbols:\n- `interface Name { ... }`\n- `type Name = ...`\n- `function Name(...) { ... }`\n- `class Name { ... }`\n- `const/let/var Name = ...`\n- `enum Name { ... }`\n\n### Command Inlines\n\nUse `` !`command` `` to execute a shell command and inline its output:\n\n```markdown\nCurrent branch: !`git branch --show-current`\nRecent commits:\n!`git log --oneline -5`\n\nBased on the above, suggest what to work on next.\n```\n\n### URL Imports\n\nFetch content from URLs (markdown and JSON only):\n\n```markdown\n@https://raw.githubusercontent.com/user/repo/main/README.md\n```\n\n**Caching:** Remote URLs are cached locally at `~/.mdflow/cache/` with a 1-hour TTL. Use `--_no-cache` to force a fresh fetch:\n\n```bash\nmdflow agent.claude.md --_no-cache\n```\n\n---\n\n## Environment Variables\n\nmdflow automatically loads `.env` files from the markdown file's directory.\n\n### Loading Order\n\nFiles are loaded in order (later files override earlier):\n\n1. `.env` - Base environment\n2. `.env.local` - Local overrides (not committed)\n3. `.env.development` / `.env.production` - Environment-specific\n4. `.env.development.local` / `.env.production.local` - Environment-specific local\n\n### Example\n\n```\nmy-agents/\n├── .env                    # API_KEY=default\n├── .env.local              # API_KEY=my-secret (gitignored)\n└── review.claude.md\n```\n\nEnvironment variables are available:\n- In command inlines: `` !`echo $API_KEY` ``\n- In the spawned command's environment\n\n---\n\n## CLI Options\n\n```\nUsage: md \u003cfile.md\u003e [flags for the command]\n       md \u003ccommand\u003e [options]\n\nCommands:\n  md create [name]        Create a new agent file (opens in $EDITOR)\n  md explain \u003cagent.md\u003e   Show resolved config without executing\n  md setup                Configure shell (PATH, aliases)\n  md logs                 Show agent log directory\n  md help                 Show this help\n\nCommand resolution:\n  1. --_command flag (e.g., md task.md --_command claude)\n  2. Filename pattern (e.g., task.claude.md → claude)\n\nAll frontmatter keys are passed as CLI flags to the command.\nGlobal defaults can be set in ~/.mdflow/config.yaml\n\nmd-specific flags (consumed, not passed to command):\n  --_command, -_c     Specify command to run\n  --_dry-run          Preview without executing\n  --_interactive, -_i Enable interactive mode\n  --_edit             Open resolved prompt in $EDITOR before execution\n  --_no-cache         Force fresh fetch for remote URLs (bypass cache)\n  --_trust            Bypass TOFU prompts for remote URLs\n  --_context          Show context tree and exit (no execution)\n  --_quiet            Skip context dashboard display before execution\n  --raw               Output raw markdown without rendering (for piping)\n\nExamples:\n  md task.claude.md -p \"print mode\"\n  md task.claude.md --model opus --verbose\n  md commit.gemini.md\n  md task.md --_command claude\n  md task.md -_c gemini\n  md task.claude.md --_dry-run    # Preview without executing\n  md task.claude.md --_edit       # Edit prompt before running\n  md https://example.com/agent.claude.md  # Remote execution\n\nWithout arguments:\n  md              Interactive agent picker (frecency-sorted)\n```\n\n### Environment Variables\n\n| Variable | Description |\n|----------|-------------|\n| `MDFLOW_FORCE_CONTEXT` | Set to `1` to disable the 100k token limit for glob imports |\n| `NODE_ENV` | Controls which `.env.[NODE_ENV]` file is loaded (default: `development`) |\n\n---\n\n## Shell Setup\n\nMake `.md` files directly executable:\n\n```bash\nmdflow setup   # One-time setup\n```\n\nThen run agents directly:\n\n```bash\ntask.claude.md                   # Just type the filename\ntask.claude.md --verbose         # With passthrough args\n```\n\n### Manual Setup (zsh)\n\nAdd to `~/.zshrc`:\n\n```bash\nalias -s md='mdflow'\nexport PATH=\"$HOME/agents:$PATH\"  # Your agent library\n```\n\n---\n\n## Building Your Agent Library\n\nCreate a directory of agents and add it to PATH:\n\n```\n~/agents/\n├── review.claude.md     # Code review\n├── commit.gemini.md     # Commit messages\n├── explain.claude.md    # Code explainer\n├── test.codex.md        # Test generator\n└── debug.claude.md      # Debugging helper\n```\n\n```bash\nexport PATH=\"$HOME/agents:$PATH\"\n```\n\nNow use them from anywhere:\n\n```bash\nreview.claude.md                 # Review current directory\ncommit.gemini.md \"add auth\"      # Generate commit message\ngit diff | review.claude.md      # Review staged changes\n```\n\n---\n\n## Rich Output Rendering\n\nBy default, LLM output is rendered with syntax highlighting and visual markdown structure (headers, code blocks, etc.). This uses `marked-terminal` for beautiful terminal output.\n\nTo bypass rendering (e.g., for piping to other commands):\n\n```bash\nmd task.claude.md --raw | jq .\n```\n\n---\n\n## Context Dashboard\n\nBefore execution, md shows a pre-flight dashboard with your context tree and token estimates:\n\n```\n┌─ Pre-Flight ──────────────────────────────────────────────────┐\n│  📄 review.claude.md                                   1.2 KB │\n│  ├── 📁 @./src/**/*.ts                     (12 files) 24.5 KB │\n│  └── 📄 @./README.md                                   3.1 KB │\n│                                                               │\n│  Total: 28.8 KB (~7,200 tokens)                              │\n└───────────────────────────────────────────────────────────────┘\n```\n\nUse `--_quiet` to skip the dashboard, or `--_context` to show it and exit without executing.\n\n---\n\n## The `md explain` Command\n\nInspect what an agent will do without running it:\n\n```bash\nmd explain review.claude.md\n```\n\nShows:\n- Resolved command and source (filename, flag, etc.)\n- Final flags after config merging (built-in → global → project → frontmatter)\n- Expanded prompt preview with token count\n- Trust status for remote URLs\n- Environment variables that will be set\n\n---\n\n## Edit Before Execute\n\nUse `--_edit` to open the fully resolved prompt in your `$EDITOR` before execution:\n\n```bash\nmd task.claude.md --_edit\n```\n\nThis lets you review and tweak the final prompt (after template substitution and import expansion) before sending it to the LLM.\n\n---\n\n## Notes\n\n- If no frontmatter is present, the file is printed as-is (unless command inferred from filename)\n- Template system uses [LiquidJS](https://liquidjs.com/) - supports conditionals, loops, and filters\n- Logs are always written to `~/.mdflow/logs/\u003cagent-name\u003e/` for debugging\n- Use `md logs` to show the log directory\n- Piped input is available as `{{ _stdin }}` template variable\n- Template variables use `_` prefix: `_name` in frontmatter → `{{ _name }}` in body → `--_name` CLI flag\n- Remote URLs are cached at `~/.mdflow/cache/` with 1-hour TTL (use `--_no-cache` to bypass)\n- Imports inside code blocks (``` or `) are ignored by the parser\n- Interactive file picker sorts by frecency (frequency + recency) for quick access to common agents\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnlindquist%2Fmdflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnlindquist%2Fmdflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnlindquist%2Fmdflow/lists"}