Skip to content

Command reference

The adr binary (published as @adrkit/cli) is a small set of deterministic commands. Run adr with no arguments or adr --help for task-oriented command discovery and common workflows. Run any command with --help, or adr help <command>, for its arguments, options, examples, and exit codes. Compact synopsis:

adr lint [paths...] [--json] [--dir docs/adr]
adr migrate --from madr [--dir docs/adr] [--dry-run] [--rename] [--json]
adr new <title> [--status draft] [--dir docs/adr] [--json]
adr graph [--dir docs/adr] [--format auto|terminal|dot|json|mermaid]
[--focus ID] [--kind supersedes|relatesTo|conflictsWith]
adr explain <path> [--dir docs/adr] [--json]
adr check <files...> [--dir docs/adr] [--json]
adr evaluate <proposal-path> --snapshot <bundle.json> --date YYYY-MM-DD [--json] [--dir docs/adr]
adr queue [--dir docs/adr] [--as-of YYYY-MM-DD] [--format markdown|json]
adr completion <bash|zsh|fish>
adr help [command] Show help, or help for one command
adr --version Print the @adrkit/cli version
adr --color <mode> <command> [options] Colorize human-readable output

Running adr with no arguments prints the top-level help to stdout and exits 0. The help groups commands by the task they perform, includes common workflows, and points to this reference. adr help <command> and adr <command> --help print the same focused command help.

Usage errors stay on stderr and exit 2. Instead of appending the full global help, a command error prints that command’s usage and points to adr help <command>. Near-miss command names and long options suggest the closest documented spelling. The closed value sets for new --status, graph --format, migrate --from, queue --format, and completion <shell> do the same:

Error: Unknown option "--jsn". Did you mean "--json"?
Usage: adr lint [paths...] [options]
Run 'adr help lint' for more information.

Suggestions are conservative: an unrelated command, option, or value is rejected without guessing.

--color auto|always|never is a global option. Put it before the command for the clearest invocation:

Terminal window
adr --color always lint

Both --color always and --color=always are recognized anywhere before the -- argument terminator. After --, --color is an ordinary positional value.

Mode Meaning
auto Default. Color each human-readable output stream only when that stream is a TTY and NO_COLOR is unset.
always Force color for human-readable output, including redirects and pipes; this explicit choice overrides NO_COLOR.
never Disable color.

Stdout and stderr are detected independently, so redirecting one does not change the other. Color changes presentation only: exit codes and the stdout/stderr split are unchanged. Machine-oriented output remains ANSI-free even under --color always, including JSON, DOT, Mermaid, generated completion scripts, and the canonical queue Markdown format.

Validate the ADR corpus. With no paths, every discoverable record under --dir is checked.

Option Meaning
--dir <path> ADR corpus directory (default: docs/adr)
--json Emit { checked, findings } as JSON
--help Show help and exit

Exit codes: 0 no error findings · 1 one or more error findings · 2 usage error (invalid invocation or unreachable corpus directory).

Migrate a MADR corpus in place, adding adrkit frontmatter and leaving the body untouched. One-way and non-destructive; round-trip sync is unsupported (ADR-0008).

Option Meaning
--from madr Source format (required; only madr is supported)
--dir <path> ADR corpus directory (default: docs/adr)
--dry-run Report what would change without writing
--rename Rename each migrated file to <id>-<slug>.md so corpus discovery can see it
--json Emit the migration result as JSON
--help Show help and exit

Exit codes: 0 migration ran (findings are reported but do not fail the run) · 2 usage error (missing or unsupported --from, unknown flag, positional argument, or unreachable corpus directory).

Scaffold a new ADR record.

Option Meaning
--status <status> Initial status (default: draft)
--dir <path> ADR corpus directory (default: docs/adr)
--json Emit { id, path } as JSON
--help Show help and exit

Exit codes: 0 created · 1 refused to overwrite an existing file · 2 usage error.

Render the decision graph (supersedes, relatesTo, conflictsWith) for the corpus.

Option Meaning
--dir <path> ADR corpus directory (default: docs/adr)
--format auto|terminal|dot|json|mermaid Output format (default: auto)
--focus <id> Keep one ADR, its incident relationships, and their endpoints
--kind <kind> Keep supersedes, relatesTo, or conflictsWith; repeat to include more kinds
--help Show help and exit

auto selects the compact terminal instrument only when stdout is a TTY. Pipes, redirects, captured subprocesses, and CI continue to receive deterministic DOT, so existing adr graph | ... workflows do not change. Explicit formats always win.

The terminal view shows status and relationship counts. Sparse and focused graphs include their relationship map; dense corpora show the most connected decisions and point to --focus/--kind instead of drawing an unreadable hairball. Large sparse and focused views are bounded as well; select another kind or a machine format for the complete edge set.

Terminal window
adr graph
adr graph --focus 0014
adr graph --kind supersedes
adr graph --kind supersedes --kind conflictsWith --format json
adr graph --format mermaid > decisions.mmd
adr graph --format dot | dot -Tsvg > decisions.svg

DOT, Mermaid, and JSON are deterministic and ANSI-free. The JSON envelope stays { nodes: [{ id, title, status }], edges: [{ from, to, kind }] }.

Graph still emits the complete projection of valid records when the corpus also contains invalid records. Those error findings go to stderr.

Exit codes: 0 rendered without corpus errors · 1 rendered with one or more corpus errors · 2 usage error (invalid invocation, unreachable corpus directory, unknown focus id, or invalid format/kind).

Report which decisions govern one repo-relative path, and why.

A decision reaches a path in two directions, and explain keeps them apart:

Decisions governing src/services/sync/retry.ts:
0009 [accepted] Resolve affects deterministically
via path: src/services/sync/**
0012 [accepted] Bind catalog entities to owned paths
declared by src/services/sync/retry.ts:3 (@adr 0012)

via is the record reaching out through its own affects pattern. declared by is the file reaching in with an @adr marker — see inbound @adr markers below.

Option Meaning
--dir <path> ADR corpus directory (default: docs/adr)
--json Emit { path, governedBy, governing, activeProposals, history, markers, findings }
--help Show help and exit

Exit codes: 0 explained · 1 corpus has error findings · 2 usage error (invalid invocation or unreachable corpus directory).

A record declares the paths it governs with affects. That pattern has to be broad enough to cover a subsystem, and breadth is what costs an agent context. An inbound marker lets affects stay narrow: any other file opts in with one line, at zero cost until something reads that file.

// @adr 0012
export function syncOnce() { … }
  • The grammar is @adr followed by whitespace and an ADR id (0012, or payments:0012 for a federated log). A comma continues a list — @adr 0012, 0013. A bare space ends it, so @adr 0012 1234567 is one declaration followed by a number, not two declarations.
  • Any language. adrkit does not parse your file. A marker must be on its own comment line: after optional whitespace, one of //, /*, {/*, *, #, --, ;, %, <!--, """, ''' begins the line and @adr is the comment’s first content. A trailing } // @adr 0012 does not declare the file.
  • Not inside a fenced block. A marker between backtick or tilde fences is an example of the syntax rather than a use of it, so documenting the feature does not make your documentation governed by the decision you used as the example. A fence closes only on a line of at least as many of the same character and nothing else, and an unclosed fence runs to the end of the scanned window (ADR-0023).
  • In markdown, use a markdown comment. In .md, .mdx, and .markdown only <!-- and {/* declare. # is a heading and * is a list bullet: they render, so they are content rather than comments, and # @adr 0012 does not declare the file. Every other introducer above belongs to a source language and is ignored there.
  • At most the first 8192 bytes of a file are scanned, and the scan stops at the last complete line inside that bound — --json reports the extent it reached. A marker is a claim that the file lives under a decision; a mention far down the file is prose about that decision.
  • At most 64 declarations per file are retained in physical/source order. --json reports the exact total and omitted count, and human output says when declarations were omitted.
  • Nothing enters the record. The edge is discovered when you run explain or check. There is no schema change and no new affects matcher type (ADR-0021).

A marker naming a record the corpus does not have is a dangling-marker finding at warn. A log-qualified marker is marker-unresolvable at info — inert against this corpus, not broken. A marker naming a superseded, rejected, or deprecated record is a stale-marker warning. It still binds to that historical record, and a superseded chain names its terminal live successor when one can be resolved; adrkit never silently substitutes the successor. None of these marker findings changes the exit code.

The --json output reports what was actually scanned, so an empty result never has to be guessed at:

"markers": {
"state": "scanned",
"windowBytes": 8192,
"scannedBytes": 1582,
"fileBytes": 1582,
"truncated": false,
"declarationLimit": 64,
"totalDeclarations": 1,
"omittedDeclarations": 0,
"declared": [{ "ref": "0012", "line": 3 }]
}

state is scanned, absent (no such file), unreadable (not a regular file, or permission denied), or out-of-tree. truncated is true when the file is longer than the header window, so a marker below it was never seen — and truncated: false is what makes declared: [] mean “nothing declares this path” rather than “we stopped reading first”.

scannedBytes and fileBytes report the observed scan extent. When truncated is true, use Math.max(0, fileBytes - scannedBytes) to estimate the unscanned remainder; the values are measured separately and may differ if the file changes during the scan.

In governedBy, a pattern match carries firedMatchers and a file declaration carries declaredBy; declaredBy is omitted entirely when nothing declared the record.

<path> is repo-relative, the same contract affects patterns are matched against. Absolute and traversing paths are out-of-tree. Every symlink is refused as unreadable before its target is resolved, including symlinked parent directories and safe in-tree symlinks, so a fork PR cannot probe target existence or permissions.

adr check and the governing-decisions Action scan markers from the changed paths before calling the pure resolver. They normalize, deduplicate, and sort paths; scan the first 3,000 with at most 16 reads in flight; and expose exact absent and skipped paths through markerScan. Paths beyond the cap still take part in affects matching, but their markers are not read. A non-blocking marker-scan-capped warning makes that loss explicit.

After reads complete, the batch retains at most 10,000 declarations in code-unit path then physical/source order. markerScan.declarations reports exact total, retained, per-file-omitted, and batch-omitted counts. Any overflow collapses into one marker-declarations-capped warning rather than one finding per dropped declaration.

The cap is GitHub’s own changed-file ceiling. The Action refuses to evaluate a pull request whose file list hit that ceiling, so every diff it does answer is scanned completely and only a local adr check can reach the cap.

Report the decisions governing a set of changed files, and validate any changed records. Deterministic and provider-agnostic — the primitive the CI Action builds on.

Option Meaning
--dir <path> ADR corpus directory (default: docs/adr)
--json Emit the CheckOutcome as JSON
--help Show help and exit

Exit codes: 0 ok · 1 a changed record has an error finding · 2 usage error (invalid invocation or unreachable corpus directory).

Marker declarations render as declared by, separately from record-authored via matchers. --json includes markerScan state counts and exact unavailable, truncated, and skipped path lists. Marker-derived information never changes the exit code; only validation errors on changed ADR records do.

Run the deterministic evaluator over one proposal against an offline snapshot bundle. The evaluator routes; it never approves, persists, or writes. There is no --write.

Option Meaning
--snapshot <path> Snapshot bundle (required)
--date <date> Evaluation date, YYYY-MM-DD (required)
--dir <path> ADR corpus directory
--json Emit the evaluation report as JSON
--help Show help and exit

Exit codes: 0 evaluated (including warn/info/inert and escalation) · 1 the proposal was returned on a rubric error · 2 usage error, unreachable corpus directory, or malformed snapshot bundle.

Emit the ARB operations queue report for the local ADR corpus to stdout — a read-only, deterministic projection of the corpus’s review metadata. Identical inputs produce byte-for-byte identical output. See Use in CI for the managed-issue Action.

Option Meaning
--dir <path> ADR corpus directory (default: docs/adr)
--as-of <date> UTC calendar date for SLA computation (default: today, UTC). Accepts YYYY-MM-DD or an ISO datetime with an explicit timezone (e.g. 2026-01-08T00:00:00Z); timezone-less datetimes are rejected.
--format markdown|json Output format (default: markdown)
--help Show help and exit

Exit codes: 0 report, no error findings · 1 report emitted with error-severity corpus findings · 2 usage error (invalid flag/value or unreachable corpus directory).

Write a deterministic completion script for Bash, Zsh, or Fish to stdout:

Terminal window
adr completion <bash|zsh|fish>

The generated definitions cover top-level commands, each command’s documented options, and closed value sets such as --format and --status. They register both adr and the adrkit binary alias where the shell format permits.

Argument or option Meaning
<shell> Required; one of bash, zsh, or fish
--help Show help and exit

Exit codes: 0 script emitted · 2 usage error (missing or unsupported shell).

Shells commonly lazy-load completion definitions by filename. Install the same generated script under both the adr and adrkit names so completion works regardless of which binary you type first.

Terminal window
mkdir -p ~/.local/share/bash-completion/completions
adr completion bash > ~/.local/share/bash-completion/completions/adr
cp ~/.local/share/bash-completion/completions/adr \
~/.local/share/bash-completion/completions/adrkit

This location is loaded by bash-completion. Reload the shell after installing the files. For only the current shell, source the generated script directly:

Terminal window
source <(adr completion bash)
Terminal window
mkdir -p ~/.zsh/completions
adr completion zsh > ~/.zsh/completions/_adr
cp ~/.zsh/completions/_adr ~/.zsh/completions/_adrkit

Add the directory to fpath before your existing compinit setup, then reload the shell:

Terminal window
fpath=(~/.zsh/completions $fpath)
autoload -Uz compinit
compinit
Terminal window
mkdir -p ~/.config/fish/completions
adr completion fish > ~/.config/fish/completions/adr.fish
ln -sf ~/.config/fish/completions/adr.fish \
~/.config/fish/completions/adrkit.fish

Copying adr.fish to adrkit.fish works too. Fish discovers either file automatically; no shell restart is required.

adr help prints the global synopsis; adr help <command> prints one command’s help (identical to adr <command> --help). adr --version (or -V) prints the @adrkit/cli version. Both exit 0.