CLI reference
All commands and flags exposed by the xs binary.
Summary
Canonical
XS Command Reference
Reference for the xs command-line tool. See STATUS.md for which subcommands are stable, partial, or planned.
> 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 8080Arguments 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 asxs <file.xs>, runs on the default VM backendxs run <file.xsc>: run a compiled bytecode file produced byxs build
xs run hello.xs
xs run app.xscxs -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.xsThe 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.xsvm: bytecode VM (default; same as barexs 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 confirmationCompares 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.
On Windows the running xs.exe is locked, so the upgrade renames the old binary to xs.exe.old, writes the new binary into the original path, then sweeps xs.exe.old opportunistically the next time xs is launched. Already-open shells continue running the old version until they're restarted.
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 confirmationOn Windows the binary is moved to xs.exe.old and scheduled for deletion at the next reboot, since the OS won't let a running .exe be removed in place.
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, exitExpression results print automatically, prefixed with => . Declarations (let, fn, struct, etc.) are registered silently.
xs 1.2.17
type :help for commands, :quit to exit (or Ctrl-D)
>> let x = 10
>> x * 3
=> 30
>> fn square(n) { n * n }
>> square(7)
=> 49Multi-line input: keep typing after an unclosed (, [, or {. The prompt switches to .. until delimiters balance.
>> fn add(a, b) {
.. a + b
.. }
>> add(3, 4)
=> 7Meta-commands:
| Command | Description |
|---|---|
:help, :h | Show this list |
:quit, :q | Exit the REPL |
:env | List all bindings in scope with their kind and type |
:clear | Reset 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 errorsThis 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.xsUseful 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.xsCode 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 possibleChecks 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.mdExtracts 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.xsOutput:
=== XS Profiler Report ===
Duration: 1.234s
Samples: 1234
%time samples function line
45.2% 558 fibonacci 5
23.1% 285 main 12xs --trace-sample <rate> <file.xs>
Production sampling: only profile a fraction of executions.
xs --trace-sample 0.01 server.xs # sample 1% of callsThe 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.xsOutput:
=== XS Coverage Report ===
File: mylib.xs
Lines: 45/60 (75.0%)
Branches: 12/16 (75.0%)
Uncovered lines: 23, 24, 42Transpilation & 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 WebAssemblyxs 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.xsTargets:
js: ES2020+ JavaScriptc: C11 source withxs_valruntime (supportsinline 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 outputDebugging & 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.xsSupports: breakpoints, step/next, variable inspection, call stack.
xs dap
Start the DAP server directly (for editor integration).
xs dapSupports: 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.xsRecords 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.xstReplay commands:
n: step forwardp: step backwardc: continue to endg <n>: goto event numberq: 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 S0003Error code prefixes:
| Prefix | Category |
|---|---|
L0xxx | Lexer errors (invalid tokens, unterminated strings) |
P0xxx | Parser errors (syntax errors, unexpected tokens) |
T0xxx | Type errors (type mismatches, undefined types) |
S0xxx | Semantic 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.xsUses 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 myappCreates:
myapp/
|-- xs.toml # package manifest
|-- src/main.xs # hello world entry point
|-- .gitignorexs 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 initxs 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/repoxs install [pkg]
Install dependencies.
xs install # install all from xs.toml
xs install http-client # install a specific packagePackages 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-clientxs update [pkg]
Update dependencies to latest compatible versions.
xs update # update all
xs update http-client # update specific packagexs 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.orgxs search <query>
Search the package registry. Hits reg.xslang.org/api/search, prints the top 20 results.
xs search jsonxs 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 listxs 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 dependencyIDE 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 implementationThe -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.xsPlugins 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.xsGlobal Flags
These flags work with any subcommand or when running scripts directly.
| Flag | Description |
|---|---|
--help / -h | Show help. Works globally and per-subcommand. |
--version / -V | Show version string. |
--no-color | Disable ANSI color output. |
--check | Run semantic analysis only, do not execute. |
--lenient | Downgrade semantic errors to warnings. |
--strict | Require type annotations on every binding and function. |
--optimize | Run AST optimizer before execution. |
--watch | Re-run on file change. |
--vm | Use bytecode VM backend. |
--jit | Use JIT compilation backend. |
--backend <name> | Select backend: interp, vm, or jit. |
--lsp | Start the LSP server (same as xs lsp). |
--dap | Start 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. |
--profile | Enable sampling profiler. |
--coverage | Enable line coverage tracking. |
--trace | Enable execution tracer (provenance recording). |
--trace-sample <rate> | Set profiler sampling rate (0.0-1.0). |
--trace-deep | Serialize complex values as JSON in execution traces. |
--gc-debug | Enable GC debug prints. |
--plugin <path> | Load native plugin before execution. |
--sandbox | Sandbox plugin execution. |
Environment variables that affect runtime:
| Variable | Effect |
|---|---|
XS_MAX_DEPTH | Interpreter 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_CMPBR | Compare+branch fusion peephole; defaults on. Set to 0, n, or N to disable. |
XS_JIT_INLINE | Self-recursive inliner; defaults on. Same disable spelling as above. |
XS_JIT_REFOPT | Refcount-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 VMPer-subcommand help: Every subcommand supports --help:
xs test --help
xs lint --help
xs transpile -h
xs fmt --helpBuilding 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| Flag | Feature | Enables | |||
|---|---|---|---|---|---|
XSC_ENABLE_VM | Bytecode VM | --vm, --emit bytecode | |||
XSC_ENABLE_JIT | JIT compilation | --jit, --backend jit | |||
XSC_ENABLE_TRANSPILER | Transpilers | `--emit js\ | c\ | wasm, xs transpile` | |
XSC_ENABLE_TRACER | Execution tracing | --record, xs replay | |||
XSC_ENABLE_LSP | Language Server | xs lsp | |||
XSC_ENABLE_DAP | Debug Adapter | --debug, xs dap | |||
XSC_ENABLE_PROFILER | Sampling profiler | xs profile, --profile | |||
XSC_ENABLE_COVERAGE | Coverage tracking | xs coverage, --coverage | |||
XSC_ENABLE_DOC | Doc generator | xs doc | |||
XSC_ENABLE_FMT | Code formatter | xs fmt | |||
XSC_ENABLE_PKG | Package manager | `xs install\ | remove\ | update\ | publish` |
XSC_ENABLE_EFFECTS | Algebraic effects | effect, perform, handle | |||
XSC_ENABLE_PLUGINS | Plugin loading | --plugin, --sandbox | |||
XSC_ENABLE_SANDBOX | Plugin 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.