Releases: jdx/usage
v3.5.0: Type-Safe SDKs and Zsh Colon Fixes
This release introduces usage generate sdk — type-safe subprocess-wrapper SDKs for TypeScript and Python derived from a usage spec — and fixes two zsh completion bugs around colons in subcommand and value names that were biting mise users.
Added
-
usage generate sdkfor TypeScript and Python (#623 by @gaojunran). A new generator emits a type-safe SDK client for any usage-described CLI. The SDK is a subprocess wrapper — not a native binding — so it works for any binary on$PATH, with typed args, flags, and choice-constrained values instead of stringly-typedsubprocess.run/spawncalls.usage generate sdk -l typescript -o ./sdk -f ./mycli.usage.kdl usage generate sdk -l python -o ./sdk -f ./mycli.usage.kdl
import { Mycli } from "./sdk"; const cli = new Mycli(); const result = await cli.build.exec( { target: "release", output: "./dist" }, { release: true }, );
Each generated SDK has three pieces: a types module (dataclasses / interfaces, with
Literal/ union types for choices and global flags propagated to every subcommand), a client module mirroring the subcommand tree withexec()methods that build the argv list, and a small runtime module wrappingsubprocess.run/child_process.spawn. See the SDK generation guide for the full walkthrough. Rust support is planned. -
usage sponsorscommand and docs sponsor block (#662, #608, #656). The new top-levelusage sponsorscommand and a sponsor strip on the docs site acknowledge 37signals and link to the canonical en.dev sponsor pages. A dedicated/sponsorsdocs page lists tiers fetched live fromen.dev/sponsors.json. -
More CLI-framework integration guides (#655, #667 by @gaojunran). The docs now cover using usage alongside JCommander, picocli, and Clikt (Java/Kotlin) and urfave/cli and Kong (Go), joining the earlier Commander.js / oclif / yargs / Typer / Click guides.
Fixed
-
zsh: all subcommands with a shared colon prefix now show up in completion (#666 by @zeitlinger).
_describegroups matches that share a\:-escaped prefix and surfaces only one entry per group, so a spec withrelease:create,release:docs-sync,release:pr, andrelease:updatewould only ever showrelease:createin the menu. The completion now builds its own display column and callscompadddirectly so every match is offered, with descriptions padded for column alignment. Regenerate zsh completions to pick up the fix. Surfaced via jdx/mise tasks. -
zsh: completion no longer truncates values at the first colon (#670 by @davidolrik). Completing a mise task named
chezmoi:brew:dumpwas insertingchezmoiand then destroying the user's typed:brew:on the next<Tab>, because_describeparsescandidate:descriptionin both the display and insert arrays it receives. The fix escapes colons in the insert column the same way the display column was already escaped. Regenerate zsh completions to pick up the fix. -
parse: orphan short/long aliases on a re-declared global flag are preserved (#659 by @JamBalaya56562). Follow-up to #649. When a subcommand re-declared an inherited global flag as non-global and added a new alias (e.g. mise's
run/tasks runre-declaring long-only--raw/--silentas-r --raw/-S --silent), the merge kept the global flag but discarded the re-declaration wholesale, throwing away the new short. The parser now unions the re-declaration's extrashort/longaliases (matched on a shared long name) onto the surviving global flag, so completions likemycli run -r sample:run -- <TAB>work andusage_raw=truereachesas_env()and mount scripts. This makes mise'spromote_orphan_shortsworkaround unnecessary.
Changed
- Docs examples use KDLv2 raw multiline strings throughout (#657 by @salim-b). The remaining KDLv1
r#"…"#raw strings in the documentation andexamples/mise.usage.kdlwere converted to KDLv2#"""…"""#, matching the rest of the docs that already use KDLv2 syntax (#true, etc.).
New Contributors
- @zeitlinger made their first contribution in #666
- @davidolrik made their first contribution in #670
- @salim-b made their first contribution in #657
Full Changelog: v3.4.0...v3.5.0
💚 Sponsor usage
usage is built by @jdx at en.dev — an independent developer-tooling studio behind mise, aube, hk, and more. Work on usage is funded by sponsorships.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider sponsoring at en.dev. Every sponsorship helps the project stay independent and moving.
v3.4.0: Readable KDL specs and tighter shell completions
A small but pleasant release: spec files round-trip with human-readable multiline strings instead of \n-laden one-liners, and three completion-side bugs are fixed across zsh, nushell, and the parser.
Added
-
Multiline KDL strings for descriptions (#639 by @gaojunran). When the spec serializer encounters a string value containing newlines — typically
long_help,help_md, multi-linecomplete runscripts, etc. — it now emits a KDL raw multiline string literal instead of a single-line string with embedded\nescapes:cmd bash help="Execute a shell script using bash" { long_help #""" Execute a shell script with the specified shell Typically, this will be called by a script's shebang. If using `var=#true` on args/flags, they will be joined with spaces using `shell_words::join()` to properly escape and quote values with spaces in them. """# ... }
The number of
#delimiters is computed automatically so values containing embedded"""sequences are always escaped safely.
Fixed
-
zsh: consistent single-quoting for choice values with spaces (#635).
usage complete-word --shell zshnow emits two tab-separated columns per match — a display string for_describe's menu rendering and a pre-shell-quoted insert string — and the generated completion script wires them through_describe ... -U -Q -S ''. Choice values likeAlice AliceorA B & Care inserted verbatim as'Alice Alice'instead of zsh's default mix of backslash and single-quote styles, and values starting with'correctly switch to menu-insert mode so the leading quote isn't truncated as a longest-common-prefix. If you have existing generated zsh completion scripts, regenerate them to pick up the fix. -
nushell: invoke commands as externals with
^(#638 by @silvanshade). Generated nushell completion scripts now prefix the user's CLI and theusagecallback with^, e.g.^mybin usage | collect | save $spec_fileand(^usage complete-word ...). Without the caret, nushell parsed baremybinas an internal function and errored withExtra positional argumentwhen loading the completion file. -
parser: keep inherited global flags when a subcommand re-declares them as non-global (#649 by @JamBalaya56562). A value-taking global flag (e.g.
-C/--cd) placed before a mounted subcommand whose definition re-declares the same flag withoutglobal=#truewas being dropped from the recognized-flag set on descent. The leftover token was then mis-parsed as a positional — producing errors likeInvalid choice for arg profile: -C, expected one of alpha, beta, gamma— and the flag's value was silently omitted fromas_env()and from the environment passed to mount scripts. This was the parser-side root cause referenced by jdx/mise#10069.
New Contributors
- @silvanshade made their first contribution in #638
- @JamBalaya56562 made their first contribution in #649
Full Changelog: v3.3.0...v3.4.0
💚 Sponsor usage
usage is built by @jdx at en.dev — an independent developer-tooling studio behind mise, aube, hk, and more. Work on usage is funded by sponsorships.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider sponsoring at en.dev. Every sponsorship helps the project stay independent and moving.
v3.3.0: Auto-completion for usage shebang scripts
The headline feature in this release is one-line auto-completion for usage-shebang scripts: source a single init script from your shell rc and every usage-powered script on your $PATH gets <Tab> completion automatically. This release also brings new third-party integration guides and a handful of docs site polish fixes.
Added
-
Auto-completion for usage shebang scripts (#620 by @jdx). A new
usage generate completion-init <shell>subcommand (aliasci) emits a one-time init script for bash, zsh, and fish. Source it once and any executable on$PATHwhose first line is ausageshebang gains tab-completion — no per-scriptusage g completion <shell> <bin> -f <script>generation step.# bash — add to ~/.bashrc (source after bash-completion) source <(usage g completion-init bash) # zsh — add to ~/.zshrc source <(usage g completion-init zsh) # fish — add to ~/.config/fish/conf.d/usage.fish usage g completion-init fish | source
Mechanism per shell:
- bash registers a
complete -Ddefault handler that peeks the candidate command's first line for ausageshebang and dispatches tousage complete-word. Non-usage commands chain to bash-completion's_completion_loader, so existing completions keep working. - zsh registers a
compdef -default-fallback with the same shebang detection, falling back to_filesfor non-usage commands. - fish has no default-completer fallback, so the init scans
$PATHonce at shell startup and registerscomplete -c <name>for each shebang script it finds.
Pass
--usage-bin(or setJDX_USAGE_BIN) if yourusagebinary is installed under a different name. - bash registers a
-
Third-party integration guides. Documentation now covers using usage alongside Commander.js, oclif, and yargs (#616 by @gaojunran) as well as Typer and Click (#619 by @gaojunran), based on the usage-integrations monorepo.
Fixed
- Docs site banner now stacks vertically on viewports
<=640pxwith the close button pinned to the top-right corner, fixing cramped two-line wrapping on mobile (#603 by @jdx).
Full Changelog: v3.2.1...v3.3.0
💚 Sponsor usage
usage is built by @jdx at en.dev — an independent developer-tooling studio behind mise, aube, hk, and more. Work on usage is funded by sponsorships.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider sponsoring at en.dev. Every sponsorship helps the project stay independent and moving.
v3.2.1: Zsh completion fix for values without descriptions
A small patch release that fixes a zsh completion regression introduced in v3.1.0. If you use zsh completions with values that contain colons (such as nested task names) and those values don't have descriptions, you should upgrade and regenerate your completions.
Fixed
-
Zsh completions now correctly escape colons in completion values even when no descriptions are present. In v3.1.0, the switch from
_argumentsto_describeintroduced a bug where the escape logic only ran when at least one completion had a description. Without escaping, a value liketest:gitwas misinterpreted by zsh -- it treatedgitas a description for the itemtestrather than as part of the value. If you have existing generated zsh completions, regenerate them to pick up this fix. (#597 by @david-hamilton-glean) -
Test suite now respects the
CARGO_BIN_EXE_usageenvironment variable when set, fixing test failures in environments with non-standard build directories such as Nixpkgs cross-compilation builds. (#568 by @kybe236)
New Contributors
- @david-hamilton-glean made their first contribution in #597
- @kybe236 made their first contribution in #568
Full Changelog: v3.2.0...v3.2.1
v3.2.0: Environment-backed choices and zsh escaping fix
This release adds the ability to source argument/flag choices from environment variables at runtime and fixes a zsh completion regression where parentheses and brackets in task descriptions caused shell errors.
Added
-
Arguments and flags can now pull their allowed values from an environment variable using
choices env=.... The env var value is split on commas and/or whitespace, deduplicated against any literal choices, and resolved at parse/completion time rather than baked into generated output. (#548 by @mustafa0x)arg "<env>" { choices env="DEPLOY_ENVS" }
With
DEPLOY_ENVS="foo,bar baz", valid values becomefoo,bar, andbaz. You can also combine literal choices with env-backed ones:flag "--env <env>" { choices "local" env="DEPLOY_ENVS" }
When the env var is unset or empty and no literal choices are provided, validation rejects all values with a clear error message. Help output and generated markdown surface the controlling env var name rather than snapshotting its current value.
This feature is currently behind the
unstable_choices_envfeature flag in the library crate.
Fixed
- Zsh completions now properly escape parentheses (
(,)) and brackets ([,]) in completion descriptions. Previously, these characters were passed through to zsh's_describefunction unescaped, causing it to interpret them as glob qualifiers or character classes -- resulting in cryptic errors likeunknown file attributeandunknown sort specifier. This was a regression from v2.x. If you have existing generated zsh completions, regenerate them to pick up this fix. (#559 by @jdx, fixes #558)
New Contributors
- @mustafa0x made their first contribution in #548
Full Changelog: v3.1.0...v3.2.0
v3.1.0: Richer help output, stdin support, and zsh completion fixes
This release improves the CLI's --help output to render all the documentation-related fields that were previously only used in manpage and markdown generation, adds stdin support for piping specs into usage commands, and fixes a long-standing zsh completion annoyance with trailing spaces.
Highlights
- The
--help/-houtput is now much richer, rendering examples, before/after help text, version headers, author/license info, and deprecation markers -- fields that were previously only surfaced in generated manpages and markdown. - You can now pipe a usage spec from another tool directly into usage via
--file -, enabling workflows likejbang usage | usage generate markdown --file -. - Zsh tab completions no longer insert unwanted trailing spaces after partial completions, fixing a bug reported nearly a year ago.
Added
-
The built-in
--help/-hrendering now includesbefore_help,after_help(and their_longvariants),examples, aname+versionheader,author/licensein the long help footer, and[deprecated: reason]markers on subcommands. Short help (-h) uses the base variants; long help (--help) prefers the_longvariants with a fallback to the base ones. (#554 by @jdx, closes #549)For example, a spec like:
before_help "Welcome to my CLI" after_help "See the project website for docs" example "mycli --verbose" header="Run with verbose output"
will now render those sections in
mycli --helpoutput, not just in generated docs. -
All
--fileflags acrossgenerate,lint, andcomplete-wordsubcommands now accept-to read the usage spec from stdin. This enables piping specs from other tools without writing a temporary file. (#555 by @jdx, closes #546)jbang usage | usage generate markdown --file - cat myspec.kdl | usage lint --file -
Not supported for
exec/shellsubcommands, which pass stdin through to the child process.
Fixed
- Zsh completions no longer append an unwanted trailing space after partial completions. Previously, completing
node@would producenode@(with a space), and path completions like/opt/homebrewwould not include a trailing slash. The generated zsh completion scripts now use_describewith-S ''instead of_argumentswith command substitution, and directory completions include a trailing/. If you have existing generated zsh completions, regenerate them to pick up this fix. (#556 by @jdx, closes #67)
Full Changelog: v3.0.0...v3.1.0
v3.0.0: Spec metadata expansion and Cobra escaping fix
This release adds several new metadata fields to the usage spec and includes a breaking API change to the Spec struct. The spec parser now supports license, before_help, after_help, before_long_help, and after_long_help -- fields that were documented in the spec reference but silently ignored until now. The Spec struct has been marked #[non_exhaustive] to allow future field additions without further breaking changes.
Breaking Changes
The Spec struct now has the #[non_exhaustive] attribute. If you construct Spec values using struct literal syntax, your code will need to be updated:
// Before (no longer compiles)
let spec = Spec { name: "my-cli".into(), bin: "mycli".into(), /* ... */ };
// After (option 1)
let mut spec = Spec::default();
spec.name = "my-cli".into();
spec.bin = "mycli".into();
// After (option 2)
let spec = Spec { name: "my-cli".into(), bin: "mycli".into(), ..Default::default() };This change was made so that new fields can be added to Spec in future minor releases without breaking downstream code. (#542 by @jdx, fixes #537)
Added
-
Support for
license,before_help,after_help,before_long_help, andafter_long_helptop-level metadata fields in the usage spec. These fields are now parsed, serialized, and included in spec merges and generated docs. For example:license "MIT" before_help "Welcome to my CLI" after_help "See the docs for more info" before_long_help "Detailed welcome text" after_long_help "Detailed footer text"
-
New community integration: Ruby's
OptionParserviaoption_parser_usage. (#533 by @packrat386)
Fixed
- The Cobra integration now correctly escapes newlines (
\n), tabs (\t), and carriage returns (\r) in KDL output. Previously, Cobra commands with multi-line help text would produce invalid KDL specs that failed to parse. (#539 by @thecodesmith)
Changed
- Updated the
roffdependency from 0.2 to 1.0 for man page generation. (#529)
New Contributors
- @thecodesmith made their first contribution in #539
- @packrat386 made their first contribution in #533
Full Changelog: v2.18.2...v3.0.0
v2.18.2: Fix noclobber compatibility in shell completions
A small patch release that fixes a compatibility issue with the noclobber shell option. If you had set -o noclobber (or set -C) enabled in bash or zsh, tab completions generated by usage would fail with a "cannot overwrite existing file" error every time the spec cache file already existed. This is now fixed.
Fixed
- Generated bash and zsh completion scripts now use
>|(force-overwrite redirection) instead of>when writing the spec cache file, preventing failures when the shell'snoclobberoption is enabled. Previously, users withnoclobberset would see errors likebash: /tmp/usage__usage_spec_mycli.spec: cannot overwrite existing fileon every tab completion. (#524 by @nkakouros)
New Contributors
- @nkakouros made their first contribution in #524
Full Changelog: v2.18.1...v2.18.2
v2.18.1: Fix choice validation for variadic args and flags
A small patch release that fixes a parsing bug where variadic arguments and flags with declared choices were not being validated. Previously, any value was silently accepted for variadic (var=#true) args and flags, even when a choices constraint was specified. Non-variadic args and flags were unaffected and already validated correctly.
Fixed
- Variadic args and flags with
choicesnow correctly reject invalid values at parse time, matching the existing behavior for non-variadic args and flags. For example, given a spec likearg "<level>" var=#true { choices "debug" "info" "warn" "error" }, passing an invalid value such as"invalid"now produces a clear error message instead of being silently accepted. (#520 by @jdx, fixes jdx/mise#8334)
Full Changelog: v2.18.0...v2.18.1
v2.18.0 (Internal CI improvements)
This is a maintenance release with no user-facing changes. The only modification is an internal CI/CD improvement that extracts the AI-powered release-note enhancement step into a separate GitHub Actions job, making it independently re-runnable if it fails due to transient errors.
There are no changes to the library, CLI, shell completions, spec format, or documentation.
Full Changelog: v2.17.4...v2.18.0