Skip to content

feat(project): add project create command#237

Merged
BYK merged 37 commits intomainfrom
feat/project-create
Mar 3, 2026
Merged

feat(project): add project create command#237
BYK merged 37 commits intomainfrom
feat/project-create

Conversation

@betegon
Copy link
Member

@betegon betegon commented Feb 12, 2026

Summary

Adds sentry project create <name> <platform> — the first write command in the CLI. Follows gh repo create conventions with org/name syntax, auto-detection of org and team, and actionable errors at every step.

Changes

  • New SentryTeam Zod schema and type
  • listTeams() and createProject() API functions with region-aware routing (TEAM_ENDPOINT_REGEX)
  • project create command with two required positionals, --team/-t flag, and --json output
  • Team auto-selection when org has exactly one team; lists available teams otherwise
  • Org fallback: on 404 from bad auto-detected org, shows user's actual organizations
  • DSN fetched after creation (best-effort) so the user can start sending events immediately
  • 19 tests covering happy path, missing args, team resolution, conflict handling, and JSON output

Test Plan

  • bun test test/commands/project/create.test.ts — 19 pass, 0 fail
  • bun run typecheck — clean
  • bun run lint — clean
  • Manually tested against live API: create with auto-team, explicit team, bad team, 409 conflict, missing args, JSON mode

@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

Semver Impact of This PR

🟡 Minor (new features)

📋 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 ✨

Trace

Other

  • (api) Add --data/-d flag and auto-detect JSON body in fields by BYK in #320
  • (formatters) Render all terminal output as markdown by BYK in #297
  • (issue-list) Global limit with fair distribution, compound cursor, and richer progress by BYK in #306
  • (project) Add project create command by betegon in #237

Bug Fixes 🐛

Api

  • Use numeric project ID to avoid "not actively selected" error by betegon in #312
  • Use limit param for issues endpoint page size by BYK in #309
  • Auto-correct ':' to '=' in --field values with a warning by BYK in #302

Formatters

  • Expand streaming table to fill terminal width by betegon in #314
  • Fix HTML entities and escaped underscores in table output by betegon in #313

Setup

  • Suppress agent skills and welcome messages on upgrade by BYK in #328
  • Suppress shell completion messages on upgrade by BYK in #326

Other

  • (ci) Generate JUnit XML to silence codecov-action warnings by BYK in #300
  • (nightly) Push to GHCR from artifacts dir so layer titles are bare filenames by BYK in #301
  • (region) Resolve DSN org prefix at resolution layer by BYK in #316
  • (test) Handle 0/-0 in getComparator anti-symmetry property test by BYK in #308
  • (trace-logs) Timestamp_precise is a number, not a string by BYK in #323

Internal Changes 🔧

Api

  • Upgrade @sentry/api to 0.21.0, remove raw HTTP pagination workarounds by BYK in #321
  • Wire listIssuesPaginated through @sentry/api SDK for type safety by BYK in #310

Other

  • (craft) Add sentry-release-registry target by BYK in #325

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 13, 2026

Codecov Results 📊

2463 passed | Total: 2463 | Pass Rate: 100% | Execution Time: 0ms

📊 Comparison with Base Branch

Metric Change
Total Tests 📈 +29
Passed Tests 📈 +29
Failed Tests
Skipped Tests

All tests are passing successfully.

❌ Patch coverage is 79.82%. Project has 3226 uncovered lines.
❌ Project coverage is 81.19%. Comparing base (base) to head (head).

Files with missing lines (5)
File Patch % Lines
api-client.ts 73.33% ⚠️ 256 Missing
resolve-team.ts 78.26% ⚠️ 25 Missing
view.ts 88.89% ⚠️ 23 Missing
create.ts 95.70% ⚠️ 11 Missing
output.ts 90.00% ⚠️ 3 Missing
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    81.23%    81.19%    -0.04%
==========================================
  Files          121       123        +2
  Lines        16711     17149      +438
  Branches         0         0         —
==========================================
+ Hits         13574     13923      +349
- Misses        3137      3226       +89
- Partials         0         0         —

Generated by Codecov Action

@betegon betegon requested a review from BYK February 13, 2026 19:46
Copy link
Member

@BYK BYK left a comment

Choose a reason for hiding this comment

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

Love that you both try to infer team and org automatically while supporting org/proj --team <team> syntax too.

See my comments and decide whether you wanna merge as it is or not. My 2 major concerns are the refactor (I may merge that before you see this) and the error when we find more than 1 team.

betegon and others added 17 commits February 26, 2026 22:25
Add team schema/type and two new API functions needed for project
creation. Also adds TEAM_ENDPOINT_REGEX so /teams/{org}/... endpoints
route to the correct region.
Adds `sentry project create <name> <platform> [--team] [--json]`.

Supports org/name syntax (like gh repo create owner/repo), auto-detects
org from config/DSN, and auto-selects team when the org has exactly one.
Fetches the DSN after creation so users can start sending events
immediately. All error paths are actionable — wrong org lists your orgs,
wrong team lists available teams, 409 links to the existing project.
When the API returns 400 for an invalid platform string, show the same
helpful platform list instead of a raw JSON error body.
Replace the confusing 'Or: - Available platforms:' pattern with a
cleaner 'Usage: ... Available platforms:' layout. Applies to both
missing platform and invalid platform errors.
- tryGetPrimaryDsn() → api-client.ts (was duplicated in view + create)
- resolveTeam() → resolve-team.ts (reusable for future team-dependent commands)
- parseOrgPrefixedArg() → arg-parsing.ts (reusable org/name parsing)
- writeKeyValue() for aligned key-value output in create.ts
- project/view.ts now uses shared tryGetPrimaryDsn instead of local copy
The /teams/{org}/{team}/projects/ endpoint returns 404 for both a bad
org and a bad team. Previously we always blamed the team, which was
misleading when --team was explicit and the org was auto-detected wrong.

Now on 404 we call listTeams(orgSlug) to check:
- If it succeeds → team is wrong, show available teams
- If it fails → org is wrong, show user's actual organizations

Only adds an API call on the error path, never on the happy path.
The view command hint on 409 used the raw name ('My Cool App') instead
of the expected slug ('my-cool-app'), pointing to a non-existent target.
handleCreateProject404 was treating any listTeams failure as proof that
the org doesn't exist. Now it checks the status code: only 404 triggers
'Organization not found'. Other failures (403, 5xx, network) get a
generic message that doesn't misdiagnose the root cause.
Same class of bug as the previous fix in handleCreateProject404:
resolveTeam was routing all ApiErrors from listTeams into the 'org not
found' path. Now only 404 triggers that diagnosis. Other failures
(403, 5xx) get a generic message that doesn't misdiagnose the cause.
- Extract shared fetchOrgListHint() in resolve-team.ts to deduplicate
  org-list fetching logic (used by both resolve-team and create 404 handler)
- Use Writer type instead of inline { write } in writeKeyValue
- Simplify Awaited<ReturnType<typeof listTeams>> to SentryTeam[]
- Add fragility comment to isPlatformError (relies on API message wording)
- Fix test import to use barrel (types/index.js)
When a project slug is already taken, Sentry silently appends a random
suffix (e.g., 'test1' becomes 'test1-0g'). This was confusing because
the user had no indication why the slug differed from the name.

Now shows: Note: Slug 'test1-0g' was assigned because 'test1' is already taken.
Previously, project create errored whenever an org had 2+ teams,
requiring --team in every non-trivial org. Now filters teams by
isMember and auto-selects when the user belongs to exactly one team.

When multiple member teams exist, only those are shown in the error
(not all org teams). Falls back to the full list when isMember data
is unavailable (self-hosted, old API).
…ists

handleCreateProject404 now checks if the teamSlug is actually present
in the returned teams list before claiming it's not found. When the team
exists (e.g., auto-selected by resolveTeam), the error correctly reports
a permission issue instead of the contradictory message.
Add NFKD normalization to handle accented characters and ligatures,
and preserve underscores to match Sentry's MIXED_SLUG_PATTERN.
Remove team view command and getTeam API function that were accidentally
included in this branch. The code imported a non-existent buildTeamUrl,
breaking typecheck. This work is preserved on feat/team-view-command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Re-throw AuthError in handleCreateProject404 so auth errors propagate
instead of being swallowed. Guard writeKeyValue against empty pairs to
avoid Math.max(...[]) returning -Infinity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace `await` with `return` on `handleCreateProject404` and
`buildOrgFailureError` calls so the `never` control flow is explicit
and subsequent code is clearly unreachable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@betegon betegon requested a review from BYK February 27, 2026 12:44
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
betegon and others added 3 commits March 2, 2026 16:21
…Error

ContextError means the user omitted a required value ("Organization is
required."), but buildOrgFailureError is reached when the org slug was
provided and returned a 404. Switch to ResolutionError so the message
reads "Organization 'my-org' not found." which matches the actual
scenario. Also drops the leaked internal detail about listTeams status.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BYK added 2 commits March 3, 2026 15:46
…olveEffectiveOrg, UX improvements

- resolveTeam now returns ResolvedTeam with source info (explicit/auto-selected/auto-created)
- Auto-create team when org has zero teams (mirrors Sentry UI behavior)
- Add createTeam() to api-client using @sentry/api SDK
- Use resolveEffectiveOrg for DSN org ID resolution on 404
- Show team notes in output when not explicitly specified
- Replace unhelpful docs.sentry.io/platforms link with inline usage hint
- Add team slug to JSON output
- Document 5-step flow and 6-step team resolution in JSDoc
- Update tests: auto-create team test replaces error-on-no-teams test
- Rename resolveTeam → resolveOrCreateTeam (BYK review)
- Add current user as team member on auto-create via addMemberToTeam('me')
- Add addMemberToTeam() API function using @sentry/api SDK
- Fix missing await on return handleCreateProject404 (BugBot)
- Fix usageHint.replace no-op — use direct string interpolation (BugBot)
- Re-throw AuthError in autoCreateTeam to preserve auto-login flow (BugBot)
- Fix team notes: remove misleading 'Change with' command
  - Auto-created: just show 'org had no teams' note
  - Auto-selected: suggest 'sentry team list' instead of incorrect command
- Update test comments to match renamed function
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.

…apture Sentry exception

- Deduplicate org-not-found handling: extract buildOrgNotFoundError from
  resolve-team.ts and reuse in handleCreateProject404 instead of duplicating
  with inconsistent behavior (BugBot Thread A)
- Fix JSON output overwriting API team object: rename 'team' to 'teamSlug'
  so spreading the project response preserves the API's team object (BugBot Thread B)
- Capture Sentry exception and warn user when auto-add to team fails,
  instead of silently swallowing the error (BYK Thread C)
- Un-export fetchOrgListHint (now internal to resolve-team.ts)
- Add biome-ignore for Sentry namespace import in api-client.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.

…ct404

Pass resolved.detectedFrom through createProjectWithErrors to
handleCreateProject404 so buildOrgNotFoundError includes 'Org was
auto-detected from...' context when the org came from DSN detection.

Refactored both handleCreateProject404 and createProjectWithErrors to
use options objects to stay within the 4-parameter lint limit.
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.

buildOrgNotFoundError was appending '--team <team-slug>' to the usage
hint even though the error is about the org not being found. The user
needs to fix the org, not the team. Now uses the base usage hint as-is.
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 c6f5be0 into main Mar 3, 2026
20 checks passed
@BYK BYK deleted the feat/project-create branch March 3, 2026 23:21
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.

3 participants