cli/prompt.nu — interactive prompts over std/term + core/io.
All prompts write to stderr (so a command's stdout stays clean for pipes) and read a line from stdin. cli_password suppresses the echo when stdin is a real terminal, and degrades to a plain read otherwise.
@ cli_prompt s question → StringAsk a free-text question; returns the entered line (trailing newline stripped by read_line). Empty String on EOF.
@ cli_confirm s question b def → bYes/no confirmation. def is returned on a bare Enter or EOF. Only the first character is inspected (y/Y → true, n/N → false, else the default).
@ cli_password s question → StringRead a secret. When stdin is a TTY the terminal echo is disabled for the duration; otherwise this is a normal (echoed) line read. No in-line editing (backspace) in the no-echo path — kept intentionally minimal.
cli/cli.nu — the ergonomic command-line facade over the stdlib.
The stdlib ships a flat argument parser (std/args), terminal control (std/term: isatty + ANSI + a line editor), stdin/stdout (core/io) and the process environment (ext/env). What every CLI re-invents on top is the same glue: a subcommand dispatch cascade, typed flag accessors with env fallbacks and defaults, coloured --help / --version / usage, error → exit-code handling, and interactive prompts.
Cli collapses that into one object:
: *Cli c ( cli_new greet a tiny greeter 1.0.0 ) ( cli_flag_str c name 110 NAME who to greet world ` ) ( cli_flag_bool c loud 108 shout it ) ( cli_cmd c hello print a greeting \ CliCtx x → i { : String who ( ctx_str x name ) ( nurl_print hello, ) ( nurl_print ( string_data who ) ) ( nurl_print \n` ) ( string_free who ) ^ 0 } ) ^ ( cli_run c ) // parse argv, route, auto --help/--version → exit code
Scope: this facades the command-line surface (args + env + stdin + tty / colour / prompts + exit codes). It deliberately does NOT absorb terminal widgets (tables/spinners), config-file loading, or logging — those compose as their own packages.
Memory model: cli_new returns a heap *Cli, mutable across the builder calls; free it with cli_free. Flags and commands accumulate into its Vecs. cli_run owns the parse and frees everything it allocates.
: CliFlag: CliFlag {
String long
i short // short-option char code, 0 = none
String metavar // value placeholder in help (empty for bool)
String help
i kind
String dflt // default rendered as text (empty = none)
String env // environment-variable fallback (empty = none)
}
Flag kinds. 0 = string 1 = int 2 = bool (presence) 3 = float
: CliCmd: CliCmd {
String name
String help
( @ i CliCtx ) handler
}
: Cli: Cli {
String prog
String about
String version
( Vec CliFlag ) globals
( Vec CliCmd ) cmds
}
: CliCtx: CliCtx {
ArgParser parser
* Cli cli
String cmdname
}
Passed to every command handler; wraps the parsed args plus a back-ref to the Cli so accessors can resolve env fallbacks and defaults.
: ~ b g_cli_color F@ cli_new s prog s about s version → *Cli@ cli_free * Cli c → v@ cli_flag_bool * Cli c s long i short s help → v@ cli_flag_str * Cli c s long i short s metavar s help s dflt s env → v@ cli_flag_int * Cli c s long i short s metavar s help i dflt s env → v@ cli_flag_float * Cli c s long i short s metavar s help f dflt s env → v@ cli_cmd * Cli c s name s help ( @ i CliCtx ) handler → v@ cli_default * Cli c ( @ i CliCtx ) handler → vDefault handler — the whole program IS the command (psql/redis-cli style: bare invocation acts, positionals like a connection URL are arguments, not subcommand names). Stored as the reserved empty-name command. When set: a first non-option token that matches no registered command routes here (and stays readable as ctx_arg 0), and a bare invocation runs it instead of printing usage. Registered subcommands still win when they match.
@ ctx_str CliCtx x s name → String@ ctx_int CliCtx x s name → i@ ctx_float CliCtx x s name → f@ ctx_bool CliCtx x s name → b@ ctx_nargs CliCtx x → iNumber of positional arguments after the command token. Under the default handler (empty cmdname) there is no command token to skip — every positional (e.g. a connection URL) is an argument.
@ ctx_arg CliCtx x i idx → StringThe idx-th positional after the command (0-based); empty String if absent. args_positionals hands back a BORROW of the parser's own vector — read from it and copy out the one element; never free it here.
@ ctx_stdin CliCtx x → StringSlurp stdin to EOF (for pipe-friendly commands).
@ ctx_cmd CliCtx x → sThe command name that was invoked.
@ cli_run * Cli c → iParse the real argv, route to a subcommand, and return its exit code. Handles --help / --version, unknown commands, and parse errors.