Skip to content

Commit 7aa4b55

Browse files
dcramercodex
andauthored
fix(cli): Align init scaffold with example Nitro app (#749)
`junior init` now emits the same core Nitro app shape that `apps/example` exercises: direct `nitro dev`/`nitro build` scripts, explicit `plugins.ts` wiring into `createApp({ plugins })`, current Nitro-related dependency versions, and `tsconfig.json`. It no longer generates the stale Vite scaffold or top-level `/api/internal/agent/continue.ts` source, and the generated env/CI templates include the database variables and install flow a fresh app actually needs. The init unit test now uses `apps/example` as a canary for core scripts, dependencies, `vercel.json`, Nitro config, server wiring, and TypeScript config so future drift fails deterministically. The setup docs and README snippets now match that generated shape. Validation: - `pnpm --filter @sentry/junior exec vitest run tests/unit/cli/init-cli.test.ts` - `pnpm docs:check` - Garfield review pass Co-authored-by: GPT-5 Codex <codex@openai.com>
1 parent 0a99698 commit 7aa4b55

10 files changed

Lines changed: 260 additions & 77 deletions

File tree

packages/docs/src/content/docs/cli/init.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ The command requires exactly one argument: the target directory.
2525

2626
The scaffold includes:
2727

28-
- `package.json` with `@sentry/junior`, `@sentry/junior-maintenance`, `@sentry/junior-memory`, `hono`, `nitro`, `vite`, `typescript`, and `jiti`
28+
- `package.json` with `@sentry/junior`, `@sentry/junior-maintenance`, `@sentry/junior-memory`, `hono`, `nitro`, `typescript`, `jiti`, and `@types/node`
2929
- `plugins.ts` with `@sentry/junior-maintenance` and `createMemoryPlugin()` enabled
3030
- `server.ts`
3131
- `nitro.config.ts` pointing at `./plugins`
32-
- `vite.config.ts`
32+
- `tsconfig.json` extending Nitro's TypeScript config
3333
- `vercel.json`
34+
- `.github/workflows/ci.yml`
3435
- `app/SOUL.md`
3536
- `app/WORLD.md`
3637
- `app/DESCRIPTION.md`
@@ -74,7 +75,7 @@ junior command failed: refusing to initialize non-empty directory: /path/to/my-b
7475
After scaffolding:
7576

7677
1. Run `cd my-bot && pnpm install`.
77-
2. Fill in the required values from `.env.example`.
78+
2. Fill in the required values from `.env.example`, including `DATABASE_URL`.
7879
3. Run `pnpm dev`.
7980
4. Check `http://localhost:3000/health`.
8081

packages/docs/src/content/docs/cli/snapshot-create.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ The common case is a Vercel build command:
6060
```json title="package.json"
6161
{
6262
"scripts": {
63-
"build": "junior snapshot create && vite build"
63+
"build": "junior snapshot create && nitro build"
6464
}
6565
}
6666
```

packages/docs/src/content/docs/extend/index.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ pnpm add @sentry/junior @sentry/junior-agent-browser @sentry/junior-cloudflare @
6565
Create one runtime-safe plugin set and point `juniorNitro()` at that module.
6666
Manifest-only packages use package-name strings. Plugins that need runtime
6767
hooks use JavaScript factories such as `githubPlugin()` and `schedulerPlugin()`.
68-
`createApp()` reads the same enabled plugin set from Nitro's virtual module at
69-
runtime.
68+
Import the same plugin set in `server.ts` and pass it to
69+
`createApp({ plugins })` so local dev and built bundles use identical runtime
70+
plugins.
7071

7172
```ts title="plugins.ts"
7273
import { defineJuniorPlugins } from "@sentry/junior";
@@ -112,8 +113,11 @@ export default defineConfig({
112113

113114
```ts title="server.ts"
114115
import { createApp } from "@sentry/junior";
116+
import { plugins } from "./plugins.ts";
115117

116-
const app = await createApp();
118+
const app = await createApp({
119+
plugins,
120+
});
117121

118122
export default app;
119123
```

packages/docs/src/content/docs/operate/sandbox-snapshots.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ The common deploy path runs snapshot warmup during build:
2222
```json title="package.json"
2323
{
2424
"scripts": {
25-
"build": "junior snapshot create && vite build"
25+
"build": "junior snapshot create && nitro build"
2626
}
2727
}
2828
```

packages/docs/src/content/docs/start-here/deploy-to-vercel.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ The scaffolded `package.json` includes the production build script:
4848
{
4949
"scripts": {
5050
"check": "junior check",
51-
"dev": "vite dev",
52-
"build": "junior snapshot create && vite build"
51+
"dev": "nitro dev",
52+
"build": "junior snapshot create && nitro build"
5353
}
5454
}
5555
```
@@ -74,7 +74,11 @@ import { juniorNitro } from "@sentry/junior/nitro";
7474

7575
export default defineConfig({
7676
preset: "vercel",
77-
modules: [juniorNitro()],
77+
modules: [
78+
juniorNitro({
79+
plugins: "./plugins",
80+
}),
81+
],
7882
routes: {
7983
"/**": { handler: "./server.ts" },
8084
},

packages/docs/src/content/docs/start-here/existing-app.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ import { initSentry } from "@sentry/junior/instrumentation";
2424
initSentry();
2525

2626
import { createApp } from "@sentry/junior";
27+
import { plugins } from "./plugins.ts";
2728

28-
const app = await createApp();
29+
const app = await createApp({
30+
plugins,
31+
});
2932

3033
export default app;
3134
```

packages/docs/src/content/docs/start-here/quickstart.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ Use the same baseline that the scaffolded CI workflow uses:
321321

322322
- Node.js 24
323323
- pnpm
324+
- A Postgres database for Junior SQL records and the default memory plugin
324325
- A Redis URL for runtime state, locks, and durable task records
325326

326327
Slack credentials are needed before the bot can reply in Slack. You can scaffold and verify the local health route first, then finish [Slack App Setup](/start-here/slack-app-setup/).
@@ -335,7 +336,7 @@ cd my-bot
335336
pnpm install
336337
```
337338

338-
`junior init` creates the app entrypoint, Nitro/Vite config, Vercel config, Vercel queue consumer source, CI workflow, app context files, local plugin and skill directories, `.env.example`, and a `plugins.ts` with maintenance and memory enabled by default.
339+
`junior init` creates the app entrypoint, Nitro config, Vercel config, TypeScript config, CI workflow, app context files, local plugin and skill directories, `.env.example`, and a `plugins.ts` with maintenance and memory enabled by default.
339340

340341
The generated `app/` files have separate jobs:
341342

@@ -363,6 +364,8 @@ Set these values before running real turns:
363364
| ------------------------- | ---------------------- | -------------------------------------------------------------- |
364365
| `SLACK_SIGNING_SECRET` | Yes, for Slack traffic | Verifies Slack requests. |
365366
| `SLACK_BOT_TOKEN` | Yes, for Slack replies | Posts thread replies and calls Slack APIs. |
367+
| `DATABASE_URL` | Yes | Postgres connection string for Junior SQL records and memory. |
368+
| `JUNIOR_DATABASE_DRIVER` | No | SQL client driver: `neon` or `postgres`. |
366369
| `REDIS_URL` | Yes | Runtime state, locks, and durable background task records. |
367370
| `JUNIOR_SECRET` | Yes | Signs internal resume callbacks and sandbox requester context. |
368371
| `JUNIOR_BOT_NAME` | No | Bot display/config name. |
@@ -376,7 +379,9 @@ Set these values before running real turns:
376379

377380
See [Config & Environment](/reference/config-and-env/) for the full reference.
378381
If you keep the default memory plugin enabled, use a Postgres database with
379-
pgvector support before running migrations.
382+
pgvector support before running migrations. Local Postgres URLs automatically
383+
use the `postgres` driver; set `JUNIOR_DATABASE_DRIVER=postgres` for other
384+
non-Neon Postgres providers.
380385

381386
## Run locally
382387

@@ -440,8 +445,8 @@ export const plugins = defineJuniorPlugins([
440445
]);
441446
```
442447

443-
Point `juniorNitro()` at that module. `createApp()` reads the same plugin set
444-
from Nitro's virtual module, so the server entry does not repeat it:
448+
Point `juniorNitro()` at that module and pass the same plugin set to
449+
`createApp()` so local dev and built bundles use identical runtime plugins:
445450

446451
```ts title="nitro.config.ts"
447452
import { defineConfig } from "nitro";
@@ -462,8 +467,11 @@ export default defineConfig({
462467

463468
```ts title="server.ts"
464469
import { createApp } from "@sentry/junior";
470+
import { plugins } from "./plugins.ts";
465471

466-
const app = await createApp();
472+
const app = await createApp({
473+
plugins,
474+
});
467475

468476
export default app;
469477
```
@@ -488,8 +496,8 @@ When enabled plugins declare sandbox runtime dependencies, the scaffolded build
488496
{
489497
"scripts": {
490498
"check": "junior check",
491-
"dev": "vite dev",
492-
"build": "junior snapshot create && vite build"
499+
"dev": "nitro dev",
500+
"build": "junior snapshot create && nitro build"
493501
}
494502
}
495503
```

packages/junior/README.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,37 @@ pnpm add @sentry/junior hono @sentry/node
1010

1111
## Quick usage
1212

13+
`plugins.ts`:
14+
15+
```ts
16+
import { defineJuniorPlugins } from "@sentry/junior";
17+
18+
export const plugins = defineJuniorPlugins([]);
19+
```
20+
1321
`server.ts`:
1422

1523
```ts
1624
import { initSentry } from "@sentry/junior/instrumentation";
1725
initSentry();
1826

1927
import { createApp } from "@sentry/junior";
28+
import { plugins } from "./plugins.ts";
2029

21-
const app = await createApp();
30+
const app = await createApp({
31+
plugins,
32+
});
2233

2334
export default app;
2435
```
2536

2637
Run `junior init my-bot` to scaffold a complete project including `vercel.json` for Vercel deployment.
2738

2839
Use `defineJuniorPlugins([...])` in a runtime-safe plugin module, then point
29-
`juniorNitro({ plugins: "./plugins" })` at that module. `createApp()` reads the
30-
same enabled set from Nitro's virtual module. Manifest-only packages use
31-
package-name strings; factories such as `githubPlugin()` register their
32-
manifest and in-process hooks together.
40+
`juniorNitro({ plugins: "./plugins" })` at that module and pass the same set to
41+
`createApp({ plugins })`. Manifest-only packages use package-name strings;
42+
factories such as `githubPlugin()` register their manifest and in-process hooks
43+
together.
3344

3445
## Full docs
3546

packages/junior/src/cli/init.ts

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,21 @@ import { juniorVercelConfig } from "../vercel";
55
function writeServerEntry(targetDir: string): void {
66
fs.writeFileSync(
77
path.join(targetDir, "server.ts"),
8-
`import { initSentry } from "@sentry/junior/instrumentation";
9-
initSentry();
8+
`import { createApp } from "@sentry/junior";
9+
import { initSentry } from "@sentry/junior/instrumentation";
10+
import { plugins } from "./plugins.ts";
1011
11-
import { createApp } from "@sentry/junior";
12+
initSentry();
1213
13-
const app = await createApp();
14+
const app = await createApp({
15+
plugins,
16+
});
1417
1518
export default app;
1619
`,
1720
);
1821
}
1922

20-
function writeQueueConsumerEntry(targetDir: string): void {
21-
const queueConsumerDir = path.join(targetDir, "api", "internal", "agent");
22-
fs.mkdirSync(queueConsumerDir, { recursive: true });
23-
fs.writeFileSync(
24-
path.join(queueConsumerDir, "continue.ts"),
25-
`import app from "../../../server.ts";
26-
27-
export const POST = (request: Request) => app.fetch(request);
28-
`,
29-
);
30-
}
31-
3223
function writePluginsFile(targetDir: string): void {
3324
fs.writeFileSync(
3425
path.join(targetDir, "plugins.ts"),
@@ -64,19 +55,17 @@ export default defineConfig({
6455
);
6556
}
6657

67-
function writeViteConfig(targetDir: string): void {
58+
function writeTsConfig(targetDir: string): void {
6859
fs.writeFileSync(
69-
path.join(targetDir, "vite.config.ts"),
70-
`import { defineConfig } from "vite";
71-
import { nitro } from "nitro/vite";
72-
73-
export default defineConfig({
74-
server: {
75-
allowedHosts: true,
76-
},
77-
plugins: [nitro()],
78-
});
79-
`,
60+
path.join(targetDir, "tsconfig.json"),
61+
`${JSON.stringify(
62+
{
63+
extends: "nitro/tsconfig",
64+
compilerOptions: {},
65+
},
66+
null,
67+
2,
68+
)}\n`,
8069
);
8170
}
8271

@@ -111,7 +100,7 @@ jobs:
111100
with:
112101
node-version: 24
113102
cache: pnpm
114-
- run: pnpm install --frozen-lockfile
103+
- run: pnpm install
115104
- run: pnpm check
116105
- run: pnpm build
117106
`,
@@ -147,21 +136,23 @@ export async function runInit(
147136
private: true,
148137
type: "module",
149138
scripts: {
150-
dev: "vite dev",
139+
dev: "nitro dev",
151140
check: "junior check",
152-
build: "junior snapshot create && vite build",
141+
build: "junior snapshot create && nitro build",
142+
preview: "nitro preview",
143+
typecheck: "tsc --noEmit",
153144
},
154145
dependencies: {
155146
"@sentry/junior": "latest",
156147
"@sentry/junior-memory": "latest",
157148
"@sentry/junior-maintenance": "latest",
158-
hono: "^4.12.0",
149+
hono: "^4.12.22",
159150
},
160151
devDependencies: {
161-
jiti: "^2.6.1",
162-
nitro: "3.0.260311-beta",
163-
typescript: "^5.9.0",
164-
vite: "^8.0.3",
152+
"@types/node": "^25.9.1",
153+
jiti: "^2.7.0",
154+
nitro: "3.0.260522-beta",
155+
typescript: "^6.0.3",
165156
},
166157
};
167158
fs.writeFileSync(
@@ -216,6 +207,8 @@ AI_EMBEDDING_MODEL=
216207
MEMORY_RECALL_MAX_VECTOR_DISTANCE=
217208
AI_VISION_MODEL=
218209
AI_WEB_SEARCH_MODEL=
210+
DATABASE_URL=
211+
JUNIOR_DATABASE_DRIVER=
219212
REDIS_URL=
220213
CRON_SECRET=
221214
SENTRY_DSN=
@@ -224,10 +217,9 @@ SENTRY_ORG_SLUG=
224217
);
225218

226219
writeServerEntry(target);
227-
writeQueueConsumerEntry(target);
228220
writePluginsFile(target);
229221
writeNitroConfig(target);
230-
writeViteConfig(target);
222+
writeTsConfig(target);
231223
writeVercelJson(target);
232224
writeGitHubWorkflow(target);
233225

0 commit comments

Comments
 (0)