From eaba23774eb7449bc493d9af8b404cd9abdb8d91 Mon Sep 17 00:00:00 2001 From: default <216188+jdx@users.noreply.github.com> Date: Mon, 17 Aug 2026 00:01:00 +0000 Subject: [PATCH 01/13] feat(cli): parse usage's own command line with the parser usage ships MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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; jdx/usage#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 --- Cargo.lock | 16 +- cli/Cargo.toml | 9 +- cli/assets/fig.ts | 1 + cli/assets/usage.1 | 78 ++-- cli/src/cli/complete_word.rs | 16 +- cli/src/cli/exec.rs | 13 +- cli/src/cli/generate/completion.rs | 18 +- cli/src/cli/generate/completion_init.rs | 8 +- cli/src/cli/generate/fig.rs | 10 +- cli/src/cli/generate/json.rs | 8 +- cli/src/cli/generate/json_schema.rs | 14 +- cli/src/cli/generate/manpage.rs | 11 +- cli/src/cli/generate/markdown.rs | 25 +- cli/src/cli/generate/mod.rs | 36 +- cli/src/cli/generate/sdk.rs | 18 +- cli/src/cli/lint.rs | 12 +- cli/src/cli/mcp.rs | 8 +- cli/src/cli/mod.rs | 115 ++++-- cli/src/cli/shell.rs | 58 ++- cli/src/cli/sponsors.rs | 20 +- cli/src/command_effects.rs | 194 +++------ cli/src/lib.rs | 3 + cli/src/usage_spec.rs | 19 +- cli/tests/clap_sort.rs | 7 - cli/tests/shell_completions_integration.rs | 8 +- cli/usage.usage.kdl | 372 +++++++----------- docs/cli/reference/bash.md | 9 +- docs/cli/reference/commands.json | 86 ++-- docs/cli/reference/complete-word.md | 3 +- docs/cli/reference/fish.md | 9 +- .../cli/reference/generate/completion-init.md | 6 +- docs/cli/reference/generate/completion.md | 6 +- docs/cli/reference/generate/manpage.md | 9 +- docs/cli/reference/lint.md | 4 +- docs/cli/reference/powershell.md | 9 +- docs/cli/reference/zsh.md | 9 +- 36 files changed, 647 insertions(+), 600 deletions(-) delete mode 100644 cli/tests/clap_sort.rs diff --git a/Cargo.lock b/Cargo.lock index e9f3b17f3..1172b5a4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -290,15 +290,6 @@ dependencies = [ "clap_derive", ] -[[package]] -name = "clap-sort" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9f374a541bd277ba6f4ccd08d955024ba09fda8dfc69ca1a750799ebed97a9" -dependencies = [ - "clap", -] - [[package]] name = "clap_builder" version = "4.6.6" @@ -2089,9 +2080,6 @@ name = "usage-cli" version = "5.1.0" dependencies = [ "assert_cmd", - "clap", - "clap-sort", - "clap_usage", "ctor", "env_logger", "exec", @@ -2111,6 +2099,8 @@ dependencies = [ "tera", "thiserror", "tokio", + "usage-argv", + "usage-derive", "usage-lib", "xx", ] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index bb414550b..ecff73933 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -26,8 +26,6 @@ name = "usage_cli" path = "src/lib.rs" [dependencies] -clap = { version = "4", features = ["derive", "string", "env"] } -clap_usage = { workspace = true } env_logger = "0.11" indexmap = "2" itertools = "0.15" @@ -43,6 +41,12 @@ serde_with = "3" tera = "2" thiserror = "2" tokio = { version = "1", features = ["rt", "macros", "io-std"] } +# The CLI is its own first adopter: `usage` parses its own command line with the +# parser it ships. `spec` because `--usage-spec` emits this CLI's own KDL, and +# `diagnostics` because a person types these commands and has to be told what +# went wrong. +usage-argv = { workspace = true, features = ["spec", "diagnostics"] } +usage-derive = { workspace = true } usage-lib = { workspace = true, features = ["clap", "docs", "unstable_choices_env"] } xx = "2" @@ -51,7 +55,6 @@ exec = "0.3" [dev-dependencies] assert_cmd = { version = "2", features = ["color-auto"] } -clap-sort = "1" ctor = "1" insta = "1" predicates = "3" diff --git a/cli/assets/fig.ts b/cli/assets/fig.ts index c2b857f02..8f426aeb9 100644 --- a/cli/assets/fig.ts +++ b/cli/assets/fig.ts @@ -460,6 +460,7 @@ const completionSpec: Fig.Spec = { }, { name: ["manpage", "man"], + description: "Generate a manpage from a usage spec", options: [ { name: ["-f", "--file"], diff --git a/cli/assets/usage.1 b/cli/assets/usage.1 index 5948c8c75..d1f080954 100644 --- a/cli/assets/usage.1 +++ b/cli/assets/usage.1 @@ -1,6 +1,6 @@ -.TH USAGE-CLI 1 +.TH USAGE 1 .SH NAME -usage\-cli \- CLI for working with usage\-based CLIs +usage \- CLI for working with usage\-based CLIs .SH SYNOPSIS \fBusage\fR [OPTIONS] [] [COMMAND] .SH DESCRIPTION @@ -17,7 +17,7 @@ Outputs completions for the specified shell for completing the `usage` CLI itsel .SH COMMANDS .TP \fBbash\fR -Execute a shell script with the specified shell +Execute a shell script using bash .TP \fBcomplete\-word\fR Generate shell completion candidates for a partial command line @@ -32,7 +32,7 @@ Execute a script, parsing args and exposing them as environment variables .RE .TP \fBfish\fR -Execute a shell script with the specified shell +Execute a shell script using fish .TP \fBgenerate\fR Generate completions, documentation, and other artifacts from usage specs @@ -65,6 +65,7 @@ Outputs a usage spec in json format Generate a JSON Schema for a CLI's config file from its usage spec .TP \fBgenerate manpage\fR +Generate a manpage from a usage spec .RS \fIAliases: \fRman .RE @@ -88,20 +89,20 @@ Serve a usage spec over the Model Context Protocol .RE .TP \fBpowershell\fR -Execute a shell script with the specified shell +Execute a shell script using PowerShell .TP \fBsponsors\fR Show the companies sponsoring usage and the jdx.dev open source tools .TP \fBzsh\fR -Execute a shell script with the specified shell +Execute a shell script using zsh .SH "USAGE BASH" -Execute a shell script with the specified shell +Execute a shell script using bash Typically, this will be called by a script's shebang. -If using `var=#true` on args/flags, they will be joined with spaces using `shell_words::join()` -to properly escape and quote values with spaces in them. +If using `var=#true` on args/flags, they will be joined with spaces using +`shell_words::join()` to properly escape and quote values with spaces in them. .PP \fBUsage:\fR usage bash [OPTIONS]