feat(cli): parse usage's own command line with the parser usage ships - #965
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Central YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
Greptile SummaryThe PR migrates the
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Reviews (2): Last reviewed commit: "fix(help): honor explicit usage synopsis" | Re-trigger Greptile |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 47d85ed. Configure here.
`usage` is now its own first adopter. The ten command structs, the root and the two command enums are declared with `usage-derive` instead of clap, and `--usage-spec` prints `Cli::to_kdl()` — the same tables that parsed the command line, rather than a transcription of a clap `Command` through `clap_usage`. Smaller than mise and far less forgiving of a lossy spec, because this CLI's spec is what generates its own docs, manpage and completions: anything the derive cannot say shows up in the checked-in output. - `clap`, `clap_usage` and the `clap-sort` dev-dependency, plus `tests/clap_sort.rs` — declaration order is held by the spec since #915. - `command_effects.rs`'s two tables, 60 lines that existed because "clap has no way to express this". Each command declares `#[usage(effect = "…")]` where it is defined; the file keeps `UNCLASSIFIED` and the coverage tests, which now read the derived metadata. A stale entry is no longer possible for the effects themselves — an effect moves with the command it is written on. - The four shell commands shared one `Shell` struct, which the derive refuses: a command collects into the struct that declares it. They are four structs flattening a shared group now, written by a macro so the paragraph of long help is not copied four times. Their docs improve as a side effect — all four used to say "Execute a shell script with the specified shell". - `sponsors` is a bare variant (#923), so its empty struct is gone. - `requires` has no positive form in the spec, so the two constraints that used it are stated as `required_if` on the other flag. `--out-dir requires --multi` is a positive requirement on a `bool` and has no spelling at all; #925 adds `requires`, and it belongs here when it lands. Gains `JDX_USAGE_BIN` on `--usage-bin` (the bridge dropped `env`), long help that keeps its line breaks, an `about` for `generate manpage`, and `name "usage"` rather than `name "usage-cli"`. Loses `subcommand_required`, which the spec can hold and the derive knows from a bare `T` subcommand field but does not emit, and strictness on subcommands: `unknown_flags` is accepted on an `Args` and ignored, and the root's is not inherited, so only the root is strict. Both are derive gaps worth their own fix rather than a workaround here. Workspace suite green, clippy clean, docs and assets re-rendered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ccepts Follow-up to the conversion, now stacked on the two gaps it found rather than describing them as open. `unknown_flags` reaches the whole tree. The root declares `error` once and every subcommand inherits it, so `usage lint --nope f.kdl` names the flag again instead of making `--nope` the file and calling the real file unexpected. The five commands that hand a command line to somebody else's script declare `value` for themselves, which is the half that needs a subcommand to be able to say something. ## From review `--version` says `usage 5.1.0`, not `usage-cli 5.1.0`. The crate is `usage-cli` and the binary is `usage`; everything else this CLI says about itself now comes from the spec, where the name is `usage`, so the version line was contradicting the help banner printed directly above it. Read from the spec by `version()`, which `-v` shares, so the two cannot drift. `--out-dir` without `--multi` is refused. clap said this as a `requires` in both directions; the spec can only state the direction that makes `--multi` need a destination, so without a check the other way `usage g markdown --out-dir docs` quietly wrote a single file somewhere else. Enforced in `run()` until #925's `requires` can carry it. `--completions` is a flag, which is how it is typed. The clap declaration made it a *positional*, so the spec, the docs, the manpage and the generated completions all described a `[COMPLETIONS]` argument that nothing accepts, while the flag that does work went undocumented. The conversion carried that over faithfully, wrong included. The point of emitting the spec from the declaration is that the two cannot disagree, so the declaration is what changes. Declined one: CodeRabbit read the `man` alias on `generate manpage` as removed. It is declared, in the emitted spec, and on the rendered page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
47d85ed to
3aba1aa
Compare
## Summary
- add `usage-rs`, the single dependency for compiled usage CLIs
- re-export the four derives, the argv runtime, and `ValueHint` from the
facade
- forward cold-path `spec`, `help`, `completions`, and `diagnostics`
features
- make generated derive paths resolve either `usage-rs` or a
direct/renamed `usage-argv` dependency
- retain direct `usage-argv` compatibility for low-level adopters
Applications can choose the short public crate name in Cargo:
```toml
[dependencies]
usage = { package = "usage-rs", version = "5.1" }
```
and then keep the declaration on one API:
```rust
use usage::Cli;
#[derive(Cli)]
struct Cli {
#[usage(long, value_hint = usage::ValueHint::FilePath)]
file: Option<std::path::PathBuf>,
}
```
## Why
Depending on `usage-derive` and `usage-argv` separately exposes an
implementation split and allows their versions or features to drift. The
facade gives adopters one versioned package while leaving the
zero-allocation runtime independently usable.
The facade crate target remains `usage_rs` to avoid colliding with the
existing `usage-lib` target inside this workspace. The documented Cargo
alias provides the intended `usage::…` source spelling.
## Stack
Base layer for #965, which migrates `usage-cli` to this facade.
## Checks
- `cargo test --all --all-features`
- `cargo clippy --all --all-features --all-targets -- -D warnings`
- `cargo +1.91.0 check -p usage-argv -p usage-derive -p usage-rs
--all-features`
- `cargo test -p usage-rs --no-default-features`
- external path-dependency check using the documented `usage = { package
= "usage-rs", … }` alias
_This PR was generated by Codex._
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Macro-generated path resolution affects every derived CLI; behavior is
covered by new fixtures, but a wrong `crate_name` resolution could break
adopters at compile time across the ecosystem.
>
> **Overview**
> Introduces **`usage-rs`**, a workspace crate meant to be depended on
as `usage = { package = "usage-rs", … }`. It re-exports
**`usage-argv`**, optional **`usage-derive`** macros behind the `spec`
feature, and forwards feature flags for help, completions, and
diagnostics so adopters get one versioned package instead of splitting
derive and runtime.
>
> **`usage-derive`** now uses **`proc-macro-crate`** to pick emitted
paths at compile time: a direct **`usage-argv`** dependency wins over
the facade (for mixed/low-level setups); otherwise generated code goes
through **`usage-rs::argv`** and the facade’s derive re-exports. Emitted
tables import the chosen runtime as `usage_argv` locally, and unit
subcommands use the resolved derive path instead of hard-coded
`::usage_derive`.
>
> Docs and model hints shift public examples from
`usage_argv::ValueHint` to **`usage::ValueHint`**. CI MSRV matrix
includes **`usage-rs`** at Rust 1.91; integration tests cover the Cargo
alias fixture and mixed direct + facade dependencies.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8fe3c3c. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Replaces #953 after flipping the stack so the CLI can consume the
facade.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added the `usage-rs` facade crate, providing a unified API for CLI
parsing and derive macros.
* Added support for runtime-selected parsing, completions, settings,
metadata, validation, and subcommands.
* Improved file-path handling with clearer errors for invalid UTF-8 and
platform conversion failures.
* **Bug Fixes**
* Updated generated APIs and examples to use the unified `usage`
interface consistently.
* **Tests**
* Added coverage for argument parsing, subcommands, value hints, and
generated metadata.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Rebased onto main, which added `Spec::usage` in #965 and broke this file — the literals are exhaustive, so a new field is a build error until somebody says what the spec puts there. Kept that way, and said so in the module docs: this is a mirror, and one that quietly defaults a field describes a CLI the spec did not declare. Carrying `usage` took one line and turned up a rule the two implementations disagree about that nothing had recorded. `usage` is an exact synopsis a spec declares, replacing the generated line on the root's page. usage-argv honours it, which is what #965 added it for; usage-lib honours it in the manpage renderer and *not* in the help renderer. So `an-explicit-synopsis-replaces-the-root-line` expects usage-argv's page and carries the corpus's first `reference` divergence, with the note pointing at the file that would need to change. Fixing usage-lib is not this PR's business — recording it is exactly what the label is for, and the two-way check means whoever fixes it is told to delete the label. `min_usage_version` was being dropped too. Nothing renders it, so nothing caught it; carried now for the same reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rebased onto main, which added `Spec::usage` in #965 and broke this file — the literals are exhaustive, so a new field is a build error until somebody says what the spec puts there. Kept that way, and said so in the module docs: this is a mirror, and one that quietly defaults a field describes a CLI the spec did not declare. Carrying `usage` took one line and turned up a rule the two implementations disagree about that nothing had recorded. `usage` is an exact synopsis a spec declares, replacing the generated line on the root's page. usage-argv honours it, which is what #965 added it for; usage-lib honours it in the manpage renderer and *not* in the help renderer. So `an-explicit-synopsis-replaces-the-root-line` expects usage-argv's page and carries the corpus's first `reference` divergence, with the note pointing at the file that would need to change. Fixing usage-lib is not this PR's business — recording it is exactly what the label is for, and the two-way check means whoever fixes it is told to delete the label. `min_usage_version` was being dropped too. Nothing renders it, so nothing caught it; carried now for the same reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Stacked on #963, the
usage-rsfacade.usageis now its own first adopter. Its command structs, root, and command enums use theusage-rsfacade instead of Clap, and--usage-specprintsCli::to_kdl()from the same tables that parsed the command line.What goes away
clap,clap_usage, and theclap-sortdev dependencytests/clap_sort.rs; declaration order is held by the specSponsorsstruct; unit subcommands are supported directlyMigration shape
The declarations remain close to their former Clap layout:
#[usage(short, long)]#[usage(alias = "c", alias_hidden("complete", "completions"))]requires, including--cache-key→--usage-cmdand--out-dir→--multi#[usage(verbatim_doc_comment)]value_hint = usage_rs::ValueHint::FilePathorDirPathfrom usage’s own runtime typeBox<T>remains an optional size optimizationThe four shell commands are separate derived structs flattening a shared group. A single destination struct cannot identify which enum variant selected it, so a macro keeps their common declaration in one place.
Parity gaps closed by the stack
unknown_flags, which reached one command out of a tree #939)The emitted spec still intentionally gains metadata the Clap bridge dropped, including
JDX_USAGE_BINon--usage-bin, preserved multiline long help, the manpage description, and the correct binary nameusage.Verification
mise run rendercargo test --all --all-featurescargo clippy --all --all-features --all-targets -- -D warningsThis PR was generated by Codex.
Note
Medium Risk
Changes how the primary
usageCLI is parsed and documented (including--completionsUX), but behavior is covered by updated integration tests and is an intentional migration to the shipped parser.Overview
The
usagebinary dogfoodsusage-rs: command structs use#[derive(usage_rs::Cli)]/#[usage(...)]instead of Clap, and--usage-specemitsCli::to_kdl()from the same parse tables that handle argv. Clap,clap_usage, andclap-sortare removed, along with the post-hoccommand_effectspatch table—effect,requires,overrides, and similar metadata now live on the declarations.Root CLI shape is corrected in spec and docs:
--completions <SHELL>is a long flag (not a positional), with an explicit multi-lineusagesynopsis forusage <COMMAND>/--completions/--usage-spec. Shell runner commands (bash,fish,zsh,powershell,exec) useunknown_flags = "value"so script arguments can include unknown flags.Help and manpages gain support for an optional root
usagestring onSpec(derive + KDL emission); root help/man synopsis prefer that over generated lines. Generated assets (usage.usage.kdl,usage.1, Fig spec, CLI reference markdown, snapshots) are refreshed to match.Reviewed by Cursor Bugbot for commit 3aba1aa. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
--completions <SHELL>for selecting a completion shell.Documentation
Replaces #936 after flipping the facade and CLI layers.