Define your CLI once.
Usage is a toolkit for building command-line tools. Define your CLI's commands, flags, and args once in a KDL spec — get argument parsing, shell completions, --help, docs, and manpages from that one definition. Experimental reference frameworks for Rust and Go build your CLI from the spec, with Python and JavaScript planned.
Frameworks
Derive your CLI from Rust types — parsing, help, and completions generated from the usage spec. Experimental: APIs may change between releases.
Build Go CLIs on the usage spec, with parsing behavior verified against the same conformance corpus as the Rust implementation. Work in progress: APIs may change between releases.
Spec & tooling
Tab completions for bash, zsh, fish, PowerShell, and nushell, generated from the spec.
--help output, markdown docs, and manpages, all generated from the same spec.
Declare a script's interface in a USAGE comment and get arg parsing, validation, and completions.
Define your CLI once in KDL. Completions, docs, and parsers are all generated from it.
The standalone tool for generating completions, docs, and parsing from any spec file.
name "mycli"
flag "-v --verbose" help="Enable verbose output"
arg "<file>" help="File to process"
cmd "deploy" { flag "--env <env>" }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