Command help is generated from the executable and verified on each
platform. Project commands select --manifest-path first, otherwise search real
ancestors from the invocation directory. init uses the current directory.
Builds and user programs execute from the selected project root.
vex.ws is a WSON object. Unknown and duplicate fields are errors. Names match
[A-Za-z_][A-Za-z0-9_]*; initialization rejects invalid names without conversion.
| Field | Type | Default / meaning |
|---|---|---|
format |
integer 1, 2, or 3 | 1 when absent; generated single-target manifests use 2 |
name |
string | Required package/import identity |
version |
version literal or string | 0.1.0; dependency equality is exact |
lib |
boolean | Formats 1/2 only: false; true selects src/lib.wave, otherwise src/main.wave |
compiler |
string | Absent means no version requirement; exact canonical wavec version, including prerelease, without build metadata |
description, author, license |
string | Optional descriptive metadata |
dependencies |
array of objects | Empty when absent |
native |
object | Prebuilt library inputs and search paths; formats 1/2 also declare library output here; see native interoperability |
targets |
object | Required in format 3; optional lib and array bins declare at least one target |
default_run |
string | Format 3: optional declared binary name |
profiles |
object | Format 3: dev/release overrides and custom profiles inheriting one builtin |
build |
object | Format 3: optional default compiler target triple |
target_config |
array of objects | Format 3: exact-triple sysroot/linker and binary platform overrides |
A dependency requires a unique name and exactly one nonempty path or git
string. Optional version is an exact package version assertion, not a range.
Git alone accepts at most one nonempty branch, tag or rev string. Without a
selector, initial fetch and explicit update follow the remote default branch;
locked reuse preserves the exact commit. Path values resolve relative to their
own declaring manifest. Dependencies must declare libraries. One package name
cannot identify conflicting sources or versions anywhere in the graph.
{
format = 2,
name = "application",
compiler = "0.2.1-pre-beta",
dependencies = [{ name = "greeting", path = "../greeting" }]
}
See targets, profiles, and artifacts for all format-3 fields, selection, platform restrictions, and migration. Existing formats are not automatically rewritten. Library dependencies may declare binaries in format 3, but consumers only use their library.
Format 1 preserves legacy literal backslashes. Formats 2 and 3 decode \\, \",
\/, \b, \f, \n, \r, \t, and JSON \uXXXX sequences;
unsupported escapes are rejected. A format change is
explicit: files are never silently reinterpreted. Additive fields may retain a
format version; incompatible string or field semantics require a new version.
Future formats fail without rewriting the manifest. Metadata exposes the format
and compiler requirement. See compatibility policy.
Commit generated vex.lock. New writes use v3; compatible v2 stays byte-identical
under --locked. Legacy v1 needs an explicit normal fetch to migrate. Unsupported,
malformed, conflicting or cyclic locks fail before publication. Git commits are
exact; path content is not pinned and must be versioned separately.
fetch prepares/reuses the graph. update refreshes all Git dependencies;
update <name>... validates names entirely from the local graph and requires
fetch when that graph is incomplete. Typo validation never contacts a remote.
--locked prohibits a lockfile change; --offline prohibits Git network access.
Both still validate manifests and checkout integrity. Git credentials may be
provided for transport but are redacted from reports and never stored in the
lockfile. Git submodules are rejected. See source policies.
A project lease covers lockfile reads, resolution and compilation. Staged checkout and lockfile publication uses a recovery journal. Normal commands recover pending publication; dry-run fails without recovering. Dry-run permits coordination files only, with no message file, checkout, lockfile migration or build output. Each run has its own output generation, and releases its build lease before spawning the program. A separate inherited OS lease protects each run generation until the program and its children release their handles. There is no automatic generation/backup garbage collection.
tree --invert <name> shows why a package is needed, walking dependents back to
the project root. --depth <n> limits edges from the displayed root; zero prints
only that root. The default is the complete dependency tree. These options use
the usual resolution rules; use --locked --offline to prohibit lock changes
and network access.
doctor inspects Vex, Git, compiler selection/version/targets, project state,
and available plan/linker tools without fetching or repairing. It also works
outside a project. --target <triple> selects a target and --format=json
returns doctor schema v1. Missing local graph data requires an
explicit fetch before project-plan inspection. Optional cross-linker gaps are
warnings; required tool or protocol failures produce a nonzero exit.
clean deletes only unchanged outputs whose creation Vex recorded; --dry-run
previews without creating coordination or message files. It preserves sources,
manifest/lockfiles, dependencies, legacy/untracked outputs, edited outputs,
symlinks/reparse points, and running generations. Empty owned directories can
be removed, but directories containing unknown files are retained. Ownership
begins with builds made by this version; existing files are never adopted merely
because a later compiler invocation writes the same path. Run lease files remain
as coordination identities, even after their generation is removed.
Builds now refuse to overwrite these unknown or edited files; move them aside
explicitly before rebuilding. A successful build verifies its declared outputs
exist before reporting artifact events.
prune previews unused Git checkouts; --apply performs deletion. A complete
current local graph and existing project coordination are required; run fetch
when they are missing or stale. Validation never fetches implicitly. Only newly
published checkouts with an intact ownership record are eligible. Dirty,
untracked, ignored, modified Git metadata, legacy/unrecorded checkouts, and
unknown files are preserved. Both cleanup commands refuse pending recovery;
use a normal fetch to recover first. Transaction backups and compiler
installations are outside cleanup scope. No shared dependency cache exists, so
reusing a pruned dependency may require network access through fetch.
| Variable | Contract |
|---|---|
VEX_WAVEC |
Explicit selected compiler; empty is an error; relative paths use invocation directory |
PATH |
Compiler and required system-tool lookup; precedes the managed compiler |
VEX_TOOLCHAIN_HOME |
Managed compiler installation root; default documented in installation |
VEX_WAVEC_ARCHIVE_SHA256 |
Optional independent exact SHA-256 pin for compiler installation |
VEX_GIT_TIMEOUT |
Integer seconds 1–86400 per Git command; default 300 |
NO_COLOR |
Presence disables color; redirected output never contains automatic ANSI color |
Git keeps authentication, SSH, proxy and ordinary configuration while removing
repository-selection environment overrides. Transport is noninteractive; timeout
and cancellation terminate supervised child trees. Release infrastructure options
such as VEX_RELEASE_HOST belong to x.py, not normal Vex commands.
Help, version, info, tree, metadata, doctor, prune reports and requested dry-run
data use stdout. Status,
warnings and errors use stderr. User program stdin/stdout/stderr and TTY are
inherited. Raw wavec flags are not Vex flags; run -- <args...> passes native
arguments directly to the program.
Global -q / --quiet suppresses Vex progress only. Results, warnings, errors,
compiler/program streams and message-file events remain available. -v /
--verbose adds major selection decisions; -vv adds diagnostic context and
reasons. Verbosity is capped at two and conflicts with quiet. All Vex diagnostic
text is credential-redacted. Options after run -- belong to the program.
| Outcome | Exit code |
|---|---|
| Success | 0 |
| Vex internal failure | 1 |
| CLI usage | 2 |
| Dependency/project resolution | 3 |
| Compiler failure | 4 |
| Execution environment | 5 |
| Timeout | 124 |
| Vex cancellation | 130 |
| User program | Its original code; Unix signal uses 128 + signal |
A program can return the same number as Vex. Use the structured origin field
to distinguish them. --message-file writes schema-1
JSONL exclusively to a new file, never mixed into stdout. Failure before program
start stops Vex; reporting failure after program start warns and preserves the
program exit. Missing final events mean an incomplete report.
Machine-readable schemas: metadata v1, metadata v2, build-plan v1, doctor and message events. Additive fields are permitted; changed existing meanings require a new schema version. There is no registry or publish command in this release.
--lib and repeatable --bin <name> select root package targets. Build/check
without selectors process all root targets; run uses --bin, default_run, or
the only binary. --target <triple> still selects a compiler platform.
--profile <name> defaults to dev; --release aliases the release profile and
conflicts with an explicit profile. The root's profile is used for imported code.
Every selected target is planned and preflighted before compilation. Targets then
compile in deterministic order, stopping at the first failure while preserving
already successful artifacts. General and native output paths are unified under
target/<compiler-triple>/<profile>/{bin,lib}/, with intermediate objects under
.vex-obj/<kind>/<name>/<output-kind>/. Old paths are left untouched. Run keeps an
independent generation, lease and lock-release boundary.
Dry-run stdout is one vex.build-plan schema-1 envelope for single or multiple
targets. It contains target, profile: {name, opt_level}, and ordered plans
entries with package_target: {name, kind} and the validated compiler_plan.
It replaces the previous direct compiler-plan output. Planning runs once per
selected target; capability queries are cached within the invocation. See the
full build contract.