feat(project): create multiple projects in one command#1191
Conversation
…names Names are now variadic (like issue merge): `sentry project create web api worker node` creates three projects; the trailing arg is the platform, or use --platform. `create <name> <platform>` still works. When a single name has spaces and the API 400s, the error points to the variadic form instead of a cryptic HTTP 400. Fixes the cause of Sentry CLI-1YY. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Codecov Results 📊✅ Patch coverage is 92.86%. Project has 5366 uncovered lines. Files with missing lines (2)
Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
+ Coverage 81.77% 81.80% +0.03%
==========================================
Files 424 425 +1
Lines 29452 29487 +35
Branches 19190 19221 +31
==========================================
+ Hits 24085 24121 +36
- Misses 5367 5366 -1
- Partials 1999 2006 +7Generated by Codecov Action |
Match the sibling error-builders (buildPlatformError, isPlatformError) which annotate their return types. Co-authored-by: Cursor <cursoragent@cursor.com>
Pair `project create` with the CLI's multi-value flag convention (`--features a,b`, set-commits `--path a,b`, `auth login --scope a,b`): each positional name may now also be comma-separated, so `create web,api,worker node` == `create web api worker node`. Commas are never valid in a slug, so this is unambiguous and prevents an agent comma-habit (`a,b,c`) from silently creating one `a-b-c` project. Co-authored-by: Cursor <cursoragent@cursor.com>
Positionals are space-separated variadic (also comma-tolerant); optional flags are comma-separated. Splits the rule by argument type so agents pick the right shape. Co-authored-by: Cursor <cursoragent@cursor.com>
USAGE_HINT now reflects variadic names + platform ([<org>/]<name...> <platform>). The crammed-name 400 message no longer asserts names "can't span multiple words" (the 400 may be a length issue), instead framing both the multi-project and single-project fixes. Co-authored-by: Cursor <cursoragent@cursor.com>
resolvePlatformAndNames passed only args[0] to buildPlatformError, so a bad platform with multiple names (`create proj1 proj2 bad`) hinted just `proj1`. Echo the full name list per branch so the usage example matches what was typed. Co-authored-by: Cursor <cursoragent@cursor.com>
In multi-project create, the auto-create team slug is now pinned to the first project for the whole batch. A real run already creates one team on the first project and reuses it; dry-run previews now agree instead of showing a divergent "would create team" per project in an empty org. Co-authored-by: Cursor <cursoragent@cursor.com>
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 3ea97b8. Configure here.
createProjectWithAutoTeamFallback rethrew 400s unchanged, so a crammed multi-word name that reached the org-scoped fallback (after a team-scoped 403) still surfaced the raw API error. Apply the same actionable guidance there. Co-authored-by: Cursor <cursoragent@cursor.com>
- Extract projectExistsError() so the 409 "already exists" message + view hint live in one place (was duplicated across the team and org-scoped fallback paths). - Pass a field to the multi-org ValidationError so error-reporting doesn't collapse it into the unfielded fingerprint. - Use USAGE_HINT for the missing-name error instead of a stale hardcoded usage string. Co-authored-by: Cursor <cursoragent@cursor.com>
Preserve quoted display names, commas, and unrelated API 400 errors instead of inferring extra project names from content. Keep batch JSON parseable, surface partial successes, and align native/generated help with the actual platform syntax.
| ); | ||
| } | ||
| if (expError.status === 409) { | ||
| const slug = slugify(name); | ||
| throw new CliError( | ||
| `A project named '${name}' already exists in ${orgSlug}.\n\n` + | ||
| `View it: sentry project view ${orgSlug}/${slug}` | ||
| ); | ||
| throw projectExistsError(orgSlug, name); | ||
| } | ||
| } | ||
| throw expError; |
There was a problem hiding this comment.
Bug: The fallback project creation function createProjectWithAutoTeamFallback does not use the new handleCreateApiError handler, leading to inconsistent error messages compared to the primary creation path.
Severity: LOW
Suggested Fix
Wrap the API call within createProjectWithAutoTeamFallback in a try...catch block that invokes handleCreateApiError. This ensures consistent and robust error handling for platform-related API errors across both the primary and fallback creation paths.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: src/commands/project/create.ts#L307-L313
Potential issue: The new `handleCreateApiError` function, which provides improved error
messages for API 400 errors, is utilized in the primary project creation path but not in
the `createProjectWithAutoTeamFallback` function. This fallback path is executed when
the initial team-based creation fails with a 403 error. This code asymmetry results in
inconsistent error handling. If a platform-related 400 error were to occur in this
fallback scenario, the user would receive a generic error message instead of the
intended actionable guidance. While client-side validation makes this specific error
unlikely, it represents an incomplete implementation.

Summary
sentry project createcan create several projects in one command by accepting project names as variadic positional arguments. The trailing positional is the platform, or the platform can be supplied with--platform/-p.This also tightens the input and output contracts found while reviewing the original CLI-1YY scenario: quoted display names and commas are preserved as one project name, unrelated API 400 responses remain
ApiErrors, and multi-project JSON is emitted as one parseable array.Usage
The distinction is based on shell arguments, not string contents:
"Web API"is one display name and becomes a slug such asweb-api.web apiis two project names.payments,euremains one project name; commas do not split positionals.What changed
--platformas an alternative.customUsageso native help,sentry help, generated docs, and the bundled skill show the required platform syntax.Test plan
pnpm exec vitest run test/commands/project/create.test.ts test/lib/command.test.ts test/lib/introspect.test.ts test/lib/help-positional.test.ts test/script/generate-skill-markdown.test.ts— 170 passed.pnpm run typecheckpnpm run lintpnpm run check:errorspnpm run check:fragmentspnpm run check:depsThe full unit run reached 8,413 passing tests. Its remaining failures are outside this diff: the API-schema and shell-completion failures reproduce on the original PR head, timezone-sensitive cases pass with
TZ=UTC, and the untouched wizard auth test has a pre-existing async race.Related Sentry issue: CLI-1YY