Skip to content

Commit ae5a386

Browse files
betegonclaudeMathurAditya724github-actions[bot]
authored
feat(init): add init command for guided Sentry project setup (#283)
## Summary Adds `sentry init` — an AI-powered wizard that walks users through adding Sentry to their project. It detects the platform, installs the SDK, instruments the code, and configures error monitoring, tracing, and session replay. ## Changes ### Core wizard - New `init` command backed by a Mastra AI workflow (hosted at [getsentry/cli-init-api](https://github.com/getsentry/cli-init-api/)) that handles platform detection, SDK installation, and code instrumentation - ASCII banner, AI transparency note, and review reminder in the wizard UX - Tracing: unique trace IDs per wizard run with flattened span hierarchy - Python platforms use venv for isolated dependency installation - Magic values extracted into named constants (`constants.ts`) - Docs page added to cli.sentry.dev ### Security hardening - Shell metacharacter blocklist: `()` subshell bypass, `>` `<` `&` redirection/background, `$` `'` `"` `\` expansion/escaping, `{` `}` `*` `?` glob/brace expansion, `#` shell comment - Environment variable injection blocking (`VAR=value cmd` pattern) - Remote-supplied `cwd` validation against project directory - Dangerous executable blocklist and path-traversal prevention - File descriptor cleanup on `readSync` failure - Cross-platform shell execution (`{ shell: true }` instead of hardcoded `sh`) ### Performance - Pre-computed directory listing sent with first API call to skip initial `list-dir` round-trip - `_prevPhases` cross-phase caching to reuse results across workflow steps (#307) ### Testing & CI - Wizard-runner unit tests (coverage 5.94% → 99.42%) - Banner extracted to `src/lib/banner.ts` to break circular import - Mock isolation fixes (`spyOn` instead of `mock.module` where possible) - Simplified coverage pipeline (removed merge-lcov workaround) ## Eval suite The eval suite validates that the wizard produces correct, buildable Sentry instrumentation for each supported platform. It uses a **3-phase test architecture**: ### Phase 1: Wizard run Each test scaffolds a fresh project from a platform template, then runs the full `sentry init` wizard against it. The wizard output (exit code, stdout/stderr, git diff, new files) is captured for the next phases. ### Phase 2: Hard assertions (deterministic) Five code-based pass/fail checks that run without any LLM: 1. **exit-code** — wizard exits 0 2. **sdk-installed** — the Sentry SDK package appears in the dependency file (`package.json` / `requirements.txt`) 3. **init-present** — `Sentry.init` (or `sentry_sdk.init`) appears in changed or new files 4. **no-placeholder-dsn** — no leftover placeholder DSNs (`___PUBLIC_DSN___`, `YOUR_DSN_HERE`, etc.) 5. **build-succeeds** — `npm run build` / equivalent passes after the wizard's changes ### Phase 3: LLM judge (per-feature) For each feature (errors, tracing, replay, logs, profiling, etc.), an LLM judge scores correctness: - Official Sentry docs are fetched as ground truth (URLs mapped in `feature-docs.json`) - GPT-4o evaluates the wizard's diff + new files against the docs on 4 criteria: feature-initialized, correct-imports, no-syntax-errors, follows-docs - Each criterion is scored pass/fail/unknown; the overall feature score must be >= 0.5 ### Platforms 6 platform templates are covered: | Platform | Template | SDK | |----------|----------|-----| | Express | `express/` | `@sentry/node` | | Next.js | `nextjs/` | `@sentry/nextjs` | | SvelteKit | `sveltekit/` | `@sentry/sveltekit` | | React + Vite | `react-vite/` | `@sentry/react` | | Flask | `python-flask/` | `sentry-sdk` | | FastAPI | `python-fastapi/` | `sentry-sdk` | ### Running ```bash bun run test:init-eval # all platforms ``` Requires `SENTRY_AUTH_TOKEN`, `SENTRY_ORG`, `SENTRY_PROJECT`, and optionally `OPENAI_API_KEY` (LLM judge is skipped without it). ## Test Plan - [x] All init unit tests pass (124 tests across 7 files) - [x] `bun run lint` and `bun run typecheck` pass - [x] CI passes (unit tests, e2e, lint, typecheck, build) - CI workflow for eval is tracked separately in #290 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 8ce5128 commit ae5a386

79 files changed

Lines changed: 6095 additions & 71 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# dependencies (bun install)
22
node_modules
3+
package-lock.json
34

45
# output
56
out
@@ -36,6 +37,7 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
3637
.cache
3738
*.tsbuildinfo
3839
.turbo
40+
.mastra
3941

4042
# docs
4143
docs/dist

biome.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
33
"extends": ["ultracite/core"],
44
"files": {
5-
"includes": ["!docs"]
5+
"includes": ["!docs", "!test/init-eval/templates"]
66
},
77
"javascript": {
88
"globals": ["Bun"]

bun.lock

Lines changed: 410 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
---
2+
title: init
3+
description: AI-powered project setup wizard for the Sentry CLI
4+
---
5+
6+
Set up Sentry in your project with an AI-powered wizard. The `init` command detects your platform and framework, installs the Sentry SDK, and instruments your code for error monitoring, tracing, and more.
7+
8+
**Prerequisites:** You must be authenticated first. Run `sentry auth login` if you haven't already.
9+
10+
## Usage
11+
12+
```bash
13+
sentry init [directory]
14+
```
15+
16+
**Arguments:**
17+
18+
| Argument | Description |
19+
|----------|-------------|
20+
| `[directory]` | Project directory (default: current directory) |
21+
22+
**Options:**
23+
24+
| Option | Description |
25+
|--------|-------------|
26+
| `--force` | Continue even if Sentry is already installed |
27+
| `-y, --yes` | Non-interactive mode (accept defaults) |
28+
| `--dry-run` | Preview changes without applying them |
29+
| `--features <list>` | Comma-separated features: `errors`, `tracing`, `logs`, `replay`, `metrics` |
30+
31+
## Examples
32+
33+
```bash
34+
# Run the wizard in the current directory
35+
sentry init
36+
37+
# Target a subdirectory
38+
sentry init ./my-app
39+
40+
# Preview what changes would be made
41+
sentry init --dry-run
42+
43+
# Select specific features
44+
sentry init --features errors,tracing,logs
45+
46+
# Non-interactive mode (accept all defaults)
47+
sentry init --yes
48+
```
49+
50+
## What the wizard does
51+
52+
1. **Detects your framework** — scans your project files to identify the platform and framework
53+
2. **Installs the SDK** — adds the appropriate Sentry SDK package to your project
54+
3. **Instruments your code** — configures error monitoring, tracing, and any selected features
55+
56+
## Supported platforms
57+
58+
The wizard currently supports:
59+
60+
- **JavaScript / TypeScript** — Next.js, Express, SvelteKit, React
61+
- **Python** — Flask, FastAPI
62+
63+
More platforms and frameworks are coming soon.

docs/src/content/docs/getting-started.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ Credentials are stored in a SQLite database at `~/.sentry/` with restricted file
106106

107107
Once authenticated, you can start using the CLI:
108108

109+
- [Initialize Sentry](../commands/init/) - Set up Sentry in your project with the guided wizard
109110
- [Organization commands](../commands/org/) - List and view organizations
110111
- [Project commands](../commands/project/) - Manage projects
111112
- [Issue commands](../commands/issue/) - Track and manage issues

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
"url": "git+https://github.com/getsentry/cli.git"
77
},
88
"devDependencies": {
9+
"@anthropic-ai/sdk": "^0.39.0",
910
"@biomejs/biome": "2.3.8",
11+
"@clack/prompts": "^0.11.0",
12+
"@mastra/client-js": "^1.4.0",
1013
"@sentry/api": "^0.21.0",
1114
"@sentry/bun": "10.39.0",
1215
"@sentry/esbuild-plugin": "^2.23.0",
@@ -27,6 +30,7 @@
2730
"http-cache-semantics": "^4.2.0",
2831
"ignore": "^7.0.5",
2932
"marked": "^15",
33+
"openai": "^6.22.0",
3034
"p-limit": "^7.2.0",
3135
"pretty-ms": "^9.3.0",
3236
"qrcode-terminal": "^0.12.0",
@@ -66,6 +70,7 @@
6670
"test:unit": "bun test --timeout 15000 test/lib test/commands test/types --coverage --coverage-reporter=lcov",
6771
"test:isolated": "bun test --timeout 15000 test/isolated",
6872
"test:e2e": "bun test --timeout 15000 test/e2e",
73+
"test:init-eval": "bun test test/init-eval --timeout 600000 --concurrency 6",
6974
"generate:skill": "bun run script/generate-skill.ts",
7075
"check:skill": "bun run script/check-skill.ts",
7176
"check:deps": "bun run script/check-no-deps.ts"

plugins/sentry-cli/skills/sentry-cli/SKILL.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,20 @@ View logs associated with a trace
630630
- `-q, --query <value> - Additional filter query (Sentry search syntax)`
631631
- `-f, --fresh - Bypass cache and fetch fresh data`
632632

633+
### Init
634+
635+
Initialize Sentry in your project
636+
637+
#### `sentry init <directory>`
638+
639+
Initialize Sentry in your project
640+
641+
**Flags:**
642+
- `--force - Continue even if Sentry is already installed`
643+
- `-y, --yes - Non-interactive mode (accept defaults)`
644+
- `--dry-run - Preview changes without applying them`
645+
- `--features <value> - Comma-separated features: errors,tracing,logs,replay,metrics`
646+
633647
### Issues
634648

635649
List issues in a project

src/app.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { whoamiCommand } from "./commands/auth/whoami.js";
1313
import { cliRoute } from "./commands/cli/index.js";
1414
import { eventRoute } from "./commands/event/index.js";
1515
import { helpCommand } from "./commands/help.js";
16+
import { initCommand } from "./commands/init.js";
1617
import { issueRoute } from "./commands/issue/index.js";
1718
import { listCommand as issueListCommand } from "./commands/issue/list.js";
1819
import { logRoute } from "./commands/log/index.js";
@@ -64,6 +65,7 @@ export const routes = buildRouteMap({
6465
event: eventRoute,
6566
log: logRoute,
6667
trace: traceRoute,
68+
init: initCommand,
6769
api: apiCommand,
6870
issues: issueListCommand,
6971
orgs: orgListCommand,

src/commands/init.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* sentry init
3+
*
4+
* Initialize Sentry in a project using the remote wizard workflow.
5+
* Communicates with the Mastra API via suspend/resume to perform
6+
* local filesystem operations and interactive prompts.
7+
*/
8+
9+
import path from "node:path";
10+
import type { SentryContext } from "../context.js";
11+
import { buildCommand } from "../lib/command.js";
12+
import { runWizard } from "../lib/init/wizard-runner.js";
13+
14+
type InitFlags = {
15+
readonly force: boolean;
16+
readonly yes: boolean;
17+
readonly "dry-run": boolean;
18+
readonly features?: string;
19+
};
20+
21+
export const initCommand = buildCommand<InitFlags, [string?], SentryContext>({
22+
docs: {
23+
brief: "Initialize Sentry in your project",
24+
fullDescription:
25+
"Runs the Sentry setup wizard to detect your project's framework, " +
26+
"install the SDK, and configure error monitoring. Uses a remote " +
27+
"workflow that coordinates local file operations through the CLI.",
28+
},
29+
parameters: {
30+
positional: {
31+
kind: "tuple",
32+
parameters: [
33+
{
34+
placeholder: "directory",
35+
brief: "Project directory (default: current directory)",
36+
parse: String,
37+
optional: true,
38+
},
39+
],
40+
},
41+
flags: {
42+
force: {
43+
kind: "boolean",
44+
brief: "Continue even if Sentry is already installed",
45+
default: false,
46+
},
47+
yes: {
48+
kind: "boolean",
49+
brief: "Non-interactive mode (accept defaults)",
50+
default: false,
51+
},
52+
"dry-run": {
53+
kind: "boolean",
54+
brief: "Preview changes without applying them",
55+
default: false,
56+
},
57+
features: {
58+
kind: "parsed",
59+
parse: String,
60+
brief: "Comma-separated features: errors,tracing,logs,replay,metrics",
61+
optional: true,
62+
placeholder: "list",
63+
},
64+
},
65+
aliases: {
66+
y: "yes",
67+
},
68+
},
69+
async func(this: SentryContext, flags: InitFlags, directory?: string) {
70+
const targetDir = directory ? path.resolve(this.cwd, directory) : this.cwd;
71+
const featuresList = flags.features
72+
?.split(",")
73+
.map((f) => f.trim())
74+
.filter(Boolean);
75+
76+
await runWizard({
77+
directory: targetDir,
78+
force: flags.force,
79+
yes: flags.yes,
80+
dryRun: flags["dry-run"],
81+
features: featuresList,
82+
});
83+
},
84+
});

src/commands/project/create.ts

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
resolveOrCreateTeam,
4747
} from "../../lib/resolve-team.js";
4848
import { buildProjectUrl } from "../../lib/sentry-urls.js";
49+
import { slugify } from "../../lib/utils.js";
4950
import type { SentryProject, Writer } from "../../types/index.js";
5051

5152
/** Usage hint template — base command without positionals */
@@ -107,25 +108,6 @@ function normalizePlatform(platform: string, stderr: Writer): string {
107108
return corrected;
108109
}
109110

110-
/**
111-
* Convert a project name to its expected Sentry slug.
112-
* Aligned with Sentry's canonical implementation:
113-
* https://github.com/getsentry/sentry/blob/master/static/app/utils/slugify.tsx
114-
*
115-
* @example slugify("My Cool App") // "my-cool-app"
116-
* @example slugify("my-app") // "my-app"
117-
* @example slugify("Café Project") // "cafe-project"
118-
* @example slugify("my_app") // "my_app"
119-
*/
120-
function slugify(name: string): string {
121-
return name
122-
.normalize("NFKD")
123-
.toLowerCase()
124-
.replace(/[^a-z0-9_\s-]/g, "")
125-
.replace(/[-\s]+/g, "-")
126-
.replace(/^-|-$/g, "");
127-
}
128-
129111
/**
130112
* Check whether an API error is about an invalid platform value.
131113
* Relies on Sentry's error message wording — may need updating if the API changes.

0 commit comments

Comments
 (0)