Skip to content

fix(project-list): add pagination and flexible target parsing#221

Merged
BYK merged 30 commits intomainfrom
fix/project-list-pagination
Feb 17, 2026
Merged

fix(project-list): add pagination and flexible target parsing#221
BYK merged 30 commits intomainfrom
fix/project-list-pagination

Conversation

@BYK
Copy link
Member

@BYK BYK commented Feb 10, 2026

Summary

Fixes three bugs in sentry project list:

  • No pagination — showed "displaying 30 out of 100 projects" with no way to see the rest. Added --cursor/-c flag with a last magic value that resumes from where you left off.
  • API pagination not followed — setting -n 200 still capped at 100 because the Sentry API paginates and we never followed cursor links. Added orgScopedPaginateAll() so listProjects() transparently fetches all pages.
  • project list sentry/sentry said "No projects found in organization 'sentry/sentry'" — now uses parseOrgProjectArg() (same as issue list) to handle org/project, org/, and bare-word targets.

Changes

src/lib/api-client.ts — Pagination infrastructure

  • PaginatedResponse<T> type, parseLinkHeader(), orgScopedRequestPaginated(), orgScopedPaginateAll()
  • listProjects() now auto-paginates transparently (all 6 callers unchanged)
  • New listProjectsPaginated() for explicit single-page fetches with cursor control

src/commands/project/list.ts — Rewritten command

  • 4 target modes: auto-detect, explicit (org/project), org-all (org/), project-search (bare slug)
  • --cursor/-c flag with last magic value for easy page navigation
  • JSON output returns { data, nextCursor?, hasMore } in paginated mode

src/lib/db/schema.ts — Schema v6

  • Added pagination_cursors table for cursor caching

src/lib/db/pagination.ts — New module

  • Cursor cache with TTL (default 5 min) and context validation

Tests

  • Updated test/e2e/project.test.ts and test/e2e/multiregion.test.ts for new org/ syntax and paginated JSON format

@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 2026

Semver Impact of This PR

🟢 Patch (bug fixes)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Build

  • Add hole-punch tool to reduce compressed binary size by BYK in #245
  • Add gzip-compressed binary downloads by BYK in #244

Other

  • (args) Parse Sentry web URLs as CLI arguments by BYK in #252

Bug Fixes 🐛

Telemetry

  • Reduce noise from version-check JSON parse errors by BYK in #253
  • Skip Sentry reporting for 4xx API errors by BYK in #251
  • Handle EPIPE errors from piped stdout gracefully by BYK in #250
  • Upgrade Sentry SDK to 10.39.0 and remove custom patches by BYK in #249

Other

  • (db) Handle readonly database gracefully instead of crashing by betegon in #235
  • (polyfill) Add exited promise and stdin to Bun.spawn Node.js polyfill by BYK in #248
  • (project-list) Add pagination and flexible target parsing by BYK in #221
  • (upgrade) Remove v prefix from release URLs and work around Bun.write streaming bug by BYK in #243

Internal Changes 🔧

  • (build) Replace local hole-punch script with binpunch package by BYK in #246
  • Use @sentry/api client for requests by MathurAditya724 in #226

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 2026

Codecov Results 📊

✅ Patch coverage is 86.87%. Project has 3963 uncovered lines.
✅ Project coverage is 71.21%. Comparing base (base) to head (head).

Files with missing lines (71)
File Patch % Lines
human.ts 58.29% ⚠️ 395 Missing
list.ts 14.39% ⚠️ 345 Missing
resolve-target.ts 25.17% ⚠️ 327 Missing
api-client.ts 66.62% ⚠️ 251 Missing
list.ts 23.47% ⚠️ 212 Missing
oauth.ts 30.94% ⚠️ 183 Missing
plan.ts 19.37% ⚠️ 154 Missing
resolver.ts 3.23% ⚠️ 120 Missing
help.ts 19.85% ⚠️ 109 Missing
upgrade.ts 61.37% ⚠️ 107 Missing
view.ts 40.23% ⚠️ 104 Missing
interactive-login.ts 9.17% ⚠️ 99 Missing
errors.ts 5.94% ⚠️ 95 Missing
view.ts 25.81% ⚠️ 92 Missing
view.ts 39.44% ⚠️ 86 Missing
clipboard.ts 4.49% ⚠️ 85 Missing
status.ts 24.07% ⚠️ 82 Missing
migration.ts 47.44% ⚠️ 82 Missing
list.ts 27.18% ⚠️ 75 Missing
browser.ts 4.11% ⚠️ 70 Missing
login.ts 33.33% ⚠️ 64 Missing
list.ts 86.12% ⚠️ 64 Missing
span-tree.ts 5.00% ⚠️ 57 Missing
explain.ts 33.33% ⚠️ 56 Missing
api.ts 89.80% ⚠️ 47 Missing
upgrade.ts 66.91% ⚠️ 46 Missing
seer.ts 75.54% ⚠️ 45 Missing
refresh.ts 40.63% ⚠️ 38 Missing
schema.ts 90.98% ⚠️ 37 Missing
seer.ts 79.87% ⚠️ 30 Missing
preload.ts 53.23% ⚠️ 29 Missing
view.ts 87.27% ⚠️ 28 Missing
telemetry.ts 93.01% ⚠️ 27 Missing
utils.ts 88.94% ⚠️ 25 Missing
view.ts 61.54% ⚠️ 25 Missing
fix.ts 89.29% ⚠️ 24 Missing
detector.ts 90.10% ⚠️ 20 Missing
arg-parsing.ts 89.29% ⚠️ 18 Missing
binary.ts 88.67% ⚠️ 17 Missing
list.ts 91.16% ⚠️ 16 Missing
list.ts 90.70% ⚠️ 16 Missing
help.ts 57.14% ⚠️ 15 Missing
sentry-client.ts 92.17% ⚠️ 13 Missing
dsn-cache.ts 94.62% ⚠️ 12 Missing
code-scanner.ts 96.25% ⚠️ 12 Missing
logout.ts 56.00% ⚠️ 11 Missing
token.ts 52.17% ⚠️ 11 Missing
qrcode.ts 33.33% ⚠️ 10 Missing
fs-utils.ts 57.14% ⚠️ 9 Missing
view.ts 94.70% ⚠️ 7 Missing
project-root.ts 97.73% ⚠️ 7 Missing
version-check.ts 92.47% ⚠️ 7 Missing
feedback.ts 84.21% ⚠️ 6 Missing
auth.ts 95.56% ⚠️ 6 Missing
shell.ts 96.23% ⚠️ 6 Missing
app.ts 93.90% ⚠️ 5 Missing
setup.ts 97.84% ⚠️ 4 Missing
list.ts 97.33% ⚠️ 4 Missing
project-aliases.ts 97.40% ⚠️ 2 Missing
project-root-cache.ts 96.92% ⚠️ 2 Missing
output.ts 89.47% ⚠️ 2 Missing
alias.ts 99.42% ⚠️ 1 Missing
completions.ts 99.37% ⚠️ 1 Missing
index.ts 98.99% ⚠️ 1 Missing
env-file.ts 99.19% ⚠️ 1 Missing
parser.ts 98.63% ⚠️ 1 Missing
colors.ts 98.21% ⚠️ 1 Missing
trace.ts 99.16% ⚠️ 1 Missing
region.ts 97.30% ⚠️ 1 Missing
helpers.ts 97.62% ⚠️ 1 Missing
helpers.ts 94.74% ⚠️ 1 Missing
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
+ Coverage    69.12%    71.21%    +2.09%
==========================================
  Files          110       111        +1
  Lines        13362     13764      +402
  Branches         0         0         —
==========================================
+ Hits          9236      9801      +565
- Misses        4126      3963      -163
- Partials         0         0         —

Generated by Codecov Action

@BYK BYK marked this pull request as ready for review February 10, 2026 00:48
@BYK BYK force-pushed the fix/project-list-pagination branch from d09e97c to 8ca6cff Compare February 10, 2026 10:46
@BYK BYK changed the base branch from feat/profile-command to main February 10, 2026 10:46
BYK added 10 commits February 10, 2026 20:54
- Follow API cursor pagination so --limit >100 actually works
- Add org/ prefix syntax to list projects for a specific org
- Add org/project syntax to show a single project directly
- Add --cursor/--prev flags for manual page navigation
- Add pagination_cursors table (schema v5) with composite PK
- Cache cursors per (command_key, context) for independent paging
- Clear pagination cursors on auth logout
- Regenerate SKILL.md for updated command help
- Property-based tests for parseLinkHeader (RFC 5988 Link header parsing)
- Model-based tests for pagination cursor storage (composite PK, TTL, upsert)
- Unit + property tests for project list helpers (buildContextKey, filterByPlatform, resolveCursor)
- Handler tests for handleExplicit, handleOrgAll, handleProjectSearch with fetch mocking
- Verify clearAuth cross-cutting invariant clears pagination cursors
- Export parseLinkHeader and project list helpers for testability
…ONFIG_DIR

Falls back to creating a temp directory when the env var is not set,
which can happen due to test file ordering and worker isolation in CI.
…AutoDetect

- Test displayProjectTable (table formatting orchestrator)
- Test fetchOrgProjects (attaches orgSlug context)
- Test fetchOrgProjectsSafe (swallows non-auth errors, propagates AuthError)
- Test fetchAllOrgProjects (fans out across orgs, skips access errors)
- Test handleAutoDetect (auto-detect mode with platform filter, limit, JSON output)
- Export additional functions from list.ts for testability
Address review comments:
- Use single-page listProjectsPaginated for single-org auto-detect without
  platform filter, avoiding unnecessary full pagination (up to 5000 items)
- Remove no-op identity mapping in handleProjectSearch
…-duplicate ProjectWithOrg

Address review comments:
- Track hasMore from listProjectsPaginated response in auto-detect fast path
  so truncation message appears when server has more results
- Make ProjectWithOrg in list.ts private to eliminate duplicate exported type
  with conflicting orgSlug optionality (canonical export in api-client.ts)
…earch

handleProjectSearch now shows 'No project X found matching platform Y' when
projects exist but are filtered out, matching handleExplicit behavior.
Previously it misleadingly said 'No project X found in any accessible org.'
…ojects

findProjectsBySlug now calls getProject(org, slug) per org (1 API call each)
instead of listProjects which auto-paginates up to 50 pages per org.
For a user with 5 orgs, this reduces worst-case from ~250 API calls to 5.
… listProjects

Tests now mock /projects/{org}/{slug}/ (direct lookup) instead of
/organizations/{org}/projects/ (list all) to match the implementation change.
BYK added 2 commits February 13, 2026 11:17
Add compositePrimaryKey field to TableSchema so columnDefsToDDL can emit
table-level PRIMARY KEY constraints. This eliminates all special-casing
for pagination_cursors: CUSTOM_DDL_TABLES, PAGINATION_CURSORS_DDL,
repairPaginationCursorsTable, and the hand-written DDL in test files.

Net -67 lines of code removed.
…message

- Lowercase platform in buildContextKey so --platform Python and
  --platform python resolve to the same cursor cache entry
- When platform filter produces no results and there are no more pages,
  show "No projects matching platform 'X'" instead of the misleading
  "No projects found in organization"
…pollution

Three test files (version-check, install-info, project-cache) were
deleting process.env.SENTRY_CONFIG_DIR in afterEach without saving
the original value set by preload.ts. When Bun's test runner shares
a process across files, this poisons the env for subsequent tests
that capture the var at module load time.
Copy link

@cursor cursor bot left a comment

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 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

When --platform is active in org-all mode, the next-page hints now
include the flag so that following the hint preserves the filter
and matches the correct cursor context key.
Copy link

@cursor cursor bot left a comment

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.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

…orrectly

- Extract fetchPaginatedSafe() to handle non-auth API errors (403,
  network) in the single-org fast path, matching fetchOrgProjectsSafe
  behavior. Reduces handleAutoDetect complexity below lint threshold.
- Fix createIsolatedDbContext cleanup to restore original env var value
  (including undefined) instead of leaking fallback path.
- Add tests for fast-path error handling (non-auth swallowed, AuthError
  propagated).
Copy link

@cursor cursor bot left a comment

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 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

…nation-v2

# Conflicts:
#	test/lib/db/install-info.test.ts
#	test/lib/db/project-cache.test.ts
#	test/lib/version-check.test.ts
@BYK BYK force-pushed the fix/project-list-pagination branch from b576c7a to 1085d5a Compare February 13, 2026 17:13
Copy link

@cursor cursor bot left a comment

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 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

…nation-v2

# Conflicts:
#	src/lib/api-client.ts
#	src/lib/region.ts
Copy link

@cursor cursor bot left a comment

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 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

findProjectsBySlug now uses getProject (SDK retrieveAProject) per org
instead of listing all projects. The mock was returning an array for
the /projects/{org}/{project}/ endpoint, causing the SDK to parse it
incorrectly. Return 404 for non-existent project detail requests.
Copy link

@cursor cursor bot left a comment

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 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

BYK added 2 commits February 17, 2026 00:33
Remove dead fallback code for missing SENTRY_CONFIG_DIR (preload.ts
always sets it) and the 'delete process.env' pattern that violates
AGENTS.md rules.
Copy link

@cursor cursor bot left a comment

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.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

BYK added 2 commits February 17, 2026 00:54
Include the Sentry base URL in buildContextKey so cursors from
different instances (SaaS vs self-hosted) are never mixed when
multiple logins are supported.
Auto-detect mode now wraps JSON output in a {data, hasMore, hint}
envelope so JSON consumers can detect truncation. When results are
incomplete, the hint field tells consumers which paginated command
to use for full results.

Also extracted fetchAutoDetectProjects helper to reduce function
complexity.
Copy link

@cursor cursor bot left a comment

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 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

The API endpoint accepts project_id_or_slug, so numeric inputs
could resolve by ID and return a project with a different slug.
Add a post-fetch check to ensure the returned project actually
matches the requested slug.
Copy link

@cursor cursor bot left a comment

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 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

@BYK BYK merged commit 618671d into main Feb 17, 2026
23 checks passed
@BYK BYK deleted the fix/project-list-pagination branch February 17, 2026 01:15
BYK added a commit that referenced this pull request Feb 19, 2026
…commands (#262)

## Summary

Extends the pagination and flexible target parsing pattern introduced in
#221 for \`project list\` to all remaining list commands: \`issue
list\`, \`team list\`, \`repo list\`, \`trace list\`, and \`log list\`.
Also introduces a shared \`dispatchOrgScopedList\` infrastructure that
all four list commands now use.

## Changes

### Shared infrastructure

- **\`src/lib/arg-parsing.ts\`**: Added \`validateLimit(value, min,
max)\` — shared limit validation, replaces duplicate implementations in
\`trace/list.ts\` and \`log/list.ts\`
- **\`src/lib/resolve-target.ts\`**: Added \`resolveOrgsForListing()\`
(deduplicates identical logic from \`team/list.ts\` and
\`repo/list.ts\`), \`resolveOrgProjectTarget()\`, and
\`resolveOrgProjectFromArg()\` — replaces local
\`resolveTraceTarget\`/\`resolveLogTarget\` in trace/log commands
- **\`src/lib/api-client.ts\`**: Added \`listIssuesPaginated()\`,
\`listTeamsPaginated()\`, \`listRepositoriesPaginated()\`,
\`listProjectTeams()\` using \`orgScopedRequestPaginated()\` for
cursor-based pagination with Link header support
- **\`src/lib/org-list.ts\`**: New shared dispatch infrastructure —
\`ListCommandMeta\`, \`OrgListConfig\`, \`ModeHandlerMap\`,
\`dispatchOrgScopedList\` — all list commands use this
- **\`src/lib/list-command.ts\`**: Shared Stricli parameter constants
and \`buildOrgListCommand\` factory for simple org-scoped commands

### Command changes

| Command | What changed |
|---|---|
| \`issue list\` | Added \`--cursor/-c\` flag; org-all mode now uses
\`listIssuesPaginated\` and returns \`{ data, hasMore, nextCursor? }\`
in JSON output |
| \`team list\` | Full rewrite: 4-mode handler via
\`dispatchOrgScopedList\`, \`--cursor/-c\` flag, project-scoped team
listing via \`listProjectTeams\` for \`org/project\` mode |
| \`repo list\` | Full rewrite: 4-mode handler via
\`dispatchOrgScopedList\`, \`--cursor/-c\` flag, org-scoped fallback
with note for \`org/project\` mode |
| \`project list\` | Migrated to \`dispatchOrgScopedList\` with mode
overrides (no behavior change) |
| \`trace list\` | Replaced local \`resolveTraceTarget\` +
\`validateLimit\` with shared versions from \`resolve-target.ts\` /
\`arg-parsing.ts\` |
| \`log list\` | Replaced local \`resolveLogTarget\` + \`validateLimit\`
with shared versions from \`resolve-target.ts\` / \`arg-parsing.ts\` |

### Tests

- \`test/commands/team/list.test.ts\`: Covers all 4 modes including
project-scoped team listing and bare-slug project search
- \`test/commands/repo/list.test.ts\`: Covers all 4 modes including
bare-slug project search with org-scoped fallback
- \`test/lib/org-list.test.ts\`: 36 tests covering the shared dispatch
infrastructure
- \`test/e2e/issue.test.ts\`: Updated org-all JSON output assertion to
match new \`{ data, hasMore }\` shape

## Behavior notes

- **\`issue list\` org-all JSON output shape changed**: was a plain
array \`[...]\`, now \`{ data: [...], hasMore: bool, nextCursor?: string
}\` — consistent with \`project list\`
- **Target parsing convention**: org-all mode requires a trailing slash
(\`<org>/\`). A bare slug (no slash) is always treated as a project slug
and triggers a cross-org project search via \`findProjectsBySlug\` —
consistent across \`project list\`, \`issue list\`, \`team list\`, and
\`repo list\`
- **\`team list org/project\`**: fetches teams scoped to that project
(via \`listProjectTeams\` endpoint)
- **\`repo list org/project\`**: repos are org-scoped; shows all repos
in the org with a note that the project part is ignored
- All 1874 unit tests pass; typecheck and lint clean

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant