Command Line (CLI)
Luma provides a unified cli handle for working with command-line arguments, flags, interactive user input, and program output.
The API is designed around type-driven parsing, with simple defaults for common cases and optional configuration for advanced usage.
A single cli handle covers all command-line interaction in a Luma program.
Creating a CLI handle
c: cli = cli()- Creates a CLI handle for the current program
- Command-line arguments are read implicitly
- Only one
clihandle is typically needed
Named flags
Flags are named arguments passed using --name=value, --name value, or short forms like -n value.
Basic flags
c: cli = cli()
name: str = c.flag("name", "guest")
count: int = c.flag("count", 0)
verbose: bool = c.flag("verbose", false)Command-line usage:
luma run app.luma --name=test --count=5 --verboseThe declared type determines how the value is parsed.
Type-driven parsing
The type of the variable receiving the flag controls parsing behavior:
name: str = c.flag("name", "guest")
port: int = c.flag("port", 8080)
rate: float = c.flag("rate", 1.5)
debug: bool = c.flag("debug", false)If a flag value cannot be parsed into the declared type, the program exits with an error.
Optional flags (no default)
Use optional types when a flag may or may not be provided.
config: ?str = c.flag("config")
limit: ?int = c.flag("limit")
if config != nil {
print("Using config: ${config}")
}- Returns
nilif the flag is not present - Invalid provided values still cause an error
Boolean flags
Boolean flags follow standard CLI conventions:
verbose: bool = c.flag("verbose", false)| Command | Result |
|---|---|
--verbose |
true |
--verbose=true |
true |
--verbose=false |
false |
| not provided | false |
Positional arguments
Arguments without a -- prefix are treated as positional.
luma run app.luma file1.txt file2.txt --verboseGet all positional arguments
files: [str] = c.args()Result:
["file1.txt", "file2.txt"]Get positional argument by index
first: ?str = c.arg(0)
second: ?str = c.arg(1)
third: ?str = c.arg(2)Returns nil if the index is out of range.
Mixing flags and positional arguments
Flags can appear anywhere on the command line.
luma run app.luma input.txt --format=json output.txt --verboseformat: str = c.flag("format", "text")
verbose: bool = c.flag("verbose", false)
files: [str] = c.args() // ["input.txt", "output.txt"]List flags (repeatable flags)
Flags declared as lists accumulate values across multiple occurrences.
luma run app.luma --tag=a --tag=b --tag=ctags: [str] = c.flag("tag", list(str))
// tags == ["a", "b", "c"]
ports: [int] = c.flag("port", list(int))- Order is preserved
- If the flag is not provided, the list is empty
- Single occurrences still produce a list
User input (prompts)
Interactive input is handled via prompt().
This replaces the previously planned wait() function.
Basic prompt
age: int = c.prompt()
name: str = c.prompt()
price: float = c.prompt()
confirm: bool = c.prompt()The declared type determines how input is parsed.
Prompt with message
name: str = c.prompt("What's your name? ")
age: int = c.prompt("Enter your age: ")Prompt with options
For advanced behavior, pass an options map.
username: str = c.prompt("Username: ", {
default: "guest",
timeout: 30,
validate: u -> u != ""
})Prompt options
| Option | Type | Description |
|---|---|---|
default |
T |
Value used when input is empty |
timeout |
int |
Seconds to wait before timeout |
validate |
(T) -> bool |
Validation function |
Prompt with default
port: int = c.prompt("Port: ", {
default: 8080
})Pressing enter without input selects the default value.
Prompt with validation
age: int = c.prompt("Age (18+): ", {
validate: a -> a >= 18
})Invalid input automatically re-prompts.
Prompt with timeout
Requires an optional type.
answer: ?str = c.prompt("Quick answer (5s): ", {
timeout: 5
})
if answer == nil {
print("Too slow!")
}Output capture
The CLI handle can capture output produced by print().
Basic capture
c.record()
print("Hello")
print("World")
output: str = c.stop()Captured output:
Hello
WorldSelective capture
print("Not captured")
c.record()
print("Captured")
output: str = c.stop()Multiple captures
c.record()
first: str = c.stop()
c.record()
second: str = c.stop()Capture for testing
fn test_greeting() {
c: cli = cli()
c.record()
greet("Alice")
output: str = c.stop()
if output == "Hello, Alice!\n" {
print("Test passed")
}
}Complete example
c: cli = cli()
verbose: bool = c.flag("verbose", false)
output_file: ?str = c.flag("output")
files: [str] = c.args()
if files.len() == 0 {
filename: str = c.prompt("Enter filename: ")
files = [filename]
}
files.walk(i, f) -> {
if verbose {
print("Processing: ${f}")
}
}
if output_file != nil {
confirm: bool = c.prompt("Save results? (true/false): ")
if confirm {
c.record()
print_results()
results: str = c.stop()
file(output_file).write(results)
}
}Summary
cli()provides a single entry point for command-line interaction- Flags and prompts are parsed based on declared types
- Optional types represent absence, not errors
- List flags support repeated values naturally
- Output capture enables testing and processing
- The common case stays simple; advanced behavior is explicit