Skip to content

feat(complete): auto-completion for usage shebang scripts#620

Merged
jdx merged 7 commits intomainfrom
feat/completion-init-shebang
May 3, 2026
Merged

feat(complete): auto-completion for usage shebang scripts#620
jdx merged 7 commits intomainfrom
feat/completion-init-shebang

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented May 3, 2026

Summary

Adds usage g completion-init <shell> (bash/zsh/fish) — a one-time init script users source from their shell rc to enable <Tab> completion for any command on \$PATH whose first line is a usage shebang. No per-script usage g completion bash <bin> -f <script> generation required.

# bash:  ~/.bashrc
source <(usage g completion-init bash)

# zsh:   ~/.zshrc
source <(usage g completion-init zsh)

# fish:  ~/.config/fish/conf.d/usage.fish
usage g completion-init fish | source

Closes the docs gap reported in #617 where users expected shebang scripts on $PATH to gain completion automatically.

Mechanism per shell

  • bash: registers complete -D default handler. On <Tab>, resolves the candidate command, peeks line 1 for #!*usage*, and dispatches to usage complete-word. Chains to _completion_loader for non-usage commands so bash-completion's dynamic loader still works.
  • zsh: registers compdef -default- fallback. Same shebang detection. Falls back to _files for non-usage commands.
  • fish: fish has no -default- equivalent, so the init scans \$PATH once at shell startup and registers complete -c <name> per usage shebang script. One-time cost proportional to \$PATH size.

Tests

Three integration tests in cli/tests/shell_completions_integration.rs drive each shell against a staged usage shebang script and assert on the resulting completion candidates (positional, flag, partial flag).

The existing "skip if shell missing" pattern was bumped: skip_if_shell_missing() now panics under CI=1 to prevent silent false-positives if a shell is missing in CI.

Test plan

  • cargo test --all --all-features passes
  • cargo clippy --all --all-features -- -D warnings clean
  • mise run ci green
  • CI=1 cargo test -p usage-cli --test shell_completions_integration — all 9 pass
  • Manual smoke test: stage examples/example.sh as ex on PATH, source init in each shell, verify <Tab> produces val-1 val-2 val-3 and flag completion
  • Reviewer: confirm zsh `compdef -default-` doesn't conflict with reviewer's existing zsh setup
  • Reviewer: confirm fish startup-scan cost is acceptable for typical $PATH sizes

Note

Medium Risk
Adds new shell init scripts that install default completion handlers (bash complete -D, zsh compdef -default-) and a fish $PATH scan, which could interfere with users’ existing completion setups or impact shell startup performance. CI changes also alter workflow behavior by requiring additional packages and making missing shells in CI a hard failure.

Overview
Adds a new usage generate completion-init (alias ci) subcommand that outputs a one-time init script for bash/zsh/fish, enabling tab-completion for any executable on $PATH whose first line is a usage shebang by dispatching to usage complete-word.

Implements shell-specific init generators in lib/src/complete/ (including bash default-handler chaining and fish startup $PATH scanning), wires them into the CLI, and expands integration tests to cover completion-init behavior plus stricter “skip” logic (panic when shells are missing under CI).

Updates generated docs/manpage/Fig spec/reference JSON and adjusts GitHub Actions workflows to install zsh, fish, and ensure pwsh is available so the new integration tests run reliably in CI.

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

jdx and others added 2 commits May 3, 2026 17:01
Adds `usage g completion-init <shell>` (bash/zsh/fish), a one-time init
script users source from their shell rc to enable <Tab> completion for
any command on $PATH whose first line is a `usage` shebang. No per-script
`usage g completion` generation required.

- bash: registers `complete -D` default handler that detects the shebang
  at completion time and dispatches to `usage complete-word`. Chains to
  `_completion_loader` for non-usage commands so bash-completion's
  dynamic loading still works.
- zsh: registers `compdef -default-` fallback. Falls back to `_files`
  for non-usage commands.
- fish: scans `$PATH` once at shell startup (no `-default-` equivalent
  exists) and registers `complete -c <name>` per usage shebang script.

Closes the docs gap reported in #617 where users expected shebang
scripts on $PATH to gain completion automatically.

Tests: integration tests in shell_completions_integration.rs drive each
shell against a staged usage shebang script. The `skip_if_shell_missing`
helper now panics under CI=1 to prevent silent skips.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reflects the bash/fish/zsh shell choices in the auto-generated reference
docs, JSON, KDL spec, and Fig completion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@jdx jdx linked an issue May 3, 2026 that may be closed by this pull request
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 3, 2026

Greptile Summary

Adds usage g completion-init <shell> — a one-shot init script that enables tab-completion for any usage-shebang script on $PATH without per-script generation. Implements bash (complete -D), zsh (compdef -default-), and fish (PATH scan at startup) backends, wires them into the CLI, adds integration tests, and updates CI to install the required shells.

Confidence Score: 5/5

Safe to merge; only P2 style suggestions found.

No P0 or P1 issues found. The implementation is well-structured: fish correctly caps reads and deduplicates, bash properly chains to prior handlers and guards against infinite recursion, and zsh's -default- fallback is correctly scoped. The two P2 findings (misleading docs heading and uncapped read -r in bash/zsh on-demand handlers) are minor quality issues that do not affect correctness.

lib/src/complete/bash.rs and lib/src/complete/zsh.rs for the uncapped read -r in the shebang peek; docs/cli/completions.md for the misleading section heading.

Important Files Changed

Filename Overview
lib/src/complete/bash.rs Adds complete_bash_init: registers complete -D fallback, captures previous handler for chaining, and dispatches to usage complete-word for usage shebangs. The unbounded read -r on the shebang peek can buffer entire binaries on each tab-press (fish already fixed with -n 128).
lib/src/complete/zsh.rs Adds complete_zsh_init: registers compdef -default- fallback; dispatches to usage complete-word for usage shebangs, falls back to _files otherwise. Same unbounded read -r issue as bash handler.
lib/src/complete/fish.rs Adds complete_fish_init: scans $PATH at startup, registers per-command completers for usage shebang scripts. Correctly uses read -n 128 cap, string replace instead of path basename for portability, and deduplicates via $registered list.
lib/src/complete/mod.rs Adds complete_init public dispatcher and tests for supported/unsupported shells; straightforward and correct.
cli/src/cli/generate/completion_init.rs New CLI subcommand wiring completion-init; uses JDX_USAGE_BIN env override and delegates to complete_init. Clean and minimal.
cli/tests/shell_completions_integration.rs Adds 3 init integration tests (bash/zsh/fish); refactors existing shell-missing checks to panic under CI=1; stages a temp usage-shebang script and asserts completion candidates.
docs/cli/completions.md New section documents the init flow for all three shells, but the section heading still says "(bash)" despite covering zsh and fish as well.
.github/workflows/test.yml Installs zsh, fish, and conditionally pwsh before tests; consistent across all three workflow files.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["User presses Tab"] --> B{Shell}
    B -->|bash| C["complete -D fires\n_usage_default_complete"]
    B -->|zsh| D["compdef -default- fires\n_usage_default_complete"]
    B -->|fish| E["complete -C fires\n(registered at startup)"]
    C --> F{Read first line\nof cmdpath}
    D --> F
    E --> G["usage complete-word\n--shell fish -f file"]
    F -->|"#!*usage*"| H["usage complete-word\n--shell bash/zsh -f file"]
    F -->|not usage shebang| I{Fallback}
    I -->|bash| J["_usage_chained_default_complete\nor _completion_loader"]
    I -->|zsh| K["_files"]
    H --> L["Candidates shown"]
    G --> L
    subgraph Fish startup
        P["__usage_register_shebang_completions"] --> Q["for dir in PATH"]
        Q --> R["read -n 128 first line"]
        R -->|"#!*usage*"| S["complete -c name -x -a ..."]
    end
Loading

Fix All in Claude Code

Reviews (5): Last reviewed commit: "chore(ci): self-heal pwsh dependency in ..." | Re-trigger Greptile

Comment thread docs/cli/completions.md Outdated
Comment thread lib/src/complete/fish.rs
Comment thread lib/src/complete/bash.rs
Comment thread lib/src/complete/fish.rs
The shell completion integration tests now panic under CI=1 if the shell
is missing rather than silently skipping. ubuntu-latest only ships bash,
so install zsh and fish in the test, autofix, and coverage workflows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 3, 2026

Codecov Report

❌ Patch coverage is 94.87179% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.94%. Comparing base (7f29f3c) to head (55c1d36).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
cli/src/cli/generate/completion_init.rs 75.00% 0 Missing and 1 partial ⚠️
cli/src/cli/generate/mod.rs 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #620      +/-   ##
==========================================
- Coverage   79.03%   78.94%   -0.10%     
==========================================
  Files          48       49       +1     
  Lines        7235     7284      +49     
  Branches     7235     7284      +49     
==========================================
+ Hits         5718     5750      +32     
- Misses       1140     1147       +7     
- Partials      377      387      +10     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- fish: replace `path basename` (fish 3.5+) with `string replace -r` so the
  init works on Ubuntu 22.04 LTS (fish 3.3) and earlier.
- fish: cap `read` at 128 chars so binary executables without a newline don't
  cause fish to buffer the entire file at startup.
- bash: capture the pre-existing `complete -D` handler at registration time
  and chain to it for non-usage commands. Removes silent breakage when
  bash-completion's own default handler is registered before this init.
- bash: tolerate `complete -p -D` exiting non-zero under `set -e` when no
  prior handler exists.
- docs: drop the stale "only bash supported" info block — zsh and fish ship
  in the same PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jdx
Copy link
Copy Markdown
Owner Author

jdx commented May 3, 2026

Thanks for the review @greptile-apps. All four addressed in f3dd562:

  1. Stale "only bash" info block — removed; replaced with a per-shell mechanism summary.
  2. path basename (fish 3.5+ only) — swapped to string replace -r '^.*/' '' -- $file, which works on every supported fish version (Ubuntu 22.04 LTS ships fish 3.3.1).
  3. complete -D ordering — the init now captures any pre-existing -D handler at registration time via complete -p -D + a BASH_REMATCH extraction, and chains to it for non-usage commands. Verified end-to-end with a stub _pretend_completion_loader registered first: usage shebangs hit complete-word, non-usage commands chain through to the captured handler. Also documented the recommended sourcing order in the generated script header.
  4. Unbounded read on binaries — capped to read -l -n 128 (128 chars is plenty to detect any shebang line) so binaries without a newline don't stall startup.

One incidental fix: complete -p -D exits non-zero when no prior handler exists, which aborts the init under set -e. Tolerated with || true.

This comment was generated by Claude Code.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 3, 2026

All four fixes are confirmed in f3dd562 and implemented correctly:

  1. Stale docs — removed per the commit message.

  2. path basenamestring replace -r — correct, with an inline comment explaining the fish 3.3 compatibility rationale.

  3. complete -D chaining — the approach is solid:

    • Captures the prior handler at init-time via complete -p -D 2>/dev/null || true + BASH_REMATCH extraction
    • Guards against self-reference (!= "_usage_default_complete") before calling the captured handler — nice defensive touch against infinite recursion
    • Falls through to _completion_loader as a second-tier fallback if the captured handler is empty
  4. Unbounded readread -l -n 128 first <$file in fish. ✓

  5. || true under set -e — handled correctly via 2>/dev/null || true in the command substitution.

The implementation looks clean and the snapshot tests will catch regressions. LGTM. ✅

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

…error

Codecov flagged 90% patch coverage on the original PR; the gap was the
`complete_init` match arms and its UnsupportedShell error path. Adds two
unit tests:

- supported shells (bash/fish/zsh) all return non-empty output
- unsupported shell ("nu") returns UsageErr::UnsupportedShell

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread lib/src/complete/fish.rs Outdated
Per Cursor Bugbot review on PR #620: the existing per-binary
`complete_fish` detects `commandline -x` (fish 3.4+) and prefers
`commandline -xpc` for accurate tokenization of quoted/complex
arguments, falling back to `-opc` on older fish. The init script
unconditionally used `-opc`, so init-registered completions were
slightly less accurate than per-binary ones.

Add the same detection at init time and embed the chosen
`commandline` form into each `complete -c` registration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

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 9e82ab4. Configure here.

Comment thread cli/tests/shell_completions_integration.rs
Per Cursor Bugbot review on PR #620: the powershell integration test now
panics under CI=1 if pwsh is missing, but the workflows didn't declare
pwsh as an explicit dependency — they relied on it being pre-installed
on the GitHub ubuntu-latest runner image.

Add a guarded `snap install powershell --classic` to all three workflows
that install shells (test, autofix, coverage). Common case: pwsh is
already on the runner and the install step is a no-op. If a future
runner image drops pwsh, CI self-heals instead of failing with a panic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jdx jdx merged commit 03b2a70 into main May 3, 2026
10 checks passed
@jdx jdx deleted the feat/completion-init-shebang branch May 3, 2026 18:06
@mise-en-dev mise-en-dev mentioned this pull request May 3, 2026
jdx pushed a commit that referenced this pull request May 3, 2026
### 🚀 Features

- **(complete)** auto-completion for usage shebang scripts by
[@jdx](https://github.com/jdx) in
[#620](#620)

### 🐛 Bug Fixes

- **(docs)** stack banner and pin close button on mobile by
[@jdx](https://github.com/jdx) in
[#603](#603)

### 📚 Documentation

- **(site)** show release version and github stars by
[@jdx](https://github.com/jdx) in
[#604](#604)
- add cross-site announcement banner by [@jdx](https://github.com/jdx)
in [#600](#600)
- fix banner dark-mode contrast by [@jdx](https://github.com/jdx) in
[#601](#601)
- respect banner expires field by [@jdx](https://github.com/jdx) in
[#602](#602)
- prefix star count with ★ glyph and populate it on deploy by
[@jdx](https://github.com/jdx) in
[#606](#606)
- integrate Commander.js, oclif and yargs by
[@gaojunran](https://github.com/gaojunran) in
[#616](#616)
- integrate Typer and Click by
[@gaojunran](https://github.com/gaojunran) in
[#619](#619)

### 🔍 Other Changes

- **(docs)** remove shrill.en.dev analytics script by
[@jdx](https://github.com/jdx) in
[#614](#614)
- **(release)** append en.dev sponsor blurb to release notes by
[@jdx](https://github.com/jdx) in
[#598](#598)
- switch analytics from gtm to plausible by
[@jdx](https://github.com/jdx) in
[#609](#609)
- pin taiki-e/install-action to commit SHA by
[@jdx](https://github.com/jdx) in
[#610](#610)
- rename CLAUDE.md to AGENTS.md and symlink by
[@jdx](https://github.com/jdx) in
[#618](#618)

### 📦️ Dependency Updates

- bump communique to 1.1.2 by [@jdx](https://github.com/jdx) in
[#605](#605)
- lock file maintenance by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#607](#607)
- update autofix-ci/action action to v1.3.4 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#611](#611)
- update apple-actions/import-codesign-certs action to v7 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#612](#612)
- update taiki-e/install-action digest to fc9eae0 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#613](#613)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
@german-molins
Copy link
Copy Markdown

Thanks!!

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.

complete does not work for shell scripts

2 participants