The wado CLI

SkillDev tools

How to drive the `wado` command, compile, run, test, serve, format, and publish a Wado program, target a Wasm world, pick an allocator, grant directories, and inspect the compiler with dump and query. Read before invoking the `wado` binary.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the The wado CLI skill

What this skill tells your AI

The instructions your AI receives, as published by wado-lang/wado in .claude/skills/wado-cli/SKILL.md and read by ahel’s review.

wado <command> --help is the source of truth for flags and is thorough — it states the allocator modes and their per-world defaults, the optimization levels, every dump phase, and every query kind. Run it rather than guessing.

Inside the wado repository, wado means cargo run --bin wado --.

Commands

Usage: wado <command> [options]

Commands:
  init [options]                      Create a new wado.toml manifest
  update [options]                    Resolve dependencies and write wado.lock
  fetch [options]                     Download the project's registry dependencies
  clean [options]                     Evict derived cache state (git worktrees)
  build [options]                     Build the project's worlds from wado.toml
  compile [options] <file.wado>       Compile a single Wado source file
  check [options] [file.wado]         Verify a source file and its Kiln generators
  run [options] [file.wado]           Compile and run a Wado CLI program
  serve [options] [file.wado]         Compile and serve a Wado HTTP service
  test [options] [files or dirs...]   Run tests in Wado source files
  format [options] <file.wado>...     Format a Wado source file
  doc [options] <file.wado>...        Generate documentation from source files
  dump [options] <file.wado>...       Dump compiler internal state
  wit [options] [file.wado | dir]     Emit the WIT contract for a Wado program
  syntax [options]                    Generate syntax definition files
  lsp [options]                       Start the language server (LSP over stdio)
  query <kind> [options] <file.wado>  Query language service information
  publish [options]                   Check whether the package can be published

Global options:
  --help     Show this help message
  --version  Show version information

build works from a wado.toml and writes build/<world>.wasm. compile takes exactly one source file; dump, doc, and format take several. check, run, serve, and wit fall back to the manifest when given no path.

Target World

A Wado program targets a Wasm world: the CLI command (wasi:cli/command, the default), the HTTP service (wasi:http/service, run via wado serve), or the synthetic test world (selected with --world test, used by E2E tests). Several defaults — including the allocator — depend on the target world.

--world <name> overrides it on compile, check, dump, and wit (check defaults to the library world instead — see Check). --world test exports the entry module's test blocks and drops everything else. run, serve, and test pick their world automatically. build --world <fq> is a different flag: it selects which of wado.toml's declared worlds to build.

wado compile --world test file.wado  # compile against the test world
wado check --world test file.wado    # type-check against the test world

Allocators

Three allocators are available via --allocator <mode>:

  • bump (default for CLI): bump pointer; never frees. Fast, minimal code.
  • freelist (default for the HTTP world): reclaims freed memory via a free list. For long-running processes.
  • debug (default for the test world): never reuses freed memory; poisons freed memory with 0xFF. For use-after-free detection.
wado compile --allocator bump file.wado      # bump allocator
wado compile --allocator freelist file.wado  # free-list allocator
wado compile --allocator debug file.wado     # debug allocator

wado compile selects the debug allocator automatically when targeting the test world; E2E tests rely on this.

Compile

wado compile -o file.wasm file.wado    # generate Wasm
wado compile -o file.wat file.wado     # generate WAT
wado compile --wat-to-stdout file.wado # output WAT to stdout

Optimization levels: -O0 (none), -O1 (development), -O2 (production, default), -O3 (aggressive), -Os (-O2 + strip symbols).

To inspect invalid Wasm when debugging codegen bugs, skip validation:

# Output raw Wasm bytes even if invalid
wado compile --no-validate --wat-to-stdout file.wado

wado check verifies Wado sources — and re-runs their Kiln generators, comparing the output against the committed source — without emitting Wasm. It resolves dependencies exactly as compile / run do, fetching what the cache lacks.

With no file it checks every world wado.toml declares, exactly the targets wado build builds. It runs at O0 since it throws the component away, so it reports everything a build would and skips the optimization loop, which is most of a large build's time.

Naming a file checks that file alone, against the world whose [world] entry names it and otherwise the library world, which requires no entry point. So a library module checks as itself; --world <name> opts into a world's contract.

Run

wado run file.wado  # run a CLI program with wasmtime

A program reaches only the directories granted to it: the current one, or exactly the --dir grants once any is given. Paths open relative to a grant, so an absolute path never opens — reach a file outside the tree by granting its directory and naming it relative to that.

wado run --dir /tmp/scratch prog.wado Foo.g4  # Foo.g4 resolves inside /tmp/scratch

Test

wado test discovers and runs test blocks (compiled against the test world, see Target World above).

wado test                           # discover and run every .wado test in the project
wado test file.wado                 # run tests in one file
wado test --filter '**/json*.wado'  # run tests in files matching a wildcard
wado test --profile guest file.wado # guest profile over that file's tests

--profile takes one file, runs it serially, and leaves a test that hangs unbounded — it samples on the epoch deadline the per-test timeout is counted in. See the wado-performance skill for reading the profile it writes.

A failure or resolved #[TODO] prints its own one-line notice immediately, otherwise a digest (N/Total files · tests, failed, todo, skip · ETA) prints every 5s, ending in a compile:/load:/skip:/test: summary. tailing the last line or two is enough to read the current state of a long run.

Serve

Use wado serve to run a Wado HTTP service (wasi:http/service world):

wado serve file.wado                        # serve on 0.0.0.0:8080 (default)
wado serve --addr 127.0.0.1:3000 file.wado  # serve on a custom address

Dump

Use wado dump to inspect compiler internal state for debugging. See wado dump --help for the full help.

wado dump file.wado                  # show final WIR (default)
wado dump --nir file.wado            # show final NIR (after optimization)
wado dump --nir -O0 file.wado        # show NIR without optimization
wado dump --ast file.wado            # show parsed AST
wado dump --modules file.wado        # show loaded modules
wado dump --symbols file.wado        # show symbol table
wado dump --types file.wado          # show type table
wado dump --tir-resolved file.wado       # show TIR after type resolution
wado dump --tir-monomorphized file.wado  # show TIR after monomorphization
wado dump --nir-lowered file.wado        # show NIR right after lowering (before optimize)
wado dump --assert-plan file.wado        # show which operands each `assert` captures

Query

wado query answers compiler questions about a symbol, for tooling and docs. A symbol is addressed either by position (--line/--column in a file) or by symbol notation MODULE#SYMBOL:

  • MODULE is the import specifier; quote it as in use — droppable for a scheme or bare name (core:json), required for a path or URL ("./utils.wado").
  • SYMBOL uses Wado's operators: bare name (free function/type/global), Type::name (associated const/fn), Type.name (method), Type^Trait::name (trait-impl member).
wado query hover --symbol core:json#from_string                   # signature / type
wado query hover --symbol ./hello.wado#run --base example          # local module
wado query definition --symbol core:cbor#CborDeserializer.peek_byte
wado query references --symbol core:cli#println --base example     # all uses (workspace)
wado query hover --line 5 --column 10 file.wado                   # position-based
wado query diagnostics file.wado                                  # errors/warnings
wado query inlay-hints file.wado                                  # hints, spliced into the source

inlay-hints takes a file, not a symbol, and prints every line that carries a hint with the labels spliced in at the anchors an editor would render them at (let x‹: i32› = add(‹a: ›1, ‹b: ›2);). That is how to check anchor placement — against example/, say — without reading positions off a list. --json prints the raw hints instead, positioned in the LSP's default UTF-16 encoding.

Common options:

  • --symbol <notation> — locate by name instead of --line/--column.
  • --base <dir> — anchor relative modules (default: cwd; core: / wasi: are location-independent).
  • --all — include private members; the default is the public-API view (matches wado doc).
  • --json — machine-readable output.

For a type, hover also lists its impl blocks. references loads every .wado under --base, so it spans the workspace. See docs/wep-2026-06-14-symbol-notation.md for the notation spec.

Format

The wado format command formats Wado source code.

wado format -w file.wado  # rewrite in place

In the wado repository, mise run format formats the whole workspace. Every package skips **/generated/** and **/build/** plus its own [format] exclude; [format] include opts any of those back in. wado-compiler excludes tests/**, so the e2e fixtures and the golden format fixtures keep the hand-authored layouts that are part of the test.

A directory argument is walked from the package that encloses it, so the globs match as authored whichever subdirectory you name. wado format -w wado-compiler/tests formats nothing and reports that directory as empty.

Caution: naming a file bypasses the filters. The golden-fixture scripts rewrite excluded fixtures that way, so wado format -w wado-compiler/tests/fixtures/x.wado reformats it too, silently discarding a layout the test depends on. When the syntax is updated, make sure to add tests to wado-compiler/tests/format.rs.

What the formatter decides — width, wrapping, comment placement — is in docs/formatter.md.

Publish

wado publish builds the package and uploads it through wkg. Credentials belong to wkg, not Wado — authenticate to the registry first (docker login, or WKG_OCI_USERNAME / WKG_OCI_PASSWORD; for GHCR the password is a token with the write:packages scope). --dry-run runs every readiness check without uploading.

Compilation Log and Timing

The compiler emits timestamped diagnostics to stderr. Use --log-level to control verbosity (debug, info, warn — the default — error, off).

wado compile --log-level debug file.wado

Optimizer Remarks

A remark: reports a cost the optimizer could not remove, at the exact source span. They are info-level, so the default warn hides them — ask when chasing why something is slower or larger than expected, not on every build.

wado check --log-level info file.wado              # runs at O0; fastest
wado check --world test --log-level info lib.wado  # a library with test blocks

Two kinds are reported, across the whole entry package — its entry point and every local module it reaches. A dependency, core: and wasi: stay out:

  • A value-semantic copy that survived. Wado deep-copies aggregates on assignment, argument passing, and return; the ones no pass removed are invisible in the source.

    file.wado:6:5: info: remark: a copy of `List<i32>` survives optimization
    
  • A compile-time parameter that still decides a branch. -D log.level=info did not strip what it was told to. The remark names the parameter, and the intermediate global when the gate reads a derived one instead.

    file.wado:111:5: info: remark: compile-time parameter `log.level` is still read
    here through global `LOG_STATIC_LEVEL`, so this branch is decided at run time;
    the code it guards was not stripped
    

Design: docs/wep-2026-06-03-optimizer-remarks.md. What a remark is currently expected to report is the "Not yet implemented" list in docs/optimizer.md.

Signals

GitHub stars
116
Forks
2
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
wado-cli
Source
github.com/wado-lang/wado