From eb36e06763427e5ba6c740d834b7d5c056e53f77 Mon Sep 17 00:00:00 2001 From: benbenbang Date: Thu, 26 Mar 2026 19:12:47 +0100 Subject: [PATCH] feat(completions): add prefix option and enhance plugin wrapper help This pull request adds a new `--prefix` option to distinguish between project name override and full prompt customization, and significantly improves the plugin wrapper's help documentation. The main changes are grouped below. **Command-line option enhancement:** * Added new `--prefix` option across all shell completions (bash, zsh, fish, nushell) that allows overriding just the project name in the prompt while keeping the `-pyX.XX` suffix, providing more granular control than the existing `--prompt` option. * Updated `--prompt` option description to clarify it replaces the auto-generated name entirely, distinguishing its behavior from the new `--prefix` option. **Plugin wrapper documentation:** * Added comprehensive help text in `adds-on/src/main.rs` explaining the plugin-aware wrapper functionality, including usage patterns, plugin dispatch mechanism, special commands, environment variables, and setup instructions. * Created new `adds-on/README.md` file to document the plugin wrapper system. * Enhanced the `uv-shell` help message in `src/main.rs` to include information about the plugin wrapper system and how `uv-` binaries become callable as `uv ` subcommands. --- adds-on/README.md | 62 +++++++++++++++++++++++++++++++++++ adds-on/src/main.rs | 36 ++++++++++++++++++++ src/completions/uv-shell.bash | 4 +-- src/completions/uv-shell.fish | 3 +- src/completions/uv-shell.nu | 3 +- src/completions/uv-shell.zsh | 3 +- src/main.rs | 7 +++- 7 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 adds-on/README.md diff --git a/adds-on/README.md b/adds-on/README.md new file mode 100644 index 0000000..4d98318 --- /dev/null +++ b/adds-on/README.md @@ -0,0 +1,62 @@ +# adds-on + +A small Rust binary named `uv` that acts as a **plugin dispatcher** for the real `uv`. When installed before the real `uv` in `PATH`, it makes any `uv-` executable callable as `uv ` — similar to how `kubectl` handles plugins. + +## What it does + +``` +uv shell → finds uv-shell in PATH → exec uv-shell +uv add requests → no uv-add plugin found → exec real uv add requests +uv → real uv completions + discovered plugins (dynamic) +uv shell → delegates to uv-shell's own completions +``` + +Any executable named `uv-` on PATH is automatically discovered — no registration needed. + +## Setup + +**1. Add to `~/.zshrc`:** +```sh +export PATH="$(brew --prefix uv-shell)/libexec/bin:$PATH" + +# Optional: skip PATH scan on every uv call +export UV_REAL_PATH="/opt/homebrew/bin/uv" +``` + +**2. Install completions once:** +```sh +# zsh — plugins discovered dynamically at tab-press time, no re-eval needed +uv generate-shell-completion zsh > "${fpath[1]}/_uv" + +# nushell — add `use ~/.config/nushell/completions/uv.nu *` to config.nu +uv generate-shell-completion nushell | save ~/.config/nushell/completions/uv.nu + +# bash +echo 'eval "$(uv generate-shell-completion bash)"' >> ~/.bashrc + +# fish +uv generate-shell-completion fish > ~/.config/fish/completions/uv.fish +``` + +## Usage + +``` +uv --help show this help +uv [args] dispatch to uv- +uv generate-shell-completion completions with plugins injected +uv __complete list plugins (used by shell completions at tab-press) +``` + +## Environment + +| Variable | Description | +|---|---| +| `UV_REAL_PATH` | Path to the real uv binary — skips PATH scan on every call | + +## How it finds the real uv + +Scans `PATH` for a binary named `uv` that is not itself (using `is_file()` + canonicalize comparison). Set `UV_REAL_PATH` to skip the scan entirely. + +## License + +[MIT](../LICENSE) diff --git a/adds-on/src/main.rs b/adds-on/src/main.rs index 69f9d7e..ef2d74f 100644 --- a/adds-on/src/main.rs +++ b/adds-on/src/main.rs @@ -2,10 +2,46 @@ use std::env; use std::path::PathBuf; use std::process::{Command, exit}; +fn print_help() { + println!( + "uv — plugin-aware wrapper for the real uv + +USAGE: + uv [args...] + +PLUGIN DISPATCH: + Any executable named `uv-` on PATH is treated as a plugin. + + uv shell [args] → finds uv-shell in PATH, execs it + uv [args] → finds uv- in PATH, execs it + uv [args] → no plugin found, passes through to real uv + +SPECIAL COMMANDS: + uv generate-shell-completion + → real uv completions + plugins injected + shells: bash zsh fish nushell + uv __complete → list discovered plugins (used by shell completions) + uv --help, -h → show this message + +ENVIRONMENT: + UV_REAL_PATH Skip PATH scan — point directly to the real uv binary + e.g. export UV_REAL_PATH=/opt/homebrew/bin/uv + +SETUP: + export PATH=\"$(brew --prefix uv-shell)/libexec/bin:$PATH\" + uv generate-shell-completion zsh > \"\${fpath[1]}/_uv\" + +Any unrecognised command is forwarded to the real uv unchanged." + ); +} + fn main() { let args: Vec = env::args().collect(); match args.get(1).map(|s| s.as_str()) { + Some("-h") | Some("--help") => { + print_help(); + } // Dynamic plugin discovery — called by shell completion at tab-press time Some("__complete") => { for plugin in discover_plugins() { diff --git a/src/completions/uv-shell.bash b/src/completions/uv-shell.bash index ddffbc1..0333d99 100644 --- a/src/completions/uv-shell.bash +++ b/src/completions/uv-shell.bash @@ -6,7 +6,7 @@ _uv_shell() { local commands="anchor completions" # All options - local opts="-p --python --seed -c --clear --allow-existing --prompt + local opts="-p --python --seed -c --clear --allow-existing --prefix --prompt --system-site-packages --relocatable --no-project --no-config --index-strategy --keyring-provider --exclude-newer --link-mode --index --default-index -i --index-url --extra-index-url @@ -17,7 +17,7 @@ _uv_shell() { # Handle completions for options that take a value case "$prev" in - -p|--python|--prompt|--index-strategy|--keyring-provider| \ + -p|--python|--prefix|--prompt|--index-strategy|--keyring-provider| \ --link-mode|--exclude-newer|--exclude-newer-package| \ --index|--default-index|-i|--index-url|--extra-index-url| \ -f|--find-links|--cache-dir|--color|--directory|--project| \ diff --git a/src/completions/uv-shell.fish b/src/completions/uv-shell.fish index 096e3e9..fef063b 100644 --- a/src/completions/uv-shell.fish +++ b/src/completions/uv-shell.fish @@ -13,7 +13,8 @@ complete -c uv-shell -n '__fish_use_subcommand' -s p -l python -d 'Python interp complete -c uv-shell -n '__fish_use_subcommand' -l seed -d 'Install seed packages (pip, setuptools, wheel)' complete -c uv-shell -n '__fish_use_subcommand' -s c -l clear -d 'Re-create the venv even if .venv already exists' complete -c uv-shell -n '__fish_use_subcommand' -l allow-existing -d 'Preserve existing files at the target path' -complete -c uv-shell -n '__fish_use_subcommand' -l prompt -d 'Custom prompt prefix (skips auto-prompt)' -r +complete -c uv-shell -n '__fish_use_subcommand' -l prefix -d 'Override project name in prompt (keeps -pyX.XX suffix)' -r +complete -c uv-shell -n '__fish_use_subcommand' -l prompt -d 'Full custom prompt (replaces auto-generated name entirely)' -r complete -c uv-shell -n '__fish_use_subcommand' -l system-site-packages -d 'Access system site packages' complete -c uv-shell -n '__fish_use_subcommand' -l relocatable -d 'Make the venv relocatable' complete -c uv-shell -n '__fish_use_subcommand' -l no-project -d 'Avoid discovering a project or workspace' diff --git a/src/completions/uv-shell.nu b/src/completions/uv-shell.nu index aa3a747..aa3b5d4 100644 --- a/src/completions/uv-shell.nu +++ b/src/completions/uv-shell.nu @@ -36,7 +36,8 @@ module uv-shell-completions { --seed # Install seed packages (pip, setuptools, wheel) --clear(-c) # Re-create the venv even if .venv already exists --allow-existing # Preserve existing files at the target path - --prompt: string # Custom prompt prefix (skips auto-prompt) + --prefix: string # Override project name in prompt (keeps -pyX.XX suffix) + --prompt: string # Full custom prompt (replaces auto-generated name entirely) --system-site-packages # Access system site packages --relocatable # Make the venv relocatable --no-project # Avoid discovering a project or workspace diff --git a/src/completions/uv-shell.zsh b/src/completions/uv-shell.zsh index 18d5070..856000e 100644 --- a/src/completions/uv-shell.zsh +++ b/src/completions/uv-shell.zsh @@ -13,7 +13,8 @@ _uv-shell() { '--seed[Install seed packages (pip, setuptools, wheel)]' '(-c --clear)'{-c,--clear}'[Re-create the venv even if .venv already exists]' '--allow-existing[Preserve existing files at the target path]' - '--prompt[Custom prompt prefix (skips auto-prompt)]:prompt:' + '--prefix[Override project name in prompt (keeps -pyX.XX suffix)]:prefix:' + '--prompt[Full custom prompt (replaces auto-generated name entirely)]:prompt:' '--system-site-packages[Give the venv access to system site packages]' '--relocatable[Make the venv relocatable]' '--no-project[Avoid discovering a project or workspace]' diff --git a/src/main.rs b/src/main.rs index 785e73f..75ce38f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -281,7 +281,12 @@ Options (forwarded to `uv venv`): -v, --verbose Verbose output -h, --help Show this help message -All other `uv venv` options are also forwarded. See `uv venv --help` for the full list." +All other `uv venv` options are also forwarded. See `uv venv --help` for the full list. + +uv plugin wrapper (adds-on): + Enables `uv shell` as a native uv subcommand with plugin-aware tab completions. + Any `uv-` binary on PATH becomes callable as `uv `. + See the README for setup instructions." ); }