Tooling
One binary, lumina, does everything: run, build, test, lint, fix, format, explain, manage packages, and serve the editor. There is no separate package manager, test runner or formatter to install.
Run and build: JIT vs AOT
$ lumina run app.lumina # JIT: compile in memory and run now
$ lumina run app.lumina --name=Ann # everything after the file goes to the program
$ lumina build app.lumina -o app # AOT: native executable via LLVM
$ ./app
Both go through the same pipeline: source to AST to HIR to MIR to LLVM IR. run JIT-compiles it for a fast edit-run loop. build produces an optimized native binary. They are the same compiler and the same semantics, so the choice depends only on when you want the machine code.
Useful build flags:
| Flag | Effect |
|---|---|
-O 0..3 | optimization level (default 2) |
--release | release build: strict capability enforcement, W408 becomes an error |
-g | keep the symbol table in the binary (stripped by default) |
--emit-llvm, --emit-asm | inspect the generated code |
--emit ast-json | dump the AST for tools |
--no-gc | every function reachable from main must be GC-free, otherwise E2052 or E2063; see Memory |
--no-warn-gc-fallback | silence the GC-fallback warnings that build prints by default |
lumina run --build runs through the AOT path instead, which is handy for benchmarking.
lumina build links with --gc-sections and strips the symbol table by default, so small programs produce small binaries; a program that needs no garbage collector (see --no-gc) shrinks further. Build with -g to keep the symbols for nm and profilers; source-level debug info for Lumina code itself is designed.
Global switches that work on every command:
--overflow=strictturns integer overflow into a deterministic trap. The default is two's-complement wraparound.--strictturns every lint into an error, for CI.--await-elision=onenables the colorless form, where calls to async functions need noawaitin progress (see Async).
Lint and fix
function greet(name: String): String {
return "Hello, " + name
}
function main(console: Console): Integer {
mut total = 0
console.println(greet("Ada"))
return total
}
$ lumina lint lintme.lumina
lintme.lumina:2:12: info [W120] string built with `+`; a template string ``…{x}…`` is the idiomatic form (S01)
lintme.lumina:6:5: info [W121] binding `total` is declared `mut` but never reassigned; drop `mut` (I02)
lintme.lumina:1:1: info [W127] this function body is a single `return`; use an expression body (`= expr`) instead of `{ return expr }`
$ lumina fix --diff lintme.lumina
-function greet(name: String): String {
- return "Hello, " + name
-}
+function greet(name: String): String = "Hello, " + name
...
- mut total = 0
+ total = 0
summary: 2 fixed, 1 need manual action
The lints teach the idiomatic style from Language_Idiomatic.md. Each one cites the idiom it enforces, such as S01 or I02. lumina fix applies the mechanical ones: --diff previews without writing, --only W121 narrows the run to given codes, -n N caps the number of fixes, and --json emits a structured report. Fixing interactively with -i is designed. To suppress a lint, put // lumina-ignore[W120] on the offending line or the line above it. lumina lint --help lists every rule.
Explain and help
Every error and warning has a code, and the compiler can explain it:
$ lumina explain E3009
E3009 — Mutating an immutable binding: Attempted mutation of immutable (non-`mut`) binding
Fix: Declare binding as `mut` if reassignment needed
lumina help <topic> shows embedded documentation, and lumina help --search move searches all of it offline. lumina agent-guide prints a version-matched prompt that teaches an AI coding agent the language; Built for Agents covers the whole agent feedback loop.
Tests
$ lumina test # every @Test in the current directory
$ lumina test src/ -f parse # filter by name
$ lumina test --watch # re-run on save
$ lumina test --coverage # needs an LLVM coverage toolchain (llvm-cov) on PATH
@Test functions live next to the code. Pure tests also run at compile time, so a failing one fails the build (see Capabilities). @Test(integration) marks tests that may use real capabilities, and you run them with --integration. lumina run --provide FileSystem=MemoryFileSystem swaps a capability implementation from the command line.
Format, verify, expand
lumina formatis the canonical formatter, so style is not up for debate.lumina verifychecks syntax and types without generating code, which makes it fast in pre-commit hooks.lumina expandprints the source after every compile-time annotation has expanded, so macros and derives are never a black box.lumina interactivestarts a REPL.
Projects and packages
$ lumina new myapp # templates: app, library, wasm, agent
$ cd myapp
$ lumina package add <git-url> # add a git dependency, resolve, pin in lumina.lock
$ lumina package install
Dependencies are git-based and pinned by commit in lumina.lock. The package tool goes beyond install and update:
whyshows why a package is in the dependency tree.list,tidyandvendor(for fully offline builds) are there too.sbomandlicensessupport supply-chain reporting.auditchecks the lockfile against an advisory store.capabilitiesandverifyaudit which effects each dependency uses.verifyre-derives a dependency's real capability usage from its source and fails if the dependency uses more than it declares. A logging library that secretly opens sockets gets caught.
Editors and debuggers
lumina lsp(aliaslanguage-server) is a Language Server Protocol server for any LSP editor.Language_Toolchain.mdlists the editor features it targets: navigation, hover docs, rename and code actions.lumina dapis a Debug Adapter Protocol server over stdio.- An MCP client exists in
stdlib/mcp/for programs that talk to MCP servers. An MCP server exposes the toolchain itself to agents designed.
Language_Toolchain.md is the full reference.
Next: More language features