CLI
Use cli for interactive console applications and args for argument parsing.
The source module cli.command adds structured subcommand support.
Argument parsing
cli.parseArgs(argv, spec) parses a list of command-line strings according to
a spec dict and returns the parsed values:
import cli;
import sys;
let opts = cli.parseArgs(sys.args(), {
"name": {"type": "string", "short": "n", "required": true},
"verbose": {"type": "bool", "short": "v"},
"port": {"type": "int", "default": 8080},
"tags": {"type": "list"}
});
io.println(opts["name"]);
if (opts["verbose"] as bool) {
io.println("verbose mode");
}
cli.help(spec) prints a usage summary derived from the spec. The low-level
args.parse(argv, spec) and args.help(spec) functions provide the same
interface without the interactive features.
Interactive prompts
let name = cli.prompt("Your name: ");
let pass = cli.password("Password: "); # input hidden
let pass2 = cli.secret("Confirm password: "); # same as password
let ok = cli.confirm("Continue? [y/N] "); # returns bool
let lang = cli.choose("Language: ", ["gb", "py", "js"]); # returns chosen string
let langs = cli.multiChoose("Languages:", ["gb", "py", "js"]); # returns list<string>
confirm returns true when the user types y or Y. choose displays the
options and validates the input, re-prompting on invalid choices.
multiChoose selects several options at once. On an interactive terminal it
shows an arrow-key checkbox list (up/down or j/k move, space toggles, a
toggles all, enter confirms, q or ctrl-c cancels). When stdin is not a
terminal (piped input, CI) it falls back to a numbered list and reads
comma-separated choices, so scripts and tests keep working. An optional third
argument pre-checks options by index:
let picked = cli.multiChoose("Pick services:",
["web", "db", "cache", "queue"],
[0, 1]); # web and db checked to start
Text styling
cli.style(text, options) returns an ANSI-styled string. cli.stripAnsi(text)
removes all escape sequences.
io.println(cli.style("Success", {"fg": "green", "bold": true}));
io.println(cli.style("Warning", {"fg": "yellow"}));
io.println(cli.style("Error", {"fg": "red", "bold": true}));
io.println(cli.style("Info", {"fg": "cyan", "italic": true}));
Style options:
| Key | Values |
|---|---|
fg |
"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "default" |
bg |
same colour names as fg |
bold |
true |
italic |
true |
underline |
true |
dim |
true |
stripAnsi is useful when writing styled output to a log file or when
detecting that stdout is not a terminal:
let message = cli.style("done", {"fg": "green"});
io.println(cli.stripAnsi(message)); # "done"
Tables
cli.table(rows, options) renders a list of dicts as an aligned text table
and returns the formatted string:
let rows = [
{"name": "Alice", "role": "admin", "active": "yes"},
{"name": "Bob", "role": "viewer", "active": "no"},
{"name": "Carol", "role": "editor", "active": "yes"}
];
io.println(cli.table(rows, {
"columns": ["name", "role", "active"],
"headers": ["Name", "Role", "Active"]
}));
Output:
Name Role Active
---- ------ ------
Alice admin yes
Bob viewer no
Carol editor yes
Options:
| Key | Description |
|---|---|
columns |
list of dict keys to include, in order |
headers |
list of header labels (defaults to the column key names) |
separator |
column separator string (default: two spaces) |
cli.command - structured subcommands
Import the source module cli.command when a tool has multiple subcommands or
needs a reusable command tree:
import cli.command as cmd;
let deploy = cmd.newCommand("deploy", "Deploy the application")
.option(cmd.newOption("env", "string").required().help("Target environment"))
.option(cmd.newOption("dry", "bool").help("Dry run, no changes"))
.option(cmd.newOption("tag", "string").help("Image tag to deploy"));
let parsed = deploy.parse(sys.args());
if (parsed == null) {
deploy.help();
sys.exit(1);
}
io.println("deploying to " + parsed["env"]);
Use newOption(name, kind) with:
.required()- fail if the option is absent.help(text)- add help text.default(value)- set a default value.short(char)- single-character alias
Progress bars and spinners
Import the source module cli.widgets for two terminal progress
indicators. Both draw to stderr, so they don't interfere with piped
stdout.
Spinner
widgets.Spinner(message) shows a rotating frame next to a message
while a task runs. The caller owns the cadence: call .tick() to
advance one frame, .update(message) to change the label, and
.stop(finalMessage?) to clear the line.
import cli.widgets as widgets;
let sp = widgets.Spinner("connecting");
for (let int i = 0; i < 20; i++) {
sp.tick();
# ... do a slice of work ...
}
sp.update("fetching data");
sp.tick();
sp.stop("connected");
| Method | Description |
|---|---|
Spinner(message) |
Create a spinner with an initial message. |
tick() |
Draw the next frame. |
update(message) |
Change the label shown after the spinner. |
stop(finalMessage = "") |
Clear the line; optionally print a final message. |
ProgressBar
widgets.ProgressBar(total, width = 30, label = "") renders a bar
like [#####-----] 50% (5/10) label. Use .advance(n = 1) for the
common increment, .set(value) for an explicit position,
.updateLabel(label) to change the trailing text, and .finish()
when done.
import cli.widgets as widgets;
let bar = widgets.ProgressBar(10, 20, "downloading");
for (let int i = 0; i < 10; i++) {
bar.advance();
# ... download one chunk ...
}
bar.finish("done");
| Method | Description |
|---|---|
ProgressBar(total, width = 30, label = "") |
Create a bar of width columns over total units. |
advance(n = 1) |
Add n to the current count and redraw. |
set(value) |
Set the current count to value and redraw. |
updateLabel(label) |
Change the trailing label and redraw. |
finish(finalMessage = "") |
Clear the line; optionally print a final message. |
cli.color - ANSI terminal styling
import cli.color as color;
import io;
io.println(color.bold(color.red("error: ") + "build failed"));
io.println(color.dim("hint: run with --verbose for details"));
Each helper wraps its argument with the matching ANSI escape sequence
and a reset. The helpers honour the NO_COLOR
convention: when NO_COLOR is set in the environment to any non-empty
value, every wrapper returns its input unchanged. This makes the same
code work on plain terminals, in CI logs, and in interactive shells
without an isTTY check.
Styles: bold, dim, italic, underline.
Foreground colors: black, red, green, yellow, blue, magenta,
cyan, white.
Background colors: bgBlack, bgRed, bgGreen, bgYellow, bgBlue,
bgMagenta, bgCyan, bgWhite.
| Function | Returns | Description |
|---|---|---|
color.isEnabled() |
bool |
true when NO_COLOR is unset or empty - i.e., when wrappers actually emit escape codes. |
To toggle color off at runtime, set the env var explicitly:
import sys;
sys.setenv("NO_COLOR", "1");