Modern command-line toolkit for Alya with typed flags, options, subcommand routing, generated help screens, and structured parse errors.
- β‘ Native & Zero-Dependency: Pure Alya implementation, no runtime dependencies, compiled by the Alya toolchain
- π§© Layered Architecture: Public facade (
src/lib.alya), data models (src/types.alya), and focused engines (src/core/) - ποΈ Complete Option Grammar: Long (
--output dist), inline (--output=dist), short (-o dist), attached (-oDist), clustered (-abc), negated (--no-color), repeatable (-I a -I b), and the--terminator - π³ Hierarchical Subcommands: Nested command trees, per-command options and arguments, and short aliases (
buildβb) - π‘οΈ Validation & Diagnostics: Required options, choice lists, required arguments, unknown options/commands β collected as structured
CliErrorvalues instead of aborting - π§Ύ Generated Usage Screens: Column-aligned help screens for the application and for every command, with defaults, choices, and environment hints
- π Environment Fallback: Options can read their value from an environment variable when omitted on the command line
- π Public/Private Visibility:
pubmarks the public surface; the shared internals insrc/core/internal.alyastay private to the package - π§ͺ Dual API Style: A fluent builder (
app.flag(...)) and a functional API (cli::add_flag(app, ...)) over the same engines - π© Feature-Gated Slices:
help(usage screens),strict(throwing/recovering parses), andenv(environment fallback) can be enabled independently - 𧬠Modern Alya: Structural interfaces,
enumvocabularies, nullable types,try/catchrecovery, and typed accessors (get_int,get_bool)
cli/
βββ .alyafmt # Formatter configuration
βββ .alyalint # Linter configuration (rules, exclusions, severity overrides)
βββ .alyatest # Test runner configuration
βββ .editorconfig # Uniform formatting rules across IDEs and editors
βββ .gitattributes # Line ending and diff rules
βββ .gitignore # Ecosystem standard ignore filters
βββ alya.toml # Package manifest with dependencies, [features] and metadata
βββ LICENSE # MIT license
βββ README.md # This document
βββ .github/ # CI, release, code scanning, and issue templates
βββ .vscode/ # VS Code workspace settings, DAP launch configurations & tasks
βββ src/
β βββ lib.alya # Public API facade (thin wrappers delegating to core/* engines)
β βββ types.alya # Data models, pub enums, pub structs, factories & error helpers
β βββ core/
β βββ spec.alya # Specification builders: applications, commands, options, arguments
β βββ parser.alya # Parsing engine: token classification, binding, validation
β βββ formatter.alya # Usage screen, version banner, and diagnostic rendering (`help`)
β βββ errors.alya # Strict parsing slice: throwing and recovering entry points (`strict`)
β βββ internal.alya # Private string and model helpers shared inside the package
βββ tests/
β βββ test_basic.alya # Application setup, flags, options, typed accessors
β βββ test_flags.alya # Clustered, negated, attached, and repeatable options
β βββ test_commands.alya # Subcommands, aliases, nesting, command-scoped options
β βββ test_validation.alya # Required options, choice lists, unknown input
β βββ test_formatter.alya # Usage screens and diagnostic reports (`help` slice)
β βββ test_strict.alya # Strict parsing and error recovery (`strict` slice)
βββ examples/
β βββ demo.alya # Comprehensive runnable walkthrough of the package
βββ benches/
βββ bench_basic.alya # Micro-benchmarks for the hot paths
Note
Visibility & Modularity: Symbols annotated with pub are exported to external consumers. src/core/internal.alya declares no pub symbol on purpose: an Alya module without public items shares its whole surface with sibling modules, so the shared helpers stay reachable inside the package while remaining invisible to package consumers.
Add cli to the [dependencies] section in your alya.toml:
[dependencies]
cli = { git = "https://github.com/alya-lang/cli", branch = "main" }Or install it directly using the Alya package CLI:
alya add cli --git https://github.com/alya-lang/cli --branch main
alya install| Feature | Default | Description |
|---|---|---|
help |
β | Usage screen, version banner, and diagnostic report rendering (format_help, format_command_help, format_version, format_errors, print_help, print_command_help, print_version). |
strict |
β | Throwing and recovering parse entry points (parse_strict, try_parse) on top of the lenient engine. |
env |
β | Environment variable fallback for options registered with env_option. |
# Full build (default)
alya install
alya test
# Slim build (parsing only)
alya install --no-default-features
alya test --no-default-featuresimport "cli" as cli
function main()
# 1. Describe the application
let app = cli::new_app("forge", "Alya build and test tool", "1.0.0")
# 2. Register the global options
app.flag("-v, --verbose", "Enable verbose logs")
app.option("-o, --output", "dist", "Output directory")
# 3. Register a subcommand with its own options and arguments
let build = cli::new_command("build", "Compile the target package", ["b"])
build.flag("-r, --release", "Build optimized artifacts")
build.argument("entrypoint", "Source file to compile", false, "src/main.alya")
app.command(build)
# 4. Parse the arguments and read the resolved values
let ctx = app.parse(["build", "-v", "--release", "server.alya"])
if ctx.has_errors()
say cli::format_errors(ctx.get_errors())
exit(1)
end
if ctx.has_command("build")
say "Release: " + str(ctx.get_flag("release"))
say "Entry : " + ctx.get_arg_named("entrypoint", "src/main.alya")
end
end
main()
| Symbol | Visibility | Description |
|---|---|---|
new_app(name, description, version) |
pub function |
Creates an application specification. |
new_command(name, description, aliases) |
pub function |
Creates a command specification. |
set_author(app, author) |
pub function |
Sets the author string of an application. |
set_version(app, version) |
pub function |
Sets the version string of an application. |
| Symbol | Visibility | Description |
|---|---|---|
add_flag(app, spec, description) |
pub function |
Registers a boolean flag (-v, --verbose). |
add_option(app, spec, default, description) |
pub function |
Registers a single-value option (-o, --output). |
add_multi_option(app, spec, description) |
pub function |
Registers a repeatable option (-I, --include). |
add_choice_option(app, spec, default, choices, description) |
pub function |
Registers an option restricted to a choice list. |
add_required_option(app, spec, description) |
pub function |
Registers a mandatory option. |
add_env_option(app, spec, env_var, default, description) |
pub function |
Registers an option falling back to an environment variable. |
add_argument(app, name, description, required, default) |
pub function |
Registers a positional argument. |
add_variadic_argument(app, name, description) |
pub function |
Registers an argument capturing the remaining values. |
add_command(app, command) |
pub function |
Registers a top-level command. |
command_add_flag(cmd, spec, description) |
pub function |
Registers a command-scoped flag. |
command_add_option(cmd, spec, default, description) |
pub function |
Registers a command-scoped option. |
command_add_multi_option(cmd, spec, description) |
pub function |
Registers a command-scoped repeatable option. |
command_add_choice_option(cmd, spec, default, choices, description) |
pub function |
Registers a command-scoped choice option. |
command_add_required_option(cmd, spec, description) |
pub function |
Registers a command-scoped mandatory option. |
command_add_env_option(cmd, spec, env_var, default, description) |
pub function |
Registers a command-scoped environment option. |
command_add_argument(cmd, name, description, required, default) |
pub function |
Registers a command-scoped positional argument. |
command_add_variadic_argument(cmd, name, description) |
pub function |
Registers a command-scoped variadic argument. |
command_add_subcommand(parent, child) |
pub function |
Nests a command under another command. |
| Symbol | Visibility | Description |
|---|---|---|
parse(app, raw_args) |
pub function |
Parses an argument array into a CliContext. |
parse_args(app) |
pub function |
Parses the process arguments into a CliContext. |
parse_strict(app, raw_args) |
pub function (strict feature, default-on) |
Parses and throws a structured CliError on the first diagnostic. |
try_parse(app, raw_args) |
pub function (strict feature, default-on) |
Parses and converts a failure into a CliResult. |
| Symbol | Visibility | Description |
|---|---|---|
get_flag(ctx, name) |
pub function |
Returns the state of a parsed flag. |
get_option(ctx, name, default) |
pub function |
Returns the value of a parsed option. |
get_int(ctx, name, default) |
pub function |
Returns the value of a parsed option converted to an integer. |
get_bool(ctx, name, default) |
pub function |
Returns the boolean value of a parsed option. |
get_all(ctx, name) |
pub function |
Returns every value collected for a repeatable option. |
get_arg(ctx, index, default) |
pub function |
Returns a positional value by position. |
get_arg_named(ctx, name, default) |
pub function |
Returns a positional value by argument name. |
get_args(ctx) |
pub function |
Returns every positional value in order. |
get_command(ctx) |
pub function |
Returns the name of the matched command. |
get_command_path(ctx) |
pub function |
Returns the full path of the matched command. |
has_command(ctx, name) |
pub function |
Reports whether the given command was matched. |
has(ctx, name) |
pub function |
Reports whether the value appeared on the command line. |
has_errors(ctx) |
pub function |
Reports whether the parse collected diagnostics. |
get_errors(ctx) |
pub function |
Returns the collected CliError values. |
help_requested(ctx) |
pub function |
Reports whether -h/--help was supplied. |
version_requested(ctx) |
pub function |
Reports whether -V/--version was supplied. |
| Symbol | Visibility | Description |
|---|---|---|
format_help(app) |
pub function (help feature, default-on) |
Renders the application usage screen. |
format_command_help(app, cmd) |
pub function (help feature, default-on) |
Renders the usage screen of a command. |
format_version(app) |
pub function (help feature, default-on) |
Renders the version banner. |
format_errors(errors) |
pub function (help feature, default-on) |
Renders a diagnostic report. |
print_help(app) |
pub function (help feature, default-on) |
Prints the application usage screen. |
print_command_help(app, cmd) |
pub function (help feature, default-on) |
Prints the usage screen of a command. |
print_version(app) |
pub function (help feature, default-on) |
Prints the version banner. |
| Symbol | Visibility | Description |
|---|---|---|
make_error(code, subject, message) |
pub function |
Constructs a CliError. |
format_error(err) |
pub function |
Renders a CliError as a diagnostic string. |
format_caught_error(err) |
pub function |
Renders a value captured by catch err. |
CliError |
pub struct |
Structured diagnostic (code, subject, message). |
CliResult |
pub struct |
Outcome of a strict parse (ok, context, error). |
| Symbol | Visibility | Description |
|---|---|---|
CliApp |
pub struct |
Application specification (name, description, version, options, arguments, commands). |
CliCommand |
pub struct |
Command specification (name, aliases, options, arguments, subcommands). |
CliOption |
pub struct |
Option specification (names, kind, default, required, choices, env variable). |
CliArgument |
pub struct |
Positional argument specification (name, required, default, variadic). |
CliContext |
pub struct |
Parsed execution context (flags, options, positionals, diagnostics). |
OptionKind |
pub enum |
Option kinds (Flag, Value, Multi). |
CommandKind |
pub enum |
Command kinds (Root, Subcommand). |
TokenKind |
pub enum |
Token kinds recognised by the parser (Inverted, Long, Short, Positional). |
ErrorCode |
pub enum |
Diagnostic codes (UnknownOption, MissingValue, MissingRequired, InvalidChoice, MissingArgument, UnknownCommand, InvalidUsage). |
| Symbol | Visibility | Description |
|---|---|---|
CliApp.flag/option/multi_option/choice_option/required_option/env_option(...) |
pub method |
Registers options on an application. |
CliApp.argument/variadic_argument(...) |
pub method |
Registers positional arguments on an application. |
CliApp.command(cmd) |
pub method |
Registers a command. |
CliApp.set_author/set_version(...) |
pub method |
Updates the application metadata. |
CliApp.parse(args) / CliApp.run(args) |
pub method |
Parses the given or the process arguments. |
CliCommand.flag/option/multi_option/choice_option/required_option/env_option(...) |
pub method |
Registers options on a command. |
CliCommand.argument/variadic_argument(...) |
pub method |
Registers positional arguments on a command. |
CliCommand.subcommand(cmd) |
pub method |
Nests a command. |
CliContext.get_flag/flag/get_option/option/get_int/get_bool/get_all(...) |
pub method |
Reads the resolved values. |
CliContext.get_arg/arg/get_arg_named/get_args/args(...) |
pub method |
Reads the positional values. |
CliContext.get_command/command/get_command_path/has_command(...) |
pub method |
Reads the command routing state. |
CliContext.has/has_errors/get_errors/errors(...) |
pub method |
Reads the diagnostic state. |
CliContext.help_requested/version_requested(...) |
pub method |
Reads the automatic switch state. |
CliResult.is_ok/is_error/message(...) |
pub method |
Reads the outcome of a strict parse. |
Tip
Internal Helpers & Documentation: Public symbols are documented with ## Markdown docstrings, enabling automatic API documentation generation via alya doc. The helpers in src/core/internal.alya are not annotated with pub and stay encapsulated within the package.
- Unknown input is reported, not ignored. An option or a command that the specification does not declare produces a diagnostic (
UnknownOption/UnknownCommand) instead of being silently accepted. - The command scope wins. When a command re-declares a global option, the command definition is authoritative: the option is listed once in the usage screen, it is validated once (so no diagnostic is duplicated), and a value given before the command name is kept instead of being reset to the command default.
- The command line wins over the environment. An option registered with
env_optionreads its environment variable only when the option was not given on the command line. An environment provided value does not count as provided forhas(). - Diagnostics are values, not strings.
get_errors()returnsCliErrorvalues:err.codeclassifies the failure,err.subjectnames the offending option, argument, or command, anderr.messageexplains it. Rebuild a diagnostic withmake_error(...)before throwing it β a value read back from the untyped diagnostics array loses its struct identity when it crosses athrow.
Run the automated test suite using alya test:
alya testExercise feature selection (the help, strict, and env slices are default-on):
alya test --features help
alya test --features strict
alya test --no-default-featuresGenerate static API documentation:
alya doc . -o docs --markdownRun the benchmark suite:
alya run benches/bench_basic.alyaRun the example demo:
alya run examples/demo.alyaCheck code formatting:
alya fmt . --checkRun static code linter:
alya lint . --checkThis package comes preconfigured with recommended workspace settings and tasks for Visual Studio Code:
- LSP & Formatting: Auto-formatting on save and real-time Language Server diagnostics via
alya-lang.vscode-alya. - DAP Debugging: Launch configurations in
.vscode/launch.jsonready for interactive step-debugging viaF5. - Predefined Tasks: Press
Ctrl+Shift+Bor run tasks (Test,Lint,Format,Build Docs) directly from the Command Palette.
Contributions are welcome! Please follow these steps:
- Fork the repository and clone it locally
- Install dependencies:
alya install
- Create your feature branch (
git checkout -b feature/my-feature) - Verify tests and formatting before opening a PR:
alya test alya fmt . --check alya lint . --check
- Commit your changes (
git commit -m "feat: add feature") and open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.