Skip to content

feat(argv): offer what the reference offers, from compiled tables - #877

Merged
jdx merged 1 commit into
agent/complete-walkfrom
agent/complete-candidates
Aug 14, 2026
Merged

feat(argv): offer what the reference offers, from compiled tables#877
jdx merged 1 commit into
agent/complete-walkfrom
agent/complete-candidates

Conversation

@jdx

@jdxjdx commented Aug 14, 2026

Copy link
Copy Markdown
Owner

The candidates for a cursor: the flags a word there could name, the subcommands it
could select, and the choices a flag or positional declares. The rules are
usage-lib's, because a CLI's completions should not change with the
implementation that answers them — a lone - offers both forms, --… offers
longs, -… narrows to one letter, a flag waiting for its value offers that flag's
choices and nothing else.

Held to the reference over mise's real spec: of eighteen lines across the tree,
thirteen agree exactly, including sets of forty and twenty-one candidates. The
five that do not are named in the test rather than dropped from it — two need
stages that do not exist yet (run= completions, the reserved type=
vocabulary), and two are one derive gap.

That gap is worth its own note: flag "-p --path --file" has a second long form
the derive cannot declare, so gen-shadow drops it. It is invisible in the help
comparison, which renders one long form per flag, and shows up here because a
completion offers every name a flag answers to. Next PR.

Comparing found a real bug, which is what comparing is for: hidden aliases were
being offered. The parse table cannot tell a hidden alias from a visible one —
both must be accepted — so which are meant to be offered is a question only
the metadata answers.

Descriptions come from the metadata the binary already carries. The reference
leaves a flag's empty, its own source saying TODO: get flag description; filling
it in is a deliberate improvement, which is why the comparison is on values.

usage_cli::complete_candidates is a seam, not a feature: "the same candidates"
is only a checkable claim if the reference's answer can be read as data rather
than watched going past on stdout.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com


Stack created with GitHub Stacks CLIGive Feedback 💬


Note

Medium Risk
Completion behavior is user-facing and must stay in sync with the reference; gaps are documented but wrong candidates or hidden-alias leaks would confuse shell users. The API surface grows (Candidate, richer Position, feature coupling completespec) without touching auth or data persistence.

Overview
Adds spec-driven shell completion in usage-argv: candidates() turns a split command line plus static Spec metadata into Candidate values (flags, subcommands, declared choices, descriptions), aligned with usage-lib rules. Position / walk now track --, help topics, and in-scope flags so offerings match what the parser would accept, including hidden commands/aliases, negated long flags, restart tokens (:::), root default subcommand first-arg values, and positionals that require -- before values.

The complete feature now enables spec. Gate tests compare candidate values from the shadow mise CLI against usage_cli::complete_candidates on real mise lines (documented gaps: run= completions, type=config_keys, extra long forms from shadow gen).

usage-cli exposes complete_candidates and makes complete_word public so parity tests read reference output as data instead of stdout.

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

@coderabbitai

coderabbitaiBot commented Aug 14, 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: c3ace3fd-4a91-454d-bea7-d01bff3885a7

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.

Comment threadargv/src/complete.rs
@greptile-apps

greptile-appsBot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds completion candidate generation from compiled argv/spec tables and exposes the reference implementation’s candidates for parity testing.

  • Adds metadata-aware flag, subcommand, positional, restart-token, and default-subcommand completion.
  • Filters hidden commands, aliases, and flags while attaching available descriptions.
  • Adds unit and mise-scale parity tests against usage-cli.

Confidence Score: 4/5

The PR is not yet safe to merge because completion after the first argument of a restarted invocation still returns candidates from stale positional state.

restarted() recognizes only an immediately preceding restart token, while the compiled parser itself does not reset across that token; therefore a line such as mise task one ::: two skips the restart branch and fails to offer the restarted invocation’s second-argument choices.

Files Needing Attention: argv/src/complete.rs

Important Files Changed

FilenameOverview
argv/src/complete.rsAdds the compiled-table completion engine; immediate restart completion is handled, but completion after consuming the restarted invocation’s first argument still uses stale parser state.
benches/gate/tests/complete.rsAdds broad mise-scale candidate parity coverage, though restart-token continuation is not included.
cli/src/cli/complete_word.rsExposes the existing reference candidate computation as data without changing its completion logic.
argv/Cargo.tomlMakes the completion feature enable the metadata/spec feature required by the new implementation.

Reviews (5): Last reviewed commit: "feat(argv): offer what the reference off..." | Re-trigger Greptile

Comment threadargv/src/complete.rs Outdated
Comment threadargv/src/complete.rs
@jdx
jdxforce-pushed the agent/complete-candidates branch from 6127056 to 2ae4671CompareAugust 14, 2026 02:49
@jdx
jdxforce-pushed the agent/complete-candidates branch from 2ae4671 to 1c2008eCompareAugust 14, 2026 03:07
@jdxcoder-jdx

jdx commented Aug 14, 2026

Copy link
Copy Markdown
OwnerAuthor

Three findings, all correct, all fixed in the head commit — and the third exposed a fourth.

Negate flags offered without dashes (Cursor) — the table holds a negation the way the parser matches it, with the dashes already taken off (model.rs:772 strips them), so offering it bare matched no -- the user had typed and put a word on a lone - that no shell would accept. Candidates now put them back.

Restart-token state is ignored (greptile) — mise task one ::: ⌶ starts a fresh invocation, so the cursor is back at the first argument rather than at the second one the words had reached. Implemented from the metadata, where restart_token lives.

Default-subcommand arguments are omitted (greptile) — mise build means mise run build, so what the fallback command's first argument accepts is a candidate at the root too. Only its first, and only at the root, which is the rule usage-lib follows.

That last one then offered those values after help, where only a command name belongs — so the position now records that it is a help topic and the fallback rule skips it.

Worth noting for anyone reading the tests: my first restart-token test proved nothing. The fixture's argument was variadic, so "back at the first" and "wherever the words reached" were the same answer, and removing the branch entirely left the test green. It now uses a command with two differently-typed arguments, and fails without the branch.

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

Comment threadargv/src/complete.rs
Comment threadargv/src/complete.rs
Comment threadargv/src/complete.rs Outdated
Comment threadargv/src/complete.rs
@jdx
jdxforce-pushed the agent/complete-candidates branch from 1c2008e to 76b02caCompareAugust 14, 2026 03:17
@jdxcoder-jdx

jdx commented Aug 14, 2026

Copy link
Copy Markdown
OwnerAuthor

Both correct, both fixed in the head commit.

Required separator never offered — an argument declared double_dash = "required" is not fillable until a -- has been typed, so offering its choices was offering words the parser would reject. It now follows the reference's rule exactly (complete_word.rs:490): the separator itself, and only when nothing has been typed to filter it away. Applied through one helper, so the restart-token branch gets it too — which was the second half of your point.

That needed separator_seen on the position, narrower than flags_possible: the latter is also false past an automatic argument, and an argument that requires a separator is asking for that specific token.

Default subcommand alias not resolved — real. default_subcommand names a command, and a spec may name it the way its author refers to it rather than canonically; matching only cmd.name silently dropped the fallback's values. It now matches any name the command answers to, and the fixture names it by an alias so the lookup is under test rather than assumed.

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

@cursorcursorBot 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 76b02ca. Configure here.

Comment threadargv/src/complete.rs
@github-actions

github-actionsBot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Instruction counts

benchmarktrendinstructionsΔwall (min)Δ
markdown▁████████175,762,001 → 175,945,514+0.10%16.93 → 15.77ms-6.82%
startup▁████████1,222,055 → 1,221,975-0.01%1.05 → 0.99ms-6.34%

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.

Shadow comparison

Parsing mise use -g node@20 against a shadow of mise's committed spec.
Reported, not gated: the shadow grows as the derive learns to express more, so
what to watch is the ratio rather than either column.

usageclapratio
instructions, cold parse299715877527196x
usage: argv -> struct 850 ns 0.85 µs
clap: build tree + parse -> struct 493645 ns 493.64 µs
clap: parse -> struct, tree reused 23769 ns 23.77 µs
clap: build tree only 307491 ns 307.49 µs

652593878a2b vs fd7a4f8e5623 · measured on the runner, not pushed to the history.

The candidates for a cursor: the flags a word there could name, the subcommands it
could select, and the choices a flag or positional declares. The rules are
usage-lib's, because a CLI's completions should not change with the
implementation that answers them — a lone `-` offers both forms, `--…` offers
longs, `-…` narrows to one letter, a flag waiting for its value offers that flag's
choices and nothing else.
Held to the reference over mise's real spec: of eighteen lines across the tree,
thirteen agree exactly, including sets of forty and twenty-one candidates. The
five that do not are named in the test rather than dropped from it — two need
stages that do not exist yet (`run=` completions, the reserved `type=`
vocabulary), and two are one derive gap.
That gap is worth its own note: `flag "-p --path --file"` has a second long form
the derive cannot declare, so `gen-shadow` drops it. It is invisible in the help
comparison, which renders one long form per flag, and shows up here because a
completion offers every name a flag answers to. Next PR.
Comparing found a real bug, which is what comparing is for: hidden aliases were
being offered. The parse table cannot tell a hidden alias from a visible one —
both must be *accepted* — so which are meant to be *offered* is a question only
the metadata answers.
Descriptions come from the metadata the binary already carries. The reference
leaves a flag's empty, its own source saying `TODO: get flag description`; filling
it in is a deliberate improvement, which is why the comparison is on values.
`usage_cli::complete_candidates` is a seam, not a feature: "the same candidates"
is only a checkable claim if the reference's answer can be read as data rather
than watched going past on stdout.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jdx
jdxforce-pushed the agent/complete-candidates branch from 76b02ca to 6525938CompareAugust 14, 2026 03:40
@jdxcoder-jdx

jdx commented Aug 14, 2026

Copy link
Copy Markdown
OwnerAuthor

Half of this matches the reference, half is a parser gap worth naming.

restarted only treating the immediately preceding word as a restart is exactly usage-lib's rule — complete_word.rs:113: parsed.cmd.restart_token.as_ref().is_some_and(|rt| prev_token == Some(rt.as_str())). So ::: value ⌶ follows the pre-restart slot on both sides, and changing it here alone would make the two implementations disagree about a CLI's completions, which is the thing this stage is trying not to do.

The deeper observation is right, though: the parser does not implement restart tokens at all — restart_token is metadata, nothing in the parse tables resets on it — so next_arg, separator_seen and flags_possible all describe the invocation before the token. Completion inherits that. Fixing it properly means teaching the parser to restart, which changes binding as well as completion and belongs in its own PR rather than being approximated here. Filed as the next parser gap after this stack.

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

@jdx
jdx merged commit 41f9503 into mainAug 14, 2026
9 checks passed
@jdx
jdx deleted the agent/complete-candidates branch August 14, 2026 12:08
@mise-en-devmise-en-dev mentioned this pull request Aug 14, 2026
Sign up for freeto 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

@jdx