DocsUsing mediva

CLI

Run mediva check in a terminal or CI — validate files against a contract, get an exit code and exact diagnostics.

MOTHER, the Nostromo's computer, never sleeps. She doesn't argue, doesn't editorialize — she scans what's in front of her and reports exactly what's wrong. mediva check is your MOTHER for Markdown: point it at a file, get back a verdict and an exit code.

npx mediva check <files...> --schema <contract.mdv.md>

Examples

# one file
npx mediva check README.md --schema readme.mdv.md

# zero-config scan: every X.mdv.md beside X.md
npx mediva check

# shell-expanded files — sweep the whole docs deck at once
npx mediva check docs/**/*.md --schema docs.mdv.md

# stdin — validate agent output with no temp file
some-agent-command | npx mediva check - --schema pr.mdv.md

stdin

Pass - as the (only) target to validate a document piped in on stdin instead of a file on disk — useful when what you're validating is a string an agent just produced, not something already written to a file:

echo "## Summary" | npx mediva check - --schema pr.mdv.md

- always requires an explicit --schema (or its --template alias) — with no file path to walk from, the mirror/sibling/_template.mdv.md discovery conventions have nothing to anchor to. Diagnostics for the piped document are labeled <stdin> in place of a filename — a coincidentally similar --schema/--context value is never affected, since only the piped document's own content is substituted. - must be the only target (mixing it with real file paths is a usage error, exit 2), and running mdv check - with nothing piped into an interactive terminal fails immediately with a clear message rather than hanging. Piped input is capped at 32MB — well past any real Markdown document — and rejected past that with a clear error rather than exhausting memory. Note that - is reserved for stdin: a real file literally named - needs a path spelling like ./- to be checked as a file.

The zero-config scan

Run mediva check with no file arguments and it sweeps the deck like MOTHER walking the ship: it walks the current directory downward and validates every X.md that has a sibling X.mdv.md contract beside it. No config file, no glob list to maintain.

What the scan skips while it walks:

  • node_modules and dist — dependencies and build output are never scanned.
  • Any hidden directory (a name starting with .): .git, .next, editor state, .claude worktrees, and so on.
  • A directory whose .mdvignore contains a * line — that prunes the whole subtree. Use this for intentionally-invalid fixtures that the zero-config scan must never enter. Pruning everything is destructive (it turns a red run into "nothing to check"), so it has to be spelled out: a .mdvignore with no usable patterns — empty, comment-only, or negation-only — prunes nothing and says so on stderr.
  • A pattern .mdvignore installs gitignore-style *, ?, and ** patterns relative to its directory and keeps walking; only matching files or directories are skipped. Nested files add another pattern scope. (! negation is not supported in v1.)
  • Symlinked directories are not followed, so a symlink loop can't hang the walk.

A pruned area is still reachable by explicit path: npx mediva check .git/HOOKS.md --schema h.mdv.md validates a file the bare scan would skip. The scan only pairs true siblings — an X.mdv.md with no matching X.md is left alone, and the CLI does not expand globs itself (let the shell do that, as in the last example above).

Output

By default mediva prints human diagnostics and a summary — a calm readout on the bridge display:

README.md
  3  error  Empty section  empty-section
      The "Installation" section has a heading but no content under it.
      fix: This heading promises content to the reader. Write the actual details for this section. If no information genuinely exists yet, write `Not specified.` — do not invent content to fill the gap.

contract: README.mdv.md
    Restructuring on purpose? Update the contract in the same change so document and contract move together. One-off exception: <!-- mdv: disable-next-line <rule> -- reason -->

✖ 1 problem (1 error, 0 warnings)

A clean run prints nothing at all and exits 0 — mediva only speaks when something needs fixing. A run with warnings but no errors still exits 0; the footer turns yellow instead of red.

Colour

On a terminal, severities are coloured (error red, warning yellow), the file header is underlined, locations and rule codes are dimmed, and each rule code is a clickable link to its reference page. Piped or redirected output contains no escape bytes of any kind — colour and links ride the same switch — so captured output stays diffable.

SettingEffect
stdout is a terminalcoloured (the default)
stdout is a pipe or fileplain
FORCE_COLOR=1coloured even through a pipe
FORCE_COLOR=0plain, explicitly
NO_COLOR=1plain, anywhere
both FORCE_COLOR and NO_COLOR setFORCE_COLOR wins (the chalk/Node ordering)
--color / --no-coloroverride everything above

CI runners never allocate a terminal, so mediva's output there is plain by default. Set FORCE_COLOR: "1" on the step to keep severity colours in your workflow logs — see Mediva CI.

Add --json for machine-readable output (stable diagnostic codes), suitable for tooling — pipe it straight into your own dashboard:

npx mediva check README.md --schema readme.mdv.md --json | jq '.results[].diagnostics[].code'

SARIF

--sarif makes check emit SARIF 2.1.0 for code-scanning upload, including the schemastore $schema, runs[].tool.driver.name = "mediva", rule descriptors, and defaultConfiguration.level mapped from mediva severity (errorerror, warnwarning). It is a check output mode, and it takes precedence if both --json and --sarif are passed:

npx mediva check README.md --schema readme.mdv.md --sarif > mediva.sarif

Exit codes

  • 0 — no errors. Self-destruct averted; bay doors open.
  • 1 — one or more errors.
  • 2 — usage or configuration error: unknown command, bad option, missing value, unreadable file.

That makes mediva check a drop-in CI gate. See Recipes → PR bodies for a GitHub Actions example, issue forms for --title, monorepo CI for the zero-config scan, and changelogs for explicit-file checks.

Commands

  • mdv check [files...] [options] — Validate explicit Markdown files, or auto-discover every X.mdv.mdX.md sibling pair when no files are passed.
  • mdv fix [files...] [--dry-run] [--json] — Apply deterministic fixes (the same ones the LSP's "Fix all mdv problems" serves) to explicit files, or every auto-discovered X.mdv.mdX.md pair, and write the result back to disk.
  • mdv author-check <contract.mdv.md> [--json] — Check a contract's OWN authoring quality: compiles clean of every fail-closed schema-author diagnostic, its own body validates as its own worked example, and every value its exemplars manifest declares actually occurs in that body. Exit 0 only when all three gates pass.
  • mdv render --schema schema.mdv.md [-o out.md] — Strip mdv directives from a contract and emit plain Markdown.
  • mdv highlight file.md [--color|--no-color] — Colourise mdv directives for a terminal.
  • mdv explain <rule|field-kind|path> — Explain a rule or field kind, or show which contract governs a Markdown path and the rules attached to each heading.
  • mdv comment mdv.json --kind pr|issue [--marker HTML] [--grace DAYS] — Render check --json output as a Mediva CI pull-request or issue comment.
  • mdv init [dir] — Scaffold the built-in GitHub/README starter into a directory you own.
  • mdv plugin validate <module-or-path> — Validate a plugin package against mediva's enforced manifest shape.
  • mdv lint-fences [paths...] [--json] — Compile every mdv fence and *.mdv.md corpus template under the given paths against its owning plugin registry.
  • mdv lsp [--stdio] — Start the language server (stdio): live diagnostics, hover docs, and completion for mdv directives in any LSP-capable editor.
  • mdv ground [--status] — Run the config ground manifest (mediva.config.mjs) and materialize each logical source name to disk (.mdv/<name>.json by default); check then binds the name to that file automatically. --status reports each output's age without regenerating. mdv check --ground runs this first as a convenience. (deprecated: use mdv sources)
  • mdv sources <sync|status|list|doctor> — Work with the config ground/sources.declare binding surface: materialize producers, check freshness, list every known binding, or cross-check bindings against what discoverable contracts actually reference. Replaces mdv ground.

Options

  • -h, --help — Show CLI help.
  • -v, --version — Show the installed mediva version.
  • --json — Emit machine-readable JSON diagnostics for check, fix, or lint-fences.
  • --sarif — Emit SARIF 2.1.0 diagnostics for check.
  • --list — Print every checked document and its contract, not only failures.
  • --dry-run — With fix, report what would change without writing any file.
  • --schema FILE — Use an explicit contract for check, fix, or render.
  • --template FILE — Alias of --schema for check, render, and show.
  • --title TEXT — Supply the PR, issue, or document title validated by title rules.
  • --context FILE — Load JSON title, issue, and value context for check.
  • --now DATE — Inject the ISO date or datetime read by freshness rules; explicit context values.now wins.
  • -o FILE — Write render output to a file instead of stdout.
  • --color — Force ANSI colour for check or highlight, even when output is piped.
  • --no-color — Disable ANSI colour for check or highlight.
  • --config FILE — Load plugins from an explicit config instead of cwd auto-discovery.
  • --no-plugins — Skip config discovery and use only built-in rules.
  • --no-providers — Skip config providers during check (only their membership-data use is deprecated; context-value facts still flow through them).
  • --resolver URL — Override the config's external-reference resolver endpoint.
  • --resolver-snapshot FILE — Replay an existing resolver snapshot, or resolve live and write one.
  • --source-snapshot FILE — Replay an existing contract-source capture; if FILE is missing, note it and record instead (use --record-sources to make recording explicit).
  • --record-sources FILE — Always resolve sources live and write the versioned capture envelope to FILE, even if it already exists.
  • --allow-capture-drift — Replay a --source-snapshot capture even when a checked schema's recorded contract no longer matches its current bytes.
  • --source NAME=FILE[,...] — Pin a declared logical source NAME to a local FILE for this run — wins over the config loader and over a snapshot replay.
  • --kind pr|issue — Select the target comment kind for comment.
  • --marker HTML — Set the stable hidden marker for a generated comment.
  • --grace DAYS — Include an issue grace-period note in a generated comment.

The ground/sources verbs, --source, and the capture flags are the CLI face of the grounding model — logical source names a contract references and a host binds. See the grounded templates guide for the spellings, requiredness, binding precedence, and CI recipe.

Plugins

check, explain (including explain <path>), and highlight auto-discover mediva.config.mjs (or mediva.config.js) — no flag needed — and load whatever plugins it declares into the rule registry before validating. Discovery walks up from the invoked directory to the nearest config, stopping at the git root (a run from a project subdirectory loads the same config CI loads at the repo root); outside a git repository only the invoked directory is consulted. A config file is executable code loaded via dynamic import(), so treat it with the same trust as any dependency; --no-plugins is the escape hatch for a checkout you don't trust. See Authoring plugins for how to write one.

External context

Rules that inspect the outside world need a little telemetry from your ship. issueState=open checks a Fixes #N reference against context.issues; without context, mediva reports missing-context instead of guessing.

Pass that context as JSON:

{
  "issues": [{ "number": 123, "state": "open" }],
  "title": "Ship the docking checklist"
}
npx mediva check PR.md --schema pr.mdv.md --context context.json

--title "..." is the shortcut for setting context.title, which is what the title* rules validate. There is no first-#-heading fallback: if neither --title nor a context.title is supplied, a title rule fails with missing-title. When the title you want to check is the document's H1, either pass it with --title, or enforce the heading in-document with a section heading.level=1 heading.pattern=/…/ instead of a document title rule.

On this page