Skip to content

completions: complete files for bun <path> and skip runtime flags before dispatch - #36629

Open
robobun wants to merge 1 commit into
mainfrom
claude/2fb721ab/completions-run-paths-flags
Open

completions: complete files for bun <path> and skip runtime flags before dispatch#36629
robobun wants to merge 1 commit into
mainfrom
claude/2fb721ab/completions-run-paths-flags

Conversation

@robobun

@robobun robobun commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7805. Fixes #6037. Fixes #20216. Fixes #30386.

Supersedes #30387 (closed): this includes its bash/zsh *) fallback and adds the runtime-flag handling and fish coverage that PR did not have. Its bash test cases (bun myscript.ts foo<TAB>, and the same with another argument before the cursor) are folded into test/cli/completions.test.ts here.

Reproduction

In a project with src/index.ts and src/other.ts, drive each shell's completion non-interactively:

# bash: source the script, set COMP_WORDS/COMP_CWORD, call _bun_completions
# fish: fish --no-config -c "source bun.fish; complete -C '<line>'"
# zsh:  via zpty + compadd capture

On main:

line bash fish zsh
bun run src/ src/index.ts src/other.ts¹ (nothing) index.ts other.ts
bun src/ (nothing) src/index.ts src/other.ts² (nothing)
bun --hot run src/ (nothing) (nothing) (nothing)
bun run --watch src/ src/index.ts src/other.ts¹ (nothing) index.ts other.ts
bun --hot src/ (nothing) (nothing) (nothing)
bun --watch src/ (nothing) src/index.ts src/other.ts² (nothing)

¹ only when extglob is already on (bash-completion loaded); otherwise nothing
² accidental: -F on the __fish_use_subcommand init/add/remove entries leaks through

Cause

All three shells located the subcommand by looking at the first word after bun, so any runtime flag there made the dispatch fall through and complete nothing.

  • bash: case "${COMP_WORDS[1]}" in hard-codes position 1. --hot at position 1 falls to the *) arm, which offers global flags but never files. Bare bun <path> hits the same arm. _file_arguments also relied on the caller having extglob on, so bun run src/ completed nothing when bash-completion wasn't loaded.
  • fish: --hot was declared -r (requires argument), so bun --hot run … consumed run as --hot's value and nothing matched the run subcommand. __bun_complete_bins_scripts emitted bun getcompletes j output (cwd files only), so bun run src/ never descended into src/. --watch/--smol/--bun/--inspect* weren't declared at all.
  • zsh: the top-level _arguments -s '1: :->cmd' '*: :->args' declares no options, so --hot becomes $line[1] and case $line[1] matches nothing. The cmd state offers bun getcompletes j via compadd, which again is cwd-only, so bun src/ never descended into src/.

Fix

  • bash: walk COMP_WORDS to find the first non-option word, skipping known value-taking flags and their values, and dispatch on that. The no-subcommand position now offers subcommands, scripts and files; the unknown-subcommand fallback offers global flags and files (matching what main offered for bun build/test/etc while adding file completion). _file_arguments now enables extglob locally, offers directories, and appends instead of overwriting.
  • fish: drop -r from --hot, declare --watch/--smol/--bun/--silent/--no-clear-screen/--inspect*/--preload/--cwd/--env-file, and add a complete -c bun -n __bun_takes_files -F rule: file completion unless the first positional (found by the same flag-skipping walk the subcommand gates use) is a subcommand whose arguments are never files (the package-manager family, link/unlink/outdated/publish/patch/info/audit/exec). That covers bun <file>, bun run <file>, bun test/bun build, and the arguments after an executed script (Tab key can't work for the 2nd argument in macOS shell #30386), including through runtime flags; bun add/link/... keep their existing behaviour.
  • zsh: declare the runtime flags on the top-level _arguments call so it skips them when locating the first positional (verified via zpty: bun --hot run src/ now sets state=args line[1]=run). Add _files -g '*.(js|mjs|cjs|ts|mts|cts|jsx|tsx|wasm|html)' to the cmd state so bun src/<TAB> descends into directories, and a *) fallback in the args dispatch that completes files when $line[1] is a script/file path.

With the fix every cell in the table above is src/index.ts src/other.ts.

Relationship to other open PRs

#35443 regenerates the entire bash and fish scripts from completions/bun-cli.json and fixes this for those two shells as part of a much larger change; it doesn't touch zsh. This PR is the minimal three-shell fix for #7805 specifically and can land independently.

#26743 is complementary, not overlapping: _read_scripts_in_package_json (untouched here) still tests =~ ${re_comp_word_script} against an undeclared variable, which is always true under GNU regex and an error under BSD regex, so package.json scripts are still filtered out of bash completions until that one-line fix lands. Both PRs merge cleanly with each other and with #27908 (bash bun test flags), #29364 (fish --frozen-lockfile), #31665 and #34062 (zsh one-liners).

Verification

test/cli/completions.test.ts drives bun completions (piped stdout emits the embedded script) and then:

  • bash (bash ≥ 4): sets COMP_WORDS/COMP_CWORD and calls _bun_completions for each line above plus bun --cwd <dir> run src/, asserting both files are offered, and that bun --hot <TAB> still offers run/install/--watch; bun myscript.ts foo and bun myscript.ts prev-arg foo complete exactly the two matching files (Tab key can't work for the 2nd argument in macOS shell #30386). Also bash -n.
  • fish (when on PATH): complete -C '<line>' for each line, plus bun src/index.ts src/ and bun src/index.ts prev-arg src/ (Tab key can't work for the 2nd argument in macOS shell #30386), and negative checks that bun add/bun link/bun outdated do not get file completion. Also fish -n.
  • zsh (when on PATH): zsh -n, plus a structural assertion that the top-level _arguments declares --hot/--watch/etc and that the cmd state offers _files.

With fish 4.8 and zsh 5.8 installed (so nothing is skipped):

USE_SYSTEM_BUN=1 bun test test/cli/completions.test.ts    7 fail, 3 pass
bun bd test test/cli/completions.test.ts                  10 pass, 0 fail

The 3 that pass either way are the bash -n/fish -n/zsh -n syntax checks.

Note for the reviewer: the only diff is under completions/ and test/; the scripts are compiled in via include_bytes! from src/runtime/cli/shell_completions.rs, so the gate's src/-stash step leaves the fixed scripts in place either side and cannot reproduce fail-before. The fail-before is demonstrated above against the released binary.


no test proof · iteration 5 · Platform-specific test-only change; deferring to CI.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

Limit details: You’ve used all 5 included reviews currently available under your plan.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6dbdddb1-d603-489a-a550-dd953f778e88

📥 Commits

Reviewing files that changed from the base of the PR and between 8326d1b and 6c595b0.

📒 Files selected for processing (4)
  • completions/bun.bash
  • completions/bun.fish
  • completions/bun.zsh
  • test/cli/completions.test.ts

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 12:40 PM PT - Aug 16th, 2026

@robobun, your commit 6c595b03a7ec1037bd6727fe3b456363e7286c54 passed in Build #99449! 🎉


🧪   To try this PR locally:

bunx bun-pr 36629

That installs a local version of the PR into your bun-36629 executable, so you can run:

bun-36629 --bun

@robobun

robobun commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

Status: reproduced in bash/fish/zsh by driving each completion non-interactively (bash via _bun_completions+COMP_WORDS, fish via complete -C, zsh via zpty). Fix is in completions/bun.{bash,fish,zsh} with coverage in test/cli/completions.test.ts; every review thread is resolved. Latest, dbef2b0: the fish token walk no longer uses a fish-3.4-only range (restoring the script's previous minimum fish version), and the fish probe asserts stderr per completion like the bash one. Branch is linear on main; the PR diff is the 4 files above.

With fish and zsh installed: USE_SYSTEM_BUN=1 bun test test/cli/completions.test.ts 7 fail / 3 pass; bun bd test 10 pass (189 assertions).

CI: the completions test passed on every lane for 631ed8d (#93801) and 4a001b4 (#93972); all red there was flaky-tagged and unrelated. Waiting on the run for dbef2b0.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Found 4 issues this PR may fix:

  1. Bash autocomplete #6037 - PR adds file completion for bare bun <path> (without run) in bash, which is the core request
  2. Bun run doesn't autocomplete for executable files in directory #20216 - PR adds real file completion via complete -c bun -n __bun_entrypoint -F in fish, fixing bun run <path> file completion
  3. bash_completion bug (macos bash) #24847 - PR rewrites bash dispatch logic and enables extglob locally in _file_arguments, fixing the regex errors on macOS bash
  4. Update shell completions #2503 - PR declares --watch, --hot, --smol, --inspect*, and other runtime flags in all three shells (bash, fish, zsh)

If this is helpful, copy the block below into the PR description to auto-close these issues on merge.

Fixes #6037
Fixes #20216
Fixes #24847
Fixes #2503

🤖 Generated with Claude Code

@robobun

robobun commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

Of the four suggested: #6037 and #20216 are the same symptom and are now in the PR description. #24847 (the macOS empty (sub)expression from re_comp_word_script / the [A-Za_z] typo) and the bun test --update-snapshots half of #2503 are untouched here; those are covered by #36515 and #35443 respectively.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. fix(completions): complete files after a script arg in bash & zsh #30387 - Also adds file completions for bun <path> (without explicit run subcommand) in bash & zsh by adding default/fallback branches to completion scripts

🤖 Generated with Claude Code

@robobun

robobun commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

Re #30387: that PR adds the *)_files fallback in bash and zsh for bun <script> <arg><TAB> (#30386). This PR includes that same fallback in both shells and adds the runtime-flag skipping (bun --hot …) plus fish coverage that #30387 does not have, so this supersedes it. PR description updated with Fixes #30386 and the supersedes note.

Comment thread completions/bun.zsh Outdated
Comment thread completions/bun.bash
Comment thread completions/bun.bash Outdated
Comment thread completions/bun.bash

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional findings (outside current diff — PR may have been updated during review):

  • 🟡 completions/bun.bash:114-123 — nit: the --backend) arm of the case "${prev}" block just below still dispatches on ${COMP_WORDS[1]} — the one COMP_WORDS[1] site the first_word rewrite didn't cover. first_word is already computed and in scope here, so bun --cwd /x add --backend <TAB> (or any global flag before the pm subcommand) offers nothing where it should offer clonefile copyfile hardlink …. One-token fix: case "${first_word}" in.

    Extended reasoning...

    What the bug is

    This PR replaces the main case "${COMP_WORDS[1]}" dispatch (now line 154) with case "${first_word}", computing first_word via a scanner (lines 114-123) that skips runtime flags and their values. But the --backend) arm of the case "${prev}" block sitting between the scanner and the main dispatch was not updated:

    --backend)
        case "${COMP_WORDS[1]}" in
            a|add|remove|rm|install|i)
                COMPREPLY=( $(compgen -W "clonefile copyfile hardlink clonefile_each_dir symlink" -- "${cur_word}") );
                ;;
        esac
        return ;;

    first_word is declared local at line 114 and populated by line 123, and the case "${prev}" block starts at line 125 — so first_word is already in scope and holds the correct subcommand at the point this arm runs.

    Step-by-step trace

    For bun --cwd /some/dir add --backend <TAB> (--cwd is a documented global flag for bun add/install/remove), COMP_WORDS=(bun --cwd /some/dir add --backend ""), COMP_CWORD=5, prev="--backend":

    step value
    first_word scanner, i=1 --cwd → matches value-taking arm → ((i++)) skips /some/dir
    first_word scanner, i=3 addfirst_word="add", break
    case "${prev}" matches --backend)
    inner case "${COMP_WORDS[1]}" "--cwd" → matches none of `a
    return

    Result: no completions. With case "${first_word}" the inner case would match add and offer clonefile copyfile hardlink clonefile_each_dir symlink. The same happens for bun --silent install --backend <TAB>, bun -b add --backend <TAB>, etc.

    Why nothing else prevents it

    The --backend) arm ends in an unconditional return, so the corrected case "${first_word}" dispatch at line 154 never gets a chance to run for this position. The only guard on offering the backend list is the inner case, and it is looking at the wrong word.

    Why this is in scope

    grep -n 'COMP_WORDS\[1\]' completions/bun.bash after this PR returns exactly one line — this one. It is the last remaining position-1 hard-code left behind by a rewrite whose entire purpose was to eliminate exactly this pattern. Per REVIEW.md → Fix the whole class in the same PR: "grep for every sibling site sharing the pattern: parallel switch arms …".

    Impact

    Not a regression — pre-PR the whole file dispatched on COMP_WORDS[1], so bun --cwd /x add --backend <TAB> completed nothing before either. Narrow: only affects --backend value completion when a global flag precedes the pm subcommand. Marked nit for that reason.

    Fix

    One-token change on line ~131:

    --backend)
        case "${first_word}" in
            a|add|remove|rm|install|i)

Comment thread completions/bun.fish Outdated
Comment thread completions/bun.bash Outdated
@robobun

robobun commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

The --backend) COMP_WORDS[1] leftover from the "Additional findings" comment is fixed in 947222c (now ${first_word}; no remaining COMP_WORDS[1] references in the file).

Comment thread completions/bun.bash
Comment thread completions/bun.bash
Comment thread completions/bun.fish Outdated
Comment thread completions/bun.bash Outdated
Comment thread completions/bun.zsh Outdated
Comment thread completions/bun.bash Outdated
Comment thread completions/bun.zsh Outdated
Comment thread completions/bun.zsh

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — completion-script-only change, all prior review rounds addressed.

What was reviewed:

  • zsh '*:: :->args' + words=($program …) shift: traced bun --hot run src/, bun run --watch src/, bun add -d, and the help) inner $line[2] path — sub-completer positional numbering lines up in each.
  • bash: replaced_script/_subcommand_comp_reply removals are dead (no remaining refs); eval "$reset" is the shopt -p output, safe.
  • fish __bun_use_subcommand now covers every prior __fish_use_subcommand gate; __bun_entrypoint's -F rule is scoped away from non-run subcommands.
Extended reasoning...

Overview

Touches only completions/bun.{bash,fish,zsh} plus a new test/cli/completions.test.ts. The scripts are embedded via include_bytes! and drive shell tab-completion; no runtime, bundler, or install code paths. The change makes each shell locate the first non-option word before dispatching, so bun <path> and bun --hot run <path> complete files instead of nothing.

Security risks

None. Shell completion scripts run in the user's interactive shell on tab press and only call compgen/_files/complete -C and bun getcompletes. No new external input reaches native code; the embedded script bytes are unchanged in how they are loaded.

Level of scrutiny

Low-to-medium. Worst-case failure is degraded or wrong tab-completion suggestions, not a crash, hang, or data corruption. The scripts are non-load-bearing for any bun invocation. That said, they ship to every user, so I traced the final-commit changes carefully:

  • zsh f44ee4a (the last fix): '*:: :->args' rewrites words/CURRENT to the positionals, then words=($program "${words[@]}"); ((CURRENT++)) restores the bun <subcmd> … layout each sub-completer's _arguments '1: :->cmd' '2: :->script' was written for. $line[1] (set by the top-level _arguments, independent of the :: rewrite) still holds the subcommand for the case dispatch, and the help) arm's inner _arguments re-derives its own $line[2] from the shifted words, so that path is unaffected. -A '-*' leaves post-subcommand flags (e.g. bun add -d) for the sub-completer as intended.
  • bash: the removed _subcommand_comp_reply and replaced_script had no remaining callers after the *)/"") split. _file_arguments now saves/restores extglob via shopt -p/eval, appends instead of overwrites, and adds compgen -d; both run) and "") call _read_scripts_in_package_json first so its always-true filter can't strip later-appended dirs.
  • fish: every __fish_use_subcommand gate (subcommand entries, $flag_applies, the not … on --version) now uses __bun_use_subcommand, so value-taking flags before the subcommand no longer suppress any completion class.

Other factors

This PR went through six review iterations; every inline finding (bash COMP_WORDS[1] leftover, =/: word-break tokens, run) reorder, fish $flag_applies, zsh =- optional-arg suffix, *-prefix for repeatable flags, and the two-layer optspec drift) was addressed with a targeted commit and a matching test assertion. All threads are resolved and the bug-hunting pass on the current head found nothing. The new test file drives each shell non-interactively, skipIfs when the shell binary is absent, and asserts the negative case (bun add src/ does not get file completion). Given the low blast radius and the resolved review history, this does not need a further human pass.

Comment thread src/analytics/lib.rs
Comment thread src/api/lib.rs Outdated
Comment thread src/ast/e.rs
Comment thread src/ast/e.rs
Comment thread src/ast/expr.rs
Comment thread src/ast/expr.rs
Comment thread src/ast/expr.rs
Comment thread src/ast/import_record.rs
Comment thread src/ast/import_record.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread src/jsc/NodeCompileCache.rs
Comment thread completions/bun.fish Outdated
@robobun
robobun force-pushed the claude/2fb721ab/completions-run-paths-flags branch from 150c45e to 631ed8d Compare August 13, 2026 01:24
@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

One thing noticed while folding #30387 in, pre-existing on main and carried through here since _file_arguments keeps the line: the empty-word branch calls compgen -fG -X "${extensions}" -- "", and -G takes the next argument (-X) as its glob, so that branch never lists files. Driving the script from this branch (bash 5.2, extglob on) in a directory containing somefile.js and src/:

bun run <TAB>   ->  --version --cwd --help --silent -v -h src      (src from the new compgen -d; no files)
bun run s<TAB>  ->  src somefile.js
bun <TAB>       ->  flags + subcommands + src, no files

So bun <TAB> / bun run <TAB> only list directories until a prefix is typed; the prefix cases the tests cover work. Not a regression from this PR, just noting it since the body says files are offered at those positions. #35443's generated script uses plain compgen -f -X there; if you want to fix it here it is that one flag, otherwise it can wait for the generator.

Comment thread completions/bun.fish Outdated

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — every prior finding from the earlier review rounds is addressed, and this run found nothing new.

Checked the three additional commits since the last round (bash #30386 test, fish __bun_takes_files, and the alias exclusion list): the fish -F gate correctly excludes package-manager aliases (i/ci/a/rm/c/etc.) so bun add src/ still doesn't get file completion; the zsh words=($program …) shift keeps sub-completer '1: :->cmd' numbering intact for all dispatch arms including help. The pre-existing compgen -fG -X empty-word quirk noted in the thread is on main and only affects the no-prefix case — not a regression here.

Extended reasoning...

Overview

Touches only completions/bun.{bash,fish,zsh} and adds test/cli/completions.test.ts. No src/ changes — the scripts are embedded via include_bytes! from shell_completions.rs, so the only runtime effect is what bun completions writes when installing shell integration. The fix makes each shell locate the first non-option positional (skipping runtime flags and their values) before dispatching, adds file completion for bare bun <path> and for the arguments after an executed script, and declares the missing runtime flags (--hot/--watch/--smol/--inspect*/etc.) so they're both offered and skipped.

Security risks

None. Shell-completion scripts are sourced by the user's own interactive shell and only run compgen/_files/bun getcompletes against the current directory. No new external-input parsing, no network, no privilege boundary.

Level of scrutiny

Low blast radius: the failure mode is "wrong completions offered" in an interactive shell, not a crash or behavioural change in the runtime. That said, the _arguments optspec and COMP_WORDS walking are subtle, which is why this went through eight prior review rounds. Every issue raised there — the zsh -A '-*' regression on bun add -d, the bash =|: word-break tokens, dead _subcommand_comp_reply/replaced_script, the _read_scripts_in_package_json ordering that stripped subcommand names, extension-list drift between arms, fish value-taking flag skipping, zsh --inspect=- optional-arg form, repeatable *-prefixed flags, and the two-layer optspec drift fixed structurally by '*:: :->args' + the words shift — is in the current diff, and each has a targeted assertion in the new test.

Other factors

The new test/cli/completions.test.ts drives all three shells non-interactively (bash via COMP_WORDS+_bun_completions, fish via complete -C, zsh via -n + structural assertions), skipIfs on missing binaries, uses tempDir/bunEnv/test.concurrent, and drains stdout/stderr/exited together. CI for the head commit passed the new test on every lane per the build-status comment. The one open note in the thread — the pre-existing compgen -fG -X in _file_arguments' empty-word branch — is on main today, only affects bun run <TAB> with no prefix (directories are still offered via the new compgen -d), and is slated for #35443's generator; it doesn't block this fix.

@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Re the compgen -fG note: fixed in 23e8008. _file_arguments is now a single compgen -f -X call for both the empty and non-empty word, so bun <TAB> and bun run <TAB> list runnable files as well as directories; the fixture now has a top-level entry.ts and a notes.txt, and the test asserts the former is offered and the latter filtered at bun , bun run and bun --hot .

Comment thread completions/bun.fish Outdated
Comment thread test/cli/completions.test.ts

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — completion-script-only change with a benign failure mode; all prior review findings have been addressed.

What was reviewed:

  • bash first_word scanner: value-taking flags, =/: word-break tokens, ordering vs. _read_scripts_in_package_json's always-true filter, _file_arguments extglob handling and the empty-word branch — all fixed across the iteration.
  • fish: --hot -r drop, __bun_use_subcommand/__bun_takes_files gating, subcommand-alias exclusion list, fish < 3.4 range compat.
  • zsh: -A '-*' so -d/-p after add aren't claimed as --define/--print; --inspect=- optional-arg form; *-prefixed repeatable flags; '*:: :->args' word-shift so sub-completers don't re-parse pre-subcommand flags.
Extended reasoning...

Overview

Touches completions/bun.{bash,fish,zsh} and adds test/cli/completions.test.ts. The fix makes each shell locate the first positional word past runtime flags (bun --hot run …, bun --watch src/…) and complete files for bare bun <path> and for arguments after an executed script. bash gets a first_word scanner and a split ""/* dispatch arm; fish gets __bun_first_positional/__bun_use_subcommand/__bun_takes_files and the missing runtime-flag declarations; zsh declares runtime flags on the top-level _arguments with -A '-*' and '*:: :->args', adds _files -g to the cmd state, and a *)_files fallback in args.

Security risks

None. Shell completion scripts read COMP_WORDS/commandline and emit candidates; they don't execute user input, touch the network, or affect bun's own execution. Worst-case failure is wrong or missing tab-completion candidates.

Level of scrutiny

Medium diff size (~460 lines) but low-risk surface: nothing here ships in the runtime code path — the scripts are include_bytes!'d and only ever sourced by the user's shell for tab completion. The subtle parts (zsh _arguments optspec semantics, bash COMP_WORDBREAKS tokenisation, fish condition gating) went through ten prior review rounds on this PR; every finding I raised (top-level -d/-p regressing bun add -d, =/: word-break handling, dead _subcommand_comp_reply/replaced_script, extension-list drift between arms/shells, run) ordering vs. the script-name filter, fish --cwd <dir> gap, zsh --inspect optional-arg greediness, repeatable-flag * prefix, and the two-layer optspec drift) has a corresponding fix commit and, where testable, a covering assertion in completions.test.ts.

Other factors

The new test file drives the embedded scripts non-interactively per shell (bash via COMP_WORDS/_bun_completions, fish via complete -C, zsh via zsh -n + structural assertions on the top-level _arguments string), asserts stderr is empty, and includes negative checks (bun add/link/outdated do not get file completion; notes.txt is filtered). Tests skipIf on missing binaries and on macOS bash 3.2. The github-actions comment-cop noise on 2026-08-13 is on files this PR does not touch and is unrelated. No outstanding unresolved reviewer comments.

…efore dispatch

bash, fish and zsh all located the subcommand by looking at the first word
after `bun`, so any runtime flag there (`bun --hot run src/`, `bun --watch
src/`) fell through the dispatch and completed nothing, and bare
`bun <path>` (no `run`) never offered files.

bash: find the first non-option word (skipping value-taking flags, their
values, and the `=`/`:` tokens readline splits them into) and dispatch on
that; complete subcommands, scripts, directories and runnable files at the
entrypoint position and files after an executed script; one extension list
shared by the run and entrypoint arms; `_file_arguments` enables extglob
itself and no longer passes -fG (which made compgen read -X as its glob and
list nothing for an empty word); dead `_subcommand_comp_reply` /
`replaced_script` removed.

fish: `--hot` takes no argument; declare the missing runtime flags; gate
every subcommand/flag rule on a first-positional walk that skips flag
values; add a file-completion rule that applies unless the first positional
is a subcommand (or alias) whose arguments are never files.

zsh: declare the runtime flags on the top-level `_arguments` (`-A '-*'`, the
`=-` form for optional-argument flags, `*` for repeatable ones), shift
`words` to the subcommand before calling the per-subcommand completers,
offer files in the subcommand position, and fall back to `_files` after a
script path.

Rebased over #38333 (audit/dedupe/prune/update completions): its new bash
arms are kept and its two new `COMP_WORDS[1]` lookups use `first_word`; its
new fish subcommand entries use the same flag-skipping gate as the rest;
`up`/`dedupe`/`prune` are excluded from fish file completion.

Fixes #7805. Fixes #6037. Fixes #20216. Fixes #30386.
@robobun
robobun force-pushed the claude/2fb721ab/completions-run-paths-flags branch from dbef2b0 to 6c595b0 Compare August 16, 2026 19:10

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — completion-script-only change with test coverage; every earlier inline finding on this PR has been addressed and resolved.

What was reviewed:

  • bash: first_word walk skips value-taking flags and =/: word-break tokens; _file_arguments now appends and enables extglob locally; _read_scripts_in_package_json ordering so a test/build script can't hide the subcommand; dead replaced_script/_subcommand_comp_reply deleted.
  • fish: --hot no longer -r; __bun_first_positional token walk; __bun_takes_files gate keeps add/link/outdated/etc from getting file completion (asserted in the test).
  • zsh: top-level _arguments -s -A '-*' with '*:: :->args' + words=($program …) prepend so sub-completers never re-parse the runtime flags; --inspect=-/--config=- optional-arg form; repeatable flags *-prefixed; *) fallback → _files.
  • test/cli/completions.test.ts: hermetic (bun getcompletes stubbed in the fish case, temp $HOME), test.concurrent, per-shell skipIf, and -n syntax checks for all three shells.
Extended reasoning...

Overview

Fixes #7805/#6037/#20216/#30386 by making all three shell completion scripts locate the first non-option word (skipping known value-taking runtime flags) before dispatching, and offering real file completion at the bun <path> position and after an executed script. The diff is confined to completions/bun.{bash,fish,zsh} plus a new test/cli/completions.test.ts; the scripts are embedded via include_bytes! in src/runtime/cli/shell_completions.rs, which is untouched.

Security risks

None. Shell completion scripts are user-invoked, read-only DX helpers that run compgen/_files/complete -C over the local filesystem. No auth, crypto, network, or privilege boundaries are involved, and no runtime code path changes.

Level of scrutiny

Low-to-medium. The failure surface is tab-completion quality: worst case a completion offers the wrong candidates or nothing at all. The zsh _arguments restructuring ('*:: :->args' + program-name prepend) is the most subtle piece and was the subject of the f44ee4a fix following my earlier inline note about two-layer optspec drift; the current shape eliminates that class structurally. -A '-*' correctly stops top-level option matching at the first positional so subcommand-specific short flags (bun add -d) aren't claimed as --define.

Other factors

This PR has been through many rounds of inline review from this bot on 2026-08-01, each addressed with a fix commit (2e4cd1e/2f7c4ab/4f5e509/f44ee4a/…/23e8008) and every thread is marked resolved. The mass of comment-cop github-actions comments dated 2026-08-13 target unrelated src/**/*.rs files that are not in this PR's diff and are all resolved — they appear to be a bot misfire during a rebase and don't bear on this change. No CODEOWNERS entry covers completions/. The new test is hermetic, concurrent, gated on shell availability (bash ≥ 4, fish/zsh on PATH), and drives bun completions piped output so it exercises the embedded bytes rather than the checked-out files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants