Reference · v0.0.1
Bela CLI reference
The @bela/cli package ships one binary, bela, with three real subcommands — init, train, eval — plus help. All flag names and defaults below are verified against packages/cli/src/cli.ts.
Today vs. in-repo dev. Bela is live on npm as bela-ai (the unscoped name bela is blocked by npm's name-similarity filter; the CLI bins still install as bela and bela-mcp), so npm install bela-ai then npx bela … works today for consumers. Inside this monorepo, however, the CLI's init/train/eval functions are exercised via packages/cli/test/cli.test.ts (vitest) or run with npx tsx against packages/cli/src/cli.ts directly, because the workspace packages (@bela/core, @bela/model) resolve to TypeScript source ("main": "src/index.ts") rather than a compiled dist/.
A plain node packages/cli/bin/bela.mjs … invocation from inside the repo only works end-to-end once the packages are built (npm run build). This page documents the CLI's real behaviour either way; the worked session below was run through the actual runInit/runTrain/runEval functions, not hand-written.
bela init [dir]
Scaffolds a starter agent.tools.mjs in dir (defaults to the current directory; also accepts --dir <path>).
| Flag | Default | Notes |
|---|---|---|
| [dir] (positional) | cwd | Current working directory. Ignored if it starts with --. |
| --dir <path> | — | Overrides the positional argument if both are given. |
- Creates
dir(and any missing parent directories) if it doesn't exist. - Refuses to overwrite: throws
refusing to overwrite: <path> already existsifagent.tools.mjsis already present indir. - The scaffold defines two example entities (
Room,Guest), aconfirm: truetool (sendCheckoutReminder), and aroles-gated tool (bookRoom) with anumber()and adateRange()param — a working reference for all four param/tool features documented in the integration guide.
$bela init ./my-agent created ./my-agent/agent.tools.mjs
Exit code 0 on success; non-zero (uncaught error printed to stderr) if the file already exists or the directory can't be created.
bela train --config <path>
Trains a model from a config file and saves it if it clears the quality gate.
| Flag | Default | Notes |
|---|---|---|
| --config <path> | required | Path to a config module whose default export is a defineAgent(...) result. Throws --config is required if omitted. |
| --out <path> | bela-model.json | Where the trained artifact is written (only if the gate passes or --force). |
| --seed <n> | 42 | Passed to trainModel's dataset/weight RNG seeding. |
| --per-tool <n> | 200 | Examples synthesized per tool for training/test data. |
| --epochs <n> | 12 | Training epochs for both the intent classifier and slot tagger. |
| --min-intent <n> | 0.9 | Gate threshold for intentAcc (see meetsGate). |
| --min-slot <n> | 0.8 | Gate threshold for slotExact. |
| --force | off | Any value other than the literal string true is ignored — pass --force (parsed as "true" since it has no following value) to save even if the gate fails. |
Behavior:
- Loads the config (
import(configPath), so.mjsconfigs run directly; see the tsx note below for.tsconfigs). - Runs
trainModel(config, { seed, perTool, epochs })and prints the metrics line:intentAcc=<n> slotExact=<n> testCount=<n>. - Checks
meetsGate(metrics, { intentAcc: minIntent, slotExact: minSlot }). - If the gate passes, or
--forceis set, callssaveModel(model, out)and printssaved model to <out>(with a(gate failed, --force)suffix if it was forced). - If the gate fails and
--forcewasn't given, nothing is saved and it printsgate failed (need intentAcc>=<n>, slotExact>=<n>); not saving. Use --force to override.
Exit code: the bela bin (packages/cli/bin/bela.mjs) calls process.exit(1) when train did not save (gate failed, no --force); otherwise exit code 0. Any thrown error (bad config path, --config missing) is caught at the top level, printed to stderr, and also exits 1.
bela eval --config <path> --model <path>
Re-evaluates a previously saved model against a freshly generated test set from the same config.
| Flag | Default | Notes |
|---|---|---|
| --config <path> | required | Same config module used (or compatible with) training. Throws --config is required if omitted. |
| --model <path> | required | Path to a saveModel-produced JSON artifact. Throws --model is required if omitted. |
| --seed <n> | 42 | Seeds the regenerated dataset — must match the seed AND --per-tool used at training time to reproduce the same split. |
| --per-tool <n> | 200 | Examples synthesized per tool for the regenerated dataset — must match the value used at training time (--per-tool on bela train) to reproduce the same split; otherwise eval silently regenerates against a different-sized dataset. |
Behavior: loads the config and model, re-collects entity values (collectEntityValues), regenerates a dataset with the given seed and --per-tool (generateDataset), evaluates the loaded model against the test split, and prints the same metrics line as train: intentAcc=<n> slotExact=<n> testCount=<n>.
As of P4, loadModel validates the artifact's shape before returning it — a corrupted or hand-edited model file fails fast with a message like invalid model artifact: intents must be a string array (also checked: version, params, tagLabels, positive-integer buckets/dim, and that each embedding/weight/bias array is exactly the expected length) — instead of crashing deep inside evaluate/predict with an opaque error.
Exit code 0 on success; a thrown error (missing flags, invalid model artifact, bad config) is caught at the top level and exits 1.
bela help
Prints the built-in usage text (also shown when no command, or an unknown command, is given):
bela — tiny self-training agent CLI
Usage:
bela init [dir] scaffold a starter agent.tools.mjs
bela train --config <path> train a model from a config
[--out <path>] [--seed <n>] [--per-tool <n>] [--epochs <n>]
[--min-intent <n>] [--min-slot <n>] [--force]
bela eval --config <path> --model <path> evaluate a saved model
[--seed <n>] [--per-tool <n>]
bela help show this message
Exit code 0.
.mjs vs. TypeScript configs
runInit scaffolds agent.tools.mjs, and both train/eval load the config with a plain dynamic import() of the given path (pathToFileURL(configPath).href) — no TypeScript transform happens in the CLI itself. That means:
- A
.mjs(or.jswith"type": "module"in scope) config runs directly — this is the supported default and whatbela initscaffolds. - A
.tsconfig is not transpiled by the CLI; runningbela train --config agent.tools.tswill fail with a syntax/module error under plainnode. If you want to write your config in TypeScript, run the CLI throughnpx tsx(e.g.npx tsx node_modules/.bin/bela train --config agent.tools.ts …once published, or thenpx tsx packages/cli/src/cli.tsequivalent inside this monorepo) so.tsimports are transpiled on the fly — the CLI's own logic doesn't change either way, only how the config file gets loaded.
Worked session
This is a real transcript — bela init, then train, then eval — run through the actual CLI functions (numbers are not fabricated; see intentAcc/slotExact/testCount below):
$bela init ./my-agent created ./my-agent/agent.tools.mjs$vim ./my-agent/agent.tools.mjs# replace the two TODO resolve() calls# with real queries against your DB/API$bela train --config ./my-agent/agent.tools.mjs --out ./my-agent/bela-model.json intentAcc=0.9891 slotExact=1.0000 testCount=92 saved model to ./my-agent/bela-model.json$bela eval --config ./my-agent/agent.tools.mjs --model ./my-agent/bela-model.json intentAcc=0.9891 slotExact=1.0000 testCount=92
The unedited starter config (two entities, two tools) already clears the default gate (intentAcc>=0.9, slotExact>=0.8) out of the box — real projects with more tools/utterances should expect similar or better numbers once entity resolve() is wired to real data, since more/varied rows give the model more to train and test on. If a change to your config drops metrics below the gate, train refuses to save (see above) — either fix your utterances/entities or explicitly --force a save while iterating.
Exit codes summary
| Command | 0 | 1 |
|---|---|---|
init | scaffold written | directory exists / can't write |
train | gate passed, or --force used | gate failed and no --force; or a thrown error (bad --config, bad config module) |
eval | evaluation printed | missing --config/--model, invalid model artifact, or bad config module |
help | always | — |