Repository navigation
feat(cli): Name filter for project, team, repo, monitor and integration lists - #1575
Open
sentry-junior[bot] wants to merge 8 commits into
Open
sentry-junior[bot] wants to merge 8 commits into
sentry-junior[bot] wants to merge 8 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
BYK
added a commit
that referenced
this pull request
Oct 23, 2026
…1594) Refs #1564. This is the first slice: the mechanism plus five commands. A `[<org>/][<project>]` positional now owns both parsing and resolution. The command picks a parser from `src/lib/target-positional.ts`, and that parser comes with its resolver: ```ts parse: orgOptionalTarget, // resolveOrgOptionalTarget parse: projectBoundTarget, // resolveProjectBoundTarget parse: orgProjectTarget(resolveViewTargets), // command-specific resolver ``` `func` gets a `LazyPositional` with three members: `target.parsed` (the `ParsedOrgProject`), `target.resolved` (a promise of the resolver's result), and `target.raw` (what the user typed): ```ts async *func(this: SentryContext, flags: ListFlags, target: OrgOptionalTarget) { const resolved = await target.resolved; ``` `func` never calls `parseOrgProjectArg` or a resolver, never picks which resolver to use, and needs no default for an omitted target. An omitted optional target arrives as auto-detect. `project view` moves its target switch into `resolveViewTargets` and reads `target.parsed.type`. - **No command names to pass.** Resolvers read the command path from `this.commandPrefix`, which Stricli's `forCommand` sets and the SDK invoker now sets from its command path. The `COMMAND_NAME` constants these commands used are gone. A context without `commandPrefix` makes resolution throw rather than print a hint with a blank command name; test mock contexts set it. - **Destructive commands guard automatically.** `buildDeleteCommand` calls `markDestructive` on its lazy positionals, and the target resolver then runs `requireExplicitTarget`. `project delete` uses plain `projectBoundTarget`. Once #1626's `destructive` flag lands, the call can move into `buildCommand` so `issue merge` and `trial start` get it too. - **Lint rule.** `sentry-cli/require-target-resolution` fails a command function that takes `OrgOptionalTarget`, `ProjectBoundTarget` or `OrgProjectTarget<R>` but never reads `.resolved`, since skipping it skips the resolver and the destructive guard. **When things run.** Both steps are lazy (`LazyPositional` in `src/lib/positional-parse.ts`, a plain class with memoized getters). The parse runs on the first `parsed` read and the resolver on the first `resolved` read; each runs once. Both happen inside `func`, so the order matches main: - The auth guard and rc URL check run first. A bad target with no login still reports "Not authenticated" (exit 10). - Parse errors keep their `CliError` semantics. `sentry project view @latest` exits 21 with the `ValidationError`, not Stricli's `Failed to parse …` (exit -4). A parse error also rejects `resolved`. - `parseOrgProjectArg`'s URL side effect still runs after those checks. For a pasted Sentry URL it calls `applySentryUrlContext`, which can switch `SENTRY_URL`/`SENTRY_HOST`, save that host as the default, or throw `HostScopeError`. Running it at Stricli scan time would have let the auth guard and rc URL check run against the host taken from the URL. Resolution stays a promise because resolvers need auth, config, DSN detection, and API calls. Shared code that handles any positional (`buildListCommand`'s plural-alias interception, `help` recovery, `setArgsContext`) reads the raw input with `rawPositional(value)`, an `instanceof LazyPositional` check. Stricli calls parsers with the command context as `this`; the resolver gets that context plus `commandName`. **Callers that skip Stricli.** The typed SDK (`buildInvoker`) and tests that call `command.loader()` pass raw strings. `buildCommand` runs `applyLazyPositionals` on them, and on omitted optional positionals, so every caller keeps passing what the user typed and `func` always gets the same shape. When plural-alias interception drops the target, `buildListCommand` runs it again so the dropped target becomes auto-detect. `sdk.project.view({ orgProject: "@latest" })` is rejected instead of silently auto-detecting. The SDK invoker also sets `directInvoke`, which turns off `help` positional recovery: a failing SDK call whose target is `help` throws its error, as on main, instead of returning help text. **Shell completion is unchanged.** Completion kinds come from a tag on the parser (`withCompletionTarget`), shared with `parseOrgTarget` and `parseOrgProjectTarget`. The generated targets file matches main. Migrated: `trace list`, `replay list`, `feedback list`, `project view`, `project delete`. Not in this PR: - `LIST_TARGET_POSITIONAL` / `buildOrgListCommand`. #1575 (`splitListTargetAndFilter`) reads the raw target and splits it before parsing (for example `acme/web-*`). These should move after it lands, using `target.raw`. - Issue and dashboard positionals. #1572 adds `parseIssueTarget` / `parseDashboardTarget` in the same files. When one of the two PRs rebases, its parsers should switch from the `PARSER_TARGETS` map to `withCompletionTarget`. - `[<org>/<project>/]<id>` positionals (`trace view`, `span list`, `event view`, `log view`, …). Stricli parses each argument on its own, but what each one means depends on how many were given. Those keep parsing in `func`. - Release `org/version` positionals. They already share `resolveReleaseTarget`. <!-- junior-request-attribution:start --> via **BYK**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:slack%3AC010884QHLG%3A1792667769.623489 --> -- [View Junior Session](https://junior-prod.sentry.dev/conversations/slack%3AC010884QHLG%3A1792667769.623489) [[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC010884QHLG%3A1792667769.623489/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: Burak Yigit Kaya <[email protected]>
…on lists Bring `sentry org list [filter]`'s name filter to the other org-scoped list commands, as an optional second positional: sentry project list my-org/ 'web-*' # glob on slug or name sentry team list my-org/ backnd # fuzzy text sentry project list 'web-*' # lone glob: auto-detected org(s) - `OrgListConfig.namesOf` opts a `buildOrgListCommand` command in (team, repo, monitor, integration); its default handlers filter each fetched set with `filterByName`. `project list` applies it in its own handlers, together with `--platform`. - Matching and fuzzy ranking need the whole set, so `<org>/` with a filter fetches every page instead of one cursor page, and `--cursor` with a filter is rejected. `--limit` applies to the matches. - `splitListTargetAndFilter` treats a lone glob (or `<org>/<glob>`) as a filter, since no slug contains glob characters. A lone bare name is still a project search. - Glob `*` now also matches `/`, so `*-sdk` matches repository names like `getsentry/sentry-python-sdk`. Refs #1554 Co-Authored-By: byk <[email protected]> Co-Authored-By: BYK <[email protected]>
A name filter on `<org>/` went through `listForOrg`, which for teams, repositories and integrations returns only the first API page, so later matches were dropped. It also used `fetchOrgSafe`, which turns a missing org, 403 or network failure into an empty "no matches" result. With a filter, the default handlers now walk every page of `listPaginated`, and org-all mode lets API errors propagate as the unfiltered `<org>/` listing does. Co-Authored-By: BYK <[email protected]>
sentry-junior
Bot
force-pushed
the
feat/cli-list-name-filter
branch
from
October 23, 2026 23:19
1e97a53 to
4b33e4b
Compare
Auto-detect tips (`<cmd> <org>/`) and project list's JSON pagination hint dropped the name filter, so following them listed a different set.
BYK
requested changes
Oct 23, 2026
This was referenced Oct 23, 2026
Review follow-ups for the list name filter: - Narrow name filters with the API's `?query=` (case-insensitive substring on name/slug) for projects, teams, repos and monitors instead of reading every page. A glob sends its longest literal part (`web-*` -> `web-`). Plain text the server finds nothing for falls back to fuzzy matching over every page. Integrations have no name search and still read every page. - `<org>/<name>` tries a direct match first, then fuzzy: project list falls back to matching project names when no such project exists; team list does the same when the project 404s; repo, monitor and integration lists match entity names and, when nothing matches, keep listing the whole org with the org-scoped note. - Document `<org>/<glob>` as the primary form and move the filter examples out of free-text `fullDescription` into `docs.examples`. Co-Authored-By: BYK <[email protected]>
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f9b38fe. Configure here.
- project list: only a 404 from the project lookup falls back to name matching; 403s, 5xx and network errors keep the plain not-found hint instead of listing other projects. - team list: when the project is missing and no team matches the name either, say both in the hint, since empty results don't render the header note.
- An `<org>/<glob>` with no matches stays empty instead of falling back to the whole org: a glob is never a project slug. - `project list <org>/<name>`: a failing fuzzy fallback (e.g. a bad org) keeps the not-found hint rather than throwing, and fuzzy matches removed by --platform say so in the hint. - Integration name filters also match the provider (`GitHub`), since `name` is the install label (e.g. a GitHub org). - Plural aliases (`sentry projects list my-org/`) shift positionals when dropping the subcommand token, so the target is not read as the new name filter.
- Repository name filters also match the name without its owner (`sentry-python` for `getsentry/sentry-python`), so a typo in the short name fuzzy-matches instead of falling back to the whole org. - A lone `/web-*` is `web-*` (a filter on the detected org) rather than target `/`, which the target grammar rejects.
The name filter ranks matches within each org or project fetch, but auto-detect and project-search concatenated those lists and applied --limit, so a weaker match from an earlier org could hide a better one from a later org. The filtered config now re-ranks merged items (by the names of the entity behind each) before the limit.
BYK
added a commit
that referenced
this pull request
Oct 24, 2026
Refs #1564. This continues #1594 with the one remaining `[<org>/][<project>]` target that nothing blocks. `explore` declared its target with `String` and called `resolveOrgOptionalFromArg` inside `func`. It now uses `orgOptionalTarget` and awaits `target.resolved`, the same way `feedback list` and `replay list` do. Resolution is unchanged: it still goes through `resolveOrgOptionalTarget` with the same command name. User-facing change: the parser carries the org/project completion tag, so `sentry explore <TAB>` now completes `org/`, `org/project` and project aliases. Before this, `explore` had no target completion. Other users of the `*FromArg` helpers are left alone on purpose: - `agent-conversation list` uses `LIST_TARGET_POSITIONAL`, which waits on #1575. - `log list`, `replay view` and `replay download` take variadic positionals. What each argument means depends on how many were given, so they keep parsing in `func`. <!-- junior-request-attribution:start --> via **BYK**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:local%3Aweb%3Aa3773d7739de1b873375a6f1 --> -- [View Junior Session in Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3Aa3773d7739de1b873375a6f1/?project=4510944073809921) <!-- junior-session-footer:end --> Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: BYK <[email protected]>
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.

Brings
sentry org list [filter]'s name filter (glob, or fuzzy text throughfilterByName) to the other org-scoped list commands. It's an optional second positional, following thesentry dashboard list [<org/project>] [<name-glob>]pattern. Refs #1554, from this review thread.How it works
OrgListConfig.namesOfopts abuildOrgListCommandcommand in. That coversteam,repo,monitor, andintegrationlist. With a filter, the default handlers wrap the config's fetchers withfilterByName.entityPluralbecomesteams matching 'x', so progress, empty-result, and count messages say what was matched.project listoverrides every dispatch mode, so it gets the filter asctx.filterand applies it in its own handlers, together with--platform.<org>/fetches every page instead of one cursor page, and--cursorwith a filter is rejected with aValidationError.--limitapplies to the matches, as inorg list. Tips that point at the unfiltered paginated<org>/listing are left out when a filter is set.splitListTargetAndFiltertreats a lone glob (web-*) or<org>/<glob>as a filter, since no slug contains glob characters. A lone bare name (sentry project list web) is still a project search, so nothing existing changes meaning.*now also matches/(picomatchbash: true). Names aren't paths, and repository names areowner/name.Not in this PR:
dashboard liststill uses its own glob-only title filter, applied while it pages through server results. Moving it tofilterByNamewould add fuzzy matching but changes its paging behaviour, so it's left for a follow-up under #1554.Testing: new tests cover
splitListTargetAndFilter, the dispatcher's filter path (fetch everything, fuzzy ranking,--limit, project scope,--cursorrejection,ctx.filterfor overrides),project listend to end (glob, fuzzy,<org>/<glob>,--platform,--limit, empty hint,--cursor), andteam listwiring.tsc,oxlint, andoxfmtare clean. Generated command docs and skills are regenerated. The full localtest/lib+test/commandsrun fails only in tests that also fail locally without this change (sandbox permissions and therequire-shimloader); thecheck-*scripts pass when run directly.via BYK.
--
View Junior Session [Sentry]