CLI reference
The langoost binary is the entry point for everything. (During development you
can substitute go run . for ./langoost.)
./langoost run [--debug] [--pprof :ADDR] [--watch] <file.goost> Execute a script
./langoost build [dir] Build src/main.goost → build/ (binary + run.sh + .env)
./langoost compile [--os OS] [--arch ARCH] [--jit] <file.goost> Compile one file to a binary
./langoost repl Interactive REPL (multiline + history)
./langoost server [--bind H] [--port N] [--auth-token T] Persistent HTTP execution server
./langoost test [dir] [-v] [-run FILTER] Run *_test.goost tests
./langoost bench [dir] [-run FILTER] [--tui] Run *_bench.goost benchmarks
./langoost install [--frozen] Install deps; write/verify langoost.lock
./langoost verify [--signed] Re-hash langoost_modules against langoost.lock
./langoost debug <file.goost> Interactive TUI debugger
./langoost dap [--port N] DAP debug adapter (stdio; --port for TCP)
./langoost disasm <file.goost> Disassemble bytecode
./langoost version Print version
run
Compile and execute a script:
$ ./langoost run hello.goost
Hello, world!
| Flag | Description |
|---|---|
--debug | Enable debug logging (the logging.debug level) |
--pprof :ADDR | Expose Go’s net/http/pprof on ADDR — visit /debug/pprof/ |
--watch | Re-run the script whenever a .goost file in its directory changes (hot reload) |
$ ./langoost run --watch app.goost # restart on save
$ ./langoost run --pprof :6060 app.goost # profile while it runs
--watch runs a supervisor that restarts the child process on every change;
--pprof is also accepted by server.
repl
Start an interactive session. The REPL supports multi-statement (multiline)
blocks and command history (arrow keys), persisted to ~/.langoost_history:
$ ./langoost repl
> let x = 21
> print(x * 2)
42
build
Build a project into a self-contained build/ directory. A project is a
folder with a src/ directory whose src/main.goost is the entry point:
myapp/
├── src/
│ └── main.goost ← entry point (required)
└── build/ ← created by `langoost build`
├── myapp ← compiled native binary (pure-Go, standalone)
├── run.sh ← loads .env, then execs the binary
└── .env ← default env config (created once, never overwritten)
$ ./langoost build # builds ./ (uses the directory name for the binary)
$ ./langoost build ./myapp # builds a specific project
$ ./myapp/build/run.sh # run it
| Flag | Description |
|---|---|
-o | Output binary name (default: the project directory name) |
--os / --arch | Cross-compile target (linux/arm64, …) |
--no-aot | Skip AOT translation; embed the bytecode + interpreter instead |
--force-env | Overwrite build/.env if it already exists |
The binary is pure-Go and standalone. Deploy by copying the build/ directory
to a server and running run.sh — no Go toolchain or Langoost install needed.
Rebuilding never clobbers a build/.env you’ve edited (unless you pass
--force-env).
compile
Compile a single file to a standalone native binary — handy for scripts that
aren’t structured as a project. Cross-compile with --os/--arch:
$ ./langoost compile app.goost # host platform
$ ./langoost compile -o app --os linux --arch arm64 app.goost # e.g. a Pi
| Flag | Description |
|---|---|
-o | Output binary name (default: the input filename without extension) |
--os | Target OS for cross-compilation (linux, windows, darwin, …) |
--arch | Target architecture (amd64, arm64, …) |
--jit | AOT-compile the script to native Go (no interpreter at runtime); falls back gracefully on unsupported opcodes |
Use build for an app you’ll deploy (it adds run.sh + .env); use
compile for a one-off binary from a single file.
test
Discover *_test.goost files under [dir] (default .) and run every
top-level function named test_*. See the testing guide
for writing tests with the assert module.
$ ./langoost test
$ ./langoost test ./tests -v -run parse # verbose, only tests matching "parse"
| Flag | Description |
|---|---|
-v | Verbose — list every test, not just failures |
-run FILTER | Only run tests whose name contains FILTER |
The process exits non-zero if any test fails.
bench
Discover *_bench.goost files and run every top-level function named bench_*,
timing each and reporting nanoseconds per iteration:
$ ./langoost bench
$ ./langoost bench -run parse --tui
| Flag | Description |
|---|---|
-run FILTER | Only run benchmarks whose name contains FILTER |
--tui | Live-updating terminal UI instead of plain text |
--jit-trace | Opt into the experimental trace JIT (for measurement) |
server
Run as a long-lived execution server. The process stays warm and serves
requests with no cold start. It binds loopback (127.0.0.1) by default —
exposing it publicly requires an auth token:
$ ./langoost server # local only
$ ./langoost server --bind 0.0.0.0 --auth-token "$TOKEN" # public, authenticated
| Flag | Default | Description |
|---|---|---|
--bind | 127.0.0.1 | Interface to bind; 0.0.0.0 exposes it publicly (needs --auth-token) |
--port | 8080 | HTTP port |
--auth-token | $LANGOOST_AUTH_TOKEN | Bearer token required for /execute and /cache |
--dir | . | Directory searched for .goost files |
--pprof :ADDR | off | Expose Go’s net/http/pprof |
--max-source-bytes | 1 MiB | Cap on submitted source/file size |
--allow-stdlib | all | Comma-separated stdlib allowlist (empty = allow all) |
--unsafe-allow-no-auth | off | Permit a public bind with no token — never use in production |
It exposes an HTTP API — POST /execute, GET /health, and DELETE /cache.
See the execution server guide for request and
response shapes and the auth model.
install
Fetch the dependencies declared in ./langoost.json into ./langoost_modules/:
$ ./langoost install
Installing 2 dependencies into langoost_modules/
| Flag | Default | Description |
|---|---|---|
--manifest | langoost.json | Path to the manifest |
--dir | langoost_modules | Where to install packages |
--frozen | off | Install exactly what langoost.lock pins; fail on any mismatch |
A regular install writes a langoost.lock lockfile recording the resolved
version and content hash of each dependency. Commit it; then install --frozen
reproduces the exact same dependency tree and fails if anything has changed —
ideal for CI and reproducible builds.
See modules & imports for the manifest format and how installed packages are resolved.
verify
Re-hash everything in langoost_modules/ and check it against langoost.lock —
detects tampering or drift without reinstalling:
$ ./langoost verify
$ ./langoost verify --signed # also require a valid signature per entry
| Flag | Default | Description |
|---|---|---|
--dir | . | Project directory containing langoost.lock |
--modules | langoost_modules | Installed-modules directory |
--signed | off | Require every lockfile entry to carry a valid ed25519 signature from a trusted signer |
--trusted-keys | $LANGOOST_TRUSTED_KEYS | Path to the trusted-keys file |
debug
Launch an interactive terminal debugger — a source viewer with breakpoints
and stepping, no editor required (for editor debugging, use dap):
$ ./langoost debug app.goost
Pass --stop-on-entry=false to start running immediately instead of pausing on
the first line. See the debugging guide.
dap
Start the Debug Adapter Protocol server so an editor can debug .goost files.
Defaults to stdio (what editors expect); --port N listens on TCP instead:
$ langoost dap # stdio
$ langoost dap --port 4711 # TCP
See the debugging guide for VS Code and Neovim setup.
disasm
Print the compiled bytecode for a script — useful for understanding what the compiler produces:
$ ./langoost disasm examples/math.goost
=== examples/math.goost (16 instructions) ===
0000 [ 3] CONST #0 (<fn add/2>)
0001 [ 3] DEF_GLOBAL #1 (add)
...
=== add (6 instructions) ===
0000 [ 4] LOAD_LOCAL slot=0
0001 [ 4] LOAD_LOCAL slot=1
0002 [ 4] ADD
0003 [ 4] RETURN
Error messages
Runtime and compile errors include the file name and line (and column for lex/parse errors):
main.goost:12: undefined variable "x"
main.goost:7: function "add" expects 2 arguments, got 3
main.goost:3: array index 5 out of bounds (len=3)
script.goost:4:12: expected }, got EOF
Uncaught runtime errors print a source-mapped stack trace — each frame shows
its function and file:line, from the throw site down to the top-level script:
runtime error: division by zero
at level3 (math.goost:9)
at level2 (math.goost:5)
at level1 (math.goost:2)
at <script> (main.goost:14)
For how the pipeline fits together, see architecture.