ref(cli): replace argv-hoist preprocessor with a Stricli top-level-flags patch - #1340
Conversation
…ags patch
Global flags placed before the subcommand (`sentry --verbose issue list`)
used to be relocated to the tail of argv by the `argv-hoist.ts` preprocessor,
because Stricli only parses flags at the leaf command and treats a global flag
in a route position as an unknown subcommand.
This teaches Stricli's route scanner about a fixed allow-list of Sentry global
flags via the existing `@stricli/core` patch (same mechanism as the `-H`
removal), so `buildRouteScanner` recognizes `--verbose`, `--json`, `--org`,
`--project`, `--log-level`, `--fields` (and the `-v` alias, plus `=`-inline and
value forms) at any route depth and forwards them to the leaf command instead
of failing route resolution. The patch also drops Stricli's built-in
`-v`=version alias so `-v` stays the CLI's `--verbose` alias at every position;
`--version` remains the version flag.
`argv-hoist.ts` is gone. The two transforms Stricli can't do at arbitrary route
depth remain as thin app-boundary glue in `argv-glue.ts`: `--version`
normalization (Stricli only prints it at argv[0]) and the `--help --json`
rewrite to the `help` command (Stricli intercepts `--help` and ignores `--json`).
- Regenerate the `@stricli/core` patch (both dist/index.{js,cjs}).
- Guard the new patch effects in `check:patches` (requiredMarker support).
- Replace argv-hoist tests with argv-glue unit tests + a run(app,...)
integration suite covering global flags at depth, value-flag consumption,
the `-v` regression, and `--` escape passthrough.
Closes #1339
|
|
marked ready — full CI is green (Build: lint/typecheck, unit + e2e tests, binary + npm smoke builds; plus Warden, Semgrep, CodeQL, Socket, dependency review). @BYK — flagging the two deliberate calls for your read: the top-level-flag token set is hardcoded in the |
Without argv hoisting, global flags can sit between `cli` and its subcommand (`sentry cli --verbose setup`), so the positional `args[1]` check in shouldSuppressNotification no longer sees `setup`/`fix` and update notifications leak into those management commands. Resolve the subcommand by skipping global flags (and value-flag values) after `cli`. Flagged by Cursor Bugbot on #1340.
shouldSuppressNotification only matched `cli` at args[0], so global flags placed before the command (`sentry --verbose cli setup`) — now possible since flags are no longer hoisted — leaked update notifications into management commands. Locate the `cli` group past leading global flags, and stop rewriteHelpJsonRequest at `--` without discarding `--help --json` already seen before it. Flagged by Cursor Bugbot and Seer on #1340.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 388f884. Configure here.
The top-level-flags scanner patch latched the next token as a value for value-taking global flags (`--org`, `--fields`, `--log-level`, `--project`) *before* the --help/-h interception. So `sentry --org --help` (value flag given without a value) consumed --help as the org value and help never fired. Skip the latch for help tokens so they fall through to the help handler, matching Stricli's leaf parser, which never consumes a following flag as a value. Flagged by Cursor Bugbot on #1340.
|
Jared, you need to make this a generic patch: something we can send as a PR upstream. I see |
… option Rework the @stricli/core route-scanner patch so the top-level (global) flags allow-list is passed in via a new `scanner.topLevelFlags` config option instead of hardcoding SENTRY_TOP_LEVEL_*_FLAGS in the patch. The scanner now reads the allow-list (booleanFlags/valueFlags sets) from config and is inert when unset, keeping stock Stricli behavior unchanged — this makes the top-level-flags feature upstreamable. The Sentry CLI supplies the allow-list from GLOBAL_FLAGS via buildTopLevelFlags() wired into app.ts's scanner config, so adding a global flag stays a one-line change. Adds the topLevelFlags field to ScannerConfiguration in the patched .d.ts, updates check:patches to assert matchTopLevelFlag, and adds a test pinning the derivation contract. Addresses review feedback from @BYK on #1340.
|
reworked the patch to be generic/upstreamable in 3b43098. the scanner no longer hardcodes the Sentry-specific allow-list is now supplied at init: the |
|
Good job Jared. What would it take for us to get rid of Btw you gotta fix that CI |
|
thanks! CI: the E2E failure was infra flake, not code — dropping
Adding either to the patch would make it less upstreamable, which cuts against the goal of the rework. |
My request here is not to teach Stricli about
|
|
got it — you're right, both of these are top-level flags in the new model, and the clean version is a scanner intercept hook, not argv string-scanning. concrete design:
Net result: I'd land this PR as-is and do the intercept-hook as a focused follow-up rather than fold a core-dispatch redesign into an already high-risk PR — it touches the scanner + |
…-glue Follow-up to #1340. Now that the @stricli/core patch carries a generic `scanner.topLevelFlags` allow-list, the two remaining app-boundary transforms in `argv-glue.ts` move into the scanner so the module can go away — `--version` and `--help` are themselves top-level flags. - Scanner: add a `versionRequested` state that trips on `--version` at any route depth (mirrors `helpRequested`), and print the version from `runApplication` after `scanner.finish()` instead of only checking `inputs[0]`. Deletes `isVersionRequest`. - Documentation: add a pluggable `renderHelp(ctx)` hook in the help-dispatch branch. The app's hook (`renderJsonHelp`) detects `--json` in the forwarded top-level flags and renders structured help via the existing `introspectAllCommands`/`introspectCommand` path; otherwise it falls back to Stricli's built-in text `formatHelp`. Deletes `rewriteHelpJsonRequest` and the `--help --json` → `help` reroute. The hook is upstreamable — Stricli gains a pluggable help renderer, not knowledge of `--json`. - Delete `argv-glue.ts`/`preprocessArgv`; `cli.ts` calls `run(app, ...)` directly. `shouldSuppressNotification`/`getErrorUpdateNotification` read the raw args, which still contain `--version`/`--json` tokens, so they need no change. - Patch: `versionRequested` scanner state, version print from finish(), `renderHelp` hook, and the `renderHelp` type on `DocumentationConfiguration`. `check-patches.ts` gains required markers for both. - Tests: replace `argv-glue.test.ts` with a `renderJsonHelp` unit suite and extend the (renamed) `scanner-flags.integration.test.ts` with `--version`-at-depth and `--help --json` cases. Fixes #1347
…-glue (#1348) Follow-up to #1340. Moves the last two app-boundary transforms out of `argv-glue.ts` and into the patched Stricli scanner, then deletes the module — `--version` and `--help` are themselves top-level flags, so they belong in the scanner, not an argv string-scan before dispatch. - **`--version` at any depth**: the scanner tracks a `versionRequested` state (mirroring `helpRequested`); `runApplication` prints the version from `scanner.finish()` instead of only checking `inputs[0]`. Deletes `isVersionRequest`. - **Pluggable help renderer**: a new `documentation.renderHelp(ctx)` hook runs in the help-dispatch branch. The app's hook (`renderJsonHelp`) detects `--json` in the forwarded top-level flags and renders structured help via the existing `introspectAllCommands`/`introspectCommand` path; otherwise it falls back to Stricli's built-in text help. Deletes `rewriteHelpJsonRequest` and the `--help --json` → `help` reroute. Upstreamable — Stricli gains a pluggable renderer, not knowledge of `--json`. - `argv-glue.ts`/`preprocessArgv` deleted; `cli.ts` calls `run(app, cliArgs, …)` directly. `shouldSuppressNotification`/`getErrorUpdateNotification` read the raw args, which still contain the `--version`/`--json` tokens, so they need no change. **Behavior note:** `sentry <cmd> --help --json <unknown>` now emits a JSON error object with exit 0 (the help-dispatch path returns success), where the old `help`-command reroute exited non-zero. The `--help`/`--version` success paths are unchanged. ## Testing - `pnpm run check:patches` — clean (new required markers for `versionRequested` + `renderHelp`) - `tsc --noEmit` — clean - `biome check` on changed files — clean - `vitest run` scanner-flags.integration, help-json, help, global-flags, version-check, commands/help (110 passed); e2e `library` `--version` (passed through the real built bundle) Closes #1347 <!-- ## Plan Root cause / scope: #1340 turned the top-level-flags allow-list into a generic `scanner.topLevelFlags` option. `--help`/`--version` are the same concept, so the two remaining transforms in argv-glue (`isVersionRequest` normalization and the `--help --json` -> help reroute) move into the patch, letting the module be deleted. Patch (@stricli/core@1.2.8, ESM+CJS+d.ts/d.cts): 1. buildRouteScanner: add `versionRequested`; trip it on `--version` at any depth in the same block as `--help`/`--helpAll`; add `--version` to the `expectTopLevelFlagValue` exclusion list so `--org --version` prints the version; return `versionRequested` from finish(). 2. runApplication: drop the pre-scanner `inputs[0] === "--version"` check; after finish(), print the version when `config.versionInfo && result.versionRequested`. 3. runApplication help branch: call `config.documentation.renderHelp?.({ prefix, unprocessedInputs, helpRequested })`; a returned string is written and wins, undefined falls through to formatHelp. 4. withDefaults: carry `documentation.renderHelp` through; add `renderHelp` to the `DocumentationConfiguration` type. 5. check-patches.ts: requiredMarker `versionRequested` + `renderHelp` (ESM+CJS). App wiring: - app.ts: add `documentation.renderHelp`. - lib/help.ts: `renderJsonHelp(prefix, unprocessedInputs)` — parse `--json`/`--fields` from the forwarded flags (stopping at `--`), drop the app name from prefix, introspect the full tree or the command, apply `filterFields` for `--fields`, return `formatJson(...) + "\n"`; undefined when no `--json`. - cli.ts: remove preprocessArgv import + normalizedArgs; use raw cliArgs. - global-flags.ts: drop the argv-glue reference in the module doc. Tests: delete argv-glue.test.ts; add help-json.test.ts (renderJsonHelp unit); rename argv-glue.integration -> scanner-flags.integration and add --version-at-depth + --help --json cases. --> --------- Co-authored-by: jared-outpost[bot] <jared-outpost[bot]@users.noreply.github.com>

Global flags before the subcommand (
sentry --verbose issue list) used to be relocated to the tail of argv byargv-hoist.ts, because Stricli only parses flags at the leaf command and treats a global flag in a route position as an unknown subcommand.This teaches Stricli's route scanner about a fixed allow-list of Sentry global flags via the existing
@stricli/corepatch (same mechanism as the-Hremoval), sobuildRouteScannerrecognizes--verbose,--json,--org,--project,--log-level,--fields(and the-valias, plus=-inline/value forms) at any route depth and forwards them to the leaf command instead of failing route resolution. The patch also drops Stricli's built-in-v=version alias so-vstays the CLI's--verbosealias at every position;--versionremains the version flag.argv-hoist.tsis deleted. The two transforms Stricli can't do at arbitrary route depth stay as thin app-boundary glue inargv-glue.ts:--versionnormalization (Stricli only prints it at argv[0]) and the--help --jsonrewrite to thehelpcommand (Stricli intercepts--helpand ignores--json).Scope note: the last checklist item from the issue — upstreaming the top-level-flags behavior to Stricli — is intentionally left out of this PR; the local patch stays until/unless accepted.
Testing
pnpm exec vitest run test/lib/argv-glue.test.ts test/lib/argv-glue.integration.test.ts test/commands/help.test.ts test/commands/bash-hook.test.ts(77 passed)pnpm run check:patches,tsc --noEmit,biome checkon changed files — all cleanrun(app, preprocessArgv(argv)):issue list --help --jsonand--help --jsonemit structured JSON;cli --versionand--versionprint the version;--verbose/--org/-vbefore or between route segments reach the leaf;-- passthruis not consumed as a global flagCloses #1339