CLI Commands¶
Complete reference for rumdl command-line interface.
Commands¶
check [PATHS...]¶
Lint Markdown files and report issues.
rumdl check . # Lint current directory
rumdl check README.md docs/ # Lint specific files/directories
rumdl check --fix . # Lint and auto-fix issues
Options:
| Option | Description |
|---|---|
--fix |
Auto-fix issues (exits 1 if unfixable issues remain) |
--config <PATH> |
Path to configuration file |
--disable <RULES> |
Disable specific rules (e.g., MD013,MD033), or all |
--enable <RULES> |
Enable only specific rules |
--exclude <PATTERNS> |
Exclude files matching patterns |
--include <PATTERNS> |
Include only files matching patterns |
--no-code-block-tools |
Skip configured tools; keep checking the outer Markdown |
--only-code-block-tools |
Run configured tools; skip the outer Markdown |
--stdin-batch |
Read NUL-delimited path/content pairs from stdin |
--stdin-batch-closed-world |
Resolve batch links only within the supplied document set |
--stdin-batch-targets |
Read NUL-terminated link target paths that are not linted |
--watch |
Watch for changes and re-lint |
--verbose |
Show detailed output |
--quiet |
Print diagnostics, but suppress summaries |
--silent |
Suppress diagnostics and summaries |
--no-exclude |
Disable exclude patterns defined in config |
--stderr |
Write diagnostics to stderr instead of stdout |
--deny-config-warnings |
Treat configuration warnings as errors (exit code 2) |
Findings go to stdout, whether the document came from a path or from --stdin,
so --output-format json redirects the same way in both. --stderr moves them;
config warnings and errors are always on stderr. The exception is a document
rewritten on stdout - check --fix --stdin and fmt --stdin - where stdout
belongs to the document and diagnostics go to stderr. fmt --check,
fmt --diff and check --diff never write the document: they print its diff
where findings go, named by --stdin-filename (or <stdin>), and exit as they
do for a file.
A file the run leaves untouched, for conflict markers (MD092) or
invalid UTF-8 (MD094), is reported on that same stream. The one
place it moves is fmt --check and fmt --diff, whose stdout is a patch and
nothing else: there the notice joins the config warnings on stderr.
The closing summary is written for a person, so a machine-readable format never
carries one and needs no --quiet to keep its output parseable.
Batch stdin¶
--stdin-batch checks many caller-supplied snapshots in one process. The byte
stream is a repeated path NUL content NUL sequence and must end in NUL:
Paths must be UTF-8, non-empty and unique after path normalization. Content may be empty; content that is not valid UTF-8 is linted and reported as MD094 findings, as a file on disk would be. Each path is both the diagnostic name and the filesystem context used for configuration and relative links. A relative path is resolved against the working directory, and the file need not exist.
A document whose path matches an exclude pattern is not linted, exactly as
rumdl check <file> skips it, but it still counts as a link target for the rest
of the batch. --no-exclude lints it. A batch in which every document is
excluded reports an empty run.
By default, supplied documents take precedence and links to documents omitted
from the batch fall back to the on-disk workspace. Add
--stdin-batch-closed-world to prohibit that fallback. In both modes a link to a
directory resolves when any supplied path lies under it, so [docs](docs/)
passes when the batch supplies docs/guide.md. Batch input never reads
or writes the persistent workspace-index cache, because supplied content may
differ from the file saved at the same path.
Link targets that are not linted¶
Links usually point at files that are not documents: PDFs, images, data files.
Supplying each as an empty document makes rumdl configure and lint it. Instead,
list such paths in a file and pass it with --stdin-batch-targets FILE. The
file holds UTF-8 paths, each terminated by a NUL byte, like the batch itself,
so any path spelling is expressible:
git ls-files -z > targets
printf 'docs/a.md\0[spec](files/spec.pdf)\n\0' | rumdl check --stdin-batch --stdin-batch-targets targets
- A listed path exists for relative links in both open and closed world, and
its parent directories are implied as for supplied documents. An entry with
a trailing
/declares a directory, which may be empty. The list answers before the disk: withguide.markdownlisted, a link toguidenames it, even when aguide.mdexists on disk. - Paths resolve like batch document paths: relative to the working directory,
and
..and absolute paths are accepted. - Listed paths are not linted, not counted, and never read from disk. Their
headings are unknown, so a link with a
#fragmentinto a listed Markdown file is not reported by MD051. The exception is a file that is also reached through another, unlisted path (a symlink, for example): fragments in links written with that path are checked against the file's headings, and once it has been read, links written with the listed path may be checked too. - A path both listed and supplied in the batch is an ordinary batch document.
- An empty file means no targets. A missing or unreadable file,
-(stdin carries the batch), invalid UTF-8, an empty entry, or a final entry without its NUL is a tool error (exit code 2). The option requires--stdin-batch.
Batch mode is check-only and cannot be combined with paths, --stdin,
--stdin-filename, --fix, --diff, --check, or --watch.
Markdown embedded in other files¶
rumdl lints Markdown files and the doc comments of Rust files (Rust Doc Comments). Markdown that lives inside another language, such as doc comments or docstrings, can still be linted by a tool that extracts it with that language's own parser and pipes it to rumdl. rumdl does not ship such extractors.
Name each extracted snippet after its source file with a Markdown suffix, for
example src/Foo.java.L42.md for the comment starting on line 42. A source-code
name such as src/Foo.java is refused with exit code 2, because rumdl would
otherwise lint the whole source file as Markdown. The .md name keeps the
snippet in its source directory, so it picks up that directory's config, and a
per-file-ignores glob can tune the rules for snippets only:
Send many snippets in one process with --stdin-batch, giving each a unique
path, and read the results with --output-format json. Positions in the
output refer to the snippet as supplied (see Output Formats),
so the extractor maps them back: add the snippet's starting line minus one to
line, and add the number of characters it stripped from the start of that
line, such as the indentation and ///, to column.
Fix ranges address the snippet's bytes, so applying them to the source needs
the extractor's own per-line byte map. Linting without fixing avoids that.
fmt [PATHS...]¶
Format Markdown files (applies fixes like rumdl check --fix, but keeps formatter-style exit codes).
rumdl fmt . # Format all files
rumdl fmt README.md # Format specific file
rumdl fmt --silent - # Format stdin to stdout without diagnostics
Files with unsuppressed MD092 conflict markers are left
byte-for-byte unchanged. fmt reports an MD092 diagnostic and exits 0; check and check --fix
report it as an error and exit 1 (unless --fail-on never is set). Other files
are still processed. fmt --check and fmt --diff also skip conflicted files.
Use scoped rumdl-disable merge-conflict / rumdl-enable merge-conflict
comments around literal conflict examples to check and format the surrounding
documentation.
Files that are not valid UTF-8 are reported by MD094 and are also left byte-for-byte unchanged.
fmt --diff, fmt --check and check --diff print a unified diff per changed
file, line endings included, and leave the files alone. The fmt previews print
that diff alone, so it is a patch: rumdl fmt --diff . > fmt.patch followed by
git apply -p0 fmt.patch (or patch -p0 < fmt.patch) writes exactly what
rumdl fmt . would. check --diff is the report of the same preview and lists
the findings it would leave unfixed beside the diff.
Detection looks for opening or closing markers at the start of a line, with
at least seven < or > characters followed by whitespace or the end of the
line. It applies even inside code fences and to partially resolved conflicts;
literal conflict examples therefore also prevent formatting. A Setext underline
(=======) alone does not trigger detection. Editor formatting and fix actions
also leave conflicted documents unchanged.
The safeguard is a rule, so a selection that drops MD092 - disable in the
config, --disable MD092, an --enable list without it, or a scoped
rumdl-disable merge-conflict comment - drops the protection with it, and the
document is checked and formatted like any other.
Options:
| Option | Description |
|---|---|
--config <PATH> |
Path to configuration file |
--diff |
Show a diff of what would change instead of rewriting files |
--check |
Exit 1 if formatting changes would be needed |
--stdin |
Read from stdin |
--stdin-filename <NAME> |
The file stdin holds, for diagnostics and per-file config |
--no-code-block-tools |
Skip configured tools; format the outer Markdown |
--only-code-block-tools |
Format configured fenced blocks only |
--output-format <FMT> |
Output format for any remaining diagnostics |
--watch |
Re-run formatting when files change |
--quiet |
Print diagnostics, but suppress summaries |
--silent |
Suppress diagnostics and summaries |
--deny-config-warnings |
Treat configuration warnings as errors (exit code 2) |
Use --silent whenever stdout should contain only formatted Markdown. Plain rumdl fmt - may also emit remaining diagnostics.
--stdin-filename names the file the piped document is, so the settings that
apply to that file apply to it: the nearest per-directory config, exclude
patterns, per-file-ignores, per-file-flavor, and .editorconfig. A relative name is resolved against the
working directory, and the file need not exist yet. When the name matches an
exclude pattern, nothing is linted: fmt and check --fix write the document
back unchanged, and check reports an empty run. --no-exclude lints it anyway.
The code-block-tool mode flags are mutually exclusive and cannot be combined
with --stdin, --stdin-batch, or the - stdin path.
--only-code-block-tools drops the outer document's rules and leaves the tool
phases as they are, so fmt still runs configured lint tools and reports what a
formatter could not fix.
init [OPTIONS]¶
Create a configuration file.
rumdl init # Create .rumdl.toml
rumdl init --preset google # Use Google style preset
rumdl init --output custom.toml # Custom output path
Options:
| Option | Description |
|---|---|
--pyproject |
Generate configuration for pyproject.toml |
--preset <NAME> |
Use a style preset (default, google, relaxed) |
--output <PATH> |
Output file path (default: .rumdl.toml) |
import <FILE>¶
Import configuration from markdownlint.
rumdl import .markdownlint.json # Import from markdownlint config
rumdl import .markdownlint.jsonc # JSONC comments are supported
rumdl import .markdownlint.yaml # YAML also works
rumdl import --dry-run .markdownlint.json
rumdl import --format json .markdownlint.yaml --output rumdl-config.json
Options:
| Option | Description |
|---|---|
--dry-run |
Show the converted config without writing |
--format <FMT> |
Output format: toml or json |
--output <PATH> |
Output file path (default: .rumdl.toml) |
rule [<RULE>]¶
Show rule documentation.
rumdl rule # List all rules
rumdl rule MD013 # Show details for specific rule
rumdl rule line-length # Use rule alias
rumdl rule --list-categories # Discover rule categories
rumdl rule MD013 --output-format json
rumdl rule MD013 --output-format json --explain
Options:
| Option | Description |
|---|---|
--list-categories |
List rule categories and exit |
--category <NAME> |
Filter listed rules by category |
--fixable |
Show only fixable rules |
--output-format <FMT> |
Structured output such as json or json-lines |
--explain |
Include full documentation in JSON-based output |
config [OPTIONS]¶
Show effective configuration.
rumdl config # Show merged configuration
rumdl config --defaults # Show default values only
rumdl config --no-defaults # Show non-default values only
server¶
Start the LSP server.
See LSP Integration for details.
vscode¶
Install VS Code extension.
rumdl vscode # Install extension
rumdl vscode --status # Check installation
rumdl vscode --update # Update the installed extension
rumdl vscode --force # Force reinstall
version¶
Show version information.
Global Options¶
These options are commonly used with check and fmt:
| Option | Description |
|---|---|
--help, -h |
Show help |
--version, -V |
Show version |
--verbose, -v |
Verbose output |
--quiet, -q |
Print diagnostics, but suppress summaries |
--color <WHEN> |
Color output (auto, always, never) |
--no-config |
Ignore discovered configuration and use built-in defaults |
--output-format <FMT> |
Output format (see Output Formats) |
Exit Codes¶
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Lint violations found |
2 |
Configuration or runtime error |
fmt vs check --fix
rumdl fmtexits 0 whether or not violations remain (formatter mode). It exits2only when the run could not be completed: a file it could not read, or a code-block tool that could not run under afailsetting.rumdl check --fixexits 1 if unfixable issues remain
Failing on configuration problems
Configuration problems (an unknown rule or option in a config file or a CLI
flag, an unknown rule in an inline rumdl-disable-line comment, a shadowed
config file, a subdirectory config that could not be loaded, an
.editorconfig property rumdl cannot apply, or a run in which every Markdown
file found was filtered out) are non-fatal warnings by default and do not
affect the exit code. Pass --deny-config-warnings to make any of them exit
with code 2, so CI catches a typo'd rule name. This
is distinct from --fail-on, which governs the severity of Markdown
violations (exit 1); a config problem exits 2 and takes precedence over
Markdown violations.
When nothing gets checked
Checking zero files and checking every file cleanly both exit 0 with no
findings, so rumdl reports which one happened on stderr. A directory holding
no Markdown says so plainly; a run whose files were all filtered out instead
reports how many were found and which setting removed them:
No markdown files left to check: 12 files found were filtered out.
12 by ignore files (.gitignore, .ignore, git excludes); pass --respect-gitignore=false to keep them
Files hidden by .markdownlintignore get a line of their own, because
--respect-gitignore=false does not turn that file off: the notice says to
remove them from it or name them on the command line instead.
The notice never shares a stream with the selected output, so it stays out
of --output-format json and the other machine-readable formats: it goes to
stderr, or to stdout when --stderr routes diagnostics the other way. It
survives --quiet; use --silent to suppress it, or
--deny-config-warnings to fail the run instead.
Usage Examples¶
Basic Linting¶
# Lint all Markdown files
rumdl check .
# Lint specific directory
rumdl check docs/
# Lint with custom config
rumdl check --config my-config.toml .
Selective Rules¶
# Disable specific rules
rumdl check --disable MD013,MD033 .
# Enable only specific rules
rumdl check --enable MD001,MD003 .
# Disable every rule; an --enable list still survives
rumdl check --disable all .
Code block tools¶
Code block tools run external linters and formatters over fenced code blocks. They are configured separately from the Markdown rules, so either side can be run without the other while the rest of the configuration keeps applying:
# Markdown rules only, configured tools skipped
rumdl check --no-code-block-tools .
rumdl fmt --no-code-block-tools .
# Configured tools only, outer Markdown left alone
rumdl check --only-code-block-tools .
rumdl fmt --only-code-block-tools .
Reach for --only-code-block-tools rather than --disable all here. The latter
empties the rule set that fenced Markdown configured with lint = ["rumdl"] is
linted with, so the built-in tool reports nothing while external tools carry on.
--no-code-block-tools is the named form of
--config 'code-block-tools.enabled = false', and the mode flags win over an
inline --config that sets the master switch the other way.
Overriding configuration for one run¶
--config also takes an inline KEY = VALUE snippet, which overrides that
setting at the highest precedence while everything else the config files set
still applies. --no-config is the blunt alternative: it discards the whole
configuration.
# One rule option
rumdl check --config 'MD013.line-length = 20' .
# A global option
rumdl check --config 'line-length = 20' .
# A non-rule section: code-block-tools, per-file-ignores, per-file-flavor
rumdl check --config 'code-block-tools.enabled = false' .
rumdl check --config 'per-file-flavor."docs/**/*.md" = "mkdocs"' .
An override sets the settings it names to the values given, and settings it does
not name keep what they were configured with. code-block-tools holds several
settings, so --config 'code-block-tools.timeout = 60000' leaves the configured
languages and tools alone. per-file-ignores and per-file-flavor are each a
single setting whose value is a map of patterns, so naming one pattern replaces
the map - the same as writing that section in a higher-precedence config file,
and the same as ruff's --config for lint.per-file-ignores. To keep the
project's other patterns for a run, name them too.
A value the setting cannot hold is reported as a [config warning] naming the
key it came from; that value is skipped and the rest of the run proceeds. See
CLI --config overrides for
what is and is not checked.
File Filtering¶
# Exclude directories
rumdl check --exclude "node_modules,dist" .
# Include only specific patterns
rumdl check --include "docs/**/*.md" .
# Combine patterns
rumdl check --include "docs/**/*.md" --exclude "docs/drafts" .
Watch Mode¶
Stdin/Stdout¶
# Format from stdin
cat README.md | rumdl fmt --silent -
# With filename context
cat README.md | rumdl check - --stdin-filename README.md
# Format clipboard (macOS)
pbpaste | rumdl fmt --silent - | pbcopy
Output Formats¶
Control how warnings are displayed with --output-format:
rumdl check --output-format full .
rumdl check --output-format json .
RUMDL_OUTPUT_FORMAT=github rumdl check .
Human-readable formats:
| Format | Description |
|---|---|
text |
One line per warning: file:line:col: [RULE] message (default) |
full |
Source lines with caret underlines highlighting the violation |
concise |
Minimal: file:line:col rule message |
grouped |
Warnings grouped by file with a header per file |
Machine-readable formats:
| Format | Description |
|---|---|
json |
JSON array of all warnings (collected) |
json-lines |
One JSON object per warning (streaming) |
sarif |
SARIF 2.1.0 for static analysis tools |
junit |
JUnit XML for CI test reporters |
See Output Formats for the field-level reference for each machine-readable format.
CI/CD formats:
| Format | Description |
|---|---|
github |
GitHub Actions annotations (::warning/::error) |
gitlab |
GitLab Code Quality report (JSON) |
azure |
Azure Pipelines logging commands |
pylint |
Pylint-compatible format |
Example: full format output:
MD013 Line length 95 exceeds 80 characters
--> README.md:42:81
|
42 | This is a long line that exceeds the configured maximum line length ...
| ^^^
|
Example: text format output (default):
Preflight formatting¶
Use rumdl fmt --preflight PATH... or rumdl check --fix --preflight PATH...
to plan the entire selected batch before writing any file. rumdl first reads
all selected inputs and requires valid UTF-8, even if MD094 is disabled. It then
computes document fixes and external formatter output and checks for fatal
processing errors, non-converging document fixes, read-only output files, and
changes to input contents, targets, or permissions during planning. A failure
in this phase exits with code 2 and discards every planned write.
Successful plans are applied with atomic replacement for each file, preserving
its permissions and original line endings. Discovery resolves symlinks and
deduplicates target paths. Plans apply to those resolved targets; changing a
symlink afterwards does not redirect a planned write. External tools run during planning
and are not run again to apply the plan. Ordinary remaining lint findings do
not abort preflight formatting; configured fatal tool policies do. Configuration and coverage warnings
abort the batch when --deny-config-warnings is used. Execution warnings under
on-error = "warn" retain that warning policy.
This is a preflight guarantee, not a multi-file transaction. An I/O failure or
concurrent edit during the later apply phase can leave earlier files formatted;
rumdl reports how many writes completed and exits with code 2. Changes made by
external commands themselves are outside this guarantee. An edit between the
last per-file validation and its atomic replacement can still race with that
replacement. --preflight does not support stdin, stdin batches, watch mode,
--diff, or --check previews. Omit it to retain the usual streaming write
behavior.