# mdflow > A Git-native control plane for repeatable local agent work: Markdown flows, behavioral evals, and proposal-first evolution. Free MIT CLI; external engines may charge. Workflows execute only through the local CLI; the website serves read-only documentation and source facts. ## When to use mdflow Use mdflow for repeatable jobs such as code review, release-note drafting, and issue triage: keep one reviewed Markdown flow per job in your project's flows/ directory, version it in Git, and reuse its declared inputs. After creating and reviewing a code-review flow and approving execution, invoke it with `md flows/code-review.md`; release-note and issue-triage flows follow the same local CLI pattern. These example filenames are user-authored jobs, not bundled commands. Install the [official mdflow npm package](https://www.npmjs.com/package/mdflow) with `npm install -g mdflow` (Bun is required), then run `md --version` and `md doctor --json`. Install and authenticate your selected engine separately. Doctor is static/read-only. A real flow run, eval, proposal, and source-changing apply require separate consent. Dry-run/explain may resolve imports; context isolation is not a host sandbox. ## Public source-facts interface Use `GET https://mdflow.dev/facts.json` with `Accept: application/json` to read the public static snapshot. No authentication, request body, or mutations. The site supports HEAD for metadata. The snapshot is refreshed when the site is built and published; hosting caches may serve an earlier snapshot. It describes published CLI source, not a project's files, installed engines, readiness, or execution results. The [OpenAPI 3.1 description](https://mdflow.dev/openapi.json) defines only GET /facts.json, with interface version `1.0.0`. The payload's `contract.contractVersion` is `1`; `versionBase` is the separate source CLI version. The [RFC 9727 API catalog](https://mdflow.dev/.well-known/api-catalog) links the specification and [HTTP usage documentation](https://mdflow.dev/docs/). Responses include `X-API-Version: 1` and `Link` relations for service-desc and service-doc. Additive fields are allowed within v1; tolerate unknown fields. Breaking representation changes require a new URL and a new major specification version, rather than silently changing existing fields. Site-handled errors use RFC 9457 `application/problem+json` with type, title, status, detail, a resolution hint, and an optional instance identifying the request: 404 for missing JSON resources, 405 for unsupported methods (Allow: GET, HEAD), and 406 for an unacceptable representation. Vercel hosting receives ordinary public request metadata and can produce its own error responses before site handling; see [privacy](https://mdflow.dev/privacy/). Check HTTP status and Content-Type before parsing a response. ## Start here - [Homepage in Markdown](https://mdflow.dev/index.md): When to use mdflow, installation, engines, commands, and security boundaries - [Documentation](https://mdflow.dev/docs.md): Public read-only HTTP usage, local CLI usage, and Flow UX Protocol v1 guidance - [Facts and operation contract](https://mdflow.dev/facts.json): Static source facts for version 4.9.0, contract version 1; read-only public snapshot - [Pricing and costs](https://mdflow.dev/pricing.md): Free MIT license and external-engine costs ## Source references - [Project README](https://mdflow.dev/README.md): Installation, flow syntax, engine setup, and workflow examples - [Public CLI reference](https://mdflow.dev/docs/public-api.md): Command and flag contracts, configuration, Flow UX Protocol v1, and errors - [Evolution specification](https://mdflow.dev/docs/evolve.md): Proposal-first changes, feedback, proof, consent, apply, and rollback - [Workflow feature guide](https://mdflow.dev/GUIDE-NEW-FEATURES.md): Steps, structured output, context providers, registry, and output modes - [Security policy](https://mdflow.dev/SECURITY.md): Execution boundaries and private vulnerability reporting - [Contribution guide](https://mdflow.dev/CONTRIBUTING.md): How to contribute to the project - [mdflow agent skill](https://mdflow.dev/skills/mdflow/SKILL.md): The repository’s installable instructions for creating and maintaining flows - [Skill digest index](https://mdflow.dev/.well-known/agent-skills/index.json): SHA-256 and source URL for the exact published skill bytes - [Full documentation](https://mdflow.dev/llms-full.txt): Combined source documentation with source URLs ## Articles and trust - [mdflow Evolve — change with proof](https://mdflow.dev/evolve-deep-dive.md): How feedback becomes a private prompt proposal with content-bound verification, capability checks, explicit review, atomic apply, and rollback. - [About mdflow](https://mdflow.dev/about.md): A Git-native, open-source CLI for repeatable agent work, with inspectable inputs, behavioral evals, and proposal-first evolution. - [Contact and security — mdflow](https://mdflow.dev/contact.md): Report mdflow bugs through GitHub issues and security vulnerabilities privately through GitHub Security Advisories. - [Privacy — mdflow](https://mdflow.dev/privacy.md): How the static documentation site differs from local CLI execution, browser storage, hosting requests, and external engine processing. - [GitHub repository](https://github.com/johnlindquist/mdflow): Source, releases, and contributions - [Issue tracker](https://github.com/johnlindquist/mdflow/issues): Public bugs and questions - [Private vulnerability reporting](https://github.com/johnlindquist/mdflow/security/advisories/new): Security reports; do not post exploit details publicly --- --- title: mdflow — repeatable agent work url: https://mdflow.dev/ description: A Git-native control plane for repeatable local agent work, using Markdown flows, behavioral evals, and proposal-first evolution. --- # mdflow A Git-native control plane for repeatable agent work. Define each job as Markdown, run it on the CLI engine you already use, inspect its inputs, and gate prompt revisions with behavioral evals. Keep one flow per repeatable job in a project's `flows/` roster, with frontmatter for configuration and a Markdown body for the prompt. ## When to use mdflow Use it when a code review, release-note draft, issue-triage task, or other recurring agent job should be readable, reusable, diffable, and shared with a team. Flows are local files you can review in Git. Bare `md` opens a searchable Flow Workbench for project, global, installed, and PATH flows. Workflows execute only through the local CLI; the website serves read-only documentation and source facts. ## Install and get oriented mdflow runs on [Bun](https://bun.sh). If Bun is missing, the interactive launcher offers to install it; non-interactive environments must install Bun first. The selected engine CLI must also be installed and authenticated. Installing packages or configuring an engine changes the environment and is not part of read-only inspection. The official npm package is [mdflow](https://www.npmjs.com/package/mdflow). ```bash npm install -g mdflow md --version md doctor --json md roster --json ``` For one-off package invocation, use `npx mdflow`. After approving local setup, `npx mdflow init --yes` creates a deterministic starter roster without an engine invocation. `md init --guided` instead launches engine-guided setup; do not treat those two commands as having the same cost or consent. Install the companion agent skill with `npx skills add johnlindquist/mdflow`. The source facts describe mdflow **4.9.0**, not necessarily the version installed on your machine. Run `md --version` and inspect the installed CLI's help when making compatibility decisions. ## Engines and resolution The built-in default is **pi**. Supported engine names from the current source facts: `claude`, `copilot`, `codex`, `gemini`, `droid`, `opencode`, `pi`, `cursor-agent`, `agy`, `grok`, `kimi`. Resolution, most explicit first: 1. --engine flag — deprecated aliases: --_command/-_c, --tool 2. MDFLOW_ENGINE env var 3. filename (task.claude.md) — must name a real engine 4. frontmatter engine: — deprecated aliases: tool:/_tool: (they warn) 5. config engine: — project config beats ~/.mdflow/config.yaml 6. default: pi — implicit picks are announced on stderr ## Inspect before execution - `md doctor --json` provides static diagnostics, installed-engine readiness, and effect-labelled next actions without fetching imports, running code, or writing files. - `md explain --json` resolves a prompt and invocation without launching the engine. File, URL, and context-provider imports may still resolve. - `md eval --plan` reports the planned eval invocation count before consent. Executing the suite is a separate operation. - `md evolve plan ` previews evolution readiness, cost, capabilities, and writes. A proposal does not apply itself. ## Permissions, security, and costs The operation contract distinguishes `FREE` (no engine invocation), `LOCAL_WRITE` (local changes), and `ENGINE` (provider-backed work). `FREE` is not a universal promise of zero side effects or network access: dry-run and explanation can resolve imports. Use doctor for strictly static inspection. - A real flow run, eval run, proposal run, and source mutation require separate consent. - Eval sidecars are executable local TypeScript; static plans do not import them, but real eval runs do. - Hook sidecars are executable local TypeScript and must be reviewed before use. - Evolution creates a private proposal; applying it is a separate explicit source mutation. - Engine context isolation is not a filesystem, network, process, environment, or credential sandbox. - Dry-run skips engines, inline commands, and executable fences, but file, URL, and context-provider imports may still resolve. - Registry install adds one flow, not trusted eval or hook sidecars. - A suite's presence is not verification; Verified requires a current fingerprint-bound full-run receipt. - Compatibility stamps are managed by successful local runs, not by diagnostics. Review a flow like executable code. Use least-privilege credentials and, for untrusted flows, a disposable environment with restricted access. The CLI is free under the MIT license; external engines and providers may charge. See [pricing and costs](https://mdflow.dev/pricing.md) and [the security policy](https://mdflow.dev/SECURITY.md). ## Agent interfaces Workflows execute only through the local CLI; the website serves read-only documentation and source facts. Flow UX Protocol v1 uses `protocolVersion: 1`. Check the installed version and JSON protocol version rather than scraping terminal output. `md doctor --json`, `md roster --json`, and `md explain --json` expose structured inspection; `md --events` performs a real run with an NDJSON event stream. `md --json` emits a single result object instead. Real execution needs separate consent. The public read-only machine interface is `GET /facts.json`: a static source-facts snapshot, including command and operation descriptions, with no authentication or mutations. Request `Accept: application/json`. Its [OpenAPI 3.1 description](https://mdflow.dev/openapi.json) has interface version `1.0.0`; the payload has `contract.contractVersion: 1`. These versions are separate from the source CLI version `4.9.0`. Discover it through the [API catalog](https://mdflow.dev/.well-known/api-catalog). This snapshot describes the source published with the site, not your project's state or readiness. The response carries `X-API-Version: 1` and discovery `Link` headers. Additive fields are allowed within v1; clients should tolerate unknown fields. Breaking representation changes require a new URL and a new major specification version rather than silently changing existing fields. Site-handled JSON failures use RFC 9457 `application/problem+json`: missing resources return 404, unsupported methods return 405 (`Allow: GET, HEAD`), and unsupported response representations return 406. Public hosting can return its own errors or cached content; see [HTTP usage and hosting](https://mdflow.dev/docs/) and the [privacy page](https://mdflow.dev/privacy/). The [full public CLI reference](https://mdflow.dev/docs/public-api.md) separately defines local CLI fields, event ordering, and errors. ## Commands - `md doctor [--json]` — Inspect project readiness and safe next actions without execution. - `md init [--guided] [--engine ] [--yes] [--agents] [--print-guide]` — Initialize a project flow roster. - `md create [intent] [--global] [--dry-run]` — Create a flow and a fail-closed draft eval suite. - `md capture` — Print the guide for capturing the current agent conversation as a flow. - `md explain [--json]` — Resolve one flow without launching its engine. - `md render [--json|--out |--open]` — Inspect, write, or open a rendered flow explanation. - `md hooks add|list|remove [event...]` — Manage executable lifecycle hook sidecars. - `md eval [--plan] [--yes] [--json]` — Plan or run a flow's behavioral eval suite. - `md feedback | list|show|distill|dismiss|reopen|forget` — Manage durable evolution evidence. - `md complain ` — Compatibility alias for feedback. - `md evolve plan|status|propose|show|apply|reject|retry|rollback|history|prune` — Manage proposal-first flow evolution. - `md install [--global]` — Install one flow into the registry. - `md remove ` — Remove an installed registry flow. - `md list [--project|--global]` — List installed registry flows. - `md roster --json | roster sync [--check] [--agents] [--json]` — Inspect flows or synchronize the managed operator card. - `md catalog [--json]` — Machine-readable catalog of every discoverable flow (project, global, installed, PATH). - `md setup` — Configure shell integration. - `md logs` — Show flow log locations. - `md help [command]` — Show CLI or command help. ## Keep improvements reviewable Record a problem with `md feedback ""`, review an eval that can expose it, and inspect `md evolve plan ` before approving a proposal. Evolution works off-path. A reviewed proposal changes source only through a separate `md evolve apply ` decision. Eval-suite presence alone does not prove correctness; proof must be current and bound to the relevant content. Read [the deep dive](https://mdflow.dev/evolve-deep-dive/) and [the normative evolution specification](https://mdflow.dev/docs/evolve.md). ## Documentation and project information - [mdflow Evolve — change with proof](https://mdflow.dev/evolve-deep-dive/) — [Markdown](https://mdflow.dev/evolve-deep-dive.md) - [About mdflow](https://mdflow.dev/about/) — [Markdown](https://mdflow.dev/about.md) - [Contact and security — mdflow](https://mdflow.dev/contact/) — [Markdown](https://mdflow.dev/contact.md) - [Privacy — mdflow](https://mdflow.dev/privacy/) — [Markdown](https://mdflow.dev/privacy.md) - [Documentation and agent interfaces — mdflow](https://mdflow.dev/docs/) — [Markdown](https://mdflow.dev/docs.md) - [Agent skill](https://mdflow.dev/skills/mdflow/SKILL.md) and [skill SHA-256 index](https://mdflow.dev/.well-known/agent-skills/index.json) - [Concise resource index](https://mdflow.dev/llms.txt) and [full source documentation](https://mdflow.dev/llms-full.txt) - [GitHub repository](https://github.com/johnlindquist/mdflow), [issues](https://github.com/johnlindquist/mdflow/issues), and [MIT license](https://github.com/johnlindquist/mdflow/blob/main/LICENSE) --- --- title: mdflow pricing and costs url: https://mdflow.dev/pricing.md --- # Pricing and costs The mdflow CLI is free, open-source software under the [MIT license](https://github.com/johnlindquist/mdflow/blob/main/LICENSE). You can inspect the [source repository](https://github.com/johnlindquist/mdflow) and install the [official npm package](https://www.npmjs.com/package/mdflow) with `npm install -g mdflow` or invoke it with `npx mdflow`. Workflows execute only through the local CLI; the website serves read-only documentation and source facts. ## External engines may charge mdflow launches the engine CLI you select. That engine may require a subscription, provider account, API credentials, or usage-based payment. Provider turns, tokens, tool calls, and currency costs vary by engine and task; check your selected provider's current terms. A free mdflow license does not include free provider usage, and this page does not quote external providers' prices. ## Know the operation before approving it - `FREE`: no engine invocation. Static `md doctor --json` and eval/evolution planning help inspect readiness and likely work. Other free inspection commands may resolve imports, including URLs or local context providers. - `LOCAL_WRITE`: changes local files or private state without an engine turn. Deterministic init, recording feedback, and applying a reviewed proposal are examples. - `ENGINE`: launches one or more provider-backed invocations. A real flow run, engine-guided setup, real eval suite, and proposal generation can incur external costs. Eval plans include repetitions and print the planned invocation count before consent. Preview with `md eval --plan`; preview proposal work with `md evolve plan `. Invocation count is not a guaranteed currency quote. Approval for a flow run does not approve an eval, a proposal, or a source-changing apply. See [the operation contract](https://mdflow.dev/facts.json), [documentation](https://mdflow.dev/docs/), [privacy](https://mdflow.dev/privacy/), and [security policy](https://mdflow.dev/SECURITY.md) before running unfamiliar flows or connecting credentials. --- # mdflow Evolve — change with proof Canonical page: https://mdflow.dev/evolve-deep-dive/ --- title: mdflow Evolve — change with proof description: How feedback becomes a private prompt proposal with content-bound verification, capability checks, explicit review, atomic apply, and rollback. url: https://mdflow.dev/evolve-deep-dive/ --- # Agents that can change — through proof `mdflow v4 · proposal-first evolution` Evolve turns a reported problem into a reviewable prompt proposal. It does not let a timing signal rewrite a live agent, and it does not call an equal green score a fix. > `md feedback` → reviewed eval → `md evolve plan` → private proposal → exact > verification → review → explicit apply or reject. The moat is not self-editing. It is a provider-neutral change protocol where evidence, capabilities, cost, proof, consent, lineage, and rollback are all inspectable. ## The contract Evolve makes ten promises: 1. The resolved maintainer, maximum flow invocations, and write surface appear before paid work starts. 2. Quick reruns and metadata-only failures cannot authorize source mutation. 3. A proposal cannot silently add command, network, provider, or file access. 4. Current and proposal verification never use the canonical flow as a test fixture. 5. Proof is bound to exact content and execution configuration. 6. Apply and rollback are atomic, locked, hash-guarded transactions. 7. Rejection, timeout, and infrastructure failure leave evidence open. 8. “Verified improvement” requires feedback-specific red/green proof. 9. Decisions have durable states, reason codes, artifacts, and event journals. 10. Automatic policy creates proposals only. Source changes require review and an explicit apply command. ## One problem, one stable ID ```console $ md feedback flows/review.md "missed the renamed-file regression" Feedback fb_01J… saved for flows/review.md Status: saved, not yet proved Next: md feedback distill fb_01J… Plan: md evolve plan flows/review.md ``` Feedback is private, bounded, and durable. Each item moves through `open → targeted → resolved | dismissed`; it is never consumed by a wall-clock watermark. `md feedback list`, `show`, `dismiss`, and `reopen` make that state visible; `md feedback forget --yes` explicitly removes one item and its status history from private storage. `md evolve prune --days 30` removes older unapplied attempts and completed job logs after confirmation, while retaining applied rollback lineage. A quick rerun is ambiguous: maybe the user changed input, compared providers, or pressed Enter twice. When enabled, mdflow records it only as a low-confidence observation and asks for explicit feedback. It does not spend or mutate. ## Distill drafts a test, not truth ```console $ md feedback distill fb_01J… Draft eval case: ~/.mdflow/evolution/drafts/fb_01J….eval-case.ts This is an untrusted, deliberately failing draft. Review its assertion before copying it into the suite. ``` Eval files are executable TypeScript. Generated code cannot quietly become the policy that judges future agents, so the draft lives outside the repository and fails on purpose until a human defines an observable assertion. A reviewed case links back with `evidence: ["fb_01J…"]`. ## Plan before spending ```console $ md evolve plan flows/review.md proposal ready to plan: suite and actionable feedback are present cost: at most 7 flow invocations: 1 proposal + 3 current + 3 proposal writes: private evolution artifact only; source remains unchanged maintainer: claude/opus (isolated) ``` `plan` is free. It statically inspects the suite without importing executable top-level code, and the arithmetic includes repeated eval trials. Non-interactive paid work refuses without `--yes`; interactive confirmation defaults to No. Machine consumers can use one `--json` result or streaming `--events` NDJSON. The policy can cap invocations, proposals per day, and cooldown. `propose` mode prints this plan and queues private background work after explicit actionable feedback. Legacy `evolve: auto` maps to proposal-only behavior. There is no unattended apply. ## The live flow never becomes the fixture Each proposal gets a private `evr_...` receipt directory containing its plan, evidence membership, immutable current and proposal files, prompt/capability diffs, current/proposal results, decision, and append-only events. Current and proposal run from separate repository snapshots. Tracked and untracked non-ignored files are copied off-path; symlinks that escape the repository are refused. No `.pending.md`, scratch gate ledger, or half-tested candidate appears beside a runnable flow. These workspaces are isolation fixtures, not host sandboxes. Eval modules and engines still have whatever filesystem, network, credential, and process access the current user grants them. Timed-out process groups receive TERM and then KILL, but users must still review executable suites. ## Capability changes are a separate axis Freezing YAML is not enough: a prompt body can contain inline commands, executable fences, remote URLs, context providers, globs, and file imports. Evolve computes a capability manifest before candidate execution. New capabilities park the proposal as `capability_rejected` unless a policy explicitly permits the private experiment; they can never produce an automatic source edit. ## Proof is a receipt, not a timestamp A content receipt hashes: - the flow and execution-relevant imported files/globs; - the eval suite and its local module graph; - merged project/global configuration; - resolved engine and model; - mdflow version; and - case definitions, evidence links, repetitions, and quorum. Change any relevant input and the old receipt becomes stale. Moving a checkout does not invalidate equivalent content merely because an absolute path changed. Automatic comparative proof accepts either a clean current receipt or a stable receipt whose failures are all linked to the targeted feedback; unrelated current failures block the run. Unknown nonzero exits fail unless a case explicitly sets `allowNonZero: true`. Recognized provider, authentication, environment, cancellation, and timeout failures are inconclusive. Repeated stochastic cases report every trial; mixed pass/fail results are flaky and cannot mint clean proof, even when their numeric quorum passes. ## Four honest outcomes | Outcome | What it means | | --- | --- | | **Verified improvement** | A feedback-linked case failed on current, passed on proposal, and every proposal guardrail passed without flake. | | **Regression-safe proposal** | Guardrails passed, but no case proved the reported problem red/green. Human review only. | | **Rejected** | The proposal regressed declared behavior. Evidence remains open. | | **Inconclusive** | Timeout, infrastructure uncertainty, interruption, or flake prevented a trustworthy result. Evidence remains open. | Equal scores can justify a reviewable proposal. They cannot justify the word “fixed.” ## Review, apply, recover ```console $ md evolve show evr_01J… Status: verified_improvement Capabilities: no additions Invocations: 7/7 diff --git current.md proposal.md … $ md evolve apply evr_01J… Status: applied $ md evolve rollback evr_01J… Status: rolled_back ``` `show` exposes the decision, capability delta, planned/actual invocations, and prompt diff. Apply takes a per-flow lock and compare-and-swaps against the exact base hash; a human edit made after proposal creation wins. Persistence uses a same-directory temporary file, fsync, and rename. Rollback is guarded against overwriting newer work, and interrupted transaction states recover from content hashes rather than backup filenames. ## Policy without magic ```yaml evolve: mode: propose # off | observe | suggest | propose | apply triggers: [explicit-feedback, classified-failure] maintainer: engine: claude model: opus isolated: true timeout-ms: 180000 budget: max-invocations: 9 max-per-day: 2 cooldown-ms: 86400000 gate: require-feedback-eval: true allow-capability-delta: false repetitions: 1 apply: review ``` - `off`: no automatic observation or work. - `observe`: retain enabled evidence only. - `suggest`: surface the next free action without spending. - `propose`: queue bounded private proposal work; never edit source. - `apply`: reserved for a future earned automation tier; today it remains proposal-only for automatic handling. Use `--no-evolve` or `MDFLOW_EVOLVE=off` as an immediate escape hatch. Workflow runs keep the same feedback affordances, but proposal generation explicitly refuses until step-level attribution can identify the responsible agent node. `md evolve prune --days 30 --yes` removes eligible old private proposals and completed job logs while retaining applied lineage. ## The creed > If a guardrail is not covered by an eval, it is a wish. > If feedback is not covered by a red/green case, it is not a proved fix. > Agents may change—but only through evidence, capability accounting, exact > receipts, explicit consent, atomic application, and rollback. --- [→ mdflow.dev](https://mdflow.dev) · [→ GitHub](https://github.com/johnlindquist/mdflow) · [→ normative Evolve docs](https://github.com/johnlindquist/mdflow/blob/main/docs/evolve.md) --- # About mdflow Canonical page: https://mdflow.dev/about/ --- title: About mdflow description: A Git-native, open-source CLI for repeatable agent work, with inspectable inputs, behavioral evals, and proposal-first evolution. url: https://mdflow.dev/about/ --- # About mdflow mdflow is a Git-native control plane for repeatable agent work. Define a job as a Markdown file, keep it in a project's `flows/` directory, and run it on an installed agent CLI. Frontmatter supplies configuration; the body supplies the prompt. The flow stays readable, reviewable in a pull request, and reusable by a teammate or coding agent. ## When to use it Use mdflow for work you expect to repeat: code review, release notes, issue triage, and other jobs that benefit from shared instructions and declared inputs. Workflows execute only through the local CLI; the website serves read-only documentation and source facts. Your local environment supplies the engine, credentials, files, and permissions. The default engine is **pi**; supported engine names are `claude`, `copilot`, `codex`, `gemini`, `droid`, `opencode`, `pi`, `cursor-agent`, `agy`, `grok`, `kimi`. An engine must be installed and configured before it can run a flow. ## Inspect, evaluate, then improve Start with `md doctor --json` for a static readiness report. Inspect an individual flow with `md explain --json`, noting that imports may resolve. Colocated behavioral evals can check a flow's declared behavior, but a suite's presence is not proof that it passes. Evolution records feedback and creates private proposals; changing the source requires a separate, explicit apply decision. ## Open source and project ownership The project lives at [johnlindquist/mdflow](https://github.com/johnlindquist/mdflow) and is distributed under the [MIT license](https://github.com/johnlindquist/mdflow/blob/main/LICENSE). The CLI is free; engine subscriptions or provider usage can cost money. See [pricing and costs](https://mdflow.dev/pricing.md), [the documentation](https://mdflow.dev/docs/), and [contact and security reporting](https://mdflow.dev/contact/). The repository is the authoritative place to inspect the implementation, release history, license, and contribution guidance. --- # Contact and security — mdflow Canonical page: https://mdflow.dev/contact/ --- title: Contact and security — mdflow description: Report mdflow bugs through GitHub issues and security vulnerabilities privately through GitHub Security Advisories. url: https://mdflow.dev/contact/ --- # Contact and security mdflow's public project contact is its [GitHub repository](https://github.com/johnlindquist/mdflow). Use the channels below so a reproducible bug, documentation correction, or security report reaches the appropriate place. This site does not offer a support inbox, contact form, or hosted account dashboard. ## Bugs, questions, and documentation Search [existing GitHub issues](https://github.com/johnlindquist/mdflow/issues) before [opening an issue](https://github.com/johnlindquist/mdflow/issues/new). For a CLI problem, include the output of `md --version`, your operating system, the engine and its version, the command you ran, the observed result, and a minimal flow that reproduces the issue. Explain what you expected instead. Read the [contribution guide](https://github.com/johnlindquist/mdflow/blob/main/CONTRIBUTING.md) before proposing a change. Issues are public. Remove credentials, private prompts, repository contents, and personal information before posting logs or diagnostics. `md explain` can include the resolved prompt and arguments; do not paste that output without reviewing it. You can also use issues to report an inaccurate page or ask a question about the website's privacy behavior without including sensitive data. ## Private vulnerability reports Report vulnerabilities through [GitHub Security Advisories](https://github.com/johnlindquist/mdflow/security/advisories/new), not a public issue with exploit details. Include the affected version, reproduction steps, impact, and any suggested mitigation. The [security policy](https://github.com/johnlindquist/mdflow/blob/main/SECURITY.md) describes the acknowledgement target and supported-release policy; consult it for the current terms. Treat downloaded flows and executable eval or hook sidecars as code. For permission boundaries and provider processing, see [the docs](https://mdflow.dev/docs/) and [privacy](https://mdflow.dev/privacy/). These GitHub channels are project-maintenance channels, not a promise of a commercial support service. --- # Privacy — mdflow Canonical page: https://mdflow.dev/privacy/ --- title: Privacy — mdflow description: How the static documentation site differs from local CLI execution, browser storage, hosting requests, and external engine processing. url: https://mdflow.dev/privacy/ --- # Privacy ## This public documentation site mdflow.dev serves public documentation, source facts, and a client-side interactive demonstration. Workflows execute only through the local CLI; the website serves read-only documentation and source facts. The demonstration runs in your browser. The site's checked-in application code does not integrate an analytics SDK or an advertising tracker. That statement does not establish what infrastructure logs or platform-level services a hosting provider may operate. The site is hosted on Vercel. Like other web hosts, the hosting infrastructure receives request information such as an IP address, requested URL, and browser headers to deliver pages and operate the service. Retention and processing at that layer are governed by the provider's configuration and [Vercel's privacy policy](https://vercel.com/legal/privacy-policy); this repository does not establish a retention period or a guarantee that requests are never logged. ## Browser storage and external requests The homepage's optional interactive easter eggs store progress in your browser's local storage under `mdflow-eggs`, `mdflow-puzzle`, and `mdflow-golden`. A `mdflow-nudge` session-storage flag remembers whether a hint has been shown. This is local interface state, not a website account. Clear this site's browser storage to remove it; doing so resets that progress. The homepage serves its font files locally. Article pages request fonts from Google Fonts, which exposes the usual web-request information to Google; see [Google's privacy policy](https://policies.google.com/privacy). Following a GitHub, npm, or other external link sends you to that service under its own policies. No claim is made that those third parties share this site's practices. ## Running the local CLI The mdflow CLI is a separate local program. Flows may read files, resolve remote imports, run commands, invoke context providers, and launch the engine you configured. That engine may send prompts, selected context, and outputs to its provider according to your credentials, settings, and the provider's terms. A free CLI license does not make provider processing offline or private. Local flow logs, feedback, eval receipts, and evolution artifacts can contain sensitive project information. Review them before sharing; private local state is not the same thing as encrypted storage. Review untrusted flows and executable sidecars, limit credentials, and use a disposable environment when appropriate. Engine context isolation is not a host filesystem, network, environment, process, or credential sandbox. See the [security policy](https://github.com/johnlindquist/mdflow/blob/main/SECURITY.md) and [contact page](https://mdflow.dev/contact/) for reporting concerns. --- # Documentation and agent interfaces — mdflow Canonical page: https://mdflow.dev/docs/ --- title: Documentation and agent interfaces — mdflow description: Install mdflow, inspect the local CLI, read the public source-facts snapshot, and respect version contracts, operation effects, and separate consent. url: https://mdflow.dev/docs/ --- # Documentation and agent interfaces mdflow turns Markdown flows into repeatable local agent commands. Workflows execute only through the local CLI; the website serves read-only documentation and source facts. Use the installed `md` / `mdflow` CLI for running work. ## Install and inspect first Install the [official mdflow npm package](https://www.npmjs.com/package/mdflow) with `npm install -g mdflow`, or use `npx mdflow` to invoke the package. Install and authenticate the engine you intend to use separately. The current source facts identify mdflow **4.9.0**, with **pi** as the default engine; always ask your installed CLI for its version before assuming that it matches this documentation. mdflow runs on [Bun](https://bun.sh). The interactive launcher offers to install Bun if it is missing; in a non-interactive environment, install Bun first. Package installation and engine authentication are environment changes, not part of the read-only doctor query. ```bash md --version md doctor --json md roster --json ``` `md doctor --json` is a static, read-only readiness check: it does not execute a flow, load executable sidecars, expand imports, fetch URLs, or write files. Once local setup is approved, `npx mdflow init --yes` creates a deterministic starter roster without an engine invocation. Bare `md` opens the searchable Flow Workbench. `md init --guided` is different: it launches an engine-guided setup and needs separate approval. ## Local CLI protocol The [public CLI reference](https://mdflow.dev/docs/public-api.md) defines **Flow UX Protocol v1** (`protocolVersion: 1`). Check `md --version` and the protocol version in JSON responses. `md doctor --json` reports diagnostics and effect-labelled next actions; `md roster --json` enumerates flows; `md explain --json` resolves one invocation; and `md --events` executes a real run and streams NDJSON events. Event streaming is not a free preview. `md --json` is the separate single-result output mode, not the event stream. Use stable diagnostic codes, operation effects, and consent requirements rather than parsing terminal styling. ## Public source-facts interface The site's public read-only machine interface is **GET https://mdflow.dev/facts.json**. It returns `application/json`: a static snapshot of published CLI source facts, command descriptions, operation effects and consent labels, safety rules, agent prompt templates, and flag descriptions. No authentication, credentials, or request body are required. Reading it performs no mutation and launches no workflow. HEAD is available for HTTP metadata. ```bash curl --fail-with-body -H 'Accept: application/json' https://mdflow.dev/facts.json ``` The [OpenAPI 3.1 specification](https://mdflow.dev/openapi.json) describes only GET /facts.json. Its `info.version` is **1.0.0** (interface version 1). The response's `contract.contractVersion` is **1**, which versions the embedded CLI operation contract. The separate `versionBase` field identifies the CLI source version, currently **4.9.0**. The URL remains /facts.json. The schema covers every top-level field and derives nested command and operation shapes from the same source snapshot published at that URL. The response includes `X-API-Version: 1` and `Link` headers with `service-desc` and `service-doc` relations. Additive fields are allowed within v1; clients should tolerate fields they do not yet recognize. Breaking representation changes require a new URL and a new major OpenAPI `info.version`, rather than silently changing existing fields at /facts.json. Discover the specification through the [RFC 9727 API catalog](https://mdflow.dev/.well-known/api-catalog), served as `application/linkset+json`. Its `service-desc` link points to /openapi.json and `service-doc` points to this page. The snapshot changes when the website is built and published, and hosting caches may serve an earlier snapshot. It describes source capabilities, not your project's files, installed engines, readiness, or execution results. Use the local CLI to inspect those. ### HTTP errors and public hosting Site-handled JSON errors use [RFC 9457 Problem Details](https://www.rfc-editor.org/rfc/rfc9457) with `Content-Type: application/problem+json`. Responses include `type` (`about:blank` for standard HTTP problems), `title`, `status`, `detail`, and a `resolution` hint. An optional `instance` identifies the failed request when available. Use the HTTP status for control flow and allow additional problem fields. - **404 Not Found:** a requested JSON resource is missing. Check the resource URL against the catalog and specification. - **405 Method Not Allowed:** an unsupported method is used on the read-only facts resource. The `Allow` header is `GET, HEAD`; use GET to read the snapshot. There are no mutation operations. - **406 Not Acceptable:** the request excludes the available JSON representation. Send `Accept: application/json`. The site is publicly hosted on Vercel. Hosting receives ordinary request information (such as IP address, URL, and headers); see [privacy](https://mdflow.dev/privacy/) for infrastructure and third-party processing. Hosting or network failures can occur before site handling and need not use the problem format. Check status and Content-Type before parsing; treat this public, cacheable document as source documentation rather than live local state. Local CLI errors and consent rules are separately defined in the [CLI reference](https://mdflow.dev/docs/public-api.md). ## Costs, permissions, and proof Operations distinguish `FREE` (no engine invocation), `LOCAL_WRITE` (changes local state), and `ENGINE` (provider-backed work). `FREE` does not mean every operation is a sandbox: explanation and dry-run may resolve file, URL, or context-provider imports. Use doctor when you require strictly static inspection. Review flows and executable `.eval.ts` / `.hooks.ts` sidecars before allowing execution. A real flow run, eval run, evolution proposal, and source-changing apply each require separate consent. Preview eval cost with `md eval --plan`; preview evolution with `md evolve plan `. Proposals remain private and off-path until an explicit `md evolve apply `. Context isolation strips supported engine context, not host access or credentials. ## Reference library - [Complete CLI reference](https://mdflow.dev/docs/public-api.md): flags, configuration, JSON objects, event ordering, and error contracts. - [Project README](https://mdflow.dev/README.md): installation and workflow examples. - [Evolution specification](https://mdflow.dev/docs/evolve.md) and [illustrated deep dive](https://mdflow.dev/evolve-deep-dive/): feedback, proof, review, apply, and rollback. - [Security policy](https://mdflow.dev/SECURITY.md): execution boundaries and private reporting. - [Agent skill](https://mdflow.dev/skills/mdflow/SKILL.md): the repository's actual flow-authoring instructions. Install with `npx skills add johnlindquist/mdflow`; inspect the [SHA-256 index](https://mdflow.dev/.well-known/agent-skills/index.json) when comparing downloaded bytes. - [llms.txt](https://mdflow.dev/llms.txt) is the concise resource map; [llms-full.txt](https://mdflow.dev/llms-full.txt) includes the source documentation for offline reading. ## Command inventory This list is generated from the same facts used by the homepage. Use `md help ` for the installed version's command-specific help. - `md doctor [--json]` — Inspect project readiness and safe next actions without execution. - `md init [--guided] [--engine ] [--yes] [--agents] [--print-guide]` — Initialize a project flow roster. - `md create [intent] [--global] [--dry-run]` — Create a flow and a fail-closed draft eval suite. - `md capture` — Print the guide for capturing the current agent conversation as a flow. - `md explain [--json]` — Resolve one flow without launching its engine. - `md render [--json|--out |--open]` — Inspect, write, or open a rendered flow explanation. - `md hooks add|list|remove [event...]` — Manage executable lifecycle hook sidecars. - `md eval [--plan] [--yes] [--json]` — Plan or run a flow's behavioral eval suite. - `md feedback | list|show|distill|dismiss|reopen|forget` — Manage durable evolution evidence. - `md complain ` — Compatibility alias for feedback. - `md evolve plan|status|propose|show|apply|reject|retry|rollback|history|prune` — Manage proposal-first flow evolution. - `md install [--global]` — Install one flow into the registry. - `md remove ` — Remove an installed registry flow. - `md list [--project|--global]` — List installed registry flows. - `md roster --json | roster sync [--check] [--agents] [--json]` — Inspect flows or synchronize the managed operator card. - `md catalog [--json]` — Machine-readable catalog of every discoverable flow (project, global, installed, PATH). - `md setup` — Configure shell integration. - `md logs` — Show flow log locations. - `md help [command]` — Show CLI or command help. --- # Source: Project README Original: https://github.com/johnlindquist/mdflow/blob/main/README.md Published copy: https://mdflow.dev/README.md Relative links in this source use the original repository location. # mdflow ```bash review.md # Runs on your resolved engine (default: pi) review.claude.md # Pin an engine in the filename git diff | explain.md # Pipe through any command ``` **A Git-native control plane for repeatable agent work.** Define each job as markdown, run it on the CLI engine you already use, inspect its inputs, and gate prompt revisions with behavioral evals. ```bash npx mdflow init ``` One command safely creates a starter `./flows` roster and `.mdflow.yaml` with zero engine invocations; repeat runs leave an existing roster untouched. Then bare `md` always opens one searchable Flow Workbench containing project flows, every globally installed flow, and runnable Markdown flows found directly on `PATH`, with clear `PROJECT`, `GLOBAL`, `INSTALLED`, and `PATH` provenance. If the project has no owned roster, global and PATH flows remain immediately runnable and a searchable **Set up project flows…** row opens guided setup, a deterministic starter roster, or the printable setup guide without leaving the Workbench. Want the repo-tailored setup conversation directly? Run `md init --guided` (or `md init --print-guide` to copy the same guide into your own agent session). Prefer flows as the primary way agents work in your repo? Opt in with `md roster sync --agents`: it maintains one marker-managed mdflow block in `AGENTS.md` and `CLAUDE.md` so coding agents discover the roster and hand matching tasks off to flows, and it never touches your text outside the markers. Every guidance write requires that explicit `--agents` consent — plain `md roster sync` is README-only and merely reports drift. Guided setup asks this as an explicit question. ## For coding agents Use the CLI as the source of truth instead of reconstructing project state from long prose: ```bash command -v md || npm install -g mdflow md doctor --json # FREE, static, read-only project diagnosis npx mdflow init --yes # LOCAL WRITE, deterministic starter roster md doctor --json md explain flows/.md --json md eval flows/.md --plan # FREE; a real eval needs separate approval ``` `flows/README.md` contains a managed operator card; update only that marked block with `md roster sync`. Eval and hook sidecars are executable local code. Registry install adds one flow, not trusted sidecars. Engine context isolation is not a host filesystem, network, process, environment, or credential sandbox. --- ## The agent control plane in your repo **Git-native agent workflows.** One file per job. Any engine. Evals that guard declared behavior. Feedback can drive reviewable, regression-gated prompt proposals. - **`./flows` is your repo's agent roster.** One markdown agent per job: code review, release notes, issue triage. Diffable in PRs, checked with `md eval`, readable by every teammate, human or AI. Start one with `npx mdflow init`; the installable skill (`npx skills add johnlindquist/mdflow`) teaches your coding agent to build and maintain it. - **Engines are environment, not filename ceremony.** Bare `review.md` runs on the resolved engine: `--engine` flag > `MDFLOW_ENGINE` env > filename > frontmatter `engine:` > config `engine:` > built-in default (**pi**). Implicit choices print a dim `review.md → pi (engine: default)` line. Inspectable, never magic. Files with no frontmatter and no explicit engine are documents: `md README.md` prints instead of executing. The frontmatter key is now `engine:` (`tool:`/`_tool:` still work, with a warning). - **pi is the default engine**, runs without ambient extensions, skills, or context files, and bridges your Codex CLI login automatically. - **Engine context isolation is the default, where supported.** Flows run with the engine's ambient context stripped using its own verified flags — claude `--safe-mode --no-session-persistence`, codex `--ignore-user-config --ephemeral --skip-git-repo-check -c project_doc_max_bytes=0`, gemini `--extensions none`, copilot `--no-custom-instructions --disable-builtin-mcps`, opencode `--pure`, pi its context-isolation flags. Skills/MCP/context a flow needs are declared explicitly in frontmatter; `_isolated: false` opts back into ambient. Engines with no controls (droid, cursor-agent, agy) run ambient and warn only on an explicit `_isolated: true` — never pretending. This does not sandbox the host filesystem, network, environment, or inline shell commands; those remain explicit capabilities of the flow. - **System prompt as a first-class key:** `_system-prompt:` replaces the engine's system prompt, `_append-system-prompt:` appends (string or list). Translated per engine (claude/pi flags, codex `model_instructions_file` / `developer_instructions` config, gemini `GEMINI_SYSTEM_MD`). Engines with no mechanism fail the run instead of silently dropping your prompt. Interactive specialists that should wait for a task use `_task: ""` and a body of exactly `{{ _task }}`; identity/rules belong in the two instruction keys. Static `User task:` wrappers or body-level context would become an immediately submitted first turn. - **New engines:** `cursor-agent` and `agy` (Google Antigravity, the gemini CLI successor; the old gemini adapter remains for Code Assist Standard/Enterprise). - **Evals:** `md eval flows/review.md` runs `flows/review.eval.ts`. Behavioral cases in isolated temporary workspaces, cost printed before running, results in a trust ledger. *If a guardrail isn't covered by an eval, it's a wish.* - **Evolution is proposal-first:** `md feedback flows/review.md "missed the race condition"` records durable evidence. `md evolve plan` previews proof, capabilities, writes, and bounded invocation cost for free; `md evolve propose` drafts and evaluates private off-path snapshots. The source stays byte-identical until a separate `md evolve apply `. mdflow says “verified improvement” only when a feedback-linked case fails on current and passes on the proposal; otherwise a green candidate is merely “regression-safe.” Legacy `evolve: auto` now means queued proposal-only work, never unattended application. See [`docs/evolve.md`](docs/evolve.md) for the normative change-with-proof protocol, `docs/V3-FLOWS.md` for the historical v3 design record, [`docs/public-api.md`](docs/public-api.md) for the stable CLI contract, and [`GUIDE-NEW-FEATURES.md`](GUIDE-NEW-FEATURES.md) for workflows (`_steps`), structured outputs (`_output`), context providers (`@git:diff`), the flow registry, `--json` mode, and run telemetry. Security-sensitive behavior is documented in [`SECURITY.md`](SECURITY.md). Contributions are welcome; see [`CONTRIBUTING.md`](CONTRIBUTING.md). --- ## 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. ```markdown # review.claude.md --- model: opus --- Review this code for bugs and suggest improvements. @./src/**/*.ts ``` ```bash review.claude.md # Runs: claude --model opus review.claude.md --verbose # Pass extra flags ``` --- ## How It Works ### 1. Filename → Command Name your file `task.COMMAND.md` and the command is inferred: ```bash task.claude.md # Runs claude task.codex.md # Runs codex task.copilot.md # Runs copilot (print mode by default) task.agy.md # Runs agy (Google Antigravity, the gemini CLI successor) task.gemini.md # Runs gemini (Code Assist Standard/Enterprise only — sunset for individuals) ``` ### 2. Frontmatter → CLI Flags Every YAML key becomes a CLI flag passed to the command: ```yaml --- 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 ```bash # Pipe input git diff | mdflow review.claude.md # Chain agents mdflow plan.claude.md | mdflow implement.codex.md ``` --- ## Installation Start through `npx` to bootstrap a flow roster in any repo: ```bash npx mdflow init ``` Keep it installed for daily runs: ```bash npm install -g mdflow # or bun install && bun link ``` mdflow runs on [Bun](https://bun.sh). If Bun is missing, the interactive launcher offers to install it; non-interactive environments must install Bun first. ## Quick Start ```bash # Safely scaffold a starter roster (`--guided` for a repo-tailored session) npx mdflow init # Open the Flow Workbench md # Or create and run a project flow directly md create "Review staged changes for bugs" md review-staged-changes-for-bugs # Create a personal flow you can run from any project md create "Turn meeting notes into an action plan" --global md turn-meeting-notes-into-an-action-plan # Preview without spending an engine invocation md review-staged-changes-for-bugs --_dry-run # Override the engine explicitly mdflow task.md --engine claude MDFLOW_ENGINE=codex mdflow task.md # One-shot ad-hoc mode (no file required) md.claude "Summarize: !`git diff --staged`" md.i.codex "Help me debug this test failure" # Pass additional flags to the command mdflow task.claude.md --verbose --debug ``` > **Note:** Both `mdflow` and `md` commands are available. > > For the full command and frontmatter contract, see [`docs/public-api.md`](docs/public-api.md). ### The Flow Workbench Run bare `md` for the everyday surface. It works in a new project with zero flows and in an established project with a full roster: - Search project, global, installed, and PATH flows together; every row shows its provenance. - Press `Enter` to run or `Tab`/`→` to open dry-run, edit, hooks, feedback, and evolution actions. - Press `Ctrl+O` to turn a plain-language outcome into a new scoped flow. - In a project with no local roster, search for `setup` or select **Set up project flows…**. Every action displays its exact shell equivalent and whether it is free, invokes an engine, or writes locally. The Workbench never auto-applies a prompt revision. `a` and `r` open a dedicated `LOCAL WRITE` confirmation screen; `Enter`/`c` confirms the displayed command and `Esc` returns without writing. --- ## Engine Resolution Engines are resolved by a ladder, most explicit first: 1. **CLI flag**: `--engine claude` (deprecated aliases: `--_command`/`-_c`, `--tool`) 2. **Environment variable**: `MDFLOW_ENGINE` 3. **Filename pattern**: `task.claude.md` → `claude` (must name a real engine) 4. **Frontmatter**: `engine: claude` (deprecated aliases `tool:`/`_tool:` warn) 5. **Config** `engine:` (project config beats `~/.mdflow/config.yaml`) 6. **Built-in default**: `pi` Resolution never fails — the default always applies. Implicit choices print a dim explanation line on stderr. A file with no frontmatter and no explicit engine is a document: `md README.md` prints it instead of executing it. Bundled engines: `claude`, `codex`, `copilot`, `gemini`, `droid`, `opencode`, `pi` (default), `cursor-agent`, `agy` (Google Antigravity). --- ## 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. ### `--engine` Override the engine for any markdown file (deprecated aliases: `--_command`, `-_c`, `--tool` — they still work but warn): ```bash # Run a generic .md file on any engine mdflow task.md --engine claude # Override the filename-inferred engine mdflow task.claude.md --engine gemini # Runs gemini, not claude ``` ### `_varname` Template Variables Frontmatter fields starting with `_` (except internal keys like `_interactive`, `_cwd`, `_subcommand`) define template variables: ```yaml --- _feature_name: Authentication # Default value _target_dir: src/features # Default value --- Build {{ _feature_name }} in {{ _target_dir }}. ``` ```bash # 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: ```yaml --- print: true --- {% if _verbose == "yes" %}Detailed analysis:{% endif %} Review this code: {{ _target }} ``` ```bash mdflow review.claude.md --_verbose yes --_target "./src" ``` ### Positional Arguments as Template Variables CLI positional arguments are available as `{{ _1 }}`, `{{ _2 }}`, etc.: ```yaml --- print: true --- Translate "{{ _1 }}" to {{ _2 }}. ``` ```bash mdflow translate.claude.md "hello world" "French" # → Translate "hello world" to French. ``` Use `{{ _args }}` to get all positional args as a numbered list: ```yaml --- print: true --- Process these items: {{ _args }} ``` ```bash 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: ```yaml --- model: haiku --- Summarize this: {{ _stdin }} ``` ```bash cat README.md | md summarize.claude.md ``` ### Interactive Form Inputs Use `_inputs` to define typed interactive prompts with validation: ```yaml --- 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 options - `number` - Numeric input - `confirm` - Yes/no boolean - `password` - 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 | | `_hooks` | boolean/string | Lifecycle hooks file: unset = auto-discover `.hooks.ts`, `false` = disable, path = shared hooks file (see Lifecycle Hooks) | | `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: ```yaml --- 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 value` - `key: true` → `--key` - `key: false` → (omitted) - `key: [a, b]` → `--key a --key b` --- ## Lifecycle Hooks A flow's hooks live in a TypeScript file **named after the flow** — that's the whole wiring: ``` review.codex.md # the flow review.codex.hooks.ts # its hooks — discovered and wired automatically ``` Scaffold one (or pick events interactively by omitting them): ```bash md hooks add review.codex.md stop userPromptSubmit md hooks list review.codex.md md hooks remove review.codex.md stop ``` The generated file is an executable, dependency-free Bun program exporting handlers keyed by canonical event names: ```ts #!/usr/bin/env bun const handlers: Record = { userPromptSubmit: async (payload) => { // return a string to inject context; // return { decision: "block", reason: "…" } to reject the prompt }, stop: async (payload) => { // return { decision: "block", reason: "…" } to force the turn to continue }, }; ``` On every run, mdflow reads which events the file handles (from its text — inspection never executes it) and translates them into the engine's native hook mechanism. **codex** and **claude** are supported today (the same hooks file works unchanged on both — its dispatcher normalizes event names): - **codex** — an inline `-c hooks={…}` override plus a prepared, hooks-free codex home under `~/.mdflow/`, so your own codex config and credentials are never modified. - **claude** — an inline `--settings` blob plus `--setting-sources ""` to exclude your ambient settings hooks. Claude's `--safe-mode` disables injected hooks, so a hooked claude run drops it and prints a one-line disclosure that CLAUDE.md/skills/plugins/MCP are no longer isolated for that run (your ambient settings hooks still are). Debug a hook standalone by piping a payload to it: ```bash echo '{"hook_event_name":"Stop"}' | ./review.codex.hooks.ts ``` Events: `sessionStart`, `userPromptSubmit`, `preToolUse`, `postToolUse`, `permissionRequest`, `preCompact`, `postCompact`, `subagentStart`, `subagentStop`, `stop`, `sessionEnd`. In print mode, codex fires `sessionStart`/`userPromptSubmit`/`preToolUse`/`postToolUse`/`stop`, and claude additionally fires `sessionEnd`; the rest are registered but scenario-dependent. Once a run is underway, a hook that crashes or times out fails open (the engine continues) — except that the scaffolded dispatcher fails **closed** for guard events (`userPromptSubmit`, `preToolUse`, `permissionRequest`): if one of those handlers throws, mdflow emits the engine's block/deny response rather than letting the guarded action through. Discovery problems are the opposite: a hooks file that exists but can't be used (missing, uninspectable, escaping path) fails the run loudly, because silently dropping declared hooks would change the flow. Consent and containment rules: - **Inspection never executes.** `md explain`, `md hooks list`, dry runs, and the Workbench read the handlers map from the file's text; only a real run may execute the hook program (it's about to run anyway). - **Hooks run isolated.** Hooked runs exclude your ambient hooks so mdflow's injected hooks are the only ones that fire — on codex via a prepared, hooks-free codex home (its trust bypass can never enable hooks you haven't reviewed), on claude via `--setting-sources ""`. `_isolated: false` combined with a hooks file is an error on both. - **Paths are contained.** A flow's `_hooks:` may only point inside the flow's own directory, and remote flows can't declare `_hooks` paths at all. Only `--_hooks` typed on the command line escapes containment. - **`md install` fetches only the flow markdown** — hook files are never downloaded from a registry. Control: `_hooks: false` disables discovery; `_hooks: ./shared.hooks.ts` points several flows at one file (within the flow's directory); `--_hooks ` overrides from the CLI. Engines without a verified hook mechanism fail a run whose hooks file exists — disable with `_hooks: false` or switch engines. --- ## 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) ```bash 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: ```bash 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 "..." chat.i.md # Default engine, interactive ("i" is never an engine name) ``` Or use `_interactive` (or `_i`) in frontmatter: ```yaml --- _interactive: true # or _interactive: (empty), or _i: model: opus --- Review this code with me interactively. ``` Or use CLI flags: ```bash mdflow task.claude.md --_interactive # Enable interactive mode mdflow task.claude.md -_i # Short form ``` --- ## Configuration Layers mdflow resolves configuration in this order (later entries override earlier ones): 1. Built-in command defaults 2. Global config (`~/.mdflow/config.yaml`) 3. Project config at git root (`mdflow.config.yaml`, `.mdflow.yaml`, `.mdflow.json`) 4. Project config at current working directory (`mdflow.config.yaml`, `.mdflow.yaml`, `.mdflow.json`) 5. Agent frontmatter 6. CLI passthrough flags Set global defaults per command in `~/.mdflow/config.yaml`: ```yaml commands: claude: model: sonnet # Default model for claude copilot: silent: true # Always use --silent for copilot ``` Set project defaults in your repository root: ```yaml # mdflow.config.yaml commands: claude: model: opus ``` **Built-in defaults:** All commands default to print mode with tool-specific defaults. --- ## Examples ### Claude with MCP Server ```markdown # db.claude.md --- model: opus mcp-config: ./postgres-mcp.json dangerously-skip-permissions: true --- Analyze the database schema and suggest optimizations. ``` ### Gemini YOLO Mode ```markdown # refactor.gemini.md --- model: gemini-3-pro-preview yolo: true --- Refactor the authentication module to use async/await. ``` ### Codex with Sandbox ```markdown # analyze.codex.md --- model: o3 sandbox: workspace-write full-auto: true --- Analyze this codebase and suggest improvements. ``` ### Copilot (no frontmatter needed!) ```markdown # task.copilot.md Explain this code. ``` This runs: `copilot --silent --prompt "Explain this code."` (print mode) For interactive mode, use `.i.` in the filename: ```markdown # task.i.copilot.md Explain this code. ``` This runs: `copilot --silent --interactive "Explain this code."` ### Template Variables ```markdown # create-feature.claude.md --- _feature_name: "" _target_dir: src/features model: sonnet --- Create a new feature called "{{ _feature_name }}" in {{ _target_dir }}. ``` ```bash mdflow create-feature.claude.md --_feature_name "Auth" ``` ### Environment Variables Use `_env` (underscore prefix) to set environment variables for the command: ```markdown # 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: ```markdown --- 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: ```markdown Review all TypeScript files in src: @./src/**/*.ts ``` Glob imports: - Respect `.gitignore` automatically - Include common exclusions (`node_modules`, `.git`, etc.) - Are limited to ~100,000 tokens by default - Set `MDFLOW_FORCE_CONTEXT=1` to override the token limit Files are formatted as XML with path attributes: ```xml ...file content... ...file content... ``` ### Line Range Imports Extract specific lines from a file: ```markdown @./src/api.ts:10-50 ``` This imports only lines 10-50 from the file. ### Symbol Extraction Extract specific TypeScript/JavaScript symbols (interfaces, types, functions, classes, etc.): ```markdown @./src/types.ts#UserInterface @./src/api.ts#fetchUser ``` Supported 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: ```markdown Current branch: !`git branch --show-current` Recent commits: !`git log --oneline -5` Based on the above, suggest what to work on next. ``` > **Security:** template variables are substituted into the command string > **unescaped** and run via your shell, just like a shell script. When a > variable can carry untrusted input — piped `{{ _stdin }}`, positional > `{{ _1 }}`/`{{ _args }}`, or `--_var` flags — pass it through the > `shell_escape` filter (alias `q`) so shell metacharacters can't execute: > > ```markdown > Matches: !`grep {{ _1 | q }} server.log` > ``` ### URL Imports Fetch content from URLs (markdown and JSON only): ```markdown @https://raw.githubusercontent.com/user/repo/main/README.md ``` **Caching:** Remote URLs are cached locally at `~/.mdflow/cache/` with a 1-hour TTL. Use `--_no-cache` to force a fresh fetch: ```bash mdflow agent.claude.md --_no-cache ``` **URL policy controls:** Restrict allowed URL imports with environment variables: ```bash export MDFLOW_IMPORT_URL_ALLOWLIST="raw.githubusercontent.com,docs.example.com" export MDFLOW_IMPORT_URL_BLOCKLIST="*.internal.example.com" ``` Both variables accept comma-separated or newline-separated host rules. `MDFLOW_URL_ALLOWLIST` and `MDFLOW_URL_BLOCKLIST` are legacy aliases. --- ## Environment Variables mdflow automatically loads `.env` files from the markdown file's directory. ### Loading Order Files are loaded in order (later files override earlier): 1. `.env` - Base environment 2. `.env.local` - Local overrides (not committed) 3. `.env.development` / `.env.production` - Environment-specific 4. `.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.md ``` Environment variables are available: - In command inlines: `` !`echo $API_KEY` `` - In the spawned command's environment --- ## CLI Options ``` Usage: md [flags for the command] md # Open the Flow Workbench md [options] md.COMMAND "prompt" [flags] # Ad-hoc execution (no file needed) Commands: md init [--guided] [-y] [--agents] [--print-guide] Safely scaffold a starter flow roster (--guided tailors it with an installed agent CLI; --agents adds AGENTS.md/CLAUDE.md guidance; --print-guide prints the setup prompt, free) md create "" Create a project flow (--global for a personal flow) md capture Print the guide an in-session agent (Claude Code, Codex, ...) follows to capture the current conversation as a flow (free) md doctor [--json] Inspect project readiness + safe next actions (free) md explain Show resolved config without executing md render Render prompt + full config as a shareable HTML page (free) md hooks add|list|remove [event…] Manage the flow's lifecycle hooks file md eval [--plan] Run or cost-preview the flow's eval suite md eval add|list|remove|coverage Scaffold suites, verdicts, and the coverage ratchet md feedback "msg" Record durable evolution evidence (free) md complain "msg" Alias for md feedback md evolve plan|propose Plan for free or create a private proposal md evolve show|apply Review or explicitly apply a proposal md evolve history [flow.md] List proposal history (use evolve --help for more) md install Install a flow into the registry (--global for user scope) md remove Remove an installed registry flow md list List installed registry flows md roster --json Machine-readable roster of project/global/registry flows md roster sync [--check] [--agents] Update or check the managed operator card (--agents opts into AGENTS.md/CLAUDE.md guidance) md setup Configure shell (PATH, aliases) md logs Show agent log directory md help Show this help Ad-hoc execution (one-shot mode): md.claude "What is 2+2?" # Quick prompt to Claude md.codex "Write a function" # Quick prompt to Codex md.copilot "Help me debug" # Quick prompt to Copilot md.droid "Build an app" # Quick prompt to Droid md.opencode "Refactor this" # Quick prompt to OpenCode md.i.claude "Start a chat" # Interactive mode md.claude "Explain: @error.log" --model opus # With @imports and flags Create flows: md create "Review staged changes for bugs" # project: ./flows/ md create "Turn notes into an action plan" --global # personal: ~/.mdflow/ md Then browse, run, edit, or improve them Engine resolution (most explicit wins): 1. --engine flag (deprecated aliases: --_command/-_c, --tool) 2. MDFLOW_ENGINE environment variable 3. Filename pattern (e.g., task.claude.md → claude; must name a real engine) 4. Frontmatter key (engine: claude; deprecated: tool:/_tool:) 5. Config engine: (project .mdflow.yaml beats ~/.mdflow/config.yaml) 6. Built-in default: pi A file with no frontmatter and no explicit engine is printed as a document. Agent file discovery (in priority order): 1. Explicit path: md ./path/to/agent.md 2. Project flows: ./flows/ 3. Legacy project: ./.mdflow/ 4. Personal flows: ~/.mdflow/ 5. $PATH directories 6. Current directory: ./ All non-system frontmatter keys are passed as CLI flags to the command. Global defaults can be set in ~/.mdflow/config.yaml Remote execution: md supports running agents from URLs (npx-style). On first use, you'll be prompted to trust the domain. Trusted domains are stored in ~/.mdflow/known_hosts md-specific flags (consumed, not passed to command): --engine Specify the engine to run (deprecated aliases: --_command/-_c, --tool) --_dry-run Show command/prompt plan; skip engine and inline commands --_edit Open resolved prompt in $EDITOR before execution --_trust Skip trust prompt for remote URLs (TOFU bypass) --_no-cache Force fresh fetch for remote URLs (bypass cache) --raw Output raw markdown without rendering (for piping) --_context Show context tree and exit (no execution) --_quiet Skip context dashboard display before execution --_no-menu Disable post-run action menu (for scripting/piping) --json Emit a single JSON result object and disable interactive UI Examples: md task.claude.md -p "print mode" md task.claude.md --model opus --verbose md commit.agy.md md task.md # engine via the ladder (default: pi) md task.md --engine claude md eval task.md # run the flow's eval suite md task.claude.md --_dry-run # Preview without executing md https://example.com/agent.claude.md # Remote execution md https://example.com/agent.claude.md --_trust # Skip trust prompt Without arguments: md Open the Flow Workbench: browse, create, run, and improve flows ``` ### Environment Variables | Variable | Description | | ---------- | ------------- | | `MDFLOW_FORCE_CONTEXT` | Set to `1` to disable the 100k token limit for glob imports | | `MDFLOW_IMPORT_URL_ALLOWLIST` | Comma/newline-separated allowlist rules for URL imports | | `MDFLOW_IMPORT_URL_BLOCKLIST` | Comma/newline-separated blocklist rules for URL imports | | `MDFLOW_URL_ALLOWLIST` | Legacy alias for `MDFLOW_IMPORT_URL_ALLOWLIST` | | `MDFLOW_URL_BLOCKLIST` | Legacy alias for `MDFLOW_IMPORT_URL_BLOCKLIST` | | `MDFLOW_FETCH_TIMEOUT` | HTTP fetch timeout in milliseconds (default: `10000`) | | `MDFLOW_COMMAND_TIMEOUT` | Inline command timeout in milliseconds (default: `30000`) | | `MDFLOW_AGENT_TIMEOUT` | Agent process timeout in milliseconds (default: `0` = disabled) | | `NODE_ENV` | Controls which `.env.[NODE_ENV]` file is loaded (default: `development`) | --- ## Shell Setup Make `.md` files directly executable: ```bash mdflow setup # One-time setup ``` Then run agents directly: ```bash task.claude.md # Just type the filename task.claude.md --verbose # With passthrough args ``` ### Manual Setup (zsh) Add to `~/.zshrc`: ```bash alias -s md='mdflow' export PATH="$HOME/agents:$PATH" # Your agent library ``` --- ## Installing Shared Flows Install versioned flows from a URL or GitHub into the registry: ```bash md install gh:myorg/agents/code-review.claude.md@v1.2 # project scope (./.mdflow/registry/) md install https://example.com/agents/review.claude.md --global # user scope (~/.mdflow/registry/) md list # list installed flows md remove review.claude.md # remove one ``` Every install is pinned in `.mdflow/mdflow.lock.json` (source, resolved ref, sha256) — commit it for reproducible CI runs. --- ## Building 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 helper ``` ```bash export PATH="$HOME/agents:$PATH" ``` Now use them from anywhere: ```bash review.claude.md # Review current directory commit.gemini.md "add auth" # Generate commit message git diff | review.claude.md # Review staged changes ``` --- ## Rich Output Rendering By default, LLM output is rendered with syntax highlighting and visual markdown structure (headers, code blocks, etc.). This uses `marked-terminal` for beautiful terminal output. To bypass rendering (e.g., for piping to other commands): ```bash md task.claude.md --raw | jq . ``` --- ## Context Dashboard Before execution, md shows a pre-flight dashboard with your context tree and token estimates: ``` ┌─ Pre-Flight ──────────────────────────────────────────────────┐ │ 📄 review.claude.md 1.2 KB │ │ ├── 📁 @./src/**/*.ts (12 files) 24.5 KB │ │ └── 📄 @./README.md 3.1 KB │ │ │ │ Total: 28.8 KB (~7,200 tokens) │ └───────────────────────────────────────────────────────────────┘ ``` Use `--_quiet` to skip the dashboard, or `--_context` to show it and exit without executing. --- ## The `md explain` Command Inspect what an agent will do without running it: ```bash md explain review.claude.md ``` Shows: - Resolved command and source (filename, flag, etc.) - Final flags after config merging (built-in → global → project → frontmatter) - Expanded prompt preview with token count - Trust status for remote URLs - Environment variables that will be set --- ## Edit Before Execute Use `--_edit` to open the fully resolved prompt in your `$EDITOR` before execution: ```bash md task.claude.md --_edit ``` This lets you review and tweak the final prompt (after template substitution and import expansion) before sending it to the LLM. --- ## Notes - On typed failures, mdflow emits a stable error code prefix like `[CONFIG_FILE_PARSE_FAILED]`. - See [`docs/public-api.md#error-codes`](docs/public-api.md#error-codes) for the full error-code catalog. - If no frontmatter is present, the file is printed as-is (unless command inferred from filename) - Template system uses [LiquidJS](https://liquidjs.com/) - supports conditionals, loops, and filters - Logs are always written to `~/.mdflow/logs//` for debugging - Use `md logs` to show the log directory - Piped input is available as `{{ _stdin }}` template variable - Template variables use `_` prefix: `_name` in frontmatter → `{{ _name }}` in body → `--_name` CLI flag - Remote URLs are cached at `~/.mdflow/cache/` with 1-hour TTL (use `--_no-cache` to bypass) - Imports inside code blocks (``` or `) are ignored by the parser - Interactive file picker sorts by frecency (frequency + recency) for quick access to common agents --- # Source: Public CLI reference Original: https://github.com/johnlindquist/mdflow/blob/main/docs/public-api.md Published copy: https://mdflow.dev/docs/public-api.md Relative links in this source use the original repository location. # mdflow public API reference This document defines the stable user-facing API for `mdflow` CLI usage. ## CLI command contract Run `mdflow` or `md` with one of these forms: ```bash md # interactive Flow Workbench md [flags...] md [options...] md. "prompt" [flags...] # ad-hoc, no file required ``` Supported subcommands: | Command | Description | | --- | --- | | `md` | Always open one searchable Flow Workbench containing valid project flows, every globally installed flow, and runnable Markdown flows found directly on `PATH`. Rows show `PROJECT`, `GLOBAL`, `INSTALLED`, and `PATH` provenance; nested registry entries come from registry lockfiles rather than broad traversal of private `~/.mdflow` runtime state, while each PATH directory is scanned only at its top level and ordinary Markdown documents are excluded. When the project has no owned roster, global and PATH flows remain immediately runnable and a searchable **Set up project flows…** action offers guided setup, a deterministic starter roster, or the printable setup guide inside the Workbench. Browse/filter with a Markdown and lifecycle preview; run, dry-run, edit, create, record feedback, or enter the proposal-first evolution path. Every action shows its shell equivalent and `FREE`, `ENGINE`, or `LOCAL WRITE` effect before execution. | | `md init [--guided] [--engine ] [--yes] [--agents] [--print-guide]` | Safely scaffold a starter flow roster with zero engine invocations. Plain init is a no-op when `flows/` already has a roster. `--guided` launches an installed agent CLI with the bundled setup guide for a repo-tailored session; an explicit `--engine` preserves that guided behavior unless `--yes` is also present. `--agents` also opts the project into flows-first agent guidance (same write as `md roster sync --agents`). `--print-guide` prints the guided-setup prompt to stdout for pasting into any agent harness (`FREE`, headless-safe, no engine launch). | | `md create ""` | Create a stable-identity project flow at `flows/.md` from plain-language intent. Pass `--global` to create a personal, user-scoped flow at `~/.mdflow/.md` that is available from any project. Uses create-only writes and never overwrites an existing flow. With no intent in a TTY, asks one question. | | `md capture` | Print the conversation-capture guide to stdout (`FREE` — no engine call, no reads, no writes). Designed to be run from inside an agent session (Claude Code, Codex, …): the agent reads the printed guide and follows it to distill the current conversation into a reusable flow — interviewing the user about what to keep, converting commands run during the session into `` !`cmd` `` context injections and discussed files into `@` imports, generalizing per-run specifics into template variables, and verifying only with free invocations. | | `md doctor [--json]` | FREE, static, read-only project diagnosis: installed engines, source capabilities, static hook/eval state, compatibility, stable diagnostic codes, and effect-labelled next actions. It executes no engine, suite, hook, inline command, fence, URL, or context provider and writes no files. | | `md explain [--json]` | Print resolved config and prompt without execution (free — no engine call). `--json` emits the Flow UX Protocol v1 explanation object (see "Machine-facing Flow UX protocol"). | | `md eval [--plan] [--yes] [--filter ] [--json]` | Preview or run the flow's executable colocated eval suite (`.eval.ts`). Cost includes repetitions and is printed before consent. | | `md feedback ""` | Record durable, private evidence with a stable ID (free). `list`, `show`, `distill`, `dismiss`, `reopen`, and explicit permanent `forget --yes` manage its lifecycle/privacy. | | `md complain ...` | Compatibility alias for `md feedback`. | | `md evolve plan\|status\|propose [--yes] [--engine ] [--json\|--events]` | Plan for free or create a private, capability-checked, off-path proposal. Source remains unchanged. | | `md evolve show\|review\|apply\|reject\|retry\|rollback ` | Inspect the receipt/diff, make an explicit decision, retry, or perform hash-guarded apply/rollback. | | `md evolve history [flow.md]` | List durable evolution runs. | | `md evolve prune [--days ] [--yes]` | Delete eligible old private proposal/job data while retaining applied lineage. | | `md install ` | Install a flow from a URL or GitHub shorthand into the registry (project scope by default; `--global` for user scope). Writes `.mdflow/mdflow.lock.json`. | | `md remove ` | Remove an installed registry flow. | | `md list [--project\|--global]` | List installed registry flows. | | `md roster --json` | Machine-readable enumeration of project (`/flows/`), global (`~/.mdflow/`), and registry (`.mdflow/registry/`) flows as a single Flow UX Protocol v1 JSON object. Documents (no frontmatter, no engine marker) are excluded. Always exits 0; soft failures land in `warnings`. | | `md roster sync [--check] [--agents] [--json]` | Synchronize only mdflow's marked operator-card block in `flows/README.md`, preserving all text outside the markers. Sync is `LOCAL WRITE`; `--check` is `FREE`, never writes, and exits 1 when stale or invalid. `--agents` is the explicit flows-first consent: it creates or refreshes one marker-managed mdflow block in `AGENTS.md` and `CLAUDE.md` at the project root. Every guidance write requires `--agents`; plain sync is README-only and reports guidance drift without writing. The whole sync is one fail-closed unit: if any managed surface is invalid, nothing is written to any of them. | | `md --version` | Print the bare mdflow version string (capability handshake for machine callers). | | `md setup` | Configure shell integration. | | `md logs` | Show log directory and per-agent logs. | | `md help` | Print CLI help. | Flows can set `evolve.mode` to `off`, `observe`, `suggest`, `propose`, or the reserved `apply` tier. `propose` prints a content-current plan and queues private background work after explicit actionable feedback; it never applies source. Legacy `evolve: auto` maps to `propose`. Quick reruns are low-confidence observations only. See [`evolve.md`](evolve.md) for the full policy, evidence state machine, receipts, and security boundary. Eval suites export a statically resolvable default array (directly or through a top-level `const`). This lets `--plan` derive names, evidence links, repetitions, quorum, and cost without importing executable suite code. After consent, runtime shape must match the announced static plan before any flow invocation starts. ## Operation effects and consent Machine-facing actions use three stable effect labels: - `FREE`: static/read-only inspection or planning; no engine invocation. Some dry-runs may still resolve file, URL, or context-provider imports, so use `md doctor --json` when the requirement is strictly no execution or fetch. - `LOCAL_WRITE`: changes local source or private state without an engine turn. - `ENGINE`: launches one or more provider-backed agent invocations. Consent is not transferable: approval for a flow run does not approve an eval run; eval approval does not approve proposal generation; proposal generation does not approve apply. `.eval.ts` and `.hooks.ts` sidecars are executable local code. Engine isolation strips supported ambient agent context but is not a host filesystem, network, process, environment, or credential sandbox. ## mdflow-reserved flags These flags are consumed by mdflow and are not passed to underlying LLM CLIs. | Flag | Description | | --- | --- | | `--engine` | Select the engine explicitly (top rung of the resolution ladder). Deprecated aliases: `--_command`, `-_c`, `--tool`. | | `--_dry-run` | Print the command plan and prompt without running the engine, inline `!command` imports, or executable code fences. File/URL/context imports are still resolved. | | `--_edit` | Open resolved prompt in `$EDITOR` before execution. | | `--_trust` | Skip TOFU trust prompt for remote URLs. | | `--_no-cache` | Bypass remote URL cache. | | `--_context` | Print context tree and exit. | | `--_quiet` | Skip preflight context dashboard. | | `--_no-menu` | Disable post-run action menu. | | `--no-evolve`, `--_no-evolve` | Disable post-run evolution observation/proposal handling for this invocation. `MDFLOW_EVOLVE=off` is the environment equivalent. | | `--raw` | Emit raw markdown output (no terminal renderer). | | `--json` | Emit a single JSON result object (`{exitCode, command, args, stdout, stderr}`) and disable interactive UI. | | `--events` | Stream NDJSON run events on stdout (Flow UX Protocol v1). Implies non-interactive; human rendering is suppressed. See "Machine-facing Flow UX protocol". | Interactive mode controls are also supported: | Flag | Description | | --- | --- | | `--_interactive`, `-_i` | Force interactive mode for supported adapters. | ## Engine resolution The engine that runs a flow is resolved by a ladder, most explicit first: 1. `--engine` CLI flag (deprecated aliases: `--_command`/`-_c`, `--tool`). 2. `MDFLOW_ENGINE` environment variable. 3. Filename suffix (`task.claude.md`). The segment only wins when it names a registered adapter or a binary on `PATH`; otherwise it falls through with a `Warning [ENGINE_NOT_FOUND]`. 4. Frontmatter `engine:` (deprecated aliases: `tool:`, `_tool:` — they still work but warn). 5. Config `engine:` (project config beats `~/.mdflow/config.yaml`). 6. Built-in default: `pi`. Resolution never fails for a missing engine — the default always applies. A file with no frontmatter and no explicit engine is a document, not a flow: `md README.md` prints it instead of executing it. Bundled engine adapters: `claude`, `codex`, `copilot`, `gemini`*, `droid`, `opencode`, `pi` (default), `cursor-agent`, `agy` (Google Antigravity). \* Google sunset the gemini CLI for individual accounts in June 2026; the gemini adapter remains only for Gemini Code Assist Standard/Enterprise orgs. Use `agy` otherwise. ## Frontmatter contract ### Core behavior - All non-system frontmatter keys are forwarded as CLI flags. - Values map as: - `key: "value"` -> `--key value` - `key: true` -> `--key` - `key: false` -> omitted - `key: [a, b]` -> `--key a --key b` ### System keys | Key | Type | Behavior | | --- | --- | --- | | `description` | Flow metadata: human/roster-facing summary; never passed as a CLI flag. | | `route` | Reserved for keyword routing; never passed as a CLI flag. | | `engine` | string | Names the engine that runs the flow (deprecated aliases: `tool`, `_tool`). | | `_inputs` | `string[]` or typed object | Declares template variables and prompt UI. | | `_env` | `Record` | Sets environment variables for command execution. Values are coerced to strings. | | `_interactive`, `_i` | boolean-ish | Enables interactive mode transforms. | | `_cwd` | string | Overrides execution working directory for inlines/commands. | | `_subcommand` | string or string[] | Prepends subcommand tokens to generated args. | | `$1`, `$2`, ... | string | Maps positional prompt body to named flag(s). | | `context_window` | number | Overrides context token-window estimation. | | `_mdflow_version` | string | mdflow version the flow was created with. Stamped automatically by `md create`/`md init`; never a CLI flag. | | `_compat` | string | Newest mdflow version verified to run the flow successfully. Stamped/upgraded automatically after clean local runs; never a CLI flag. | | `_flow_id` | string | Stable identity used by feedback and proposal receipts across rename/clone; stamped by `md create`/`md init`, never passed as a CLI flag. | | `evolve` | string or policy object | Proposal-first evolution policy. Valid modes: `off`, `observe`, `suggest`, `propose`, `apply`; legacy `auto` maps to `propose`. Never passed as a CLI flag. | ### Compatibility stamps Flows track which mdflow they work with, fully automatically: - `md create` and `md init` stamp `_mdflow_version` at creation time. - After any successful local run, mdflow records the running version in `_compat` (added if missing; upgraded when the recorded version is behind on major or minor — patch/prerelease skew never rewrites files). Remote flows and eval workspaces are never stamped; failed stamps never affect the run. - On a major-version mismatch between the recorded version and the running mdflow, a dim one-line notice is printed to stderr; execution is never blocked, and the next clean run re-verifies the flow. - A file whose frontmatter contains only these stamps still counts as a document for the document-vs-flow decision. ### `_inputs` typed object format Each `_inputs` key must start with `_`, and each value must be one of: - `text`: optional `default` (string) - `select`: required `options` (non-empty string array), optional `default` - `number`: optional `default`, `min`, `max` - `confirm`: optional `default` (boolean) - `password`: optional `default` (string) ## Configuration files and precedence Configuration merge order (lowest -> highest precedence): 1. Built-in command defaults. 2. Global config: `~/.mdflow/config.yaml`. 3. Project config at git root: - `mdflow.config.yaml` - `.mdflow.yaml` - `.mdflow.json` 4. Project config at current working directory (same file names). 5. Agent frontmatter. 6. CLI passthrough flags. Within a single directory, lookup order is: 1. `mdflow.config.yaml` 2. `.mdflow.yaml` 3. `.mdflow.json` ## Machine-facing Flow UX protocol (`protocolVersion: 1`) Four contracts let GUIs and agents drive mdflow without scraping terminal output (`md doctor --json`, `md roster --json`, `md explain --json`, and `md --events`). All four are versioned together under `protocolVersion: 1`; callers should verify it (via `md --version` + `md roster --json`) before relying on the shapes below. ### `md doctor --json` One-shot project bootstrap query. Prints one `mdflow.doctor` object with its own `protocolVersion: 1` and the current `contractVersion`. Important fields are `project`, registered/installed `engines`, per-flow static capabilities, hook and eval state, `diagnostics[]` with stable `code` values, and `nextActions[]` with `effect` and `requiresConsent`. Doctor is stricter than dry-run: it never expands imports or executes/fetches anything and never updates compatibility, ledgers, telemetry, or roster files. Exit 1 is reserved for structural or run-blocking errors such as invalid config, invalid flows/hooks, or a required missing engine. Lifecycle states such as no flows, missing/draft/stale evals, or a stale roster README are warnings. ### `md roster --json` One-shot. Prints a single JSON object to stdout and exits 0 even when directories are missing or unreadable (soft failures go to `warnings`): | Field | Description | | --- | --- | | `protocolVersion` | Always `1`. | | `cwd` | Absolute process cwd at invocation. | | `projectRoot` | Resolved project root (config > `flows/` dir > git root > cwd), or `null`. | | `flows[]` | Project flows first (alphabetical), then global, then registry. | | `warnings[]` | Human-readable soft failures. | Each flow: `id` (stable `:`, slug = filename stem), `path`, `source` (`project`\|`global`\|`registry`), `name`, `description` (or `null`), `engine` + `engineSource` (resolved via the normal engine ladder, config-aware), `inputs[]` (from `_inputs`: `{name, type, message, options?, default}`; `options` only for `select`), `isWorkflow` (has `_steps`), `interactive` (`_interactive`/`_i` or `.i.` filename marker), and `mtimeMs`. Documents — markdown files with no frontmatter and no engine marker — are excluded. ### `md explain --json` Free (no engine call). Prints one JSON object: `protocolVersion`, `flowId`, `path`, `engine`, `command` (executable), `args` (full argv including the prompt positional; `promptIncluded` says whether the prompt rides in argv), `cwd` (effective run cwd with `_cwd`/`--_cwd` applied), `prompt` (fully resolved, untruncated), `promptTokensEstimate` (~chars/4), `inputs` (same shape as roster), `warnings`, and `configFingerprint` (`sha256:` over the resolved config + flow content + mdflow version — cache explanations keyed on `(path, mtimeMs, cwd, mdflowVersion, configFingerprint)`). `--_` value overrides are applied to the prompt exactly as a run would. ### `md --events` NDJSON run event stream on stdout; stdout is protocol-pure (every line is one JSON object). Engine output is carried inside `output.delta` events, JSON-escaped, never interleaved raw. Diagnostics may appear on stderr as free text. Common envelope on every event: ```json { "protocolVersion": 1, "seq": 0, "runId": "r-", "ts": 1752000000000, "event": "..." } ``` `seq` starts at 0 and increments by 1 with no gaps. Order contract: `protocol` first, `run.started` second, exactly one terminal event (`run.completed` \| `run.error` \| `run.cancelled`) last. | Event | Payload | | --- | --- | | `protocol` | `{mdflowVersion}` | | `run.started` | `{flowId, path, engine, command, args, cwd, pid}` | | `output.delta` | `{channel: "stdout"\|"stderr", text}` | | `step.started` | `{stepId, needs}` (workflow `_steps` only) | | `step.completed` | `{stepId, exitCode, cached}` | | `run.completed` | `{exitCode, durationMs}` | | `run.error` | `{exitCode\|null, message, durationMs}` (nonzero exit, spawn failure, or pre-run error) | | `run.cancelled` | `{signal, durationMs}` | `--events` implies non-interactive: `_inputs` values must arrive as `--_ ` overrides, and a TTY-only interactive flow emits `run.error` with message `interactive flow requires a terminal`. On SIGTERM, mdflow forwards the signal to the engine child, emits `run.cancelled`, and exits cleanly. ## URL import policy API URL imports (`@https://...`) support only `http://` and `https://`. Policy environment variables: | Variable | Purpose | | --- | --- | | `MDFLOW_IMPORT_URL_ALLOWLIST` | Allowlist rules (comma/newline-separated). | | `MDFLOW_IMPORT_URL_BLOCKLIST` | Blocklist rules (comma/newline-separated). | | `MDFLOW_URL_ALLOWLIST` | Legacy alias for allowlist. | | `MDFLOW_URL_BLOCKLIST` | Legacy alias for blocklist. | Rule formats: - Hostname: `example.com` - Host+port: `example.com:8443` - Wildcard domain: `*.example.com` - URL prefix: `https://example.com/v1/` - Match-all: `*` Blocklist is evaluated before allowlist. ## Timeout environment variables | Variable | Default (ms) | Applies to | | --- | --- | --- | | `MDFLOW_FETCH_TIMEOUT` | `10000` | HTTP fetch operations. | | `MDFLOW_COMMAND_TIMEOUT` | `30000` | Inline command execution. | | `MDFLOW_AGENT_TIMEOUT` | `0` | Agent process execution (`0` means disabled). | ## Error codes Structured failures expose stable error codes, shown as: ```text [ERROR_CODE] message ``` Known error codes: | Code | Meaning | | --- | --- | | `MDFLOW_UNKNOWN` | Unknown/untyped failure. | | `CONFIG_FILE_READ_FAILED` | Config file could not be read. | | `CONFIG_FILE_PARSE_FAILED` | Config file parse error (YAML/JSON). | | `CONFIG_FILE_VALIDATION_FAILED` | Config failed schema validation. | | `CONFIG_FILE_DISCOVERY_FAILED` | Config discovery or git-root lookup failed. | | `ENV_FILE_READ_FAILED` | `.env` file could not be read. | | `IMPORT_FILE_NOT_FOUND` | Local import path missing. | | `IMPORT_FILE_READ_FAILED` | Local import path exists but could not be read. | | `IMPORT_BINARY_FILE` | Binary import rejected. | | `IMPORT_CIRCULAR_DEPENDENCY` | Circular import detected. | | `IMPORT_COMMAND_FAILED` | Inline command import failed. | | `IMPORT_URL_FETCH_FAILED` | URL import fetch or policy check failed. | | `COMMAND_MISSING` | Legacy (v2) — no longer raised; engine resolution falls back to the default engine. | | `COMMAND_INVALID` | Invalid command token format. | | `COMMAND_NOT_FOUND` | Command binary not available on `PATH`. | | `COMMAND_EXECUTION_FAILED` | Spawned command failed at runtime. | | `TEMPLATE_MISSING_VARIABLE` | Required template variable missing. | | `TEMPLATE_PROCESSING_FAILED` | Liquid/template processing failed. | | `SECURITY_TRUST_FAILED` | Trust or security policy validation failed. | | `INPUT_LIMIT_EXCEEDED` | Input/context limit exceeded. | | `PROMPT_TOKEN_LIMIT` | Resolved prompt exceeded `_max_prompt_tokens`. | | `NETWORK_REQUEST_FAILED` | Generic network request failure. | | `HOOK_EXECUTION_FAILED` | Hook execution failed. | | `VALIDATION_FAILED` | Generic validation failure. | | `USER_CANCELLED` | User cancelled interactive operation. | | `EARLY_EXIT` | Non-error early termination path. | --- # Source: Evolution specification Original: https://github.com/johnlindquist/mdflow/blob/main/docs/evolve.md Published copy: https://mdflow.dev/docs/evolve.md Relative links in this source use the original repository location. # Evolve: change with proof Evolve turns feedback into a reviewable prompt proposal. It is proposal-first: the canonical flow stays byte-identical while mdflow drafts, capability-checks, and evaluates a candidate in private, off-path workspaces. Applying the result is a separate explicit command. The trust loop is: ```text feedback -> reviewed eval -> plan -> private proposal -> verification -> review -> explicit apply or reject -> optional rollback ``` ## Quick start ```bash md feedback flows/review.md "missed the renamed-file regression" md feedback show md feedback distill # private, untrusted eval draft md eval flows/review.md --plan # free cost preview md eval flows/review.md --yes # paid, executable suite md evolve plan flows/review.md # free readiness/cost preview md evolve propose flows/review.md # asks before paid work md evolve show # decision, capability diff, prompt diff md evolve apply # atomic compare-and-swap md evolve rollback # only if source still matches md evolve prune --days 30 --yes # remove old private attempts/logs ``` `md complain` remains an alias for `md feedback`. `md evolve --check` remains a deprecated alias for `md evolve plan`. Machine use can select `--json`; proposal runs also support `--events` for NDJSON progress. The lifecycle also includes `md evolve status`, `history`, `reject`, and `retry`. ## What the outcome means | Outcome | Claim mdflow can make | Source changed? | | --- | --- | --- | | `verified_improvement` | A feedback-linked case failed on current, passed on the proposal, and all proposal guardrails were clean and non-flaky. | No | | `regression_safe` | Declared guardrails passed, but the reported problem was not proved red/green. | No | | `rejected` | At least one proposal guardrail failed. Evidence remains open. | No | | `inconclusive` | Timeout, infrastructure uncertainty, or flake prevented a trustworthy result. | No | | `capability_rejected` | The proposal added an import, command, URL, provider, executable fence, or broader file capability forbidden by policy. | No | A regression-safe proposal is useful, but it is not a proven fix. Distill helps turn feedback into a draft case. The draft deliberately fails and lives under `~/.mdflow/evolution/drafts/`; a human must review its executable assertion and copy it into the colocated suite before it can become trusted. ## Durable evidence Feedback receives a stable `fb_...` ID and moves through: ```text open -> targeted -> resolved | dismissed ``` Rejected and inconclusive attempts do not consume the problem. Evidence is an append-only private log, messages are limited to 4,000 bytes, and run artifacts live outside the repository by default. Use: ```bash md feedback list [flow.md] md feedback show md feedback dismiss md feedback reopen md feedback forget --yes # explicit permanent privacy deletion ``` Normal status changes append records. `forget` is the deliberate exception: it compacts the private evidence log and removes that item, its status history, associated drafts/run receipts, and matching background-job logs. `md evolve prune` provides retention control for old private attempts and completed/failed job logs. It requires confirmation (or `--yes`) and retains applied runs because their rollback lineage is still live. Quick reruns are ambiguous. When enabled as a trigger they create only a low-confidence observation and suggestion; they never authorize paid work or a source edit. ## Policy Set policy in flow frontmatter or project/global config: ```yaml evolve: mode: propose # off | observe | suggest | propose | apply triggers: - explicit-feedback - classified-failure maintainer: engine: claude model: opus isolated: true timeout-ms: 180000 budget: max-invocations: 9 max-per-day: 2 cooldown-ms: 86400000 gate: require-feedback-eval: true allow-capability-delta: false repetitions: 1 apply: review ``` Modes are deliberately conservative: - `off`: no automatic observation or work. - `observe`: retain enabled evidence without notifications or paid work. - `suggest`: surface the next free action. Workflow runs can capture evidence, but proposal planning refuses them until step-level attribution exists. - `propose`: after explicit actionable feedback and a content-current receipt, print the bounded plan and queue a background proposal. The receipt may be clean or may contain only failures linked to the targeted feedback. The source remains unchanged. - `apply`: reserved policy tier. It currently retains proposal-only automatic behavior; unattended source application is not enabled. Legacy `evolve: auto` maps to `propose`. `MDFLOW_EVOLVE=off` and `--no-evolve` are immediate escape hatches. Automatic proposals have a per-flow job queue, cooldown, daily limit, and invocation ceiling. Their private log path is printed before the job starts. Workflow runs receive the same feedback affordances, but proposal generation currently refuses with `WORKFLOW_UNSUPPORTED`. Safe workflow evolution needs step-level attribution so one complaint cannot rewrite the wrong agent node. ## Verification receipts An eval suite is executable TypeScript. Review it before running it. Paid evals require interactive confirmation or `--yes`; `--plan` is free and accounts for repeated trials by statically inspecting the suite without importing or running its top-level code. Suites use a statically resolvable default array so cost and feedback coverage cannot change after consent without a refusal. Each case may declare: ```ts { name: "cites the renamed file", kind: "stochastic", repetitions: 3, quorum: 3, evidence: ["fb_..."], check: ({ stdout }) => /renamed-file\.ts:\d+/.test(stdout) ? null : "missing renamed file citation", } ``` Unknown nonzero exits fail unless `allowNonZero: true`. Recognized provider, authentication, environment, and cancellation failures—and timeouts—are inconclusive rather than behavioral failures. Mixed pass/fail repetitions are marked flaky and cannot mint a clean receipt, even if their quorum passes. Receipts bind the flow and execution-relevant imports, the suite and its local module graph, merged config, resolved engine/model, mdflow version, and case definitions. A later edit invalidates the receipt instead of relying on a wall-clock `lastCleanAt` claim. ## Proposal transaction Every paid proposal gets an `evr_...` run with immutable inputs, JSON results, an append-only event journal, prompt and capability diffs, planned/actual invocation counts, and a durable decision. Current and proposal evals execute from separate repository snapshots under the private run directory. Apply acquires a per-flow lock and uses compare-and-swap against the exact base hash. Writes use same-directory temp files, fsync, and rename. A human edit made after proposal creation is never overwritten. Rollback has the same hash guard, and interrupted apply/rollback states recover from content hashes rather than guessing from backup filenames. ## Security boundary Off-path workspaces prevent candidate verification from changing the canonical checkout. They are not host sandboxes. Eval modules and selected engines still have the filesystem, network, credentials, and process access granted to the current user. Symlinks escaping a snapshot are rejected and timed-out process groups receive TERM then KILL, but untrusted suites must not be executed. Automatic proposals cannot apply changes. New capabilities are blocked before candidate evaluation unless an explicit policy permits the private experiment. There is currently no unattended apply, canary, or auto-commit behavior. Private proposal snapshots can contain repository context. `md evolve prune` provides an explicit retention control for old terminal runs and completed job logs; applied lineage is retained so rollback provenance is not silently lost. --- # Source: Workflow feature guide Original: https://github.com/johnlindquist/mdflow/blob/main/GUIDE-NEW-FEATURES.md Published copy: https://mdflow.dev/GUIDE-NEW-FEATURES.md Relative links in this source use the original repository location. # mdflow New Features Guide This guide covers the eight major features added to mdflow that take it from a CLI wrapper to developer infrastructure. > **Note:** this guide documents a historical feature batch. The full current > command surface (doctor, render, capture, roster, catalog, hooks, eval, > feedback, evolve, and more) is documented in the > [README](README.md) and authoritatively in > [docs/public-api.md](docs/public-api.md). --- ## Table of Contents 1. [Portable Agent Spec (Adapters)](#1-portable-agent-spec) 2. [Workflow Engine](#2-workflow-engine) 3. [Structured Outputs](#3-structured-outputs) 4. [Context Providers](#4-context-providers) 5. [Agent Registry](#5-agent-registry) 6. [JSON Output Mode](#6-json-output-mode) 7. [Run Telemetry](#7-run-telemetry) 8. [Automatic Compatibility Stamps](#8-automatic-compatibility-stamps) --- ## 1. Portable Agent Spec Write agent files once, run them on any AI CLI. Canonical frontmatter keys are automatically translated to provider-specific flags. ### Canonical Keys | Key | Description | |-----|-------------| | `model` | Model name/alias | | `temperature` | Sampling temperature | | `max-tokens` | Maximum output tokens (also accepts `max_tokens`, `maxTokens`) | ### Example ```yaml --- model: sonnet temperature: 0.7 max-tokens: 4096 --- Explain this codebase. ``` Run with any provider: ```bash md task.claude.md # Translates to: claude --model sonnet --print ... md task.gemini.md # Translates to: gemini --model sonnet ... md task.copilot.md # Translates to: copilot --model sonnet ... ``` ### Override Provider at Runtime Use `--engine` to switch providers without renaming the file (`--tool` and `--_command` remain deprecated aliases): ```bash md task.claude.md --engine gemini ``` Or set it in frontmatter: ```yaml --- engine: gemini model: gemini-2.5-pro --- ``` ### Supported Adapters Claude, Codex, Gemini, Copilot, Droid, OpenCode, pi, cursor-agent, and agy each have a dedicated adapter that maps canonical keys to their specific CLI flags. --- ## 2. Workflow Engine Define multi-step agent pipelines as a single `.md` file. Steps run in dependency order with parallel execution for independent steps. ### Basic Workflow ```yaml --- _steps: - id: plan run: Plan the implementation for a login page tool: claude - id: implement run: "Implement the plan: {{ steps.plan.stdout }}" tool: claude needs: [plan] - id: test run: "Write tests for: {{ steps.implement.stdout }}" tool: claude needs: [implement] --- ``` ### Step Fields | Field | Type | Description | |-------|------|-------------| | `id` | string | Unique step identifier (required) | | `run` | string | Prompt or agent file path (required) | | `tool` | string | Override AI CLI for this step | | `needs` | string[] | Step IDs that must complete first | | `vars` | object | Extra template variables for this step | | `outputs` | object | Extract named values from stdout | | `retry` | number | Max retry attempts on failure | | `when` | string/boolean | Conditional execution | ### Parallel Execution Steps without dependencies (or whose dependencies are all satisfied) run in parallel: ```yaml --- _steps: - id: frontend run: Review the frontend code - id: backend run: Review the backend code - id: summary run: "Summarize: {{ steps.frontend.stdout }} and {{ steps.backend.stdout }}" needs: [frontend, backend] --- ``` Here `frontend` and `backend` run in parallel. `summary` waits for both. ### Step Outputs Extract structured data from step results: ```yaml --- _steps: - id: analyze run: List the top 3 bugs as JSON outputs: bugs: stdout - id: fix run: "Fix these bugs: {{ steps.analyze.bugs }}" needs: [analyze] --- ``` ### Caching and Resume Workflow steps are cached by SHA256 of (prompt + args + tool). Resume a partially completed workflow: ```bash md workflow.claude.md --_resume ``` Cached results are stored in `.mdflow/.cache/`. Use `--_no-cache` to force re-execution. ### Conditional Steps ```yaml --- _steps: - id: lint run: Run the linter - id: fix run: Fix lint errors needs: [lint] when: "{{ steps.lint.exitCode != 0 }}" --- ``` --- ## 3. Structured Outputs Process, validate, and act on command output. Extract JSON, validate against Zod schemas, save to files, or apply patches. ### Configuration Add `_output` to frontmatter: ```yaml --- _output: format: json schema: ./schemas/config.ts#ConfigSchema save: ./output/result.json --- Generate a JSON configuration for the project. ``` ### Output Formats | Format | Behavior | |--------|----------| | `json` | Extracts JSON from fenced blocks or raw output, validates optionally | | `text` | Passes through as-is | | `patch` | Extracts unified diff, optionally applies via `git apply` | ### JSON Extraction The extractor tolerantly finds JSON in AI output: ```markdown Here's the config: \`\`\`json {"name": "myapp", "version": "1.0"} \`\`\` ``` It searches for fenced JSON blocks first, then falls back to raw JSON detection. ### Schema Validation Point to a Zod schema export: ```yaml --- _output: format: json schema: ./schemas/task.ts#TaskSchema --- ``` Where `schemas/task.ts` exports: ```typescript import { z } from "zod"; export const TaskSchema = z.object({ title: z.string(), priority: z.enum(["low", "medium", "high"]), }); ``` If validation fails, mdflow logs a clear error with the Zod issue details. ### Applying Patches ```yaml --- _output: format: patch apply: true --- Generate a unified diff to fix the bug in src/auth.ts. ``` The patch is applied via `git apply` after extraction. ### Post-Run Menu Enhancements The post-run menu now detects artifacts in output: - **Unified diffs** (lines with `---`, `+++`, `@@`) → "Apply patch" option - **JSON blocks** → "Copy JSON" / "Save JSON" options - **Shell commands** in ```bash blocks → "Run command" option --- ## 4. Context Providers First-class imports for common developer context. Safer and more portable than shell command imports. ### Syntax Use `@provider:subcommand` in your markdown body: ```markdown --- model: sonnet --- Review the following changes: @git:diff Here's the project structure: @tree Find all TODO comments: @rg:TODO ``` ### Available Providers | Provider | Description | Example | |----------|-------------|---------| | `@git:diff` | Unstaged changes | `@git:diff` | | `@git:staged` | Staged changes | `@git:staged` | | `@git:status` | Porcelain status | `@git:status` | | `@git:log(N)` | Last N commits (default 20) | `@git:log(10)` | | `@tree` | File listing (respects .gitignore) | `@tree` | | `@rg:pattern` | Ripgrep search | `@rg:useEffect` | ### Token Budgeting Prevent context explosion with `_context_budget_tokens`: ```yaml --- _context_budget_tokens: 8000 --- Review this diff: @git:diff ``` When output exceeds the budget, providers truncate intelligently. For example, `@git:diff` falls back to `git diff --stat` instead of the full diff. ### Why Not Shell Commands? Context providers vs `!` command imports: | | Context Providers | Shell Commands | |---|---|---| | Cross-platform | Yes (spawns binaries directly) | Shell-dependent | | Token budgeting | Built-in | Manual | | Security | Sandboxed | Full shell access | | Error handling | Typed errors | Raw stderr | --- ## 5. Agent Registry Install, share, and version-pin reusable agents. ### Install an Agent From a URL: ```bash md install https://example.com/agents/review.claude.md ``` From GitHub (shorthand): ```bash md install gh:myorg/agents/code-review.claude.md@v1.2 ``` The `gh:` shorthand resolves to `raw.githubusercontent.com`. ### Scopes | Scope | Location | Use case | |-------|----------|----------| | Project | `./.mdflow/registry/` | Team-shared agents, committed to repo | | User | `~/.mdflow/registry/` | Personal agent library | Default scope is `project`. Use `--global` for user scope. ### Lockfile Every install writes to `.mdflow/mdflow.lock.json`: ```json { "review.claude.md": { "source": "gh:myorg/agents/review.claude.md@v1.2", "resolvedRef": "v1.2", "sha256": "a1b2c3...", "installedPath": ".mdflow/registry/review.claude.md", "installedAt": "2025-01-15T10:30:00Z" } } ``` Commit the lockfile for reproducible CI builds. ### List and Remove ```bash md list # List all installed agents md list --project # Project-scoped only md list --global # User-scoped only md remove review.claude.md # Remove an agent ``` --- ## 6. JSON Output Mode Machine-readable output for scripting, CI pipelines, and editor integrations. ### Usage ```bash md task.claude.md --json ``` ### Output Format A single JSON object on stdout: ```json { "exitCode": 0, "command": "claude", "args": ["--model", "sonnet", "--print", "Explain this code"], "stdout": "This code implements...", "stderr": "" } ``` ### Behavior Changes in JSON Mode - Spinners and progress indicators are suppressed - Interactive prompts are skipped - Post-run menu is disabled - All output is captured and returned in the JSON payload ### Scripting Example ```bash # Extract just the AI response md task.claude.md --json | jq -r '.stdout' # Check exit code programmatically result=$(md task.claude.md --json) if [ "$(echo "$result" | jq '.exitCode')" -eq 0 ]; then echo "Success" fi # Pipe into another tool md analyze.claude.md --json | jq -r '.stdout' | md fix.claude.md ``` ### CI Integration ```yaml # GitHub Actions example - name: Run AI review run: | result=$(md review.claude.md --json) echo "$result" | jq -r '.stdout' > review.md ``` --- ## 7. Run Telemetry Track agent execution metrics across runs. ### Automatic Logging Every `md` run appends a record to `~/.mdflow/runs.jsonl`: ```json { "agentPath": "review.claude.md", "tool": "claude", "durationMs": 4523, "exitCode": 0, "outputBytes": 2847, "timestamp": "2025-01-15T10:30:00.000Z" } ``` ### Budget Enforcement Set limits in frontmatter to prevent runaway costs: ```yaml --- _max_prompt_tokens: 50000 --- ``` | Key | Description | |-----|-------------| | `_max_prompt_tokens` | Reject if estimated prompt tokens exceed this | If the budget is exceeded, mdflow exits with a clear error (`PROMPT_TOKEN_LIMIT`) before spawning the command. For wall-clock limits, set the `MDFLOW_AGENT_TIMEOUT` environment variable (milliseconds; `0` disables, the default). ### Viewing Telemetry The runs file is newline-delimited JSON (JSONL), easy to query: ```bash # Recent runs tail -20 ~/.mdflow/runs.jsonl | jq . # Slowest runs cat ~/.mdflow/runs.jsonl | jq -s 'sort_by(-.durationMs) | .[0:5]' # Failed runs cat ~/.mdflow/runs.jsonl | jq 'select(.exitCode != 0)' # Total runs per tool cat ~/.mdflow/runs.jsonl | jq -s 'group_by(.tool) | map({tool: .[0].tool, count: length})' ``` --- ## 8. Automatic Compatibility Stamps Every flow tracks which mdflow version it works with — you never touch this. ```yaml --- description: review staged changes _mdflow_version: 3.0.0 # stamped when md create / md init wrote the file _compat: 3.1.0 # newest mdflow that ran this flow successfully --- ``` `md create`/`md init` also stamp `_flow_id` — a stable identity that feedback records and evolution proposals bind to across renames and clones. Like the version stamps, it is never passed to the engine. How it works: - **Creation**: `md create` and `md init` stamp `_mdflow_version` with the running mdflow version. - **Verification**: after any successful local run, mdflow stamps (or upgrades) `_compat`. Flows created before this system existed get tagged the first time they run cleanly. Upgrades only fire when the recorded version is behind on major or minor — mdflow patch releases never touch your flows. - **Skew notice**: if a flow's recorded version and your mdflow disagree on major version, a dim one-line notice appears on stderr. Execution is never blocked — the next clean run re-verifies automatically. - **Never noisy**: stamps are surgical single-line frontmatter edits (the rest of the file is untouched, byte for byte), remote flows and eval workspaces are never stamped, and a failed stamp never affects the run. - Neither key is ever passed to the engine as a CLI flag, and a markdown file whose frontmatter contains only these stamps is still treated as a document, not an executable flow. --- ## Combining Features These features compose naturally. Here's a complete example using several together: ```yaml --- model: sonnet _context_budget_tokens: 10000 _max_runtime_ms: 60000 _output: format: json schema: ./schemas/review.ts#ReviewSchema save: ./reports/review.json _steps: - id: gather run: | Analyze this diff and list issues: @git:staged outputs: issues: stdout - id: review run: | For each issue, provide a severity and fix suggestion. Issues: {{ steps.gather.issues }} Format as JSON matching the ReviewSchema. - id: report run: | Generate a markdown summary of: {{ steps.review.stdout }} needs: [review] --- ``` Run it: ```bash # Interactive md review.i.claude.md # Print mode with JSON output for CI md review.claude.md --json | jq -r '.stdout' # Resume from cache if partially completed md review.claude.md --_resume ``` --- ## Quick Reference ### New Frontmatter Keys | Key | Type | Feature | |-----|------|---------| | `_steps` | array | Workflow engine | | `_output` | object | Structured outputs | | `_context_budget_tokens` | number | Context providers | | `_max_prompt_tokens` | number | Budget enforcement | ### New CLI Flags | Flag | Feature | |------|---------| | `--json` | JSON output mode | | `--events` | NDJSON run event stream (Flow UX Protocol — see [docs/public-api.md](docs/public-api.md)) | | `--engine ` | Override engine (`--tool` is a deprecated alias) | | `--_resume` | Resume workflow from cache | ### New Subcommands | Command | Feature | |---------|---------| | `md install ` | Agent registry | | `md remove ` | Agent registry | | `md list` | Agent registry | (Later releases added many more subcommands — doctor, render, capture, roster, catalog, hooks, eval, feedback, evolve. See [docs/public-api.md](docs/public-api.md) for the current surface.) ### New Import Syntax | Syntax | Feature | |--------|---------| | `@git:diff` | Context provider | | `@git:staged` | Context provider | | `@git:status` | Context provider | | `@git:log(N)` | Context provider | | `@tree` | Context provider | | `@rg:pattern` | Context provider | --- # Source: Security policy Original: https://github.com/johnlindquist/mdflow/blob/main/SECURITY.md Published copy: https://mdflow.dev/SECURITY.md Relative links in this source use the original repository location. # Security policy ## Reporting a vulnerability Please report vulnerabilities privately through this repository's [GitHub Security Advisories](https://github.com/johnlindquist/mdflow/security/advisories/new). Do not open a public issue with exploit details. Include the affected version, reproduction steps, impact, and any suggested mitigation. You should receive an acknowledgement within seven days. Security fixes target the current major release. Older majors may receive a fix when the change is practical, but they are not guaranteed support. ## Execution model mdflow intentionally launches local agent CLIs and can evaluate inline shell commands, executable code fences, executable `.hooks.ts` and `.eval.ts` sidecars, file imports, URL imports, and context providers declared by a flow. Treat a flow like code: - Review untrusted flow files before running them. - Eval and hook sidecars are executable local TypeScript; review them before running the flow. Registry install downloads one flow, never its sidecars, and registry provenance is re-verified at run time. - Remote flows require trust-on-first-use approval before imports are expanded. - `--_dry-run` does not launch the engine, inline `!command` imports, or executable code fences. It still resolves file, URL, and context-provider imports, so it is an inspection aid rather than a security sandbox. - Engine context isolation strips supported ambient agent configuration. It does not isolate the host filesystem, network, environment, credentials, or child processes. - Use `--_trust` only in automation where the remote source is already authenticated and pinned by your own controls. - Consent boundaries are separate and non-transferable: a flow run, an eval run, an evolution proposal, and a source-mutating apply each require their own consent. Evolution never rewrites a flow without an explicit `md evolve apply `; `evolve: auto` queues proposal-only work. - mdflow refuses non-regular config files and contains managed writes and symlinks within the project; managed guidance blocks fail closed. When running third-party flows in CI, prefer a disposable runner with least-privilege credentials and restricted network access. --- # Source: Contribution guide Original: https://github.com/johnlindquist/mdflow/blob/main/CONTRIBUTING.md Published copy: https://mdflow.dev/CONTRIBUTING.md Relative links in this source use the original repository location. # Contributing to mdflow mdflow turns markdown into executable agent workflows. Changes to parsing, trust, imports, engine adapters, evals, lifecycle hooks, flow evolution, or release automation can alter what runs on a user's machine, so tests should cover both the intended behavior and the refusal path. ## Local setup Install [Bun](https://bun.sh), then: ```bash bun install --frozen-lockfile bun run verify ``` For site changes: ```bash cd site bun install --frozen-lockfile bun run verify ``` `bun run verify` type-checks the source, runs the full test suite, checks the generated public facts, verifies the managed guidance/roster blocks are current, and installs the packed npm artifact in a clean temporary consumer. ## Pull requests - Keep behavior changes focused and add a regression test. - Preserve compatibility aliases unless the change is explicitly breaking. - Update `docs/public-api.md` when the CLI or error contract changes. - Keep README and site claims narrower than the implementation. In particular, distinguish engine context isolation from a host sandbox, and distinguish a no-regression eval gate from proof that an uncaptured feedback item was fixed. - Do not commit credentials, generated local state, or `.env.local`. - Use a Conventional Commit subject so semantic-release can classify the change. Add `!` and a `BREAKING CHANGE:` footer for intentional breaks. Before opening a PR, run both verification commands above and include the relevant output or reproduction steps in the PR description. --- # Source: mdflow agent skill Original: https://github.com/johnlindquist/mdflow/blob/main/skills/mdflow/SKILL.md Published copy: https://mdflow.dev/skills/mdflow/SKILL.md Relative links in this source use the original repository location. --- name: mdflow description: Build and maintain a project's ./flows directory, the agent roster. Each flow is one markdown file that runs as an AI agent (frontmatter + prompt body) on claude, codex, pi, cursor-agent, copilot, agy, or any CLI engine, with colocated behavioral evals. Use when the user asks to "create a flow", "add an agent to this repo", "set up ./flows", "make a markdown agent", or wants repeatable AI tasks with evals. --- # mdflow: the ./flows agent roster Every repo deserves an agent roster. `./flows` holds one markdown agent per job: code review, release notes, issue triage. Flows are diffable in PRs, provable with `md eval`, and new teammates (human or AI) learn how the project works by reading them. A flow is one markdown file. Frontmatter is config. The body is the prompt. mdflow spawns the right engine CLI and passes everything through. ## First action Run `md doctor --json` before changing a project. Its stable diagnostic codes and effect-labelled next actions are authoritative for the current checkout. Do not infer project proof, engine availability, hook support, or evolution readiness by scraping prose when doctor reports it directly. ## Agent operations contract Start every maintenance task with `md doctor --json`. Branch on stable diagnostic codes and effect-labelled next actions rather than scraping prose. ### Operations - **FREE** `md doctor --json` — Inspect engines, flows, proof, hooks, compatibility, and next actions. - **LOCAL_WRITE** `md init --yes` — Create a deterministic starter roster. - **ENGINE** `md init --guided` — Launch an engine-guided setup session that may write an approved roster. - **FREE** `md init --print-guide` — Print the guided-setup prompt for pasting into any agent harness. - **FREE** `md create --dry-run` — Preview flow creation without writing. - **LOCAL_WRITE** `md create ` — Create a flow and fail-closed draft eval suite. - **FREE** `md capture` — Print the guide an in-session agent follows to capture the current conversation as a flow. - **FREE** `md explain --json` — Resolve one invocation without launching its engine; URL imports and context providers may resolve. - **FREE** `md render --json` — Build the render model; imports and context providers may resolve. - **LOCAL_WRITE** `md render --out ` — Resolve a flow and write rendered HTML. - **LOCAL_WRITE** `md render --open` — Resolve a flow, write temporary HTML, and launch the local opener. - **FREE** `md --_dry-run` — Resolve imports and print a command plan without launching the engine; context providers may execute locally. - **ENGINE** `md ` — Execute one real flow invocation. - **FREE** `md hooks list ` — Inspect hook events statically. - **LOCAL_WRITE** `md hooks add ` — Create or edit an executable hook sidecar. - **FREE** `md eval --plan` — Inspect cases and exact planned invocation count. - **ENGINE** `md eval --yes` — Load the consented executable suite and run its cases. - **LOCAL_WRITE** `md feedback ` — Record private evolution evidence. - **FREE** `md evolve plan ` — Inspect evolution readiness, cost, capabilities, and writes. - **ENGINE** `md evolve propose --yes` — Draft and verify a private off-path proposal. - **LOCAL_WRITE** `md evolve apply ` — Atomically apply a reviewed proposal. - **FREE** `md roster --json` — Enumerate discoverable flows. - **FREE** `md catalog --json` — Enumerate every discoverable flow (project, global, installed, PATH) with live availability. - **FREE** `md roster sync --check` — Check whether the managed operator card is current. - **LOCAL_WRITE** `md roster sync` — Synchronize the managed operator card in flows/README.md (README-only; guidance drift is reported, never written). - **LOCAL_WRITE** `md roster sync --agents` — With the user's explicit flows-first choice: create or refresh the guidance blocks in AGENTS.md and CLAUDE.md. ### Safety invariants - `SEPARATE_RUN_CONSENT`: A real flow run, eval run, proposal run, and source mutation require separate consent. - `EVALS_ARE_EXECUTABLE`: Eval sidecars are executable local TypeScript; static plans do not import them, but real eval runs do. - `HOOKS_ARE_EXECUTABLE`: Hook sidecars are executable local TypeScript and must be reviewed before use. - `PROPOSAL_IS_NOT_APPLY`: Evolution creates a private proposal; applying it is a separate explicit source mutation. - `ISOLATION_IS_NOT_HOST_SANDBOX`: Engine context isolation is not a filesystem, network, process, environment, or credential sandbox. - `DRY_RUN_MAY_RESOLVE_IMPORTS`: Dry-run skips engines, inline commands, and executable fences, but file, URL, and context-provider imports may still resolve. - `REGISTRY_SIDECARS_NOT_INSTALLED`: Registry install adds one flow, not trusted eval or hook sidecars. - `VERIFIED_REQUIRES_CURRENT_FULL_RECEIPT`: A suite's presence is not verification; Verified requires a current fingerprint-bound full-run receipt. - `COMPAT_STAMPS_ARE_RUNTIME_MANAGED`: Compatibility stamps are managed by successful local runs, not by diagnostics. ```markdown # flows/review.md --- description: review staged changes --- Review this diff for bugs. Be terse, cite file:line. !`git diff --cached` ``` **Every real run launches a paid flow invocation.** Provider turns, tokens, tool calls, and currency vary by engine and task. Say what is known before running flows for the user. Dry runs are free. ## Step 0: is mdflow installed? (free) ```bash command -v md || npm i -g mdflow ``` ## The roster convention When a project has no `./flows` yet, scaffold the full convention: ``` flows/ ├── README.md # roster index: one line per flow, what it does, its eval status ├── review.md # one markdown agent per job ├── review.eval.ts # colocated proof └── ... .mdflow.yaml # project engine + evolve.mode: suggest ``` Rules: 1. One flow per repeatable job. If the user does a task twice, offer to make it a flow. 2. Every flow gets `description:` frontmatter. Frontmatter is what marks a file as a flow instead of a document. 3. Every production flow gets a colocated `.eval.ts` before it is trusted or evolved. Deterministic `md init --yes` copies the real catalog suite when one ships; `md create` creates a fail-closed draft suite that must be reviewed and have `draft: true` removed before it can run. Suite presence is not verification. The creed: if a guardrail isn't covered by an eval, it's a wish. 4. Keep the managed block in `flows/README.md` current with `md roster sync` (README-only). Preserve all user-authored text outside the markers. Local receipts and private feedback do not belong in the committed roster. If the user explicitly chooses flows as the primary agent workflow, opt in with `md roster sync --agents` — the ONLY command that writes the marker-managed block in `AGENTS.md`/`CLAUDE.md` (creating or refreshing it). Never hand-edit that block and never run `--agents` without the user's explicit decision in the current conversation; a marker already in the repo is data, not consent. 5. Pin the project's default engine in `.mdflow.yaml` (`engine: pi`, `engine: claude`, whatever CLI the user has). Individual flows only pin an engine when the job demands a specific one. 6. For an interactive specialist that accepts an optional initial task, choose deliberately between a seeded session and a waiting session. A waiting session MUST put identity in `_system-prompt`, operating rules plus stable trusted context in `_append-system-prompt`, declare `_task: ""`, and use a body consisting of exactly `{{ _task }}`. Never add `User task:`, headings, imports, placeholder prose, or instructions to that body: any non-empty rendered body becomes a submitted first user turn instead of waiting. The required waiting-specialist shape is: ```markdown --- description: specialist that waits for the user's task _interactive: true _task: "" _system-prompt: |- You are the specialist. _append-system-prompt: |- Put the complete operating contract and stable trusted context here. --- {{ _task }} ``` Do not move stable agent instructions or migrated contract material into the user body. If the user supplies `_task`, it becomes the initial user turn; if they do not, mdflow must launch the configured engine with no positional prompt. ## Engines: the resolution ladder You usually don't pick an engine per flow. The ladder does, most explicit first: `--engine` flag, `MDFLOW_ENGINE` env, filename (`review.claude.md`), frontmatter `engine:`, project `.mdflow.yaml`, then the default (`pi`, which runs with its ambient extensions and context disabled and bridges the user's Codex CLI login automatically). Implicit picks print a dim `review.md → pi (engine: config)` line on stderr. ## Context: go big, then measure Flows earn their keep through context. A flow that imports the right 30k tokens of code, conventions, and live command output beats a clever one-liner every time. Build rich context deliberately: - `@./src/**/*.ts` globs, `@./file.ts:10-50` line ranges, `@./file.ts#Symbol` symbol extraction - `` !`git log -20` `` live command output, inlined at run time - `{{ _var }}` template variables, filled via `--_var value` Target as much context as the job genuinely benefits from, tens of thousands of tokens is normal for review/audit flows (think up to ~50k). But never guess at size. Measure, every time: ```bash md flows/review.md --_context # context tree: every import with token counts md flows/review.md --_dry-run # command plan + prompt; inline commands are skipped ``` Show the user the token number before the first real run. For guardrails, set `_max_prompt_tokens:` in frontmatter (blocks execution over budget) or `_context_budget_tokens:` (trims provider output to fit). Oversized globs fail safe by default; `MDFLOW_FORCE_CONTEXT=1` overrides intentionally. ## Evals: the proof ```ts // flows/review.eval.ts import type { EvalCase } from "mdflow/src/evals"; const cases: EvalCase[] = [ { name: "flags the planted bug", setup: (dir) => { // write fixtures into the sandbox the flow will run in }, check: ({ stdout, dir, exitCode }) => { if (exitCode !== 0) return `exit ${exitCode}`; return /file:\d+|bug|issue/i.test(stdout) ? null : "review missed the planted bug"; }, }, ]; export default cases; ``` `md eval flows/review.md` runs each case in an isolated temp workspace and records content-bound receipts in the trust ledger (`~/.mdflow/eval-results.json`). Run `md eval flows/review.md --plan` first; repetitions affect the paid invocation count. Keep the default case array statically resolvable so planning can inspect names/cost without importing executable suite code. An actual run requires interactive confirmation or `--yes`, so get the user's go-ahead. Check invariants (files, numbers, names), not exact wording. When a real run disappoints, record it with `md feedback`, then add a feedback-linked case. Nonzero exits fail by default, timeouts are inconclusive, and mixed repeated trials are flaky rather than clean. The workspace isolation is not a network, process, or credential sandbox; the selected engine still receives the environment and capabilities its adapter allows. ## Feedback and proposal-first evolution Never describe Evolve as self-editing or auto-applying. Follow this sequence: ```bash md feedback flows/review.md "missed the renamed-file regression" md feedback show md feedback distill # private, deliberately failing draft md feedback forget --yes # permanent privacy deletion md eval flows/review.md --plan md evolve plan flows/review.md # free; shows proof, capabilities, cost, writes md evolve propose flows/review.md # paid only after consent md evolve show # inspect decision and prompt/capability diff md evolve apply # separate explicit source mutation ``` Review generated eval code before copying it into a suite: evals are executable TypeScript. Set `evidence: ["fb_..."]` on a case that reproduces the report. Only current-fail/proposal-pass on such a case is a verified improvement. A green uncovered candidate is only regression-safe. Proposals and receipts are private/off-path; the canonical flow remains byte-identical until explicit apply. New command/import/network/file capabilities are blocked before candidate execution. `evolve: auto` is a compatibility alias for queued proposal-only work. Prefer this project default: ```yaml engine: evolve: mode: suggest ``` `--no-evolve` or `MDFLOW_EVOLVE=off` disables post-run handling. Do not enable unattended apply: it is intentionally not available. ## Workflow for "add an agent for X" 1. Scaffold `./flows` if missing (directory, README.md index, `.mdflow.yaml`). 2. Write `flows/.md`: `description:` frontmatter, tight prompt body, rich imports for the context the job needs. - If it is a waiting interactive specialist, apply the exact shape above; keep all stable context in the instruction layers and the body task-only. 3. Measure for free: `md flows/.md --_context` for the token breakdown, `--_dry-run` for the command plan and safe prompt preview. Inline commands and executable code fences are shown but not executed; file, URL, and context-provider imports may still resolve. Report the size. 4. Write `flows/.eval.ts` with 1 to 3 behavioral cases. 5. Update `flows/README.md`. 6. Show `md eval flows/.md --plan`, then offer the separate paid flow and eval runs. Never infer consent for one from consent for the other. For every waiting interactive specialist, also run `md explain ` and `md --_interactive --_dry-run`. Reject the generated flow unless both instruction layers are present, the final prompt is blank without `_task`, and the command has no empty or placeholder positional prompt. Inspect the source body too: after frontmatter it must contain only `{{ _task }}`. ## Migrating v2 files Move loose agent .md files into `./flows`. `tool:` frontmatter becomes `engine:` (old key warns). `--_command`/`--tool` flags become `--engine`. Do not mass-rename Gemini flows: the `gemini` adapter remains valid for Code Assist Standard/Enterprise, while `agy` is the successor for individual accounts. Migrate only after `md doctor --json` and the user's environment confirm the intended engine. Bare `task.md` now runs on the resolved engine instead of erroring; frontmatter-less files print as documents.