The toolkit for command-line interfaces
Define your CLI once.
Commands, flags, arguments, and settings in one definition. Build a typed Rust CLI, or bring a KDL spec to generate completions, help, documentation, and client libraries.
Already have a CLI? Connect your framework →
Bash · Zsh · Fish · PowerShell · Nushell
bin "mycli"
cmd "deploy" help="Deploy a service" {
arg "<service>"
flag "--env <env>" {
choices "staging" "production"
}
}Choose your starting point
Declare commands with structs and enums. Get typed parsing, help, completion, validation, and configuration support, plus a portable spec you can export.
Export a spec from clap, Cobra, or another framework, or write KDL directly. Generate reference docs and completions while keeping your application code.
Declare arguments and flags in comments. Usage validates the input, handles --help, and passes the parsed values to your script as environment variables.
One definition, every artifact
Use the same commands, descriptions, choices, and defaults across the tools your users see. Regenerate the artifacts as your interface evolves.
Benchmarks
What parsing mise use -g node@20 costs each framework, against a shadow of mise's CLI: 211 commands, 711 flags. Every program on both cards is generated from that one checked-in spec.
wall time, in-process parse throughput How this is measured Each parser runs repeatedly inside one process; process startup is excluded, so these bars are not full CLI invocation times. The chart reports the fastest per-parse time from many short rounds. Minima and their ratios drift a few percent between runs and machines, hence the ~. Instructions for one cold parse, which do not drift: 4,155 · 5.89M · 21.9M, agreeing across two machines to 0.15%.
usage-rs starts from compiler-emitted static tables and scans only the current command's flags plus inherited globals. clap and bpaf build a parser before they can use one Where their time goes Most of clap's is constructing and validating its command tree. bpaf's is larger because it assembles a combinator tree per run as well, which reusing the parser across parses only halves. . Heap allocations for a bare parse: zero, against clap's 6,560.
wall time, in-process parse throughput How this is measured The same way as the Rust card, by a harness written to match it: each parser runs repeatedly inside one process and the fastest per-parse time from many short rounds is reported, with the collector run between rounds rather than inside them. Process startup is excluded — a Go process is about a millisecond old before main, which no parser can touch. Whole-process cost, and instructions for one parse: go/README.md.
usage-go binds against package-level tables the linker laid out before main. cobra and urfave build a command tree per process and kong reflects over a struct, none of which a spec-driven parser has to do. Instructions for the same parse: 123k vs cobra's 2.8M, urfave/cli's 5.8M, kong's 66.7M — and 1,955 for the binder under usage-go's typed front door.
Methodology and raw numbers: go/README.md · tasks/perf-shadow.sh · time-sweep.rs