Project capacity remains megawatts for --capacity-mw; the console's new kW/MW selector does not change CLI units. Partial document reports and direct site-plan Attach DWG are console views, not new CLI operations.
Prompt inspection is read-only: prompts list and prompts diff remain; snapshot, activate and
rollback commands are removed. Prompt changes require reviewed Git changes and separately
authorized database activation. Repository eval:check, eval:compare and eval:verify scripts
are not packaged CLI commands and do not authorize paid execution or release acceptance.
The Salesforce installed application's Layer 2 packet-submission source adds no GridWonk CLI
command. Salesforce will initiate its runtime requests over a separate signed API; the Salesforce
CLI used by engineers for development and testing is not a runtime transport. The installed-app
endpoint and processing remain disabled.
AX optimization commands are removed
The optimize command and opt alias, including status and list, are no longer available.
Prompt history and evaluation commands remain.
The @gridwonk/cli package provides the GridWonk command-line interface (CLI). The package scope is canonical; the executable command remains gridwonk during the binary naming transition.
It is the command-line projection of the Canonical Capability Manifest, which is the source of truth for capability IDs, auth lanes, lifecycle, risk metadata, and surface identifiers.
A projection is a surface-specific catalog of the same capability contract, not a separate source of truth.
The manifest's operation-level bindings distinguish reads, writes, confirmation requirements, and
optional OAuth scopes so a CLI command cannot imply broader authority than its shared capability
operation.
Guarded Codex-only administration is recorded separately from CLI authority: adding a read to the
protected admin MCP catalog does not silently create a CLI command or grant impersonation, billing,
or model-catalog write access.
Protected document recovery follows the same boundary: admin-document-recovery adds no CLI or
customer SDK command. Its human-operator preview/apply/status flow does not change the existing
packet upload-retry commands or authorize paid recovery through a stored CLI key.
The review-application capability's forced fresh-perception option is likewise console-only,
limited to platform administrators on demo packets. It does not add a CLI command or broaden the
partner packet review command.
Packet Owner Evidence keeps the stable capability ID packet-applicant-evidence. Its human
confirmations—including utility staff confirmations on their own editable packets—remain in
the console, not CLI commands, SDK methods, REST writes, or MCP calls. The utility-owner repair
does not broaden packet write credentials or introduce a developer invitation command.
The CLI provides terminal access to the GridWonk API v1, session-backed admin workflows, and Model Context Protocol (MCP) gateway operations.
It supports two output modes: an interactive Ink
terminal UI for human operators, and a --json flag for machine-readable output
consumed by AI coding agents (Claude Code, Codex) and CI/CD pipelines.
Most runtime commands use @gridwonk/sdk; browser login, bootstrap, and
session-only helper commands call the matching GridWonk route APIs directly.
SDK-backed commands inherit SDK retry logic, rate limiting, and error mapping.
The CLI defaults to the canonical https://gridwonk.com API origin. An
authenticated request therefore needs no base URL or trust override:
gridwonk utilities list --state CA --json
Use --base-url only for an approved alternate environment. Custom origins
also require --allow-untrusted-base-url. Exact legacy production hosts are
accepted for compatibility and normalized to https://gridwonk.com before an
authenticated request is sent.
# Install the CLI (requires GitHub Packages ~/.npmrc setup — see below)npm install -g @gridwonk/cli# Store your API keygridwonk auth login --api-key gw_live_YOUR_KEY# Inspect the locally configured credential source and effective hostgridwonk whoami# Verify the credential with a real API read against the canonical GridWonk hostgridwonk utilities list --state CA --json
The package installs two commands: gridwonk, the canonical one, and
gridboost, the pre-rebrand name kept so existing scripts and aliases keep
running. Both are the same launcher, so they behave identically — but help,
usage, examples, and shell completions all speak gridwonk, and completions are
registered for that name.
# Legacy gb_live_/gb_test_ keys are no longer accepted; use gw_live_ or gw_test_ keys.# Query customer-released requirements against the canonical GridWonk hostgridwonk requirements published --utility "nashville-electric-service" --json
GitHub Packages setup: The @gridwonk/cli package is hosted on GitHub Packages.
Add the following to your ~/.npmrc (one-time setup):
Local installs require a GitHub personal access token (classic) with read:packages
scope and access to the package. See GitHub's registry authentication guidance
for local installation and GitHub Actions credential options.
For human session-backed CLI access, use browser login:
gridwonk auth login --browsergridwonk auth status --jsongridwonk orgs list --jsongridwonk orgs switch <org-id>gridwonk auth connect --client codex --org <org-id>
Browser-backed login is the baseline path for session-only mutations. Once you have a stored session and active org, the same terminal can execute high-risk writes that are intentionally blocked for API keys.
The CLI acts as you, so browser login first asks you to accept any updated legal terms, then finishes the login. The CLI waits two minutes for the browser; if it times out while you review the terms, run gridwonk auth login --browser again and it finishes right away. If the terms change while you hold a session, auth bootstrap-status and auth bootstrap-key answer 403 FORBIDDEN with reason legal_acceptance_required and a link to accept; other session commands are refused until you accept in the browser.
For direct session-token fallback or agent handoff, the CLI still accepts an explicit session token:
~/.gridwonk/config.json (stored via gridwonk auth login)
Project .env.local loading is opt-in only. Supported keys:
GRIDWONK_API_KEY
GRIDWONK_BASE_URL
The pre-rebrand GRIDBOOST_* variables and ~/.gridboost/config.json are still
read when their GRIDWONK_* / ~/.gridwonk counterparts are absent, so an
existing shell or CI job keeps working. The CLI prints a one-time deprecation
warning; rename them when convenient. Setting a GRIDWONK_* variable to an
empty string means "cleared" and does not fall back to the legacy name,
project file, or stored config.
Config writes always target ~/.gridwonk/config.json; the first write carries
forward fields read from a legacy-only config. A failed first write leaves the
legacy config active, and gridwonk config show reports the file actually
being read. gridwonk auth logout removes stored credentials from both
locations.
# CI/CD: use environment variableexport GRIDWONK_API_KEY=gw_live_...gridwonk jobs list --json# Interactive: store API key locallygridwonk auth login# Opt-in project .env.local for one commandgridwonk --project-env auth status# Opt-in project .env.local in automationGRIDWONK_ENABLE_PROJECT_ENV=1 gridwonk usage --json
The config directory is created with 0o700 permissions and the config file
with 0o600 to protect stored credentials.
Session-backed commands resolve auth in this order:
--session-token <token> flag
GRIDWONK_SESSION_TOKEN shell environment variable
~/.gridwonk/config.json (stored via gridwonk auth login --browser or gridwonk auth login --session-token ...)
Session-only mutation commands reject API keys and point operators back to:
Every command listed here is covered by npm run capabilities:contracts: a capability whose cli
binding is marked available must resolve to a registered command path, so this reference cannot
silently drift from what the CLI actually ships.
The active packet-combined-split capability remains console-only behind a server-side
per-organization setting (the COMBINED_SPLIT dial merged with database rows that organization
admins and platform admins manage). The manifest deliberately has no CLI binding for proposing,
confirming,
retrying, discarding, or materializing a split. Developers use More actions → Split PDF in the
console for eligible PDFs, including those already assigned a document type or reviewed.
The mixed-upload coordinator now binds through packets intake-batches (manifest operation
family utility-packet-intake.upload-batch). It reserves, reads, heartbeats, pairs, cancels
unstarted items, and abandons selections while preserving the existing intake identities.
ZIP creation and recovery use packets archive-intake; standalone CAD upload still has no CLI create command.
Applicant-owned inspected CAD items or archive children can be paired with intake-batches pair.
The standalone native-CAD intake, visualization, and preflight capabilities have no CLI binding. The
intake contract provides a partner REST route, while this layer has no command for uploading,
downloading, viewing, or inspecting a DWG. Optional DWG companions are available for any utility
through the existing console and partner REST intake; see Upload.
The high-risk admin-purge-empty-synthetic-projects capability also has no CLI binding. It is
limited to the protected GridWonk admin MCP preview/apply flow so it cannot become a general-purpose
project deletion command.
Create and update support --dry-run and explicit --yes confirmation. These commands manage real
developer projects only; synthetic demo creation and project deletion are not customer CLI
capabilities.
--declared-tier-slug <slug> declares the interconnection program (the API's declaredTierSlug) and
is required when the utility publishes two or more programs for the chosen level (and refused
otherwise). The API's refusal message lists the valid slugs, and --json output carries them under
error.details.programs together with error.reason on 503 responses.
projects update cannot change the declared program, and --interconnection-level is refused when
the change would leave the project in a state creation would not have accepted: a project that
declared a program is bound to its track, and an undeclared project cannot move onto a track where
the utility publishes two or more programs. Create a new project with --declared-tier-slug instead.
gridwonk auth login [--api-key <key>] # Store API keygridwonk auth login --browser # Browser-assisted session logingridwonk auth login --session-token <tok> # Store session token directlygridwonk auth connect --client codex # Validate key or session + show onboardinggridwonk auth logout # Remove stored credentialsgridwonk auth status # Show current auth contextgridwonk auth token [--expires-in <sec>] # Exchange API key for JWTgridwonk auth bootstrap-status --org <id> # Check first-key bootstrap eligibilitygridwonk auth bootstrap-key --org <id> --name "Initial Management Key"
bootstrap-key requires an explicit, nonblank --org <id> flag, either before or after the
subcommand. It does not choose the target from stored configuration or environment variables.
Use --dry-run to preview the target without making a network request.
bootstrap-status and bootstrap-key return 403 FORBIDDEN with reason legal_acceptance_required while your account has updated legal terms to accept; the error message includes the accept link. bootstrap-status reports not_allowlisted with the reason API access is suspended for this organization or This organization is suspended when a platform admin has suspended the organization's API and MCP access or the account is suspended; bootstrap-key is refused in the same state. Contact support to restore access.
Utility and consultant organizations are allowlisted by default, so an owner or admin of either can bootstrap a first management key without a platform-admin step; developer organizations still report not_allowlisted until a platform admin enables their API profile. Utility owners and admins can also mint keys in the browser at /utility/api-keys.
gridwonk capabilities --json returns the CLI-specific agent discovery catalog from packages/cli/src/capabilities.ts.
That catalog tells agents which CLI commands are useful, which auth lane they need, and whether JSON output is supported.
It is a CLI projection of the Canonical Capability Manifest, not the canonical manifest itself.
The manifest declares CLI bindings today; npm run capabilities:contracts verifies available manifest bindings against registered CLI command paths, while npm run capabilities:surface-report highlights broader CLI-only commands that may need manifest bindings or reduction decisions.
gridwonk orgs list # List accessible orgsgridwonk orgs get <org-id> # Get org detailgridwonk orgs switch <org-id> # Persist active org for session commands
The remediation commands read working-copy spans, patch an edited span after mutation confirmation, create clean/redline/evidence export bundles, and fetch export status plus artifact URLs.
These commands summarize the published Packet Intake review catalog. Open /admin/requirements-review to approve, reject, or refine an individual requirement.
--review-status accepts ready, needs-review, changed, blocked, or evidence-only, and
filters server-side. Omit it to list every requirement set in scope. Both commands are scoped by
--org the same way the other operator review commands are.
admin utilities delete refuses roster-managed utilities (rows the utility roster catalog
matches) with HTTP 409 — remove them via the domains/utilities publish flow instead (ADR-068).
Workspace appearance is selected in Team and logo > Workspace appearance using the local
preview and Save appearance. The session-authorized browser route does not create a CLI or SDK
appearance command; existing organization branding administration remains separately scoped.
The diagnosis check requires platform-admin authority and explicit approval to send a synthetic
system incident to the configured operational Slack channel and reserve at most $0.10 for its
private-queue AI assessment. Without --yes, the command asks for confirmation; --dry-run
sends nothing. Only one attempt is allowed per 24 hours across operators and deployments.
No customer data or prompt is accepted. A queued result does not prove inference or Slack delivery;
verify the matching incident ID and completion log. With the operational Slack bot configured,
the assessment is a reply beneath the original synthetic alert; it does not request Claude triage.
Native Vercel Agent billing is separate.
docs classify accepts text or extracted markdown. It rejects PDFs directly; upload PDFs through
packet intake (the packets commands), then classify the extracted text. The former docs upload
command was removed with the legacy 3 MiB upload on 2026-09-30.
Both docs commands are deprecated pre-packet compatibility adapters.
They still work unchanged and are not scheduled for removal, but packet
workflows use Storage-native intake and the pinned, authoritative role-decision
flow instead. New integrations should target those rather than
building on docs classify / docs digitize.
gridwonk assist --prompt "Summarize Duke Energy NC site-control requirements"gridwonk assist --prompt "Answer from this context" --context ./requirements.md --stream
gridwonk agents list # List available agent IDsgridwonk agents chat --utility <name> \ [--state <ST>] [--message <text>] \ [--session-id <id>] # Chat with an agentgridwonk agents execute --agent-id <id> \ --utility <name> --query <text> \ [--state <ST>] [--watch] # Execute a named agent
Single-message mode (non-interactive):
gridwonk agents chat --utility "PG&E" \ --message "What docs do I need for 5MW solar?" --json
Human-readable responses append a de-duplicated Sources list when the answer has citations. With
--json, the response is NDJSON — one JSON event per line from the SSE stream, including structured
source events.
Interactive mode (no --message flag) opens a full-screen Ink chat TUI with
keyboard input and streaming responses.
Agent execution with --watch streams job progress via SSE:
gridwonk jobs list [--status <s>] \ [--type <t>] [--limit <n>] # List jobs (table UI)gridwonk jobs get <job-id> # Job detailsgridwonk jobs cancel <job-id> # Cancel a running jobgridwonk jobs watch <job-id> # Live SSE progress bar
Cancelling an authorized packet-review job also settles its linked active review and prevents
late packet results without deleting completed document findings. It does not broaden the key's
job-organization scope. A successful CLI cancellation is not proof that an already-running
provider request stopped. Use the project's Review Actions → Cancel Packet Review control
or ask support to confirm runtime-stop warnings before retrying a stuck packet.
Illustrative jobs list output: Ink renders an auto-formatted table:
┌──────────┬─────────────┬──────────┬──────────┐│ ID │ Type │ Status │ Progress │├──────────┼─────────────┼──────────┼──────────┤│ abc12345 │ compliance │ complete │ 100% ││ def67890 │ discovery │ running │ 42% │└──────────┴─────────────┴──────────┴──────────┘
jobs watch provides a live progress bar and status updates powered by
Server-Sent Events:
gridwonk jobs watch abc12345
With --json, the stream is output as NDJSON:
gridwonk jobs watch abc12345 --json# {"type":"job.update","data":{"status":"running","progress":42}}# {"type":"job.update","data":{"status":"running","progress":85}}# {"type":"job.complete","data":{"status":"complete","progress":100}}
Webhook secrets are masked in terminal output (only last 4 characters shown).
The existing --events option also accepts the five utility.* families documented in
Webhooks. Their schema activation and
production release remain separately held. Subscription management still requires the existing
management-key authority, and utility events are scoped to the receiving utility organization.
Subscribing does not grant notice-sending or human-decision permission. There is no new CLI
command for provider receipts or resetting an exhausted utility-event delivery.
gridwonk keys list [--status <s>] # List org API keysgridwonk keys create --name <name> \ [--env <live|test>] [--rpm <number>] # Create a new keygridwonk keys get <id> # Key detailsgridwonk keys update <id> \ [--name <name>] [--status <s>] [--rpm <number>] # Update keygridwonk keys revoke <id> # Permanently revoke key
Subcommand
Description
list
List API keys for the authenticated organization. Filter by --status (active, suspended, revoked).
create
Create a new API key. The plaintext key is shown once — store it securely.
get
Show key details including usage stats and rate limits.
update
Update key name, status (active/suspended), or rate limit.
revoke
Permanently revoke a key. Cannot be undone. Self-revoke is blocked.
keys update --allowed-endpoints <csv> accepts a comma-separated list. It trims entries, removes
empty entries between commas, and preserves order and duplicates. Omitting the option or passing
--allowed-endpoints "" leaves allowedEndpoints out of the request. Whitespace-only or comma-only
input sends an explicit empty list (allowedEndpoints: []); it is not equivalent to omission.
Use keys update --dry-run --json with the key ID and proposed options to inspect the parsed
settings before confirming a change. Existing access requirements are unchanged.
Example — create and list keys:
$ gridwonk keys create --name "CI Pipeline" --env test --rpm 120Key created: gw_test_...WARNING: Store this key securely. It will not be shown again.$ gridwonk keys list --status active --json{"data":{"keys":[...],"total":3}}
The current command. It wraps the SDK queryPublishedRequirements() method, which calls
GET /api/v1/published-requirements, and prints the response summary followed by each
requirement with its cited sources. --limit accepts 1-200 (default 50); when more rows remain, the
renderer tells you to page with --offset.
The capability manifest assigns both this replacement command and the console Requirements view
to published-requirements; the legacy capability no longer claims the UI destination.
Coverage is everything published except utility-rejected rows, with honest gaps:
The utility name is not fuzzy-resolved. This command sends your input straight through and
lets the server resolve it exactly (name, then canonical slug). An
unknown utility returns an honest empty result rather than a guessed neighbour.
Only utility-rejected rows are omitted. Every other published row is returned. With --json,
each row carries reviewStatus: approved (utility reviewers signed off) or pending (awaiting
a current utility decision). In the human-readable output, pending rows are marked
[pending utility review]. A decision made stale by a later change reverts that row to
pending; it is never dropped.
With --json, data.releaseStatus (released, partially_released, review_incomplete, or
not_published) reports the utility's review progress. It does not gate what comes back — a
review_incomplete scope still returns its rows.
--category fees always returns an empty set — the published store has no fee role.
Rows whose published wording is still under review are withheld and reported in the summary. That
is a separate mechanism from reviewStatus.
gridwonk requirements published --utility "nashville-electric-service" --state TN --category forms --json
The old requirements query and requirements list aliases have been removed. Existing scripts
must migrate to requirements published.
--status is a backward-compatible alias that accepts either location states
(exact, approximate, unresolved, pending) or remediation states
(open, in_progress, pending_revalidation, resolved, wont_fix).
Use --include-anchors when you need source and working-copy anchor details.
The MCP commands reach the same public MCP gateway described in MCP Servers.
The CLI does not own MCP tool definitions; it lists and calls the current gateway projection, which is backed by lib/mcp/public-tool-definitions.ts and checked against the Canonical Capability Manifest.
query_published_requirements therefore behaves exactly like gridwonk requirements published,
including the all-except-rejected rule and per-row reviewStatus. The old public MCP
query_requirements tool has been removed. The internal chat-runtime tool that shares its short
name is separate and always reads published Packet Intake requirements — see
Requirement Lookups.
Run dispatch currently accepts only the checked-in deterministic location and digitizer Markdown
datasets. An optional path must equal that agent's registered path; arbitrary paths are rejected.
Paid chatbot runs are unavailable until immutable target reconstruction and server-owned spending
approval exist. Stored or copied approval fields do not authorize a run. History/report remain readable.
gridwonk evals list [--agent <id>] \ [--status <status>] # List eval runsgridwonk evals run --agent <id> \ [--dataset <path>] # Trigger an eval rungridwonk evals report <run-id> # Show eval run details
gridwonk whoami # Inspect local auth state + key source; no API request
Illustrative output: The base URL is the effective locally configured API origin. Run a read command such as utilities list --state CA --json to verify the credential with the server.
Authenticated Key: gw_...cdef Source: config Base URL: <configured API origin>
The --json flag makes every command consumable by AI coding agents. Combined
with exit codes, the CLI becomes structured tooling for automated pipelines.
This runnable document pipeline requires an authenticated CLI, jq, and the id of a document
that already exists in a packet (upload it through packet intake first):
The CLI can also be installed as a standalone binary (no Node.js required). The bun compile build produces a single-file gridwonk executable for each platform.
GitHub Releases: Automated via .github/workflows/cli-release.yml. Each release publishes binaries for macOS (arm64, x64) and Linux (x64).
The binary bundles all dependencies — the SDK, Ink UI, and Commander CLI framework — into a single executable. No npm install or GitHub Packages token is required.
Partner packet intake and review. Every command requires a partner-scoped
access token (POST /api/v1/oauth/token with scope=partner.read partner.write)
passed as the API key — raw operator keys are refused on these endpoints.
Command
Description
packets intake create
Create a Storage-native intake and receive upload authorization (--submission, --idempotency-key, --file-name, --mime-type, --size-bytes; optional --replace-document, or both --batch and --batch-item)
Correct or explicitly confirm a content role; a correction re-runs the analysis applicable to that role
packets review start --submission <id>
Start an applicant or eligible utility packet review through the shared review budget (50/hour per user+org); active, unexpired upload batches return HTTP 409 until settled, abandoned, or expired
packets review get <runId> --submission <id>
Review run with the cited deficiency report, coverage, and recorded per-check outcomes
packets status --submission <id>
Canonical workflow status + immutable packages
packets screening --submission <id>
Utility-only package-bound screening status
packets documents pdf <documentId> --submission <id> --version-id <id> [--package <id>]
Short-lived signed URL for the accepted sanitized rendition (never the original upload); prints the typed preparing/unavailable status while the rendition is cold
All three document mutations use the standard confirmation prompt and accept --yes or
--dry-run. Dry-run prints the planned request without contacting the server, so it does not prove
authorization. Use --retry-failed only with packets documents review; the MCP review tool does
not expose that option.
Utility draft creation and shape changes validate capacity before sending it: invalid numbers are
refused by both direct commands and the interactive navigator. The API accepts capacities from
0.000001 through 99999999.999999 MW in whole-watt increments and returns 409 when an
application number already belongs to the utility organization.
A utility organization's partner-scoped token (partner.read) reads its own Applications workspace.
The organization comes from the token; no flag selects another organization.
Utility screening uses the existing packets review start --submission <id> command with
partner.write; the server selects utility authority. It requires the utility cohort, matching
utility identity, an eligible application stage, and current write membership. It cannot screen a
developer draft or use a utility-issued consultant grant to enter the utility lane.
For a formal application in pending, queued, or resubmitted, the command records
screening together with the queued review before work is dispatched. Completion moves it
to screened; a failed start or run restores the prior status when no other utility review
is active. Console, REST, SDK, and MCP starts use these same transitions. Re-screening preserves
existing decisions and notice states, and utility-owned drafts keep their package-freeze path.
Create a manifest of drafts in one retry-safe call (partner.write). Rows take the same fields as create; a row with an unknown or misspelled field, or without an applicationNumber, is refused before anything is sent
Commit a send request using an eligible address on file (partner.write)
Mutations honor the global --dry-run and --yes flags.
The notice commands are implemented in the dependent utility-notice stack; deployment and schema
activation remain separate approvals. They require current direct utility membership, and writes
require a writer role. A notice from a different application is refused even within the same
organization. API send recipients must be the recorded applicant address or an active, unexpired
owner/admin/member address in the applicant organization. No command lists contact addresses.
Pending, held and provider-accepted delivery are reported separately from canonical notice status;
none proves inbox delivery. A repeated finalized send or acknowledgment reports no change.
See notice contracts before retrying.
All four manual-decision commands require partner.write and a current utility writer role.
They use the console's shared cores and are excluded from MCP. --dry-run makes no HTTP request
and does not prove server authorization. Dismiss, defer, and edit require --rationale; an edit
also requires one or more of --edit-title, --edit-description, --edit-recommendation, and
--edit-severity. Refresh the package findings after a version conflict. Study requirements
leave the existing frozen package and report unchanged. See the
utility lane guide for lifecycle constraints and receipts.
Salesforce has no dedicated CLI binding. The connector is implemented but disabled,
and ordinary packet commands cannot recapture or edit source-owned evidence. Reads of those copies
require the effective human viewer's current Salesforce permissions. If a utility queue query
matches more than 200 Salesforce-owned applications, narrow its filters; lowering --limit alone
does not reduce the permission-check candidate set.
During a temporary Salesforce permission-check outage, utility applications list keeps ordinary
and successfully authorized applications available and prints a partial-list/count warning.
JSON output includes salesforceReadUnavailable: true; rows and total exclude unverified source
applications. Retry the command to recheck live permissions. No old permission result is reused.
The partner CLI can confirm or correct a role, but it does not expose the platform-admin aggregate
that counts later human changes to automatic assignments. That measurement remains read-only and
internal; it does not add a CLI activation switch.
Combined-PDF splitting remains console-only. ZIP expansion uses packets archive-intake and the
same owner checks and file limits as the packet API.
Create prints short-lived upload authorization: keep that output private. Upload the bytes, then
finalize. Retry a 503 WORKFLOW_START_FAILED using the same archive or intake ID. Utility ZIP
uploads accept PDF, PNG and JPEG; native CAD remains excluded. Mutations support --dry-run and
require the normal confirmation or --yes.
Native-CAD companion intake has no CLI binding. Use the console or partner REST routes so the exact
site-plan version and direct binary upload authorization remain explicit.
Interactive CAD visualization and preflight also remain console-only; the CLI receives neither
native DWG bytes nor Autodesk viewer tokens.