Skip to content

feat(cli): parse usage's own command line with the parser usage ships - #965

Merged
jdx merged 13 commits into
agent/usage-rs-facade-basefrom
agent/usage-cli-facade
Aug 17, 2026
Merged

feat(cli): parse usage's own command line with the parser usage ships#965
jdx merged 13 commits into
agent/usage-rs-facade-basefrom
agent/usage-cli-facade

Conversation

@jdx

@jdx jdx commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Stacked on #963, the usage-rs facade.

usage is now its own first adopter. Its command structs, root, and command enums use the usage-rs facade instead of Clap, and --usage-spec prints Cli::to_kdl() from the same tables that parsed the command line.

What goes away

  • clap, clap_usage, and the clap-sort dev dependency
  • tests/clap_sort.rs; declaration order is held by the spec
  • the duplicated command-effect tables; effects now live on the commands they describe
  • the empty Sponsors struct; unit subcommands are supported directly
  • runtime checks for relationships the spec can express

Migration shape

The declarations remain close to their former Clap layout:

  • inferred shorts use #[usage(short, long)]
  • command aliases live on their command structs, e.g. #[usage(alias = "c", alias_hidden("complete", "completions"))]
  • positive dependencies use requires, including --cache-key--usage-cmd and --out-dir--multi
  • aliases written on enum variants still merge with struct aliases for compatibility
  • deliberately formatted docs keep #[usage(verbatim_doc_comment)]
  • path hints keep value_hint = usage_rs::ValueHint::FilePath or DirPath from usage’s own runtime type
  • subcommand payloads stay unboxed as they were under Clap; Box<T> remains an optional size optimization

The 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

The emitted spec still intentionally gains metadata the Clap bridge dropped, including JDX_USAGE_BIN on --usage-bin, preserved multiline long help, the manpage description, and the correct binary name usage.

Verification

  • mise run render
  • cargo test --all --all-features
  • cargo clippy --all --all-features --all-targets -- -D warnings
  • direct binary checks for no-argument help, duplicate flags, command aliases, and positive requirements

This PR was generated by Codex.


Note

Medium Risk
Changes how the primary usage CLI is parsed and documented (including --completions UX), but behavior is covered by updated integration tests and is an intentional migration to the shipped parser.

Overview
The usage binary dogfoods usage-rs: command structs use #[derive(usage_rs::Cli)] / #[usage(...)] instead of Clap, and --usage-spec emits Cli::to_kdl() from the same parse tables that handle argv. Clap, clap_usage, and clap-sort are removed, along with the post-hoc command_effects patch tableeffect, 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-line usage synopsis for usage <COMMAND> / --completions / --usage-spec. Shell runner commands (bash, fish, zsh, powershell, exec) use unknown_flags = "value" so script arguments can include unknown flags.

Help and manpages gain support for an optional root usage string on Spec (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

    • Added --completions <SHELL> for selecting a completion shell.
    • Improved command validation, aliases, defaults, environment settings, and file-output completion hints.
    • Shell commands now forward unrecognized arguments as script values.
    • Added clearer help text and descriptions for generation commands.
  • Documentation

    • Updated CLI references, manpages, usage specifications, and shell documentation to reflect revised options and behavior.

Replaces #936 after flipping the facade and CLI layers.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a38dfe0-5a59-4d16-a52e-7aeaabaa76c6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@jdx jdx changed the title feat(cli): parse usage own command line with the parser it ships feat(cli): parse usage's own command line with the parser usage ships Aug 17, 2026
@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR migrates the usage binary from Clap to its own usage-rs parser and generates its self-description from the same metadata used for parsing.

  • Replaces Clap-derived CLI declarations and dispatch with usage-rs derives.
  • Adds explicit root synopsis metadata and propagates it through KDL, help, and manpage rendering.
  • Moves command effects into command declarations and refreshes generated specifications, documentation, and completion assets.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
cli/src/cli/mod.rs Migrates root parsing, diagnostics, help/version handling, and command dispatch to the shipped parser.
argv/src/spec.rs Adds explicit synopsis metadata and serializes it safely into generated KDL.
argv/src/help.rs Uses the explicit synopsis for root help while preserving generated usage lines for subcommands.
conformance/tests/spec_roundtrip.rs Adds exact round-trip coverage for a multiline explicit synopsis containing quoted text.
lib/src/docs/manpage/renderer.rs Incorporates explicit root synopsis alternatives into generated manpages.

Reviews (2): Last reviewed commit: "fix(help): honor explicit usage synopsis" | Re-trigger Greptile

Comment thread conformance/tests/spec_roundtrip.rs Outdated

@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 47d85ed. Configure here.

Comment thread cli/src/cli/mod.rs
jdx and others added 13 commits August 17, 2026 12:17
`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>
@jdx
jdx force-pushed the agent/usage-cli-facade branch from 47d85ed to 3aba1aa Compare August 17, 2026 12:21
@jdx
jdx merged commit ab46ba0 into main Aug 17, 2026
9 of 10 checks passed
@jdx
jdx deleted the agent/usage-cli-facade branch August 17, 2026 12:28
jdx added a commit that referenced this pull request Aug 17, 2026
## 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 -->
jdx added a commit that referenced this pull request Aug 17, 2026
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>
jdx added a commit that referenced this pull request Aug 17, 2026
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>
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