Skip to content

feat(cli): Name filter for project, team, repo, monitor and integration lists - #1575

Open
sentry-junior[bot] wants to merge 13 commits into
mainfrom
feat/cli-list-name-filter
Open

sentry-junior[bot] wants to merge 13 commits into
mainfrom
feat/cli-list-name-filter

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Brings sentry org list [filter]'s name filter (glob, or fuzzy text through filterByName) to the other org-scoped list commands. It's an optional second positional, following the sentry dashboard list [<org/project>] [<name-glob>] pattern. Refs #1554, from this review thread.

sentry project list my-org/ 'web-*'   # glob on slug or name
sentry team list my-org/ backnd       # fuzzy: finds "backend"
sentry project list 'web-*'           # lone glob: auto-detected org(s)
sentry repo list my-org/ '*-sdk'      # * also matches / (repo names are owner/name)

How it works

  • OrgListConfig.namesOf opts a buildOrgListCommand command in. That covers team, repo, monitor, and integration list. With a filter, the default handlers wrap the config's fetchers with filterByName. entityPlural becomes teams matching 'x', so progress, empty-result, and count messages say what was matched.
  • project list overrides every dispatch mode, so it gets the filter as ctx.filter and applies it in its own handlers, together with --platform.
  • Pagination: matching and fuzzy ranking need the whole set. With a filter, <org>/ fetches every page instead of one cursor page, and --cursor with a filter is rejected with a ValidationError. --limit applies to the matches, as in org list. Tips that point at the unfiltered paginated <org>/ listing are left out when a filter is set.
  • Argument grammar: splitListTargetAndFilter treats 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.
  • Glob * now also matches / (picomatch bash: true). Names aren't paths, and repository names are owner/name.

Not in this PR: dashboard list still uses its own glob-only title filter, applied while it pages through server results. Moving it to filterByName would 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, --cursor rejection, ctx.filter for overrides), project list end to end (glob, fuzzy, <org>/<glob>, --platform, --limit, empty hint, --cursor), and team list wiring. tsc, oxlint, and oxfmt are clean. Generated command docs and skills are regenerated. The full local test/lib + test/commands run fails only in tests that also fail locally without this change (sandbox permissions and the require-shim loader); the check-* scripts pass when run directly.

via BYK.

--

View Junior Session [Sentry]

@vercel

vercel Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
sentry-local Skipped Skipped Oct 27, 2026 3:14pm UTC

Request Review

@github-actions github-actions Bot added the risk: medium PR risk score: medium label Oct 21, 2026

@cursor cursor 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.

Stale Bugbot comment from a previous run.

Comment thread packages/cli/src/lib/org-list.ts Outdated
Comment thread packages/cli/src/lib/org-list.ts Outdated
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 <burak.kaya@sentry.io>
sentry-junior Bot and others added 2 commits October 23, 2026 23:18
…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 <burak.kaya@sentry.io>

Co-Authored-By: BYK <burak.kaya@sentry.io>
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 <burak.kaya@sentry.io>
@sentry-junior
sentry-junior Bot force-pushed the feat/cli-list-name-filter branch from 1e97a53 to 4b33e4b Compare October 23, 2026 23:19

@cursor cursor 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.

Stale Bugbot comment from a previous run.

Comment thread packages/cli/src/lib/org-list.ts
Auto-detect tips (`<cmd> <org>/`) and project list's JSON pagination
hint dropped the name filter, so following them listed a different set.
Comment thread packages/cli/src/commands/integration/list.ts Outdated
Comment thread packages/cli/src/commands/project/list.ts Outdated
Comment thread packages/cli/src/commands/project/list.ts Outdated
Comment thread packages/cli/src/lib/org-list.ts
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 <burak.kaya@sentry.io>

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ 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.

Comment thread packages/cli/src/commands/project/list.ts
Comment thread packages/cli/src/lib/org-list.ts
- 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.
Comment thread packages/cli/src/commands/integration/list.ts Outdated
Comment thread packages/cli/src/commands/project/list.ts
Comment thread packages/cli/src/commands/project/list.ts
Comment thread packages/cli/src/commands/project/list.ts Outdated
- 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.
Comment thread packages/cli/src/commands/repo/list.ts Outdated
Comment thread packages/cli/src/lib/list-filter.ts Outdated
- 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.
Comment thread packages/cli/src/lib/org-list.ts
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 <burak.kaya@sentry.io>
…lter

# Conflicts:
#	packages/cli/plugins/sentry-cli/skills/sentry-cli/references/project.md
#	packages/cli/src/commands/integration/list.ts
#	packages/cli/src/commands/monitor/list.ts
#	packages/cli/src/commands/project/list.ts
#	packages/cli/src/commands/repo/list.ts
#	packages/cli/src/commands/team/list.ts
#	packages/cli/src/lib/list-command.ts

Co-Authored-By: BYK <burak.kaya@sentry.io>
Comment thread packages/cli/plugins/sentry-cli/skills/sentry-cli/references/project.md Outdated
Comment thread packages/cli/src/lib/list-filter.ts Outdated
Per review, list commands no longer show a separate `[<filter>]`
positional that really applied to the target. The one `<org/project>`
target decides automatically:

- `<org>/` lists everything; a glob (`<org>/web-*`, `web-*`) filters
  names.
- `<org>/<name>` is a project first, else matching names (as before).
- A bare `<name>` that is no project now matches names in the detected
  orgs (teams, repos, monitors, integrations) instead of failing.

Hints build `<org>/<filter>` targets for text too. The plural-alias
argument shift and its e2e test go away with the second positional.

Co-Authored-By: BYK <burak.kaya@sentry.io>
The target positional of team list now accepts names as well as
projects, so its brief comes from NAMED_LIST_TARGET_BRIEF.
Comment thread packages/cli/src/commands/project/list.ts Outdated
When a name filter and --platform together match nothing, auto-detect
blamed only the name filter. Report the platform too, like the
org-wide filtered path does.

@BYK BYK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm also surprised not seeing this glob syntax being reflected on the peggyjs parser itself.

Am I approaching this problem from the wrong angle?

Comment thread packages/cli/src/commands/project/list.ts Outdated
The org/project target grammar now owns the glob syntax: with an
isNameFilter hook, [<org>/]<glob> parses as a name filter instead of a
project. parseListTarget() passes isGlobFilter as that hook and replaces
splitListTarget(), which split the raw string before parsing. Other
commands parse targets as before. The target syntax table in the docs
gains the glob forms from their @Form lines.

This branch was previously deployed

1 inactive deployment
Preview — fd3ff028 Deployed Oct 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant