Repository navigation
Releases
v7.3.0: Shared `completion` subcommand for usage-rs CLIs
usage-rs now ships a ready-made completion subcommand. Every CLI built on it can mount the command, so they all print and install shell completions the same way.
Added
-
(lib) New
usage_rs::completion::Completionsubcommand, available with thecompletionsfeature (#1624, @jdx). Until now each tool wrote its owncompletioncommand: some had--install, while others printed a script for the user to redirect by hand. You can now mount the shared command instead:use usage_rs::completion::Completion; #[derive(Subcommands)] enum Commands { /// Generate shell completions Completion(Completion), } // in the run function: completion.run("ex", &mut std::io::stdout())?;
Any tool that mounts it gets these commands:
ex completion fish # print the script, e.g. `ex completion fish | source` ex completion fish --install # write it where fish looks for it ex completion fish --install --force # also replace a file usage did not generate- Shells:
bash,elvish,fish,nu/nushell,powershell/pwshandzshare accepted. Any other shell name fails to parse. - Flags:
--forcerequires--install. - Scripts: The scripts are the short callback scripts. They contain no spec and don't need the
usagebinary. On every Tab they ask the installed tool for candidates, so they stay correct when the tool is upgraded. - Installing:
--installnever edits a startup file. If a shell needs a line added once, the command prints that line after the install path. - Errors:
Completion::runreturnsError::InstallorError::Output. A closed pipe (for exampleex completion zsh | head) is not treated as an error. - Testing:
Completion::run_intakes an explicitEnv, so tests can install without touching the real home directory.
The Rust completions guide (
docs/rust/completions.md) now usesCompletionin place of the hand-written example. - Shells:
Full Changelog: v7.2.0...v7.3.0
💚 Sponsor usage
usage is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep usage fast, free, and independent.
v7.2.0: Refuse unknown environment variables under an application prefix
A CLI can now claim an environment variable prefix. Any variable under that prefix that the spec doesn't declare then makes the parse fail, which catches typos and stale names in deployment config. This release also lets derive flags declare their own settings, makes flags inside clauses set their settings, and adds --help/--version to tab completion.
Added
-
(spec) New opt-in
unknown_envnode (#1609, @jdx). When it's set, a parse fails if the environment holds any variable under the prefix that nothing in the spec reads, and the error lists every such variable at once. Without the node, nothing changes.unknown_env prefix="MOQ_" { allow "MOQ_VAAPI_DEVICE" "MOQ_V4L2_ENCODER" } flag "--port <port>" env="MOQ_PORT" cmd "serve" { flag "--tls-cert <path>" env="MOQ_TLS_CERT" deprecated_env="MOQ_CERT" }
Rust derive CLIs declare it on the root struct:
#[usage(bin = "moq", unknown_env(prefix = "MOQ_", allow("MOQ_VAAPI_DEVICE")))]. WithMOQ_PROTandMOQ_TSLset, a derived CLI exits 2:error: unknown environment variables under 'MOQ_': MOQ_PROT MOQ_TSLA variable counts as known if anything in the command tree declares it, not only the command that ran. That covers
env,env_fallbackanddeprecated_envon any flag or argument, including those in subcommands, flattened groups and clauses. It also covers variables read byconfigproperties and names inallow. Other details:- The prefix only limits which variables are checked. It is never used to build a variable name, so this is not clap's
env_prefix. - Deprecated names still produce a deprecation warning rather than an error.
--helpand--versionare answered before the check runs.- On Windows, names are compared case-insensitively.
In usage-lib,
Parserchecks the map passed toParser::with_envif there is one, and the process environment otherwise. It reports aUsageErr::UnknownEnv. - The prefix only limits which variables are checked. It is never used to build a variable name, so this is not clap's
-
(go) Parsers from
usage generate gonow enforceunknown_envtoo (#1612, @jdx). Before this, generated Go ignored the node. The check runs before required flags and arguments are checked, and it reports offenders in sorted order:error: unknown environment variables under `MOQ_`: `MOQ_PROT`, `MOQ_TSL`The Go generator can't see what variables a mounted command declares, so
usage generate gofails on a spec that has bothunknown_envand amount.go/argvaddsUnknownEnv,RefuseUnknownEnv,EnvNamesand the error codeCodeUnknownEnv. -
(derive) A field with
setting = "key"now declares that setting on its own, so you no longer need a matching#[derive(Config)]field (#1610, @jdx). The registry entry takes its type from the field's type and its environment names fromenv/env_fallback/deprecated_env. List splitting comes fromdelimiter(,or:), help comes from the doc comment, and the flags are every flag bound to the key. The rootClinow exposesSETTINGS_PROPSandSETTINGS_REGISTRY, and its spec gets aconfigblock even when there is noconfig = Type.#[derive(usage_rs::Args)] struct ListenTls { /// Load the given certificate from disk. #[usage(long = "listen-tls-cert", env = "EX_LISTEN_TLS_CERT", delimiter = ',', setting = "listen.tls.cert")] cert: Vec<std::path::PathBuf>, }
If several subcommands bind the same key, they must agree on field type, environment names and delimiter. Otherwise it's a compile error. Existing CLIs don't need to change, because a key declared on the
configstruct takes precedence over the flag. Settings that need a default,merge,scopeorchoicesstill have to beConfigfields. Settings declared only by a flag are always optional and have no default.
Fixed
- (complete) Tab completion now offers
-h/--help, and-V/--versionwhere the parser accepts them (#1605, @jdx). Before this fix, CLIs compiled from the derive andusageitself never offered these flags. This applies to both the derive andusage complete-word. A spelling that a declared flag already uses (hidden flags included) is not offered twice, anddisable_help_flag/disable_version_flagare respected. Also, merging a mounted spec no longer clearsdisable_help_flag,disable_help_subcommandordisable_version_flagon the host command. For example,mise runno longer gets--helpback from the mounted root. - (derive) A flag with
setting = "key"inside a#[usage(clause)]element now sets its setting, the same way a flag in a flattened group does (#1611, @jdx). When several clause items give the same setting, list settings collect every entry in order and other settings keep the last value. A root whose only bound flags are in clauses can now use#[usage(settings)]. Before this fix, that failed to compile.
Full Changelog: v7.1.5...v7.2.0
💚 Sponsor usage
usage is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep usage fast, free, and independent.
v7.1.5: Reject relationship properties that name several targets
Specs that list several flags in one relationship property, or repeat the property, now fail to load with an error. Before this release, usage accepted them but enforced nothing.
Fixed
-
(spec) A relationship property such as
conflicts="--b --c"is now rejected when the spec loads (#1599, @jdx). Previously,--b --cwas read as one selector that matched no flag, so--a --band--a --cboth parsed without error. Repeating the property (conflicts="--b" conflicts="--c") also loaded, but only the last value was kept. Both forms now produce an error:conflicts takes one flag or argument per property, but "--b --c" has several; write them as a child node instead: conflicts "--a" "--b" conflicts is given more than once; the second would replace the first, so write several as a child node instead: conflicts "--a" "--b"This check applies to
conflicts,overrides,requires,required_if,required_unlessandrequired_unless_allon flags, and to the same properties exceptoverrideson positional args. A value is refused only if it has more than one word and at least one starts with a dash. Values likeconflicts="input file"still refer to a positional named[input file].Upgrading: These specs were never enforcing their relationships, so they now need to be rewritten in the child-node form. That form already worked and has not changed:
flag "--a" { conflicts "--b" "--c" }
The flag reference (
docs/spec/reference/flag.md) now covers the child-node form and these load-time errors.
Full Changelog: v7.1.4...v7.1.5
💚 Sponsor usage
usage is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep usage fast, free, and independent.
v7.1.4: Mounted scripts tolerate rejected env values; Cobra exporter quotes boolean and numeric defaults
A mount run= that reads another usage script's spec no longer fails when an environment variable holds a value that script rejects, and the Cobra exporter now writes specs that usage lint accepts.
Fixed
-
(lib) A
mount run=that gets its spec from another usage-shebang script now works even when the environment holds a value that one of that script's env-bound flags rejects (#1591, @jdx). For example, if the child declaresflag "--mode <m>" env="X_MODE" { choices "a" "b" }, thenX_MODE=zzz ./parent subused to fail withInvalid choice for option mode: zzz(exit 181), and completion failed the same way. When usage runs the script itself and the parse fails, it now drops the env values the spec rejects and keeps the valid ones. If the parse still fails, the original error is reported. Running the script directly still reports invalid env values, and a bad value typed on the command line is still an error.When a mount does fail, the error now names the command being resolved:
could not read the commands mounted under `sub`: exited with code 1 sh -c ./child2This message comes from
usage-lib, so CLIs built on it show the same wording. Script exit codes have not changed, and they are now documented indocs/cli/scripts.md: the script's own code when it runs,1when the command line doesn't satisfy the spec, and181when usage can't read or run something the spec depends on, such as an invalid spec or a failing mount. -
(cli) The Cobra integration now quotes flag defaults that KDL would otherwise read as non-strings (#1595, @jdx). Before this fix, a
Boolflag defaulting totrueor anIntflag defaulting to-1produceddefault=trueordefault=-1, andusage lintrejected the spec. Defaults such astrue,false,null,inf,-inf,nan, signed numbers (-1,+1,-0.5) and values containing[or]are now written as quoted strings. Plain values such asjsonare still written without quotes.
Full Changelog: v7.1.3...v7.1.4
💚 Sponsor usage
usage is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep usage fast, free, and independent.
v7.1.3: Order subcommand help sections by display_order
display_order on a subcommand now sets where its whole help section appears, not just its position within the section. Before this, the ungrouped Commands: section, which holds the built-in help, always came first.
Fixed
-
(cli) CLIs that group subcommands with
help_headingcan now put the defaultCommands:section after their named sections (#1583, @jdx). Each section is placed according to the lowestdisplay_orderamong its commands. A command without adisplay_ordercounts as999, so the built-inhelpnever moves its section forward. Sections with the same value keep their current order.usage-argvruntime help and theusage-libreference renderer both follow this rule.enum Cmds { #[usage(display_order = 99)] Completion(NoArgs), #[usage(help_heading = "Source", display_order = 1)] Fmt(NoArgs), #[usage(help_heading = "Dependencies", display_order = 2)] Fetch(NoArgs), }
Help used to list
Commands:(completion, help), thenSource:, thenDependencies:. It now listsSource:,Dependencies:, thenCommands:.If none of your commands set
display_order, help looks the same as before. If you setdisplay_orderon grouped commands but not on ungrouped ones, the ungrouped section now appears last.
Full Changelog: v7.1.2...v7.1.3
💚 Sponsor usage
usage is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep usage fast, free, and independent.
v7.1.2: Completion scripts treat candidates as plain text; mounted commands stop inheriting top-level completers
Generated bash, zsh and fish completion scripts no longer expand completion candidates or file extensions as shell code. Mounted commands also stop picking up the mounting spec's top-level completers. The first fix matters for tools like mise, where candidates can come from project files. Regenerate any completion files you've installed to get the script fixes.
Fixed
- (complete) Completion candidates and extension filters are now handled as plain text in every shell (#1581, @jdx):
- bash:
usage generate completion bashused to pass candidates throughcompgen -W, which expands them as shell input. Arun=completer that printed$(touch pwned)ran that command when you pressed Tab, and a*candidate turned into the files in the current directory. Thecompletion-inithandler had the same globbing problem. Both now read one candidate per line without expanding it, so a candidate that contains a space stays one entry instead of splitting in two. - zsh: extension names were inserted directly into the
_files -gpattern, and zsh runs glob qualifiers as code. The pattern is now built only from plain extension names (letters, digits,_ . + -). If no usable names are left, completion falls back to unfiltered_files. This also fixes a single extension, which produced*.(toml)and matched nothing. It now produces*.toml. - fish: extensions are matched as a literal suffix, so a
*or?in an extension no longer matches extra files.
- bash:
Changed
- (complete) A command brought in by
mountnow uses only the completers it declares itself, in its owncompletenodes or inside itsargnodes (#1581, @jdx). Before, a mounted argument that shared a name with a top-level completer (for example<tool>) used the mounting spec's completer, even when the mounted command declared its own. If you type one of the mounting CLI's flags inside a mounted command, its value still completes the same way it does outside the mount. Specs without mounts are unaffected. If a mounted command relied on a top-level completer, declare it in the spec that the mount emits instead, for example withcomplete "tool" run="..."inside thecmd.
Full Changelog: v7.1.1...v7.1.2
💚 Sponsor usage
usage is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep usage fast, free, and independent.
v7.1.1: Fix nushell 0.116 deprecation warning in generated completions
Nushell completion scripts generated by usage-argv and the Go runtime no longer print a deprecation warning on nushell 0.116.
Fixed
- (complete) Nushell 0.116 deprecated external completers that take a positional
spansargument, so the generated script printed anu::shell::deprecatedwarning every time it completed (#1570, @jdx). The completer now uses the{|place|}signature. On 0.116 it reads the tokens from$place.command, and on older nushell releases, which pass the token list directly, it uses that list. If you already had an external completer installed, it still gets called, and it receives the input in the format it expects: the token list for completers written withspans, the original input for everything else. If you saved the script to a file, regenerate it to pick up the fix.
Full Changelog: v7.1.0...v7.1.1
💚 Sponsor usage
usage is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep usage fast, free, and independent.
v7.1.0: Effect v4 CLI exporter and bash completion escaping
This release adds usage-spec-effect, a new npm package that turns Effect v4 effect/cli command trees into usage specs. It also fixes bash completions from usage-argv, which could pass the command a different value than the one offered when a candidate contained quotes, spaces or other shell syntax.
Added
-
(spec)
usage-spec-effectexports a usage spec from an Effect v4effect/clicommand tree (#1564, @jdx). Effect still parses and runs the CLI, and the spec drives shell completions, markdown docs and man pages. It requireseffect4.x.npm install usage-spec-effect
import { generate } from "usage-spec-effect"; if (process.argv.includes("--usage-spec")) { process.stdout.write(generate(app, { version: "1.0.0" })); process.exit(0); }
mycli --usage-spec | usage generate completion bash mycli --file -The exporter handles nested subcommands, aliases, unlisted commands, examples, shared flags (exported as
global), flag aliases, boolean and valued flags, choices, defaults, and optional and variadic args. Some constructs can only be exported approximately:orElsealternatives, file, redacted and key-value types (exported as strings), non-primitive defaults, and flags with an optional value.generateWithReportreturns these in anunsupportedlist so they aren't dropped without notice. Effect's publicCommandtype doesn't expose flags or arguments, so the exporter reads the command's runtime config. If a future Effect release changes that shape,generatethrows an error that names the command. See the Effect integration guide.
Fixed
- (bash)
usage-argvandusage-dynamicnow escape bash completion candidates so the command gets the value that was offered (#1566, @jdx). Before this fix, Readline put candidates on the line unescaped, so a candidate liketasks."hello.sh"reached the command astasks.hello.sh, andit's hereleft an unclosed quote on the line. Candidates are now backslash-escaped. When a quote is already open in the word (',"or$'), each candidate is escaped for that quote style instead. Path completions still use Readline's own filename quoting, and zsh and fish were already correct. Hosts handling__complete_word__should callrender_request(&answer, &request)instead ofrender(&answer, shell)so the open-quote state is used.App::completion_requestinusage-dynamicalready does this. The script-based bash completions behindusage complete-wordare unchanged, sincecompgen -Wcan't carry spaces or quotes in candidates.
Full Changelog: v7.0.2...v7.1.0
💚 Sponsor usage
usage is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep usage fast, free, and independent.
v7.0.2: Completion honors .usage.kdl sidecars and shows help for short flags
Two completion fixes: completing a script now uses the same .<script>.usage.kdl sidecar spec that usage exec runs with, and short flags show their help text in completion menus.
Fixed
- (complete)
usage complete-word -f <script>now reads a.<script>.usage.kdlfile next to the script before falling back to itsUSAGEcomments, the same lookupusage execalready did (#1558, @jdx). Before this, a script with a sidecar completed against the comment spec, or against nothing at all. The bash, zsh and fish scripts fromusage generate completion-initcallcomplete-word, so they get the fix without being regenerated. Otherusage generatesubcommands that take-f <script>, such ascompletionandmarkdown, now prefer the sidecar too. When the sidecar doesn't setnameorbin, the script's basename is used. Inline specs and stdin input work as before. - (complete) Short flags now show their help text in completion menus, as long flags already did (#1561, @Ehsan-hash). For
flag "-v --verbose" help="Verbose output", completing-vused to show no description and now showsVerbose output. If a root command and a subcommand both define the same short flag, the menu shows the help for the flag that actually applies. Bash completions still list flag names only, with no descriptions.
New Contributors
- @Ehsan-hash made their first contribution in #1561
Full Changelog: v7.0.1...v7.0.2
💚 Sponsor usage
usage is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep usage fast, free, and independent.
v7.0.1: Smaller usage-argv help, completion and error rendering code
This release reduces how much code usage-argv adds to CLIs that embed it. Help, -h, version, completion and parse-error output stay byte-for-byte the same, and startup and parse speed are unchanged.
Changed
- (lib) Help, completion and error rendering in
usage-argvproduce less machine code (#1551, #1553, #1554, @jdx). String appends, text wrapping, slicing, searching, completion splitting and error suggestions now go through shared helpers instead of being expanded at every call site. In stripped release builds of oxlint and oxfmt, the three PRs together removed about 85 KB from each binary. That covers most of the size those CLIs gained when they moved from bpaf tousage-rs.
Fixed
- (complete)
Split::walkedandSplit::argvno longer panic whencwordis out of range. They now return the whole word list and an empty slice (#1554, @jdx).
Full Changelog: v7.0.0...v7.0.1
💚 Sponsor usage
usage is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep usage fast, free, and independent.