Repository navigation
feat(cli): Complete flags at every command depth in bash, zsh, and fish - #1760
Merged
Merged
Conversation
The generated shell scripts only knew the first two command levels, so flag names for nested commands (`alert issues list --<TAB>`) completed nothing, zsh completed no paths, and fish leaked a group's subcommands into later words. Bash also added a space after a directory and inserted names with spaces unescaped. `__complete` now answers flag names: a partial starting with `-` gets the resolved command's visible flags and aliases, with their briefs, minus those already given unless they are variadic. The generator emits each command's visible flags for it. The three scripts keep their static command and subcommand lists and ask `__complete` for every later word, so all shells share one tested code path. Bash escapes suggestions with `printf %q` and sets `nospace` after a single `/` suggestion; zsh adds `/` suggestions with `-S ''` and unquotes words before passing them on; fish unescapes the current token. Fish descriptions no longer show backslashes before parentheses.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
BYK
approved these changes
Oct 30, 2026
BYK
enabled auto-merge (squash)
October 30, 2026 21:15
BYK
added a commit
that referenced
this pull request
Oct 31, 2026
Shell completion runs `sentry __complete` on every TAB press, and since #1760 also for every flag name in bash and fish. Each call was paying for most of the CLI before it printed anything. This addresses the review thread on #1751 (`complete.ts`). **Where the time went** - The npm bin required the whole 7.6 MB `dist/index.cjs` just to reach the completion branch in `cli.ts`. - The completion engine's eager import graph still reached `@sentry/node`, even with telemetry off. `db/auth.ts` and `db/user.ts` imported `withDbSpan` from `telemetry.ts`, the cache modules imported `recordCacheHit`, and `logger.ts` imported `createConsolaReporter`. That graph was 659 modules: Sentry SDK, OpenTelemetry, and the API client. The completers themselves cost under 10 ms. **What changed** - `src/bin.ts` and `dist/bin.cjs` check for `__complete` first. They call `runCompletion` in `src/lib/complete.ts` without loading `cli.ts`. The npm package ships that path as a separate `dist/complete.cjs` (322 KB). The SEA binary takes it as a lazily evaluated chunk of its single bundle. - The database modules now get `withDbSpan` and `recordCacheHit` from `db/index.ts`. Like the existing tracing proxy there, these require telemetry lazily and skip it when telemetry is off, which `runCompletion` sets. `initSentry` now passes the Sentry reporter to `attachSentryReporter`, so `logger.ts` no longer imports the SDK. - The eager graph is down to 40 modules. Completion output and the protocol are unchanged: I diffed the output of several cases between the old and new npm and SEA builds. **Numbers.** Median of 20 runs on Node 24 (Linux x64), against a seeded config DB: | | bare node | `issue list ""` | `issue list --` | `init ./` (directory) | |---|---|---|---|---| | npm before | 26 ms | 356 ms | 359 ms | 346 ms | | npm after | 26 ms | 67 ms | 72 ms | 76 ms | | SEA before | 28 ms | 145 ms | 148 ms | 145 ms | | SEA after | 27 ms | 97 ms | 97 ms | 99 ms | In dev (tsx), the same calls went from about 650 ms to about 315 ms; most of what's left is tsx's own startup. **Guard.** `test/script/cli-startup.test.ts` bundles `lib/complete.ts` without its deferred imports. It fails on any package outside `COMPLETION_PACKAGES`, naming the importer, or when there are more than 50 source modules. It also now covers `bin.ts`. The npm e2e suite checks that `__complete` loads `complete.cjs` and never `index.cjs`. The "Shell completion" section of `AGENTS.md` describes the rule. **What's left.** `consola` is about 15 ms of the remaining npm time, and Stricli (through `errors.ts`) about 3 ms. In the SEA binary, the rest is mostly the cost of loading the single 7.6 MB embedded script. Each of these would need a larger change (a lazy logger, a second SEA script), so they are left for follow-ups. <!-- junior-request-attribution:start --> via **BYK**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:agent%3A58bcfa0dc51d17a81b0bf4c80824fe21 --> -- [View Session in Sentry](https://sentry.sentry.io/explore/conversations/agent%3A58bcfa0dc51d17a81b0bf4c80824fe21/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: BYK <burak.kaya@sentry.io>
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows #1751. The generated completion scripts only knew two command levels. That caused four problems:
alert issues list --<TAB>,dashboard widget add --<TAB>) completed nothing.event send --level <TAB>offeredlist) and showed\(in descriptions.dist/and inserteddocs dir/unescaped.What changed
__completenow completes flag names. When the word being completed starts with-, it suggests the resolved command's visible flags and aliases, booleans included, each with its brief.--limit 5,-n 5,--limit=5), unless they're variadic.flagNamesfield. Existing fields are unchanged.__complete, so flags, values, paths, and nested subcommands come from the same TypeScript code in every shell, at any depth. The per-command flag and enum tables are gone from the bash and fish scripts.printf %q, keeping a leading~/, and removes escapes from the words it passes on. It setscompopt -o nospacewhen the only suggestion ends in/. It does not use-o default.(Q)and shows descriptions through_describe. It adds suggestions ending in/with-S '', so no space follows them.string unescapeon the current token. Each group's subcommands are offered only right after the group name.Tradeoff
In bash and fish, flag names used to come from static tables. Each flag TAB now starts a
__completeprocess, as positional values already did. On the npm bundle in this sandbox, one call took about 600 ms:node -e 0takes about 50 ms, and requiringdist/index.cjsalone takes about 420 ms. I didn't measure the SEA binary. Command and subcommand TABs stay instant because those lists are still static. If the delay bothers people, the fix is a lighter entry point for__complete, not more static tables.Verification
_init_completionstub. It runs the real CLI through asentrywrapper on PATH forissue list --,alert issues list --li,sourcemap upload danddocs\ d(escaping and no trailing space), andevent send --level.complete -C. It's skipped when fish isn't installed.docs\ dir/anddist/with no trailing space, and--limitandwarningwith one. Zsh isn't in the automated tests.Known gaps
:and=(bash-completion's default), soproject create name:plat<TAB>and--flag=<TAB>don't complete in bash.sentry issue --<TAB>) don't complete that command's flags.via BYK.
--
View Session in Sentry