CLI reference

All commands and flags exposed by the xs binary.

Summary

Canonical

XS Command Reference

Reference for the xs command-line tool. Most of these work, some are rough around the edges.

> Convention: Optional arguments are shown in [brackets], required > arguments in <angle brackets>. Flags can appear before or after positional > arguments unless noted otherwise.


Running Scripts

xs <file.xs> [args...]

Run an XS script on the bytecode VM (the default backend).

xs hello.xs
xs server.xs --port 8080

Arguments after the filename are available in the script via os.args after import os. os.args does not include the interpreter path or the script filename; only the arguments that follow.

xs run <file.xs|file.xsc> [args...]

Run a source file or compiled bytecode:

  • xs run <file.xs>: same as xs <file.xs>, runs on the default VM backend
  • xs run <file.xsc>: run a compiled bytecode file produced by xs build
xs run hello.xs
xs run app.xsc

xs -e <code> / xs --eval <code>

Evaluate an inline XS expression without a file.

xs -e 'println("hello")'
xs --eval 'println(2 ** 10)'

Useful for quick one-liners and shell scripting.

xs --vm <file.xs>

Run a script with the bytecode VM backend instead of the tree-walker.

xs --vm fib.xs

The VM compiles the AST to bytecode first, then executes it. Has full feature parity with the interpreter: all language features, builtins, and methods work. Faster for compute-heavy code (loops, recursion, tight numeric work).

xs --backend <interp|vm|jit> <file.xs>

Select execution backend explicitly.

xs --backend vm program.xs
xs --backend interp program.xs
xs --backend jit program.xs
  • vm: bytecode VM (default; same as bare xs file.xs)
  • interp: tree-walker interpreter (use for plugin debugging or AST-level runtime hooks)
  • jit: JIT compilation on x86-64 and aarch64. A register-allocating

specialiser lowers bytecode through a small linear IR, runs liveness and linear-scan allocation over three callee-saved regs, and emits native code with SMI fast paths for integer arithmetic/compares, an XMM fast path for boxed floats, an inlined monomorphic inline cache for LOAD_GLOBAL, inline closure-upvalue access, a fused compare- and-branch peephole, and a refcount-pair elimination pass. Protos whose bytecode steps outside the supported subset drop to the bytecode VM; there is no template-JIT middle tier. Requires XSC_ENABLE_JIT at build time.


Self-Management

xs upgrade

Replace the current binary with the latest release from github.com/xs-lang0/xs.

xs upgrade         # prompt before replacing
xs upgrade --yes   # skip confirmation

Compares the current version against the GitHub releases API. If already up to date, exits 0 without touching anything. Verifies the SHA-256 of the downloaded binary against the sibling .sha256 file before swapping.

Not supported on Windows in this build - re-run the install script instead:

irm xslang.org/install.ps1 | iex

xs uninstall

Remove the xs binary from the system.

xs uninstall                 # remove only the binary (prompts)
xs uninstall --with-data     # also remove ~/.xs and ~/.xs_cache
xs uninstall --yes           # skip confirmation

Prompts for confirmation by default. Use --yes or -y to skip.


Interactive REPL

xs (no arguments) / xs repl

Start the interactive Read-Eval-Print Loop. Bindings persist across lines for the duration of the session.

xs                       # launch REPL
xs repl                  # same
printf '1 + 1\n:q\n' | xs repl  # pipe input, exit

Expression results print automatically, prefixed with => . Declarations (let, fn, struct, etc.) are registered silently.

xs 1.2.10
type :help for commands, :quit to exit (or Ctrl-D)
>> let x = 10
>> x * 3
=> 30
>> fn square(n) { n * n }
>> square(7)
=> 49

Multi-line input: keep typing after an unclosed (, [, or {. The prompt switches to .. until delimiters balance.

>> fn add(a, b) {
..   a + b
.. }
>> add(3, 4)
=> 7

Meta-commands:

CommandDescription
:help, :hShow this list
:quit, :qExit the REPL
:envList all bindings in scope with their kind and type
:clearReset to a fresh interpreter, dropping all bindings
:t <expr>Show the runtime type of an expression

Error recovery: parse errors and runtime errors print a diagnostic and re-prompt. The session does not exit.


Static Analysis

xs --check <file.xs> / xs check <file.xs>

Run semantic analysis (type checking, symbol resolution, exhaustiveness checks) without executing the script. Returns exit code 0 if clean, 1 if errors found.

xs --check mylib.xs
xs check mylib.xs
echo $?  # 0 = no errors

This catches type mismatches, undefined variables, non-exhaustive matches, and other semantic issues that can't be detected by parsing alone. Hindley-Milner type inference is wired in, so it can catch type errors even in unannotated code.

xs --lenient <file.xs>

Run with lenient mode: semantic errors are downgraded to warnings instead of blocking execution.

xs --lenient script.xs

Useful during development when you want to run partially-typed code.

xs --optimize <file.xs>

Run the AST optimizer before execution. Performs constant folding and other optimizations.

xs --optimize heavy_compute.xs

Code Quality Tools

xs lint [file|dir] [--fix]

Lint source files for style and correctness issues.

xs lint src/main.xs
xs lint .                 # lint current directory
xs lint src/ --fix        # auto-fix where possible

Checks performed:

  • Unused variables
  • Naming conventions (snake_case for variables/functions)
  • Unreachable code after return/break/continue
  • Empty blocks
  • Deeply nested code (>5 levels)
  • Shadowed variables

xs fmt <file.xs> [--check]

Format source files in canonical XS style.

xs fmt main.xs            # format in-place
xs fmt main.xs --check    # check without modifying (exit code 1 if changes needed)

Style rules: 4-space indent, canonical spacing around operators, consistent brace placement.

The --check flag is useful in CI pipelines: it reports whether formatting changes are needed without modifying files.

xs doc <file.xs|dir>

Generate documentation from source files.

xs doc src/lib.xs         # generate docs for a file
xs doc .                  # generate docs for all files
xs doc src/lib.xs > api.md

Extracts function signatures, struct fields, enum variants, traits, and type aliases. Outputs Markdown to stdout.


Testing & Benchmarking

xs test [pattern]

Run test files. Scans for files matching test_*.xs or *_test.xs.

xs test                   # run all tests
xs test math              # run tests matching "math"

Output:

Running tests...
  PASS  test_math.xs (0.012s)
  FAIL  test_parser.xs (0.005s)

Results: 1 passed, 1 failed, 2 total (0.017s)

Tests use assert(condition, message?) and assert_eq(a, b) for assertions. A test file fails if any assertion panics.

xs bench [pattern]

Run benchmark files. Scans for bench_*.xs or *_bench.xs.

xs bench                  # run all benchmarks
xs bench sort             # run benchmarks matching "sort"

Each benchmark is run 10 times. Reports min/max/avg execution time.


Profiling & Coverage

xs profile <file.xs> / xs --profile <file.xs>

Run a script with the sampling profiler enabled.

xs profile compute.xs

Output:

=== XS Profiler Report ===
Duration: 1.234s
Samples: 1234

  %time  samples  function       line
  45.2%      558  fibonacci         5
  23.1%      285  main             12

xs --trace-sample <rate> <file.xs>

Production sampling: only profile a fraction of executions.

xs --trace-sample 0.01 server.xs  # sample 1% of calls

The rate is a float between 0.0 and 1.0.

xs coverage <file.xs> / xs --coverage <file.xs>

Run a script with line and branch coverage tracking.

xs coverage mylib.xs

Output:

=== XS Coverage Report ===
File: mylib.xs
Lines:    45/60  (75.0%)
Branches: 12/16  (75.0%)

Uncovered lines: 23, 24, 42

Transpilation & IR Dumps

xs --emit <target> <file.xs>

Dump intermediate representations or transpile to other languages.

xs --emit ast script.xs       # print AST tree
xs --emit bytecode script.xs  # print VM bytecode
xs --emit ir script.xs        # print SSA IR (three-address form)
xs --emit js script.xs        # transpile to JavaScript
xs --emit c script.xs         # transpile to C
xs --emit wasm script.xs      # transpile to WebAssembly

xs transpile --target <js|c|wasm32|wasi> <file.xs>

Explicit transpile command with target selection.

xs transpile --target js app.xs > app.js
xs transpile --target c lib.xs > lib.c
xs transpile --target wasm32 module.xs
xs transpile --target wasi server.xs

Targets:

  • js: ES2020+ JavaScript
  • c: C11 source with xs_val runtime (supports inline c { ... } blocks)
  • wasm32: WebAssembly binary (.wasm)
  • wasi: WASI-compatible WebAssembly

The WASM backend is the least mature of the three -- it handles basic arithmetic and function calls but doesn't cover the full language yet.

xs build <file.xs> [-o out.xsc]

Compile a source file to bytecode without executing it. Writes a .xsc file: defaults to the same name with the .xsc extension, or a custom path via -o.

xs build app.xs                    # produces app.xsc
xs build app.xs -o dist/app.xsc   # write to a specific path
xs run app.xsc                     # run the compiled output

Debugging & Tracing

xs --debug <file.xs>

Run with the Debug Adapter Protocol (DAP) server. Connect from VS Code, Neovim, or any DAP client.

xs --debug app.xs

Supports: breakpoints, step/next, variable inspection, call stack.

xs dap

Start the DAP server directly (for editor integration).

xs dap

Supports: breakpoints (including conditional breakpoints), step in / next / step out, variable inspection, evaluate expressions, call stack. Supports stopOnEntry in the launch configuration to pause at the first line automatically. The VS Code extension wires this up automatically: no manual setup needed.

xs --record <file.xst> <file.xs>

Record an execution trace for time-travel debugging.

xs --record trace.xst program.xs

Records every function call, return, variable store, and I/O operation to a .xst trace file. Use --trace-deep to serialize complex values (arrays, maps, structs) as JSON in the trace output.

xs replay <trace.xst> / xs --replay <trace.xst>

Replay a recorded execution trace interactively.

xs replay trace.xst

Replay commands:

  • n: step forward
  • p: step backward
  • c: continue to end
  • g <n>: goto event number
  • q: quit

Error Diagnostics

xs --explain <code> / xs explain <code>

Show a detailed explanation of an error code. Error codes appear in diagnostic output (e.g., error[T0001]).

xs --explain T0001
xs explain S0003

Error code prefixes:

PrefixCategory
L0xxxLexer errors (invalid tokens, unterminated strings)
P0xxxParser errors (syntax errors, unexpected tokens)
T0xxxType errors (type mismatches, undefined types)
S0xxxSemantic errors (unused variables, shadowing, unreachable code)

File Watching

xs --watch <file.xs>

Run a script and re-execute automatically when the file changes.

xs --watch server.xs

Uses filesystem polling (300ms interval). On file change, the script is re-parsed and re-run with a fresh interpreter.


Package Management

xs new <name>

Scaffold a new XS project in a new directory.

xs new myapp

Creates:

myapp/
|-- xs.toml                 # package manifest
|-- src/main.xs             # hello world entry point
|-- .gitignore

xs init

Initialize an XS project in the current directory. Writes xs.toml, writes src/main.xs if absent, and adds a .gitignore if none exists. Fails if xs.toml already exists.

xs init

xs add <name>

Add a dependency. Accepts a bare package name or a user/repo shortform. Updates xs.toml and installs to xs_lib/.

xs add http-client
xs add user/repo

xs install [pkg]

Install dependencies.

xs install              # install all from xs.toml
xs install http-client  # install a specific package

Packages are installed to .xs_lib/ in the project directory. Global packages (installed via xsi get -g) go to /usr/local/xs/lib/.

xs remove <pkg>

Remove an installed package.

xs remove http-client

xs update [pkg]

Update dependencies to latest compatible versions.

xs update               # update all
xs update http-client   # update specific package

xs publish

Publish the current package to the XS registry. Reads the API token from $XS_REGISTRY_TOKEN, falling back to ~/.xs/credentials (set via xs login). Without a token the tarball is built locally and the path is printed so you can sideload it.

xs login           # one-time, stores ~/.xs/credentials
xs publish         # builds tarball, POSTs to reg.xslang.org

xs search <query>

Search the package registry. Hits reg.xslang.org/api/search, prints the top 20 results.

xs search json

xs login

Store a registry token at ~/.xs/credentials (chmod 600). Reads $XS_REGISTRY_TOKEN if set, otherwise prompts on stdin. Get the token from the registry account page.

xs logout

Delete ~/.xs/credentials.

xs whoami

Print the username and email associated with the stored token.

xs list

List packages installed in the current project (xs_lib/).

xs list

xs pkg <subcommand>

Alternative package management interface. Implemented subcommands: install, remove, update, list, add, search, publish.

xs pkg list             # list installed packages
xs pkg install foo      # install a package
xs pkg add user/repo    # add a dependency

IDE Integration

xs lsp [-s <lsp.xs>]

Start the Language Server Protocol server on stdin/stdout.

xs lsp
xs lsp -s /path/to/lsp.xs   # use a custom LSP implementation

The -s/--source flag lets you point at a specific lsp.xs script. The VS Code extension bundles its own lsp.xs and passes it via -s automatically.

Supported features:

  • Diagnostics (parse errors, type errors, semantic errors)
  • Hover (identifier info, inferred types)
  • Completion: keywords, builtins, and dot completion for types and modules
  • Signature help
  • Go to definition
  • Find references
  • Document symbols
  • Formatting
  • Rename
  • Code actions

Configure in your editor by pointing the LSP client to xs lsp.


Plugin System

xs --plugin <path>

Load a native plugin (.so/.dll/.dylib) before running the script.

xs --plugin ./my_plugin.so app.xs

Plugins must export xs_plugin_init(Interp*, int api_version).

xs --sandbox

Run plugins in sandboxed mode, restricting I/O and network access.

xs --sandbox --plugin untrusted.so app.xs

Global Flags

These flags work with any subcommand or when running scripts directly.

FlagDescription
--help / -hShow help. Works globally and per-subcommand.
--version / -VShow version string.
--no-colorDisable ANSI color output.
--checkRun semantic analysis only, do not execute.
--lenientDowngrade semantic errors to warnings.
--strictRequire type annotations on every binding and function.
--optimizeRun AST optimizer before execution.
--watchRe-run on file change.
--vmUse bytecode VM backend.
--jitUse JIT compilation backend.
--backend <name>Select backend: interp, vm, or jit.
--lspStart the LSP server (same as xs lsp).
--dapStart the DAP server (same as xs dap).
--emit <target>Dump IR or transpile: ast, bytecode, ir, js, c, wasm.
-e / --eval <code>Evaluate inline code.
--explain <code>Explain an error code.
--record <file>Record execution trace to .xst file.
--replay <file>Replay a .xst trace file.
--debug <file>Run with the DAP debug server attached.
--profileEnable sampling profiler.
--coverageEnable line coverage tracking.
--traceEnable execution tracer (provenance recording).
--trace-sample <rate>Set profiler sampling rate (0.0-1.0).
--trace-deepSerialize complex values as JSON in execution traces.
--gc-debugEnable GC debug prints.
--plugin <path>Load native plugin before execution.
--sandboxSandbox plugin execution.

Environment variables that affect runtime:

VariableEffect
XS_MAX_DEPTHInterpreter call-depth cap (default 500). On overflow the interpreter throws a catchable StackOverflow. The VM has its own growable stack and ignores this.
XS_JIT_CMPBRCompare+branch fusion peephole; defaults on. Set to 0, n, or N to disable.
XS_JIT_INLINESelf-recursive inliner; defaults on. Same disable spelling as above.
XS_JIT_REFOPTRefcount-pair elimination peephole; defaults on.

Flag placement: Global flags like --no-color, --vm, and --check can appear anywhere in the argument list: before or after the filename or subcommand. Flags can be stacked freely. For example, all of these work:

xs --no-color --check mylib.xs
xs mylib.xs --no-color --check
xs --check --vm mylib.xs         -- type-check then run on the VM

Per-subcommand help: Every subcommand supports --help:

xs test --help
xs lint --help
xs transpile -h
xs fmt --help

Building from Source

Makefile Targets

make                # default build (-O2)
make debug          # build with AddressSanitizer + UBSan (-g -O0)
make release        # optimized build (-O3, LTO, stripped)
make test           # run smoke tests
make clean          # remove build artifacts
make install        # install to /usr/local/bin/xs (builds release first)

Feature Flags

All features are enabled by default. Disable specific features by setting flags to 0:

make XSC_ENABLE_JIT=0       # build without JIT
make XSC_ENABLE_PLUGINS=0   # build without plugin support
FlagFeatureEnables
XSC_ENABLE_VMBytecode VM--vm, --emit bytecode
XSC_ENABLE_JITJIT compilation--jit, --backend jit
XSC_ENABLE_TRANSPILERTranspilers`--emit js\c\wasm, xs transpile`
XSC_ENABLE_TRACERExecution tracing--record, xs replay
XSC_ENABLE_LSPLanguage Serverxs lsp
XSC_ENABLE_DAPDebug Adapter--debug, xs dap
XSC_ENABLE_PROFILERSampling profilerxs profile, --profile
XSC_ENABLE_COVERAGECoverage trackingxs coverage, --coverage
XSC_ENABLE_DOCDoc generatorxs doc
XSC_ENABLE_FMTCode formatterxs fmt
XSC_ENABLE_PKGPackage manager`xs install\remove\update\publish`
XSC_ENABLE_EFFECTSAlgebraic effectseffect, perform, handle
XSC_ENABLE_PLUGINSPlugin loading--plugin, --sandbox
XSC_ENABLE_SANDBOXPlugin sandboxing--sandbox

If a feature is disabled at compile time, the corresponding CLI command will print an error saying the feature isn't available in this build.