Skip to content

Latest commit

 

History

History
426 lines (380 loc) · 27.9 KB

commands.md

File metadata and controls

426 lines (380 loc) · 27.9 KB

CLI architecture, commands, and conversion

← CLI contract index

Architecture

input (file | directory | `-` stdin)
    ↓  discovery: kind detection, locale, root, identity (wright-driver::input)
CompilerSession (wright-driver)
    ├─ source adapter: OPY | Workshop | protocol JSON
    ├─ validation (WIR)
    ├─ lowering (HIR → WIR)
    ├─ analysis (SemanticService: semantic facts, symbols, references, CFG)
    ├─ emission (Workshop text)
    └─ reconstruction (Workshop artifact → canonical OPY source through the
       provider's negotiated `reconstruct` capability, #126)
            ↓
   Envelope<T> (typed result + diagnostics + exit code; finding selection
   narrows reported sets without touching the verdict, #430)
            ↓
  `wright` CLI: text rendering | JSON serialization

The CLI is a thin argv/presentation layer. Library consumers construct a [CompilerSession] directly and receive the same typed envelopes; CLI JSON output is the serialization of that exact model, never a separately formatted result.

Commands

Command Purpose Text-mode stdout
wright compile [INPUT] [--to LOCALE] Parse, lower, validate, emit Workshop text; warn on known client import limits verdict, diagnostics, the written path, and the output SHA-256; the program goes to <input stem>.ow (or -o PATH; -o - prints only the program); --to emits in another Workshop client locale
wright convert [INPUT] --target opy|ostw Reconstruct validated Workshop input as canonical OPY or OSTW source through a provider that negotiates reconstruct (the shipped OPY provider does since 0.3.19; no OSTW provider is shipped) the reconstructed source
wright check [INPUT] Parse, lower, validate, and report correctness diagnostics verdict and validation diagnostics
wright analyze [INPUT] [--all] Summarize Workshop cost, ranked complexity hotspots, performance/stability risk indicators, and cross-cutting state overview, cost, top hotspots, complexity, risks, and top variables with exact/static/heuristic evidence labels; --all lifts list caps
wright lint [INPUT] [--all] [--fix [--write]] Parse, lower, lint; report findings finding counts by severity and actionable findings; --all lifts list caps; --fix previews each offered fix's validated diff, --write applies them
wright lint package <search|install|list|outdated|update|remove> Discover, install, and manage GitHub lint rule packages in the Wright-owned store keyed by commit SHA (#844); takes no INPUT and never activates a package per-package rows: source, status, SHA, entry directory, rule ids, and error detail (lint configuration and findings)
wright inspect [INPUT] [--all] Parse, lower, and inspect the program overview overview, rule names, and the detail commands; --all lifts the rule-list cap
wright inspect rules [INPUT] [--content] [--rule RULE] [--file PATH] [--max N] List rules; --content adds each selected rule's conditions and actions as canonical call trees (ADR-0024) the rule list, with content when requested
wright inspect symbols [INPUT] [--only KIND] [--file PATH] [--max N] List semantic symbols, optionally narrowed to one kind the symbol list with resolved locations
wright inspect refs <NAME> [INPUT] [--only KIND] [--rule RULE] [--file PATH] [--max N] References and usage counts for one symbol, addressed by name usage-count header plus the reference list
wright inspect cfg <RULE> [INPUT] [--only KIND] [--max N] Control-flow graph of one rule, addressed by name block/edge listing
wright inspect callgraph [INPUT] [--caller RULE] [--callee SUBROUTINE] [--max N] Subroutine call graph (caller rules to callee subroutines) call edges
wright inspect cost [INPUT] [--rule RULE] [--no-cache] [--baseline NAME] [--save-baseline NAME] [--max-total N] Exact generated-resource counts plus static findings; --rule drills into one rule's element cost, and by default the run compares against the previous run's element cost resource counts and findings plus the change against the previous run or named baseline, or the rule's statements and node-name shares
wright lookup [QUERY] Resolve a Workshop or OverPy display name, near spelling, or guess to the owner-accepted spelling and signature (#529); takes no input one line per entry: signature or spelling, kind, identity
wright serve [INPUT] [--transport T] [--kind K] [--profile P] [--lint-config YAML] [--rule-file PATH] [--disable-rule ID] [--rule-severity ID:SEV] Serve wright-agent/v1 over stdio, JSON-RPC 2.0, or MCP one structured response per request
wright completion <SHELL> Generate static completion script for bash, zsh, fish, or powershell the generated completion script
wright completion install [SHELL] Install generated completion into standard user-local directory installation progress and guidance
wright update [self|provider [NAME]] Update Wright-managed components: a standalone installation and installed first-party providers update progress (text only)
wright agent install Install the canonical Wright agent guide into the project's agent skills directory installation progress and guidance
wright agent tools [--format messages|json-schema] Emit the advertised operations as client tool definitions for code-executing agents wright-agent/v1 tool-definition document (JSON)

wright --version prints the implementation version banner (wright <version> (wright-driver <version>)); the version is the single authoritative workspace implementation version and is also reported inside every wright-result/v1 envelope. wright --help is the canonical help surface.

Commands that take input accept a file path, a project directory, or - for stdin (wright lookup, wright lint package, wright completion, wright update, and wright agent install take none). An omitted input uses the current directory. Input kind is detected from the extension (.opy, .ostw/.del, .json, .txt/.ws) or, for a directory, from the source files it contains; mixed source kinds fail with structured ambiguity guidance. Stdin content is auto-detected (protocol JSON starts with {, otherwise Workshop text). Detection can be overridden with --kind auto|opy|ostw|workshop|protocol. --locale overrides Workshop client-locale detection; compile --to <LOCALE> selects the Workshop output locale independently of the input locale (default: the input's locale); --root sets the include/project root; -o/--output chooses the compile output file. --profile <off|compat> selects the WIR transformation policy (default off) and applies to Workshop input only (ADR-0028, #725): passing it with an OPY or OSTW input is a usage error (exit 2) on compile, inspect, and the other input-taking commands, and in wright serve — at startup when --kind fixes the type, per request when the kind resolves from the input. .ostw/.del inputs remain recognized source kinds for a future provider, but Wright does not ship a static DEL/OSTW adapter. Every DEL/OSTW workflow fails with the structured source-provider-unavailable diagnostic (exit 4), without partial output or an upstream/static fallback. DEL/OSTW provider support is not currently shipped with Wright and is outside this contract. OPY, Workshop, and protocol inputs continue through their existing owner-backed paths.

wright compile output (ADR-0025)

wright compile writes the program to a file and reports only status and problems: the verdict, diagnostics (including the client import-limit warnings below), the written path, and the SHA-256 of the written program. Text and JSON report the same content and neither carries the program text. A failed compile writes no file.

  • The default file is <input stem>.ow in the current directory (the directory name for a directory project). -o PATH chooses another file. -o - prints the program to stdout and nothing else; it cannot be combined with --format json.
  • An existing output file is replaced only after an interactive Overwrite <path>? [y/N] confirmation (default No; No exits 1 and writes nothing). Without a terminal the command refuses with exit 2 and a diagnostic naming --force, leaving the file unchanged. --force replaces it without asking.
  • The input source file is never overwritten, with or without --force; paths are compared after resolution.
  • --to <LOCALE> emits the program in another Workshop client locale, so wright compile --to en-US zh-input.ow -o en.ow converts between client locales without changing the input locale or the input file. An undeclared locale is a usage error; a declared locale missing mappings for the program fails explicitly (missing-mapping) and writes nothing — there is no silent fallback locale.
  • --copy also copies the program to the system clipboard. A clipboard failure is reported as a clipboard-failed warning and keeps the written file. On Linux the clipboard is served by the process through X11, so the contents persist after exit only with a clipboard manager running.

wright compile also surfaces known exact Workshop client import constraints as warnings without failing compilation (#488): a program whose canonical element count exceeds the Overwatch client's 32768-element import limit still emits its artifact and exits 0, carrying a target-element-limit warning that reports the observed count and names the largest contributing rule. The distinction is deliberate. compile means Wright produced valid Workshop text, not that the current client imports it under every resource limit. wright analyze remains the detailed cost and hotspot surface; check does not evaluate client importability.

The rationale for current-directory defaults, directory targets, and explicit ownership ambiguity is recorded in ADR-0016.

wright agent install: the agent guide (#415)

wright agent install writes the canonical Wright agent guide, the wright skill from wrightkit/skills vendored into the binary so the command needs no Node tooling and no network, into the project's agent skills directory, default ./.agents/skills/wright/ (--dest DIR selects another skills directory and installs under DIR/wright/; --dry-run reports the destination without writing, though the foreign-path refusal below still applies). The installed content is a SKILL.md plus its references/ and a BUILD.json that records the Wright version, the upstream skill pin, and the guide's content hash; it contains no executable semantic or tool implementation.

The guide teaches an agent to prefer Wright's structured surfaces, such as capability discovery, check/lint/analyze/inspect, serve, and validated edits, over textual guessing, to re-validate after edits, and to surface refused or unsupported capabilities rather than work around them. Manual installation of the same guide stays possible and unchanged: copy skills/wright/ from the wrightkit/skills repository into .agents/skills/ (or run npx skills add wrightkit/skills).

Re-running install refreshes the guide in place: an identical install reports already-up-to-date, while an older or edited Wright-installed guide is replaced with the current copy. The directory is recreated, so files dropped inside it are removed. A path that was not installed by wright agent install (directory, file, or link) is refused unless --force is passed. Removing the directory uninstalls the guide; nothing outside the skills directory is touched.

MCP bootstrap (--mcp, #509)

wright agent install --mcp <TARGET> also writes the one entry that makes a coding-agent harness start wright serve --transport mcp for the project, so the shipped wright_* tools are discoverable without hand-written MCP configuration (ADR-0020). --no-guide skips the guide and registers only the server; --force and --dry-run apply to both parts. It runs from the project root and writes only project-local config:

Target File Servers key Session input
claude .mcp.json mcpServers ${CLAUDE_PROJECT_DIR:-.}
cursor .cursor/mcp.json mcpServers ${workspaceFolder}
vscode .vscode/mcp.json servers ${workspaceFolder}

Any other target is a usage error; Wright does not guess harness formats. The harness owns starting the stdio process; Wright adds no daemon or registry. Claude Code sets CLAUDE_PROJECT_DIR only in the server's own environment, so its ${...} expansion in args falls back to . (the server's working directory, the project root in a normal session).

Re-running reports up to date when the wright entry is exactly the one Wright generates. Any other wright entry, including one that already runs wright serve --transport mcp with extra fields or a different input, is refused untouched unless --force replaces it (user-added fields such as env are lost). A Wright MCP entry under a different key is refused as a duplicate. Other servers and settings are preserved (JSON key order may be normalized). A config that is not plain JSON, or a symlinked config file or directory, is refused; writes replace the file atomically.

To remove the server, delete the wright entry from the config file (and the guide directory if installed); to update, re-run with --force.

wright agent tools: client tool definitions (#535)

wright agent tools prints every operation capabilities advertises as client tool definitions for code-executing agents, versioned wright-agent/v1 and deterministic. --format messages (default) emits Anthropic Messages API tool definitions: name, description (which also describes the result), input_schema, and allowed_callers: ["code_execution_20260120"] for programmatic tool calling; emitted schemas have no recursive $ref, which the API rejects. --format json-schema emits the same operations as plain JSON Schema for other harnesses. See the agent contract.

Commands that report findings (check, analyze, lint, inspect cost) share the finding-selection options --severity, --rule-id, --file, and --max, which narrow reported diagnostics/findings without changing verdicts or exit codes; see lint configuration and findings and presentation.

Every workflow command accepts the opt-in gating option --fail-on <error|warning|info> (ADR-0030 decision 13): when the complete result's highest diagnostic or finding severity reaches the threshold — judged like the verdict, on the full set before selection — the command exits 1 and reports a FAIL verdict on every renderer and in JSON (ok: false, exit: 1). The gate only raises a verdict: a result below the threshold is untouched (a WARN result still exits 0, so gating is off by default), and a command already failing keeps its own exit code and verdict rather than reporting FAIL.

The flag configures SessionConfig.fail_on, so the driver — not the CLI — decides the verdict (#1002): every surface over the same session configuration, including the lint agent operation, reports the same ok/exit. Deciding when a workflow enables the gate belongs to the wright action; the CLI carries no policy.

check, lint, analyze, inspect, and the inspect subcommands report verdicts and actionable content by default. Text and compact JSON carry the same items; JSON is for programs processing results. Long lists show at most 20 items, their total, and an expansion hint. Lint may show fewer items to keep its compact result within 5500 bytes before the envelope; --all also lifts this length bound. The ranked analyze report preserves its top-five hotspots, risks, and variables and top-three condition trees. --all (agent field all: true) lifts the default cap; filters and an explicit --max still select results. Verdicts and exit codes use the complete set, even when an error is withheld.

The former --brief flag and brief request field are removed. Bare inspect and analyze no longer expose complete fact dumps: use the inspect subcommands to obtain a specific detail. The in-process CompilerSession API retains complete typed results. This pre-1.0 shape change is recorded in ADR-0025.

Semantic query commands (#429)

inspect owns the semantic query surface: the bare command prints the bounded summary, and its six subcommands, inspect rules, inspect symbols, inspect refs, inspect cfg, inspect callgraph, inspect cost, expose each detail area. They run the same operations the agent contract serves (rules, symbols, references + usage, cfg, callGraph, costEstimate) through the session's ToolService, so a CLI result payload equals the agent operation's payload for the same input. Nesting them under inspect keeps the top-level command surface to distinct user intents (#439); the agent request names stay flat operation names, not command paths.

inspect refs <NAME> and inspect cfg <RULE> address their target by its declared name, a variables/subroutines entry or a rule("name"), resolved against the loaded program's semantic index inside the driver. Callers never need the program's numbering, where symbol ids and rule indexes are different spaces (the agent contract keeps accepting numeric ids too). An unmatched name is a structured unknown-symbol/unknown-rule diagnostic with exit 1; a name shared by several candidates is ambiguous-symbol/ambiguous-rule listing the numeric ids to fall back to. Resolution never guesses.

  • inspect refs reports the usage counts (reads, writes, calls, rules) as the header of the reference list; there is no separate usage command. The reference list includes the declaration entry alongside reads and writes, with identifier spans reported by the analyzer (#433). When a source provider's map is statement-grain — no identifier occurrences — a reference's span falls back to the enclosing statement's span, the same at= inspect cost reports (#892).

  • inspect symbols --only <KIND> narrows the list to globalVariable, playerVariable, subroutine, or rule (kebab-case aliases work). It is spelled --only because --kind already selects the input frontend. The result reports the selected list and its selection total on every surface.

  • The query subcommands accept the same selection fields the agent operations take (#531): inspect rules adds --rule, --file, --max, and --content (without --max, --content returns at most 20 rules unless --all is set), inspect symbols adds --only/--file/--max, inspect refs adds --only (a reference kind), --rule, --file, and --max, inspect cfg adds --only (a block kind) and --max, and inspect callgraph adds --caller, --callee, and --max. With any selection flag the JSON result wraps the list in {<items>, "selection": {"total", "withheld"}}; an unknown filter value exits 2 with an invalid-selection diagnostic.

  • inspect cost accepts the finding-selection options, applied to its findings list exactly as on costEstimate. Its elementCount member is the canonical workshop-rs element count against the 32768-element client budget. Each rule's share is ranked by count, largest first, where index is the canonical rule position; the 5 largest are shown by default and --all shows every rule. The count is a structure count, not a statement of client importability; when the count cannot run elementCount carries only budget (the known client limit still reports) and elementCountUnavailable names the reason — the count is never reported as zero (ADR-0030 decision 3). Each rule entry and each finding carries its source location (span); a node without a retained span renders and serializes with no location (#722). Likewise exact.emittedBytes is null plus an emittedBytesUnavailable reason when emission fails, never 0.

    Run cache and gate (ADR-0027). compile and inspect cost keep a small project-local run cache under .wright/ at the project root and, by default, report the change in element cost against the previous successful run of the same key (resolved input, kind, locale, profile). The comparison is a convenience for people: it never changes the verdict or exit code, and it is not part of the default JSON or agent result — an agent requests a comparison explicitly by passing the reference as inline data. --baseline <name> compares against a named reference (an unknown name is a usage error) and --save-baseline <name> records the current run under a name. --max-total <N> is an explicit gate on inspect cost: exit 1 naming the overage when the element total exceeds N, identical with and without a cache. --no-cache or a non-empty WRIGHT_NO_CACHE disable reading and writing .wright/; add it to your .gitignore — Wright does not edit it for you. A failing run never replaces the previous reference, and cache failures never fail a command.

    Under a truthy CI the implicit previous-run comparison stays off, but the explicit baseline flags still read and write .wright/ so a CI cache can carry it between runs (ADR-0030): --save-baseline records the run and --baseline compares against it only when a usable record exists. A missing cache, an unknown name, or a record that is unreadable, incompatible, or keyed to a different input, kind, locale or profile turns the comparison off with no error, warning or output — the verdict and exit code are unchanged.

  • inspect cost --rule <NAME> shows where one declared rule's element cost goes (#724): the rule's Costliest statements (its direct conditions and actions ranked by recursive count) and By node name (each node's own base_count + adjustment cost summed per canonical node name, with occurrences). The byNode entries reconcile to the rule's count. An unknown rule name exits 2 with an invalid-selection diagnostic. The agent costEstimate operation accepts the same optional rule and returns rule, statements[] and byNode[] as additive fields.

  • persistentObjects has no standalone command; analyze reports aggregate counts under result.persistentObjects; use the agent operation for details.

Text output follows the human-first presentation described in presentation: each query leads with the target identity and its summary: the program inventory and a bounded rule preview for bare inspect, kind-grouped symbols with primary locations for symbols, the usage counts before the reference list for refs, the graph shape before block detail for cfg, fan-in/fan-out highlights before the edge list for callgraph, and exact totals before findings for cost. Long lists show a first page of 20 entries followed by the total and an expansion hint; --format json reports the essential result in structured form, with the same list cap as text.

wright lookup: owner name and signature lookup (#529)

wright lookup [QUERY] runs the agent contract's lookup operation (ADR-0021): it resolves a display name, a near spelling, or a guess to the language owner's accepted spelling and signature, answering from language vocabulary alone. It takes no INPUT and never loads or requires a project. Agents should use it before writing in an unfamiliar language and when check/compile rejects a name; text output of those commands prints a one-line hint after each unknown-* diagnostic pointing here (the structured result is unchanged).

  • --language workshop|opy (default workshop) selects the owner: Workshop answers from the in-process workshop-rs catalog; OverPy answers through the configured opy-rs provider's LPP lookup capability, and reports an explicit unavailable payload with exit 3 when the provider does not negotiate it. Wright never guesses locally.
  • --only narrows the entry kinds: action, value, event, enum-member, enum, setting, parameter, setting-path, member-action, member-value, structural, operator. This is the union of the kinds both owners emit (#563). A kind one language never emits answers [] on it.
  • --within <IDENTITY> lists what an identity contains: an enum domain's members, a callable's parameters, or a settings path's child segments. An unmatched scope identity refuses with lookup.unknownWithin.
  • --locale <LOCALE> selects the Workshop display-name locale; --limit <N> bounds the entry count (default 3, maximum 10).
  • Text mode prints one line per entry, the rendered signature for a callable or otherwise the accepted spelling, with its kind and owner identity. -f json prints the same result payload serve returns (language, owner, entries[], or unavailable).

The structured Workshop editor

The structured editor for raw Workshop is the experimental web application at web.wrightkit.dev (ADR-0037), built in the wrightkit/editor repository over the WebAssembly Wright core. The retired wright ui command and its loopback host are recorded in ADR-0032.

wright convert and the reconstruction surface (#126)

wright convert [INPUT] --target opy|ostw converts validated Workshop input through the configured language provider, or refuses the recognized OSTW target because DEL/OSTW provider support is not currently shipped, through the shared driver/session conversion operation (CompilerSession::convert). The CLI is a thin passthrough: it parses argv, builds the session, calls the driver workflow, and renders the envelope. Reconstruction logic lives in the language provider rather than the CLI layer. The driver reuses its own load() path (kind detection, Workshop parsing, WIR validation), emits the validated program as a canonical Workshop artifact, and delegates OPY reconstruction to the provider's lpp/reconstruct; OSTW is an explicit provider boundary and never uses a static Wright implementation.

  • The target flag is required and explicit (--target opy|ostw); a missing or unknown target is a usage error (exit 2), and --target on any other command is a usage error too.
  • Only Workshop input is accepted, and there is no direct conversion between OPY and OSTW. A non-Workshop input fails with the structured convert-input-kind diagnostic (exit 1).
  • Workshop-to-OPY reconstruction is available only when the provider negotiates reconstruct; the shipped first-party opy-rs provider advertises it starting with 0.3.19, and an older or third-party provider without it refuses with capability-unavailable (exit 3) and produces no source. Workshop-to-OSTW is recognized but unavailable because no DEL/OSTW provider is shipped (source-provider-unavailable).
  • When a provider does negotiate reconstruct, the result is canonical reconstructed source for the selected target (result.text) plus its deterministic SHA-256 (result.sha256) and the target (result.target). Reconstruction is semantic, not original-source recovery: comments, formatting, macros, functions, and source abstractions are not recovered (see the support matrices for the exact reconstructed and rejected surfaces).
  • Non-representable constructs fail deterministically with the reconstructor's stable structured diagnostics (stage reconstruction, exit code 3) and never carry partial source. The supported directions and their limits are documented in docs/opy/support-matrix.md and docs/ostw/support-matrix.md.

The conversion boundary is covered by the CLI and driver contract tests. They assert the capability-gated Workshop-to-OPY provider handoff and the explicit Workshop-to-OSTW refusal; owner reconstruction semantics are tested in the owning language repository.