The wright command model is defined once with clap. The same model drives
argv parsing, generated help, and shell completion:
# Pure completion generation (stdout)
wright completion bash
wright completion zsh
wright completion fish
wright completion powershell
# Automatic or explicit user-local completion installation
wright completion install
wright completion install zsh
wright completion install --dir <DIR>
wright completion install --dry-run
wright completion install --all
Wright manages its completion lifecycle directly so installations and updates provide working completions without duplicating shell filesystem logic or mutating shell startup configuration:
wright completion <shell>: generates pure static completion scripts for Bash, Zsh, Fish, or PowerShell (pwsh) directly to stdout for manual setup or package-manager packaging.wright completion install [SHELL]: detects the current shell (or takes an explicit shell/--shelloverride) and installs generated completion into the standard conventional user-local directories without modifying.zshrc,.bashrc, or user startup scripts.--dir <DIR>overrides the target directory.--dry-runprints planned installation paths without writing files.--allinstalls/refreshes completions for all supported shells.--forceforces re-writing existing completion files.
- Shell detection: resolves
$WRIGHT_SHELLfirst, then inspects$SHELLpath/filename, followed by shell environment variables (ZSH_VERSION,BASH_VERSION,FISH_VERSION,PSModulePath), falling back to PowerShell on Windows. - Conventional installation locations:
- Fish:
~/.config/fish/completions/wright.fish(or~/.local/share/fish/vendor_completions.d/wright.fish), automatically loaded by Fish. - Bash:
~/.local/share/bash-completion/completions/wright(or~/.bash_completion.d/wright), loaded automatically whenbash-completionis active. - Zsh:
$ZSH_CUSTOM/completions/_wright(if$ZSH_CUSTOMset),~/.oh-my-zsh/custom/completions/_wright(if Oh My Zsh exists),~/.zfunc/_wright,~/.zsh/completions/_wright, or~/.local/share/zsh/site-functions/_wright. - PowerShell:
~/Documents/PowerShell/Scripts/_wright.ps1(Windows) or~/.config/powershell/completions/_wright.ps1(Unix).
- Fish:
- Install and Update integration:
install.shinvokeswright completion installpost-installation and reports non-fatal guidance if automatic completion installation cannot run.wright updaterefreshes existing Wright completion files in conventional locations upon replacing the binaries so completions stay aligned with the installed command model.
- Package-manager boundaries: Homebrew, Scoop, and WinGet packaging
consume
wright completion <shell>generation using native package conventions rather than maintaining hand-written completion scripts.
Workflow commands accept these CLI-only presentation options:
--format text|jsonselects human or machine output (-fremains an alias).--renderer auto|terminal|plain|github-actionsselects the presentation environment.autoselects GitHub Actions whenGITHUB_ACTIONSis truthy, plain output for genericCIor a non-TTY, and terminal output otherwise.--color auto|always|nevercontrols ANSI color. Explicit options take precedence over environment detection; GitHub Actions keeps workflow command lines free of ANSI even when color is explicitly requested.
check, analyze, lint, and inspect cost also accept the
finding-selection options --severity, --rule-id, --file, and --max
(#430), applied by the driver to reported diagnostics and lint findings.
Selection is a rendering concern only: it runs after the verdict and exit
code are fixed on the
complete set, so it can never turn a failing command into exit 0 or a
WARN verdict into PASS. Consecutive lint findings sharing a rule id and
message render as one entry listing all locations on the terminal renderer
(the plain renderer keeps one record per finding); when --max withholds
findings, text output states the withheld count and the JSON envelope carries
selection.total/selection.withheld beside the filtered array, plus
selection.byCode counting the filtered set per code before max truncates
it (#791).
The plain renderer — the default on a non-TTY or generic CI — renders every non-artifact report as line records: one fact per line, deterministic, and free of decoration, pluralized units, and wall-clock values. A record is
<kind>: <key>=<value> [<key>=<value> …]
except the leading verdict line, which is <STATUS> <command> [key=value …]. The status shares the renderers' tag set: PASS, WARN,
FAIL (a raised --fail-on verdict), ERROR (a genuine failure), USAGE
and UNSUPPORTED for exit 2/3 envelopes; a clean query command (inspect,
rules, symbols, refs, cfg, callgraph, cost, lookup, and the
read-only package-search, package-list, package-outdated) shows its
uppercase name in place of a vacuous PASS. Verdict fields carry the input
root= plus program kind=/locale= when the result reports an origin.
Records lead with the verdict, then summary: — the headline counts named
after their JSON fields (errors=, warnings=, notes=/findings=,
affected=, emittedBytes=, rules=, symbols=, reads=, writes=,
calls=, blocks=, edges=, packages=, …) — then the command's section records:
program:, rule:, symbol:, ref:, block:, edge:, fan-in:,
fan-out:, share:, statement:, byNode:, hotspot:, tree:,
complexity:, variable:, objects:, cost:, output: (written-path
plus sha256=), fix:/patch:/refusal: for lint --fix,
package: records for lint package (source=/status= lead, then
sha=/remote=/dir=/rules=/downloaded=/code=/message=/
description= as the row carries them), and entry:
records for lookup. diagnostic: records render envelope diagnostics on
stderr; result records stay on stdout, and artifacts keep byte-exact
stdout ownership. A failed analysis result is the exception: its verdict,
summary, and diagnostic: records all go to stdout, and stderr carries
only usage errors (exit 2) and internal failures (exit 4) (#891) —
non-envelope surfaces such as lookup keep their own split. A
diagnostic: carries <severity> <code> then
at=<path:line:col>, stage=, status=, source=, locale=, and
message=; a finding: carries the same shape plus count=/spanKind=/
expression= when the repeated member is present and evidence= when
reported. Locations are single at=path:line:col tokens — never a -->
line or source frame.
Key names share the JSON fields they mirror, and values stay bare unless
they contain whitespace, =, ", or \, in which case they are
double-quoted with \", \\, \n, and \t escapes. Unavailable
computations report unavailable plus the payload's own reason member
(elementCountUnavailable, emittedBytesUnavailable,
unavailableReason) — never a fabricated zero (ADR-0030 decision 3).
Truncation is explicit: truncated: <list>=<shown>/<total> [byCode=<code>:<n>,…] expand="<command>", and a selection closes with
selected: <noun>=<shown>/<total> [byCode=…] [withheld=<n> expand=…].
Run-cache and baseline comparisons render comparison:/change: records —
their values depend on .wright/ state and are the one output that varies
between runs on identical input (ADR-0027; off with --no-cache,
WRIGHT_NO_CACHE, or CI),
and next: records name follow-up commands — the shared list the result
carries (#792) first, then the wright lookup pointer a rejected-name
diagnostic implies. Non-envelope command errors use error:/unavailable: records
on stderr: update refusals are the record alone, while lookup request
errors also print the <STATUS> lookup verdict line on stdout.
JSON output is one wright-result/v1 envelope on stdout with no ANSI, progress,
or workflow commands. The convert source artifact and the compile -o -
program remain the only stdout payloads in text mode, including when GitHub
Actions presentation is selected; every other compile report is a verdict,
the written path, and the output SHA-256.
The GitHub Actions renderer — auto's choice when GITHUB_ACTIONS is
truthy — presents the same envelope three ways, so the platform's annotation
budget never drops information silently:
- Annotations are
::error/::warningworkflow commands on stderr — informational severities never draw a card. They are deduplicated by rule (title=, the diagnostic code or finding code) and location, ordered by severity, and bounded to the platform's ten-per-class per-step limit; when the bound withholds items one closing::noticestates how many and points at the log group and summary.file/line/col/endLine/endColumncarry the span when it maps to a real source path;fileis the same display pathat=records use, so it is repository-relative when wright runs from the checkout root — under a differentworking-directoryor--root, run wright from the repository root for GitHub to attach the annotation to the file. Property values escape%, CR, LF,:and,; messages escape%, CR, and LF. - The log group
::group::wright <command> — <STATUS> <headline>wraps the same line records the plain renderer prints (artifact streams still own stdout), so the full report — including items the annotation bound withheld — is one expandable group in the run log. - The step summary appends a Markdown section to
GITHUB_STEP_SUMMARY:## Wright \` — STATUS (exit N), the headline metadata, the leading annotated items (linked toGITHUB_SERVER_URL/GITHUB_REPOSITORY/GITHUB_SHAblob URLs when the runner exports them, a code span otherwise), the baseline comparison when one ran, and the full record report in a collapsedDetails
block bounded under the platform's 1 MiB limit. Without the env var the renderer falls back to a one-line::notice`.
The verdict, exit code, and counts match the plain renderer and the JSON
envelope: errors produce ERROR, warnings produce WARN, and info/
notice-only results produce PASS; a result raised by --fail-on produces
FAIL (exit 1).
Interactive terminal mode is TUI-lite by design. For text workflows selected
as terminal, Wright prints immediate activity feedback and then renders
truthful session phases such as input resolution, parsing, semantic analysis,
linting, emission, or conversion. A lightweight spinner starts only after a
short anti-flicker threshold; phase output is transient and is fully cleared
before the final verdict, diagnostics, report, or source artifact is rendered.
Completed check, lint, analyze, inspect, and the inspect query
subcommands (symbols, refs, cfg, callgraph, cost) print a
command-specific verdict tag and compact summary before details;
diagnostics and findings include a one-line source context when the reported
provenance path is readable. The driver exposes typed progress events through
ProgressObserver; no terminal strings, spinner frames, ANSI sequence, or
source context enters the driver envelope or JSON. Plain output,
redirected/piped output, TERM=dumb, CI, GitHub Actions, and explicit JSON
rendering remain static and deterministic — comparison:/change:
records excepted, which follow .wright/ state (ADR-0027).
On the terminal renderer only, a report leads with one filled element —
the verdict tag (#795, ADR-0030 decision 4). The tag word maps to the exit
code: PASS or WARN for clean results, FAIL for a --fail-on-gated
result, USAGE for exit 2, UNSUPPORTED for exit 3, and ERROR for
other failures; a successful query command shows its own name (INSPECT,
SYMBOLS, CFG, COST, LOOKUP, PACKAGE-SEARCH, PACKAGE-LIST,
PACKAGE-OUTDATED, …) rather than a judging word. A
status word names the command after it ( WARN lint); a query tag
stands alone. Colour is semantic and drawn from the eight ANSI hues only —
green pass, yellow warn, red error class, cyan neutral — but the word
alone carries the status, so the report reads identically with
--color never and under NO_COLOR. Sections then group the report:
headings name each area (stating uniform evidence once, such as
Lint findings (evidence: static-indicator), instead of repeating it per
item), items indent under their section, and locations, source frames,
and secondary notes indent under their item. Counts are plain plurals —
1 error, 2 warnings — never (s) suffixes, and a failed computation
reports unavailable with its reason rather than a fabricated value.
The layout caps at 100 columns regardless of terminal width; below 90
columns it switches to the compact layout where children of a section
item align at the item's column instead of indenting deeper, and
COLUMNS overrides the detected width. Commands that stream an operation
rather than produce a report (update) close their lines with the tag
instead of leading with it, and artifact commands (convert,
compile -o -) keep stdout pure, letting the tag and diagnostics ride
stderr only when there is something to report. The tag is a terminal
detail: lines keep the stdout/stderr stream they use elsewhere, plain,
GitHub Actions, CI, and JSON keep their existing undecorated layout, and
the reported findings, verdicts, exit codes, caps, and --all expansion
are identical across renderers.
The check report follows a human-first hierarchy (#443): the verdict line
leads with the blocking error count before the non-blocking severities, and
diagnostics render in action order (errors before warnings and info notes,
stable within a severity). On the terminal renderer a mapped diagnostic
carries its --> location plus a one-line source frame on the diagnostic
stream; on the plain renderer the same location is the record's at= field.
Spans whose path is not a readable source file, such as
<stdin>, <provider-artifact>, or a dangling <file N>, are never dressed
up as file locations; the reported position and the pipeline stage trail each
diagnostic as secondary metadata instead. A diagnostic whose code carries an
unknown- kind (a rejected Workshop or OverPy name) is followed by a
one-line hint: run 'wright lookup' ... pointer on check and compile
(#529, ADR-0021); an OPY-origin diagnostic names its vocabulary:
wright lookup --language opy (#692). The plain renderer carries the same
follow-up as a next: record. The hint is text presentation only
and never enters the envelope. A compact footer closes the terminal report
with the count of affected source files and, on an interactive terminal, the
elapsed workflow time; the plain renderer folds the same affected count into
its summary: record and omits wall-clock values so it stays
deterministic. Severity ordering, secondary metadata, the lookup hint, and
the footer are presentation-layer concerns: the wright-result/v1 envelope
keeps the driver's diagnostic set and production order, and the GitHub
Actions renderer annotates the same set.
The lint report applies the same hierarchy to findings: the verdict
leads with finding counts by severity, findings render in action order, and a
finding entry leads with severity, rule id, and message before its location
(the terminal renderer's --> line and one-line source frame; the plain
record's at= field). Evidence class and boundedness trail
dimmed as secondary metadata on the terminal renderer and as evidence=
fields on the plain renderer; pseudo-path spans degrade to position notes.
On the terminal renderer repeated findings keep the shared collapse, one
entry naming its finding count and using the selected locations — the plain
renderer emits one finding: record per finding — and interactive elapsed
time stays presentation-only.
The wright-result/v1 envelope uses the same selected finding set,
which the GitHub Actions renderer annotates and summarizes.
The inspect queries follow the same human-first hierarchy (#446): on the
terminal renderer as stacked sections, on the plain renderer as the same
facts in record order. Bare wright inspect prints the program
inventory of file, rule, variable, subroutine, and finding counts, followed
by a bounded rule-name preview and the detail commands, so the first screen
answers "what is this" and routes deeper questions to the owning query — on
the plain renderer the detail commands are next: records. Each
subcommand leads with its subject and summary before its detail:
inspect symbolsgroups entries under their kind and prints each symbol's name beside its primarypath:line:collocation (symbol:records on the plain renderer).inspect refsprints the queried symbol's identity and usage counts in the verdict metadata (summary:fields on the plain renderer), then the reference list where each entry carries its location, kind, and rule/action index (ref:records).inspect cfgprints the queried rule's name, the graph shape (block, edge, loop-header, wait, and call-site counts; entry and exit blocks), then the block/edge listing (block:/edge:records).inspect callgraphprints the edge shape first, then notable fan-in and fan-out, that is, subroutines with more than one caller and rules calling more than one subroutine, then the edge list (fan-in:/fan-out:/edge:records on the plain renderer).inspect costprints exact generated-resource totals — the declared program rule countanalyzereports among them — the canonical element count against the client element budget with each rule's share ranked by count and located in source (path:line:col;share:records), then static findings ordered by severity with consecutive identical findings collapsed into one counted entry that lists every occurrence's location on the terminal renderer (#722). A failed computation is never a zero: a failed element count printselement count unavailable: <reason> — the 32768-element budget still applies, and failed emission printsemitted bytes: unavailable — <reason>(elements=unavailable/emittedBytes=unavailableplus theelementCountUnavailable/emittedBytesUnavailablereason fields on the plain renderer) (ADR-0030 decision 3). Skipped rule evaluations are disclosed likelint's (#788 —skipped=on the plainsummary:). With--rule,inspect costinstead prints that rule's identity and location, itsCostliest statementsranked by recursive count, and theBy node nameown-cost shares that reconcile to the rule's total (#724) (statement:/byNode:records).
Text and compact JSON present the same selected items. Detail lists default
to at most 20 items, with their total and an expansion hint when truncated —
a truncated: record naming the expansion command on the plain renderer;
--all lifts the default cap. The analyze report keeps its ranked top-five
hotspots, risks and variables and top-three condition trees, with the same
explicit expansion metadata. JSON is the structured form for programmatic
processing. Verdicts still describe the full set. When a query fails, its
verdict and diagnostics render without result metadata, so a default result
cannot masquerade as an answer.
This document is the normative contract for the compiler driver and CLI.
It defines the shared driver model, the command surface, exit codes,
stdout/stderr ownership, and the wright-result/v1 envelope that CI and
agents consume.