Skip to content

feat(derive): compile subcommands from an enum - #816

Merged
jdx merged 2 commits into
mainfrom
agent/derive-subcommands
Aug 11, 2026
Merged

feat(derive): compile subcommands from an enum#816
jdx merged 2 commits into
mainfrom
agent/derive-subcommands

Conversation

@jdx

@jdx jdx commented Aug 11, 2026

Copy link
Copy Markdown
Owner

First of the next stack, and the largest gap in the derive: it could describe one command, so nothing with subcommands could use it — which is every CLI it is meant for.

Shape

#[derive(Cli)]
#[usage(bin = "ex")]
struct Ex {
    #[usage(short = 'v', long, global)]
    verbose: bool,
    #[usage(subcommand)]
    command: Option<Commands>,
}

#[derive(Subcommands)]
enum Commands {
    /// Install a tool
    Install(Install),
    /// Run a task
    #[usage(name = "run")]
    RunTask(Run),
}

/// Install a tool
#[derive(Args)]
struct Install {
    #[usage(short = 'f', long)]
    force: bool,
    tools: Vec<String>,
}

Two problems worth explaining, because they shaped the design

A macro sees one item. The three derives cannot share a counter or read each other's fields, so keys carry a hash of the type they came from in the high half, the index in the low half, and a tag for command/flag/argument. Independently expanded macros therefore cannot hand two fields the same key — which matters because a parse dispatches on it. Two type names would have to collide in 32 bits, and Spec::to_kdl now asserts the tree holds no duplicates, so a collision fails a test instead of binding the wrong field. A test checks that invariant directly.

A static cannot call a method. The parent splices its children's tables into its own static, so the traits expose them as associated consts, not functions. That keeps the tables static all the way down — nothing is assembled at run time, which was the whole point.

Restructuring that fell out

Every derived struct now collects into a generated Partial rather than into locals, since a subcommand's values cannot live in the root's parse function. That replaced the prefixed-locals machinery from #803 entirely, and made apply shared between a root and a subcommand.

A doc comment on a variant overrides the struct's, because that is where a reader of the enum expects to describe the command — ignoring it would lose the description silently. Overriding one field of the struct's metadata is possible in a const, so this costs nothing at run time.

Verified

Eight tests: routing by name and by a renamed variant, per-command fields with defaults, a global flag on either side of the command word, one command not answering to another's flag, an absent subcommand, key uniqueness across separately expanded types, and a snapshot of the emitted spec:

name "ex"
bin "ex"
flag "-v --verbose" help="Say more" global=#true
cmd "install" help="Install a tool" {
    flag "-f --force" help="Overwrite an existing install"
    flag "-j --jobs" help="How many at once" default="4" {
        arg "<jobs>"
    }
    arg "[tools]..." help="What to install"
}
cmd "run" help="Run a task" {
    arg "<task>" help="The task to run"
}

Nested cmd blocks that read like something a person would have written, which is what usage g markdown|manpage needs.

Stated limits

  • A subcommand field must be Option<T>: reporting that one was required is a required-ness question, and that layer lands in the next PR of this stack.
  • Nesting stops at one level — a subcommand's own subcommands need Args to carry a subcommand field too.

AI-assisted — Tool: Claude Code; model: anthropic/claude-fable-5; version: unavailable.


Note

Medium Risk
Large derive/codegen refactor plus a public key-type change (u32u64) that affects parse dispatch. Mitigated by conformance tests and debug duplicate-key checks, but wrong keys would bind the wrong fields.

Overview
Enables declaring subcommands with the derive: a #[usage(subcommand)] field holds an enum (Subcommands), and each variant wraps an Args struct for that command's flags and arguments.

Independently expanded macros cannot share a counter, so keys widen from u32 to u64 and carry a type-name hash. New CommandArgs / Subcommands traits expose associated consts so parents splice children's tables into static parse tables with no runtime assembly. Spec::to_kdl debug-asserts the tree has no duplicate keys.

Codegen now accumulates into a generated Partial (shared apply) instead of parse-local variables, which is what lets a root route events into subcommands it cannot see. Variant doc comments override the wrapped struct's help; nesting and required subcommands remain out of scope for now.

Reviewed by Cursor Bugbot for commit 5373abb. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Added support for defining and parsing CLI subcommands, including nested structures, command-specific options, global flags, defaults, and optional subcommands.
    • Added Args and Subcommands derive macros for generating command-line parsers and metadata.
    • Expanded command keys to support larger values and improved uniqueness checks.
  • Bug Fixes

    • Added validation and diagnostics for duplicate command keys and subcommand names.
    • Improved error reporting for invalid subcommand definitions.

The largest gap in the derive: it described one command, so nothing with
subcommands could use it — which is every CLI it is meant for.

Three derives now cooperate. `Cli` on the root, `Subcommands` on an enum whose
variants each wrap a struct, and `Args` on those structs. A macro sees one
item, so they cannot share a counter or read each other's fields, and two
things follow from that.

Keys carry a hash of the type they came from, in the high half, with the index
in the low half and a tag for whether it is a command, a flag, or an argument.
Independently expanded macros therefore cannot hand two fields the same key,
which matters because a parse dispatches on it. Two type names would have to
collide in 32 bits, and `Spec::to_kdl` now asserts the tree holds no
duplicates, so that fails a test rather than binding the wrong field.

The tables are joined through two traits whose associated consts a parent
splices into its own `static` tables — consts rather than methods precisely
because a method call is not allowed in a `static`. Nothing is assembled at run
time, which was the point of the exercise.

Along the way, every derived struct now collects into a generated `Partial`
rather than into locals, since a subcommand's values cannot live in the root's
parse function. That replaced the prefixed-locals machinery entirely.

A doc comment on a variant overrides the struct's, because that is where a
reader of the enum expects to describe the command; ignoring it would lose the
description without saying so. Overriding one field of a const's metadata keeps
the tables static.

Eight tests, including one asserting keys are unique across separately expanded
types, and a snapshot of the emitted spec — nested `cmd` blocks that read like
something a person would have written.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds derive-generated CLI subcommands with typed routing, partial-state parsing, metadata generation, key collision detection, and conformance tests. Public parser keys now use u64.

Changes

Subcommand parsing

Layer / File(s) Summary
Public contracts and key validation
argv/src/lib.rs, argv/src/spec.rs
Public command, flag, and argument keys now use u64. CommandArgs and Subcommands expose generated parsing APIs. Specification generation checks recursive key uniqueness.
Subcommand model and derive entry points
derive/src/model.rs, derive/src/lib.rs
The derive model validates optional subcommand fields and enum variants. Args and Subcommands derive macros generate the related implementations.
Keyed parser generation
derive/src/codegen.rs
Generated parsers use type-derived keys, partial accumulators, defaults, qualified type paths, and event application.
Subcommand routing and value construction
derive/src/codegen.rs
Generated code routes events to subcommands, aggregates metadata, selects variants, and builds final values.
End-to-end conformance coverage
conformance/tests/subcommands.rs
Tests cover routing, defaults, global flags, metadata, key uniqueness, and KDL snapshots.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant GeneratedArgs
  participant GeneratedSubcommands
  participant CommandArgs
  CLI->>GeneratedArgs: Provide parse events
  GeneratedArgs->>CommandArgs: Apply root events
  GeneratedArgs->>GeneratedSubcommands: Route subcommand events
  GeneratedSubcommands->>GeneratedSubcommands: Select variant by key
  GeneratedArgs->>CommandArgs: Build final CLI value
Loading

Possibly related PRs

  • jdx/usage#797: Adds related subcommand parsing and conformance tests.
  • jdx/usage#798: Adds related argv parser types and event routing.
  • jdx/usage#803: Introduces the derive-based parsing infrastructure extended by this PR.

Poem

A rabbit hops through commands bright,
Keys grow wide in moonlit light.
Flags and args join the queue,
Subcommands know just what to do.
KDL records the trail anew.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding derive support for compiling subcommands from an enum.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Instruction counts

benchmark trend instructions Δ wall (min) Δ
markdown ▁▁▁▁▁▁▁▁▁███████ 148,166,271 → 148,227,681 +0.04% 14.44 → 13.82ms -4.32%
startup ▄▄▄▄▄▄▄▄▄█████▁▁ 1,199,527 → 1,199,593 +0.01% 0.99 → 0.97ms -1.97%

No instruction-count regression above 1%.

Only instruction counts gate. Wall clock is shown for context — on identical hardware it moves 4-20% run to run.

Measured by tak — instruction-counted CLI benchmarks, stored in this repository's git notes.

5373abbdc400 vs 2cdd69da7ec6 · measured on the runner, not pushed to the history.

@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds derive-based subcommand compilation and addresses the previously reported naming, qualified-path, and sibling key-collision failures.

  • Generates static command and metadata tables for Args and Subcommands.
  • Routes parser events into per-command partial values and constructs the selected enum variant.
  • Uses variant-level command names and descriptions in emitted specifications.
  • Widens parser keys and validates duplicate keys during debug KDL emission.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
derive/src/codegen.rs Generates subcommand tables, partial-state dispatch, variant-specific names, scope-aware type paths, and widened keys; the previously reported code-generation defects are addressed.
derive/src/model.rs Models and validates optional subcommand fields and enum variants, including duplicate names and wrapped-type collisions.
argv/src/spec.rs Adds the static traits used by generated command trees and a debug duplicate-key assertion for emitted specifications.
argv/src/lib.rs Widens command, flag, and argument keys to support independently generated command tables.
conformance/tests/subcommands.rs Exercises renamed variant routing, command-specific values, global flags, optional selection, key uniqueness, and emitted KDL.

Reviews (2): Last reviewed commit: "fix(derive): let the variant name its co..." | Re-trigger Greptile

Comment thread derive/src/codegen.rs Outdated
Comment thread derive/src/codegen.rs
Comment thread derive/src/codegen.rs

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4d9599f. Configure here.

Comment thread derive/src/codegen.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (4)
conformance/tests/subcommands.rs (1)

63-65: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test that an explicit value overrides the default.

Lines 63-65 only test the absent-value path. Add a parse case with --jobs 8 and assert install.jobs.as_deref() == Some("8"). This detects implementations that apply defaults after event binding and overwrite user input.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@conformance/tests/subcommands.rs` around lines 63 - 65, Extend the relevant
parsing test around the existing install.jobs default assertion to parse an
explicit --jobs 8 value, then assert install.jobs.as_deref() is Some("8"). Keep
the untouched-flag assertion for the default and verify the explicit user value
remains unchanged after parsing.
derive/src/model.rs (1)

883-899: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Point the duplicate-name error at the variant.

dup receives variant.ty.span(), which is the wrapped struct's span. Two variants that wrap the same struct type therefore report both spans at the same place. Use variant.ident.span() so the message points at each variant.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@derive/src/model.rs` around lines 883 - 899, Update the duplicate-name error
construction in the variants loop to pass variant.ident.span() to dup instead of
variant.ty.span(), so each duplicate diagnostic points to its variant identifier
while preserving the existing first-duplicate span.
argv/src/spec.rs (1)

747-767: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Drop the Default bound from CommandArgs::Partial.

The generated parser uses CommandArgs::start() for command partials. It does not call CommandArgs::Partial::default(). Keep Subcommands::Partial: Default, because the generated root parser calls it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@argv/src/spec.rs` around lines 747 - 767, Remove the Default bound from the
associated type Partial in the CommandArgs trait, since command parsing
initializes partials through CommandArgs::start(). Preserve the Default bound on
Subcommands::Partial because the generated root parser still calls its default
constructor.
derive/src/codegen.rs (1)

163-175: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The two byte-to-string helpers are emitted twice with identical bodies.

__usage_text and __usage_value_text appear in the emit module and again in the emit_args module. The bodies are the same. A change to the lossy-conversion policy, which the comment says arrives with typed fields, must then be made in two places.

Extract the pair into one function that returns the token stream, and call it from both emitters.

♻️ Proposed refactor
+/// The byte-to-string helpers every generated module needs.
+fn text_helpers() -> TokenStream {
+    quote! {
+        pub fn __usage_text(value: &[u8]) -> ::std::string::String {
+            ::std::string::String::from_utf8_lossy(value).into_owned()
+        }
+
+        pub fn __usage_value_text(
+            value: ::std::option::Option<&[u8]>,
+        ) -> ::std::string::String {
+            value.map(__usage_text).unwrap_or_default()
+        }
+    }
+}

Then splice #helpers into both generated modules.

Also applies to: 649-657

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@derive/src/codegen.rs` around lines 163 - 175, Extract the shared
`__usage_text` and `__usage_value_text` definitions into a single helper
function that returns their token stream. Update both `emit` and `emit_args` to
call this helper and splice the resulting `#helpers` into their generated
modules, removing the duplicated definitions while preserving their current
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@argv/src/lib.rs`:
- Around line 108-115: Update the documentation for the public key field to
state that key 0 is reserved for “no subcommand selected” and must not be
assigned to subcommands, including commands created with Command::EMPTY.

In `@argv/src/spec.rs`:
- Around line 29-52: Update collect_keys to track visited command addresses and
skip commands already seen before collecting their key, flags, arguments, or
recursing into subcommands. Pass the visited set through recursive calls so
shared command statics are not reported as duplicates and self-referential
command graphs terminate safely; keep duplicate_key’s sorting and duplicate
detection behavior unchanged.

In `@derive/src/codegen.rs`:
- Around line 571-577: The emit_args path must not generate invalid builds for
Kind::Subcommand fields: either add complete subcommand support matching
emit—including filtered field_finals, COMMAND and COMMAND_META subcommands
entries, and Event::Command routing in apply—or reject such fields during model
validation with a clear error. At minimum, update field_finals in emit_args to
exclude subcommand fields, and add the model-level rejection if routing is not
implemented.
- Around line 95-103: Update the generated command-event handling in the
`quote!` block so `__usage_selected` records only the first command key
encountered during descent, preserving the direct root subcommand key when
nested `Event::Command` values arrive. Keep forwarding every event to `<`#ty` as
::usage_argv::spec::Subcommands>::apply`, and ensure direct command selection
continues to match the recorded key.
- Around line 504-507: Update the bool-default handling in the Shape::Bool
branch so unrecognized values cannot silently become false. First check model
validation for boolean defaults; if absent, add validation there to reject
values other than the supported boolean spellings, or explicitly accept the
intended common spellings before generating the assignment. Preserve generation
of the correct enabled or disabled boolean value for valid defaults.
- Around line 386-401: Update key_base and its callers to hash a caller-supplied
type qualifier in addition to the bare type name, ensuring same-named types from
different modules receive distinct keys. Trace all key_base call sites in the
code-generation paths, including emit, emit_args, and emit_subcommands, and pass
the available disambiguating identity through consistently. Revise the key_base
documentation to describe the qualifier and retain the probabilistic collision
guarantee.
- Around line 629-635: Update emit_args and the generated COMMAND initializer to
honor cli.unknown_flags using the same resolution as emit, while preserving the
intended nested-command precedence; alternatively, explicitly reject
unknown_flags on Args during model validation. Do not leave the attribute
silently ignored or rely on Command::EMPTY for its value.

In `@derive/src/lib.rs`:
- Around line 155-162: Add `Cli::check_args` beside `Cli::check` to reject
`Kind::Subcommand` fields with the documented nesting error and reject any
`bin`, `version`, or `unknown_flags` declarations with an item-level diagnostic.
Invoke this validation from `derive_args` after `Cli::from_input` succeeds and
before `codegen::emit_args`, returning its compile error instead of generating
invalid or incomplete code.

In `@derive/src/model.rs`:
- Around line 911-930: Validate the variant name after processing attributes in
the variant-name derivation flow, rejecting it when strip_dashes produces an
empty string. Mirror the existing validation behavior used by Field::from_field,
while preserving the default kebab-case name and valid explicit names.
- Around line 307-323: Update the subcommand handling in from_field and
Self::subcommand so subcommand fields do not silently discard metadata: reject a
field doc comment and sibling usage options such as long or default with clear
diagnostics, while preserving the existing subcommand construction for valid
fields.
- Around line 288-305: Update the subcommand type handling around type_name to
inspect the original syn::Type::Path and extract the inner Option<T> argument
directly, preserving qualified paths such as commands::Commands. Remove the
syn::parse_str round trip and the unused _optional binding while retaining the
existing validation and error for non-Option subcommand fields.

---

Nitpick comments:
In `@argv/src/spec.rs`:
- Around line 747-767: Remove the Default bound from the associated type Partial
in the CommandArgs trait, since command parsing initializes partials through
CommandArgs::start(). Preserve the Default bound on Subcommands::Partial because
the generated root parser still calls its default constructor.

In `@conformance/tests/subcommands.rs`:
- Around line 63-65: Extend the relevant parsing test around the existing
install.jobs default assertion to parse an explicit --jobs 8 value, then assert
install.jobs.as_deref() is Some("8"). Keep the untouched-flag assertion for the
default and verify the explicit user value remains unchanged after parsing.

In `@derive/src/codegen.rs`:
- Around line 163-175: Extract the shared `__usage_text` and
`__usage_value_text` definitions into a single helper function that returns
their token stream. Update both `emit` and `emit_args` to call this helper and
splice the resulting `#helpers` into their generated modules, removing the
duplicated definitions while preserving their current behavior.

In `@derive/src/model.rs`:
- Around line 883-899: Update the duplicate-name error construction in the
variants loop to pass variant.ident.span() to dup instead of variant.ty.span(),
so each duplicate diagnostic points to its variant identifier while preserving
the existing first-duplicate span.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b5ea04d-5b77-4314-8fa6-cacf69196b1e

📥 Commits

Reviewing files that changed from the base of the PR and between 2cdd69d and 4d9599f.

⛔ Files ignored due to path filters (1)
  • conformance/tests/snapshots/subcommands__the_emitted_spec_reads_the_way_a_handwritten_one_would.snap is excluded by !**/*.snap
📒 Files selected for processing (6)
  • argv/src/lib.rs
  • argv/src/spec.rs
  • conformance/tests/subcommands.rs
  • derive/src/codegen.rs
  • derive/src/lib.rs
  • derive/src/model.rs

Comment thread argv/src/lib.rs
Comment thread argv/src/spec.rs
Comment on lines +29 to +52
/// The first key that appears twice anywhere in a command tree, if any.
///
/// Keys are what a parse dispatches on, and a derive assigns them without being able
/// to see other expansions — it hashes the type name to keep them apart. That makes
/// a collision astronomically unlikely rather than impossible, so it is checked
/// where a CLI is written out, which every adopter does in a test.
fn duplicate_key(cmd: &Command<'_>) -> Option<u64> {
let mut keys = std::vec::Vec::new();
collect_keys(cmd, &mut keys);
keys.sort_unstable();
keys.windows(2)
.find(|pair| pair[0] == pair[1])
.map(|pair| pair[0])
}

fn collect_keys(cmd: &Command<'_>, keys: &mut std::vec::Vec<u64>) {
keys.push(cmd.key);
keys.extend(cmd.flags.iter().map(|f| f.key));
keys.extend(cmd.args.iter().map(|a| a.key));
for sub in cmd.subcommands {
collect_keys(sub, keys);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

collect_keys treats a shared subcommand as a duplicate.

The command tree is a graph of &'static Command references, not necessarily a tree. Subcommands::COMMANDS splices <Ty as CommandArgs>::COMMAND in, so the same static can be reached from two parents. Examples: one Args type listed in two enums, or one enum used by two commands. collect_keys then pushes that command's key, flag keys, and arg keys twice, and duplicate_key reports a collision that does not exist. The debug_assert! in to_kdl turns that into a test failure for legal code.

A self-referential static Command also makes this recursion unbounded, which overflows the stack instead of reporting anything.

Track visited command addresses to fix both.

🐛 Proposed fix: skip commands already visited
 fn duplicate_key(cmd: &Command<'_>) -> Option<u64> {
     let mut keys = std::vec::Vec::new();
-    collect_keys(cmd, &mut keys);
+    let mut seen: std::vec::Vec<*const Command<'_>> = std::vec::Vec::new();
+    collect_keys(cmd, &mut keys, &mut seen);
     keys.sort_unstable();
     keys.windows(2)
         .find(|pair| pair[0] == pair[1])
         .map(|pair| pair[0])
 }
 
-fn collect_keys(cmd: &Command<'_>, keys: &mut std::vec::Vec<u64>) {
+fn collect_keys(
+    cmd: &Command<'_>,
+    keys: &mut std::vec::Vec<u64>,
+    seen: &mut std::vec::Vec<*const Command<'_>>,
+) {
+    // The same command can be reached from two parents, and a static can even
+    // reference itself. Counting it twice would report a collision that is not one.
+    let addr = cmd as *const _;
+    if seen.contains(&addr) {
+        return;
+    }
+    seen.push(addr);
     keys.push(cmd.key);
     keys.extend(cmd.flags.iter().map(|f| f.key));
     keys.extend(cmd.args.iter().map(|a| a.key));
     for sub in cmd.subcommands {
-        collect_keys(sub, keys);
+        collect_keys(sub, keys, seen);
     }
 }

Run the following script to check whether any generated or hand-written spec reaches one command static from two parents:

#!/bin/bash
# Description: Find command statics spliced into more than one parent table.
rg -n -C6 'COMMANDS|subcommands:' derive/src/codegen.rs
rg -n -C4 'subcommands:' --type=rust -g '!derive/**'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@argv/src/spec.rs` around lines 29 - 52, Update collect_keys to track visited
command addresses and skip commands already seen before collecting their key,
flags, arguments, or recursing into subcommands. Pass the visited set through
recursive calls so shared command statics are not reported as duplicates and
self-referential command graphs terminate safely; keep duplicate_key’s sorting
and duplicate detection behavior unchanged.

Comment thread derive/src/codegen.rs
Comment on lines +95 to +103
quote! {
if let ::usage_argv::Event::Command(__usage_cmd) = &__usage_event {
__usage_selected = __usage_cmd.key;
}
<#ty as ::usage_argv::spec::Subcommands>::apply(
&mut __usage_sub,
&__usage_event,
);
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Any Event::Command overwrites __usage_selected, so a nested command discards the whole subcommand.

sub_route assigns __usage_selected = __usage_cmd.key for every command event that the root does not claim. apply in the generated root returns false for all Event::Command values (Line 564), so every command event in the parse reaches this block, including one for a grandchild command.

After a two-level descent, __usage_selected holds the grandchild key. Subcommands::select compares that key against each direct variant's COMMAND.key, finds no match, and returns None. The parse then produces None for the subcommand field even though the variant's partial was filled. No error is reported.

Record the first command key instead, or match the key against the direct variants before overwriting.

🐛 Proposed fix: keep the first command key
                 quote! {
                     if let ::usage_argv::Event::Command(__usage_cmd) = &__usage_event {
-                        __usage_selected = __usage_cmd.key;
+                        // The first command word selects the variant. A deeper command
+                        // belongs to that variant, and must not replace the selection.
+                        if __usage_selected == 0 {
+                            __usage_selected = __usage_cmd.key;
+                        }
                     }

Run the following script to confirm that the parser emits a command event per descent level:

#!/bin/bash
# Description: Inspect how the parser emits Event::Command while descending.
rg -n -C8 'Event::Command' argv/src/lib.rs

# Check whether any conformance test covers two levels of commands.
rg -n -C10 'Subcommands' conformance/tests/subcommands.rs
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@derive/src/codegen.rs` around lines 95 - 103, Update the generated
command-event handling in the `quote!` block so `__usage_selected` records only
the first command key encountered during descent, preserving the direct root
subcommand key when nested `Event::Command` values arrive. Keep forwarding every
event to `<`#ty` as ::usage_argv::spec::Subcommands>::apply`, and ensure direct
command selection continues to match the recorded key.

Comment thread derive/src/codegen.rs
Comment thread derive/src/codegen.rs
Comment on lines +504 to +507
Shape::Bool => {
let on = default == "true";
quote!(partial.#ident = #on;)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

A bool default that is not the exact text true becomes false without a diagnostic.

default == "true" treats default = "1", default = "yes", and default = "True" as false. The user asked for an enabled default and receives a disabled one. Reject an unrecognized value in the model, or accept the common spellings here.

Run the following script to check whether the model already validates this:

#!/bin/bash
# Description: Look for validation of a bool field's default value.
rg -n -C6 'default' derive/src/model.rs | rg -n -C6 'Bool|bool|true|false'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@derive/src/codegen.rs` around lines 504 - 507, Update the bool-default
handling in the Shape::Bool branch so unrecognized values cannot silently become
false. First check model validation for boolean defaults; if absent, add
validation there to reject values other than the supported boolean spellings, or
explicitly accept the intended common spellings before generating the
assignment. Preserve generation of the correct enabled or disabled boolean value
for valid defaults.

Comment thread derive/src/codegen.rs
Comment on lines +629 to +635
pub static COMMAND: Command = Command {
name: #name,
key: #command_key,
flags: &[#(#flag_refs),*],
args: &[#(#arg_refs),*],
..Command::EMPTY
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

emit_args drops cli.unknown_flags, so the setting is silent on a subcommand.

emit resolves cli.unknown_flags into UnknownFlags::Error or UnknownFlags::Value and writes it into ROOT. This COMMAND omits the field and takes whatever Command::EMPTY provides. A user who writes unknown_flags = "error" on an Args struct gets no effect and no warning.

Either apply the same resolution here, or reject the attribute on an Args struct in the model.

♻️ Proposed fix: honour the declared mode
+    let unknown_flags = match cli.unknown_flags.as_deref() {
+        Some("error") => quote!(::usage_argv::UnknownFlags::Error),
+        _ => quote!(::usage_argv::UnknownFlags::Value),
+    };
...
             pub static COMMAND: Command = Command {
+                unknown_flags: `#unknown_flags`,
                 name: `#name`,
                 key: `#command_key`,

Note that emit inherits nothing from a parent, and the parser may resolve inheritance differently for a nested command. Confirm the intended precedence before applying.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@derive/src/codegen.rs` around lines 629 - 635, Update emit_args and the
generated COMMAND initializer to honor cli.unknown_flags using the same
resolution as emit, while preserving the intended nested-command precedence;
alternatively, explicitly reject unknown_flags on Args during model validation.
Do not leave the attribute silently ignored or rely on Command::EMPTY for its
value.

Comment thread derive/src/lib.rs
Comment on lines +155 to +162
#[proc_macro_derive(Args, attributes(usage))]
pub fn derive_args(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
match model::Cli::from_input(&input) {
Ok(cli) => codegen::emit_args(&cli).into(),
Err(e) => e.to_compile_error().into(),
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Args accepts declarations it cannot compile.

derive_args reuses model::Cli::from_input, so an Args struct passes the same validation as a Cli struct. Two cases get through and then behave badly:

  1. A #[usage(subcommand)] field. partial_struct skips Kind::Subcommand fields, but the field_finals in emit_args does not filter them, so build names a field that Partial does not have. The user gets an error inside generated code rather than the "nesting deeper than one level is not supported yet" message that lines 120-121 promise.
  2. bin, version, and unknown_flags. emit_args reads none of them, and its COMMAND falls back to Command::EMPTY for unknown_flags. The options are accepted and then dropped without a diagnostic.

Validate both in the Args path and report them as errors on the offending item.

🐛 Proposed fix: reject what `Args` cannot express
 #[proc_macro_derive(Args, attributes(usage))]
 pub fn derive_args(input: TokenStream) -> TokenStream {
     let input = parse_macro_input!(input as DeriveInput);
-    match model::Cli::from_input(&input) {
-        Ok(cli) => codegen::emit_args(&cli).into(),
-        Err(e) => e.to_compile_error().into(),
-    }
+    match model::Cli::from_input(&input).and_then(|cli| {
+        cli.check_args()?;
+        Ok(cli)
+    }) {
+        Ok(cli) => codegen::emit_args(&cli).into(),
+        Err(e) => e.to_compile_error().into(),
+    }
 }

Add check_args next to Cli::check in derive/src/model.rs:

impl Cli {
    /// Reject what an `Args` struct declares but cannot compile into.
    pub fn check_args(&self) -> syn::Result<()> {
        if let Some(field) = self
            .fields
            .iter()
            .find(|f| matches!(f.kind, Kind::Subcommand { .. }))
        {
            return Err(syn::Error::new(
                field.span,
                "a subcommand cannot declare its own subcommands yet: nesting is one \
                 level deep",
            ));
        }
        if self.bin.is_some() || self.version.is_some() || self.unknown_flags.is_some() {
            return Err(syn::Error::new(
                self.ident.span(),
                "`bin`, `version`, and `unknown_flags` describe a program rather than \
                 one of its commands, so usage::Args does not take them",
            ));
        }
        Ok(())
    }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@derive/src/lib.rs` around lines 155 - 162, Add `Cli::check_args` beside
`Cli::check` to reject `Kind::Subcommand` fields with the documented nesting
error and reject any `bin`, `version`, or `unknown_flags` declarations with an
item-level diagnostic. Invoke this validation from `derive_args` after
`Cli::from_input` succeeds and before `codegen::emit_args`, returning its
compile error instead of generating invalid or incomplete code.

Comment thread derive/src/model.rs
Comment thread derive/src/model.rs
Comment on lines +307 to +323
Ok(Some(Field {
ident: ident.clone(),
ty: field.ty.clone(),
name: to_kebab(&ident.to_string()),
kind: Kind::Subcommand { ty },
// A subcommand field holds a command, not a value, so none of what
// describes a value applies to it.
shape: Shape::Bool,
help: None,
long_help: None,
env: None,
default: None,
help_heading: None,
hide: false,
repeatable: false,
span,
}))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

A subcommand field silently drops its doc comment and its sibling options.

from_field reads the doc comment before it calls Self::subcommand, and this constructor then sets help: None and long_help: None. The doc comment on the field disappears without a diagnostic. The conformance fixture writes /// What to do on command, so this is reachable.

Self::subcommand also returns as soon as it sees subcommand, so #[usage(subcommand, long, default = "x")] compiles and ignores everything except subcommand.

Choose one behavior for each and make it explicit: carry the values, or reject them with a message.

♻️ Proposed change: reject the options a subcommand field cannot use
         let mut is_subcommand = false;
+        let mut extra: Option<proc_macro2::Span> = None;
         for attr in attrs(&field.attrs) {
             for meta in nested(attr)? {
-                if ident_of(&meta.path().clone()) == "subcommand" {
+                let path = meta.path().clone();
+                if ident_of(&path) == "subcommand" {
                     if !matches!(meta, Meta::Path(_)) {
                         return Err(syn::Error::new_spanned(
                             meta.path(),
                             "`subcommand` takes no value: the enum it holds is the \
                              field's type",
                         ));
                     }
                     is_subcommand = true;
+                } else if extra.is_none() {
+                    extra = Some(path.span());
                 }
             }
         }
         if !is_subcommand {
             return Ok(None);
         }
+        if let Some(span) = extra {
+            return Err(syn::Error::new(
+                span,
+                "a `subcommand` field holds a command rather than a value, so it \
+                 takes no other options",
+            ));
+        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@derive/src/model.rs` around lines 307 - 323, Update the subcommand handling
in from_field and Self::subcommand so subcommand fields do not silently discard
metadata: reject a field doc comment and sibling usage options such as long or
default with clear diagnostics, while preserving the existing subcommand
construction for valid fields.

Comment thread derive/src/model.rs
Comment on lines +911 to +930
let mut name = to_kebab(&variant.ident.to_string());

for attr in attrs(&variant.attrs) {
for meta in nested(attr)? {
let path = meta.path().clone();
match ident_of(&path).as_str() {
"name" => name = strip_dashes(&string_value(&meta)?),
other => {
return Err(syn::Error::new_spanned(
path,
format!(
"unknown option `{other}` on a variant; a subcommand \
variant takes `name` here, and its description comes \
from the doc comment"
),
));
}
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject an empty variant name.

name is written from strip_dashes, so #[usage(name = "-")] leaves an empty string. An empty command name can never be selected on the command line, and it is written into the spec as an empty node name. Field::from_field already rejects this case for a flag or an argument. Apply the same check here.

🛡️ Proposed fix
                 match ident_of(&path).as_str() {
-                    "name" => name = strip_dashes(&string_value(&meta)?),
+                    "name" => {
+                        name = strip_dashes(&string_value(&meta)?);
+                        if name.is_empty() {
+                            return Err(syn::Error::new_spanned(
+                                path,
+                                "`name` is empty once its dashes are removed, and a \
+                                 subcommand needs something to be called",
+                            ));
+                        }
+                    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let mut name = to_kebab(&variant.ident.to_string());
for attr in attrs(&variant.attrs) {
for meta in nested(attr)? {
let path = meta.path().clone();
match ident_of(&path).as_str() {
"name" => name = strip_dashes(&string_value(&meta)?),
other => {
return Err(syn::Error::new_spanned(
path,
format!(
"unknown option `{other}` on a variant; a subcommand \
variant takes `name` here, and its description comes \
from the doc comment"
),
));
}
}
}
}
let mut name = to_kebab(&variant.ident.to_string());
for attr in attrs(&variant.attrs) {
for meta in nested(attr)? {
let path = meta.path().clone();
match ident_of(&path).as_str() {
"name" => {
name = strip_dashes(&string_value(&meta)?);
if name.is_empty() {
return Err(syn::Error::new_spanned(
path,
"`name` is empty once its dashes are removed, and a \
subcommand needs something to be called",
));
}
}
other => {
return Err(syn::Error::new_spanned(
path,
format!(
"unknown option `{other}` on a variant; a subcommand \
variant takes `name` here, and its description comes \
from the doc comment"
),
));
}
}
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@derive/src/model.rs` around lines 911 - 930, Validate the variant name after
processing attributes in the variant-name derivation flow, rejecting it when
strip_dashes produces an empty string. Mirror the existing validation behavior
used by Field::from_field, while preserving the default kebab-case name and
valid explicit names.

…ruct

Four review findings, two of which my tests had passed by coincidence.

The variant's name never reached the tables. `Subcommands` read it, validated
it, documented it as the command name — and then spliced the wrapped struct's
command unchanged, so the *struct's* name selected the command. My test wrote
`RunTask(Run)` with `name = "run"`, where both spellings agree, so it passed
while the feature did not work. Each variant now gets its own command and
metadata overriding the name, with a test where the two names differ and one
asserting the struct's name selects nothing.

Two variants wrapping one struct is now refused. A command's values collect
into the struct that declares them, and its fields' keys come from that struct,
so two commands sharing one would collect into whichever was reached first and
choosing between them would be a coin toss. That is a compile error naming both
sites rather than a silent misbinding — writing the test for it is what showed
the misbinding.

`in_module` prepended `super::` to any path, which turned `crate::cmds::X` into
nonsense and quietly redirected `self::X` and `super::X`. Each prefix now
shifts by the right amount.

The duplicate-key check treated one table met twice as a collision, so it
paired each key with the address it came from: two *different* tables sharing a
key is the fault, meeting the same one twice is not.

Also: "no subcommand given" is an `Option` rather than a reserved key, and
`apply` arms evaluate to whether they claimed the event instead of returning —
a command with no flags of its own had every arm diverge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

jdx commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

Four findings fixed, and two of them my tests had passed by coincidence — worth spelling out, because it is the same trap twice.

The variant's name never reached the tables. Subcommands read it, validated it against its siblings, documented it as the command name — and then spliced the wrapped struct's command unchanged, so the struct's name selected the command. My test wrote RunTask(Run) with name = "run", where the two spellings agree, so it passed while the feature did not work. Each variant now gets its own command and metadata overriding the name, and the new test uses Install(AddArgs) named add, plus an assertion that the struct's name selects nothing.

Two variants wrapping one struct cannot work, and writing the test for it is what showed why: a command's values collect into the struct that declares them, and its fields' keys come from that struct — so two commands sharing one collect into whichever was reached first, and select picks between them by a key they share. It is now a compile error naming both sites:

error: two variants both wrap `S`, and a command collects into the struct that
       declares it — so give each command its own struct, even if the fields
       are identical

in_module prepended super:: to any path, turning crate::cmds::X into nonsense and quietly redirecting self::X and super::X to the wrong module. Each prefix now shifts by the right amount, and an absolute path is left alone.

The duplicate-key check counted one table met twice as a collision. It now pairs each key with the address it came from: two different tables sharing a key is the fault; meeting the same table twice is not.

Plus two smaller ones: "no subcommand given" is an Option rather than a reserved key, and apply arms evaluate to whether they claimed the event instead of returning — a command with no flags of its own had every arm diverge, which clippy caught as an unreachable tail.

10 tests on this file now. The coverage failure earlier was a runner TLS error in actions/checkout, not the code; re-run and green.

AI-assisted — Tool: Claude Code; model: anthropic/claude-fable-5; version: unavailable.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@conformance/tests/subcommands.rs`:
- Around line 49-51: Update the conformance test cases around the wrapped-struct
command routing to use the actual command names “add-args” and “remove-args”
instead of the nonexistent “shared” command. Revise the nearby comment to remove
the claim that one argument struct is shared, while preserving coverage that
wrapped struct names cannot route.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: ac09f1c0-d51b-46b0-a1a4-951bc0b47096

📥 Commits

Reviewing files that changed from the base of the PR and between 4d9599f and 5373abb.

📒 Files selected for processing (3)
  • conformance/tests/subcommands.rs
  • derive/src/codegen.rs
  • derive/src/model.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • derive/src/model.rs

Comment on lines +49 to +51
/// A second CLI where the variant's name and the struct's differ, and where one
/// argument struct is shared by two variants. The first version passed its tests by
/// coincidence: `RunTask(Run)` named its command `run` either way.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Test the actual wrapped struct command names.

No Shared struct exists in this test. "shared" only tests an arbitrary unknown command. Test add-args and remove-args so the test verifies that wrapped struct command names cannot route. Update the stale comment that says one struct is shared.

Also applies to: 89-94

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@conformance/tests/subcommands.rs` around lines 49 - 51, Update the
conformance test cases around the wrapped-struct command routing to use the
actual command names “add-args” and “remove-args” instead of the nonexistent
“shared” command. Revise the nearby comment to remove the claim that one
argument struct is shared, while preserving coverage that wrapped struct names
cannot route.

@jdx
jdx merged commit ac75068 into main Aug 11, 2026
9 checks passed
@jdx
jdx deleted the agent/derive-subcommands branch August 11, 2026 22:24
jdx added a commit that referenced this pull request Aug 12, 2026
Third of the stack. mise reaches four levels — `mise bootstrap macos
launchd-agents apply` — so #816's one-level limit had to go.

## A nested command is not a special case

An `Args` struct carries a `subcommand` field exactly as the root does:

```rust
#[derive(Args)]
struct Settings {
    #[usage(long)]
    file: Option<String>,
    #[usage(subcommand)]
    command: SettingsCommands,   // and these can nest again
}
```

What made that cheap was pulling the wiring out of the root's emitter
into one place both use — the tables to splice, the state to carry, how
an event is routed, how the field is built. **The root now differs from
a nested command only in how it is entered.**

## Two consequences worth flagging

**`build` and `select` are fallible.** A command can require a
subcommand of its own, and "none was given" is only knowable where the
value has to exist.

**Every generated reference to a user type now sits at one scope.** The
root's post-binding checks were emitted beside the parse rather than
inside the generated module — harmless until a *nested* command's check
referred to the user's enum from there and `super::` escaped the crate
root:

```
error[E0433]: too many leading `super` keywords
```

That was worth more than the fix: two emitters had drifted into putting
the same code at different scopes, and the bug only appeared once the
two met. Both now put the checks in the module and call them, so there
is one answer to "where does this code live". A unit test pins
`in_module`'s behaviour for plain, `crate::`, `::absolute`, `self::`,
and `super::` paths, since I had reasoned about it twice and been wrong
once.

## Verified

Eight tests on a three-level CLI: routing to the deepest command, each
level keeping its own flags, a global reaching any depth *and* working
after the deepest command, a middle command requiring one of its own, a
deep command's requirements being its own rather than its parent's
(`settings set jobs` → `MissingRequired { name: "value" }` while
`settings ls` is fine), and the spec:

```kdl
cmd "settings" help="Manage settings" {
    flag "--file" help="Which settings file" {
        arg "<file>"
    }
    cmd "set" help="Set a value" {
        arg "<key>" help="Which setting"
        arg "<value>" help="The value"
    }
    cmd "ls" help="Show every value" {
        flag "--json" help="As JSON"
    }
}
```

## What is left before a mise-shaped CLI is expressible

`flatten`, and the `conflicts`/`requires`/`overrides` family — which
need the order flags arrived in, so they want a small ordering record.
Then the bench harness and the gate.

*AI-assisted — Tool: Claude Code; model: anthropic/claude-fable-5;
version: unavailable.*

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches core derive codegen and trait APIs
(`CommandArgs`/`Subcommands`), including fallible `build`/`select` and
selection-by-position instead of key. Well covered by new nesting
conformance tests, but hand-written trait impls would break.
> 
> **Overview**
> **Enables arbitrarily nested subcommands** — an `Args` struct can
carry a `subcommand` field exactly as the root does, to any depth
(needed for mise's four-level trees).
> 
> Root and nested commands now share one `subcommand_parts` wiring path
for tables, routing, checks, and builds. `CommandArgs::build` and
`Subcommands::select` become fallible so a middle command can require
its own subcommand. Selection uses table position (via pointer identity)
instead of command keys, so key collisions cannot pick the wrong
variant.
> 
> Also hardens key assignment: fingerprints hash the whole declaration
(not just the type name), and flag/arg match arms verify table identity
so same-named structs in different modules cannot misbind. Post-binding
checks move into the generated module so root and nested code share one
scope.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7822d16. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added support for arbitrarily nested subcommands, including
command-specific and global options.
  * Added support for required nested subcommands and arguments.
* Improved routing, validation, and error reporting during nested
command construction.

* **Bug Fixes**
* Resolved naming collisions for same-named arguments in different
modules, ensuring commands route correctly.

* **Documentation**
  * Updated documentation to reflect deep subcommand nesting support.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Aug 24, 2026
⚠️ **CAUTION: this is a major update, indicating a breaking change!** ⚠️

This MR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [usage](https://github.com/jdx/usage) | tools | major | `5.1.0` → `6.2.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>jdx/usage (usage)</summary>

### [`v6.2.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#620---2026-08-24)

[Compare Source](jdx/usage@v6.1.1...v6.2.0)

##### 🚀 Features

- **(argv)** add embedded parse outcomes by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1250](jdx/usage#1250)
- **(cli)** render inline formatting in help text by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1245](jdx/usage#1245)
- **(cli)** split grouped help template sections by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1251](jdx/usage#1251)
- **(complete)** add presentation labels to candidates by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1239](jdx/usage#1239)
- **(complete)** expose structured completion traces by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1241](jdx/usage#1241)
- **(complete)** add semantic candidate kinds by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1242](jdx/usage#1242)
- **(complete)** add Elvish runtime completions by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1243](jdx/usage#1243)
- **(derive)** let argument groups carry values by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1253](jdx/usage#1253)
- **(derive)** add typed command finalization by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1254](jdx/usage#1254)
- **(derive)** add runtime-computed defaults by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1256](jdx/usage#1256)
- **(derive)** dispatch embedded control requests by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1270](jdx/usage#1270)
- **(derive)** emit embedded\_outcome\_into for converted CLIs by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1281](jdx/usage#1281)
- **(docs)** allow overriding markdown templates by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1267](jdx/usage#1267)
- **(docs)** default to compact markdown references by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1272](jdx/usage#1272)
- **(docs)** polish compact markdown references by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1280](jdx/usage#1280)
- **(help)** expose addressable help topics by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1257](jdx/usage#1257)
- **(help)** list commands by name in one aligned column by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1284](jdx/usage#1284)
- **(help)** wrap the short help page by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1287](jdx/usage#1287)
- **(parse)** add structured diagnostic reports by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1255](jdx/usage#1255)
- **(parse)** add opt-in response files by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1259](jdx/usage#1259)
- **(parse)** preserve ordered argument groups by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1271](jdx/usage#1271)
- **(spec)** declare command outputs and exit codes by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1249](jdx/usage#1249)
- **(spec)** add surface availability metadata by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1258](jdx/usage#1258)
- **(spec)** add semantic note and warning blocks by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1273](jdx/usage#1273)
- **(spec)** add output media types by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1274](jdx/usage#1274)
- **(spec)** add help prose to heading sections by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1282](jdx/usage#1282)
- add dynamic command catalogs by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1275](jdx/usage#1275)

##### 🐛 Bug Fixes

- **(completion)** handle attached values and emit built-ins by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1277](jdx/usage#1277)
- **(derive)** preserve flattened command metadata by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1268](jdx/usage#1268)
- **(derive)** skip choice checks for typed defaults by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1269](jdx/usage#1269)
- **(derive)** suppress generated partial field lint by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1278](jdx/usage#1278)
- **(derive)** keep an invalid choice after an override displaces the flag by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1286](jdx/usage#1286)
- **(spec)** make the two KDL writers agree on three more nodes by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1289](jdx/usage#1289)

##### 🚜 Refactor

- **(deps)** replace versions with semver by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1285](jdx/usage#1285)

##### ⚡ Performance

- **(argv)** reduce sort code size by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1264](jdx/usage#1264)
- **(markdown)** skip empty admonition context by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1279](jdx/usage#1279)
- document usage-rs parser tradeoffs by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1265](jdx/usage#1265)

##### 🛡️ Security

- **(complete)** filter path candidates by extension by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1240](jdx/usage#1240)

##### 🔍 Other Changes

- update usage of deprecated `str downcase` thingy in nushell by [@&#8203;TheBearodactyl](https://github.com/TheBearodactyl) in [#&#8203;1262](jdx/usage#1262)

##### New Contributors

- [@&#8203;TheBearodactyl](https://github.com/TheBearodactyl) made their first contribution in [#&#8203;1262](jdx/usage#1262)

### [`v6.1.1`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#611---2026-08-23)

[Compare Source](jdx/usage@v6.1.0...v6.1.1)

##### 🐛 Bug Fixes

- **(argv)** simplify generated completion headers by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1226](jdx/usage#1226)
- **(argv)** plan for the target platform, not the host by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;1233](jdx/usage#1233)
- **(complete)** keep the path separator the caller typed by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;1230](jdx/usage#1230)
- **(config)** report config paths without the verbatim prefix by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;1232](jdx/usage#1232)
- **(docs)** separate visible flag aliases by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1228](jdx/usage#1228)
- **(test)** compile the platform-conditional fixtures warning-free on windows by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;1234](jdx/usage#1234)

##### ⚡ Performance

- **(derive)** outline invalid-value error construction from generated builds by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1235](jdx/usage#1235)
- **(derive)** share the repeated-value collection loop across fields by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1236](jdx/usage#1236)

##### 🧪 Testing

- **(windows)** let the suite run where zsh, fish and bash-completion are not by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;1229](jdx/usage#1229)

### [`v6.1.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#610---2026-08-22)

[Compare Source](jdx/usage@v6.0.0...v6.1.0)

##### 🚀 Features

- **(cli)** read settings under a prefix mise does not strip by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;1213](jdx/usage#1213)
- **(derive)** dispatch more of the matches CLIs already write by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1221](jdx/usage#1221)
- **(spec)** apply runtime identity and flatten headings in help by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1220](jdx/usage#1220)

##### 🐛 Bug Fixes

- **(derive)** flow long help and emit kdl raw multiline strings by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1215](jdx/usage#1215)

##### 📚 Documentation

- **(rust)** drop the restated one-declaration line from the intro by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1211](jdx/usage#1211)
- **(spec)** complete KDL reference by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1214](jdx/usage#1214)

### [`v6.0.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#600---2026-08-22)

[Compare Source](jdx/usage@v5.1.0...v6.0.0)

##### 🚀 Features

- **(argv)** add a zero-allocation argv parser by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;798](jdx/usage#798)
- **(argv)** emit a usage spec from static metadata by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;801](jdx/usage#801)
- **(argv)** a bound stops a variadic by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;826](jdx/usage#826)
- **(argv)** route a word that names nothing to the default subcommand by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;848](jdx/usage#848)
- **(argv)** join static tables at compile time by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;851](jdx/usage#851)
- **(argv)** render the usage line, byte-identical to usage-lib's by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;854](jdx/usage#854)
- **(argv)** render `-h`, byte-identical to usage-lib's by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;860](jdx/usage#860)
- **(argv)** render `--help` too, byte-identical to usage-lib's by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;866](jdx/usage#866)
- **(argv)** answer `--help` and `-h` by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;870](jdx/usage#870)
- **(argv)** answer the `help` subcommand by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;872](jdx/usage#872)
- **(argv)** split a command line the way the shell that typed it would by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;874](jdx/usage#874)
- **(argv)** read the cursor's position off a real parse by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;876](jdx/usage#876)
- **(argv)** offer what the reference offers, from compiled tables by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;877](jdx/usage#877)
- **(argv)** generate the shell script each shell wants by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;887](jdx/usage#887)
- **(argv)** let a Rust function answer for a value by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;888](jdx/usage#888)
- **(argv)** write the `run=` a declared completer answers by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;890](jdx/usage#890)
- **(argv)** say what went wrong the way clap says it by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;895](jdx/usage#895)
- **(argv)** suggest what was probably meant by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;897](jdx/usage#897)
- **(argv)** answer `--version`, which an adopter loses on the way from clap by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;909](jdx/usage#909)
- **(argv)** a flag whose value may be left off by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;969](jdx/usage#969)
- **(argv)** take flag-like detached values when declared by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1012](jdx/usage#1012)
- **(bench)** count what a parse allocates, and stop allocating for commands nobody ran by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;829](jdx/usage#829)
- **(cli)** hold a spec's declaration order, the way clap-sort holds a clap CLI's by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;915](jdx/usage#915)
- **(cli)** parse usage's own command line with the parser usage ships by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;965](jdx/usage#965)
- **(cli)** support long version text by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1120](jdx/usage#1120)
- **(cli)** check that examples still parse, and let the derive declare them by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1168](jdx/usage#1168)
- **(cli)** add usage explain by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1179](jdx/usage#1179)
- **(cli)** add usage diff for spec compatibility checking by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1171](jdx/usage#1171)
- **(complete)** complete config keys and values from the spec by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;840](jdx/usage#840)
- **(complete)** add async runtime overlays by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1060](jdx/usage#1060)
- **(complete)** support command value hints by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1081](jdx/usage#1081)
- **(complete)** add shell quoting filter by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1114](jdx/usage#1114)
- **(complete)** support full value hint vocabulary by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1119](jdx/usage#1119)
- **(complete)** expand partial path segments by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1128](jdx/usage#1128)
- **(complete)** support shell alias registration by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1158](jdx/usage#1158)
- **(complete)** **breaking** remove the vendored bash-completion copy by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1176](jdx/usage#1176)
- **(complete)** install a completion script where its shell looks for it by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1188](jdx/usage#1188)
- **(config)** read config files as a layer by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;856](jdx/usage#856)
- **(config)** explain why a setting has the value it has by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;857](jdx/usage#857)
- **(config)** read a resolution as the types a struct holds by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;862](jdx/usage#862)
- **(config)** generate the settings registry from the spec by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;864](jdx/usage#864)
- **(config)** generate the settings struct a CLI reads by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;865](jdx/usage#865)
- **(config)** hold a value to the choices its setting declares by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;868](jdx/usage#868)
- **(config)** carry a setting's choices into the generated registry by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;869](jdx/usage#869)
- **(config)** say what sort of thing each warning is by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;873](jdx/usage#873)
- **(config)** carry the flags a setting declares into its registry by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;880](jdx/usage#880)
- **(config)** read the command line as a layer by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;881](jdx/usage#881)
- **(config)** compare the flags a spec declares with the flags a CLI binds by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;884](jdx/usage#884)
- **(config)** support optional props and aliases by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1134](jdx/usage#1134)
- **(config)** read YAML config files by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1192](jdx/usage#1192)
- **(config)** ask for provenance by key, like a value by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1195](jdx/usage#1195)
- **(config)** a read that keeps every setting that reads by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1196](jdx/usage#1196)
- **(config)** close Config derive and spec authoring gaps by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1202](jdx/usage#1202)
- **(config)** gate deprecated settings by explicit CLI version by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1201](jdx/usage#1201)
- **(derive)** compile a struct into parse tables and a spec by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;803](jdx/usage#803)
- **(derive)** compile subcommands from an enum by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;816](jdx/usage#816)
- **(derive)** check what a parse cannot decide on its own by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;817](jdx/usage#817)
- **(derive)** nest commands to any depth by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;818](jdx/usage#818)
- **(derive)** declare which flags conflict and which require each other by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;820](jdx/usage#820)
- **(derive)** let a flag displace another, the last one given winning by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;821](jdx/usage#821)
- **(derive)** let a command answer to more than one name by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;827](jdx/usage#827)
- **(derive)** let a variant hold its command in a `Box` by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;828](jdx/usage#828)
- **(derive)** let a field be the type it means by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;833](jdx/usage#833)
- **(derive)** declare the words a value may be by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;838](jdx/usage#838)
- **(derive)** hold the bytes a word arrived as by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;841](jdx/usage#841)
- **(derive)** declare the properties mise patches in by hand by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;842](jdx/usage#842)
- **(derive)** accept a value the OS accepts and UTF-8 does not by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;844](jdx/usage#844)
- **(derive)** share declarations between commands with flatten by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;852](jdx/usage#852)
- **(derive)** say three things about a CLI the spec could and the derive could not by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;853](jdx/usage#853)
- **(derive)** answer a completion request from the binary itself by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;885](jdx/usage#885)
- **(derive)** bind a flag to a setting, from what the parser saw by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;889](jdx/usage#889)
- **(derive)** a setting can be declared wherever a flag is by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;896](jdx/usage#896)
- **(derive)** let a field name the function that completes it by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;892](jdx/usage#892)
- **(derive)** say how an argument relates to `--`, all four ways by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;900](jdx/usage#900)
- **(derive)** a default a collecting field can hold by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;902](jdx/usage#902)
- **(derive)** say what a command does to the world by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;905](jdx/usage#905)
- **(derive)** name a value the way clap names it, and say which usage can read the spec by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;907](jdx/usage#907)
- **(derive)** let `parse()` answer a failure the way a program does by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;910](jdx/usage#910)
- **(derive)** read the package's version, and be called what the binary is called by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;917](jdx/usage#917)
- **(derive)** a command that takes nothing can be written that way by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;923](jdx/usage#923)
- **(derive)** say that a command cannot be run alone, which it knew and did not write by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;937](jdx/usage#937)
- **(derive)** keep command aliases on their args by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;946](jdx/usage#946)
- **(derive)** preserve verbatim doc comments by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;949](jdx/usage#949)
- **(derive)** support path value hints by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;951](jdx/usage#951)
- **(derive)** declare a group where the flags are declared by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;934](jdx/usage#934)
- **(derive)** add value-conditional requirements by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1002](jdx/usage#1002)
- **(derive)** add skip for fields that are not arguments by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1009](jdx/usage#1009)
- **(derive)** support inline subcommand fields by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1055](jdx/usage#1055)
- **(derive)** accept runtime metadata expressions by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1056](jdx/usage#1056)
- **(derive)** accept clap value attributes by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1057](jdx/usage#1057)
- **(derive)** parse full argv with program name by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1063](jdx/usage#1063)
- **(derive)** support clap no binary name by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1064](jdx/usage#1064)
- **(derive)** support unit command structs by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1071](jdx/usage#1071)
- **(derive)** reuse args across commands by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1076](jdx/usage#1076)
- **(derive)** support runtime program identity by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1078](jdx/usage#1078)
- **(derive)** preserve value enum metadata by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1079](jdx/usage#1079)
- **(derive)** accept clap field spellings by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1086](jdx/usage#1086)
- **(derive)** preserve hidden flag aliases by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1087](jdx/usage#1087)
- **(derive)** resolve relationships through flatten by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1088](jdx/usage#1088)
- **(derive)** support flattened overrides by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1089](jdx/usage#1089)
- **(derive)** preserve flattened help headings by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1090](jdx/usage#1090)
- **(derive)** support clap casing policies by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1094](jdx/usage#1094)
- **(derive)** bind value enums directly by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1110](jdx/usage#1110)
- **(derive)** accept portable clap field spellings by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1135](jdx/usage#1135)
- **(derive)** inherit clap command metadata by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1136](jdx/usage#1136)
- **(derive)** support clap implicit groups by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1137](jdx/usage#1137)
- **(derive)** generate command dispatch by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1182](jdx/usage#1182)
- **(derive)** add usage::Config derive for settings declared in code by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1180](jdx/usage#1180)
- **(derive)** close remaining PLAN gaps for 6.x by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1197](jdx/usage#1197)
- **(docs)** support granular help visibility by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1107](jdx/usage#1107)
- **(docs)** customize subcommand presentation by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1108](jdx/usage#1108)
- **(docs)** color process-facing help by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1111](https://github.com/jdx/usage/pull/1111)
- **(docs)** support help width controls by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1113](https://github.com/jdx/usage/pull/1113)
- **(docs)** support next-line help layout by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1117](https://github.com/jdx/usage/pull/1117)
- **(docs)** support flattened subcommand help by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1118](https://github.com/jdx/usage/pull/1118)
- **(docs)** support explicit display order by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1121](https://github.com/jdx/usage/pull/1121)
- **(docs)** group subcommands under help headings by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1153](https://github.com/jdx/usage/pull/1153)
- **(docs)** add recursive help by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1132](https://github.com/jdx/usage/pull/1132)
- **(generate)** add json-schema for a CLI's config file by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;839](https://github.com/jdx/usage/pull/839)
- **(go)** emit Go parse tables from a spec, which is what Go has instead of a derive by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;931](https://github.com/jdx/usage/pull/931)
- **(go)** emit the cold table too, so generated code can apply the rules by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;959](https://github.com/jdx/usage/pull/959)
- **(go)** render the usage line, from a third table that costs nothing unused by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;964](https://github.com/jdx/usage/pull/964)
- **(go)** render a failure as something a person can act on by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;977](https://github.com/jdx/usage/pull/977)
- **(go)** generate a struct per command, and the Parse that fills them by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;990](https://github.com/jdx/usage/pull/990)
- **(go)** answer the completion request a shell sends by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1005](https://github.com/jdx/usage/pull/1005)
- **(go)** enforce value-conditional requirements by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1003](https://github.com/jdx/usage/pull/1003)
- **(help)** line the flag column up, and give the short page a column at all by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;912](https://github.com/jdx/usage/pull/912)
- **(help)** list the flags a command inherits by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;913](https://github.com/jdx/usage/pull/913)
- **(help)** list `--help` and `--version`, which every page answers by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;914](https://github.com/jdx/usage/pull/914)
- **(lib)** add usage-rs facade by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;963](https://github.com/jdx/usage/pull/963)
- **(lib)** ship usage-rs as the one-crate rust default by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1041](https://github.com/jdx/usage/pull/1041)
- **(parse)** support inferred prefixes by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1080](https://github.com/jdx/usage/pull/1080)
- **(parse)** support arg required else help by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1093](https://github.com/jdx/usage/pull/1093)
- **(parse)** add narrow token boundary controls by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1097](https://github.com/jdx/usage/pull/1097)
- **(parse)** preserve trailing delimiters by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1098](https://github.com/jdx/usage/pull/1098)
- **(parse)** add scalar repeat policy by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1102](https://github.com/jdx/usage/pull/1102)
- **(parse)** add subcommand requirement policy by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1103](https://github.com/jdx/usage/pull/1103)
- **(parse)** add argument subcommand conflicts by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1104](https://github.com/jdx/usage/pull/1104)
- **(parse)** add subcommand value precedence by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1105](https://github.com/jdx/usage/pull/1105)
- **(parse)** support missing optional positionals by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1106](https://github.com/jdx/usage/pull/1106)
- **(parse)** support optional flag values by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1109](https://github.com/jdx/usage/pull/1109)
- **(parse)** support custom help and version actions by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1123](https://github.com/jdx/usage/pull/1123)
- **(parse)** accept explicit boolean values by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1124](https://github.com/jdx/usage/pull/1124)
- **(parse)** support non-strict choices by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1127](https://github.com/jdx/usage/pull/1127)
- **(parse)** support ordered environment fallbacks by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1130](https://github.com/jdx/usage/pull/1130)
- **(parse)** warn at runtime when a deprecated declaration is used by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1186](https://github.com/jdx/usage/pull/1186)
- **(spec)** support flag relationships by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;793](https://github.com/jdx/usage/pull/793)
- **(spec)** add help\_heading, and render it by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;802](https://github.com/jdx/usage/pull/802)
- **(spec)** allow a mount at the top level by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;806](https://github.com/jdx/usage/pull/806)
- **(spec)** make unknown flags configurable, and keep them as values by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;810](https://github.com/jdx/usage/pull/810)
- **(spec)** add `conflicts` to flags by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;819](https://github.com/jdx/usage/pull/819)
- **(spec)** say that one flag needs another, which nothing here could by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;925](https://github.com/jdx/usage/pull/925)
- **(spec)** **breaking** a group, for the rule that no single flag can state by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;927](https://github.com/jdx/usage/pull/927)
- **(spec)** a flag that has to be given on its own by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;941](https://github.com/jdx/usage/pull/941)
- **(spec)** split a value the way clap splits one by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;961](https://github.com/jdx/usage/pull/961)
- **(spec)** add value-conditional requirements by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1001](https://github.com/jdx/usage/pull/1001)
- **(spec)** refuse a detached value when require\_equals is set by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1013](https://github.com/jdx/usage/pull/1013)
- **(spec)** bind a value when a flag is given with none by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1015](https://github.com/jdx/usage/pull/1015)
- **(spec)** forward unmatched words as an external subcommand by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1021](https://github.com/jdx/usage/pull/1021)
- **(spec)** bind a default when another flag is given by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1023](https://github.com/jdx/usage/pull/1023)
- **(spec)** add portable expression validation by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1037](https://github.com/jdx/usage/pull/1037)
- **(spec)** add borrowed metadata overlays by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1059](https://github.com/jdx/usage/pull/1059)
- **(spec)** omit versions from metadata views by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1066](https://github.com/jdx/usage/pull/1066)
- **(spec)** support positional conflicts and groups by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1085](https://github.com/jdx/usage/pull/1085)
- **(spec)** add fixed arity value names by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1099](https://github.com/jdx/usage/pull/1099)
- **(spec)** complete relationship families by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1100](https://github.com/jdx/usage/pull/1100)
- **(spec)** expose package metadata by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1116](https://github.com/jdx/usage/pull/1116)
- **(spec)** add deprecation milestones by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1129](https://github.com/jdx/usage/pull/1129)
- **(spec)** add executable views by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1143](https://github.com/jdx/usage/pull/1143)
- **(spec)** add deprecated config environment aliases by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1159](https://github.com/jdx/usage/pull/1159)
- **(spec)** declare source\_code\_link\_template on the derive by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1184](https://github.com/jdx/usage/pull/1184)
- **(spec)** answer **usage\_spec** from a binary's own tables by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1183](https://github.com/jdx/usage/pull/1183)
- **(spec)** reusable flag declarations with flagset and use by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1170](https://github.com/jdx/usage/pull/1170)
- **(spec)** **breaking** lower the derive's flatten into a flagset by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1172](https://github.com/jdx/usage/pull/1172)
- **(test)** a test harness for an adopter's own suite by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1181](https://github.com/jdx/usage/pull/1181)

##### 🐛 Bug Fixes

- **(argv)** stop a repeatable flag from eating a positional by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;799](https://github.com/jdx/usage/pull/799)
- **(argv)** inherit `unknown_flags`, which reached one command out of a tree by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;939](https://github.com/jdx/usage/pull/939)
- **(argv)** reject duplicate flags by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;945](https://github.com/jdx/usage/pull/945)
- **(argv)** show choices when a subcommand is required by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;947](https://github.com/jdx/usage/pull/947)
- **(argv)** a bare `-` binds where it was typed by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;986](https://github.com/jdx/usage/pull/986)
- **(argv)** put zsh's magic comment first, and print fish's candidates as data by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1033](https://github.com/jdx/usage/pull/1033)
- **(ci)** unblock releases by cutting usage-derive's dev-dependency by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;811](https://github.com/jdx/usage/pull/811)
- **(ci)** check the version the crates promise, and promise one that is true by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;918](https://github.com/jdx/usage/pull/918)
- **(clap)** say what clap would do with an unknown flag by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;899](https://github.com/jdx/usage/pull/899)
- **(cli)** recognize about as root command help by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;794](https://github.com/jdx/usage/pull/794)
- **(complete)** resolve config keys through aliases and renames by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1169](https://github.com/jdx/usage/pull/1169)
- **(config)** accept case-insensitive boolean words by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1207](https://github.com/jdx/usage/pull/1207)
- **(derive)** let a `--`-only argument follow a variadic by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;823](https://github.com/jdx/usage/pull/823)
- **(derive)** three more descriptions a spec keeps and the derive lost by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;861](https://github.com/jdx/usage/pull/861)
- **(derive)** name the mistake when `settings` has nothing to collect by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;904](https://github.com/jdx/usage/pull/904)
- **(derive)** emit the tables beside the user's types, not in a module above them by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;938](https://github.com/jdx/usage/pull/938)
- **(derive)** a global flag may be given once per command, not once per line by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;991](https://github.com/jdx/usage/pull/991)
- **(derive)** separate value metadata from parsing by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1054](https://github.com/jdx/usage/pull/1054)
- **(derive)** make defaulted fields optional in metadata by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1065](https://github.com/jdx/usage/pull/1065)
- **(derive)** isolate process exit from adopters by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1139](https://github.com/jdx/usage/pull/1139)
- **(derive)** propagate redeclared global values by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1140](https://github.com/jdx/usage/pull/1140)
- **(derive)** preserve set-false actions by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1156](https://github.com/jdx/usage/pull/1156)
- **(derive)** name the count type in standing presence checks by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1205](https://github.com/jdx/usage/pull/1205)
- **(docs)** link multi-word commands to their real source files by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;845](https://github.com/jdx/usage/pull/845)
- **(docs)** link every command to the file that implements it by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;846](https://github.com/jdx/usage/pull/846)
- **(docs)** keep hidden entries out of help by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;859](https://github.com/jdx/usage/pull/859)
- **(docs)** list visible flag aliases by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1112](https://github.com/jdx/usage/pull/1112)
- **(help)** a command's page should say what that command does by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;911](https://github.com/jdx/usage/pull/911)
- **(help)** a declared name is not a short form, and blank help is no help by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;916](https://github.com/jdx/usage/pull/916)
- **(help)** render the page for the mount the words reached by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;928](https://github.com/jdx/usage/pull/928)
- **(help)** a description ending in a break adds no blank line by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;970](https://github.com/jdx/usage/pull/970)
- **(lib)** validate every variadic fallback by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1049](https://github.com/jdx/usage/pull/1049)
- **(parse)** keep every `--` after the first by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;809](https://github.com/jdx/usage/pull/809)
- **(parse)** stop losing a flag that is missing its value by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;807](https://github.com/jdx/usage/pull/807)
- **(parse)** answer the five vectors the reference implementation was failing by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;930](https://github.com/jdx/usage/pull/930)
- **(parse)** **breaking** a command that needs a subcommand says so by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;992](https://github.com/jdx/usage/pull/992)
- **(parse)** keep optional validation lint-clean by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1141](https://github.com/jdx/usage/pull/1141)
- **(parse)** honor separator after automatic args by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1164](https://github.com/jdx/usage/pull/1164)
- **(parse)** let a bundle contain a supplied short by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1175](https://github.com/jdx/usage/pull/1175)
- **(spec)** make the config block survive being written out by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;832](https://github.com/jdx/usage/pull/832)
- **(spec)** apply default\_subcommand only at the root by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;850](https://github.com/jdx/usage/pull/850)
- **(spec)** split a clap default by the delimiter clap splits it by by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;901](https://github.com/jdx/usage/pull/901)
- **(spec)** rank a subcommand name above another command's alias by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;967](https://github.com/jdx/usage/pull/967)
- **(spec)** preserve clap value count bounds by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1032](https://github.com/jdx/usage/pull/1032)
- **(spec)** deduplicate derived completers by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1072](https://github.com/jdx/usage/pull/1072)
- **(spec)** canonicalize derived kdl by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1095](https://github.com/jdx/usage/pull/1095)

##### 🚜 Refactor

- **(deps)** **breaking** stop shipping features and crates nobody uses by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1185](https://github.com/jdx/usage/pull/1185)
- **(deps)** drop heck from usage-derive by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1187](https://github.com/jdx/usage/pull/1187)
- **(deps)** take expr-lang without the builtins a spec cannot reach by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1191](https://github.com/jdx/usage/pull/1191)

##### 📚 Documentation

- **(plan)** tick landed clap gaps and stop quoting vector counts by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1027](https://github.com/jdx/usage/pull/1027)
- correct current Rust limitations by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1029](https://github.com/jdx/usage/pull/1029)
- audit 6.x release documentation by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1084](https://github.com/jdx/usage/pull/1084)
- add third-party license notices by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1174](https://github.com/jdx/usage/pull/1174)

##### ⚡ Performance

- **(derive)** fill the partial through \&mut instead of returning it by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;980](https://github.com/jdx/usage/pull/980)
- **(derive)** hold one subcommand's partial, not every subcommand's by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;981](https://github.com/jdx/usage/pull/981)
- **(derive)** drop proc-macro-crate transitive deps by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1042](https://github.com/jdx/usage/pull/1042)

##### 🧪 Testing

- **(clap)** preserve choices in external adopter probes by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1157](https://github.com/jdx/usage/pull/1157)
- **(corpus)** pin what completes where the cursor is by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;998](https://github.com/jdx/usage/pull/998)
- **(derive)** cover verbatim doc compatibility by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1092](https://github.com/jdx/usage/pull/1092)
- **(docs)** preserve fleet footer spacing by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1142](https://github.com/jdx/usage/pull/1142)
- **(fleet)** refresh typed adopter fixtures by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1115](https://github.com/jdx/usage/pull/1115)
- **(parse)** cover mounted command discovery by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1131](https://github.com/jdx/usage/pull/1131)
- **(parse)** add clap micro-conformance by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1133](https://github.com/jdx/usage/pull/1133)
- **(spec)** import the argv questions clap's suite answers and ours did not by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;926](https://github.com/jdx/usage/pull/926)
- **(spec)** verify portable parser settings by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1053](https://github.com/jdx/usage/pull/1053)

##### 🛡️ Security

- **(config)** resolve settings from layers, with provenance by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;849](https://github.com/jdx/usage/pull/849)
- **(config)** read the environment as a layer by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;867](https://github.com/jdx/usage/pull/867)
- **(config)** give a deprecation notice from anywhere along a rename chain by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;893](https://github.com/jdx/usage/pull/893)
- **(derive)** keep parsed fields live for lints by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1138](https://github.com/jdx/usage/pull/1138)
- **(docs)** render the config block by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;837](https://github.com/jdx/usage/pull/837)
- **(go)** render the page `-h` prints, matching usage-lib on all 211 of mise's by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;974](https://github.com/jdx/usage/pull/974)
- **(go)** render `--help` too, matching usage-lib on all 211 of mise's long pages by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;975](https://github.com/jdx/usage/pull/975)
- **(parse)** require exact command and flag names by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1096](https://github.com/jdx/usage/pull/1096)
- **(spec)** the config vocabulary by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;835](https://github.com/jdx/usage/pull/835)

##### 🔍 Other Changes

- **(docs)** remove stale mise spec fixture by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;1200](https://github.com/jdx/usage/pull/1200)
- **(perf)** say when the clap ratio slides, and record why the derive is stricter by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;996](https://github.com/jdx/usage/pull/996)
- agent/complete files by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;883](https://github.com/jdx/usage/pull/883)

##### 📦️ Dependency Updates

- update rust crate syn to v3 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;808](https://github.com/jdx/usage/pull/808)
- update rust crate toml to v1 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;1016](https://github.com/jdx/usage/pull/1016)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODguMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4OC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiLCJhdXRvbWF0aW9uOmJvdC1hdXRob3JlZCIsImRlcGVuZGVuY3ktdHlwZTo6bWFqb3IiXX0=-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant