Developer CLI
Starting with Perastage 1.7.0, release packages ship perastage-cli as an
experimental headless command-line interface for automation and advanced
workflows. Its CMake target is perastage_cli; the produced console,
non-bundle executable is perastage-cli (perastage-cli.exe on Windows).
It remains separate from GUI startup, wxApp, MainWindow and viewer dependencies.
Experimental describes the maturity of public support and stability; future
releases may extend the surface. Existing safety, validation, atomicity and
machine-contract rules still apply. Command grammar, help and protocol fields
remain technical English. inspect is read-only; scene requires an explicit,
separate output and never overwrites its source; live transport remains
loopback-only. Platform locations and launch commands are documented in
Packaging and the user manual.
The Rust MCP adapter remains separately configured and is not installed with
Perastage.
Grammar
The current technical-English interface accepts these forms:
perastage-cli -h
perastage-cli --help
perastage-cli --version
perastage-cli inspect --help
perastage-cli inspect <file>
perastage-cli inspect <file> --view summary
perastage-cli inspect <file> --view inventory
perastage-cli inspect <file> --view resources
perastage-cli inspect <file> --view diagnostics
perastage-cli inspect <file> --view xml
perastage-cli inspect <file> --json
perastage-cli capabilities [--json]
perastage-cli scene <input.mvr> --output <output.mvr> --command <text> [--command <text> ...] [--overwrite] [--json]
perastage-cli live command <text> [--port <port>]
perastage-cli live query <operation-id> [--args <JSON-object>] [--port <port>]
perastage-cli live execute <operation-id> --args <JSON-object> [--port <port>]
The scene command is the first external mutation workflow. Its input is a
read-only MVR container and its output is mandatory, explicit, and must resolve
to a different file. An existing output is rejected unless --overwrite is
present. Repeated commands execute in source order through the shared Console
text processor; only fixture/truss selection, clear, and position/rotation
transforms currently form this surface. A parse, validation, execution,
resource, serialization, or publication failure leaves both the source and any
pre-existing output unchanged. Successful output uses the production canonical
MVR preparation, resource collection, XML serialization, and archive writer.
Application-owned exporters explicitly supply their resolved fixture fallback
through MvrExportEnvironment; the neutral CLI export supplies no fallback and
never searches Perastage's installed or user fixture libraries.
Unlike the embedded Console, which mutates the active GUI project and records application Undo state, this workflow uses an isolated headless scene and has no persistent interactive Undo stack. It does report dirty semantic mutations through the normal Command transaction contract. It neither attaches to nor controls a running Perastage process.
live connects to an already-running Perastage instance using bounded,
newline-framed JSON schema version 1 over IPv4 TCP bound strictly to
127.0.0.1; the default port is 49155. live command uses the existing
Console text adapter and Command transaction, Undo, validation, dirty-state,
and frontend refresh boundaries against the active project and selection.
live execute directly invokes the allowlisted typed semantic operations
scene.selection.update and scene.transform.batch. Selection updates accept
every selectable category and create no scene change, Undo entry, or dirty
state. Application publication synchronizes tables and 2D/3D/Layout selection
without reloading scene content. Batch transforms use explicit typed UUIDs and
preserve the existing selection; a changed batch creates one Undo entry, dirties
the project once, and publishes scene refresh once.
The read-only live query surface is scene.summary, scene.selection.get,
scene.objects.list, scene.object.get, scene.layers.list, and
scene.groups.list. It reuses Query Core's deterministic descriptors, including
stored fixture identifiers and patch addresses where applicable. Object lookup
requires a kind and UUID; clients filter structured object lists themselves.
The generic --args option accepts one JSON object and projects it into the
optional arguments member of the existing schema version 1 request envelope.
It is available for query/execute operations; legacy command text is unchanged.
Unsupported operations receive a structured error. The endpoint remains a
local transport. Transport, protocol, unsupported-operation, and semantic
command failures return exit code 4; code 3 remains reserved for fatal
inspection input.
Discover objects and look up a returned identity:
perastage-cli live query scene.objects.list
perastage-cli live query scene.object.get --args '{"kind":"fixture","uuid":"<returned UUID>"}'
Replace the fixture selection, then add and remove explicit UUIDs:
perastage-cli live execute scene.selection.update --args '{"target_kind":"fixture","preserve_existing":false,"operation_kinds":["add"],"object_kinds":["fixture"],"object_uuids":["<returned UUID>"]}'
perastage-cli live execute scene.selection.update --args '{"target_kind":"fixture","preserve_existing":true,"operation_kinds":["add","remove"],"object_kinds":["fixture","fixture"],"object_uuids":["<UUID to add>","<UUID to remove>"]}'
The supported selection kind tokens are fixture, truss, support, and
scene_object. Object references must match target_kind; all three operation
lists have equal length and execute in order. Replacement clears only the
target category before applying operations. Empty lists with
preserve_existing: false clear that category. Other categories retain their
selection. Missing, empty, wrong-kind, or unknown UUIDs reject the update before
any selection change. Groups are discoverable Query identities and are not
selection targets. See Query API, Command API,
and MCP adapter for the shared contracts.
Apply independently calculated fixture positions and an ordered rotation in one atomic operation, with position values in millimeters and rotations in degrees:
perastage-cli live execute scene.transform.batch --args '{"target_kinds":["fixture","fixture","fixture"],"target_uuids":["<first UUID>","<second UUID>","<first UUID>"],"component_kinds":["position","position","rotation"],"axes":["x","x","z"],"values":[-1500,1500,45],"modes":["absolute","absolute","relative"],"spaces":["world","world","local"]}'
The seven required arrays are aligned and non-empty; each index is one
component, and repeated targets execute in array order. Targets support
fixture, truss, support, scene_object, and group. Components are
position or rotation; axes are x, y, or z; modes are absolute or
relative; spaces are world or local. Every value must be finite. A child
UUID transforms that exact child; an explicit group retains normal descendant
synchronization. The complete request is validated and applied to a preview
before publication. Any failed component leaves the scene and selection
unchanged, with no Undo or dirty state. A semantic no-op publishes nothing.
Structured execution retains Command diagnostics in its result records and
has an empty parse_diagnostics list. Batch transforms use typed arguments
directly, and this execute surface remains limited to its two operation IDs.
inspect accepts exactly one filesystem input with a case-insensitive .gdtf
or .mvr extension. Its default view is summary. --json and --view are
mutually exclusive, may appear only once, and options follow the input file.
Unknown options or views, missing view values, and extra positional arguments
are usage errors. Shell-quoted paths containing spaces and Unicode filesystem
paths are passed directly through the standard C++ filesystem boundary.
Automation contract
The CLI is shipped experimentally starting with 1.7.0. Automation should use
the executable name perastage-cli, the inspect command, and the
inspect <file> --json form. For that machine-facing form, the compatibility
surface is:
- the command identity and argument roles shown above;
- the JSON field names, value types, enum tokens, diagnostic codes, and meanings declared stable by the Inspection API;
- the top-level
schema_version,request,success,has_findings,worst_severity,diagnostics,format,status,package,resources, andvalidationmembers, plus the format-specificdocument(GDTF) orsnapshot(MVR) member; - the exit codes and stdout/stderr responsibilities below;
- UTF-8 JSON strings and lossless Unicode filesystem-path round trips; and
- diagnostic locations, when Core supplies them:
source_pathidentifies the inspected filesystem input,package_entryidentifies an archive member,xml_pathidentifies a location within XML, andline/columnprovide one-based parser coordinates when available.
Consumers must select behavior by schema_version and should ignore unknown
object members so additive fields remain compatible. Schema version 1 is the
current contract. Within one schema version, existing fields and diagnostic
location members keep their names, JSON types, token meanings, and semantics;
new optional fields may be added. Removing or renaming a field, changing its
type, or reinterpreting its meaning incompatibly requires a new schema version.
Array order is contractual only where the Inspection API describes the data as
ordered; JSON object member order and serialized whitespace are not.
Human --view output is presentation-only. Headings, whitespace, alignment,
prose, and layout may change and must not be parsed by automation. Help and
version output formatting is likewise presentation-only unless a token is
explicitly added to this section as stable. Exact XML output is a retained
source payload for inspection, not a versioned Perastage data schema.
The human GDTF summary labels its semantic result as Read status; this is
independent from the separately displayed XML and schema validation statuses.
The JSON member remains status with its existing values and schema version.
For complete GDTF and MVR reports, success means that the read operation
produced the format-specific structured document or snapshot; it does not
claim standards conformance. has_findings is true when any base or validation
diagnostic is warning, error, or fatal, and worst_severity is the greatest
severity across both collections (including information). The top-level
diagnostics array continues to contain only base inspection diagnostics;
validation findings remain in their original validation layers so XML
well-formedness, schema conformance, and semantic/interoperability provenance
stay distinct. Thus a parseable schema-invalid file has success: true,
has_findings: true, an aggregate worst_severity of error, and exit code
1. An unreadable file has success: false and exit code 3.
MVR validation is version-aware. MVR 1.6 uses the pinned official 1.6 XSD and
the separate parser-level 1.6 semantic rules. No authoritative MVR 1.5 XSD is
available in the pinned upstream schema history, so an MVR 1.5 report truthfully
marks the schema layer unavailable and identifies the pinned specification
revision used for parser-level 1.5 semantic checks; that Markdown specification
revision is not an XSD. Unsupported versions instead identify that no
applicable specification is pinned and leave source_revision empty. MVR 1.5
is not tested against the incompatible structural expectations of the 1.6 XSD.
The focused MVR 1.5 Support check currently proves required ChainLength
element presence; numeric value-domain validation remains unavailable without
an applicable official schema.
The CLI is experimental and does not yet promise a stable public API. The versioned machine schemas retain the field and token rules above; incompatible JSON changes require an explicit schema-version change. Command or exit-code changes require an intentional compatibility decision with corresponding documentation and regression tests. These internal contract rules do not promise public CLI stability or stable presentation text.
Inspection views
summaryrenders compact format-specific document or scene facts, package and resource totals, validation status, and diagnostic severity counts.inventoryrenders the ordered CorePackageInventory, including entry type, safe display path, known size, and path-safety state.resourcesrenders ordered CoreResourceDescriptorvalues, including kind, known size, supported operations, and path safety. It does not decode images or models.diagnosticsrenders semantically unique top-level and validation-layer findings with severity, classification, domain, stable code, message, and available location. It keeps standards and compatibility findings distinct. Identical findings repeated for validation provenance are shown and counted once in human views; the complete JSON retains every original layer member.xmlwrites only the exact retaineddescription.xmlorGeneralSceneDescription.xmlpayload. It adds no heading or newline and performs no parsing, rewriting, or pretty-printing.--jsonwrites one complete schema-version-1 report. The Coreperastage_inspection_report_serializationboundary composes format status, package, resources, validation, and GDTF document or MVR snapshot facts over the minimal base result serialization. The baseSerializeResultToJson(Result)contract remains unchanged. MVR snapshots add deterministicfocus_points,video_screens, andprojectorscollections alongside the existing scene-node collections.
Both presentation modes consume the same single in-memory result returned by
InspectGdtf or InspectMvr. Resource metadata comes from the neutral resource
inspection API, whose default filesystem overload owns the bounded sniff limit.
No CLI formatter parses package or XML semantics.
Exit codes and streams
| Code | Meaning |
|---|---|
0 |
Successful clean inspection, scene publication, or help/version command; information-only findings are clean. |
1 |
Inspection completed with non-fatal warning or error findings, including compatibility findings. |
2 |
Malformed CLI usage. |
3 |
A supported input could not be usefully inspected because of fatal input, package, or XML failure. |
4 |
Unsupported inspection/live input, or a scene/live command/export/publication failure. |
5 |
Unexpected internal CLI failure. |
Requested data (human views, exact XML, and JSON) goes to standard output.
Structured JSON remains available there for a supported malformed source when
Core returns diagnostics. Standard error is reserved for usage errors,
unsupported types, and concise unexpected process failures; normal inspection
findings are not duplicated there. Automation should interpret the process exit
code before inspecting output; warning/error reports at code 1 and structured
fatal reports at code 3 can still contain valid JSON on standard output.
Architecture and distribution
The standalone Rust MCP adapter under tools/mcp/ is a process consumer of
this machine interface. It invokes the executable directly with argv, consumes
JSON and exit codes, and does not link against CLI, Core, GUI, format parsing,
or local-IPC implementation. See MCP adapter for its typed
tool surface and stdio startup contract.
cli/main.cpp only adapts process arguments and invokes the independently
testable standard-C++ runner. CLI sources consume public Core inspection,
capability, Command, external-scene workflow and local-IPC boundaries.
Inspection semantics stay in Inspection Core, including its private or
transitive reader dependencies. CLI code does not include or directly call MVR
or GDTF reader implementations. The neutral external-scene workflow composes
the acquired-package reader, shared Command text processor, headless mutation
host, and canonical MVR exporter; CLI code owns only grammar, output policy,
and result formatting.
The command starts no wxApp, creates no window, and initializes no App, GUI,
viewer, ConfigManager, localization, application project state, library, or
credential service. scene owns an isolated headless scene; live uses only
the loopback client transport. The executable remains a Windows console program
and a non-bundle macOS executable, installed in normal release packages through
cmake/PerastageInstall.cmake. Because the CLI never owns or loads
credential storage, user-secret services, or application networking state, those contents
cannot enter its output; adding such a dependency is forbidden rather than
handled by output scrubbing. Reports expose the requested source path and
archive/XML locations supplied by Inspection Core, but not private temporary
workspace paths used by implementations.