From 9639c3699238aca0859a6e382a4bb6e5c934b4e9 Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Tue, 11 Aug 2026 10:59:37 -0600 Subject: [PATCH 01/13] Add --project-id/--org-id/--branch-id to neon init Let returning users skip org/project discovery by naming an existing project up front. When --project-id is given, the getting-started flow collapses its org/project select-or-create chain into a single verified `neon link` step (infers org, records branch, writes .neon); --org-id alone scopes project selection to that org, and --branch-id targets a branch for env pull. Works in both agent and interactive modes. Co-authored-by: Isaac --- packages/cli/src/commands/init.test.ts | 16 +- packages/cli/src/commands/init.ts | 36 +++- .../__snapshots__/agent_snapshot.test.ts.snap | 9 + packages/cli/src/init/ids.ts | 11 ++ packages/cli/src/init/interactive.ts | 11 ++ packages/cli/src/init/orchestrate.ts | 9 + packages/cli/src/init/phases/db.ts | 25 ++- .../cli/src/init/phases/getting_started.ts | 184 +++++++++++------- 8 files changed, 215 insertions(+), 86 deletions(-) create mode 100644 packages/cli/src/init/ids.ts diff --git a/packages/cli/src/commands/init.test.ts b/packages/cli/src/commands/init.test.ts index 4413a6cf..8c1d0713 100644 --- a/packages/cli/src/commands/init.test.ts +++ b/packages/cli/src/commands/init.test.ts @@ -77,6 +77,9 @@ describe("init", () => { agent: "cursor", skipMigrations: undefined, preview: undefined, + projectId: undefined, + orgId: undefined, + branchId: undefined, }); expect(interactiveInit).not.toHaveBeenCalled(); }); @@ -96,6 +99,9 @@ describe("init", () => { agent: "claude", skipMigrations: true, preview: undefined, + projectId: undefined, + orgId: undefined, + branchId: undefined, }); }); @@ -105,7 +111,12 @@ describe("init", () => { await handler({ preview: true }); - expect(interactiveInit).toHaveBeenCalledWith({ preview: true }); + expect(interactiveInit).toHaveBeenCalledWith({ + preview: true, + projectId: undefined, + orgId: undefined, + branchId: undefined, + }); }); test("should pass preview to orchestrate in agent mode", async () => { @@ -120,6 +131,9 @@ describe("init", () => { agent: "cursor", skipMigrations: undefined, preview: true, + projectId: undefined, + orgId: undefined, + branchId: undefined, }); }); diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index a2c4129c..c4fb45f6 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -3,6 +3,7 @@ import type yargs from "yargs"; import { closeAnalytics, sendError } from "../analytics.js"; import { detectAgent } from "../init/detect_agent.js"; import { enrichResponse } from "../init/enrich_output.js"; +import { assertSafeId } from "../init/ids.js"; import { interactiveInit } from "../init/interactive.js"; import { orchestrate } from "../init/orchestrate.js"; import { routeDataStep } from "../init/route_command.js"; @@ -38,6 +39,21 @@ export const builder = (yargs: yargs.Argv) => describe: "Enable preview features (e.g. project bootstrapping from templates).", }) + .option("project-id", { + type: "string", + describe: + "Use an existing Neon project by ID. Skips organization and project selection.", + }) + .option("org-id", { + type: "string", + describe: + "Scope setup to an existing organization by ID. Skips organization selection.", + }) + .option("branch-id", { + type: "string", + describe: + "Target a specific branch by ID when pulling environment variables.", + }) .strict(false); /** @@ -77,6 +93,9 @@ export const handler = async (argv: { data?: string; skipMigrations?: boolean; preview?: boolean; + projectId?: string; + orgId?: string; + branchId?: string; profile?: string; }) => { // Auto-detect agent from environment. When --agent is explicitly passed, @@ -115,6 +134,13 @@ export const handler = async (argv: { ); } + // Validate IDs up front — they are interpolated into shell commands + // downstream, and an early, well-shaped rejection beats a confusing + // failure several phases later. + if (argv.projectId) assertSafeId(argv.projectId, "project ID"); + if (argv.orgId) assertSafeId(argv.orgId, "org ID"); + if (argv.branchId) assertSafeId(argv.branchId, "branch ID"); + // --data with a "step" field routes to the appropriate phase if (argv.data && isAgentMode) { let data: Record; @@ -143,10 +169,18 @@ export const handler = async (argv: { agent, skipMigrations: argv.skipMigrations, preview: argv.preview, + projectId: argv.projectId, + orgId: argv.orgId, + branchId: argv.branchId, }), ); } else { - await interactiveInit({ preview: argv.preview }); + await interactiveInit({ + preview: argv.preview, + projectId: argv.projectId, + orgId: argv.orgId, + branchId: argv.branchId, + }); } } catch (error) { const cause = error instanceof Error ? error : new Error(String(error)); diff --git a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap b/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap index c6059cac..f675a4ce 100644 --- a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap +++ b/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap @@ -14564,6 +14564,15 @@ Options: --preview └────────────────> Enable preview features (e.g. project bootstrapping from tem plates). [boolean] [default: false] +--project-id +└────────────────> Use an existing Neon project by ID. Skips organization and p + roject selection. [string] +--org-id +└────────────────> Scope setup to an existing organization by ID. Skips organiz + ation selection. [string] +--branch-id +└────────────────> Target a specific branch by ID when pulling environment vari + ables. [string] --- subprocesses --- " `; diff --git a/packages/cli/src/init/ids.ts b/packages/cli/src/init/ids.ts new file mode 100644 index 00000000..326d3f90 --- /dev/null +++ b/packages/cli/src/init/ids.ts @@ -0,0 +1,11 @@ +/** + * Validates that an ID contains only safe characters for shell interpolation. + * Neon org/project/branch IDs are UUIDs or slug-like strings. + */ +export function assertSafeId(value: string, label: string): void { + if (!/^[\w.:-]+$/.test(value)) { + throw new Error( + `Invalid ${label}: "${value}". Expected alphanumeric, hyphens, underscores, dots, or colons.`, + ); + } +} diff --git a/packages/cli/src/init/interactive.ts b/packages/cli/src/init/interactive.ts index b31fac9c..e4393366 100644 --- a/packages/cli/src/init/interactive.ts +++ b/packages/cli/src/init/interactive.ts @@ -185,6 +185,12 @@ async function selectTemplate( export type InteractiveInitOptions = { preview?: boolean; + /** Existing project to use — carried into the agent hand-off command. */ + projectId?: string; + /** Existing org to scope to — carried into the agent hand-off command. */ + orgId?: string; + /** Branch to target — carried into the agent hand-off command. */ + branchId?: string; }; export async function interactiveInit( @@ -783,6 +789,11 @@ async function interactiveInitInner( if (selectedFeatures.length > 0) gettingStartedData.features = selectedFeatures; if (options.preview) gettingStartedData.preview = true; + // Carry any explicitly provided IDs into the hand-off so the agent runs the + // verified fast path (single `neon link`) instead of the selection flow. + if (options.projectId) gettingStartedData.projectId = options.projectId; + if (options.orgId) gettingStartedData.orgId = options.orgId; + if (options.branchId) gettingStartedData.branchId = options.branchId; // Build a prompt for the user to paste into their agent chat const cmd = `neon init --agent --data '${JSON.stringify({ step: "getting-started", ...gettingStartedData })}'`; diff --git a/packages/cli/src/init/orchestrate.ts b/packages/cli/src/init/orchestrate.ts index 20733af7..0d978e56 100644 --- a/packages/cli/src/init/orchestrate.ts +++ b/packages/cli/src/init/orchestrate.ts @@ -15,6 +15,12 @@ export type OrchestratorOptions = { skipMigrations?: boolean; /** Enable preview features (e.g. project bootstrapping from templates) */ preview?: boolean; + /** Existing project to use — skips org/project selection in getting-started */ + projectId?: string; + /** Existing org to scope to — skips org selection in getting-started */ + orgId?: string; + /** Branch to target when pulling env */ + branchId?: string; }; /** @@ -132,6 +138,9 @@ export async function orchestrate( migrationDir: inspection.migrationDir as string | undefined, features, preview: options.preview, + projectId: options.projectId, + orgId: options.orgId, + branchId: options.branchId, }); } diff --git a/packages/cli/src/init/phases/db.ts b/packages/cli/src/init/phases/db.ts index 3522e69f..9b1c1d56 100644 --- a/packages/cli/src/init/phases/db.ts +++ b/packages/cli/src/init/phases/db.ts @@ -1,23 +1,13 @@ +import { assertSafeId } from "../ids.js"; import { neonctlCmd } from "../neonctl.js"; import { SKILL_REFERENCE_URLS } from "../skills.js"; import type { PhaseResponse } from "../types.js"; -/** - * Validates that an ID contains only safe characters for shell interpolation. - * Neon org/project IDs are typically UUIDs or slug-like strings. - */ -function assertSafeId(value: string, label: string): void { - if (!/^[\w.:-]+$/.test(value)) { - throw new Error( - `Invalid ${label}: "${value}". Expected alphanumeric, hyphens, underscores, dots, or colons.`, - ); - } -} - export type DbPhaseOptions = { agent?: string; orgId?: string; projectId?: string; + branchId?: string; orgsResult?: string; projectsResult?: string; framework?: string; @@ -35,6 +25,7 @@ export async function handleDbPhase( // Validate IDs that will be interpolated into shell commands if (options.projectId) assertSafeId(options.projectId, "project ID"); if (options.orgId) assertSafeId(options.orgId, "org ID"); + if (options.branchId) assertSafeId(options.branchId, "branch ID"); // Error from a previous step if (options.error) { @@ -59,10 +50,16 @@ export async function handleDbPhase( // If we have a project ID, we're in the "wire it up" phase if (options.projectId) { + const branchFlag = options.branchId + ? ` --branch-id ${options.branchId}` + : ""; return { phase: "db", status: "project_ready", - project: { id: options.projectId }, + project: { + id: options.projectId, + ...(options.branchId ? { branchId: options.branchId } : {}), + }, nextAction: { type: "agent_action", prerequisite: SKILL_REFERENCE_URLS.connectionMethods, @@ -70,7 +67,7 @@ export async function handleDbPhase( { id: "get_connection_string", description: "Get the database connection string", - command: `${neonctlCmd()} connection-string --project-id ${options.projectId}`, + command: `${neonctlCmd()} connection-string --project-id ${options.projectId}${branchFlag}`, }, { id: "store_env", diff --git a/packages/cli/src/init/phases/getting_started.ts b/packages/cli/src/init/phases/getting_started.ts index 6cba14f4..45dd5d76 100644 --- a/packages/cli/src/init/phases/getting_started.ts +++ b/packages/cli/src/init/phases/getting_started.ts @@ -5,6 +5,7 @@ import { MISSING_BINARY_HINT, resolvePackageManager, } from "../../utils/package_manager.js"; +import { assertSafeId } from "../ids.js"; import { neonctlCmd } from "../neonctl.js"; import { ensureSkillsUpToDate, SKILL_REFERENCE_URLS } from "../skills.js"; import type { PhaseResponse } from "../types.js"; @@ -20,6 +21,15 @@ export type GettingStartedPhaseOptions = { features?: string[]; /** Preview mode — restricts project creation to new projects in AWS us-east */ preview?: boolean; + /** + * Existing project to use. When set, org/project selection is skipped and a + * single verified `neon link` step writes the .neon context. + */ + projectId?: string; + /** Existing org to scope to. When set (without projectId), org selection is skipped. */ + orgId?: string; + /** Branch to target — recorded in the .neon context so `env pull` uses it. */ + branchId?: string; /** The project directory the emitted commands will run in. */ cwd: string; }; @@ -43,45 +53,41 @@ export async function handleGettingStartedPhase( const installPm = resolvePackageManager(options.cwd); if (!options.hasConnectionString) { - if (options.preview) { - // Public beta: platform features are only in AWS us-east-2 for now - steps.push( - { - id: "select_org", - description: [ - "List the user's Neon organizations using the CLI command below.", - "If only one org exists, use it automatically.", - "If multiple orgs exist, ask the user which one to use.", - "Remember the selected org ID for the next steps.", - ].join(" "), - command: `${neonctlCmd()} orgs list --output json`, - }, - { - id: "select_or_create_project", - description: [ - "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID).", - "IMPORTANT: Neon features (Functions, Object Storage, and AI Gateway) are currently in beta and only available in the AWS us-east-2 region (more regions coming shortly). Projects must have region_id 'aws-us-east-2' and be created on or after 2026-06-15.", - "Filter the project list to ONLY show projects where region_id is 'aws-us-east-2' AND created_at is on or after '2026-06-15'.", - "If eligible projects exist, present them alongside a 'Create new project' option.", - "If no eligible projects exist, tell the user and proceed directly to creating a new one.", - "IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - ].join(" "), - command: `${neonctlCmd()} projects list --org-id --output json`, - }, - { - id: "create_project_if_needed", - description: [ - "If the user chose to create a new project, create it in the AWS us-east-2 region using the CLI command below (replace and ).", - "Ask the user for a project name (suggest the current directory name).", - "If the user chose an existing eligible project, skip this step.", - ].join(" "), - command: `${neonctlCmd()} projects create --name --org-id --region-id aws-us-east-2 --output json`, - }, - ); + if (options.projectId) { + // Verified fast path: the user named an existing project, so there is + // nothing to list or ask. A single `neon link` verifies the IDs against + // the account, infers the org, records the branch, and writes .neon — + // which makes the org/project selection steps and create_neon_context + // below unnecessary. + assertSafeId(options.projectId, "project ID"); + const linkFlags = [`--project-id ${options.projectId}`]; + if (options.orgId) { + assertSafeId(options.orgId, "org ID"); + linkFlags.push(`--org-id ${options.orgId}`); + } + if (options.branchId) { + assertSafeId(options.branchId, "branch ID"); + linkFlags.push(`--branch-id ${options.branchId}`); + } + steps.push({ + id: "link_project", + description: [ + "Link this directory to the existing Neon project using the CLI command below.", + "This verifies the IDs against the user's Neon account, infers the organization, and writes the .neon context file (merging into any existing file — do NOT overwrite it).", + "No organization or project selection is needed.", + ].join(" "), + command: `${neonctlCmd()} link ${linkFlags.join(" ")}`, + }); } else { - // Standard mode: let user choose existing or create new - steps.push( - { + // No project chosen yet. If an org was provided, skip org selection and + // scope the project commands to it; otherwise start by listing orgs. + const orgKnown = Boolean(options.orgId); + if (orgKnown) assertSafeId(options.orgId as string, "org ID"); + const orgRef = options.orgId ?? ""; + const orgFlag = `--org-id ${orgRef}`; + + if (!orgKnown) { + steps.push({ id: "select_org", description: [ "List the user's Neon organizations using the CLI command below.", @@ -90,39 +96,77 @@ export async function handleGettingStartedPhase( "Remember the selected org ID for the next steps.", ].join(" "), command: `${neonctlCmd()} orgs list --output json`, - }, - { - id: "select_or_create_project", - description: [ - "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID).", - "Ask the user whether they want to use an existing project or create a new one.", - "If creating new, ask the user for a project name (suggest the current directory name).", - "IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - ].join(" "), - command: `${neonctlCmd()} projects list --org-id --output json`, - }, - { - id: "create_project_if_needed", - description: [ - "If the user chose to create a new project, create it using the CLI command below (replace and ).", - "If the user chose an existing project, skip this step.", - ].join(" "), - command: `${neonctlCmd()} projects create --name --org-id --output json`, - }, - ); - } + }); + } - // Create/update .neon context file - steps.push({ - id: "create_neon_context", - description: [ - "Update the .neon context file in the project root with the selected org and project IDs.", - "IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved.", - "If no .neon file exists, create one.", - 'The file is JSON. Add/update only the orgId and projectId fields: {"orgId": "", "projectId": "", ...existing fields}.', - "This file is safe to commit — it contains no secrets.", - ].join(" "), - }); + const orgPhrase = orgKnown + ? `organization ${orgRef}` + : "selected organization"; + const listSuffix = orgKnown + ? "" + : " (replace with the selected org ID)"; + + if (options.preview) { + // Public beta: platform features are only in AWS us-east-2 for now + steps.push( + { + id: "select_or_create_project", + description: [ + `List existing Neon projects in the ${orgPhrase} using the CLI command below${listSuffix}.`, + "IMPORTANT: Neon features (Functions, Object Storage, and AI Gateway) are currently in beta and only available in the AWS us-east-2 region (more regions coming shortly). Projects must have region_id 'aws-us-east-2' and be created on or after 2026-06-15.", + "Filter the project list to ONLY show projects where region_id is 'aws-us-east-2' AND created_at is on or after '2026-06-15'.", + "If eligible projects exist, present them alongside a 'Create new project' option.", + "If no eligible projects exist, tell the user and proceed directly to creating a new one.", + "IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", + ].join(" "), + command: `${neonctlCmd()} projects list ${orgFlag} --output json`, + }, + { + id: "create_project_if_needed", + description: [ + `If the user chose to create a new project, create it in the AWS us-east-2 region using the CLI command below${orgKnown ? "" : " (replace and )"}.`, + "Ask the user for a project name (suggest the current directory name).", + "If the user chose an existing eligible project, skip this step.", + ].join(" "), + command: `${neonctlCmd()} projects create --name ${orgFlag} --region-id aws-us-east-2 --output json`, + }, + ); + } else { + // Standard mode: let user choose existing or create new + steps.push( + { + id: "select_or_create_project", + description: [ + `List existing Neon projects in the ${orgPhrase} using the CLI command below${listSuffix}.`, + "Ask the user whether they want to use an existing project or create a new one.", + "If creating new, ask the user for a project name (suggest the current directory name).", + "IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", + ].join(" "), + command: `${neonctlCmd()} projects list ${orgFlag} --output json`, + }, + { + id: "create_project_if_needed", + description: [ + `If the user chose to create a new project, create it using the CLI command below${orgKnown ? "" : " (replace and )"}.`, + "If the user chose an existing project, skip this step.", + ].join(" "), + command: `${neonctlCmd()} projects create --name ${orgFlag} --output json`, + }, + ); + } + + // Create/update .neon context file + steps.push({ + id: "create_neon_context", + description: [ + "Update the .neon context file in the project root with the selected org and project IDs.", + "IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved.", + "If no .neon file exists, create one.", + 'The file is JSON. Add/update only the orgId and projectId fields: {"orgId": "", "projectId": "", ...existing fields}.', + "This file is safe to commit — it contains no secrets.", + ].join(" "), + }); + } // Install project dependencies (required before env pull — config files may import packages) steps.push({ From f56ae3d79e1bdcca5d6be9ef387b90039f41e139 Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Tue, 11 Aug 2026 14:45:20 -0600 Subject: [PATCH 02/13] Make `neon init` minimal: install tooling, then hand off to the agent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `neon init` now authenticates and installs the Neon tooling (MCP server, agent skills, and the editor extension in a VS Code–based IDE), then hands off to the agent. It no longer selects an org/project, links the directory, pulls env vars, asks which features to enable, or provisions Auth / Object Storage / Functions / AI Gateway — the installed Neon skill drives that from here (or run `neon link` yourself). - Removed flags: --preview, --project-id, --org-id, --branch-id, --skip-migrations. - Agent --data steps reduced to auth, setup, mcp, skills (getting-started, db, migrations, neon-auth, status, finalize removed). - Interactive init no longer scaffolds templates or configures Neon Auth. - Deleted phases/support: getting_started, neon_auth (phase only; the top-level `neon neon-auth` command stays), migrations, db, cleanup, status, resolve_context, ids. - The standalone `neon bootstrap` command is unaffected. Co-authored-by: Isaac --- .changeset/minimal-neon-init.md | 11 + packages/cli/src/commands/init.test.ts | 62 +- packages/cli/src/commands/init.ts | 57 +- .../__snapshots__/agent_snapshot.test.ts.snap | 10301 ++-------------- packages/cli/src/init/agent_snapshot.test.ts | 72 +- packages/cli/src/init/enrich_output.ts | 8 - packages/cli/src/init/handoff.ts | 25 + packages/cli/src/init/ids.ts | 11 - packages/cli/src/init/interactive.ts | 819 +- packages/cli/src/init/orchestrate.test.ts | 207 +- packages/cli/src/init/orchestrate.ts | 209 +- packages/cli/src/init/phases/cleanup.ts | 33 - packages/cli/src/init/phases/db.test.ts | 120 - packages/cli/src/init/phases/db.ts | 314 - .../src/init/phases/getting_started.test.ts | 243 - .../cli/src/init/phases/getting_started.ts | 340 - .../cli/src/init/phases/migrations.test.ts | 218 - packages/cli/src/init/phases/migrations.ts | 320 - packages/cli/src/init/phases/neon_auth.ts | 157 - packages/cli/src/init/phases/setup.test.ts | 188 +- packages/cli/src/init/phases/setup.ts | 377 +- packages/cli/src/init/phases/status.ts | 87 - packages/cli/src/init/resolve_context.ts | 128 - packages/cli/src/init/route_command.test.ts | 45 +- packages/cli/src/init/route_command.ts | 56 +- packages/cli/src/init/skills.ts | 21 +- packages/cli/src/init/types.ts | 41 +- 27 files changed, 1756 insertions(+), 12714 deletions(-) create mode 100644 .changeset/minimal-neon-init.md create mode 100644 packages/cli/src/init/handoff.ts delete mode 100644 packages/cli/src/init/ids.ts delete mode 100644 packages/cli/src/init/phases/cleanup.ts delete mode 100644 packages/cli/src/init/phases/db.test.ts delete mode 100644 packages/cli/src/init/phases/db.ts delete mode 100644 packages/cli/src/init/phases/getting_started.test.ts delete mode 100644 packages/cli/src/init/phases/getting_started.ts delete mode 100644 packages/cli/src/init/phases/migrations.test.ts delete mode 100644 packages/cli/src/init/phases/migrations.ts delete mode 100644 packages/cli/src/init/phases/neon_auth.ts delete mode 100644 packages/cli/src/init/phases/status.ts delete mode 100644 packages/cli/src/init/resolve_context.ts diff --git a/.changeset/minimal-neon-init.md b/.changeset/minimal-neon-init.md new file mode 100644 index 00000000..77e976e8 --- /dev/null +++ b/.changeset/minimal-neon-init.md @@ -0,0 +1,11 @@ +--- +"neon": major +--- + +`neon init` is now minimal: it authenticates and installs the Neon tooling (MCP server, agent skills, and — in a VS Code–based IDE — the editor extension), then hands off to the agent. It no longer selects an organization or project, links the directory, pulls environment variables, asks which features to enable, or provisions Auth / Object Storage / Functions / AI Gateway — the installed Neon skill drives all of that from here (or you can run `neon link` yourself). + +- Removed flags: `--preview`, `--project-id`, `--org-id`, `--branch-id`, and `--skip-migrations`. +- The agent `--data` step protocol is reduced to `auth`, `setup`, `mcp`, and `skills`. The `getting-started`, `db`, `migrations`, `neon-auth`, `status`, and `finalize` steps are removed. +- Interactive `neon init` no longer scaffolds from a template or configures Neon Auth; it installs tooling and points you at your agent (or `neon link`). + +The standalone `neon bootstrap` command is unaffected. diff --git a/packages/cli/src/commands/init.test.ts b/packages/cli/src/commands/init.test.ts index 8c1d0713..c1241b00 100644 --- a/packages/cli/src/commands/init.test.ts +++ b/packages/cli/src/commands/init.test.ts @@ -73,70 +73,10 @@ describe("init", () => { await handler({ agent: true }); - expect(orchestrate).toHaveBeenCalledWith({ - agent: "cursor", - skipMigrations: undefined, - preview: undefined, - projectId: undefined, - orgId: undefined, - branchId: undefined, - }); + expect(orchestrate).toHaveBeenCalledWith({ agent: "cursor" }); expect(interactiveInit).not.toHaveBeenCalled(); }); - test("should pass skipMigrations to orchestrate", async () => { - const { handler } = await import("./init.js"); - const { orchestrate } = await import("../init/orchestrate.js"); - const { detectAgent } = await import("../init/detect_agent.js"); - vi.mocked(detectAgent).mockReturnValue("claude"); - - await handler({ - agent: true, - skipMigrations: true, - }); - - expect(orchestrate).toHaveBeenCalledWith({ - agent: "claude", - skipMigrations: true, - preview: undefined, - projectId: undefined, - orgId: undefined, - branchId: undefined, - }); - }); - - test("should pass preview to interactiveInit", async () => { - const { handler } = await import("./init.js"); - const { interactiveInit } = await import("../init/interactive.js"); - - await handler({ preview: true }); - - expect(interactiveInit).toHaveBeenCalledWith({ - preview: true, - projectId: undefined, - orgId: undefined, - branchId: undefined, - }); - }); - - test("should pass preview to orchestrate in agent mode", async () => { - const { handler } = await import("./init.js"); - const { orchestrate } = await import("../init/orchestrate.js"); - const { detectAgent } = await import("../init/detect_agent.js"); - vi.mocked(detectAgent).mockReturnValue("cursor"); - - await handler({ agent: true, preview: true }); - - expect(orchestrate).toHaveBeenCalledWith({ - agent: "cursor", - skipMigrations: undefined, - preview: true, - projectId: undefined, - orgId: undefined, - branchId: undefined, - }); - }); - test("routes a --data step and writes the enriched response as bare JSON on stdout", async () => { const { handler } = await import("./init.js"); const { routeDataStep } = await import("../init/route_command.js"); diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index c4fb45f6..d00d69e9 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -3,7 +3,6 @@ import type yargs from "yargs"; import { closeAnalytics, sendError } from "../analytics.js"; import { detectAgent } from "../init/detect_agent.js"; import { enrichResponse } from "../init/enrich_output.js"; -import { assertSafeId } from "../init/ids.js"; import { interactiveInit } from "../init/interactive.js"; import { orchestrate } from "../init/orchestrate.js"; import { routeDataStep } from "../init/route_command.js"; @@ -28,32 +27,6 @@ export const builder = (yargs: yargs.Argv) => describe: 'JSON object with a "step" field to route to a specific phase and phase-specific options.', }) - .option("skip-migrations", { - type: "boolean", - default: false, - describe: "Skip the migrations phase.", - }) - .option("preview", { - type: "boolean", - default: false, - describe: - "Enable preview features (e.g. project bootstrapping from templates).", - }) - .option("project-id", { - type: "string", - describe: - "Use an existing Neon project by ID. Skips organization and project selection.", - }) - .option("org-id", { - type: "string", - describe: - "Scope setup to an existing organization by ID. Skips organization selection.", - }) - .option("branch-id", { - type: "string", - describe: - "Target a specific branch by ID when pulling environment variables.", - }) .strict(false); /** @@ -91,11 +64,6 @@ const parsePosition = (parseError: unknown): string => { export const handler = async (argv: { agent?: boolean; data?: string; - skipMigrations?: boolean; - preview?: boolean; - projectId?: string; - orgId?: string; - branchId?: string; profile?: string; }) => { // Auto-detect agent from environment. When --agent is explicitly passed, @@ -134,13 +102,6 @@ export const handler = async (argv: { ); } - // Validate IDs up front — they are interpolated into shell commands - // downstream, and an early, well-shaped rejection beats a confusing - // failure several phases later. - if (argv.projectId) assertSafeId(argv.projectId, "project ID"); - if (argv.orgId) assertSafeId(argv.orgId, "org ID"); - if (argv.branchId) assertSafeId(argv.branchId, "branch ID"); - // --data with a "step" field routes to the appropriate phase if (argv.data && isAgentMode) { let data: Record; @@ -164,23 +125,9 @@ export const handler = async (argv: { } if (isAgentMode) { - writeAgentResponse( - await orchestrate({ - agent, - skipMigrations: argv.skipMigrations, - preview: argv.preview, - projectId: argv.projectId, - orgId: argv.orgId, - branchId: argv.branchId, - }), - ); + writeAgentResponse(await orchestrate({ agent })); } else { - await interactiveInit({ - preview: argv.preview, - projectId: argv.projectId, - orgId: argv.orgId, - branchId: argv.branchId, - }); + await interactiveInit(); } } catch (error) { const cause = error instanceof Error ? error : new Error(String(error)); diff --git a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap b/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap index f675a4ce..1cc24d1f 100644 --- a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap +++ b/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap @@ -184,256 +184,6 @@ exports[`neon init --agent: every step, against every project shape > connected " `; -exports[`neon init --agent: every step, against every project shape > connected > db 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "ready", - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon orgs list --output json", - "description": "Listing your Neon organizations.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"orgs-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > db-error 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "error", - "error": "project create failed", - "nextAction": { - "type": "ask_user", - "question": "An error occurred during database setup: project create failed. Would you like to try again or skip this step?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, - { - "value": "skip", - "label": "Skip database setup" - } - ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > db-with-org 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "org_selected", - "org": { - "id": "org-snap-1" - }, - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon projects list --org-id org-snap-1 --output json", - "description": "Listing Neon projects.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgId\\":\\"org-snap-1\\",\\"projectsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"projects-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > db-with-project 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "project_ready", - "project": { - "id": "proj-snap-1" - }, - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "steps": [ - { - "id": "get_connection_string", - "description": "Get the database connection string", - "command": "CI= npx -y neon connection-string --project-id proj-snap-1" - }, - { - "id": "store_env", - "description": "Append DATABASE_URL= to .env. Create .env if it doesn't exist. Do NOT overwrite existing entries. Ensure .env is in .gitignore." - }, - { - "id": "detect_framework", - "description": "Examine the project to determine the framework (Next.js, Remix, Express, etc.) and ORM (Prisma, Drizzle, raw SQL) in use." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > finalize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "complete", - "nextAction": { - "type": "complete", - "message": "Neon setup is complete! Your database is connected and your agent has the Neon MCP server and skills available." - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > connected > getting-started-stack 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "run_migrations", - "description": "Check if the prisma/migrations directory contains migration folders. If migrations exist, apply them with \`npx --no prisma migrate deploy\`. If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run \`npx --no prisma migrate dev --name init\` to create and apply the initial migration. If no models are defined, skip this step. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - exports[`neon init --agent: every step, against every project shape > connected > mcp-install 1`] = ` "exit: 0 --- stdout --- @@ -510,312 +260,52 @@ exports[`neon init --agent: every step, against every project shape > connected " `; -exports[`neon init --agent: every step, against every project shape > connected > migrations 1`] = ` +exports[`neon init --agent: every step, against every project shape > connected > setup 1`] = ` "exit: 0 --- stdout --- { - "phase": "migrations", - "status": "detection_needed", + "phase": "setup", + "status": "pending", + "detectedIde": "cursor", + "installedEditors": , + "mcpConfigured": null, + "mcpScope": null, + "skillsInstalled": null, + "skillsScope": null, "nextAction": { "type": "agent_check", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { - "id": "existing_migrations", - "description": "Check if the project has existing database migrations", + "id": "neonctl", + "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", + "lookFor": [] + }, + { + "id": "mcp_server", + "description": "Check if the Neon MCP server is already configured in your MCP server list", "lookFor": [ - "prisma/migrations/ directory or schema.prisma file", - "drizzle/ directory or drizzle.config.ts", - "migrations/ or db/migrations/ directory", - "knex migration files (knexfile.js/ts)" + "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"tool\\":\\"\\",\\"migrationDir\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > connected > migrations-apply 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "applying", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "ensure_env", - "description": "Verify DATABASE_URL is set in .env" }, { - "id": "apply", - "description": "Apply migrations to the Neon database. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" + "id": "agent_type", + "description": "Identify which coding agent is running this command", + "lookFor": [ + "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", + "Report your own agent identifier — this is used to configure the MCP server for the correct tool" + ] }, { - "id": "generate", - "description": "Generate the Prisma client.", - "command": "npx --no prisma generate" + "id": "extension_installed", + "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", + "lookFor": [ + "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", + "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" + ] } ], - "onComplete": { - "type": "complete", - "message": "Database migrations applied successfully." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > connected > migrations-prisma 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "found", - "detected": { - "tool": "prisma", - "migrationDir": null - }, - "nextAction": { - "type": "ask_user", - "question": "Found prisma migrations. Would you like to apply them to your Neon database?", - "options": [ - { - "value": "apply", - "label": "Yes, apply migrations" - }, - { - "value": "skip", - "label": "Skip for now" - } - ], - "responseMapping": { - "apply": { - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"apply\\":true,\\"tool\\":\\"prisma\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\",\\"skipMigrations\\":true}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > connected > migrations-scaffold 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "scaffolding", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "install_prisma", - "description": "Install Prisma as a dev dependency. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install -D prisma" - }, - { - "id": "init_prisma", - "description": "Initialize Prisma with PostgreSQL provider. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma init --datasource-provider postgresql" - }, - { - "id": "configure_env", - "description": "Ensure DATABASE_URL in .env points to your Neon database. The prisma init command may have created a placeholder — replace it with the real connection string if needed." - }, - { - "id": "create_schema", - "description": "Help the user define their initial schema in prisma/schema.prisma based on their application needs." - }, - { - "id": "run_migration", - "description": "Create and apply the initial migration. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate dev --name init" - } - ], - "onComplete": { - "type": "complete", - "message": "Prisma is set up with your Neon database. You can now define models in schema.prisma and run migrations with \`npx --no prisma migrate dev\`." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > connected > neon-auth-info 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "info", - "nextAction": { - "type": "ask_user", - "question": "Neon Auth provides drop-in user authentication that integrates with your Neon database. It handles user sign-up, sign-in, and session management. Would you like to set it up?", - "options": [ - { - "value": "yes", - "label": "Yes, set up Neon Auth" - }, - { - "value": "no", - "label": "No, skip for now" - } - ], - "context": "Full documentation: https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "responseMapping": { - "yes": { - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\",\\"setup\\":true}'" - }, - "no": { - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > connected > neon-auth-setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "in_progress", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "steps": [ - { - "id": "provision", - "description": "Enable Neon Auth on the project using the Neon CLI. Run: \`CI= npx -y neon neon-auth enable --project-id --output json\`. You can check current status with: \`CI= npx -y neon neon-auth status --project-id --output json\`. Project ID: proj-snap-1." - }, - { - "id": "install_packages", - "description": "Install required packages per the skill reference. The exact packages depend on the framework (Next.js, React, etc.)." - }, - { - "id": "create_components", - "description": "Create auth components per the skill reference. Follow the exact patterns and imports specified." - }, - { - "id": "pull_env", - "description": "Run \`CI= npx -y neon env pull\` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.", - "command": "CI= npx -y neon env pull" - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'", - "description": "Run this command to complete the setup. This is the final step — do not run any other neon init commands after this." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > connected > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, + "userPreferences": [ { "id": "mode", "question": "Use default settings or customize?", @@ -905,7 +395,7 @@ exports[`neon init --agent: every step, against every project shape > connected ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -939,8 +429,8 @@ exports[`neon init --agent: every step, against every project shape > connected } ], "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"hasConnectionString\\\\\\":true,\\\\\\"framework\\\\\\":\\\\\\"next\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"none\\\\\\"}\\"}'" + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- @@ -978,8 +468,8 @@ exports[`neon init --agent: every step, against every project shape > connected } ], "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"next\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"none\\\\\\"}\\"}'" + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- @@ -1101,49 +591,6 @@ exports[`neon init --agent: every step, against every project shape > connected " `; -exports[`neon init --agent: every step, against every project shape > connected > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": true - }, - "migrations": { - "tool": "prisma", - "hasMigrations": false - }, - "recommendations": [ - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - }, - { - "priority": "medium", - "message": "prisma detected but no migrations found", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\"}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - exports[`neon init --agent: every step, against every project shape > drizzle > auth 1`] = ` "exit: 0 --- stdout --- @@ -1212,25 +659,43 @@ exports[`neon init --agent: every step, against every project shape > drizzle > " `; -exports[`neon init --agent: every step, against every project shape > drizzle > db 1`] = ` +exports[`neon init --agent: every step, against every project shape > drizzle > mcp-install 1`] = ` "exit: 0 --- stdout --- { - "phase": "db", - "status": "ready", + "phase": "tooling", + "status": "installing", "nextAction": { "type": "run_command", - "command": "CI= npx -y neon orgs list --output json", - "description": "Listing your Neon organizations.", - "timeout": 30000, + "command": "npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor", + "description": "Installing Neon MCP server (global scope) for cursor.", + "timeout": 60000, "onSuccess": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgsResult\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" }, "onFailure": { "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"orgs-list-failed\\"}'" + "type": "ask_user", + "question": "Failed to install the Neon MCP server automatically. Would you like to try again or configure it manually?", + "options": [ + { + "value": "retry", + "label": "Try again" + }, + { + "value": "manual", + "label": "I'll configure it manually" + } + ], + "responseMapping": { + "retry": { + "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" + }, + "manual": { + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } + } } } } @@ -1241,65 +706,26 @@ exports[`neon init --agent: every step, against every project shape > drizzle > " `; -exports[`neon init --agent: every step, against every project shape > drizzle > db-error 1`] = ` +exports[`neon init --agent: every step, against every project shape > drizzle > mcp-status 1`] = ` "exit: 0 --- stdout --- { - "phase": "db", - "status": "error", - "error": "project create failed", + "phase": "tooling", + "status": "status", "nextAction": { - "type": "ask_user", - "question": "An error occurred during database setup: project create failed. Would you like to try again or skip this step?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, + "type": "agent_check", + "checks": [ { - "value": "skip", - "label": "Skip database setup" + "id": "mcp_server", + "description": "Check if the Neon MCP server is already configured in your MCP server list", + "lookFor": [ + "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" + ] } ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > db-with-org 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "org_selected", - "org": { - "id": "org-snap-1" - }, - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon projects list --org-id org-snap-1 --output json", - "description": "Listing Neon projects.", - "timeout": 30000, - "onSuccess": { + "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgId\\":\\"org-snap-1\\",\\"projectsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"projects-list-failed\\"}'" - } + "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" } } } @@ -1309,36 +735,142 @@ exports[`neon init --agent: every step, against every project shape > drizzle > " `; -exports[`neon init --agent: every step, against every project shape > drizzle > db-with-project 1`] = ` +exports[`neon init --agent: every step, against every project shape > drizzle > setup 1`] = ` "exit: 0 --- stdout --- { - "phase": "db", - "status": "project_ready", - "project": { - "id": "proj-snap-1" - }, + "phase": "setup", + "status": "pending", + "detectedIde": "cursor", + "installedEditors": , + "mcpConfigured": null, + "mcpScope": null, + "skillsInstalled": null, + "skillsScope": null, "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "steps": [ + "type": "agent_check", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "checks": [ { - "id": "get_connection_string", - "description": "Get the database connection string", - "command": "CI= npx -y neon connection-string --project-id proj-snap-1" + "id": "neonctl", + "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", + "lookFor": [] }, { - "id": "store_env", - "description": "Append DATABASE_URL= to .env. Create .env if it doesn't exist. Do NOT overwrite existing entries. Ensure .env is in .gitignore." + "id": "mcp_server", + "description": "Check if the Neon MCP server is already configured in your MCP server list", + "lookFor": [ + "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" + ] + }, + { + "id": "agent_type", + "description": "Identify which coding agent is running this command", + "lookFor": [ + "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", + "Report your own agent identifier — this is used to configure the MCP server for the correct tool" + ] }, { - "id": "detect_framework", - "description": "Examine the project to determine the framework (Next.js, Remix, Express, etc.) and ORM (Prisma, Drizzle, raw SQL) in use." + "id": "extension_installed", + "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", + "lookFor": [ + "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", + "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" + ] } ], - "onComplete": { + "userPreferences": [ + { + "id": "mode", + "question": "Use default settings or customize?", + "phase": "after_checks", + "options": [ + { + "value": "defaults", + "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + }, + { + "value": "customize", + "label": "Customize installation settings" + } + ], + "default": "defaults" + }, + { + "id": "mcpScope", + "question": "Where should the Neon MCP server be configured?", + "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", + "phase": "after_checks", + "options": [ + { + "value": "global", + "label": "Global (available in all projects)" + }, + { + "value": "project", + "label": "Project-level (scoped to this project only)" + }, + { + "value": "none", + "label": "Skip — do not install the MCP server" + } + ], + "default": "global", + "condition": { + "preferenceId": "mode", + "equals": "customize" + }, + "group": "customize" + }, + { + "id": "skillsScope", + "question": "Where should Neon agent skills be installed?", + "context": "Only ask if skills are not already installed.", + "phase": "after_checks", + "options": [ + { + "value": "global", + "label": "Global (available in all projects)" + }, + { + "value": "project", + "label": "Project-level (scoped to this project only)" + } + ], + "default": "project", + "condition": { + "preferenceId": "mode", + "equals": "customize" + }, + "group": "customize" + }, + { + "id": "installExtension", + "question": "Install the Neon editor extension for local database browsing?", + "phase": "after_checks", + "options": [ + { + "value": "true", + "label": "Yes" + }, + { + "value": "false", + "label": "No" + } + ], + "default": "true", + "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", + "condition": { + "preferenceId": "mode", + "equals": "customize" + }, + "group": "customize" + } + ], + "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -1348,159 +880,115 @@ exports[`neon init --agent: every step, against every project shape > drizzle > " `; -exports[`neon init --agent: every step, against every project shape > drizzle > finalize 1`] = ` +exports[`neon init --agent: every step, against every project shape > drizzle > setup-customize 1`] = ` "exit: 0 --- stdout --- { "phase": "setup", - "status": "complete", + "status": "installed", + "results": [ + { + "id": "neonctl", + "description": "Neon CLI is up to date (v2.45.0)", + "status": "success" + }, + { + "id": "install_mcp", + "description": "Installed Neon MCP server (global scope)", + "status": "success" + }, + { + "id": "install_skills", + "description": "Neon agent skills installed", + "status": "success" + } + ], "nextAction": { "type": "complete", - "message": "Neon setup is complete! Your database is connected and your agent has the Neon MCP server and skills available." - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- --- subprocesses --- +/neonctl --version +/npm view neonctl version +/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y /skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" `; -exports[`neon init --agent: every step, against every project shape > drizzle > getting-started-stack 1`] = ` +exports[`neon init --agent: every step, against every project shape > drizzle > setup-defaults 1`] = ` "exit: 0 --- stdout --- { "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "run_migrations", - "description": "Check if the prisma/migrations directory contains migration folders. If migrations exist, apply them with \`npx --no prisma migrate deploy\`. If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run \`npx --no prisma migrate dev --name init\` to create and apply the initial migration. If no models are defined, skip this step. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" + "status": "installed", + "results": [ + { + "id": "neonctl", + "description": "Neon CLI is up to date (v2.45.0)", + "status": "success" + }, + { + "id": "install_mcp", + "description": "Installed Neon MCP server (global scope)", + "status": "success" + }, + { + "id": "install_skills", + "description": "Neon agent skills installed", + "status": "success" } + ], + "nextAction": { + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- --- subprocesses --- +/neonctl --version +/npm view neonctl version +/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y /skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" `; -exports[`neon init --agent: every step, against every project shape > drizzle > mcp-install 1`] = ` +exports[`neon init --agent: every step, against every project shape > drizzle > skills-install 1`] = ` "exit: 0 --- stdout --- { "phase": "tooling", - "status": "installing", + "status": "installing_skills", "nextAction": { "type": "run_command", - "command": "npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor", - "description": "Installing Neon MCP server (global scope) for cursor.", - "timeout": 60000, + "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", + "description": "Installing Neon agent skills for cursor.", + "timeout": 30000, "onSuccess": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" }, "onFailure": { "other": { - "type": "ask_user", - "question": "Failed to install the Neon MCP server automatically. Would you like to try again or configure it manually?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, - { - "value": "manual", - "label": "I'll configure it manually" - } - ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "manual": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" } } + }, + "skillReferences": { + "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", + "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", + "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", + "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", + "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", + "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", + "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", + "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" } } --- stderr --- @@ -1509,26 +997,27 @@ exports[`neon init --agent: every step, against every project shape > drizzle > " `; -exports[`neon init --agent: every step, against every project shape > drizzle > mcp-status 1`] = ` +exports[`neon init --agent: every step, against every project shape > drizzle > skills-status 1`] = ` "exit: 0 --- stdout --- { "phase": "tooling", - "status": "status", + "status": "skills_check", "nextAction": { "type": "agent_check", "checks": [ { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", + "id": "skills", + "description": "Check if Neon agent skills are installed in this project", "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" + "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", + "A .skills/ directory with neon-related content" ] } ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" } } } @@ -1538,194 +1027,150 @@ exports[`neon init --agent: every step, against every project shape > drizzle > " `; -exports[`neon init --agent: every step, against every project shape > drizzle > migrations 1`] = ` +exports[`neon init --agent: every step, against every project shape > drizzle > skills-update 1`] = ` "exit: 0 --- stdout --- { - "phase": "migrations", - "status": "detection_needed", + "phase": "tooling", + "status": "installing_skills", "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "existing_migrations", - "description": "Check if the project has existing database migrations", - "lookFor": [ - "prisma/migrations/ directory or schema.prisma file", - "drizzle/ directory or drizzle.config.ts", - "migrations/ or db/migrations/ directory", - "knex migration files (knexfile.js/ts)" - ] - } - ], - "reportBack": { + "type": "run_command", + "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", + "description": "Installing Neon agent skills for cursor.", + "timeout": 30000, + "onSuccess": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"tool\\":\\"\\",\\"migrationDir\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + }, + "onFailure": { + "other": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } } + }, + "skillReferences": { + "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", + "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", + "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", + "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", + "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", + "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", + "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", + "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" } } --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > drizzle > migrations-apply 1`] = ` +exports[`neon init --agent: every step, against every project shape > fully-configured > auth 1`] = ` "exit: 0 --- stdout --- { - "phase": "migrations", - "status": "applying", - "tool": "prisma", + "phase": "auth", + "status": "verified", "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "ensure_env", - "description": "Verify DATABASE_URL is set in .env" - }, - { - "id": "apply", - "description": "Apply migrations to the Neon database. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "generate", - "description": "Generate the Prisma client.", - "command": "npx --no prisma generate" - } - ], - "onComplete": { - "type": "complete", - "message": "Database migrations applied successfully." - } + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" } } --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > drizzle > migrations-prisma 1`] = ` +exports[`neon init --agent: every step, against every project shape > fully-configured > auth-existing 1`] = ` "exit: 0 --- stdout --- { - "phase": "migrations", - "status": "found", - "detected": { - "tool": "prisma", - "migrationDir": null - }, + "phase": "auth", + "status": "verified", "nextAction": { - "type": "ask_user", - "question": "Found prisma migrations. Would you like to apply them to your Neon database?", - "options": [ - { - "value": "apply", - "label": "Yes, apply migrations" - }, - { - "value": "skip", - "label": "Skip for now" - } - ], - "responseMapping": { - "apply": { - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"apply\\":true,\\"tool\\":\\"prisma\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\",\\"skipMigrations\\":true}'" - } - } + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" } } --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > drizzle > migrations-scaffold 1`] = ` +exports[`neon init --agent: every step, against every project shape > fully-configured > auth-new 1`] = ` "exit: 0 --- stdout --- { - "phase": "migrations", - "status": "scaffolding", - "tool": "prisma", + "phase": "auth", + "status": "verified", "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "install_prisma", - "description": "Install Prisma as a dev dependency. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install -D prisma" - }, - { - "id": "init_prisma", - "description": "Initialize Prisma with PostgreSQL provider. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma init --datasource-provider postgresql" - }, - { - "id": "configure_env", - "description": "Ensure DATABASE_URL in .env points to your Neon database. The prisma init command may have created a placeholder — replace it with the real connection string if needed." - }, - { - "id": "create_schema", - "description": "Help the user define their initial schema in prisma/schema.prisma based on their application needs." - }, - { - "id": "run_migration", - "description": "Create and apply the initial migration. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate dev --name init" - } - ], - "onComplete": { - "type": "complete", - "message": "Prisma is set up with your Neon database. You can now define models in schema.prisma and run migrations with \`npx --no prisma migrate dev\`." - } + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" } } --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > drizzle > neon-auth-info 1`] = ` +exports[`neon init --agent: every step, against every project shape > fully-configured > auth-verify 1`] = ` "exit: 0 --- stdout --- { - "phase": "neon_auth", - "status": "info", + "phase": "auth", + "status": "verified", "nextAction": { - "type": "ask_user", - "question": "Neon Auth provides drop-in user authentication that integrates with your Neon database. It handles user sign-up, sign-in, and session management. Would you like to set it up?", - "options": [ - { - "value": "yes", - "label": "Yes, set up Neon Auth" - }, - { - "value": "no", - "label": "No, skip for now" - } - ], - "context": "Full documentation: https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "responseMapping": { - "yes": { - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\",\\"setup\\":true}'" - }, - "no": { - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'" + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } +} +--- stderr --- + +--- subprocesses --- +" +`; + +exports[`neon init --agent: every step, against every project shape > fully-configured > mcp-install 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "tooling", + "status": "installing", + "nextAction": { + "type": "run_command", + "command": "npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor", + "description": "Installing Neon MCP server (global scope) for cursor.", + "timeout": 60000, + "onSuccess": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" + }, + "onFailure": { + "other": { + "type": "ask_user", + "question": "Failed to install the Neon MCP server automatically. Would you like to try again or configure it manually?", + "options": [ + { + "value": "retry", + "label": "Try again" + }, + { + "value": "manual", + "label": "I'll configure it manually" + } + ], + "responseMapping": { + "retry": { + "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" + }, + "manual": { + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } + } } } } @@ -1733,55 +1178,39 @@ exports[`neon init --agent: every step, against every project shape > drizzle > --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > drizzle > neon-auth-setup 1`] = ` +exports[`neon init --agent: every step, against every project shape > fully-configured > mcp-status 1`] = ` "exit: 0 --- stdout --- { - "phase": "neon_auth", - "status": "in_progress", + "phase": "tooling", + "status": "status", "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "steps": [ - { - "id": "provision", - "description": "Enable Neon Auth on the project using the Neon CLI. Run: \`CI= npx -y neon neon-auth enable --project-id --output json\`. You can check current status with: \`CI= npx -y neon neon-auth status --project-id --output json\`. Project ID: proj-snap-1." - }, - { - "id": "install_packages", - "description": "Install required packages per the skill reference. The exact packages depend on the framework (Next.js, React, etc.)." - }, - { - "id": "create_components", - "description": "Create auth components per the skill reference. Follow the exact patterns and imports specified." - }, + "type": "agent_check", + "checks": [ { - "id": "pull_env", - "description": "Run \`CI= npx -y neon env pull\` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.", - "command": "CI= npx -y neon env pull" + "id": "mcp_server", + "description": "Check if the Neon MCP server is already configured in your MCP server list", + "lookFor": [ + "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" + ] } ], - "onComplete": { + "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'", - "description": "Run this command to complete the setup. This is the final step — do not run any other neon init commands after this." + "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" } } } --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > drizzle > setup 1`] = ` +exports[`neon init --agent: every step, against every project shape > fully-configured > setup 1`] = ` "exit: 0 --- stdout --- { @@ -1795,7 +1224,7 @@ exports[`neon init --agent: every step, against every project shape > drizzle > "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -1827,23 +1256,6 @@ exports[`neon init --agent: every step, against every project shape > drizzle > } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -1933,7 +1345,7 @@ exports[`neon init --agent: every step, against every project shape > drizzle > ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -1943,7 +1355,7 @@ exports[`neon init --agent: every step, against every project shape > drizzle > " `; -exports[`neon init --agent: every step, against every project shape > drizzle > setup-customize 1`] = ` +exports[`neon init --agent: every step, against every project shape > fully-configured > setup-customize 1`] = ` "exit: 0 --- stdout --- { @@ -1967,8 +1379,8 @@ exports[`neon init --agent: every step, against every project shape > drizzle > } ], "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"hono\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"drizzle\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"drizzle\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"drizzle\\\\\\"}\\"}'" + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- @@ -1977,12 +1389,10 @@ exports[`neon init --agent: every step, against every project shape > drizzle > /neonctl --version /npm view neonctl version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +/skills --version" `; -exports[`neon init --agent: every step, against every project shape > drizzle > setup-defaults 1`] = ` +exports[`neon init --agent: every step, against every project shape > fully-configured > setup-defaults 1`] = ` "exit: 0 --- stdout --- { @@ -2006,8 +1416,8 @@ exports[`neon init --agent: every step, against every project shape > drizzle > } ], "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"hono\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"drizzle\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"drizzle\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"drizzle\\\\\\"}\\"}'" + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- @@ -2016,12 +1426,10 @@ exports[`neon init --agent: every step, against every project shape > drizzle > /neonctl --version /npm view neonctl version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +/skills --version" `; -exports[`neon init --agent: every step, against every project shape > drizzle > skills-install 1`] = ` +exports[`neon init --agent: every step, against every project shape > fully-configured > skills-install 1`] = ` "exit: 0 --- stdout --- { @@ -2060,7 +1468,7 @@ exports[`neon init --agent: every step, against every project shape > drizzle > " `; -exports[`neon init --agent: every step, against every project shape > drizzle > skills-status 1`] = ` +exports[`neon init --agent: every step, against every project shape > fully-configured > skills-status 1`] = ` "exit: 0 --- stdout --- { @@ -2090,7 +1498,7 @@ exports[`neon init --agent: every step, against every project shape > drizzle > " `; -exports[`neon init --agent: every step, against every project shape > drizzle > skills-update 1`] = ` +exports[`neon init --agent: every step, against every project shape > fully-configured > skills-update 1`] = ` "exit: 0 --- stdout --- { @@ -2129,50 +1537,7 @@ exports[`neon init --agent: every step, against every project shape > drizzle > " `; -exports[`neon init --agent: every step, against every project shape > drizzle > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": false - }, - "migrations": { - "tool": "drizzle", - "hasMigrations": true - }, - "recommendations": [ - { - "priority": "high", - "message": "No DATABASE_URL found in .env", - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > auth 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > auth 1`] = ` "exit: 0 --- stdout --- { @@ -2189,7 +1554,7 @@ exports[`neon init --agent: every step, against every project shape > fully-conf " `; -exports[`neon init --agent: every step, against every project shape > fully-configured > auth-existing 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > auth-existing 1`] = ` "exit: 0 --- stdout --- { @@ -2206,7 +1571,7 @@ exports[`neon init --agent: every step, against every project shape > fully-conf " `; -exports[`neon init --agent: every step, against every project shape > fully-configured > auth-new 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > auth-new 1`] = ` "exit: 0 --- stdout --- { @@ -2223,7 +1588,7 @@ exports[`neon init --agent: every step, against every project shape > fully-conf " `; -exports[`neon init --agent: every step, against every project shape > fully-configured > auth-verify 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > auth-verify 1`] = ` "exit: 0 --- stdout --- { @@ -2240,253 +1605,7 @@ exports[`neon init --agent: every step, against every project shape > fully-conf " `; -exports[`neon init --agent: every step, against every project shape > fully-configured > db 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "ready", - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon orgs list --output json", - "description": "Listing your Neon organizations.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"orgs-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > db-error 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "error", - "error": "project create failed", - "nextAction": { - "type": "ask_user", - "question": "An error occurred during database setup: project create failed. Would you like to try again or skip this step?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, - { - "value": "skip", - "label": "Skip database setup" - } - ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > db-with-org 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "org_selected", - "org": { - "id": "org-snap-1" - }, - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon projects list --org-id org-snap-1 --output json", - "description": "Listing Neon projects.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgId\\":\\"org-snap-1\\",\\"projectsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"projects-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > db-with-project 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "project_ready", - "project": { - "id": "proj-snap-1" - }, - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "steps": [ - { - "id": "get_connection_string", - "description": "Get the database connection string", - "command": "CI= npx -y neon connection-string --project-id proj-snap-1" - }, - { - "id": "store_env", - "description": "Append DATABASE_URL= to .env. Create .env if it doesn't exist. Do NOT overwrite existing entries. Ensure .env is in .gitignore." - }, - { - "id": "detect_framework", - "description": "Examine the project to determine the framework (Next.js, Remix, Express, etc.) and ORM (Prisma, Drizzle, raw SQL) in use." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > finalize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "complete", - "nextAction": { - "type": "complete", - "message": "Neon setup is complete! Your database is connected and your agent has the Neon MCP server and skills available." - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > getting-started-stack 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "run_migrations", - "description": "Check if the prisma/migrations directory contains migration folders. If migrations exist, apply them with \`npx --no prisma migrate deploy\`. If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run \`npx --no prisma migrate dev --name init\` to create and apply the initial migration. If no models are defined, skip this step. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > mcp-install 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > mcp-install 1`] = ` "exit: 0 --- stdout --- { @@ -2533,7 +1652,7 @@ exports[`neon init --agent: every step, against every project shape > fully-conf " `; -exports[`neon init --agent: every step, against every project shape > fully-configured > mcp-status 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > mcp-status 1`] = ` "exit: 0 --- stdout --- { @@ -2562,315 +1681,67 @@ exports[`neon init --agent: every step, against every project shape > fully-conf " `; -exports[`neon init --agent: every step, against every project shape > fully-configured > migrations 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > setup 1`] = ` "exit: 0 --- stdout --- { - "phase": "migrations", - "status": "detection_needed", + "phase": "setup", + "status": "pending", + "detectedIde": "cursor", + "installedEditors": , + "mcpConfigured": null, + "mcpScope": null, + "skillsInstalled": null, + "skillsScope": null, "nextAction": { "type": "agent_check", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { - "id": "existing_migrations", - "description": "Check if the project has existing database migrations", + "id": "neonctl", + "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", + "lookFor": [] + }, + { + "id": "mcp_server", + "description": "Check if the Neon MCP server is already configured in your MCP server list", "lookFor": [ - "prisma/migrations/ directory or schema.prisma file", - "drizzle/ directory or drizzle.config.ts", - "migrations/ or db/migrations/ directory", - "knex migration files (knexfile.js/ts)" + "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"tool\\":\\"\\",\\"migrationDir\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > migrations-apply 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "applying", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "ensure_env", - "description": "Verify DATABASE_URL is set in .env" }, { - "id": "apply", - "description": "Apply migrations to the Neon database. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" + "id": "agent_type", + "description": "Identify which coding agent is running this command", + "lookFor": [ + "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", + "Report your own agent identifier — this is used to configure the MCP server for the correct tool" + ] }, { - "id": "generate", - "description": "Generate the Prisma client.", - "command": "npx --no prisma generate" + "id": "extension_installed", + "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", + "lookFor": [ + "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", + "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" + ] } ], - "onComplete": { - "type": "complete", - "message": "Database migrations applied successfully." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > migrations-prisma 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "found", - "detected": { - "tool": "prisma", - "migrationDir": null - }, - "nextAction": { - "type": "ask_user", - "question": "Found prisma migrations. Would you like to apply them to your Neon database?", - "options": [ + "userPreferences": [ { - "value": "apply", - "label": "Yes, apply migrations" - }, - { - "value": "skip", - "label": "Skip for now" - } - ], - "responseMapping": { - "apply": { - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"apply\\":true,\\"tool\\":\\"prisma\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\",\\"skipMigrations\\":true}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > migrations-scaffold 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "scaffolding", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "install_prisma", - "description": "Install Prisma as a dev dependency. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install -D prisma" - }, - { - "id": "init_prisma", - "description": "Initialize Prisma with PostgreSQL provider. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma init --datasource-provider postgresql" - }, - { - "id": "configure_env", - "description": "Ensure DATABASE_URL in .env points to your Neon database. The prisma init command may have created a placeholder — replace it with the real connection string if needed." - }, - { - "id": "create_schema", - "description": "Help the user define their initial schema in prisma/schema.prisma based on their application needs." - }, - { - "id": "run_migration", - "description": "Create and apply the initial migration. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate dev --name init" - } - ], - "onComplete": { - "type": "complete", - "message": "Prisma is set up with your Neon database. You can now define models in schema.prisma and run migrations with \`npx --no prisma migrate dev\`." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > neon-auth-info 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "info", - "nextAction": { - "type": "ask_user", - "question": "Neon Auth provides drop-in user authentication that integrates with your Neon database. It handles user sign-up, sign-in, and session management. Would you like to set it up?", - "options": [ - { - "value": "yes", - "label": "Yes, set up Neon Auth" - }, - { - "value": "no", - "label": "No, skip for now" - } - ], - "context": "Full documentation: https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "responseMapping": { - "yes": { - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\",\\"setup\\":true}'" - }, - "no": { - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > neon-auth-setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "in_progress", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "steps": [ - { - "id": "provision", - "description": "Enable Neon Auth on the project using the Neon CLI. Run: \`CI= npx -y neon neon-auth enable --project-id --output json\`. You can check current status with: \`CI= npx -y neon neon-auth status --project-id --output json\`. Project ID: proj-snap-1." - }, - { - "id": "install_packages", - "description": "Install required packages per the skill reference. The exact packages depend on the framework (Next.js, React, etc.)." - }, - { - "id": "create_components", - "description": "Create auth components per the skill reference. Follow the exact patterns and imports specified." - }, - { - "id": "pull_env", - "description": "Run \`CI= npx -y neon env pull\` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.", - "command": "CI= npx -y neon env pull" - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'", - "description": "Run this command to complete the setup. This is the final step — do not run any other neon init commands after this." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" + "id": "mode", + "question": "Use default settings or customize?", + "phase": "after_checks", + "options": [ + { + "value": "defaults", + "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + }, + { + "value": "customize", + "label": "Customize installation settings" + } + ], + "default": "defaults" }, { "id": "mcpScope", @@ -2945,7 +1816,7 @@ exports[`neon init --agent: every step, against every project shape > fully-conf ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -2955,7 +1826,7 @@ exports[`neon init --agent: every step, against every project shape > fully-conf " `; -exports[`neon init --agent: every step, against every project shape > fully-configured > setup-customize 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > setup-customize 1`] = ` "exit: 0 --- stdout --- { @@ -2979,8 +1850,8 @@ exports[`neon init --agent: every step, against every project shape > fully-conf } ], "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"hasConnectionString\\\\\\":true,\\\\\\"framework\\\\\\":\\\\\\"next\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"prisma/migrations\\\\\\"}\\"}'" + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- @@ -2989,10 +1860,12 @@ exports[`neon init --agent: every step, against every project shape > fully-conf /neonctl --version /npm view neonctl version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor -/skills --version" +/skills --version +/skills add neondatabase/agent-skills --skill neon --agent cursor -y +/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" `; -exports[`neon init --agent: every step, against every project shape > fully-configured > setup-defaults 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > setup-defaults 1`] = ` "exit: 0 --- stdout --- { @@ -3016,8 +1889,8 @@ exports[`neon init --agent: every step, against every project shape > fully-conf } ], "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"next\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"prisma/migrations\\\\\\"}\\"}'" + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- @@ -3026,10 +1899,12 @@ exports[`neon init --agent: every step, against every project shape > fully-conf /neonctl --version /npm view neonctl version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor -/skills --version" +/skills --version +/skills add neondatabase/agent-skills --skill neon --agent cursor -y +/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" `; -exports[`neon init --agent: every step, against every project shape > fully-configured > skills-install 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > skills-install 1`] = ` "exit: 0 --- stdout --- { @@ -3068,7 +1943,7 @@ exports[`neon init --agent: every step, against every project shape > fully-conf " `; -exports[`neon init --agent: every step, against every project shape > fully-configured > skills-status 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > skills-status 1`] = ` "exit: 0 --- stdout --- { @@ -3098,7 +1973,7 @@ exports[`neon init --agent: every step, against every project shape > fully-conf " `; -exports[`neon init --agent: every step, against every project shape > fully-configured > skills-update 1`] = ` +exports[`neon init --agent: every step, against every project shape > greenfield > skills-update 1`] = ` "exit: 0 --- stdout --- { @@ -3137,39 +2012,7 @@ exports[`neon init --agent: every step, against every project shape > fully-conf " `; -exports[`neon init --agent: every step, against every project shape > fully-configured > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": true, - "scope": "project" - }, - "skills": { - "installed": true, - "scope": "project" - } - }, - "project": { - "databaseUrl": true - }, - "migrations": { - "tool": "prisma", - "hasMigrations": true - }, - "recommendations": [] -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > auth 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > auth 1`] = ` "exit: 0 --- stdout --- { @@ -3186,7 +2029,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > auth-existing 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > auth-existing 1`] = ` "exit: 0 --- stdout --- { @@ -3203,7 +2046,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > auth-new 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > auth-new 1`] = ` "exit: 0 --- stdout --- { @@ -3220,7 +2063,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > auth-verify 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > auth-verify 1`] = ` "exit: 0 --- stdout --- { @@ -3237,25 +2080,43 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > db 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > mcp-install 1`] = ` "exit: 0 --- stdout --- { - "phase": "db", - "status": "ready", + "phase": "tooling", + "status": "installing", "nextAction": { "type": "run_command", - "command": "CI= npx -y neon orgs list --output json", - "description": "Listing your Neon organizations.", - "timeout": 30000, + "command": "npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor", + "description": "Installing Neon MCP server (global scope) for cursor.", + "timeout": 60000, "onSuccess": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgsResult\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" }, "onFailure": { "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"orgs-list-failed\\"}'" + "type": "ask_user", + "question": "Failed to install the Neon MCP server automatically. Would you like to try again or configure it manually?", + "options": [ + { + "value": "retry", + "label": "Try again" + }, + { + "value": "manual", + "label": "I'll configure it manually" + } + ], + "responseMapping": { + "retry": { + "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" + }, + "manual": { + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } + } } } } @@ -3266,33 +2127,26 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > db-error 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > mcp-status 1`] = ` "exit: 0 --- stdout --- { - "phase": "db", - "status": "error", - "error": "project create failed", + "phase": "tooling", + "status": "status", "nextAction": { - "type": "ask_user", - "question": "An error occurred during database setup: project create failed. Would you like to try again or skip this step?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, + "type": "agent_check", + "checks": [ { - "value": "skip", - "label": "Skip database setup" + "id": "mcp_server", + "description": "Check if the Neon MCP server is already configured in your MCP server list", + "lookFor": [ + "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" + ] } ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } + "reportBack": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" } } } @@ -3302,68 +2156,142 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > db-with-org 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > setup 1`] = ` "exit: 0 --- stdout --- { - "phase": "db", - "status": "org_selected", - "org": { - "id": "org-snap-1" - }, + "phase": "setup", + "status": "pending", + "detectedIde": "cursor", + "installedEditors": , + "mcpConfigured": null, + "mcpScope": null, + "skillsInstalled": null, + "skillsScope": null, "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon projects list --org-id org-snap-1 --output json", - "description": "Listing Neon projects.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgId\\":\\"org-snap-1\\",\\"projectsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"projects-list-failed\\"}'" + "type": "agent_check", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "checks": [ + { + "id": "neonctl", + "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", + "lookFor": [] + }, + { + "id": "mcp_server", + "description": "Check if the Neon MCP server is already configured in your MCP server list", + "lookFor": [ + "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" + ] + }, + { + "id": "agent_type", + "description": "Identify which coding agent is running this command", + "lookFor": [ + "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", + "Report your own agent identifier — this is used to configure the MCP server for the correct tool" + ] + }, + { + "id": "extension_installed", + "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", + "lookFor": [ + "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", + "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" + ] } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > db-with-project 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "project_ready", - "project": { - "id": "proj-snap-1" - }, - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "steps": [ + ], + "userPreferences": [ + { + "id": "mode", + "question": "Use default settings or customize?", + "phase": "after_checks", + "options": [ + { + "value": "defaults", + "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + }, + { + "value": "customize", + "label": "Customize installation settings" + } + ], + "default": "defaults" + }, { - "id": "get_connection_string", - "description": "Get the database connection string", - "command": "CI= npx -y neon connection-string --project-id proj-snap-1" + "id": "mcpScope", + "question": "Where should the Neon MCP server be configured?", + "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", + "phase": "after_checks", + "options": [ + { + "value": "global", + "label": "Global (available in all projects)" + }, + { + "value": "project", + "label": "Project-level (scoped to this project only)" + }, + { + "value": "none", + "label": "Skip — do not install the MCP server" + } + ], + "default": "global", + "condition": { + "preferenceId": "mode", + "equals": "customize" + }, + "group": "customize" }, { - "id": "store_env", - "description": "Append DATABASE_URL= to .env. Create .env if it doesn't exist. Do NOT overwrite existing entries. Ensure .env is in .gitignore." + "id": "skillsScope", + "question": "Where should Neon agent skills be installed?", + "context": "Only ask if skills are not already installed.", + "phase": "after_checks", + "options": [ + { + "value": "global", + "label": "Global (available in all projects)" + }, + { + "value": "project", + "label": "Project-level (scoped to this project only)" + } + ], + "default": "project", + "condition": { + "preferenceId": "mode", + "equals": "customize" + }, + "group": "customize" }, { - "id": "detect_framework", - "description": "Examine the project to determine the framework (Next.js, Remix, Express, etc.) and ORM (Prisma, Drizzle, raw SQL) in use." + "id": "installExtension", + "question": "Install the Neon editor extension for local database browsing?", + "phase": "after_checks", + "options": [ + { + "value": "true", + "label": "Yes" + }, + { + "value": "false", + "label": "No" + } + ], + "default": "true", + "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", + "condition": { + "preferenceId": "mode", + "equals": "customize" + }, + "group": "customize" } ], - "onComplete": { + "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -3373,159 +2301,115 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > finalize 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > setup-customize 1`] = ` "exit: 0 --- stdout --- { "phase": "setup", - "status": "complete", + "status": "installed", + "results": [ + { + "id": "neonctl", + "description": "Neon CLI is up to date (v2.45.0)", + "status": "success" + }, + { + "id": "install_mcp", + "description": "Installed Neon MCP server (global scope)", + "status": "success" + }, + { + "id": "install_skills", + "description": "Neon agent skills installed", + "status": "success" + } + ], "nextAction": { "type": "complete", - "message": "Neon setup is complete! Your database is connected and your agent has the Neon MCP server and skills available." - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- --- subprocesses --- +/neonctl --version +/npm view neonctl version +/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y /skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" `; -exports[`neon init --agent: every step, against every project shape > greenfield > getting-started-stack 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > setup-defaults 1`] = ` "exit: 0 --- stdout --- { "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "run_migrations", - "description": "Check if the prisma/migrations directory contains migration folders. If migrations exist, apply them with \`npx --no prisma migrate deploy\`. If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run \`npx --no prisma migrate dev --name init\` to create and apply the initial migration. If no models are defined, skip this step. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" + "status": "installed", + "results": [ + { + "id": "neonctl", + "description": "Neon CLI is up to date (v2.45.0)", + "status": "success" + }, + { + "id": "install_mcp", + "description": "Installed Neon MCP server (global scope)", + "status": "success" + }, + { + "id": "install_skills", + "description": "Neon agent skills installed", + "status": "success" } + ], + "nextAction": { + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- --- subprocesses --- +/neonctl --version +/npm view neonctl version +/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y /skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" `; -exports[`neon init --agent: every step, against every project shape > greenfield > mcp-install 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > skills-install 1`] = ` "exit: 0 --- stdout --- { "phase": "tooling", - "status": "installing", + "status": "installing_skills", "nextAction": { "type": "run_command", - "command": "npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor", - "description": "Installing Neon MCP server (global scope) for cursor.", - "timeout": 60000, + "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", + "description": "Installing Neon agent skills for cursor.", + "timeout": 30000, "onSuccess": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" }, "onFailure": { "other": { - "type": "ask_user", - "question": "Failed to install the Neon MCP server automatically. Would you like to try again or configure it manually?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, - { - "value": "manual", - "label": "I'll configure it manually" - } - ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "manual": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" } } + }, + "skillReferences": { + "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", + "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", + "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", + "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", + "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", + "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", + "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", + "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" } } --- stderr --- @@ -3534,26 +2418,27 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > mcp-status 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > skills-status 1`] = ` "exit: 0 --- stdout --- { "phase": "tooling", - "status": "status", + "status": "skills_check", "nextAction": { "type": "agent_check", "checks": [ { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", + "id": "skills", + "description": "Check if Neon agent skills are installed in this project", "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" + "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", + "A .skills/ directory with neon-related content" ] } ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" } } } @@ -3563,194 +2448,150 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > migrations 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma > skills-update 1`] = ` "exit: 0 --- stdout --- { - "phase": "migrations", - "status": "detection_needed", + "phase": "tooling", + "status": "installing_skills", "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "existing_migrations", - "description": "Check if the project has existing database migrations", - "lookFor": [ - "prisma/migrations/ directory or schema.prisma file", - "drizzle/ directory or drizzle.config.ts", - "migrations/ or db/migrations/ directory", - "knex migration files (knexfile.js/ts)" - ] - } - ], - "reportBack": { + "type": "run_command", + "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", + "description": "Installing Neon agent skills for cursor.", + "timeout": 30000, + "onSuccess": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"tool\\":\\"\\",\\"migrationDir\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + }, + "onFailure": { + "other": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } } + }, + "skillReferences": { + "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", + "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", + "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", + "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", + "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", + "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", + "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", + "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" } } --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > greenfield > migrations-apply 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth 1`] = ` "exit: 0 --- stdout --- { - "phase": "migrations", - "status": "applying", - "tool": "prisma", + "phase": "auth", + "status": "verified", "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "ensure_env", - "description": "Verify DATABASE_URL is set in .env" - }, - { - "id": "apply", - "description": "Apply migrations to the Neon database. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "generate", - "description": "Generate the Prisma client.", - "command": "npx --no prisma generate" - } - ], - "onComplete": { - "type": "complete", - "message": "Database migrations applied successfully." - } + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" } } --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > greenfield > migrations-prisma 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth-existing 1`] = ` "exit: 0 --- stdout --- { - "phase": "migrations", - "status": "found", - "detected": { - "tool": "prisma", - "migrationDir": null - }, + "phase": "auth", + "status": "verified", "nextAction": { - "type": "ask_user", - "question": "Found prisma migrations. Would you like to apply them to your Neon database?", - "options": [ - { - "value": "apply", - "label": "Yes, apply migrations" - }, - { - "value": "skip", - "label": "Skip for now" - } - ], - "responseMapping": { - "apply": { - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"apply\\":true,\\"tool\\":\\"prisma\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\",\\"skipMigrations\\":true}'" - } - } + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" } } --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > greenfield > migrations-scaffold 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth-new 1`] = ` "exit: 0 --- stdout --- { - "phase": "migrations", - "status": "scaffolding", - "tool": "prisma", + "phase": "auth", + "status": "verified", "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "install_prisma", - "description": "Install Prisma as a dev dependency. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install -D prisma" - }, - { - "id": "init_prisma", - "description": "Initialize Prisma with PostgreSQL provider. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma init --datasource-provider postgresql" - }, - { - "id": "configure_env", - "description": "Ensure DATABASE_URL in .env points to your Neon database. The prisma init command may have created a placeholder — replace it with the real connection string if needed." - }, - { - "id": "create_schema", - "description": "Help the user define their initial schema in prisma/schema.prisma based on their application needs." - }, - { - "id": "run_migration", - "description": "Create and apply the initial migration. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate dev --name init" - } - ], - "onComplete": { - "type": "complete", - "message": "Prisma is set up with your Neon database. You can now define models in schema.prisma and run migrations with \`npx --no prisma migrate dev\`." - } + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" } } --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > greenfield > neon-auth-info 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth-verify 1`] = ` "exit: 0 --- stdout --- { - "phase": "neon_auth", - "status": "info", + "phase": "auth", + "status": "verified", "nextAction": { - "type": "ask_user", - "question": "Neon Auth provides drop-in user authentication that integrates with your Neon database. It handles user sign-up, sign-in, and session management. Would you like to set it up?", - "options": [ - { - "value": "yes", - "label": "Yes, set up Neon Auth" - }, - { - "value": "no", - "label": "No, skip for now" - } - ], - "context": "Full documentation: https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "responseMapping": { - "yes": { - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\",\\"setup\\":true}'" - }, - "no": { - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'" + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } +} +--- stderr --- + +--- subprocesses --- +" +`; + +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > mcp-install 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "tooling", + "status": "installing", + "nextAction": { + "type": "run_command", + "command": "npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor", + "description": "Installing Neon MCP server (global scope) for cursor.", + "timeout": 60000, + "onSuccess": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" + }, + "onFailure": { + "other": { + "type": "ask_user", + "question": "Failed to install the Neon MCP server automatically. Would you like to try again or configure it manually?", + "options": [ + { + "value": "retry", + "label": "Try again" + }, + { + "value": "manual", + "label": "I'll configure it manually" + } + ], + "responseMapping": { + "retry": { + "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" + }, + "manual": { + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } + } } } } @@ -3758,55 +2599,39 @@ exports[`neon init --agent: every step, against every project shape > greenfield --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > greenfield > neon-auth-setup 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > mcp-status 1`] = ` "exit: 0 --- stdout --- { - "phase": "neon_auth", - "status": "in_progress", + "phase": "tooling", + "status": "status", "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "steps": [ - { - "id": "provision", - "description": "Enable Neon Auth on the project using the Neon CLI. Run: \`CI= npx -y neon neon-auth enable --project-id --output json\`. You can check current status with: \`CI= npx -y neon neon-auth status --project-id --output json\`. Project ID: proj-snap-1." - }, - { - "id": "install_packages", - "description": "Install required packages per the skill reference. The exact packages depend on the framework (Next.js, React, etc.)." - }, - { - "id": "create_components", - "description": "Create auth components per the skill reference. Follow the exact patterns and imports specified." - }, + "type": "agent_check", + "checks": [ { - "id": "pull_env", - "description": "Run \`CI= npx -y neon env pull\` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.", - "command": "CI= npx -y neon env pull" + "id": "mcp_server", + "description": "Check if the Neon MCP server is already configured in your MCP server list", + "lookFor": [ + "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" + ] } ], - "onComplete": { + "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'", - "description": "Run this command to complete the setup. This is the final step — do not run any other neon init commands after this." + "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" } } } --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +" `; -exports[`neon init --agent: every step, against every project shape > greenfield > setup 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > setup 1`] = ` "exit: 0 --- stdout --- { @@ -3820,7 +2645,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -3852,23 +2677,6 @@ exports[`neon init --agent: every step, against every project shape > greenfield } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -3958,7 +2766,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -3968,7 +2776,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > setup-customize 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > setup-customize 1`] = ` "exit: 0 --- stdout --- { @@ -3992,8 +2800,8 @@ exports[`neon init --agent: every step, against every project shape > greenfield } ], "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"none\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"none\\\\\\"}\\"}'" + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- @@ -4007,7 +2815,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield /skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" `; -exports[`neon init --agent: every step, against every project shape > greenfield > setup-defaults 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > setup-defaults 1`] = ` "exit: 0 --- stdout --- { @@ -4031,8 +2839,8 @@ exports[`neon init --agent: every step, against every project shape > greenfield } ], "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"none\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"none\\\\\\"}\\"}'" + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- @@ -4046,7 +2854,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield /skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" `; -exports[`neon init --agent: every step, against every project shape > greenfield > skills-install 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > skills-install 1`] = ` "exit: 0 --- stdout --- { @@ -4085,7 +2893,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > skills-status 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > skills-status 1`] = ` "exit: 0 --- stdout --- { @@ -4115,7 +2923,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > skills-update 1`] = ` +exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > skills-update 1`] = ` "exit: 0 --- stdout --- { @@ -4154,55 +2962,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield " `; -exports[`neon init --agent: every step, against every project shape > greenfield > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": false - }, - "migrations": { - "tool": "none", - "hasMigrations": false - }, - "recommendations": [ - { - "priority": "high", - "message": "No DATABASE_URL found in .env", - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - }, - { - "priority": "medium", - "message": "none detected but no migrations found", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\"}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > auth 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > auth 1`] = ` "exit: 0 --- stdout --- { @@ -4219,7 +2979,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism " `; -exports[`neon init --agent: every step, against every project shape > next-prisma > auth-existing 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > auth-existing 1`] = ` "exit: 0 --- stdout --- { @@ -4236,7 +2996,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism " `; -exports[`neon init --agent: every step, against every project shape > next-prisma > auth-new 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > auth-new 1`] = ` "exit: 0 --- stdout --- { @@ -4253,7 +3013,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism " `; -exports[`neon init --agent: every step, against every project shape > next-prisma > auth-verify 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > auth-verify 1`] = ` "exit: 0 --- stdout --- { @@ -4270,257 +3030,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism " `; -exports[`neon init --agent: every step, against every project shape > next-prisma > db 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "ready", - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon orgs list --output json", - "description": "Listing your Neon organizations.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"orgs-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > db-error 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "error", - "error": "project create failed", - "nextAction": { - "type": "ask_user", - "question": "An error occurred during database setup: project create failed. Would you like to try again or skip this step?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, - { - "value": "skip", - "label": "Skip database setup" - } - ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > db-with-org 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "org_selected", - "org": { - "id": "org-snap-1" - }, - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon projects list --org-id org-snap-1 --output json", - "description": "Listing Neon projects.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgId\\":\\"org-snap-1\\",\\"projectsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"projects-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > db-with-project 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "project_ready", - "project": { - "id": "proj-snap-1" - }, - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "steps": [ - { - "id": "get_connection_string", - "description": "Get the database connection string", - "command": "CI= npx -y neon connection-string --project-id proj-snap-1" - }, - { - "id": "store_env", - "description": "Append DATABASE_URL= to .env. Create .env if it doesn't exist. Do NOT overwrite existing entries. Ensure .env is in .gitignore." - }, - { - "id": "detect_framework", - "description": "Examine the project to determine the framework (Next.js, Remix, Express, etc.) and ORM (Prisma, Drizzle, raw SQL) in use." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > finalize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "complete", - "nextAction": { - "type": "complete", - "message": "Neon setup is complete! Your database is connected and your agent has the Neon MCP server and skills available." - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > getting-started-stack 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "run_migrations", - "description": "Check if the prisma/migrations directory contains migration folders. If migrations exist, apply them with \`npx --no prisma migrate deploy\`. If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run \`npx --no prisma migrate dev --name init\` to create and apply the initial migration. If no models are defined, skip this step. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > mcp-install 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > mcp-install 1`] = ` "exit: 0 --- stdout --- { @@ -4567,7 +3077,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism " `; -exports[`neon init --agent: every step, against every project shape > next-prisma > mcp-status 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > mcp-status 1`] = ` "exit: 0 --- stdout --- { @@ -4596,312 +3106,52 @@ exports[`neon init --agent: every step, against every project shape > next-prism " `; -exports[`neon init --agent: every step, against every project shape > next-prisma > migrations 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > setup 1`] = ` "exit: 0 --- stdout --- { - "phase": "migrations", - "status": "detection_needed", + "phase": "setup", + "status": "pending", + "detectedIde": "cursor", + "installedEditors": , + "mcpConfigured": null, + "mcpScope": null, + "skillsInstalled": null, + "skillsScope": null, "nextAction": { "type": "agent_check", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { - "id": "existing_migrations", - "description": "Check if the project has existing database migrations", + "id": "neonctl", + "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", + "lookFor": [] + }, + { + "id": "mcp_server", + "description": "Check if the Neon MCP server is already configured in your MCP server list", "lookFor": [ - "prisma/migrations/ directory or schema.prisma file", - "drizzle/ directory or drizzle.config.ts", - "migrations/ or db/migrations/ directory", - "knex migration files (knexfile.js/ts)" + "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"tool\\":\\"\\",\\"migrationDir\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > migrations-apply 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "applying", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "ensure_env", - "description": "Verify DATABASE_URL is set in .env" }, { - "id": "apply", - "description": "Apply migrations to the Neon database. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" + "id": "agent_type", + "description": "Identify which coding agent is running this command", + "lookFor": [ + "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", + "Report your own agent identifier — this is used to configure the MCP server for the correct tool" + ] }, { - "id": "generate", - "description": "Generate the Prisma client.", - "command": "npx --no prisma generate" - } - ], - "onComplete": { - "type": "complete", - "message": "Database migrations applied successfully." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > migrations-prisma 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "found", - "detected": { - "tool": "prisma", - "migrationDir": null - }, - "nextAction": { - "type": "ask_user", - "question": "Found prisma migrations. Would you like to apply them to your Neon database?", - "options": [ - { - "value": "apply", - "label": "Yes, apply migrations" - }, - { - "value": "skip", - "label": "Skip for now" - } - ], - "responseMapping": { - "apply": { - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"apply\\":true,\\"tool\\":\\"prisma\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\",\\"skipMigrations\\":true}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > migrations-scaffold 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "scaffolding", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "install_prisma", - "description": "Install Prisma as a dev dependency. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install -D prisma" - }, - { - "id": "init_prisma", - "description": "Initialize Prisma with PostgreSQL provider. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma init --datasource-provider postgresql" - }, - { - "id": "configure_env", - "description": "Ensure DATABASE_URL in .env points to your Neon database. The prisma init command may have created a placeholder — replace it with the real connection string if needed." - }, - { - "id": "create_schema", - "description": "Help the user define their initial schema in prisma/schema.prisma based on their application needs." - }, - { - "id": "run_migration", - "description": "Create and apply the initial migration. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate dev --name init" - } - ], - "onComplete": { - "type": "complete", - "message": "Prisma is set up with your Neon database. You can now define models in schema.prisma and run migrations with \`npx --no prisma migrate dev\`." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > neon-auth-info 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "info", - "nextAction": { - "type": "ask_user", - "question": "Neon Auth provides drop-in user authentication that integrates with your Neon database. It handles user sign-up, sign-in, and session management. Would you like to set it up?", - "options": [ - { - "value": "yes", - "label": "Yes, set up Neon Auth" - }, - { - "value": "no", - "label": "No, skip for now" - } - ], - "context": "Full documentation: https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "responseMapping": { - "yes": { - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\",\\"setup\\":true}'" - }, - "no": { - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > neon-auth-setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "in_progress", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "steps": [ - { - "id": "provision", - "description": "Enable Neon Auth on the project using the Neon CLI. Run: \`CI= npx -y neon neon-auth enable --project-id --output json\`. You can check current status with: \`CI= npx -y neon neon-auth status --project-id --output json\`. Project ID: proj-snap-1." - }, - { - "id": "install_packages", - "description": "Install required packages per the skill reference. The exact packages depend on the framework (Next.js, React, etc.)." - }, - { - "id": "create_components", - "description": "Create auth components per the skill reference. Follow the exact patterns and imports specified." - }, - { - "id": "pull_env", - "description": "Run \`CI= npx -y neon env pull\` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.", - "command": "CI= npx -y neon env pull" - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'", - "description": "Run this command to complete the setup. This is the final step — do not run any other neon init commands after this." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] + "id": "extension_installed", + "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", + "lookFor": [ + "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", + "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" + ] } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -4991,7 +3241,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -5001,7 +3251,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism " `; -exports[`neon init --agent: every step, against every project shape > next-prisma > setup-customize 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > setup-customize 1`] = ` "exit: 0 --- stdout --- { @@ -5025,8 +3275,8 @@ exports[`neon init --agent: every step, against every project shape > next-prism } ], "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"next\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"none\\\\\\"}\\"}'" + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- @@ -5040,7 +3290,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism /skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" `; -exports[`neon init --agent: every step, against every project shape > next-prisma > setup-defaults 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > setup-defaults 1`] = ` "exit: 0 --- stdout --- { @@ -5064,8 +3314,8 @@ exports[`neon init --agent: every step, against every project shape > next-prism } ], "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"next\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"none\\\\\\"}\\"}'" + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." } } --- stderr --- @@ -5079,7 +3329,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism /skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" `; -exports[`neon init --agent: every step, against every project shape > next-prisma > skills-install 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > skills-install 1`] = ` "exit: 0 --- stdout --- { @@ -5118,7 +3368,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism " `; -exports[`neon init --agent: every step, against every project shape > next-prisma > skills-status 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > skills-status 1`] = ` "exit: 0 --- stdout --- { @@ -5148,7 +3398,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism " `; -exports[`neon init --agent: every step, against every project shape > next-prisma > skills-update 1`] = ` +exports[`neon init --agent: every step, against every project shape > node-app > skills-update 1`] = ` "exit: 0 --- stdout --- { @@ -5187,89 +3437,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism " `; -exports[`neon init --agent: every step, against every project shape > next-prisma > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": false - }, - "migrations": { - "tool": "prisma", - "hasMigrations": false - }, - "recommendations": [ - { - "priority": "high", - "message": "No DATABASE_URL found in .env", - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - }, - { - "priority": "medium", - "message": "prisma detected but no migrations found", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\"}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth-existing 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth-new 1`] = ` +exports[`neon init --agent: every step, against every project shape > other-database > auth 1`] = ` "exit: 0 --- stdout --- { @@ -5286,7 +3454,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism " `; -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth-verify 1`] = ` +exports[`neon init --agent: every step, against every project shape > other-database > auth-existing 1`] = ` "exit: 0 --- stdout --- { @@ -5294,5284 +3462,87 @@ exports[`neon init --agent: every step, against every project shape > next-prism "status": "verified", "nextAction": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > db 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "ready", - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon orgs list --output json", - "description": "Listing your Neon organizations.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"orgs-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > db-error 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "error", - "error": "project create failed", - "nextAction": { - "type": "ask_user", - "question": "An error occurred during database setup: project create failed. Would you like to try again or skip this step?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, - { - "value": "skip", - "label": "Skip database setup" - } - ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > db-with-org 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "org_selected", - "org": { - "id": "org-snap-1" - }, - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon projects list --org-id org-snap-1 --output json", - "description": "Listing Neon projects.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgId\\":\\"org-snap-1\\",\\"projectsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"projects-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > db-with-project 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "project_ready", - "project": { - "id": "proj-snap-1" - }, - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "steps": [ - { - "id": "get_connection_string", - "description": "Get the database connection string", - "command": "CI= npx -y neon connection-string --project-id proj-snap-1" - }, - { - "id": "store_env", - "description": "Append DATABASE_URL= to .env. Create .env if it doesn't exist. Do NOT overwrite existing entries. Ensure .env is in .gitignore." - }, - { - "id": "detect_framework", - "description": "Examine the project to determine the framework (Next.js, Remix, Express, etc.) and ORM (Prisma, Drizzle, raw SQL) in use." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > finalize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "complete", - "nextAction": { - "type": "complete", - "message": "Neon setup is complete! Your database is connected and your agent has the Neon MCP server and skills available." - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > getting-started-stack 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "run_migrations", - "description": "Check if the prisma/migrations directory contains migration folders. If migrations exist, apply them with \`npx --no prisma migrate deploy\`. If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run \`npx --no prisma migrate dev --name init\` to create and apply the initial migration. If no models are defined, skip this step. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > mcp-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing", - "nextAction": { - "type": "run_command", - "command": "npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor", - "description": "Installing Neon MCP server (global scope) for cursor.", - "timeout": 60000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "onFailure": { - "other": { - "type": "ask_user", - "question": "Failed to install the Neon MCP server automatically. Would you like to try again or configure it manually?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, - { - "value": "manual", - "label": "I'll configure it manually" - } - ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "manual": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > migrations 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "detection_needed", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "existing_migrations", - "description": "Check if the project has existing database migrations", - "lookFor": [ - "prisma/migrations/ directory or schema.prisma file", - "drizzle/ directory or drizzle.config.ts", - "migrations/ or db/migrations/ directory", - "knex migration files (knexfile.js/ts)" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"tool\\":\\"\\",\\"migrationDir\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > migrations-apply 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "applying", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "ensure_env", - "description": "Verify DATABASE_URL is set in .env" - }, - { - "id": "apply", - "description": "Apply migrations to the Neon database. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "generate", - "description": "Generate the Prisma client.", - "command": "npx --no prisma generate" - } - ], - "onComplete": { - "type": "complete", - "message": "Database migrations applied successfully." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > migrations-prisma 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "found", - "detected": { - "tool": "prisma", - "migrationDir": null - }, - "nextAction": { - "type": "ask_user", - "question": "Found prisma migrations. Would you like to apply them to your Neon database?", - "options": [ - { - "value": "apply", - "label": "Yes, apply migrations" - }, - { - "value": "skip", - "label": "Skip for now" - } - ], - "responseMapping": { - "apply": { - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"apply\\":true,\\"tool\\":\\"prisma\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\",\\"skipMigrations\\":true}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > migrations-scaffold 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "scaffolding", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "install_prisma", - "description": "Install Prisma as a dev dependency. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install -D prisma" - }, - { - "id": "init_prisma", - "description": "Initialize Prisma with PostgreSQL provider. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma init --datasource-provider postgresql" - }, - { - "id": "configure_env", - "description": "Ensure DATABASE_URL in .env points to your Neon database. The prisma init command may have created a placeholder — replace it with the real connection string if needed." - }, - { - "id": "create_schema", - "description": "Help the user define their initial schema in prisma/schema.prisma based on their application needs." - }, - { - "id": "run_migration", - "description": "Create and apply the initial migration. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate dev --name init" - } - ], - "onComplete": { - "type": "complete", - "message": "Prisma is set up with your Neon database. You can now define models in schema.prisma and run migrations with \`npx --no prisma migrate dev\`." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > neon-auth-info 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "info", - "nextAction": { - "type": "ask_user", - "question": "Neon Auth provides drop-in user authentication that integrates with your Neon database. It handles user sign-up, sign-in, and session management. Would you like to set it up?", - "options": [ - { - "value": "yes", - "label": "Yes, set up Neon Auth" - }, - { - "value": "no", - "label": "No, skip for now" - } - ], - "context": "Full documentation: https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "responseMapping": { - "yes": { - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\",\\"setup\\":true}'" - }, - "no": { - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > neon-auth-setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "in_progress", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "steps": [ - { - "id": "provision", - "description": "Enable Neon Auth on the project using the Neon CLI. Run: \`CI= npx -y neon neon-auth enable --project-id --output json\`. You can check current status with: \`CI= npx -y neon neon-auth status --project-id --output json\`. Project ID: proj-snap-1." - }, - { - "id": "install_packages", - "description": "Install required packages per the skill reference. The exact packages depend on the framework (Next.js, React, etc.)." - }, - { - "id": "create_components", - "description": "Create auth components per the skill reference. Follow the exact patterns and imports specified." - }, - { - "id": "pull_env", - "description": "Run \`CI= npx -y neon env pull\` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.", - "command": "CI= npx -y neon env pull" - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'", - "description": "Run this command to complete the setup. This is the final step — do not run any other neon init commands after this." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > setup-customize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"next\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"prisma/migrations\\\\\\"}\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -/neonctl --version -/npm view neonctl version -/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > setup-defaults 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"next\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"prisma\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"prisma/migrations\\\\\\"}\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -/neonctl --version -/npm view neonctl version -/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > skills-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > skills-update 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": false - }, - "migrations": { - "tool": "prisma", - "hasMigrations": true - }, - "recommendations": [ - { - "priority": "high", - "message": "No DATABASE_URL found in .env", - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > auth-existing 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > auth-new 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > auth-verify 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > db 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "ready", - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon orgs list --output json", - "description": "Listing your Neon organizations.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"orgs-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > db-error 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "error", - "error": "project create failed", - "nextAction": { - "type": "ask_user", - "question": "An error occurred during database setup: project create failed. Would you like to try again or skip this step?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, - { - "value": "skip", - "label": "Skip database setup" - } - ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > db-with-org 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "org_selected", - "org": { - "id": "org-snap-1" - }, - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon projects list --org-id org-snap-1 --output json", - "description": "Listing Neon projects.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgId\\":\\"org-snap-1\\",\\"projectsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"projects-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > db-with-project 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "project_ready", - "project": { - "id": "proj-snap-1" - }, - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "steps": [ - { - "id": "get_connection_string", - "description": "Get the database connection string", - "command": "CI= npx -y neon connection-string --project-id proj-snap-1" - }, - { - "id": "store_env", - "description": "Append DATABASE_URL= to .env. Create .env if it doesn't exist. Do NOT overwrite existing entries. Ensure .env is in .gitignore." - }, - { - "id": "detect_framework", - "description": "Examine the project to determine the framework (Next.js, Remix, Express, etc.) and ORM (Prisma, Drizzle, raw SQL) in use." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > finalize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "complete", - "nextAction": { - "type": "complete", - "message": "Neon setup is complete! Your database is connected and your agent has the Neon MCP server and skills available." - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > getting-started-stack 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "run_migrations", - "description": "Check if the prisma/migrations directory contains migration folders. If migrations exist, apply them with \`npx --no prisma migrate deploy\`. If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run \`npx --no prisma migrate dev --name init\` to create and apply the initial migration. If no models are defined, skip this step. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > mcp-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing", - "nextAction": { - "type": "run_command", - "command": "npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor", - "description": "Installing Neon MCP server (global scope) for cursor.", - "timeout": 60000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "onFailure": { - "other": { - "type": "ask_user", - "question": "Failed to install the Neon MCP server automatically. Would you like to try again or configure it manually?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, - { - "value": "manual", - "label": "I'll configure it manually" - } - ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "manual": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > migrations 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "detection_needed", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "existing_migrations", - "description": "Check if the project has existing database migrations", - "lookFor": [ - "prisma/migrations/ directory or schema.prisma file", - "drizzle/ directory or drizzle.config.ts", - "migrations/ or db/migrations/ directory", - "knex migration files (knexfile.js/ts)" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"tool\\":\\"\\",\\"migrationDir\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > migrations-apply 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "applying", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "ensure_env", - "description": "Verify DATABASE_URL is set in .env" - }, - { - "id": "apply", - "description": "Apply migrations to the Neon database. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "generate", - "description": "Generate the Prisma client.", - "command": "npx --no prisma generate" - } - ], - "onComplete": { - "type": "complete", - "message": "Database migrations applied successfully." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > migrations-prisma 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "found", - "detected": { - "tool": "prisma", - "migrationDir": null - }, - "nextAction": { - "type": "ask_user", - "question": "Found prisma migrations. Would you like to apply them to your Neon database?", - "options": [ - { - "value": "apply", - "label": "Yes, apply migrations" - }, - { - "value": "skip", - "label": "Skip for now" - } - ], - "responseMapping": { - "apply": { - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"apply\\":true,\\"tool\\":\\"prisma\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\",\\"skipMigrations\\":true}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > migrations-scaffold 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "scaffolding", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "install_prisma", - "description": "Install Prisma as a dev dependency. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install -D prisma" - }, - { - "id": "init_prisma", - "description": "Initialize Prisma with PostgreSQL provider. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma init --datasource-provider postgresql" - }, - { - "id": "configure_env", - "description": "Ensure DATABASE_URL in .env points to your Neon database. The prisma init command may have created a placeholder — replace it with the real connection string if needed." - }, - { - "id": "create_schema", - "description": "Help the user define their initial schema in prisma/schema.prisma based on their application needs." - }, - { - "id": "run_migration", - "description": "Create and apply the initial migration. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate dev --name init" - } - ], - "onComplete": { - "type": "complete", - "message": "Prisma is set up with your Neon database. You can now define models in schema.prisma and run migrations with \`npx --no prisma migrate dev\`." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > neon-auth-info 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "info", - "nextAction": { - "type": "ask_user", - "question": "Neon Auth provides drop-in user authentication that integrates with your Neon database. It handles user sign-up, sign-in, and session management. Would you like to set it up?", - "options": [ - { - "value": "yes", - "label": "Yes, set up Neon Auth" - }, - { - "value": "no", - "label": "No, skip for now" - } - ], - "context": "Full documentation: https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "responseMapping": { - "yes": { - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\",\\"setup\\":true}'" - }, - "no": { - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > neon-auth-setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "in_progress", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "steps": [ - { - "id": "provision", - "description": "Enable Neon Auth on the project using the Neon CLI. Run: \`CI= npx -y neon neon-auth enable --project-id --output json\`. You can check current status with: \`CI= npx -y neon neon-auth status --project-id --output json\`. Project ID: proj-snap-1." - }, - { - "id": "install_packages", - "description": "Install required packages per the skill reference. The exact packages depend on the framework (Next.js, React, etc.)." - }, - { - "id": "create_components", - "description": "Create auth components per the skill reference. Follow the exact patterns and imports specified." - }, - { - "id": "pull_env", - "description": "Run \`CI= npx -y neon env pull\` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.", - "command": "CI= npx -y neon env pull" - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'", - "description": "Run this command to complete the setup. This is the final step — do not run any other neon init commands after this." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > setup-customize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"hono\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"none\\\\\\"}\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -/neonctl --version -/npm view neonctl version -/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > setup-defaults 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"hono\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"none\\\\\\"}\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -/neonctl --version -/npm view neonctl version -/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > skills-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > skills-update 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": false - }, - "migrations": { - "tool": "none", - "hasMigrations": false - }, - "recommendations": [ - { - "priority": "high", - "message": "No DATABASE_URL found in .env", - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - }, - { - "priority": "medium", - "message": "none detected but no migrations found", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\"}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > auth-existing 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > auth-new 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > auth-verify 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > db 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "ready", - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon orgs list --output json", - "description": "Listing your Neon organizations.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"orgs-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > db-error 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "error", - "error": "project create failed", - "nextAction": { - "type": "ask_user", - "question": "An error occurred during database setup: project create failed. Would you like to try again or skip this step?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, - { - "value": "skip", - "label": "Skip database setup" - } - ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > db-with-org 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "org_selected", - "org": { - "id": "org-snap-1" - }, - "nextAction": { - "type": "run_command", - "command": "CI= npx -y neon projects list --org-id org-snap-1 --output json", - "description": "Listing Neon projects.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"orgId\\":\\"org-snap-1\\",\\"projectsResult\\":\\"\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"db\\",\\"error\\":\\"projects-list-failed\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > db-with-project 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "db", - "status": "project_ready", - "project": { - "id": "proj-snap-1" - }, - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "steps": [ - { - "id": "get_connection_string", - "description": "Get the database connection string", - "command": "CI= npx -y neon connection-string --project-id proj-snap-1" - }, - { - "id": "store_env", - "description": "Append DATABASE_URL= to .env. Create .env if it doesn't exist. Do NOT overwrite existing entries. Ensure .env is in .gitignore." - }, - { - "id": "detect_framework", - "description": "Examine the project to determine the framework (Next.js, Remix, Express, etc.) and ORM (Prisma, Drizzle, raw SQL) in use." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > finalize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "complete", - "nextAction": { - "type": "complete", - "message": "Neon setup is complete! Your database is connected and your agent has the Neon MCP server and skills available." - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > getting-started-stack 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "run_migrations", - "description": "Check if the prisma/migrations directory contains migration folders. If migrations exist, apply them with \`npx --no prisma migrate deploy\`. If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run \`npx --no prisma migrate dev --name init\` to create and apply the initial migration. If no models are defined, skip this step. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > mcp-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing", - "nextAction": { - "type": "run_command", - "command": "npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor", - "description": "Installing Neon MCP server (global scope) for cursor.", - "timeout": 60000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "onFailure": { - "other": { - "type": "ask_user", - "question": "Failed to install the Neon MCP server automatically. Would you like to try again or configure it manually?", - "options": [ - { - "value": "retry", - "label": "Try again" - }, - { - "value": "manual", - "label": "I'll configure it manually" - } - ], - "responseMapping": { - "retry": { - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "manual": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > migrations 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "detection_needed", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "existing_migrations", - "description": "Check if the project has existing database migrations", - "lookFor": [ - "prisma/migrations/ directory or schema.prisma file", - "drizzle/ directory or drizzle.config.ts", - "migrations/ or db/migrations/ directory", - "knex migration files (knexfile.js/ts)" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"tool\\":\\"\\",\\"migrationDir\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > migrations-apply 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "applying", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "ensure_env", - "description": "Verify DATABASE_URL is set in .env" - }, - { - "id": "apply", - "description": "Apply migrations to the Neon database. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "generate", - "description": "Generate the Prisma client.", - "command": "npx --no prisma generate" - } - ], - "onComplete": { - "type": "complete", - "message": "Database migrations applied successfully." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > migrations-prisma 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "found", - "detected": { - "tool": "prisma", - "migrationDir": null - }, - "nextAction": { - "type": "ask_user", - "question": "Found prisma migrations. Would you like to apply them to your Neon database?", - "options": [ - { - "value": "apply", - "label": "Yes, apply migrations" - }, - { - "value": "skip", - "label": "Skip for now" - } - ], - "responseMapping": { - "apply": { - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"apply\\":true,\\"tool\\":\\"prisma\\"}'" - }, - "skip": { - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\",\\"skipMigrations\\":true}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > migrations-scaffold 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "scaffolding", - "tool": "prisma", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "install_prisma", - "description": "Install Prisma as a dev dependency. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install -D prisma" - }, - { - "id": "init_prisma", - "description": "Initialize Prisma with PostgreSQL provider. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma init --datasource-provider postgresql" - }, - { - "id": "configure_env", - "description": "Ensure DATABASE_URL in .env points to your Neon database. The prisma init command may have created a placeholder — replace it with the real connection string if needed." - }, - { - "id": "create_schema", - "description": "Help the user define their initial schema in prisma/schema.prisma based on their application needs." - }, - { - "id": "run_migration", - "description": "Create and apply the initial migration. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate dev --name init" - } - ], - "onComplete": { - "type": "complete", - "message": "Prisma is set up with your Neon database. You can now define models in schema.prisma and run migrations with \`npx --no prisma migrate dev\`." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > neon-auth-info 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "info", - "nextAction": { - "type": "ask_user", - "question": "Neon Auth provides drop-in user authentication that integrates with your Neon database. It handles user sign-up, sign-in, and session management. Would you like to set it up?", - "options": [ - { - "value": "yes", - "label": "Yes, set up Neon Auth" - }, - { - "value": "no", - "label": "No, skip for now" - } - ], - "context": "Full documentation: https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "responseMapping": { - "yes": { - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\",\\"setup\\":true}'" - }, - "no": { - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > neon-auth-setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "neon_auth", - "status": "in_progress", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "steps": [ - { - "id": "provision", - "description": "Enable Neon Auth on the project using the Neon CLI. Run: \`CI= npx -y neon neon-auth enable --project-id --output json\`. You can check current status with: \`CI= npx -y neon neon-auth status --project-id --output json\`. Project ID: proj-snap-1." - }, - { - "id": "install_packages", - "description": "Install required packages per the skill reference. The exact packages depend on the framework (Next.js, React, etc.)." - }, - { - "id": "create_components", - "description": "Create auth components per the skill reference. Follow the exact patterns and imports specified." - }, - { - "id": "pull_env", - "description": "Run \`CI= npx -y neon env pull\` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.", - "command": "CI= npx -y neon env pull" - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"finalize\\",\\"agent\\":\\"cursor\\"}'", - "description": "Run this command to complete the setup. This is the final step — do not run any other neon init commands after this." - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > setup-customize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"express\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"none\\\\\\"}\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -/neonctl --version -/npm view neonctl version -/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > setup-defaults 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"getting-started\\",\\"data\\":\\"{\\\\\\"framework\\\\\\":\\\\\\"express\\\\\\",\\\\\\"orm\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationTool\\\\\\":\\\\\\"none\\\\\\",\\\\\\"migrationDir\\\\\\":\\\\\\"none\\\\\\"}\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -/neonctl --version -/npm view neonctl version -/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > skills-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > skills-update 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": true - }, - "migrations": { - "tool": "none", - "hasMigrations": false - }, - "recommendations": [ - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - }, - { - "priority": "medium", - "message": "none detected but no migrations found", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\"}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: nested and string-encoded --data payloads > JSON-encoded string under a data key 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "run_migrations", - "description": "Check if prisma/migrations contains migration folders. If migrations exist, apply them with \`npx --no prisma migrate deploy\`. If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run \`npx --no prisma migrate dev --name init\` to create and apply the initial migration. If no models are defined, skip this step. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: nested and string-encoded --data payloads > nested object under a data key 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "run_migrations", - "description": "Check if prisma/migrations contains migration folders. If migrations exist, apply them with \`npx --no prisma migrate deploy\`. If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run \`npx --no prisma migrate dev --name init\` to create and apply the initial migration. If no models are defined, skip this step. If this fails because the tool is not installed, install the project's dependencies — re-running the install step above if this payload has one — then retry this step. Do not add -y, and do not swap the runner for a fetching one (\`npx\` without \`--no\`, \`pnpm dlx\`, \`yarn dlx\`, \`bunx\`): those download an unpinned copy of the tool and run it against the user's database.", - "command": "npx --no prisma migrate deploy" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > connected — --preview 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > connected — --skip-migrations 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > connected — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > drizzle — --preview 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > drizzle — --skip-migrations 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > drizzle — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > fully-configured — --preview 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": true, - "mcpScope": "project", - "skillsInstalled": false, - "skillsScope": "project-partial", - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: configured (project). Agent skills: partially installed (project-partial) — missing skills will be auto-installed to the same scope. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > fully-configured — --skip-migrations 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "complete", - "nextAction": { - "type": "complete", - "message": "Neon setup is complete. Your database is configured and your agent has the Neon MCP server and skills available." - } -} ---- stderr --- - ---- subprocesses --- -/npx -y neonctl orgs list --output json" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > fully-configured — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "migrations", - "status": "detection_needed", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "existing_migrations", - "description": "Check if the project has existing database migrations", - "lookFor": [ - "prisma/migrations/ directory or schema.prisma file", - "drizzle/ directory or drizzle.config.ts", - "migrations/ or db/migrations/ directory", - "knex migration files (knexfile.js/ts)" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\",\\"tool\\":\\"\\",\\"migrationDir\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/npx -y neonctl orgs list --output json -/skills --version" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > greenfield — --preview 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "bootstrap_needed", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nNo application was detected in this directory. Ask the user if they'd like to scaffold a new project from a template (the \`template\` preference). Present ALL template options and the 'No thanks' option — do NOT auto-select even if there is only one template. If the user selects a template, the scaffolded template includes agent skills so skills installation will be skipped. If the user chooses 'none', continue with the remaining setup preferences normally. Then perform the agent checks and present the remaining preferences ONE AT A TIME.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "template", - "question": "No application was detected in this directory. Would you like to scaffold a new project from a template?", - "phase": "before_checks", - "options": [ - { - "value": "hono", - "label": "REST API (Hono, Drizzle) — A Hono REST API on Neon Functions." - }, - { - "value": "realtime-chat", - "label": "Realtime chat — A Next.js chat app with Neon Auth." - }, - { - "value": "none", - "label": "No thanks — continue without scaffolding" - } - ], - "default": "none" - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global — skills included in template)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > greenfield — --skip-migrations 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > greenfield — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > next-prisma — --preview 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > next-prisma — --skip-migrations 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > next-prisma — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } +} +--- stderr --- + +--- subprocesses --- +" +`; + +exports[`neon init --agent: every step, against every project shape > other-database > auth-new 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "auth", + "status": "verified", + "nextAction": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } +} +--- stderr --- + +--- subprocesses --- +" +`; + +exports[`neon init --agent: every step, against every project shape > other-database > auth-verify 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "auth", + "status": "verified", + "nextAction": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } +} +--- stderr --- + +--- subprocesses --- +" +`; + +exports[`neon init --agent: every step, against every project shape > other-database > mcp-install 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "tooling", + "status": "installing", + "nextAction": { + "type": "run_command", + "command": "npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor", + "description": "Installing Neon MCP server (global scope) for cursor.", + "timeout": 60000, + "onSuccess": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" + }, + "onFailure": { + "other": { + "type": "ask_user", + "question": "Failed to install the Neon MCP server automatically. Would you like to try again or configure it manually?", "options": [ { - "value": "global", - "label": "Global (available in all projects)" + "value": "retry", + "label": "Try again" }, { - "value": "project", - "label": "Project-level (scoped to this project only)" + "value": "manual", + "label": "I'll configure it manually" } ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" + "responseMapping": { + "retry": { + "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" }, - { - "value": "false", - "label": "No" + "manual": { + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" + } } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -10581,159 +3552,26 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma " `; -exports[`neon init --agent: the orchestrator picks the next phase > next-prisma-migrated — --preview 1`] = ` +exports[`neon init --agent: every step, against every project shape > other-database > mcp-status 1`] = ` "exit: 0 --- stdout --- { - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, + "phase": "tooling", + "status": "status", "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, { "id": "mcp_server", "description": "Check if the Neon MCP server is already configured in your MCP server list", "lookFor": [ "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" } ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" } } } @@ -10743,7 +3581,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma- " `; -exports[`neon init --agent: the orchestrator picks the next phase > next-prisma-migrated — --skip-migrations 1`] = ` +exports[`neon init --agent: every step, against every project shape > other-database > setup 1`] = ` "exit: 0 --- stdout --- { @@ -10751,13 +3589,13 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma- "status": "pending", "detectedIde": "cursor", "installedEditors": , - "mcpConfigured": false, + "mcpConfigured": null, "mcpScope": null, - "skillsInstalled": false, + "skillsInstalled": null, "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -10782,30 +3620,13 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma- { "id": "extension_installed", "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, + "lookFor": [ + "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", + "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" + ] + } + ], + "userPreferences": [ { "id": "mode", "question": "Use default settings or customize?", @@ -10871,31 +3692,277 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma- "group": "customize" }, { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" + "id": "installExtension", + "question": "Install the Neon editor extension for local database browsing?", + "phase": "after_checks", + "options": [ + { + "value": "true", + "label": "Yes" + }, + { + "value": "false", + "label": "No" + } + ], + "default": "true", + "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", + "condition": { + "preferenceId": "mode", + "equals": "customize" + }, + "group": "customize" + } + ], + "reportBack": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + } + } +} +--- stderr --- + +--- subprocesses --- +" +`; + +exports[`neon init --agent: every step, against every project shape > other-database > setup-customize 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "setup", + "status": "installed", + "results": [ + { + "id": "neonctl", + "description": "Neon CLI is up to date (v2.45.0)", + "status": "success" + }, + { + "id": "install_mcp", + "description": "Installed Neon MCP server (global scope)", + "status": "success" + }, + { + "id": "install_skills", + "description": "Neon agent skills installed", + "status": "success" + } + ], + "nextAction": { + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." + } +} +--- stderr --- + +--- subprocesses --- +/neonctl --version +/npm view neonctl version +/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor +/skills --version +/skills add neondatabase/agent-skills --skill neon --agent cursor -y +/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +`; + +exports[`neon init --agent: every step, against every project shape > other-database > setup-defaults 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "setup", + "status": "installed", + "results": [ + { + "id": "neonctl", + "description": "Neon CLI is up to date (v2.45.0)", + "status": "success" + }, + { + "id": "install_mcp", + "description": "Installed Neon MCP server (global scope)", + "status": "success" + }, + { + "id": "install_skills", + "description": "Neon agent skills installed", + "status": "success" + } + ], + "nextAction": { + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." + } +} +--- stderr --- + +--- subprocesses --- +/neonctl --version +/npm view neonctl version +/npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor +/skills --version +/skills add neondatabase/agent-skills --skill neon --agent cursor -y +/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" +`; + +exports[`neon init --agent: every step, against every project shape > other-database > skills-install 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "tooling", + "status": "installing_skills", + "nextAction": { + "type": "run_command", + "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", + "description": "Installing Neon agent skills for cursor.", + "timeout": 30000, + "onSuccess": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + }, + "onFailure": { + "other": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } + } + }, + "skillReferences": { + "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", + "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", + "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", + "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", + "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", + "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", + "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", + "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" + } +} +--- stderr --- + +--- subprocesses --- +" +`; + +exports[`neon init --agent: every step, against every project shape > other-database > skills-status 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "tooling", + "status": "skills_check", + "nextAction": { + "type": "agent_check", + "checks": [ + { + "id": "skills", + "description": "Check if Neon agent skills are installed in this project", + "lookFor": [ + "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", + "A .skills/ directory with neon-related content" + ] + } + ], + "reportBack": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" + } + } +} +--- stderr --- + +--- subprocesses --- +" +`; + +exports[`neon init --agent: every step, against every project shape > other-database > skills-update 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "tooling", + "status": "installing_skills", + "nextAction": { + "type": "run_command", + "command": "npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", + "description": "Installing Neon agent skills for cursor.", + "timeout": 30000, + "onSuccess": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + }, + "onFailure": { + "other": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" + } + } + }, + "skillReferences": { + "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", + "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", + "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", + "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", + "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", + "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", + "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", + "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" + } +} +--- stderr --- + +--- subprocesses --- +" +`; + +exports[`neon init --agent: nested and string-encoded --data payloads > JSON-encoded string under a data key 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "tooling", + "status": "skills_check", + "nextAction": { + "type": "agent_check", + "checks": [ + { + "id": "skills", + "description": "Check if Neon agent skills are installed in this project", + "lookFor": [ + "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", + "A .skills/ directory with neon-related content" + ] + } + ], + "reportBack": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" + } + } +} +--- stderr --- + +--- subprocesses --- +" +`; + +exports[`neon init --agent: nested and string-encoded --data payloads > nested object under a data key 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "tooling", + "status": "skills_check", + "nextAction": { + "type": "agent_check", + "checks": [ + { + "id": "skills", + "description": "Check if Neon agent skills are installed in this project", + "lookFor": [ + "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", + "A .skills/ directory with neon-related content" + ] } ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" } } } @@ -10905,7 +3972,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma- " `; -exports[`neon init --agent: the orchestrator picks the next phase > next-prisma-migrated — no --data 1`] = ` +exports[`neon init --agent: the orchestrator picks the next phase > connected — no --data 1`] = ` "exit: 0 --- stdout --- { @@ -10919,7 +3986,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma- "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -10951,23 +4018,6 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma- } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -11057,7 +4107,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma- ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -11067,7 +4117,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma- " `; -exports[`neon init --agent: the orchestrator picks the next phase > node-app — --preview 1`] = ` +exports[`neon init --agent: the orchestrator picks the next phase > drizzle — no --data 1`] = ` "exit: 0 --- stdout --- { @@ -11081,7 +4131,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -11113,23 +4163,6 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -11219,7 +4252,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -11229,7 +4262,24 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — " `; -exports[`neon init --agent: the orchestrator picks the next phase > node-app — --skip-migrations 1`] = ` +exports[`neon init --agent: the orchestrator picks the next phase > fully-configured — no --data 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "setup", + "status": "complete", + "nextAction": { + "type": "complete", + "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." + } +} +--- stderr --- + +--- subprocesses --- +" +`; + +exports[`neon init --agent: the orchestrator picks the next phase > greenfield — no --data 1`] = ` "exit: 0 --- stdout --- { @@ -11243,7 +4293,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -11275,23 +4325,6 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -11381,7 +4414,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -11391,7 +4424,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — " `; -exports[`neon init --agent: the orchestrator picks the next phase > node-app — no --data 1`] = ` +exports[`neon init --agent: the orchestrator picks the next phase > next-prisma — no --data 1`] = ` "exit: 0 --- stdout --- { @@ -11405,7 +4438,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -11437,23 +4470,6 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -11543,7 +4559,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -11553,7 +4569,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — " `; -exports[`neon init --agent: the orchestrator picks the next phase > other-database — --preview 1`] = ` +exports[`neon init --agent: the orchestrator picks the next phase > next-prisma-migrated — no --data 1`] = ` "exit: 0 --- stdout --- { @@ -11567,7 +4583,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > other-databa "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -11599,23 +4615,6 @@ exports[`neon init --agent: the orchestrator picks the next phase > other-databa } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -11705,7 +4704,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > other-databa ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -11715,7 +4714,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > other-databa " `; -exports[`neon init --agent: the orchestrator picks the next phase > other-database — --skip-migrations 1`] = ` +exports[`neon init --agent: the orchestrator picks the next phase > node-app — no --data 1`] = ` "exit: 0 --- stdout --- { @@ -11729,7 +4728,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > other-databa "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -11761,23 +4760,6 @@ exports[`neon init --agent: the orchestrator picks the next phase > other-databa } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -11867,7 +4849,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > other-databa ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -11891,7 +4873,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > other-databa "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -11923,23 +4905,6 @@ exports[`neon init --agent: the orchestrator picks the next phase > other-databa } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -12029,7 +4994,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > other-databa ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -12056,69 +5021,6 @@ exports[`neon init --agent: the response depends on which agent is driving > cla " `; -exports[`neon init --agent: the response depends on which agent is driving > claude-code > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"claude-code\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent claude-code -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent claude-code -y" -`; - exports[`neon init --agent: the response depends on which agent is driving > claude-code > mcp-status 1`] = ` "exit: 0 --- stdout --- @@ -12162,7 +5064,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cla "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -12186,23 +5088,6 @@ exports[`neon init --agent: the response depends on which agent is driving > cla } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -12292,7 +5177,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cla ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -12316,7 +5201,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cla "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -12340,23 +5225,6 @@ exports[`neon init --agent: the response depends on which agent is driving > cla } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -12446,7 +5314,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cla ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -12467,151 +5335,40 @@ exports[`neon init --agent: the response depends on which agent is driving > cla "checks": [ { "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"claude-code\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > claude-code > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": false - }, - "migrations": { - "tool": "prisma", - "hasMigrations": false - }, - "recommendations": [ - { - "priority": "high", - "message": "No DATABASE_URL found in .env", - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - }, - { - "priority": "medium", - "message": "prisma detected but no migrations found", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\"}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > codex > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"codex\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > codex > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." + "description": "Check if Neon agent skills are installed in this project", + "lookFor": [ + "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", + "A .skills/ directory with neon-related content" + ] } ], - "onComplete": { + "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"codex\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"claude-code\\",\\"install\\":true}'" } } } --- stderr --- --- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent codex -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent codex -y" +" +`; + +exports[`neon init --agent: the response depends on which agent is driving > codex > auth 1`] = ` +"exit: 0 +--- stdout --- +{ + "phase": "auth", + "status": "verified", + "nextAction": { + "type": "run_shell_command", + "command": "neon init --agent --data '{\\"agent\\":\\"codex\\"}'" + } +} +--- stderr --- + +--- subprocesses --- +" `; exports[`neon init --agent: the response depends on which agent is driving > codex > mcp-status 1`] = ` @@ -12657,7 +5414,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cod "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -12681,23 +5438,6 @@ exports[`neon init --agent: the response depends on which agent is driving > cod } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -12787,7 +5527,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cod ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -12811,7 +5551,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cod "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -12835,23 +5575,6 @@ exports[`neon init --agent: the response depends on which agent is driving > cod } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -12941,7 +5664,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cod ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -12981,54 +5704,6 @@ exports[`neon init --agent: the response depends on which agent is driving > cod " `; -exports[`neon init --agent: the response depends on which agent is driving > codex > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": false - }, - "migrations": { - "tool": "prisma", - "hasMigrations": false - }, - "recommendations": [ - { - "priority": "high", - "message": "No DATABASE_URL found in .env", - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - }, - { - "priority": "medium", - "message": "prisma detected but no migrations found", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\"}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - exports[`neon init --agent: the response depends on which agent is driving > cursor > auth 1`] = ` "exit: 0 --- stdout --- @@ -13046,69 +5721,6 @@ exports[`neon init --agent: the response depends on which agent is driving > cur " `; -exports[`neon init --agent: the response depends on which agent is driving > cursor > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\",\\"agent\\":\\"cursor\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - exports[`neon init --agent: the response depends on which agent is driving > cursor > mcp-status 1`] = ` "exit: 0 --- stdout --- @@ -13152,7 +5764,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cur "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -13184,23 +5796,6 @@ exports[`neon init --agent: the response depends on which agent is driving > cur } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -13290,7 +5885,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cur ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -13314,7 +5909,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cur "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -13346,23 +5941,6 @@ exports[`neon init --agent: the response depends on which agent is driving > cur } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -13452,7 +6030,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cur ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -13492,54 +6070,6 @@ exports[`neon init --agent: the response depends on which agent is driving > cur " `; -exports[`neon init --agent: the response depends on which agent is driving > cursor > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": false - }, - "migrations": { - "tool": "prisma", - "hasMigrations": false - }, - "recommendations": [ - { - "priority": "high", - "message": "No DATABASE_URL found in .env", - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - }, - { - "priority": "medium", - "message": "prisma detected but no migrations found", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\"}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - exports[`neon init --agent: the response depends on which agent is driving > none > auth 1`] = ` "exit: 0 --- stdout --- @@ -13557,67 +6087,6 @@ exports[`neon init --agent: the response depends on which agent is driving > non " `; -exports[`neon init --agent: the response depends on which agent is driving > none > getting-started 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "getting_started", - "nextAction": { - "type": "agent_action", - "prerequisite": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "steps": [ - { - "id": "select_org", - "description": "List the user's Neon organizations using the CLI command below. If only one org exists, use it automatically. If multiple orgs exist, ask the user which one to use. Remember the selected org ID for the next steps.", - "command": "CI= npx -y neon orgs list --output json" - }, - { - "id": "select_or_create_project", - "description": "List existing Neon projects in the selected organization using the CLI command below (replace with the selected org ID). Ask the user whether they want to use an existing project or create a new one. If creating new, ask the user for a project name (suggest the current directory name). IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - "command": "CI= npx -y neon projects list --org-id --output json" - }, - { - "id": "create_project_if_needed", - "description": "If the user chose to create a new project, create it using the CLI command below (replace and ). If the user chose an existing project, skip this step.", - "command": "CI= npx -y neon projects create --name --org-id --output json" - }, - { - "id": "create_neon_context", - "description": "Update the .neon context file in the project root with the selected org and project IDs. IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved. If no .neon file exists, create one. The file is JSON. Add/update only the orgId and projectId fields: {\\"orgId\\": \\"\\", \\"projectId\\": \\"\\", ...existing fields}. This file is safe to commit — it contains no secrets." - }, - { - "id": "install_dependencies", - "description": "Check if node_modules exists in the project root. If not, install the project's dependencies. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager. This must be done before \`neon env pull\` because the project's Neon config file may import packages that need to be installed first.", - "command": "npm install" - }, - { - "id": "pull_env", - "description": "Now that the .neon context file is in place and dependencies are installed, run \`neon env pull\` to populate the project's environment variables. This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file. It reads the .neon context file to determine the project, and writes to the appropriate env file for the project. Ensure the target env file is listed in .gitignore.", - "command": "CI= npx -y neon env pull" - }, - { - "id": "install_driver", - "description": "Install the Neon serverless driver for direct database access. Run this step's \`command\` exactly as written — it already uses this project's package manager. Do not rewrite it to npm or any other manager.", - "command": "npm install @neondatabase/serverless" - }, - { - "id": "verify_connection", - "description": "Verify the database connection works by running a SQL query against the Neon database. Write and run a short script that connects using DATABASE_URL from the project's env file and executes \`SELECT 1\` (or queries a table from the migration if migrations were run). Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity." - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"neon-auth\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - exports[`neon init --agent: the response depends on which agent is driving > none > mcp-status 1`] = ` "exit: 0 --- stdout --- @@ -13661,7 +6130,7 @@ exports[`neon init --agent: the response depends on which agent is driving > non "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -13685,23 +6154,6 @@ exports[`neon init --agent: the response depends on which agent is driving > non } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -13791,7 +6243,7 @@ exports[`neon init --agent: the response depends on which agent is driving > non ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -13815,7 +6267,7 @@ exports[`neon init --agent: the response depends on which agent is driving > non "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -13839,23 +6291,6 @@ exports[`neon init --agent: the response depends on which agent is driving > non } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -13945,7 +6380,7 @@ exports[`neon init --agent: the response depends on which agent is driving > non ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -13985,54 +6420,6 @@ exports[`neon init --agent: the response depends on which agent is driving > non " `; -exports[`neon init --agent: the response depends on which agent is driving > none > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": true - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": false - }, - "migrations": { - "tool": "prisma", - "hasMigrations": false - }, - "recommendations": [ - { - "priority": "high", - "message": "No DATABASE_URL found in .env", - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - }, - { - "priority": "medium", - "message": "prisma detected but no migrations found", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\"}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - exports[`neon init --agent: the signed-out auth branches > a damaged credentials file is an error, not a fresh machine 1`] = ` "exit: 1 --- stdout --- @@ -14221,59 +6608,6 @@ exports[`neon init --agent: the signed-out auth branches > auth-verify 1`] = ` " `; -exports[`neon init --agent: the signed-out auth branches > status 1`] = ` -"exit: 0 ---- stdout --- -{ - "auth": { - "authenticated": false - }, - "tooling": { - "mcpServer": { - "configured": false, - "scope": null - }, - "skills": { - "installed": false, - "scope": null - } - }, - "project": { - "databaseUrl": false - }, - "migrations": { - "tool": "prisma", - "hasMigrations": false - }, - "recommendations": [ - { - "priority": "high", - "message": "Not authenticated with Neon", - "command": "neon init --agent --data '{\\"step\\":\\"auth\\"}'" - }, - { - "priority": "high", - "message": "No DATABASE_URL found in .env", - "command": "neon init --agent --data '{\\"step\\":\\"db\\"}'" - }, - { - "priority": "medium", - "message": "Neon agent skills not detected in this project", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - }, - { - "priority": "medium", - "message": "prisma detected but no migrations found", - "command": "neon init --agent --data '{\\"step\\":\\"migrations\\"}'" - } - ] -} ---- stderr --- - ---- subprocesses --- -" -`; - exports[`neon init --agent: the signed-out auth branches > the orchestrator sends a signed-out caller to the auth phase 1`] = ` "exit: 0 --- stdout --- @@ -14373,7 +6707,7 @@ exports[`neon init: failure output > --data without a step falls through to the "skillsScope": null, "nextAction": { "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", + "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", "checks": [ { "id": "neonctl", @@ -14405,23 +6739,6 @@ exports[`neon init: failure output > --data without a step falls through to the } ], "userPreferences": [ - { - "id": "features", - "question": "Which Neon features would you like to enable for this project?", - "phase": "after_checks", - "options": [ - { - "value": "database", - "label": "Database (always included)" - }, - { - "value": "database,auth", - "label": "Database + Neon Auth (adds authentication via Neon)" - } - ], - "default": "database", - "context": "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon." - }, { "id": "mode", "question": "Use default settings or customize?", @@ -14511,7 +6828,7 @@ exports[`neon init: failure output > --data without a step falls through to the ], "reportBack": { "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" + "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" } } } @@ -14559,20 +6876,6 @@ Options: --data └────────────────> JSON object with a "step" field to route to a specific phase and phase-specific options. [string] ---skip-migrations -└────────────────> Skip the migrations phase. [boolean] [default: false] ---preview -└────────────────> Enable preview features (e.g. project bootstrapping from tem - plates). [boolean] [default: false] ---project-id -└────────────────> Use an existing Neon project by ID. Skips organization and p - roject selection. [string] ---org-id -└────────────────> Scope setup to an existing organization by ID. Skips organiz - ation selection. [string] ---branch-id -└────────────────> Target a specific branch by ID when pulling environment vari - ables. [string] --- subprocesses --- " `; @@ -14641,7 +6944,7 @@ exports[`neon init: failure output > unknown step 1`] = ` --- stdout --- { "success": false, - "error": "Unknown step: \\"not-a-step\\". Valid steps: auth, db, setup, getting-started, mcp, skills, migrations, neon-auth, status, finalize" + "error": "Unknown step: \\"not-a-step\\". Valid steps: auth, setup, mcp, skills" } --- stderr --- diff --git a/packages/cli/src/init/agent_snapshot.test.ts b/packages/cli/src/init/agent_snapshot.test.ts index 963a1d87..8d6b0380 100644 --- a/packages/cli/src/init/agent_snapshot.test.ts +++ b/packages/cli/src/init/agent_snapshot.test.ts @@ -477,23 +477,10 @@ const FIXTURE_NAMES = Object.keys(FIXTURES) as (keyof typeof FIXTURES)[]; * names; this table has to stay in step with it. */ const STEPS: { name: string; data: Record }[] = [ - { name: "status", data: { step: "status" } }, { name: "auth", data: { step: "auth" } }, { name: "auth-verify", data: { step: "auth", verify: true } }, { name: "auth-existing", data: { step: "auth", method: "existing" } }, { name: "auth-new", data: { step: "auth", method: "new" } }, - { name: "db", data: { step: "db" } }, - { name: "db-with-org", data: { step: "db", orgId: "org-snap-1" } }, - { - name: "db-with-project", - data: { - step: "db", - projectId: "proj-snap-1", - framework: "next", - orm: "prisma", - }, - }, - { name: "db-error", data: { step: "db", error: "project create failed" } }, { name: "setup", data: { step: "setup" } }, { name: "setup-defaults", @@ -501,7 +488,6 @@ const STEPS: { name: string; data: Record }[] = [ step: "setup", mode: "defaults", mcpConfigured: false, - connectionString: false, isVscodeIde: true, installExtension: false, }, @@ -516,18 +502,6 @@ const STEPS: { name: string; data: Record }[] = [ installExtension: false, }, }, - { name: "getting-started", data: { step: "getting-started" } }, - { - name: "getting-started-stack", - data: { - step: "getting-started", - hasConnectionString: true, - framework: "next", - orm: "prisma", - migrationTool: "prisma", - migrationDir: "prisma/migrations", - }, - }, { name: "mcp-status", data: { step: "mcp", status: true } }, { name: "mcp-install", @@ -536,22 +510,6 @@ const STEPS: { name: string; data: Record }[] = [ { name: "skills-status", data: { step: "skills", status: true } }, { name: "skills-install", data: { step: "skills", install: true } }, { name: "skills-update", data: { step: "skills", update: true } }, - { name: "migrations", data: { step: "migrations" } }, - { name: "migrations-prisma", data: { step: "migrations", tool: "prisma" } }, - { - name: "migrations-scaffold", - data: { step: "migrations", scaffold: "prisma" }, - }, - { - name: "migrations-apply", - data: { step: "migrations", apply: true, tool: "prisma" }, - }, - { name: "neon-auth-info", data: { step: "neon-auth", info: true } }, - { - name: "neon-auth-setup", - data: { step: "neon-auth", setup: true, projectId: "proj-snap-1" }, - }, - { name: "finalize", data: { step: "finalize" } }, ]; describe("neon init --agent: every step, against every project shape", () => { @@ -578,20 +536,6 @@ describe("neon init --agent: the orchestrator picks the next phase", () => { snapshotOf(await runInit(fixture, ["--agent"])), ).toMatchSnapshot(); }); - - test(`${fixture} — --skip-migrations`, async () => { - expect( - snapshotOf( - await runInit(fixture, ["--agent", "--skip-migrations"]), - ), - ).toMatchSnapshot(); - }); - - test(`${fixture} — --preview`, async () => { - expect( - snapshotOf(await runInit(fixture, ["--agent", "--preview"])), - ).toMatchSnapshot(); - }); } }); @@ -599,12 +543,10 @@ describe("neon init --agent: the response depends on which agent is driving", () for (const agent of Object.keys(AGENTS) as (keyof typeof AGENTS)[]) { describe(agent, () => { for (const step of [ - "status", "auth", "setup", "mcp-status", "skills-status", - "getting-started", ]) { const found = STEPS.find((s) => s.name === step); if (!found) throw new Error(`no step named ${step}`); @@ -643,7 +585,6 @@ describe("neon init --agent: the signed-out auth branches", () => { data: { step: "auth", method: "existing" }, }, { name: "auth-new-account", data: { step: "auth", method: "new" } }, - { name: "status", data: { step: "status" } }, ]) { test(step.name, async () => { const result = await runInit( @@ -672,7 +613,7 @@ describe("neon init --agent: the signed-out auth branches", () => { snapshotOf( await runInit( "next-prisma", - ["--agent", "--data", '{"step":"status"}'], + ["--agent", "--data", '{"step":"auth","verify":true}'], { credentials: "{ not json" }, ), ), @@ -703,8 +644,8 @@ describe("neon init --agent: nested and string-encoded --data payloads", () => { "--agent", "--data", JSON.stringify({ - step: "getting-started", - data: { framework: "next", migrationTool: "prisma" }, + step: "skills", + data: { status: true }, }), ]); expect(snapshotOf(result)).toMatchSnapshot(); @@ -715,11 +656,8 @@ describe("neon init --agent: nested and string-encoded --data payloads", () => { "--agent", "--data", JSON.stringify({ - step: "getting-started", - data: JSON.stringify({ - framework: "next", - migrationTool: "prisma", - }), + step: "skills", + data: JSON.stringify({ status: true }), }), ]); expect(snapshotOf(result)).toMatchSnapshot(); diff --git a/packages/cli/src/init/enrich_output.ts b/packages/cli/src/init/enrich_output.ts index 21c3945c..bacc5c0a 100644 --- a/packages/cli/src/init/enrich_output.ts +++ b/packages/cli/src/init/enrich_output.ts @@ -64,14 +64,6 @@ export function enrichResponse(obj: unknown): unknown { // Rename run_neon_init → run_shell_command so agents don't infer subcommand patterns if (result.type === "run_neon_init") { result.type = "run_shell_command"; - // Help agents understand finalize is the terminal step - if ( - typeof result.command === "string" && - result.command.includes('"step":"finalize"') - ) { - result.description = - "Run this command to complete the setup. This is the final step — do not run any other neon init commands after this."; - } } return result; diff --git a/packages/cli/src/init/handoff.ts b/packages/cli/src/init/handoff.ts new file mode 100644 index 00000000..d203fb65 --- /dev/null +++ b/packages/cli/src/init/handoff.ts @@ -0,0 +1,25 @@ +import type { CompleteAction, PhaseResponse } from "./types.js"; + +/** + * `neon init` stops once the tooling is in place. Connecting a database and + * configuring features (Auth, Object Storage, Functions, AI Gateway) is the + * agent's job from here, driven by the installed Neon skill — the CLI does not + * link projects, pull env, or provision anything. + */ +export const HANDOFF_MESSAGE = + "Neon tooling is installed — the MCP server and agent skills are available. " + + "Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), " + + "then follow the Neon skill to connect the database and configure the selected features."; + +export function handoffAction(): CompleteAction { + return { type: "complete", message: HANDOFF_MESSAGE }; +} + +/** The terminal `neon init --agent` response once tooling is installed. */ +export function handoffResponse(): PhaseResponse { + return { + phase: "setup", + status: "complete", + nextAction: handoffAction(), + }; +} diff --git a/packages/cli/src/init/ids.ts b/packages/cli/src/init/ids.ts deleted file mode 100644 index 326d3f90..00000000 --- a/packages/cli/src/init/ids.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Validates that an ID contains only safe characters for shell interpolation. - * Neon org/project/branch IDs are UUIDs or slug-like strings. - */ -export function assertSafeId(value: string, label: string): void { - if (!/^[\w.:-]+$/.test(value)) { - throw new Error( - `Invalid ${label}: "${value}". Expected alphanumeric, hyphens, underscores, dots, or colons.`, - ); - } -} diff --git a/packages/cli/src/init/interactive.ts b/packages/cli/src/init/interactive.ts index e4393366..5af378ef 100644 --- a/packages/cli/src/init/interactive.ts +++ b/packages/cli/src/init/interactive.ts @@ -1,11 +1,11 @@ /** * Interactive v2 CLI — purpose-built guided flow for humans. - * Uses the same underlying install functions but with a clean clack-based UX. + * + * Installs the Neon tooling (MCP server, agent skills, IDE extension) and stops + * there. Connecting a database and configuring features is left to the user's + * agent — which now has the Neon skill — or to a manual `neon link`. */ -import { existsSync, readFileSync, writeFileSync } from "node:fs"; -import { resolve } from "node:path"; -import { SelectPrompt } from "@clack/core"; import { confirm, isCancel, @@ -17,16 +17,9 @@ import { } from "@clack/prompts"; import { execa } from "execa"; import which from "which"; -import { bold, dim, gray, italic } from "yoctocolors"; +import { bold, dim } from "yoctocolors"; import { ALL_CONFIGURABLE_AGENTS, getAddMcpAgentId } from "./agents.js"; import { ensureNeonctlAuth, isAuthenticated } from "./auth.js"; -import { - type BootstrapTemplate, - FALLBACK_TEMPLATES, - fetchTemplates, - type NeonFeature, - scaffoldTemplate, -} from "./bootstrap.js"; import { detectAgent, detectIde } from "./detect_agent.js"; import { detectAvailableEditors } from "./editors.js"; import { @@ -82,131 +75,16 @@ function patchClackColors(): () => void { }; } -// clack box-drawing glyphs, mirrored from @clack/prompts so our custom -// template picker lines up with every other step in the flow. -const S_BAR = "│"; -const S_BAR_END = "└"; -const S_STEP_ACTIVE = "◆"; -const S_STEP_SUBMIT = "◇"; -const S_STEP_CANCEL = "■"; -const S_RADIO_ACTIVE = "●"; -const S_RADIO_INACTIVE = "○"; - -type TemplateOption = { - value: string; - label: string; - tools?: string[]; - description?: string; -}; - -const SENTINEL_NONE = "none"; - -/** - * Bespoke template picker built on @clack/core's `SelectPrompt`. - * - * The stock `@clack/prompts` `select` can only render an option on a single - * line (title + a parenthesized hint on the focused row), which can't express - * what we want here: a clean title-only list, and a focused row that expands to - * `Title (tools)` with the description on its own italic line beneath. Driving - * the core prompt directly lets us own the frame, so we emit a second - * gutter-aligned line for the active option only. - */ -async function selectTemplate( - templates: BootstrapTemplate[], - message: string, -): Promise { - const options: TemplateOption[] = [ - ...templates.map((t) => ({ - value: t.id, - label: t.title, - tools: t.tools, - description: t.description, - })), - { - value: SENTINEL_NONE, - label: "No thanks — continue without scaffolding", - }, - ]; - - const green = neonGreenFn; - // Title sits at column 6 ("│ ● "), so wrapped description lines indent four - // spaces past the gutter to line up under it. - const descIndent = " "; - const descWidth = Math.max(24, (process.stdout.columns || 80) - 8); - - const renderActive = (option: TemplateOption): string => { - const tools = - option.tools && option.tools.length > 0 - ? ` ${dim(`(${option.tools.join(", ")})`)}` - : ""; - const head = `${green(S_RADIO_ACTIVE)} ${option.label}${tools}`; - if (!option.description) return head; - const body = wordWrap(option.description, descWidth) - .split("\n") - .map((line) => `${green(S_BAR)}${descIndent}${italic(dim(line))}`) - .join("\n"); - return `${head}\n${body}`; - }; - - const prompt = new SelectPrompt({ - options, - initialValue: options[0]?.value, - render() { - const active = this.options[this.cursor]; - const heading = (symbol: string) => - `${gray(S_BAR)}\n${symbol} ${message}\n`; - - if (this.state === "submit") { - return `${heading(green(S_STEP_SUBMIT))}${gray(S_BAR)} ${dim( - active?.label ?? "", - )}`; - } - if (this.state === "cancel") { - return `${heading(green(S_STEP_CANCEL))}${gray(S_BAR)} ${dim( - active?.label ?? "", - )}\n${gray(S_BAR)}`; - } - - const body = this.options - .map((option) => - option === active - ? renderActive(option) - : `${dim(S_RADIO_INACTIVE)} ${dim(option.label)}`, - ) - .join(`\n${green(S_BAR)} `); - return `${heading(green(S_STEP_ACTIVE))}${green(S_BAR)} ${body}\n${green( - S_BAR_END, - )}\n`; - }, - }); - - return prompt.prompt(); -} - -export type InteractiveInitOptions = { - preview?: boolean; - /** Existing project to use — carried into the agent hand-off command. */ - projectId?: string; - /** Existing org to scope to — carried into the agent hand-off command. */ - orgId?: string; - /** Branch to target — carried into the agent hand-off command. */ - branchId?: string; -}; - -export async function interactiveInit( - options: InteractiveInitOptions = {}, -): Promise { +export async function interactiveInit(): Promise { const restoreColors = patchClackColors(); try { - await interactiveInitInner(options); + await interactiveInitInner(); } finally { restoreColors(); } } -async function interactiveInitInner( - options: InteractiveInitOptions, -): Promise { +async function interactiveInitInner(): Promise { // Written straight to stdout rather than through `log`, which prefixes // every line with a level and goes to stderr — this is the banner of an // interactive session, not a diagnostic. @@ -223,7 +101,7 @@ async function interactiveInitInner( process.stdout.write( `${dim( wordWrap( - "\nLet's get your project set up with Neon. We'll install the MCP server, agent skills, and IDE extension, then connect your app to a database.\n", + "\nLet's set up Neon for your AI coding assistant. We'll install the MCP server, agent skills, and IDE extension so your agent can take it from here.\n", process.stdout.columns || 80, ), )}\n`, @@ -240,148 +118,26 @@ async function interactiveInitInner( const inspectSpinner = spinner(); inspectSpinner.start("Checking existing configuration..."); const inspection = await inspectProject([ - { id: "has_app", description: "", lookFor: [] }, { id: "mcp_server", description: "", lookFor: [] }, { id: "skills", description: "", lookFor: [] }, - { id: "connection_string", description: "", lookFor: [] }, - { id: "project_stack", description: "", lookFor: [] }, - { id: "migrations", description: "", lookFor: [] }, { id: "ide_type", description: "", lookFor: [] }, ]); inspectSpinner.stop(dim("Configuration checked ✓")); - const hasApp = inspection.hasApp === true; - let selectedFeatures: NeonFeature[] = []; - let selectedTemplate: BootstrapTemplate | null = null; - - // Preview mode: bootstrap from template if no app detected - if (options.preview && !hasApp) { - let templates = FALLBACK_TEMPLATES; - try { - const fetched = await fetchTemplates(); - if (fetched && fetched.length > 0) templates = fetched; - } catch {} - - const templateResult = await selectTemplate( - templates, - "No application detected. Would you like to scaffold a new project from a template?", - ); - if (isCancel(templateResult)) { - outro("Setup cancelled."); - return; - } - if (templateResult !== "none") { - selectedTemplate = - templates.find((t) => t.id === templateResult) ?? null; - if (selectedTemplate) { - selectedFeatures = selectedTemplate.requires; - const bootstrapS = spinner(); - bootstrapS.start( - `Scaffolding project from template "${selectedTemplate.title}"...`, - ); - try { - await scaffoldTemplate(selectedTemplate, ".", { - onWarn: (message) => log.warn(message), - }); - bootstrapS.stop( - dim( - `Scaffolded project from "${selectedTemplate.title}" ✓`, - ), - ); - } catch (err) { - const msg = - err instanceof Error ? err.message : "Unknown error"; - bootstrapS.stop("Failed to scaffold project"); - log.error(msg); - outro("Setup failed."); - return; - } - } - } - } - - // For brownfield flows (existing app), ask which features to enable - if (!selectedTemplate && hasApp) { - const featuresResult = await select({ - message: - "Which Neon features would you like to enable for this project?", - options: [ - { value: "database", label: "Database" }, - { - value: "database,auth", - label: "Database + Neon Auth (adds authentication via Neon)", - }, - ], - initialValue: "database", - }); - if (isCancel(featuresResult)) { - outro("Setup cancelled."); - return; - } - selectedFeatures = (featuresResult as string).split( - ",", - ) as NeonFeature[]; - } - - // Write _init metadata to .neon - if (selectedFeatures.length > 0) { - const neonPath = resolve(process.cwd(), ".neon"); - let existing: Record = {}; - if (existsSync(neonPath)) { - try { - existing = JSON.parse(readFileSync(neonPath, "utf-8")); - } catch {} - } - existing._init = { features: selectedFeatures }; - writeFileSync(neonPath, `${JSON.stringify(existing, null, 2)}\n`); - } - const mcpAlready = inspection.mcpConfigured === true; - // If we bootstrapped, skills come from the template - const skillsAlready = - inspection.skillsInstalled === true || selectedTemplate !== null; - const hasNeonConnection = inspection.connectionString === true; + const skillsAlready = inspection.skillsInstalled === true; let needsMcp = !mcpAlready; const needsSkills = !skillsAlready; const needsInstall = needsMcp || needsSkills; - // Check if .neon context file exists - const neonContextPath = resolve(process.cwd(), ".neon"); - const hasNeonContext = - existsSync(neonContextPath) && - (() => { - try { - const content = JSON.parse( - readFileSync(neonContextPath, "utf-8"), - ); - return !!content.projectId; - } catch { - return false; - } - })(); - - // Check if Neon Auth is configured - const hasNeonAuth = (() => { - for (const envFile of [".env", ".env.local"]) { - const envPath = resolve(process.cwd(), envFile); - if (existsSync(envPath)) { - try { - const content = readFileSync(envPath, "utf-8"); - if (/^NEON_AUTH_/m.test(content)) return true; - } catch {} - } - } - return false; - })(); - // Check if extension is installed for the detected editor let extensionAlready = false; if (detectedEditor && usesExtension(detectedEditor)) { extensionAlready = await isExtensionInstalled(detectedEditor); } - // If tooling + database are configured, check if there's anything left to do - if (mcpAlready && skillsAlready && hasNeonConnection && hasNeonContext) { + // Everything already in place — nothing to install. + if (!needsInstall) { log.step( dim( `Neon MCP server already configured (${inspection.mcpScope || "detected"}) ✓`, @@ -394,53 +150,7 @@ async function interactiveInitInner( ); if (extensionAlready) log.step(dim("Neon editor extension installed ✓")); - log.step(dim("Neon database connected ✓")); - - if (hasNeonAuth) { - log.step(dim("Neon Auth configured ✓")); - outro( - dim( - "Your project is fully configured with Neon. Nothing to do.", - ), - ); - return; - } - - // Neon Auth not configured — ask if they want it - const authResult = await select({ - message: - "Would you like to set up Neon Auth for user authentication?", - options: [ - { value: "yes", label: "Yes, set up Neon Auth" }, - { value: "no", label: "No, skip for now" }, - ], - initialValue: "no", - }); - - if (isCancel(authResult) || authResult === "no") { - outro( - dim( - `Your project is configured with Neon. You can set up Neon Auth later by having your agent run: neon init --agent --data '{"step":"neon-auth"}'`, - ), - ); - return; - } - - // Read .neon for project context - let projectId: string | null = null; - try { - const neonCtx = JSON.parse(readFileSync(neonContextPath, "utf-8")); - projectId = neonCtx.projectId ?? null; - } catch {} - - log.step("Next steps"); - const promptLines = ["Set up Neon Auth for this project."]; - if (projectId) promptLines.push(`Project ID: ${projectId}.`); - log.message(dim("Copy the following into your agent chat:")); - log.message( - promptLines.map((line) => bold(neonGreenFn(line))).join("\n"), - ); - outro(dim("Have feedback? Email us at feedback@neon.tech")); + printNextSteps(); return; } @@ -459,20 +169,99 @@ async function interactiveInitInner( ); // ----------------------------------------------------------------------- - // Step 3–5: Install what's missing (skip entirely if everything is configured) + // Step 2: Install what's missing // ----------------------------------------------------------------------- - if (needsInstall) { - const homeDir = process.env.HOME || process.env.USERPROFILE; - if (!homeDir) { - log.error("Could not determine home directory."); - outro("Setup failed."); + const homeDir = process.env.HOME || process.env.USERPROFILE; + if (!homeDir) { + log.error("Could not determine home directory."); + outro("Setup failed."); + return; + } + + let selectedEditors: Editor[]; + if (detectedEditor) { + selectedEditors = [detectedEditor]; + } else { + const availableEditors = await detectAvailableEditors(homeDir); + const editorResponse = await multiselect({ + message: "Which editor(s) would you like to configure?", + options: ALL_CONFIGURABLE_AGENTS.map((agent) => ({ + value: agent.editor, + label: agent.editor, + hint: agent.hint, + })), + initialValues: availableEditors, + required: true, + }); + if (isCancel(editorResponse)) { + outro("Setup cancelled."); + return; + } + selectedEditors = editorResponse as Editor[]; + if (selectedEditors.length === 0) { + log.warn("No editors selected."); + outro("Setup cancelled."); return; } + } - let selectedEditors: Editor[]; - if (detectedEditor) { - selectedEditors = [detectedEditor]; - } else { + // Check extension status + const vscodeEditors = selectedEditors.filter(usesExtension); + let extensionAlreadyInstalled = false; + if (vscodeEditors.length > 0) { + const checks = await Promise.all( + vscodeEditors.map((e) => isExtensionInstalled(e)), + ); + extensionAlreadyInstalled = checks.every(Boolean); + if (extensionAlreadyInstalled) { + log.step(dim("Neon editor extension already installed ✓")); + } + } + const canInstallExtension = + vscodeEditors.length > 0 && !extensionAlreadyInstalled; + let doInstallExtension = false; + + // Build hint showing only what needs installing + const hintParts: string[] = []; + if (needsMcp) hintParts.push("MCP server (global)"); + if (needsSkills) hintParts.push("agent skills (project)"); + + // Installation preferences + let mcpScope: "global" | "project" | "none" = "global"; + let skillsScope: "global" | "project" = "project"; + + let modeResult: string; + while (true) { + const editorName = selectedEditors.join(", "); + const result = await select({ + message: `Configure ${editorName} for Neon:`, + options: [ + { + value: "defaults", + label: "Install with defaults", + hint: hintParts.join(", "), + }, + { + value: "customize", + label: "Customize installation", + hint: canInstallExtension + ? "choose scopes and optional editor extension" + : "choose scopes and options", + }, + { + value: "change_editor", + label: "Configure a different editor", + }, + ], + initialValue: "defaults", + }); + + if (isCancel(result)) { + outro("Setup cancelled."); + return; + } + + if (result === "change_editor") { const availableEditors = await detectAvailableEditors(homeDir); const editorResponse = await multiselect({ message: "Which editor(s) would you like to configure?", @@ -490,260 +279,174 @@ async function interactiveInitInner( } selectedEditors = editorResponse as Editor[]; if (selectedEditors.length === 0) { - log.warn("No editors selected."); outro("Setup cancelled."); return; } + continue; } - // Check extension status - const vscodeEditors = selectedEditors.filter(usesExtension); - let extensionAlreadyInstalled = false; - if (vscodeEditors.length > 0) { - const checks = await Promise.all( - vscodeEditors.map((e) => isExtensionInstalled(e)), - ); - extensionAlreadyInstalled = checks.every(Boolean); - if (extensionAlreadyInstalled) { - log.step(dim("Neon editor extension already installed ✓")); - } - } - const canInstallExtension = - vscodeEditors.length > 0 && !extensionAlreadyInstalled; - let doInstallExtension = false; - - // Build hint showing only what needs installing - const hintParts: string[] = []; - if (needsMcp) hintParts.push("MCP server (global)"); - if (needsSkills) hintParts.push("agent skills (project)"); - - // Installation preferences - let mcpScope: "global" | "project" | "none" = "global"; - let skillsScope: "global" | "project" = "project"; - - let modeResult: string; - while (true) { - const editorName = selectedEditors.join(", "); - const result = await select({ - message: `Configure ${editorName} for Neon:`, + modeResult = result as string; + break; + } + + if (modeResult === "customize") { + if (needsMcp) { + const scopeResult = await select({ + message: "Where should the Neon MCP server be configured?", options: [ { - value: "defaults", - label: "Install with defaults", - hint: hintParts.join(", "), + value: "global", + label: "Global (available in all projects)", }, { - value: "customize", - label: "Customize installation", - hint: canInstallExtension - ? "choose scopes and optional editor extension" - : "choose scopes and options", + value: "project", + label: "Project-level (this project only)", }, { - value: "change_editor", - label: "Configure a different editor", + value: "none", + label: "Skip — do not install the MCP server", }, ], - initialValue: "defaults", }); - - if (isCancel(result)) { + if (isCancel(scopeResult)) { outro("Setup cancelled."); return; } - - if (result === "change_editor") { - const availableEditors = await detectAvailableEditors(homeDir); - const editorResponse = await multiselect({ - message: "Which editor(s) would you like to configure?", - options: ALL_CONFIGURABLE_AGENTS.map((agent) => ({ - value: agent.editor, - label: agent.editor, - hint: agent.hint, - })), - initialValues: availableEditors, - required: true, - }); - if (isCancel(editorResponse)) { - outro("Setup cancelled."); - return; - } - selectedEditors = editorResponse as Editor[]; - if (selectedEditors.length === 0) { - outro("Setup cancelled."); - return; - } - continue; - } - - modeResult = result as string; - break; + mcpScope = scopeResult as "global" | "project" | "none"; + if (mcpScope === "none") needsMcp = false; } - if (modeResult === "customize") { - if (needsMcp) { - const scopeResult = await select({ - message: "Where should the Neon MCP server be configured?", - options: [ - { - value: "global", - label: "Global (available in all projects)", - }, - { - value: "project", - label: "Project-level (this project only)", - }, - { - value: "none", - label: "Skip — do not install the MCP server", - }, - ], - }); - if (isCancel(scopeResult)) { - outro("Setup cancelled."); - return; - } - mcpScope = scopeResult as "global" | "project" | "none"; - if (mcpScope === "none") needsMcp = false; - } - - if (needsSkills) { - const skillsScopeResult = await select({ - message: "Where should Neon agent skills be installed?", - options: [ - { - value: "global", - label: "Global (available in all projects)", - }, - { - value: "project", - label: "Project-level (this project only)", - }, - ], - initialValue: "project", - }); - if (isCancel(skillsScopeResult)) { - outro("Setup cancelled."); - return; - } - skillsScope = skillsScopeResult as "global" | "project"; - } - - if (canInstallExtension) { - const extResult = await confirm({ - message: `Install the Neon extension for ${vscodeEditors.join(", ")}?`, - }); - if (isCancel(extResult)) { - outro("Setup cancelled."); - return; - } - doInstallExtension = extResult; + if (needsSkills) { + const skillsScopeResult = await select({ + message: "Where should Neon agent skills be installed?", + options: [ + { + value: "global", + label: "Global (available in all projects)", + }, + { + value: "project", + label: "Project-level (this project only)", + }, + ], + initialValue: "project", + }); + if (isCancel(skillsScopeResult)) { + outro("Setup cancelled."); + return; } + skillsScope = skillsScopeResult as "global" | "project"; } - // Auth check before install - const installAuthed = await isAuthenticated(); - if (!installAuthed) { - const authS = spinner(); - authS.start("Authenticating with Neon..."); - const authSuccess = await ensureNeonctlAuth(); - if (!authSuccess) { - authS.stop("Authentication failed."); - outro("Run `neon init` again after signing in."); + if (canInstallExtension) { + const extResult = await confirm({ + message: `Install the Neon extension for ${vscodeEditors.join(", ")}?`, + }); + if (isCancel(extResult)) { + outro("Setup cancelled."); return; } - authS.stop("Authenticated."); + doInstallExtension = extResult; } + } - // Ensure the Neon CLI is installed and up to date - const nctlS = spinner(); - nctlS.start("Checking Neon CLI..."); - const nctlResult = await ensureNeonctl(); - switch (nctlResult.status) { - case "already_current": - nctlS.stop( - dim(`Neon CLI is up to date (v${nctlResult.version}) ✓`), - ); - break; - case "installed": - nctlS.stop( - dim(`Installed Neon CLI (v${nctlResult.version}) ✓`), - ); - break; - case "updated": - nctlS.stop(dim(`Updated Neon CLI to v${nctlResult.version} ✓`)); - break; - case "failed": - nctlS.stop("Failed to install Neon CLI"); - log.warn( - nctlResult.error ?? - "The Neon CLI could not be installed automatically.", - ); - // Only promise npx when it exists. It ships with npm, so it is - // gone in exactly the failure where nothing could install - // globally — but in the ordinary failure (EACCES, registry down) - // it is there, and saying so is what tells the user setup is not - // broken. - if (which.sync("npx", { nothrow: true })) { - log.warn("Setup will continue using npx."); - } - break; + // Auth check before install + const installAuthed = await isAuthenticated(); + if (!installAuthed) { + const authS = spinner(); + authS.start("Authenticating with Neon..."); + const authSuccess = await ensureNeonctlAuth(); + if (!authSuccess) { + authS.stop("Authentication failed."); + outro("Run `neon init` again after signing in."); + return; } + authS.stop("Authenticated."); + } - // Install only what's missing - for (const editor of selectedEditors) { - if (needsMcp) { - const mcpAgentId = getAddMcpAgentId(editor); - const mcpArgs = [ - "-y", - "add-mcp", - "https://mcp.neon.tech/mcp", - "-n", - "Neon", - "-y", - "-a", - mcpAgentId, - ]; - if (mcpScope === "global") mcpArgs.splice(5, 0, "-g"); - - const mcpS = spinner(); - mcpS.start(`Installing Neon MCP server for ${editor}...`); - try { - await execa("npx", mcpArgs, { - stdio: "pipe", - timeout: 60000, - }); - mcpS.stop( - dim( - `Neon MCP server configured for ${editor} (${mcpScope}) ✓`, - ), - ); - } catch (err) { - const msg = - err instanceof Error ? err.message : "Unknown error"; - mcpS.stop(`Failed to configure MCP server for ${editor}`); - log.error(msg); - } + // Ensure the Neon CLI is installed and up to date + const nctlS = spinner(); + nctlS.start("Checking Neon CLI..."); + const nctlResult = await ensureNeonctl(); + switch (nctlResult.status) { + case "already_current": + nctlS.stop( + dim(`Neon CLI is up to date (v${nctlResult.version}) ✓`), + ); + break; + case "installed": + nctlS.stop(dim(`Installed Neon CLI (v${nctlResult.version}) ✓`)); + break; + case "updated": + nctlS.stop(dim(`Updated Neon CLI to v${nctlResult.version} ✓`)); + break; + case "failed": + nctlS.stop("Failed to install Neon CLI"); + log.warn( + nctlResult.error ?? + "The Neon CLI could not be installed automatically.", + ); + // Only promise npx when it exists. It ships with npm, so it is + // gone in exactly the failure where nothing could install + // globally — but in the ordinary failure (EACCES, registry down) + // it is there, and saying so is what tells the user setup is not + // broken. + if (which.sync("npx", { nothrow: true })) { + log.warn("Setup will continue using npx."); } + break; + } - if (needsSkills) { - await installAgentSkills([editor], { - scope: skillsScope, - preview: options.preview, + // Install only what's missing + for (const editor of selectedEditors) { + if (needsMcp) { + const mcpAgentId = getAddMcpAgentId(editor); + const mcpArgs = [ + "-y", + "add-mcp", + "https://mcp.neon.tech/mcp", + "-n", + "Neon", + "-y", + "-a", + mcpAgentId, + ]; + if (mcpScope === "global") mcpArgs.splice(5, 0, "-g"); + + const mcpS = spinner(); + mcpS.start(`Installing Neon MCP server for ${editor}...`); + try { + await execa("npx", mcpArgs, { + stdio: "pipe", + timeout: 60000, }); + mcpS.stop( + dim( + `Neon MCP server configured for ${editor} (${mcpScope}) ✓`, + ), + ); + } catch (err) { + const msg = + err instanceof Error ? err.message : "Unknown error"; + mcpS.stop(`Failed to configure MCP server for ${editor}`); + log.error(msg); } + } - if (doInstallExtension && usesExtension(editor)) { - const extS = spinner(); - extS.start(`Installing Neon extension for ${editor}...`); - const extOk = await installExtension(editor); - if (extOk) { - extS.stop(dim(`Neon extension installed for ${editor} ✓`)); - } else { - extS.stop( - `Extension install failed — install manually from the extensions panel.`, - ); - } + if (needsSkills) { + await installAgentSkills([editor], { scope: skillsScope }); + } + + if (doInstallExtension && usesExtension(editor)) { + const extS = spinner(); + extS.start(`Installing Neon extension for ${editor}...`); + const extOk = await installExtension(editor); + if (extOk) { + extS.stop(dim(`Neon extension installed for ${editor} ✓`)); + } else { + extS.stop( + `Extension install failed — install manually from the extensions panel.`, + ); } } } @@ -764,47 +467,25 @@ async function interactiveInitInner( if (agentForSkills) { const detectedSkillsScope = inspection.skillsScope === "global" ? "global" : undefined; - await ensureSkillsUpToDate( - agentForSkills, - detectedSkillsScope, - options.preview, - ); + await ensureSkillsUpToDate(agentForSkills, detectedSkillsScope); } - // ----------------------------------------------------------------------- - // Step 6: Done — build prompt for the agent to continue - // ----------------------------------------------------------------------- + printNextSteps(); +} - // Build the getting-started data payload (same as agent mode) - const gettingStartedData: Record = {}; - if (hasNeonConnection) gettingStartedData.hasConnectionString = true; - if (inspection.framework && inspection.framework !== "none") - gettingStartedData.framework = inspection.framework; - if (inspection.orm && inspection.orm !== "none") - gettingStartedData.orm = inspection.orm; - if (inspection.migrationTool && inspection.migrationTool !== "none") - gettingStartedData.migrationTool = inspection.migrationTool; - if (inspection.migrationDir && inspection.migrationDir !== "none") - gettingStartedData.migrationDir = inspection.migrationDir; - if (selectedFeatures.length > 0) - gettingStartedData.features = selectedFeatures; - if (options.preview) gettingStartedData.preview = true; - // Carry any explicitly provided IDs into the hand-off so the agent runs the - // verified fast path (single `neon link`) instead of the selection flow. - if (options.projectId) gettingStartedData.projectId = options.projectId; - if (options.orgId) gettingStartedData.orgId = options.orgId; - if (options.branchId) gettingStartedData.branchId = options.branchId; - - // Build a prompt for the user to paste into their agent chat - const cmd = `neon init --agent --data '${JSON.stringify({ step: "getting-started", ...gettingStartedData })}'`; - // Account for clack's "│ " prefix (3 chars) when wrapping +/** + * `neon init` installs tooling and stops. Point the user at the two ways to go + * further: let their agent (now equipped with the Neon skill) set things up, or + * link a project themselves. + */ +function printNextSteps(): void { const cols = (process.stdout.columns || 80) - 3; - const promptText = `To finish setting up Neon using Neon's agent-guided onboarding experience, have your agent run this shell command: ${cmd}`; - log.step("Next steps"); - log.message(dim("Copy the following into your agent chat:")); log.message( - wordWrap(promptText, cols) + wordWrap( + "Neon tooling is installed. To connect a database and configure features (Auth, Object Storage, Functions, AI Gateway), ask your AI agent to set up Neon — it now has the Neon skill and MCP server. Or link a project yourself with `neon link`.", + cols, + ) .split("\n") .map((line) => bold(neonGreenFn(line))) .join("\n"), diff --git a/packages/cli/src/init/orchestrate.test.ts b/packages/cli/src/init/orchestrate.test.ts index 92265e4e..9dd190d7 100644 --- a/packages/cli/src/init/orchestrate.test.ts +++ b/packages/cli/src/init/orchestrate.test.ts @@ -23,39 +23,6 @@ vi.mock("node:path", async (importOriginal) => { return { ...actual }; }); -vi.mock("./skills.js", async (importOriginal) => { - const actual = (await importOriginal()) as Record; - return { ...actual, ensureSkillsUpToDate: vi.fn().mockResolvedValue(true) }; -}); - -vi.mock("./resolve_context.js", () => ({ - resolveNeonContext: vi.fn().mockResolvedValue(null), -})); - -vi.mock("./bootstrap.js", () => { - const templates = [ - { - id: "hono", - title: "Hono API", - description: "A Hono template.", - requires: ["database"], - source: { - owner: "neondatabase", - repo: "examples", - ref: "main", - subdir: "with-hono", - }, - }, - ]; - return { - fetchTemplates: vi.fn().mockResolvedValue(templates), - FALLBACK_TEMPLATES: templates, - findTemplate: (ts: typeof templates, id: string) => - ts.find((t) => t.id === id), - scaffoldTemplate: vi.fn().mockResolvedValue(1), - }; -}); - import { isAuthenticated } from "./auth.js"; import { orchestrate } from "./orchestrate.js"; @@ -95,12 +62,13 @@ function mockToolingInstalled(extraFiles: Record = {}) { '{"mcpServers":{"Neon":{"url":"https://mcp.neon.tech/mcp"}}}', // skills directory exists and contains neon-postgres skill with SKILL.md ".cursor/skills/neon-postgres/SKILL.md": "", + ".cursor/skills/neon/SKILL.md": "", ...extraFiles, }; mockFs(files); } -describe("v2 orchestrator", () => { +describe("v2 orchestrator (minimal: install tooling, then hand off)", () => { beforeEach(() => { vi.clearAllMocks(); mockExistsSync.mockReturnValue(false); @@ -124,43 +92,20 @@ describe("v2 orchestrator", () => { } }); - test("enters setup with bootstrap when no app is detected and --preview", async () => { - mockIsAuthenticated.mockResolvedValue(true); + test("auth phase existing_account maps to inline run_command", async () => { + mockIsAuthenticated.mockResolvedValue(false); - const result = await orchestrate({ agent: "claude", preview: true }); + const result = await orchestrate({ agent: "cursor" }); - expect(result.phase).toBe("setup"); - expect(result.status).toBe("bootstrap_needed"); - expect(result.nextAction.type).toBe("agent_check"); - if (result.nextAction.type === "agent_check") { - // Should include template preference before mode - const prefs = result.nextAction.userPreferences ?? []; - expect(prefs[0]?.id).toBe("template"); - // skillsScope is present but conditional on customize mode - // (if user picks a template, skills are bundled; if not, they need scope choice) - const skillsPref = prefs.find((p) => p.id === "skillsScope"); - if (skillsPref) { - expect(skillsPref.condition).toEqual({ - preferenceId: "mode", - equals: "customize", - }); + if (result.nextAction.type === "ask_user") { + const existing = result.nextAction.responseMapping.existing_account; + expect("action" in existing).toBe(true); + if ("action" in existing) { + expect(existing.action.type).toBe("run_command"); } } }); - test("skips bootstrap without --preview even when no app detected", async () => { - mockIsAuthenticated.mockResolvedValue(true); - - const result = await orchestrate({ agent: "claude" }); - - expect(result.phase).toBe("setup"); - expect(result.status).toBe("pending"); - if (result.nextAction.type === "agent_check") { - const prefs = result.nextAction.userPreferences ?? []; - expect(prefs.find((p) => p.id === "template")).toBeUndefined(); - } - }); - test("enters setup phase when no tooling is installed", async () => { mockIsAuthenticated.mockResolvedValue(true); mockAppExists(); @@ -172,141 +117,47 @@ describe("v2 orchestrator", () => { expect(result.nextAction.type).toBe("agent_check"); if (result.nextAction.type === "agent_check") { const prefs = result.nextAction.userPreferences ?? []; - // Brownfield: features question first, then mode - expect(prefs[0]?.id).toBe("features"); - expect(prefs[1]?.id).toBe("mode"); + // No feature question anymore — install preferences only, starting with mode. + expect(prefs.find((p) => p.id === "features")).toBeUndefined(); + expect(prefs.find((p) => p.id === "template")).toBeUndefined(); + expect(prefs[0]?.id).toBe("mode"); } }); - test("skips setup, goes to getting-started when MCP + skills installed but no connection string", async () => { + test("enters setup when MCP configured but skills missing", async () => { mockIsAuthenticated.mockResolvedValue(true); - mockToolingInstalled(); + mockAppExists({ + ".cursor/mcp.json": + '{"mcpServers":{"Neon":{"url":"https://mcp.neon.tech/mcp"}}}', + }); const result = await orchestrate({ agent: "cursor" }); expect(result.phase).toBe("setup"); - expect(result.status).toBe("getting_started"); - expect(result.nextAction.type).toBe("agent_action"); + expect(result.status).toBe("pending"); }); - test("skips setup and getting-started when fully configured, goes to migrations", async () => { + test("hands off to the agent once MCP + skills are installed", async () => { mockIsAuthenticated.mockResolvedValue(true); - mockToolingInstalled({ - ".env": "DATABASE_URL=postgres://user:pass@ep-foo.us-east-2.aws.neon.tech/neondb", - ".neon": '{"projectId":"proj-123"}', - }); + mockToolingInstalled(); const result = await orchestrate({ agent: "cursor" }); - // With no features set, neon_auth is skipped, goes to migrations - expect(result.phase).toBe("migrations"); - }); - - test("skips neon_auth when features are empty", async () => { - mockIsAuthenticated.mockResolvedValue(true); - mockToolingInstalled({ - ".env": "DATABASE_URL=postgres://user:pass@ep-foo.us-east-2.aws.neon.tech/neondb", - ".neon": '{"projectId":"proj-123"}', - }); - - const result = await orchestrate({ - agent: "claude", - }); - - expect(result.phase).toBe("migrations"); - }); - - test("reaches complete when all phases are satisfied or skipped", async () => { - mockIsAuthenticated.mockResolvedValue(true); - mockToolingInstalled({ - ".env": "DATABASE_URL=postgres://user:pass@ep-foo.us-east-2.aws.neon.tech/neondb\nNEON_AUTH_TOKEN=abc", - ".neon": '{"projectId":"proj-123"}', - }); - - const result = await orchestrate({ - agent: "claude", - skipMigrations: true, - }); - + expect(result.phase).toBe("setup"); + expect(result.status).toBe("complete"); expect(result.nextAction.type).toBe("complete"); if (result.nextAction.type === "complete") { - expect(result.nextAction.message).toContain("complete"); + expect(result.nextAction.message).toContain("tooling is installed"); } }); - test("skips neon_auth when .neon _init features don't include auth", async () => { + test("hand-off does not depend on a database or .neon file", async () => { mockIsAuthenticated.mockResolvedValue(true); - mockToolingInstalled({ - ".env": "DATABASE_URL=postgres://user:pass@ep-foo.us-east-2.aws.neon.tech/neondb", - ".neon": - '{"projectId":"proj-123","_init":{"features":["database"]}}', - }); - - const result = await orchestrate({ agent: "claude" }); - - // Should skip neon_auth and go to migrations - expect(result.phase).toBe("migrations"); - }); - - test("runs neon_auth when .neon _init features include auth", async () => { - mockIsAuthenticated.mockResolvedValue(true); - mockToolingInstalled({ - ".env": "DATABASE_URL=postgres://user:pass@ep-foo.us-east-2.aws.neon.tech/neondb", - ".neon": - '{"projectId":"proj-123","_init":{"features":["database","auth"]}}', - }); + // Tooling installed, no .env / no .neon — still a clean hand-off. + mockToolingInstalled(); const result = await orchestrate({ agent: "claude" }); - expect(result.phase).toBe("neon_auth"); - }); - - test("enters setup even with DATABASE_URL if MCP not configured", async () => { - mockIsAuthenticated.mockResolvedValue(true); - mockAppExists({ - ".env": "DATABASE_URL=postgres://user:pass@ep-foo.us-east-2.aws.neon.tech/neondb", - }); - - const result = await orchestrate({ agent: "cursor" }); - - expect(result.phase).toBe("setup"); - expect(result.status).toBe("pending"); - }); - - test("auth phase existing_account maps to inline run_command", async () => { - mockIsAuthenticated.mockResolvedValue(false); - - const result = await orchestrate({ agent: "cursor" }); - - if (result.nextAction.type === "ask_user") { - const existing = result.nextAction.responseMapping.existing_account; - expect("action" in existing).toBe(true); - if ("action" in existing) { - expect(existing.action.type).toBe("run_command"); - } - } - }); - - test("passes detected stack info to getting-started phase", async () => { - mockIsAuthenticated.mockResolvedValue(true); - mockToolingInstalled({ - "package.json": JSON.stringify({ - dependencies: { next: "14.0.0", "@prisma/client": "5.0.0" }, - devDependencies: { prisma: "5.0.0" }, - }), - "prisma/schema.prisma": "generator client {}", - }); - - const result = await orchestrate({ agent: "cursor" }); - - expect(result.phase).toBe("setup"); - expect(result.status).toBe("getting_started"); - if (result.nextAction.type === "agent_action") { - // Should include migration step since prisma was detected - const stepIds = result.nextAction.steps.map( - (s: { id: string }) => s.id, - ); - expect(stepIds).toContain("run_migrations"); - } + expect(result.nextAction.type).toBe("complete"); }); }); diff --git a/packages/cli/src/init/orchestrate.ts b/packages/cli/src/init/orchestrate.ts index 0d978e56..2ec13af4 100644 --- a/packages/cli/src/init/orchestrate.ts +++ b/packages/cli/src/init/orchestrate.ts @@ -1,44 +1,26 @@ -import { existsSync, readFileSync, writeFileSync } from "node:fs"; -import { resolve } from "node:path"; import { isAuthenticated } from "./auth.js"; +import { handoffResponse } from "./handoff.js"; import { inspectProject } from "./inspect.js"; import { handleAuthPhase } from "./phases/auth.js"; -import { handleGettingStartedPhase } from "./phases/getting_started.js"; -import { handleMigrationsPhase } from "./phases/migrations.js"; -import { handleNeonAuthPhase } from "./phases/neon_auth.js"; import { handleSetupPhase } from "./phases/setup.js"; -import { resolveNeonContext } from "./resolve_context.js"; import type { PhaseResponse } from "./types.js"; export type OrchestratorOptions = { agent?: string; - skipMigrations?: boolean; - /** Enable preview features (e.g. project bootstrapping from templates) */ - preview?: boolean; - /** Existing project to use — skips org/project selection in getting-started */ - projectId?: string; - /** Existing org to scope to — skips org selection in getting-started */ - orgId?: string; - /** Branch to target when pulling env */ - branchId?: string; }; /** - * v2 orchestrator: checks phases in order and returns the first that needs attention. + * v2 orchestrator: checks phases in order and returns the first that needs + * attention. * * Phase order: - * auth -> setup (if tooling not installed) - * -> getting-started (if tooling installed but no Neon connection string) - * -> resolve .neon context (if connection string exists but no .neon file) - * -> neon_auth (optional) -> complete + * auth -> setup (if MCP server or skills not installed) + * -> hand off to the agent (tooling installed) * - * Each call is stateless — it re-checks everything from the file system and credentials. - * - * The orchestrator uses filesystem inspection to decide what to do: - * - No app detected → bootstrap phase (scaffold from template) - * - MCP not configured → full setup flow (inspect → install → getting-started) - * - MCP configured, no connection string → skip install, go to getting-started - * - MCP configured + connection string → fall through to neon-auth/migrations/complete + * Each call is stateless — it re-checks credentials and the filesystem every + * time. `neon init` installs tooling and stops there; connecting a database and + * configuring features is handed to the agent (see {@link handoffResponse}), so + * the orchestrator never links projects, pulls env, or touches `.neon`. */ export async function orchestrate( options: OrchestratorOptions, @@ -49,182 +31,27 @@ export async function orchestrate( return handleAuthPhase({ agent: options.agent }); } - const cwd = process.cwd(); - - // Phase 2: Inspect what's already in place + // Phase 2: Is the tooling in place? const inspection = await inspectProject([ - { id: "has_app", description: "", lookFor: [] }, { id: "mcp_server", description: "", lookFor: [] }, { id: "skills", description: "", lookFor: [] }, - { id: "connection_string", description: "", lookFor: [] }, - { id: "project_stack", description: "", lookFor: [] }, - { id: "migrations", description: "", lookFor: [] }, ]); - // Only detect empty projects when --preview is enabled - const hasApp = options.preview ? inspection.hasApp === true : true; - - // When preview is enabled, check that all preview skills are installed too - let skillsInstalled = inspection.skillsInstalled; - if (options.preview && skillsInstalled && inspection.skillsScope) { - const { getSkillList } = await import("./skills.js"); - const previewSkills = getSkillList(true); - const { existsSync: exists } = await import("node:fs"); - const { resolve: resolvePath } = await import("node:path"); - const home = process.env.HOME || process.env.USERPROFILE || ""; - const scope = inspection.skillsScope; - const dirs = - scope === "project" - ? [ - resolvePath(cwd, ".cursor", "skills"), - resolvePath(cwd, ".claude", "skills"), - resolvePath(cwd, ".agents", "skills"), - ] - : [ - resolvePath(home, ".cursor", "skills"), - resolvePath(home, ".claude", "skills"), - resolvePath(home, ".agents", "skills"), - ]; - const allPresent = previewSkills.every((skill) => - dirs.some((dir) => exists(resolvePath(dir, skill, "SKILL.md"))), - ); - if (!allPresent) { - skillsInstalled = false; - // Mark as partial so setup auto-completes to the same scope - inspection.skillsScope = - `${inspection.skillsScope}-partial` as typeof inspection.skillsScope; - } - } + const toolingInstalled = + inspection.mcpConfigured === true && + inspection.skillsInstalled === true; - const toolingInstalled = inspection.mcpConfigured && skillsInstalled; - const hasNeonConnection = inspection.connectionString === true; - - // Phase 3a: No app or tooling not installed → setup flow - // When !hasApp (preview mode), setup will offer template selection before asking about tooling. - // Clean up any stale _init state from a previous run. - if (!hasApp || !toolingInstalled) { - cleanupInitState(resolve(cwd, ".neon")); + // Phase 3: Install anything missing. + if (!toolingInstalled) { return handleSetupPhase({ agent: options.agent, - preview: options.preview, - hasApp, mcpConfigured: inspection.mcpConfigured ?? null, mcpScope: inspection.mcpScope || undefined, - skillsInstalled: skillsInstalled ?? null, + skillsInstalled: inspection.skillsInstalled ?? null, skillsScope: inspection.skillsScope || undefined, }); } - // Read .neon context early — needed for feature-based routing - const neonContextPath = resolve(cwd, ".neon"); - const neonContext = readNeonContext(neonContextPath); - const initState = - typeof neonContext._init === "object" && neonContext._init !== null - ? (neonContext._init as Record) - : {}; - const features: string[] = Array.isArray(initState.features) - ? initState.features - : []; - - // Phase 3b: Tooling installed but no Neon connection string → getting-started - if (!hasNeonConnection) { - return handleGettingStartedPhase({ - agent: options.agent, - cwd, - hasConnectionString: false, - framework: inspection.framework as string | undefined, - orm: inspection.orm as string | undefined, - migrationTool: inspection.migrationTool as string | undefined, - migrationDir: inspection.migrationDir as string | undefined, - features, - preview: options.preview, - projectId: options.projectId, - orgId: options.orgId, - branchId: options.branchId, - }); - } - - // Phase 3c: Neon connection exists but no .neon context file → resolve project - - if (!neonContext.projectId) { - // Resolve the project from the connection string and merge into .neon - try { - const resolved = await resolveNeonContext(cwd); - if (resolved) { - const merged = { ...neonContext }; - if (resolved.orgId) merged.orgId = resolved.orgId; - if (resolved.projectId) merged.projectId = resolved.projectId; - writeFileSync( - neonContextPath, - `${JSON.stringify(merged, null, 2)}\n`, - ); - } - } catch { - // Continue the flow regardless — missing .neon is not a blocker - } - } - - // Phase 4: Neon Auth — only if features include "auth" - if (features.includes("auth")) { - const hasNeonAuth = checkNeonAuth(cwd); - if (!hasNeonAuth) { - return handleNeonAuthPhase({ - agent: options.agent, - setup: true, - }); - } - } - - // Phase 5: Migrations - if (!options.skipMigrations) { - return handleMigrationsPhase({ agent: options.agent, cwd }); - } - - // All done — clean up ephemeral _init state from .neon - cleanupInitState(neonContextPath); - - return { - phase: "setup", - status: "complete", - nextAction: { - type: "complete", - message: - "Neon setup is complete. Your database is configured and your agent has the Neon MCP server and skills available.", - }, - }; -} - -/** Remove the ephemeral _init key from .neon, preserving other fields. */ -function cleanupInitState(neonContextPath: string): void { - if (!existsSync(neonContextPath)) return; - try { - const context = JSON.parse(readFileSync(neonContextPath, "utf-8")); - if (context._init !== undefined) { - delete context._init; - writeFileSync( - neonContextPath, - `${JSON.stringify(context, null, 2)}\n`, - ); - } - } catch {} -} - -function readNeonContext(neonContextPath: string): Record { - if (!existsSync(neonContextPath)) return {}; - try { - return JSON.parse(readFileSync(neonContextPath, "utf-8")); - } catch { - return {}; - } -} - -function checkNeonAuth(cwd: string): boolean { - const envPath = resolve(cwd, ".env"); - if (!existsSync(envPath)) return false; - try { - const content = readFileSync(envPath, "utf-8"); - return /^NEON_AUTH_/m.test(content); - } catch { - return false; - } + // Tooling is ready — hand the rest off to the agent. + return handoffResponse(); } diff --git a/packages/cli/src/init/phases/cleanup.ts b/packages/cli/src/init/phases/cleanup.ts deleted file mode 100644 index 1b4c9512..00000000 --- a/packages/cli/src/init/phases/cleanup.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { existsSync, readFileSync, writeFileSync } from "node:fs"; -import { resolve } from "node:path"; -import type { PhaseResponse } from "../types.js"; - -/** - * Terminal phase: cleans up ephemeral _init state from .neon and - * returns a complete message. All feature chains should end here. - */ -export function handleCleanup(): PhaseResponse { - const neonContextPath = resolve(process.cwd(), ".neon"); - if (existsSync(neonContextPath)) { - try { - const context = JSON.parse(readFileSync(neonContextPath, "utf-8")); - if (context._init !== undefined) { - delete context._init; - writeFileSync( - neonContextPath, - `${JSON.stringify(context, null, 2)}\n`, - ); - } - } catch {} - } - - return { - phase: "setup", - status: "complete", - nextAction: { - type: "complete", - message: - "Neon setup is complete! Your database is connected and your agent has the Neon MCP server and skills available.", - }, - }; -} diff --git a/packages/cli/src/init/phases/db.test.ts b/packages/cli/src/init/phases/db.test.ts deleted file mode 100644 index d7a58683..00000000 --- a/packages/cli/src/init/phases/db.test.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { describe, expect, test } from "vitest"; -import { handleDbPhase } from "./db.js"; - -describe("handleDbPhase", () => { - test("starts by listing orgs when no args provided", async () => { - const result = await handleDbPhase({}); - - expect(result.phase).toBe("db"); - expect(result.status).toBe("ready"); - expect(result.nextAction.type).toBe("run_command"); - if (result.nextAction.type === "run_command") { - expect(result.nextAction.command).toContain("neon orgs list"); - expect(result.nextAction.onSuccess.args).toContain("--orgs-result"); - } - }); - - test("selects single org and lists projects", async () => { - const orgsResult = JSON.stringify([{ id: "org-abc", name: "My Org" }]); - - const result = await handleDbPhase({ orgsResult }); - - expect(result.status).toBe("org_selected"); - expect(result.nextAction.type).toBe("run_command"); - if (result.nextAction.type === "run_command") { - expect(result.nextAction.command).toContain("--org-id org-abc"); - expect(result.nextAction.command).toContain("projects list"); - } - }); - - test("asks user to choose org when multiple exist", async () => { - const orgsResult = JSON.stringify([ - { id: "org-1", name: "Org 1" }, - { id: "org-2", name: "Org 2" }, - ]); - - const result = await handleDbPhase({ orgsResult }); - - expect(result.status).toBe("select_org"); - expect(result.nextAction.type).toBe("ask_user"); - if (result.nextAction.type === "ask_user") { - expect(result.nextAction.options).toHaveLength(2); - expect(result.nextAction.responseMapping).toHaveProperty("org-1"); - expect(result.nextAction.responseMapping).toHaveProperty("org-2"); - } - }); - - test("asks user to choose project from list", async () => { - const projectsResult = JSON.stringify({ - projects: [ - { id: "proj-1", name: "App 1" }, - { id: "proj-2", name: "App 2" }, - ], - }); - - const result = await handleDbPhase({ projectsResult, orgId: "org-1" }); - - expect(result.status).toBe("select_project"); - expect(result.nextAction.type).toBe("ask_user"); - if (result.nextAction.type === "ask_user") { - // 2 projects + "create new" option - expect(result.nextAction.options).toHaveLength(3); - expect(result.nextAction.responseMapping).toHaveProperty("proj-1"); - expect(result.nextAction.responseMapping).toHaveProperty( - "create_new", - ); - } - }); - - test("returns agent_action when project is selected", async () => { - const result = await handleDbPhase({ projectId: "proj-xyz" }); - - expect(result.status).toBe("project_ready"); - expect(result.nextAction.type).toBe("agent_action"); - if (result.nextAction.type === "agent_action") { - expect(result.nextAction.steps).toHaveLength(3); - expect(result.nextAction.steps[0].command).toContain("proj-xyz"); - expect(result.nextAction.prerequisite).toContain( - "connection-methods", - ); - } - }); - - test("handles empty projects list", async () => { - const projectsResult = JSON.stringify({ projects: [] }); - - const result = await handleDbPhase({ projectsResult }); - - expect(result.status).toBe("no_projects"); - expect(result.nextAction.type).toBe("ask_user"); - }); - - test("handles error from previous step", async () => { - const result = await handleDbPhase({ error: "orgs-list-failed" }); - - expect(result.status).toBe("error"); - expect(result.nextAction.type).toBe("ask_user"); - }); - - test("rejects project ID with shell metacharacters", async () => { - await expect( - handleDbPhase({ projectId: "proj; rm -rf /" }), - ).rejects.toThrow("Invalid project ID"); - }); - - test("rejects org ID with shell metacharacters", async () => { - await expect(handleDbPhase({ orgId: "org$(whoami)" })).rejects.toThrow( - "Invalid org ID", - ); - }); - - test("lists projects directly when org-id is provided without orgs-result", async () => { - const result = await handleDbPhase({ orgId: "org-abc" }); - - expect(result.status).toBe("org_selected"); - expect(result.nextAction.type).toBe("run_command"); - if (result.nextAction.type === "run_command") { - expect(result.nextAction.command).toContain("--org-id org-abc"); - } - }); -}); diff --git a/packages/cli/src/init/phases/db.ts b/packages/cli/src/init/phases/db.ts deleted file mode 100644 index 9b1c1d56..00000000 --- a/packages/cli/src/init/phases/db.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { assertSafeId } from "../ids.js"; -import { neonctlCmd } from "../neonctl.js"; -import { SKILL_REFERENCE_URLS } from "../skills.js"; -import type { PhaseResponse } from "../types.js"; - -export type DbPhaseOptions = { - agent?: string; - orgId?: string; - projectId?: string; - branchId?: string; - orgsResult?: string; - projectsResult?: string; - framework?: string; - orm?: string; - error?: string; -}; - -export async function handleDbPhase( - options: DbPhaseOptions, -): Promise { - const agentArgs = options.agent - ? ["--agent", options.agent, "--json"] - : ["--json"]; - - // Validate IDs that will be interpolated into shell commands - if (options.projectId) assertSafeId(options.projectId, "project ID"); - if (options.orgId) assertSafeId(options.orgId, "org ID"); - if (options.branchId) assertSafeId(options.branchId, "branch ID"); - - // Error from a previous step - if (options.error) { - return { - phase: "db", - status: "error", - error: options.error, - nextAction: { - type: "ask_user", - question: `An error occurred during database setup: ${options.error}. Would you like to try again or skip this step?`, - options: [ - { value: "retry", label: "Try again" }, - { value: "skip", label: "Skip database setup" }, - ], - responseMapping: { - retry: { args: ["db", "--json"] }, - skip: { args: agentArgs }, - }, - }, - }; - } - - // If we have a project ID, we're in the "wire it up" phase - if (options.projectId) { - const branchFlag = options.branchId - ? ` --branch-id ${options.branchId}` - : ""; - return { - phase: "db", - status: "project_ready", - project: { - id: options.projectId, - ...(options.branchId ? { branchId: options.branchId } : {}), - }, - nextAction: { - type: "agent_action", - prerequisite: SKILL_REFERENCE_URLS.connectionMethods, - steps: [ - { - id: "get_connection_string", - description: "Get the database connection string", - command: `${neonctlCmd()} connection-string --project-id ${options.projectId}${branchFlag}`, - }, - { - id: "store_env", - description: - "Append DATABASE_URL= to .env. Create .env if it doesn't exist. Do NOT overwrite existing entries. Ensure .env is in .gitignore.", - }, - { - id: "detect_framework", - description: - "Examine the project to determine the framework (Next.js, Remix, Express, etc.) and ORM (Prisma, Drizzle, raw SQL) in use.", - }, - ], - onComplete: { - type: "run_neon_init", - args: agentArgs, - }, - }, - }; - } - - // If we have projects result, let user pick or create - if (options.projectsResult) { - let projects: { id: string; name: string }[]; - try { - const parsed = JSON.parse(options.projectsResult); - projects = Array.isArray(parsed.projects) - ? parsed.projects - : Array.isArray(parsed) - ? parsed - : []; - } catch { - projects = []; - } - - const orgIdArgs = options.orgId ? ["--org-id", options.orgId] : []; - - if (projects.length === 0) { - return { - phase: "db", - status: "no_projects", - nextAction: { - type: "ask_user", - question: - "You don't have any Neon projects yet. What would you like to name your new project?", - options: [ - { - value: "create", - label: "Create a new project (provide a name)", - }, - ], - context: - `The agent should ask the user for a project name, then run: ${neonctlCmd()} projects create --name --output json` + - (options.orgId ? ` --org-id ${options.orgId}` : "") + - " and pass the result back.", - responseMapping: { - create: { - args: [ - "db", - "--json", - ...orgIdArgs, - "--project-id", - "", - ], - }, - }, - }, - }; - } - - const projectOptions = projects.map((p) => ({ - value: p.id, - label: `${p.name} (${p.id})`, - })); - projectOptions.push({ - value: "create_new", - label: "Create a new project", - }); - - const responseMapping: Record = {}; - for (const p of projects) { - responseMapping[p.id] = { - args: ["db", "--json", ...orgIdArgs, "--project-id", p.id], - }; - } - responseMapping.create_new = { - args: [ - "db", - "--json", - ...orgIdArgs, - "--project-id", - "", - ], - }; - - return { - phase: "db", - status: "select_project", - nextAction: { - type: "ask_user", - question: "Which Neon project would you like to use?", - options: projectOptions, - context: `If the user wants to create a new project, ask for a name then run: ${neonctlCmd()} projects create --name --output json and use the returned project id.`, - responseMapping, - }, - }; - } - - // If we have orgs result, decide next step - if (options.orgsResult) { - let orgs: { id: string; name: string }[]; - try { - const parsed = JSON.parse(options.orgsResult); - orgs = Array.isArray(parsed.organizations) - ? parsed.organizations - : Array.isArray(parsed) - ? parsed - : []; - } catch { - orgs = []; - } - - // Single org or org already selected: list projects - const orgId = options.orgId ?? (orgs.length === 1 ? orgs[0].id : null); - - if (orgId) { - assertSafeId(orgId, "org ID"); - return { - phase: "db", - status: "org_selected", - org: { id: orgId }, - nextAction: { - type: "run_command", - command: `${neonctlCmd()} projects list --org-id ${orgId} --output json`, - description: "Listing Neon projects.", - timeout: 30000, - onSuccess: { - type: "run_neon_init", - args: [ - "db", - "--json", - "--org-id", - orgId, - "--projects-result", - "", - ], - }, - onFailure: { - other: { - type: "run_neon_init", - args: [ - "db", - "--json", - "--error", - "projects-list-failed", - ], - }, - }, - }, - }; - } - - // Multiple orgs: ask user to pick - const orgOptions = orgs.map((o) => ({ - value: o.id, - label: `${o.name} (${o.id})`, - })); - const responseMapping: Record = {}; - for (const o of orgs) { - responseMapping[o.id] = { - args: ["db", "--json", "--org-id", o.id], - }; - } - - return { - phase: "db", - status: "select_org", - nextAction: { - type: "ask_user", - question: "Which Neon organization would you like to use?", - options: orgOptions, - responseMapping, - }, - }; - } - - // If org-id provided but no orgs-result, list projects directly - if (options.orgId) { - return { - phase: "db", - status: "org_selected", - org: { id: options.orgId }, - nextAction: { - type: "run_command", - command: `${neonctlCmd()} projects list --org-id ${options.orgId} --output json`, - description: "Listing Neon projects.", - timeout: 30000, - onSuccess: { - type: "run_neon_init", - args: [ - "db", - "--json", - "--org-id", - options.orgId, - "--projects-result", - "", - ], - }, - onFailure: { - other: { - type: "run_neon_init", - args: [ - "db", - "--json", - "--error", - "projects-list-failed", - ], - }, - }, - }, - }; - } - - // Default: start by listing orgs - return { - phase: "db", - status: "ready", - nextAction: { - type: "run_command", - command: `${neonctlCmd()} orgs list --output json`, - description: "Listing your Neon organizations.", - timeout: 30000, - onSuccess: { - type: "run_neon_init", - args: ["db", "--json", "--orgs-result", ""], - }, - onFailure: { - other: { - type: "run_neon_init", - args: ["db", "--json", "--error", "orgs-list-failed"], - }, - }, - }, - }; -} diff --git a/packages/cli/src/init/phases/getting_started.test.ts b/packages/cli/src/init/phases/getting_started.test.ts deleted file mode 100644 index 6a8f8793..00000000 --- a/packages/cli/src/init/phases/getting_started.test.ts +++ /dev/null @@ -1,243 +0,0 @@ -import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; -import { makeProjectDir } from "../../test_utils/project_dir.js"; - -vi.mock("../skills.js", async (importOriginal) => { - const actual = (await importOriginal()) as Record; - return { ...actual, ensureSkillsUpToDate: vi.fn().mockResolvedValue(true) }; -}); - -import { handleGettingStartedPhase } from "./getting_started.js"; - -describe("getting-started phase", () => { - // Every case runs against a real project directory rather than the repo it is - // being tested from, so no assertion depends on this checkout's lockfile. - let project: ReturnType; - - beforeEach(() => { - project = makeProjectDir("npm"); - }); - - afterEach(() => { - project.cleanup(); - }); - - test("returns agent_action with getting-started prerequisite", async () => { - const result = await handleGettingStartedPhase({ - agent: "claude", - cwd: project.dir, - }); - - expect(result.phase).toBe("setup"); - expect(result.status).toBe("getting_started"); - expect(result.nextAction.type).toBe("agent_action"); - - if (result.nextAction.type === "agent_action") { - expect(result.nextAction.prerequisite).toContain( - "getting-started.md", - ); - expect(result.nextAction.onComplete.type).toBe("run_neon_init"); - } - }); - - test("includes org selection, project selection, and connection string steps when no connection string", async () => { - const result = await handleGettingStartedPhase({ - agent: "claude", - cwd: project.dir, - hasConnectionString: false, - }); - - if (result.nextAction.type === "agent_action") { - const stepIds = result.nextAction.steps.map((s) => s.id); - expect(stepIds).toContain("select_org"); - expect(stepIds).toContain("select_or_create_project"); - expect(stepIds).toContain("create_project_if_needed"); - expect(stepIds).toContain("pull_env"); - // Org step should list orgs first - const orgStep = result.nextAction.steps.find( - (s) => s.id === "select_org", - ); - expect(orgStep?.command).toContain("neon orgs list"); - expect(orgStep?.description).toContain("CLI"); - // Create step should include --org-id - const createStep = result.nextAction.steps.find( - (s) => s.id === "create_project_if_needed", - ); - expect(createStep?.command).toContain("--org-id"); - } - }); - - test("skips project creation steps when connection string exists", async () => { - const result = await handleGettingStartedPhase({ - agent: "claude", - cwd: project.dir, - hasConnectionString: true, - }); - - if (result.nextAction.type === "agent_action") { - const stepIds = result.nextAction.steps.map((s) => s.id); - expect(stepIds).not.toContain("select_org"); - expect(stepIds).not.toContain("select_or_create_project"); - expect(stepIds).not.toContain("pull_env"); - } - }); - - test("includes prisma driver install when ORM is prisma", async () => { - const result = await handleGettingStartedPhase({ - agent: "claude", - cwd: project.dir, - hasConnectionString: false, - orm: "prisma", - }); - - if (result.nextAction.type === "agent_action") { - const stepIds = result.nextAction.steps.map((s) => s.id); - expect(stepIds).toContain("install_driver"); - const driverStep = result.nextAction.steps.find( - (s) => s.id === "install_driver", - ); - expect(driverStep?.command).toContain("@neondatabase/serverless"); - expect(driverStep?.command).toContain("@prisma/adapter-neon"); - } - }); - - test("includes drizzle driver install when ORM is drizzle", async () => { - const result = await handleGettingStartedPhase({ - agent: "claude", - cwd: project.dir, - hasConnectionString: false, - orm: "drizzle", - }); - - if (result.nextAction.type === "agent_action") { - const driverStep = result.nextAction.steps.find( - (s) => s.id === "install_driver", - ); - expect(driverStep?.command).toContain("@neondatabase/serverless"); - expect(driverStep?.command).not.toContain("@prisma/adapter-neon"); - } - }); - - test("includes migration command when migration tool detected", async () => { - const result = await handleGettingStartedPhase({ - agent: "claude", - cwd: project.dir, - hasConnectionString: true, - migrationTool: "prisma", - }); - - if (result.nextAction.type === "agent_action") { - const stepIds = result.nextAction.steps.map((s) => s.id); - expect(stepIds).toContain("run_migrations"); - const migrationStep = result.nextAction.steps.find( - (s) => s.id === "run_migrations", - ); - expect(migrationStep?.command).toBe( - "npx --no prisma migrate deploy", - ); - } - }); - - test("includes knex migration command", async () => { - const result = await handleGettingStartedPhase({ - agent: "claude", - cwd: project.dir, - hasConnectionString: true, - migrationTool: "knex", - }); - - if (result.nextAction.type === "agent_action") { - const migrationStep = result.nextAction.steps.find( - (s) => s.id === "run_migrations", - ); - expect(migrationStep?.command).toBe("npx --no knex migrate:latest"); - } - }); - - describe("install commands follow the project's package manager", () => { - test.each([ - [ - "pnpm", - "pnpm install", - "pnpm add @neondatabase/serverless @prisma/adapter-neon", - ], - [ - "yarn", - "yarn install", - "yarn add @neondatabase/serverless @prisma/adapter-neon", - ], - [ - "bun", - "bun install", - "bun add @neondatabase/serverless @prisma/adapter-neon", - ], - [ - "npm", - "npm install", - "npm install @neondatabase/serverless @prisma/adapter-neon", - ], - ] as const)("a %s project gets %s", async (pm, expectedInstall, expectedDriver) => { - const { dir, cleanup } = makeProjectDir(pm); - try { - const result = await handleGettingStartedPhase({ - agent: "claude", - cwd: dir, - hasConnectionString: false, - orm: "prisma", - }); - - expect(result.nextAction.type).toBe("agent_action"); - if (result.nextAction.type !== "agent_action") return; - const steps = result.nextAction.steps; - const commandOf = (id: string) => - steps.find((s) => s.id === id)?.command; - - expect(commandOf("install_dependencies")).toBe(expectedInstall); - expect(commandOf("install_driver")).toBe(expectedDriver); - } finally { - cleanup(); - } - }); - - test("the drizzle driver install follows the project too", async () => { - const { dir, cleanup } = makeProjectDir("bun"); - try { - const result = await handleGettingStartedPhase({ - agent: "claude", - cwd: dir, - hasConnectionString: false, - orm: "drizzle", - }); - - expect(result.nextAction.type).toBe("agent_action"); - if (result.nextAction.type !== "agent_action") return; - const driver = result.nextAction.steps.find( - (s) => s.id === "install_driver", - ); - expect(driver?.command).toBe( - "bun add @neondatabase/serverless", - ); - } finally { - cleanup(); - } - }); - }); - - test("always includes verify_connection step", async () => { - const result = await handleGettingStartedPhase({ - agent: "claude", - cwd: project.dir, - hasConnectionString: true, - }); - - if (result.nextAction.type === "agent_action") { - const stepIds = result.nextAction.steps.map((s) => s.id); - expect(stepIds).toContain("verify_connection"); - const verifyStep = result.nextAction.steps.find( - (s) => s.id === "verify_connection", - ); - expect(verifyStep?.description).toContain( - "direct database connection", - ); - } - }); -}); diff --git a/packages/cli/src/init/phases/getting_started.ts b/packages/cli/src/init/phases/getting_started.ts deleted file mode 100644 index 45dd5d76..00000000 --- a/packages/cli/src/init/phases/getting_started.ts +++ /dev/null @@ -1,340 +0,0 @@ -import { - DO_NOT_SUBSTITUTE_HINT, - formatExecCommand, - formatInstallCommand, - MISSING_BINARY_HINT, - resolvePackageManager, -} from "../../utils/package_manager.js"; -import { assertSafeId } from "../ids.js"; -import { neonctlCmd } from "../neonctl.js"; -import { ensureSkillsUpToDate, SKILL_REFERENCE_URLS } from "../skills.js"; -import type { PhaseResponse } from "../types.js"; - -export type GettingStartedPhaseOptions = { - agent?: string; - hasConnectionString?: boolean; - framework?: string; - orm?: string; - migrationTool?: string; - migrationDir?: string; - /** Neon features required by the project (from .neon or template) */ - features?: string[]; - /** Preview mode — restricts project creation to new projects in AWS us-east */ - preview?: boolean; - /** - * Existing project to use. When set, org/project selection is skipped and a - * single verified `neon link` step writes the .neon context. - */ - projectId?: string; - /** Existing org to scope to. When set (without projectId), org selection is skipped. */ - orgId?: string; - /** Branch to target — recorded in the .neon context so `env pull` uses it. */ - branchId?: string; - /** The project directory the emitted commands will run in. */ - cwd: string; -}; - -/** - * Initiates the "Get started with Neon" workflow. - * - * Steps are concrete and executable — each has a CLI command to run - * or a specific file operation. The agent should attempt each step - * in order and actually perform the action using the Neon CLI. - */ -export async function handleGettingStartedPhase( - options: GettingStartedPhaseOptions, -): Promise { - // Ensure skills are up to date (no-op if recently updated) - if (options.agent) { - await ensureSkillsUpToDate(options.agent); - } - const steps: { id: string; description: string; command?: string }[] = []; - - const installPm = resolvePackageManager(options.cwd); - - if (!options.hasConnectionString) { - if (options.projectId) { - // Verified fast path: the user named an existing project, so there is - // nothing to list or ask. A single `neon link` verifies the IDs against - // the account, infers the org, records the branch, and writes .neon — - // which makes the org/project selection steps and create_neon_context - // below unnecessary. - assertSafeId(options.projectId, "project ID"); - const linkFlags = [`--project-id ${options.projectId}`]; - if (options.orgId) { - assertSafeId(options.orgId, "org ID"); - linkFlags.push(`--org-id ${options.orgId}`); - } - if (options.branchId) { - assertSafeId(options.branchId, "branch ID"); - linkFlags.push(`--branch-id ${options.branchId}`); - } - steps.push({ - id: "link_project", - description: [ - "Link this directory to the existing Neon project using the CLI command below.", - "This verifies the IDs against the user's Neon account, infers the organization, and writes the .neon context file (merging into any existing file — do NOT overwrite it).", - "No organization or project selection is needed.", - ].join(" "), - command: `${neonctlCmd()} link ${linkFlags.join(" ")}`, - }); - } else { - // No project chosen yet. If an org was provided, skip org selection and - // scope the project commands to it; otherwise start by listing orgs. - const orgKnown = Boolean(options.orgId); - if (orgKnown) assertSafeId(options.orgId as string, "org ID"); - const orgRef = options.orgId ?? ""; - const orgFlag = `--org-id ${orgRef}`; - - if (!orgKnown) { - steps.push({ - id: "select_org", - description: [ - "List the user's Neon organizations using the CLI command below.", - "If only one org exists, use it automatically.", - "If multiple orgs exist, ask the user which one to use.", - "Remember the selected org ID for the next steps.", - ].join(" "), - command: `${neonctlCmd()} orgs list --output json`, - }); - } - - const orgPhrase = orgKnown - ? `organization ${orgRef}` - : "selected organization"; - const listSuffix = orgKnown - ? "" - : " (replace with the selected org ID)"; - - if (options.preview) { - // Public beta: platform features are only in AWS us-east-2 for now - steps.push( - { - id: "select_or_create_project", - description: [ - `List existing Neon projects in the ${orgPhrase} using the CLI command below${listSuffix}.`, - "IMPORTANT: Neon features (Functions, Object Storage, and AI Gateway) are currently in beta and only available in the AWS us-east-2 region (more regions coming shortly). Projects must have region_id 'aws-us-east-2' and be created on or after 2026-06-15.", - "Filter the project list to ONLY show projects where region_id is 'aws-us-east-2' AND created_at is on or after '2026-06-15'.", - "If eligible projects exist, present them alongside a 'Create new project' option.", - "If no eligible projects exist, tell the user and proceed directly to creating a new one.", - "IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - ].join(" "), - command: `${neonctlCmd()} projects list ${orgFlag} --output json`, - }, - { - id: "create_project_if_needed", - description: [ - `If the user chose to create a new project, create it in the AWS us-east-2 region using the CLI command below${orgKnown ? "" : " (replace and )"}.`, - "Ask the user for a project name (suggest the current directory name).", - "If the user chose an existing eligible project, skip this step.", - ].join(" "), - command: `${neonctlCmd()} projects create --name ${orgFlag} --region-id aws-us-east-2 --output json`, - }, - ); - } else { - // Standard mode: let user choose existing or create new - steps.push( - { - id: "select_or_create_project", - description: [ - `List existing Neon projects in the ${orgPhrase} using the CLI command below${listSuffix}.`, - "Ask the user whether they want to use an existing project or create a new one.", - "If creating new, ask the user for a project name (suggest the current directory name).", - "IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.", - ].join(" "), - command: `${neonctlCmd()} projects list ${orgFlag} --output json`, - }, - { - id: "create_project_if_needed", - description: [ - `If the user chose to create a new project, create it using the CLI command below${orgKnown ? "" : " (replace and )"}.`, - "If the user chose an existing project, skip this step.", - ].join(" "), - command: `${neonctlCmd()} projects create --name ${orgFlag} --output json`, - }, - ); - } - - // Create/update .neon context file - steps.push({ - id: "create_neon_context", - description: [ - "Update the .neon context file in the project root with the selected org and project IDs.", - "IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved.", - "If no .neon file exists, create one.", - 'The file is JSON. Add/update only the orgId and projectId fields: {"orgId": "", "projectId": "", ...existing fields}.', - "This file is safe to commit — it contains no secrets.", - ].join(" "), - }); - } - - // Install project dependencies (required before env pull — config files may import packages) - steps.push({ - id: "install_dependencies", - description: [ - "Check if node_modules exists in the project root. If not, install the project's dependencies.", - DO_NOT_SUBSTITUTE_HINT, - "This must be done before `neon env pull` because the project's Neon config file may import packages that need to be installed first.", - ].join(" "), - command: formatInstallCommand(installPm), - }); - - // Pull environment variables (connection string, etc.) from Neon - steps.push({ - id: "pull_env", - description: [ - "Now that the .neon context file is in place and dependencies are installed, run `neon env pull` to populate the project's environment variables.", - "This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file.", - "It reads the .neon context file to determine the project, and writes to the appropriate env file for the project.", - "Ensure the target env file is listed in .gitignore.", - ].join(" "), - command: `${neonctlCmd()} env pull`, - }); - - // Step 6: Install Neon serverless driver if needed - if (options.orm === "prisma") { - steps.push({ - id: "install_driver", - description: [ - "Install the @neondatabase/serverless driver adapter for Prisma.", - "This enables Prisma to use Neon's serverless driver for edge/serverless deployments.", - DO_NOT_SUBSTITUTE_HINT, - ].join(" "), - command: formatInstallCommand(installPm, [ - "@neondatabase/serverless", - "@prisma/adapter-neon", - ]), - }); - } else if (options.orm === "drizzle" || options.orm === "drizzle-orm") { - steps.push({ - id: "install_driver", - description: `Install the Neon serverless driver for Drizzle. ${DO_NOT_SUBSTITUTE_HINT}`, - command: formatInstallCommand(installPm, [ - "@neondatabase/serverless", - ]), - }); - } else if (!options.orm || options.orm === "none") { - steps.push({ - id: "install_driver", - description: `Install the Neon serverless driver for direct database access. ${DO_NOT_SUBSTITUTE_HINT}`, - command: formatInstallCommand(installPm, [ - "@neondatabase/serverless", - ]), - }); - } - } - - // Run migrations if applicable - if (options.migrationTool && options.migrationTool !== "none") { - const tool = options.migrationTool.toLowerCase(); - const migrationDir = options.migrationDir; - const hasMigrationDir = migrationDir && migrationDir !== "none"; - - if (tool === "drizzle") { - const migrate = formatExecCommand(installPm, "drizzle-kit", [ - "migrate", - ]); - const generate = formatExecCommand(installPm, "drizzle-kit", [ - "generate", - ]); - steps.push({ - id: "run_migrations", - description: [ - hasMigrationDir - ? `Check if the ${migrationDir} directory contains .sql migration files.` - : "Check if a drizzle migrations directory exists with .sql files.", - `If .sql files exist, apply them with \`${migrate}\`.`, - `If the directory is empty or missing but a drizzle schema file exists (e.g. src/db/schema.ts, drizzle/schema.ts), run \`${generate}\` first to create migrations, then \`${migrate}\` to apply them.`, - "If neither schema nor migrations exist, skip this step.", - MISSING_BINARY_HINT, - ].join(" "), - command: migrate, - }); - } else if (tool === "prisma") { - const deploy = formatExecCommand(installPm, "prisma", [ - "migrate", - "deploy", - ]); - steps.push({ - id: "run_migrations", - description: [ - hasMigrationDir - ? `Check if the ${migrationDir} directory contains migration folders.` - : "Check if prisma/migrations contains migration folders.", - `If migrations exist, apply them with \`${deploy}\`.`, - `If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run \`${formatExecCommand(installPm, "prisma", ["migrate", "dev", "--name", "init"])}\` to create and apply the initial migration.`, - "If no models are defined, skip this step.", - MISSING_BINARY_HINT, - ].join(" "), - command: deploy, - }); - } else if (tool === "knex") { - steps.push({ - id: "run_migrations", - description: `Apply existing knex migrations to the Neon database. ${MISSING_BINARY_HINT}`, - command: formatExecCommand(installPm, "knex", [ - "migrate:latest", - ]), - }); - } - } else if (options.preview) { - // Bootstrap flow: migration tool wasn't detected because the project was - // inspected before scaffolding. Detect and run migrations from the scaffolded template. - steps.push({ - id: "run_migrations", - description: [ - "Check the scaffolded project for a migration tool and schema.", - "Look for: drizzle.config.ts/js (Drizzle), prisma/schema.prisma (Prisma), or knexfile.ts/js (Knex).", - `If Drizzle is found: check if a drizzle migrations directory exists with .sql files. If .sql files exist, run \`${formatExecCommand(installPm, "drizzle-kit", ["migrate"])}\`. If the directory is empty or missing but a schema file exists, run \`${formatExecCommand(installPm, "drizzle-kit", ["generate"])}\` first, then \`${formatExecCommand(installPm, "drizzle-kit", ["migrate"])}\`.`, - `If Prisma is found: check if prisma/migrations contains migration folders. If yes, run \`${formatExecCommand(installPm, "prisma", ["migrate", "deploy"])}\`. If not but models exist, run \`${formatExecCommand(installPm, "prisma", ["migrate", "dev", "--name", "init"])}\`.`, - "If no migration tool is found, skip this step.", - MISSING_BINARY_HINT, - ].join(" "), - }); - } - - // Verify the connection - steps.push({ - id: "verify_connection", - description: [ - "Verify the database connection works by running a SQL query against the Neon database.", - "Write and run a short script that connects using DATABASE_URL from the project's env file and executes `SELECT 1` (or queries a table from the migration if migrations were run).", - "Do NOT use the Neon CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity.", - ].join(" "), - }); - - return { - phase: "setup", - status: "getting_started", - nextAction: { - type: "agent_action", - prerequisite: SKILL_REFERENCE_URLS.gettingStarted, - steps, - onComplete: buildOnComplete(options), - }, - }; -} - -function buildOnComplete( - options: GettingStartedPhaseOptions, -): import("../types.js").RunNeonInitAction { - const agentArgs = options.agent ? ["--agent", options.agent] : []; - const features = options.features ?? []; - const hasFeatureRequirements = features.length > 0; - - // If features are specified and auth is not required, go to finalize - if (hasFeatureRequirements && !features.includes("auth")) { - return { - type: "run_neon_init", - args: ["finalize", "--json", ...agentArgs], - }; - } - - // Chain to neon-auth — if user already selected auth via features, go straight to setup - const authSetup = - hasFeatureRequirements && features.includes("auth") ? ["--setup"] : []; - return { - type: "run_neon_init", - args: ["neon-auth", "--json", ...agentArgs, ...authSetup], - }; -} diff --git a/packages/cli/src/init/phases/migrations.test.ts b/packages/cli/src/init/phases/migrations.test.ts deleted file mode 100644 index d24cdade..00000000 --- a/packages/cli/src/init/phases/migrations.test.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; -import { makeProjectDir } from "../../test_utils/project_dir.js"; - -vi.mock("../skills.js", async (importOriginal) => { - const actual = (await importOriginal()) as Record; - return { ...actual, ensureSkillsUpToDate: vi.fn().mockResolvedValue(true) }; -}); - -import { handleMigrationsPhase } from "./migrations.js"; - -describe("handleMigrationsPhase", () => { - let project: ReturnType; - - beforeEach(() => { - project = makeProjectDir("npm"); - }); - - afterEach(() => { - project.cleanup(); - }); - - test("asks agent to detect migrations by default", async () => { - const result = await handleMigrationsPhase({ cwd: project.dir }); - - expect(result.phase).toBe("migrations"); - expect(result.status).toBe("detection_needed"); - expect(result.nextAction.type).toBe("agent_check"); - if (result.nextAction.type === "agent_check") { - expect(result.nextAction.checks[0].id).toBe("existing_migrations"); - } - }); - - test("returns found status when tool is detected", async () => { - const result = await handleMigrationsPhase({ - cwd: project.dir, - tool: "prisma", - migrationDir: "prisma/migrations", - }); - - expect(result.status).toBe("found"); - expect(result.nextAction.type).toBe("ask_user"); - if (result.nextAction.type === "ask_user") { - expect(result.nextAction.question).toContain("prisma"); - expect(result.nextAction.responseMapping).toHaveProperty("apply"); - } - }); - - test("returns none_found when tool is none", async () => { - const result = await handleMigrationsPhase({ - cwd: project.dir, - tool: "none", - }); - - expect(result.status).toBe("none_found"); - expect(result.nextAction.type).toBe("ask_user"); - if (result.nextAction.type === "ask_user") { - expect(result.nextAction.responseMapping).toHaveProperty("prisma"); - expect(result.nextAction.responseMapping).toHaveProperty("drizzle"); - expect(result.nextAction.responseMapping).toHaveProperty("skip"); - } - }); - - test("--scaffold prisma returns agent_action steps", async () => { - const result = await handleMigrationsPhase({ - cwd: project.dir, - scaffold: "prisma", - }); - - expect(result.status).toBe("scaffolding"); - expect(result.nextAction.type).toBe("agent_action"); - if (result.nextAction.type === "agent_action") { - expect(result.nextAction.steps.length).toBeGreaterThan(0); - expect(result.nextAction.steps[0].command).toContain("prisma"); - } - }); - - test("--scaffold drizzle returns agent_action steps", async () => { - const result = await handleMigrationsPhase({ - cwd: project.dir, - scaffold: "drizzle", - }); - - expect(result.status).toBe("scaffolding"); - expect(result.nextAction.type).toBe("agent_action"); - if (result.nextAction.type === "agent_action") { - expect(result.nextAction.steps[0].command).toContain("drizzle"); - } - }); - - describe("scaffold installs use the project's package manager", () => { - // The dev-dependency flag is the part that differs beyond the verb: bun - // spells it -d, the other three -D. - test.each([ - ["npm", "npm install -D prisma"], - ["pnpm", "pnpm add -D prisma"], - ["yarn", "yarn add -D prisma"], - ["bun", "bun add -D prisma"], - ] as const)("prisma on %s", async (pm, expected) => { - const { dir, cleanup } = makeProjectDir(pm); - try { - const result = await handleMigrationsPhase({ - cwd: dir, - scaffold: "prisma", - }); - expect(result.nextAction.type).toBe("agent_action"); - if (result.nextAction.type !== "agent_action") return; - const step = result.nextAction.steps.find( - (s) => s.id === "install_prisma", - ); - expect(step?.command).toBe(expected); - } finally { - cleanup(); - } - }); - - test.each([ - [ - "npm", - "npm install drizzle-orm @neondatabase/serverless && npm install -D drizzle-kit", - ], - [ - "pnpm", - "pnpm add drizzle-orm @neondatabase/serverless && pnpm add -D drizzle-kit", - ], - [ - "bun", - "bun add drizzle-orm @neondatabase/serverless && bun add -D drizzle-kit", - ], - ] as const)("drizzle on %s", async (pm, expected) => { - const { dir, cleanup } = makeProjectDir(pm); - try { - const result = await handleMigrationsPhase({ - cwd: dir, - scaffold: "drizzle", - }); - expect(result.nextAction.type).toBe("agent_action"); - if (result.nextAction.type !== "agent_action") return; - const step = result.nextAction.steps.find( - (s) => s.id === "install_drizzle", - ); - expect(step?.command).toBe(expected); - } finally { - cleanup(); - } - }); - }); - - describe("the tool is invoked through the project's runner too", () => { - // A step list that installs with bun and then runs the binary with npx is - // the inconsistency this covers. - test.each([ - [ - "bun", - "bun run drizzle-kit generate && bun run drizzle-kit migrate", - ], - [ - "pnpm", - "pnpm exec drizzle-kit generate && pnpm exec drizzle-kit migrate", - ], - [ - "npm", - "npx --no drizzle-kit generate && npx --no drizzle-kit migrate", - ], - ] as const)("drizzle on %s", async (pm, expected) => { - const { dir, cleanup } = makeProjectDir(pm); - try { - const result = await handleMigrationsPhase({ - cwd: dir, - scaffold: "drizzle", - }); - if (result.nextAction.type !== "agent_action") - throw new Error(); - expect( - result.nextAction.steps.find( - (s) => s.id === "run_migration", - )?.command, - ).toBe(expected); - } finally { - cleanup(); - } - }); - - test("applying prisma migrations uses the project's runner", async () => { - const { dir, cleanup } = makeProjectDir("bun"); - try { - const result = await handleMigrationsPhase({ - cwd: dir, - apply: true, - tool: "prisma", - }); - if (result.nextAction.type !== "agent_action") - throw new Error(); - const commands = result.nextAction.steps.map((s) => s.command); - expect(commands).toContain("bun run prisma migrate deploy"); - expect(commands).toContain("bun run prisma generate"); - } finally { - cleanup(); - } - }); - }); - - test("--apply with tool returns agent_action", async () => { - const result = await handleMigrationsPhase({ - cwd: project.dir, - apply: true, - tool: "prisma", - }); - - expect(result.status).toBe("applying"); - expect(result.nextAction.type).toBe("agent_action"); - if (result.nextAction.type === "agent_action") { - const applyStep = result.nextAction.steps.find( - (s) => s.id === "apply", - ); - expect(applyStep?.command).toContain("prisma migrate deploy"); - } - }); -}); diff --git a/packages/cli/src/init/phases/migrations.ts b/packages/cli/src/init/phases/migrations.ts deleted file mode 100644 index 6848acad..00000000 --- a/packages/cli/src/init/phases/migrations.ts +++ /dev/null @@ -1,320 +0,0 @@ -import { - DO_NOT_SUBSTITUTE_HINT, - formatExecCommand, - formatInstallCommand, - MISSING_BINARY_HINT, - type PackageManager, - resolvePackageManager, -} from "../../utils/package_manager.js"; -import { ensureSkillsUpToDate } from "../skills.js"; -import type { PhaseResponse } from "../types.js"; - -export type MigrationsPhaseOptions = { - agent?: string; - tool?: string; - migrationDir?: string; - scaffold?: "prisma" | "drizzle"; - apply?: boolean; - /** The project directory the emitted commands will run in. */ - cwd: string; -}; - -export async function handleMigrationsPhase( - options: MigrationsPhaseOptions, -): Promise { - // Ensure skills are up to date (no-op if recently updated) - if (options.agent) { - await ensureSkillsUpToDate(options.agent); - } - const agentArgs = options.agent - ? ["--agent", options.agent, "--json"] - : ["--json"]; - const pm = resolvePackageManager(options.cwd); - - // --scaffold: set up a new migration framework - if (options.scaffold) { - if (options.scaffold === "prisma") { - return { - phase: "migrations", - status: "scaffolding", - tool: "prisma", - nextAction: { - type: "agent_action", - steps: [ - { - id: "install_prisma", - description: `Install Prisma as a dev dependency. ${DO_NOT_SUBSTITUTE_HINT}`, - command: formatInstallCommand(pm, ["prisma"], { - dev: true, - }), - }, - { - id: "init_prisma", - description: `Initialize Prisma with PostgreSQL provider. ${MISSING_BINARY_HINT}`, - command: formatExecCommand(pm, "prisma", [ - "init", - "--datasource-provider", - "postgresql", - ]), - }, - { - id: "configure_env", - description: - "Ensure DATABASE_URL in .env points to your Neon database. The prisma init command may have created a placeholder — replace it with the real connection string if needed.", - }, - { - id: "create_schema", - description: - "Help the user define their initial schema in prisma/schema.prisma based on their application needs.", - }, - { - id: "run_migration", - description: `Create and apply the initial migration. ${MISSING_BINARY_HINT}`, - command: formatExecCommand(pm, "prisma", [ - "migrate", - "dev", - "--name", - "init", - ]), - }, - ], - onComplete: { - type: "complete", - message: `Prisma is set up with your Neon database. You can now define models in schema.prisma and run migrations with \`${formatExecCommand(pm, "prisma", ["migrate", "dev"])}\`.`, - }, - }, - }; - } - - // drizzle - return { - phase: "migrations", - status: "scaffolding", - tool: "drizzle", - nextAction: { - type: "agent_action", - steps: [ - { - id: "install_drizzle", - description: `Install Drizzle ORM, drizzle-kit, and the Neon serverless driver. ${DO_NOT_SUBSTITUTE_HINT}`, - command: [ - formatInstallCommand(pm, [ - "drizzle-orm", - "@neondatabase/serverless", - ]), - formatInstallCommand(pm, ["drizzle-kit"], { - dev: true, - }), - ].join(" && "), - }, - { - id: "create_config", - description: - "Create drizzle.config.ts at the project root. Set the dialect to 'postgresql' and dbCredentials.url to process.env.DATABASE_URL.", - }, - { - id: "create_schema", - description: - "Create a schema file (e.g. src/db/schema.ts) and help the user define their initial tables using Drizzle's table builder.", - }, - { - id: "run_migration", - description: `Generate and apply the initial migration. ${MISSING_BINARY_HINT}`, - command: drizzleGenerateAndMigrate(pm), - }, - ], - onComplete: { - type: "complete", - message: `Drizzle ORM is set up with your Neon database. Define tables in your schema file and run migrations with \`${drizzleGenerateAndMigrate(pm)}\`.`, - }, - }, - }; - } - - // --apply: apply existing migrations - if (options.apply && options.tool) { - const applySteps = getMigrationApplySteps(options.tool, pm); - return { - phase: "migrations", - status: "applying", - tool: options.tool, - nextAction: { - type: "agent_action", - steps: applySteps, - onComplete: { - type: "complete", - message: "Database migrations applied successfully.", - }, - }, - }; - } - - // Agent reported detection results via --tool and --migration-dir - if (options.tool && options.tool !== "none") { - return { - phase: "migrations", - status: "found", - detected: { - tool: options.tool, - migrationDir: options.migrationDir ?? null, - }, - nextAction: { - type: "ask_user", - question: `Found ${options.tool} migrations${options.migrationDir ? ` in ${options.migrationDir}` : ""}. Would you like to apply them to your Neon database?`, - options: [ - { value: "apply", label: "Yes, apply migrations" }, - { value: "skip", label: "Skip for now" }, - ], - responseMapping: { - apply: { - args: [ - "migrations", - "--json", - "--apply", - "--tool", - options.tool, - ], - }, - skip: { args: [...agentArgs, "--skip-migrations"] }, - }, - }, - }; - } - - if (options.tool === "none") { - return { - phase: "migrations", - status: "none_found", - nextAction: { - type: "ask_user", - question: - "No existing database migrations were found. Would you like to set up a migration framework?", - options: [ - { - value: "prisma", - label: "Prisma (recommended for TypeScript)", - }, - { value: "drizzle", label: "Drizzle ORM" }, - { - value: "skip", - label: "Skip - I'll handle migrations myself", - }, - ], - context: - "A migration framework helps you version-control your database schema changes.", - responseMapping: { - prisma: { - args: ["migrations", "--json", "--scaffold", "prisma"], - }, - drizzle: { - args: ["migrations", "--json", "--scaffold", "drizzle"], - }, - skip: { args: [...agentArgs, "--skip-migrations"] }, - }, - }, - }; - } - - // Default: ask agent to detect migration tooling - return { - phase: "migrations", - status: "detection_needed", - nextAction: { - type: "agent_check", - checks: [ - { - id: "existing_migrations", - description: - "Check if the project has existing database migrations", - lookFor: [ - "prisma/migrations/ directory or schema.prisma file", - "drizzle/ directory or drizzle.config.ts", - "migrations/ or db/migrations/ directory", - "knex migration files (knexfile.js/ts)", - ], - }, - ], - reportBack: { - type: "run_neon_init", - args: [ - "migrations", - "--json", - "--tool", - "", - "--migration-dir", - "", - ], - }, - }, - }; -} - -/** `drizzle-kit generate` then `migrate`, both through the project's runner. */ -const drizzleGenerateAndMigrate = (pm: PackageManager): string => - [ - formatExecCommand(pm, "drizzle-kit", ["generate"]), - formatExecCommand(pm, "drizzle-kit", ["migrate"]), - ].join(" && "); - -function getMigrationApplySteps(tool: string, pm: PackageManager) { - switch (tool) { - case "prisma": - return [ - { - id: "ensure_env", - description: "Verify DATABASE_URL is set in .env", - }, - { - id: "apply", - description: `Apply migrations to the Neon database. ${MISSING_BINARY_HINT}`, - command: formatExecCommand(pm, "prisma", [ - "migrate", - "deploy", - ]), - }, - { - // No MISSING_BINARY_HINT: the apply step immediately above - // carries it, and `prisma generate` writes a client without - // touching a database, so that warning is not true here. - id: "generate", - description: "Generate the Prisma client.", - command: formatExecCommand(pm, "prisma", ["generate"]), - }, - ]; - case "drizzle": - return [ - { - id: "ensure_env", - description: "Verify DATABASE_URL is set in .env", - }, - { - id: "apply", - description: `Apply migrations to the Neon database. ${MISSING_BINARY_HINT}`, - command: formatExecCommand(pm, "drizzle-kit", ["migrate"]), - }, - ]; - case "knex": - return [ - { - id: "ensure_env", - description: "Verify DATABASE_URL is set in .env", - }, - { - id: "apply", - description: `Apply migrations to the Neon database. ${MISSING_BINARY_HINT}`, - command: formatExecCommand(pm, "knex", ["migrate:latest"]), - }, - ]; - default: - return [ - { - id: "ensure_env", - description: "Verify DATABASE_URL is set in .env", - }, - { - id: "apply", - description: `Apply ${tool} migrations to the Neon database using the appropriate command for your migration tool.`, - }, - ]; - } -} diff --git a/packages/cli/src/init/phases/neon_auth.ts b/packages/cli/src/init/phases/neon_auth.ts deleted file mode 100644 index e9104e3b..00000000 --- a/packages/cli/src/init/phases/neon_auth.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { neonctlCmd } from "../neonctl.js"; -import { ensureSkillsUpToDate, SKILL_REFERENCE_URLS } from "../skills.js"; -import type { PhaseResponse } from "../types.js"; - -export type NeonAuthPhaseOptions = { - agent?: string; - setup?: boolean; - info?: boolean; - projectId?: string; -}; - -export async function handleNeonAuthPhase( - options: NeonAuthPhaseOptions, -): Promise { - // Validate IDs that may be interpolated into instruction strings - if (options.projectId && !/^[\w.:-]+$/.test(options.projectId)) { - throw new Error( - `Invalid project ID: "${options.projectId}". Expected alphanumeric, hyphens, underscores, dots, or colons.`, - ); - } - - // Ensure skills are up to date (no-op if recently updated) - if (options.agent) { - await ensureSkillsUpToDate(options.agent); - } - // --info: return information about Neon Auth - if (options.info) { - return { - phase: "neon_auth", - status: "info", - nextAction: { - type: "ask_user", - question: - "Neon Auth provides drop-in user authentication that integrates with your Neon database. It handles user sign-up, sign-in, and session management. Would you like to set it up?", - options: [ - { value: "yes", label: "Yes, set up Neon Auth" }, - { value: "no", label: "No, skip for now" }, - ], - context: `Full documentation: ${SKILL_REFERENCE_URLS.neonAuth}`, - responseMapping: { - yes: { - args: [ - "neon-auth", - "--json", - ...(options.agent - ? ["--agent", options.agent] - : []), - "--setup", - ...(options.projectId - ? ["--project-id", options.projectId] - : []), - ], - }, - no: { - args: [ - "finalize", - "--json", - ...(options.agent - ? ["--agent", options.agent] - : []), - ], - }, - }, - }, - }; - } - - // --setup: guide through Neon Auth configuration - if (options.setup) { - return { - phase: "neon_auth", - status: "in_progress", - nextAction: { - type: "agent_action", - prerequisite: SKILL_REFERENCE_URLS.neonAuth, - steps: [ - { - id: "provision", - description: - "Enable Neon Auth on the project using the Neon CLI. " + - `Run: \`${neonctlCmd()} neon-auth enable --project-id --output json\`. ` + - `You can check current status with: \`${neonctlCmd()} neon-auth status --project-id --output json\`.` + - (options.projectId - ? ` Project ID: ${options.projectId}.` - : " Determine the project ID from the .neon file in the project root, or from the DATABASE_URL in .env, or ask the user."), - }, - { - id: "install_packages", - description: - "Install required packages per the skill reference. The exact packages depend on the framework (Next.js, React, etc.).", - }, - { - id: "create_components", - description: - "Create auth components per the skill reference. Follow the exact patterns and imports specified.", - }, - { - id: "pull_env", - description: `Run \`${neonctlCmd()} env pull\` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.`, - command: `${neonctlCmd()} env pull`, - }, - ], - onComplete: { - type: "run_neon_init", - args: [ - "finalize", - "--json", - ...(options.agent ? ["--agent", options.agent] : []), - ], - }, - }, - }; - } - - // Default: ask if they want Neon Auth - return { - phase: "neon_auth", - status: "optional", - nextAction: { - type: "ask_user", - question: - "Would you like to set up Neon Auth for user authentication in your app?", - options: [ - { value: "yes", label: "Yes, set up Neon Auth" }, - { value: "no", label: "No, skip for now" }, - { value: "info", label: "Tell me more about Neon Auth" }, - ], - context: - "Neon Auth provides drop-in user authentication that integrates with your Neon database.", - responseMapping: { - yes: { - args: [ - "neon-auth", - "--json", - ...(options.agent ? ["--agent", options.agent] : []), - "--setup", - ], - }, - no: { - args: [ - "finalize", - "--json", - ...(options.agent ? ["--agent", options.agent] : []), - ], - }, - info: { - args: [ - "neon-auth", - "--json", - ...(options.agent ? ["--agent", options.agent] : []), - "--info", - ], - }, - }, - }, - }; -} diff --git a/packages/cli/src/init/phases/setup.test.ts b/packages/cli/src/init/phases/setup.test.ts index 21a94f41..b35a9d57 100644 --- a/packages/cli/src/init/phases/setup.test.ts +++ b/packages/cli/src/init/phases/setup.test.ts @@ -16,11 +16,6 @@ vi.mock("execa", () => ({ vi.mock("../inspect.js", () => ({ inspectProject: vi.fn().mockResolvedValue({ - connectionString: false, - framework: "next", - orm: "prisma", - migrationTool: "prisma", - migrationDir: "prisma/migrations", isVscodeIde: true, }), })); @@ -48,30 +43,6 @@ vi.mock("node:fs/promises", () => ({ unlink: vi.fn().mockReturnValue(Promise.resolve()), })); -vi.mock("../bootstrap.js", () => { - const templates = [ - { - id: "hono", - title: "Hono API", - description: "A Hono template.", - requires: ["database"], - source: { - owner: "neondatabase", - repo: "examples", - ref: "main", - subdir: "with-hono", - }, - }, - ]; - return { - fetchTemplates: vi.fn().mockResolvedValue(templates), - FALLBACK_TEMPLATES: templates, - findTemplate: (ts: typeof templates, id: string) => - ts.find((t) => t.id === id), - scaffoldTemplate: vi.fn().mockResolvedValue(1), - }; -}); - // Mock global fetch for Open VSX VSIX download const mockFetch = vi.fn(); vi.stubGlobal("fetch", mockFetch); @@ -100,6 +71,7 @@ describe("setup phase", () => { body: "mock-stream", }); }); + test("returns inspection checks with phased userPreferences and instructions", async () => { const result = await handleSetupPhase({ agent: "claude" }); @@ -127,28 +99,22 @@ describe("setup phase", () => { ); expect(dataPlaceholder).toContain("agent"); expect(dataPlaceholder).toContain("mcpConfigured"); - // Should NOT require framework/orm/etc — CLI handles those + // Should NOT ask for features or stack info anymore. expect(dataPlaceholder).not.toContain("framework"); + expect(dataPlaceholder).not.toContain("features"); // reportBack should use --data flag expect(result.nextAction.reportBack.args[0]).toBe("setup"); expect(result.nextAction.reportBack.args).toContain("--data"); - // No consent preference — mode should be first + // No feature/consent preference — mode should be first const prefs = result.nextAction.userPreferences ?? []; expect(prefs.find((p) => p.id === "consent")).toBeUndefined(); + expect(prefs.find((p) => p.id === "features")).toBeUndefined(); + expect(prefs.find((p) => p.id === "template")).toBeUndefined(); const modePref = prefs.find((p) => p.id === "mode"); expect(modePref?.phase).toBe("after_checks"); - const defaultsOption = modePref?.options.find( - (option) => - typeof option !== "string" && option.value === "defaults", - ); - expect( - typeof defaultsOption === "string" - ? defaultsOption - : defaultsOption?.label, - ).not.toContain("extension"); const mcpScopePref = prefs.find((p) => p.id === "mcpScope"); expect(mcpScopePref?.phase).toBe("after_checks"); @@ -167,37 +133,10 @@ describe("setup phase", () => { } }); - test("asks defaults vs customize after inspection results received (legacy flags)", async () => { - const result = await handleSetupPhase({ - agent: "claude", - mcpConfigured: false, - connectionString: false, - framework: "next", - orm: "prisma", - migrationTool: "prisma", - migrationDir: "prisma/migrations", - isVscodeIde: true, - }); - - expect(result.phase).toBe("setup"); - expect(result.status).toBe("pending"); - expect(result.nextAction.type).toBe("agent_check"); - - if (result.nextAction.type === "agent_check") { - const prefs = ( - result.nextAction as unknown as Record - ).userPreferences as { id: string }[]; - expect(prefs.some((p) => p.id === "mode")).toBe(true); - } - }); - - test("defaults mode executes installation and chains to getting-started", async () => { + test("defaults mode executes installation and hands off to the agent", async () => { const result = await handleSetupPhase({ agent: "vscode", mcpConfigured: false, - connectionString: false, - framework: "next", - orm: "prisma", isVscodeIde: true, mode: "defaults", }); @@ -215,10 +154,11 @@ describe("setup phase", () => { expect(resultIds).not.toContain("install_extension"); expect(results.every((r) => r.status === "success")).toBe(true); - // nextAction should chain to the getting-started CLI command - expect(result.nextAction.type).toBe("run_neon_init"); - const args = (result.nextAction as { args: string[] }).args; - expect(args[0]).toBe("getting-started"); + // nextAction hands off to the agent — no more neon init commands. + expect(result.nextAction.type).toBe("complete"); + if (result.nextAction.type === "complete") { + expect(result.nextAction.message).toContain("tooling is installed"); + } // Should have called execa for MCP only (neonctl mocked, skills via ensureSkillsUpToDate) expect(mockExeca).toHaveBeenCalledTimes(1); @@ -235,9 +175,6 @@ describe("setup phase", () => { const result = await handleSetupPhase({ agent: "claude", mcpConfigured: true, - connectionString: true, - framework: "express", - orm: "none", isVscodeIde: false, mode: "defaults", }); @@ -249,52 +186,20 @@ describe("setup phase", () => { // Should not have install_extension since not a vscode IDE expect(resultIds).not.toContain("install_extension"); - // nextAction should chain to getting-started with --data containing hasConnectionString - if (result.nextAction.type === "run_neon_init") { - expect(result.nextAction.args).toContain("--data"); - const dataArg = - result.nextAction.args[ - result.nextAction.args.indexOf("--data") + 1 - ]; - expect(JSON.parse(dataArg)).toHaveProperty( - "hasConnectionString", - true, - ); - } + expect(result.nextAction.type).toBe("complete"); }); - test("customize mode goes straight to execution", async () => { + test("customize mode goes straight to execution and hands off", async () => { const result = await handleSetupPhase({ agent: "claude", mcpConfigured: false, - connectionString: false, - framework: "none", - orm: "none", isVscodeIde: true, mode: "customize", }); expect(result.phase).toBe("setup"); expect(result.status).toBe("installed"); - }); - - test("customize mode without vscode IDE omits extension options", async () => { - const result = await handleSetupPhase({ - agent: "claude", - mcpConfigured: false, - connectionString: false, - framework: "none", - orm: "none", - isVscodeIde: false, - mode: "customize", - }); - - if (result.nextAction.type === "ask_user") { - const optionValues = ( - result.nextAction.options as { value: string; label: string }[] - ).map((o) => o.value); - expect(optionValues).not.toContain("global_project_noext"); - } + expect(result.nextAction.type).toBe("complete"); }); test("defaults mode does not install the Cursor extension", async () => { @@ -305,9 +210,6 @@ describe("setup phase", () => { const result = await handleSetupPhase({ agent: "cursor", mcpConfigured: false, - connectionString: false, - framework: "django", - orm: "none", isVscodeIde: true, mode: "defaults", }); @@ -335,9 +237,6 @@ describe("setup phase", () => { const result = await handleSetupPhase({ agent: "cursor", mcpConfigured: false, - connectionString: false, - framework: "none", - orm: "none", isVscodeIde: true, mode: "customize", installExtension: true, @@ -353,38 +252,10 @@ describe("setup phase", () => { expect(extResult?.manualAction).toBe(true); }); - test("--data with defaults mode executes installation directly", async () => { - const result = await handleSetupPhase({ - agent: "cursor", - mcpConfigured: false, - connectionString: false, - framework: "next", - orm: "prisma", - migrationTool: "prisma", - migrationDir: "prisma/migrations", - isVscodeIde: true, - mode: "defaults", - }); - - expect(result.phase).toBe("setup"); - expect(result.status).toBe("installed"); - - const results = result.results as { id: string; status: string }[]; - expect(results.every((r) => r.status === "success")).toBe(true); - - // nextAction should chain to the getting-started CLI command - expect(result.nextAction.type).toBe("run_neon_init"); - const args = (result.nextAction as { args: string[] }).args; - expect(args[0]).toBe("getting-started"); - }); - - test("--data with customize mode and scopes executes installation directly", async () => { + test("customize mode with scopes installs MCP at project scope", async () => { const result = await handleSetupPhase({ agent: "claude", mcpConfigured: false, - connectionString: true, - framework: "remix", - orm: "drizzle", isVscodeIde: true, mode: "customize", mcpScope: "project", @@ -409,28 +280,10 @@ describe("setup phase", () => { expect(mcpCall?.[1]).not.toContain("-g"); }); - test("--data with customize mode goes straight to execution", async () => { - const result = await handleSetupPhase({ - agent: "claude", - mcpConfigured: false, - connectionString: false, - framework: "none", - orm: "none", - isVscodeIde: true, - mode: "customize", - }); - - expect(result.phase).toBe("setup"); - expect(result.status).toBe("installed"); - }); - test("execute flag triggers installation directly", async () => { const result = await handleSetupPhase({ agent: "claude", mcpConfigured: false, - connectionString: true, - framework: "remix", - orm: "drizzle", isVscodeIde: false, mcpScope: "project", skillsScope: "project", @@ -455,9 +308,6 @@ describe("setup phase", () => { const result = await handleSetupPhase({ agent: "claude", mcpConfigured: false, - connectionString: false, - framework: "none", - orm: "none", isVscodeIde: false, mode: "defaults", }); @@ -476,9 +326,7 @@ describe("setup phase", () => { "failed", ); - // Should still proceed to getting-started even with partial failure - expect(result.nextAction.type).toBe("run_neon_init"); - const args = (result.nextAction as { args: string[] }).args; - expect(args[0]).toBe("getting-started"); + // Even on partial failure, hand off to the agent. + expect(result.nextAction.type).toBe("complete"); }); }); diff --git a/packages/cli/src/init/phases/setup.ts b/packages/cli/src/init/phases/setup.ts index a63f9554..def3e45b 100644 --- a/packages/cli/src/init/phases/setup.ts +++ b/packages/cli/src/init/phases/setup.ts @@ -1,21 +1,13 @@ -import { writeFileSync } from "node:fs"; import { unlink } from "node:fs/promises"; -import { resolve } from "node:path"; import { execa } from "execa"; import { resolveAddMcpAgentId } from "../agents.js"; -import { - FALLBACK_TEMPLATES, - fetchTemplates, - findTemplate, - type NeonFeature, - scaffoldTemplate, -} from "../bootstrap.js"; import { detectIde, isCursorInstalled, isVSCodeInstalled, } from "../detect_agent.js"; import { findEditorCommand } from "../extension.js"; +import { handoffAction } from "../handoff.js"; import { inspectProject } from "../inspect.js"; import { ensureNeonctl } from "../neonctl.js"; import { ensureSkillsUpToDate } from "../skills.js"; @@ -26,25 +18,9 @@ export type SetupPhaseOptions = { agent?: string; /** The IDE/editor the user is running in (e.g. "cursor", "vscode") — reported by agent */ ide?: string; - /** Enable preview skills (neon-object-storage, neon-functions, neon-ai-gateway) */ - preview?: boolean; - /** Whether the directory already contains an application */ - hasApp?: boolean; - /** Template ID to scaffold (when bootstrapping a new project) */ - template?: string; - /** Neon features required by the selected template */ - templateRequires?: NeonFeature[]; - /** Neon features selected by the user (brownfield flows) */ - features?: NeonFeature[]; // Inspection results — pre-filled by orchestrator or reported by agent mcpConfigured?: boolean | null; skillsInstalled?: boolean | null; - connectionString?: boolean | null; - connectionParams?: string; // JSON with host/dbname/etc if found - framework?: string; - orm?: string; - migrationTool?: string; - migrationDir?: string; isVscodeIde?: boolean | null; // User preferences (also used for pre-detected scope from inspection) mode?: string; @@ -56,48 +32,25 @@ export type SetupPhaseOptions = { }; /** - * Comprehensive setup phase: inspects repo state, collects user preferences, - * then batches all installation commands together. + * Setup phase: inspects tooling state, collects install preferences, then + * batches the MCP server / skills / extension installs together. * - * With --data JSON, the agent sends inspection results AND user preferences - * in a single call, so the CLI can go straight to installation. + * With --data JSON, the agent sends inspection results AND user preferences in + * a single call, so the CLI can go straight to installation. Once install + * finishes, `neon init` hands off to the agent (see {@link handoffAction}) — it + * does not connect a database or configure features. */ export async function handleSetupPhase( options: SetupPhaseOptions, ): Promise { - // Parse features from comma-separated string (e.g. "database,auth" from agent --data) - if (typeof options.features === "string") { - options.features = (options.features as unknown as string) - .split(",") - .map((f) => f.trim()) as NeonFeature[]; - } - - // Treat "none" as no template selected - const templateWasAnswered = options.template !== undefined; - if (options.template === "none") { - options.template = undefined; - } - - // Resolve template requirements if a template was selected but requires not yet populated - if (options.template && !options.templateRequires) { - const templates = await fetchTemplates(); - const selected = templates.find((t) => t.id === options.template); - if (selected) { - options.templateRequires = selected.requires; - } - } - // --execute: run the batched installation (legacy path) if (options.execute) { return executeBatchedInstallation(await mergeCliInspection(options)); } // Treat any explicit mode value that isn't "customize"/"custom" as defaults. - // Also treat as defaults when mode is missing but agent reported back - // (template was answered, nothing left to customize). - const hasReportedBack = options.mode || templateWasAnswered; if ( - hasReportedBack && + options.mode && options.mode !== "customize" && options.mode !== "custom" ) { @@ -123,49 +76,13 @@ export async function handleSetupPhase( }); } - // Default: send inspection checks with user preferences (all in one response) + // Default: send inspection checks with install preferences (all in one response) return buildBulkInspection(options); } -function buildTemplatePreference( - templates: { - id: string; - title: string; - description: string; - tools?: string[]; - }[], -) { - return [ - { - id: "template", - question: - "No application was detected in this directory. Would you like to scaffold a new project from a template?", - phase: "before_checks" as const, - options: [ - ...templates.map((t) => { - const tools = - t.tools && t.tools.length > 0 - ? ` (${t.tools.join(", ")})` - : ""; - return { - value: t.id, - label: `${t.title}${tools} — ${t.description}`, - }; - }), - { - value: "none", - label: "No thanks — continue without scaffolding", - }, - ], - default: "none", - }, - ]; -} - async function buildBulkInspection( options: SetupPhaseOptions, ): Promise { - const hasApp = options.hasApp !== false; const detectedIde = detectIde(); // If no IDE detected (e.g. standalone terminal), check what's installed @@ -175,20 +92,16 @@ async function buildBulkInspection( if (isVSCodeInstalled()) installedEditors.push("vscode"); } - // Fetch available templates when no app is detected - let templatePreferences: ReturnType = []; - if (!hasApp) { - let templates = FALLBACK_TEMPLATES; - try { - const fetched = await fetchTemplates(); - if (fetched && fetched.length > 0) templates = fetched; - } catch {} - templatePreferences = buildTemplatePreference(templates); - } + const isPartialSkills = String(options.skillsScope ?? "").includes( + "partial", + ); + const needsMcpChoice = !options.mcpConfigured; + const needsSkillsChoice = !options.skillsInstalled && !isPartialSkills; + const hasModeQuestion = needsMcpChoice || needsSkillsChoice; return { phase: "setup", - status: hasApp ? "pending" : "bootstrap_needed", + status: "pending", detectedIde: detectedIde?.toLowerCase() ?? null, installedEditors: installedEditors.length > 0 ? installedEditors : null, // Pre-detected state from filesystem inspection @@ -199,13 +112,11 @@ async function buildBulkInspection( nextAction: { type: "agent_check", instructions: [ - "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.", + "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.", "", - hasApp - ? "Perform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the `condition` field — only show a question if its condition is met." - : "No application was detected in this directory. Ask the user if they'd like to scaffold a new project from a template (the `template` preference). Present ALL template options and the 'No thanks' option — do NOT auto-select even if there is only one template. If the user selects a template, the scaffolded template includes agent skills so skills installation will be skipped. If the user chooses 'none', continue with the remaining setup preferences normally. Then perform the agent checks and present the remaining preferences ONE AT A TIME.", + "Perform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the `condition` field — only show a question if its condition is met.", "", - `The CLI has pre-detected the following from the filesystem: MCP server: ${options.mcpConfigured ? `configured (${options.mcpScope})` : "not configured"}. Agent skills: ${options.skillsInstalled ? `installed (${options.skillsScope})` : String(options.skillsScope ?? "").includes("partial") ? `partially installed (${options.skillsScope}) — missing skills will be auto-installed to the same scope` : "not installed"}. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.`, + `The CLI has pre-detected the following from the filesystem: MCP server: ${options.mcpConfigured ? `configured (${options.mcpScope})` : "not configured"}. Agent skills: ${options.skillsInstalled ? `installed (${options.skillsScope})` : isPartialSkills ? `partially installed (${options.skillsScope}) — missing skills will be auto-installed to the same scope` : "not installed"}. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.`, "", "IMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.", "", @@ -258,70 +169,34 @@ async function buildBulkInspection( : []), ], userPreferences: [ - ...templatePreferences, - // For brownfield flows, ask which Neon features to enable - ...(hasApp + // Only show defaults/customize when there's something to customize: + // MCP not configured, or skills need a scope choice. + ...(hasModeQuestion ? [ { - id: "features", - question: - "Which Neon features would you like to enable for this project?", + id: "mode", + question: "Use default settings or customize?", phase: "after_checks" as const, options: [ { - value: "database", - label: "Database (always included)", + value: "defaults", + label: "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)", }, { - value: "database,auth", - label: "Database + Neon Auth (adds authentication via Neon)", + value: "customize", + label: "Customize installation settings", }, ], - default: "database", - context: - "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon.", + default: "defaults", }, ] : []), - // Only show defaults/customize when there's something to customize: - // MCP not configured, skills need scope choice, or extension not detected. - ...(() => { - const isPartialSkills = String( - options.skillsScope ?? "", - ).includes("partial"); - const needsMcpChoice = !options.mcpConfigured; - const needsSkillsChoice = - !options.skillsInstalled && !isPartialSkills; - const hasCustomizableOptions = - needsMcpChoice || needsSkillsChoice; - if (!hasCustomizableOptions) return []; - return [ - { - id: "mode", - question: "Use default settings or customize?", - phase: "after_checks" as const, - options: [ - { - value: "defaults", - label: hasApp - ? "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" - : "Use defaults (Neon CLI, MCP: global — skills included in template)", - }, - { - value: "customize", - label: "Customize installation settings", - }, - ], - default: "defaults", - }, - ]; - })(), { id: "mcpScope", question: "Where should the Neon MCP server be configured?", context: "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - phase: "after_checks", + phase: "after_checks" as const, options: [ { value: "global", @@ -342,8 +217,7 @@ async function buildBulkInspection( }, // Show skills scope when skills aren't detected and no partial install exists. // Partial installations are auto-completed to the same scope silently. - ...(!options.skillsInstalled && - !String(options.skillsScope ?? "").includes("partial") + ...(needsSkillsChoice ? [ { id: "skillsScope", @@ -375,7 +249,7 @@ async function buildBulkInspection( id: "installExtension", question: "Install the Neon editor extension for local database browsing?", - phase: "after_checks", + phase: "after_checks" as const, options: [ { value: "true", label: "Yes" }, { value: "false", label: "No" }, @@ -394,37 +268,22 @@ async function buildBulkInspection( "--json", "--data", (() => { - const partialScope = String( - options.skillsScope ?? "", - ).replace("-partial", ""); - const hasPartial = String( - options.skillsScope ?? "", - ).includes("partial"); - const previewFlag = options.preview - ? ", preview: true" - : ""; - const needsMcpChoice = !options.mcpConfigured; - const needsSkillsChoice = - !options.skillsInstalled && !hasPartial; - const hasModeQuestion = - needsMcpChoice || needsSkillsChoice; + const prefilledSkills = + options.skillsInstalled || isPartialSkills + ? `, skillsScope: "${options.skillsInstalled ? options.skillsScope || "project" : String(options.skillsScope ?? "").replace("-partial", "")}"` + : needsSkillsChoice + ? ", skillsScope?: 'global'|'project'" + : ""; const modeField = hasModeQuestion ? ", mode: string" : ""; const mcpField = hasModeQuestion ? ", mcpScope?: 'global'|'project'|'none'" : ""; - const skillsField = needsSkillsChoice - ? ", skillsScope?: string" - : ""; const extField = hasModeQuestion ? ", installExtension?: bool" : ""; - const prefilledSkills = - options.skillsInstalled || hasPartial - ? `, skillsScope: "${options.skillsInstalled ? options.skillsScope || "project" : partialScope}"` - : skillsField; - return ``; + return ``; })(), ], }, @@ -446,7 +305,7 @@ type InstallResult = { /** * Executes the batched installation of MCP server, skills, and extension. * Runs commands directly in the CLI process — the agent does NOT run these. - * Returns results and chains to the getting-started phase. + * Once install finishes, hands off to the agent (see {@link handoffAction}). */ async function executeBatchedInstallation( options: SetupPhaseOptions, @@ -457,45 +316,6 @@ async function executeBatchedInstallation( const installExt = options.installExtension === true; const results: InstallResult[] = []; - const isBootstrap = !!options.template; - - // Step 0: Bootstrap project from template if specified - if (isBootstrap && options.template) { - try { - const templates = await fetchTemplates(); - const template = - findTemplate(templates, options.template) ?? - findTemplate(FALLBACK_TEMPLATES, options.template); - if (!template) { - throw new Error(`Unknown template "${options.template}".`); - } - await scaffoldTemplate(template, "."); - results.push({ - id: "bootstrap", - description: `Scaffolded project from template "${options.template}"`, - status: "success", - }); - - // Write template features to .neon under _init (ephemeral, cleaned up when init completes) - if (options.templateRequires) { - const neonContextPath = resolve(process.cwd(), ".neon"); - const context: Record = { - _init: { features: options.templateRequires }, - }; - writeFileSync( - neonContextPath, - `${JSON.stringify(context, null, 2)}\n`, - ); - } - } catch (err) { - results.push({ - id: "bootstrap", - description: `Failed to scaffold project from template "${options.template}"`, - status: "failed", - error: err instanceof Error ? err.message : "Unknown error", - }); - } - } // Step 1: Ensure the Neon CLI is installed and up to date const neonctlResult = await ensureNeonctl(); @@ -603,45 +423,33 @@ async function executeBatchedInstallation( } } - // Step 3: Install/update skills (skip when bootstrapping — templates bundle skills) - if (isBootstrap) { + // Step 3: Install/update skills + const skillsScope = (options.skillsScope ?? "project") as + | "global" + | "project"; + const skillsOk = await ensureSkillsUpToDate(agentId, skillsScope); + if (skillsOk) { results.push({ id: "install_skills", - description: "Neon agent skills included in template", + description: "Neon agent skills installed", status: "success", }); } else { - const skillsScope = (options.skillsScope ?? "project") as - | "global" - | "project"; - const skillsOk = await ensureSkillsUpToDate( - agentId, - skillsScope, - options.preview, + // Build the install commands for the agent to run directly + // (sandboxed environments may block child process writes) + const { getSkillList } = await import("../skills.js"); + const skillList = getSkillList(); + const cmds = skillList.map( + (s) => + `skills add neondatabase/agent-skills --skill ${s} --agent ${agentId}${skillsScope === "global" ? " -g" : ""} -y`, ); - if (skillsOk) { - results.push({ - id: "install_skills", - description: "Neon agent skills installed", - status: "success", - }); - } else { - // Build the install commands for the agent to run directly - // (sandboxed environments may block child process writes) - const { getSkillList } = await import("../skills.js"); - const skillList = getSkillList(options.preview); - const cmds = skillList.map( - (s) => - `skills add neondatabase/agent-skills --skill ${s} --agent ${agentId}${skillsScope === "global" ? " -g" : ""} -y`, - ); - results.push({ - id: "install_skills", - description: - "Failed to install Neon agent skills automatically. Run these commands to install manually:", - status: "failed", - commands: cmds, - }); - } + results.push({ + id: "install_skills", + description: + "Failed to install Neon agent skills automatically. Run these commands to install manually:", + status: "failed", + commands: cmds, + }); } // Step 4: Install editor extension if requested @@ -652,75 +460,32 @@ async function executeBatchedInstallation( results.push(extResult); } - // Step 5: Write selected features to .neon under _init for brownfield flows - // (Bootstrap flows already wrote _init in step 0) - if (!isBootstrap && options.features && options.features.length > 0) { - const neonContextPath = resolve(process.cwd(), ".neon"); - const context: Record = { - _init: { features: options.features }, - }; - writeFileSync(neonContextPath, `${JSON.stringify(context, null, 2)}\n`); - } - const allSucceeded = results.every((r) => r.status === "success"); - // Build args to chain to the getting-started phase as a separate CLI call. - // This ensures the agent gets a clean response with ONLY the getting-started - // action — no competing "results" array to distract it. - const gettingStartedData: Record = {}; - if (options.connectionString) gettingStartedData.hasConnectionString = true; - if (options.framework) gettingStartedData.framework = options.framework; - if (options.orm) gettingStartedData.orm = options.orm; - if (options.migrationTool) - gettingStartedData.migrationTool = options.migrationTool; - if (options.migrationDir) - gettingStartedData.migrationDir = options.migrationDir; - // Pass features so getting-started knows which phases to chain to - const resolvedFeatures = options.templateRequires ?? options.features; - if (resolvedFeatures && resolvedFeatures.length > 0) - gettingStartedData.features = resolvedFeatures; - // Bootstrap implies preview mode (new project in us-east required) - if (isBootstrap) gettingStartedData.preview = true; - const gettingStartedArgs = [ - "getting-started", - "--json", - "--data", - JSON.stringify(gettingStartedData), - ]; - return { phase: "setup", status: allSucceeded ? "installed" : "partial", results, - nextAction: { - type: "run_neon_init", - args: gettingStartedArgs, - }, + nextAction: handoffAction(), }; } /** - * Fills in missing filesystem inspection fields by running inspectProject(). + * Fills in the IDE-related inspection fields when the agent didn't report them. * Agent-reported data (mcpConfigured, agent, mode, scopes) is preserved. - * CLI-detectable fields (framework, orm, migrations, connectionString, isVscodeIde) - * are filled in only if not already present. */ async function mergeCliInspection( options: SetupPhaseOptions, ): Promise { - // If the agent already provided these, no need to re-inspect - if (options.framework !== undefined && options.orm !== undefined) { + // If the agent already told us the IDE, no need to re-inspect. + if (options.ide !== undefined && options.isVscodeIde !== undefined) { return options; } const inspection = await inspectProject([ - { id: "connection_string", description: "", lookFor: [] }, - { id: "project_stack", description: "", lookFor: [] }, - { id: "migrations", description: "", lookFor: [] }, { id: "ide_type", description: "", lookFor: [] }, ]); - // Also detect IDE if not already reported by the agent const ide = options.ide?.toLowerCase().replace(/\s+/g, "-") || detectIde()?.toLowerCase().replace(/\s+/g, "-") || @@ -729,18 +494,6 @@ async function mergeCliInspection( return { ...options, ide, - connectionString: - options.connectionString ?? - (inspection.connectionString as boolean | undefined), - framework: - options.framework ?? (inspection.framework as string | undefined), - orm: options.orm ?? (inspection.orm as string | undefined), - migrationTool: - options.migrationTool ?? - (inspection.migrationTool as string | undefined), - migrationDir: - options.migrationDir ?? - (inspection.migrationDir as string | undefined), isVscodeIde: options.isVscodeIde ?? (inspection.isVscodeIde as boolean | undefined), diff --git a/packages/cli/src/init/phases/status.ts b/packages/cli/src/init/phases/status.ts deleted file mode 100644 index 9c537e60..00000000 --- a/packages/cli/src/init/phases/status.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { isAuthenticated } from "../auth.js"; -import { inspectProject } from "../inspect.js"; -import type { StatusResponse } from "../types.js"; - -export type StatusOptions = { - agent?: string; -}; - -export async function handleStatusPhase( - _options: StatusOptions, -): Promise { - const authed = await isAuthenticated(); - - const inspection = await inspectProject([ - { id: "mcp_server", description: "", lookFor: [] }, - { id: "database_url", description: "", lookFor: [] }, - { id: "skills", description: "", lookFor: [] }, - { id: "migrations", description: "", lookFor: [] }, - ]); - - const hasDatabaseUrl = inspection.databaseUrl === true; - const mcpConfigured = inspection.mcpConfigured === true; - const skillsInstalled = inspection.skillsInstalled === true; - const migrationTool = (inspection.migrationTool as string | null) ?? null; - const hasMigrations = - migrationTool !== null && - migrationTool !== "none" && - inspection.migrationDir !== "none"; - - const recommendations: StatusResponse["recommendations"] = []; - - if (!authed) { - recommendations.push({ - priority: "high", - message: "Not authenticated with Neon", - command: `neon init --agent --data '{"step":"auth"}'`, - }); - } - - if (!hasDatabaseUrl) { - recommendations.push({ - priority: "high", - message: "No DATABASE_URL found in .env", - command: `neon init --agent --data '{"step":"db"}'`, - }); - } - - if (!skillsInstalled) { - recommendations.push({ - priority: "medium", - message: "Neon agent skills not detected in this project", - command: `neon init --agent --data '{"step":"skills","install":true}'`, - }); - } - - if (migrationTool && !hasMigrations) { - recommendations.push({ - priority: "medium", - message: `${migrationTool} detected but no migrations found`, - command: `neon init --agent --data '{"step":"migrations"}'`, - }); - } - - return { - auth: { - authenticated: authed, - }, - tooling: { - mcpServer: { - configured: mcpConfigured, - scope: inspection.mcpScope || null, - }, - skills: { - installed: skillsInstalled, - scope: inspection.skillsScope || null, - }, - }, - project: { - databaseUrl: hasDatabaseUrl, - }, - migrations: { - tool: migrationTool, - hasMigrations, - }, - recommendations, - }; -} diff --git a/packages/cli/src/init/resolve_context.ts b/packages/cli/src/init/resolve_context.ts deleted file mode 100644 index 425d14b8..00000000 --- a/packages/cli/src/init/resolve_context.ts +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Resolves the Neon org/project from a DATABASE_URL in .env. - * Parses the endpoint hostname, then queries the Neon API to find - * which project it belongs to. - */ -import { existsSync, readFileSync } from "node:fs"; -import { resolve } from "node:path"; -import { execa } from "execa"; - -type NeonContext = { - orgId: string; - projectId: string; -}; - -/** - * Extracts the Neon endpoint host from .env's DATABASE_URL or PGHOST. - * Returns the hostname (e.g. "ep-cool-name-12345.us-east-2.aws.neon.tech") or null. - */ -function extractNeonHost(cwd: string): string | null { - for (const envFile of [".env", ".env.local"]) { - const envPath = resolve(cwd, envFile); - if (!existsSync(envPath)) continue; - try { - const content = readFileSync(envPath, "utf-8"); - - // Try DATABASE_URL - const dbUrlMatch = content.match( - /^DATABASE_URL=.*@([^/:\s]+\.neon\.tech)/m, - ); - if (dbUrlMatch) return dbUrlMatch[1]; - - // Try PGHOST - const pgHostMatch = content.match(/^PGHOST=([^\s]+\.neon\.tech)/m); - if (pgHostMatch) return pgHostMatch[1]; - } catch {} - } - return null; -} - -/** - * Resolves the Neon org and project that own the endpoint in DATABASE_URL. - * - * Strategy: - * 1. Extract endpoint hostname from .env - * 2. List orgs via neonctl - * 3. For each org, list projects - * 4. For each project, get connection string and compare hostnames - * 5. Return the matching org/project - */ -export async function resolveNeonContext( - cwd: string, -): Promise { - const neonHost = extractNeonHost(cwd); - if (!neonHost) return null; - - // List orgs - let orgs: { id: string; name: string }[]; - try { - const result = await execa( - "npx", - ["-y", "neonctl", "orgs", "list", "--output", "json"], - { - stdio: "pipe", - timeout: 30000, - env: { ...process.env, CI: undefined }, - }, - ); - orgs = JSON.parse(result.stdout); - } catch { - return null; - } - - if (orgs.length === 0) return null; - - // Search each org's projects for a matching endpoint - for (const org of orgs) { - let projects: { id: string; name: string }[]; - try { - const result = await execa( - "npx", - [ - "-y", - "neonctl", - "projects", - "list", - "--org-id", - org.id, - "--output", - "json", - ], - { - stdio: "pipe", - timeout: 30000, - env: { ...process.env, CI: undefined }, - }, - ); - projects = JSON.parse(result.stdout); - } catch { - continue; - } - - for (const project of projects) { - try { - const result = await execa( - "npx", - [ - "-y", - "neonctl", - "connection-string", - "--project-id", - project.id, - ], - { - stdio: "pipe", - timeout: 15000, - env: { ...process.env, CI: undefined }, - }, - ); - const connStr = result.stdout.trim(); - if (connStr.includes(neonHost)) { - return { orgId: org.id, projectId: project.id }; - } - } catch {} - } - } - - return null; -} diff --git a/packages/cli/src/init/route_command.test.ts b/packages/cli/src/init/route_command.test.ts index 879b2279..f4c37c3a 100644 --- a/packages/cli/src/init/route_command.test.ts +++ b/packages/cli/src/init/route_command.test.ts @@ -26,44 +26,41 @@ describe("routeDataStep", () => { expect(result.phase).toBe("auth"); }); - test("routes to getting-started via step field", async () => { + test("routes to setup via step field", async () => { + const result = (await routeDataStep({ step: "setup" }, undefined)) as { + phase: string; + status: string; + }; + expect(result.phase).toBe("setup"); + expect(result.status).toBe("pending"); + }); + + test("routes to mcp via step field", async () => { const result = (await routeDataStep( - { step: "getting-started", framework: "next" }, + { step: "mcp", status: true }, undefined, - )) as { phase: string; status: string }; - expect(result.phase).toBe("setup"); - expect(result.status).toBe("getting_started"); + )) as { phase: string }; + expect(result.phase).toBe("tooling"); }); - test("routes to migrations via step field", async () => { + test("routes to skills via step field", async () => { const result = (await routeDataStep( - { step: "migrations", tool: "prisma" }, + { step: "skills", status: true }, undefined, )) as { phase: string }; - expect(result.phase).toBe("migrations"); + expect(result.phase).toBe("tooling"); }); test("unwraps a nested data payload", async () => { const result = (await routeDataStep( { - step: "getting-started", - data: JSON.stringify({ - framework: "next", - migrationTool: "prisma", - }), + step: "skills", + data: JSON.stringify({ status: true }), }, undefined, - )) as { - phase: string; - status: string; - nextAction: { type: string; steps?: { id: string }[] }; - }; - expect(result.phase).toBe("setup"); - expect(result.status).toBe("getting_started"); - if (result.nextAction.type === "agent_action") { - const stepIds = result.nextAction.steps?.map((s) => s.id); - expect(stepIds).toContain("run_migrations"); - } + )) as { phase: string; nextAction: { type: string } }; + expect(result.phase).toBe("tooling"); + expect(result.nextAction.type).toBe("agent_check"); }); test("prefers agentId from the payload over the caller's agent", async () => { diff --git a/packages/cli/src/init/route_command.ts b/packages/cli/src/init/route_command.ts index a26efb40..42547784 100644 --- a/packages/cli/src/init/route_command.ts +++ b/packages/cli/src/init/route_command.ts @@ -3,7 +3,7 @@ * shelling out. This is the whole agent-facing surface of `neon init`: * * neon init --agent --data '{"step":"auth"}' - * neon init --agent --data '{"step":"db","projectId":"xyz"}' + * neon init --agent --data '{"step":"setup","mode":"defaults"}' */ export async function routeDataStep( data: Record, @@ -43,14 +43,6 @@ export async function routeDataStep( } as Parameters[0]); } - case "db": { - const { handleDbPhase } = await import("./phases/db.js"); - return handleDbPhase({ - agent: resolvedAgent, - ...rest, - } as Parameters[0]); - } - case "setup": { const { handleSetupPhase } = await import("./phases/setup.js"); return handleSetupPhase({ @@ -59,19 +51,6 @@ export async function routeDataStep( } as Parameters[0]); } - case "getting-started": { - const { handleGettingStartedPhase } = await import( - "./phases/getting_started.js" - ); - return handleGettingStartedPhase({ - agent: resolvedAgent, - ...rest, - // After the spread: the directory we install into is where the - // CLI is running, never something the agent's payload picks. - cwd: process.cwd(), - } as Parameters[0]); - } - case "mcp": { const { handleMcpPhase } = await import("./phases/mcp.js"); return handleMcpPhase({ @@ -88,40 +67,9 @@ export async function routeDataStep( } as Parameters[0]); } - case "migrations": { - const { handleMigrationsPhase } = await import( - "./phases/migrations.js" - ); - return handleMigrationsPhase({ - agent: resolvedAgent, - ...rest, - cwd: process.cwd(), - } as Parameters[0]); - } - - case "neon-auth": { - const { handleNeonAuthPhase } = await import( - "./phases/neon_auth.js" - ); - return handleNeonAuthPhase({ - agent: resolvedAgent, - ...rest, - } as Parameters[0]); - } - - case "status": { - const { handleStatusPhase } = await import("./phases/status.js"); - return handleStatusPhase({ agent: resolvedAgent }); - } - - case "finalize": { - const { handleCleanup } = await import("./phases/cleanup.js"); - return handleCleanup(); - } - default: throw new Error( - `Unknown step: "${step}". Valid steps: auth, db, setup, getting-started, mcp, skills, migrations, neon-auth, status, finalize`, + `Unknown step: "${step}". Valid steps: auth, setup, mcp, skills`, ); } } diff --git a/packages/cli/src/init/skills.ts b/packages/cli/src/init/skills.ts index f32cfd3b..e42c6434 100644 --- a/packages/cli/src/init/skills.ts +++ b/packages/cli/src/init/skills.ts @@ -39,19 +39,12 @@ async function ensureSkillsCli(): Promise { } } -/** Base skills installed for all invocations */ +/** Skills installed by `neon init`. */ const BASE_SKILLS = ["neon", "neon-postgres"]; -/** Additional skills installed for preview (non-bootstrap) invocations */ -const PREVIEW_SKILLS = [ - "neon-object-storage", - "neon-functions", - "neon-ai-gateway", -]; - -/** Returns the skill list based on whether preview mode is active */ -export function getSkillList(preview?: boolean): string[] { - return preview ? [...BASE_SKILLS, ...PREVIEW_SKILLS] : BASE_SKILLS; +/** Returns the list of Neon agent skills to install. */ +export function getSkillList(): string[] { + return BASE_SKILLS; } const SKILL_BASE_URL = @@ -107,7 +100,6 @@ function editorToSkillsAgent(editor: Editor): string { export type InstallSkillsOptions = { json?: boolean; scope?: "global" | "project"; - preview?: boolean; }; /** @@ -133,7 +125,7 @@ export async function installAgentSkills( let anyFailed = false; await ensureSkillsCli(); - const skills = getSkillList(options?.preview); + const skills = getSkillList(); for (const editor of editorsWithSkills) { const agentName = editorToSkillsAgent(editor); @@ -266,10 +258,9 @@ function skillsAreFresh(agent: string, requiredSkills: string[]): boolean { export async function ensureSkillsUpToDate( agent: string | undefined, scope?: "global" | "project", - preview?: boolean, ): Promise { const resolvedAgent = agent || "cursor"; - const skills = getSkillList(preview); + const skills = getSkillList(); if (skillsAreFresh(resolvedAgent, skills)) return true; await ensureSkillsCli(); diff --git a/packages/cli/src/init/types.ts b/packages/cli/src/init/types.ts index 783a0d55..bd93277b 100644 --- a/packages/cli/src/init/types.ts +++ b/packages/cli/src/init/types.ts @@ -18,13 +18,7 @@ export type Editor = // Agent-driven state machine protocol // --------------------------------------------------------------------------- -export type Phase = - | "auth" - | "tooling" - | "setup" - | "db" - | "neon_auth" - | "migrations"; +export type Phase = "auth" | "tooling" | "setup"; // -- NextAction discriminated union ---------------------------------------- @@ -34,7 +28,6 @@ export type NextAction = | RunNeonInitAction | AgentCheckAction | AgentActionAction - | RestartRequiredAction | CompleteAction; export type AskUserAction = { @@ -110,12 +103,6 @@ export type AgentActionAction = { onComplete: RunNeonInitAction | CompleteAction; }; -export type RestartRequiredAction = { - type: "restart_required"; - reason: string; - resumeCommand: string; -}; - export type CompleteAction = { type: "complete"; message: string; @@ -129,29 +116,3 @@ export type PhaseResponse = { nextAction: NextAction; [key: string]: unknown; }; - -// -- Status response (read-only, no nextAction) ---------------------------- - -export type StatusResponse = { - auth: { - authenticated: boolean; - user?: string; - }; - tooling: { - mcpServer: { - configured: boolean | null; - scope?: string | null; - location?: string; - }; - skills: { installed: boolean; scope?: string | null }; - extension?: { installed: boolean; editor: string } | null; - }; - project: { - databaseUrl: boolean; - }; - migrations: { - tool: string | null; - hasMigrations: boolean; - }; - recommendations: { priority: string; message: string; command: string }[]; -}; From 97054a26f3bacb15f4557d9020acb1765d07c130 Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Tue, 11 Aug 2026 15:33:52 -0600 Subject: [PATCH 03/13] Update init e2e test for the minimal init flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `neon init` no longer emits the getting-started org/project/env sequence, so the e2e that drove `{step:"getting-started"}` (and asserted the emitted `orgs list` / `projects create` / `env pull` commands) no longer applies — it was hitting the "Unknown step" error and exiting 1. Replace it with a credential-free check that the `setup` step still answers with an `agent_check` and installs nothing into the working directory just by being asked, and keep the unknown-step guard. Co-authored-by: Isaac --- packages/cli/e2e/init.e2e.test.ts | 170 ++++-------------------------- 1 file changed, 22 insertions(+), 148 deletions(-) diff --git a/packages/cli/e2e/init.e2e.test.ts b/packages/cli/e2e/init.e2e.test.ts index b9c50782..c45b8a58 100644 --- a/packages/cli/e2e/init.e2e.test.ts +++ b/packages/cli/e2e/init.e2e.test.ts @@ -2,47 +2,29 @@ import { existsSync, mkdirSync, mkdtempSync, - readFileSync, rmSync, writeFileSync, } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { configuredOrgId } from "@neon/e2e-harness"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { - createProject, - deleteProject, - runCli, - uniqueProjectName, -} from "./helpers.js"; +import { runCli } from "./helpers.js"; /** * `neon init --agent` is a protocol rather than a command that does the work: it answers with - * the shell commands an agent should run on the user's behalf. The snapshot tests pin those - * strings exactly; what no test checks is that the commands still exist and still work. That - * is the regression this covers — init handing an agent an invocation the CLI no longer - * accepts, which the agent then reports as a failed setup. - * - * The phase under test is `getting-started`, because that is the one a real run reaches: a - * directory with no Neon connection routes there (`init/orchestrate.ts`), and it is where the - * org, project and env commands are emitted. + * what an agent should do next. `neon init` now stops at installing the Neon tooling (MCP + * server, agent skills, editor extension) and hands the rest off to the agent — it no longer + * selects an org/project, links, or pulls env. The snapshot tests pin the emitted strings + * exactly; this suite guards the two things a live run must still get right: the CLI accepts + * the steps it hands out, and merely *asking* what to do installs nothing into the project. */ -/** The exact prefix the protocol emits. Substituted for the binary under test — and asserted, because a silent change here would make every substitution below a no-op. */ -const EMITTED_PREFIX = "CI= npx -y neon "; - /** * Every variable `detectAgent` and `detectIde` read (`init/detect_agent.ts`). Clearing them - * keeps the run agentless, which matters for more than determinism: given an agent id, the - * phase calls `ensureSkillsUpToDate`, which fetches the skills CLI and installs skills — into - * the home directory, and into the working directory it is invoked from. - * - * This has to be applied to the phase invocation itself, not only to the commands it emits. - * It was not, once, and the run wrote `.agents/skills/` and `skills-lock.json` into - * `packages/cli`. Hence also the temp working directory below, and the assertion that nothing - * was installed into it: a scrub that silently stops working should fail this test rather - * than reconfigure the machine. + * keeps the run agentless — which matters for determinism, and because with an agent id the + * setup execution path would fetch the skills CLI and install skills into the home and working + * directories. The default `setup` step under test never executes installs, but the scrub + * keeps that guarantee honest if that ever changes. * * The value scan at `detect_agent.ts:48` only runs inside the VS Code branch, which these * variables gate, so clearing them is sufficient. @@ -64,155 +46,47 @@ const NO_AGENT_ENV: Record = { VSCODE_CWD: undefined, }; -type AgentStep = { id: string; description: string; command?: string }; - type PhaseResponse = { phase: string; status: string; nextAction?: { type: string; - steps?: AgentStep[]; - onComplete?: { type: string }; [key: string]: unknown; }; }; -describe.sequential("e2e — neon init emits commands that work", () => { - let projectId: string; - /** Projects the emitted `projects create` produced, removed in teardown. */ - const created: string[] = []; - const orgId = configuredOrgId(); - +describe.sequential("e2e — neon init emits a protocol the CLI still accepts", () => { /** - * Everything runs here rather than in the checkout: `env pull` writes a connection string - * and password into it, and the phase would install skills here if the scrub above ever - * stopped working. Removed in teardown, credentials included. + * A throwaway working directory: the phase must not write anything here just for being + * asked what to do. Removed in teardown. */ const workdir = mkdtempSync(join(tmpdir(), "neon-init-e2e-")); const contextFile = join(workdir, ".neon"); - beforeAll(async () => { - // Make the workdir a pnpm project: the emitted install steps must follow - // it rather than the npm this suite used to assert, and the `.git` marker - // stops the lockfile walk from climbing into $TMPDIR's ancestors. + beforeAll(() => { + // Make the workdir a project root so inspection doesn't climb into $TMPDIR's ancestors. mkdirSync(join(workdir, ".git")); writeFileSync(join(workdir, "pnpm-lock.yaml"), ""); - - projectId = await createProject({ - name: uniqueProjectName("cli-init"), - }); - writeFileSync(contextFile, `${JSON.stringify({ orgId, projectId })}\n`); }); - afterAll(async () => { + afterAll(() => { rmSync(workdir, { recursive: true, force: true }); - for (const id of [...created, projectId]) { - if (id) await deleteProject(id); - } }); - it("hands an agent a working org, project and env sequence", async () => { - if (!orgId) { - throw new Error( - "NEON_ORG_ID is required: the emitted commands take an --org-id, and substituting one is the point of this test.", - ); - } - + it("answers the setup step with an agent_check and installs nothing by asking", async () => { const phase = await runCli( - [ - "init", - "--agent", - "--data", - JSON.stringify({ step: "getting-started" }), - ], + ["init", "--agent", "--data", JSON.stringify({ step: "setup" })], { env: NO_AGENT_ENV, cwd: workdir, contextFile }, ); expect(phase.code, phase.stderr).toBe(0); + const response = JSON.parse(phase.stdout) as PhaseResponse; + expect(response.phase).toBe("setup"); + expect(response.nextAction?.type).toBe("agent_check"); - // Nothing may have been installed by asking the phase what to do. + // Asking the phase what to do must not install anything into the working directory. expect(existsSync(join(workdir, ".agents"))).toBe(false); expect(existsSync(join(workdir, "skills-lock.json"))).toBe(false); - - expect(response.nextAction?.type).toBe("agent_action"); - const steps = response.nextAction?.steps ?? []; - const ids = steps.map((step) => step.id); - // The sequence a user is walked through. Pinned so a step disappearing from the - // flow fails here rather than quietly reducing what this test exercises. - expect(ids).toEqual( - expect.arrayContaining([ - "select_org", - "select_or_create_project", - "create_project_if_needed", - "pull_env", - ]), - ); - - const executed: string[] = []; - for (const step of steps) { - if (!step.command) continue; - - // Install steps belong to the user's package manager, not to us. Assert we - // addressed the workdir's pnpm rather than running an install on the machine. - if (!step.command.startsWith(EMITTED_PREFIX)) { - expect(step.command).toMatch(/^pnpm (install|add) ?/); - continue; - } - - const args = step.command - .slice(EMITTED_PREFIX.length) - .replace("", orgId) - .replace("", uniqueProjectName("init-emitted")) - .split(/\s+/); - - const result = await runCli(args, { - env: { ...NO_AGENT_ENV, CI: "" }, - cwd: workdir, - contextFile, - }); - expect(result.code, `${step.command}\n${result.stderr}`).toBe(0); - executed.push(step.id); - - if (step.id === "select_org") { - const orgs = JSON.parse(result.stdout) as { id: string }[]; - expect(orgs.map((org) => org.id)).toContain(orgId); - } - if (step.id === "select_or_create_project") { - // The phase tells the agent to filter this list, so it has to be parseable - // and it has to contain the project the agent would pick. - const projects = JSON.parse(result.stdout) as { id: string }[]; - expect(projects.map((project) => project.id)).toContain( - projectId, - ); - } - if (step.id === "create_project_if_needed") { - const { project } = JSON.parse(result.stdout) as { - project: { id: string; name: string }; - }; - created.push(project.id); - expect(project.name).toMatch(/^neon-ts-e2e-/); - } - if (step.id === "pull_env") { - // Exit 0 is not the outcome that matters: `env pull` succeeds when it - // resolves nothing. The whole sequence exists to leave a connection string - // on disk, so read the file it wrote. - const envFile = join(workdir, ".env.local"); - expect(existsSync(envFile)).toBe(true); - expect(readFileSync(envFile, "utf8")).toMatch( - /^DATABASE_URL="?postgresql:\/\/.+/m, - ); - } - } - - // Every command the flow emits for us must have run, or this test proved less than - // it claims. `pull_env` last: it is the step that produces the connection string the - // whole sequence exists to obtain. - expect(executed).toEqual([ - "select_org", - "select_or_create_project", - "create_project_if_needed", - "pull_env", - ]); }); it("reports an unknown step instead of guessing", async () => { From e3ab1d8ecc4b7f33d09ccfeef3b5706b0fca6e3a Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Fri, 14 Aug 2026 11:14:36 -0600 Subject: [PATCH 04/13] Emit the installed `neon` binary when it's on PATH, npx otherwise MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `neon init` installs the Neon CLI (ensureNeonctl), so once a `neon` binary is on PATH the flow now emits bare `neon ...` for the commands it hands to agents/users — driving the installed CLI directly instead of paying npx's resolution cost on every command. When nothing is installed yet (a first run started with `npx neon init`, before setup installs the CLI), it falls back to `npx -y neon` so the emitted command still works. Detection runs per call, at response-build time, so the prefix reflects the machine's current state. Regenerated the agent_snapshot fixtures: the hermetic run has a `neon` stub on PATH, so the emitted prefix is now `CI= neon`. Co-authored-by: Isaac --- .../__snapshots__/agent_snapshot.test.ts.snap | 6 ++--- packages/cli/src/init/neonctl.ts | 22 ++++++++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap b/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap index 1cc24d1f..c2383fe2 100644 --- a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap +++ b/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap @@ -6455,7 +6455,7 @@ exports[`neon init --agent: the signed-out auth branches > auth 1`] = ` "existing_account": { "action": { "type": "run_command", - "command": "CI= npx -y neon auth", + "command": "CI= neon auth", "description": "This will open your browser for Neon OAuth sign-in.", "timeout": 120000, "onSuccess": { @@ -6523,7 +6523,7 @@ exports[`neon init --agent: the signed-out auth branches > auth-existing-account "status": "in_progress", "nextAction": { "type": "run_command", - "command": "CI= npx -y neon auth", + "command": "CI= neon auth", "description": "This will open your browser for Neon OAuth sign-in.", "timeout": 120000, "onSuccess": { @@ -6633,7 +6633,7 @@ exports[`neon init --agent: the signed-out auth branches > the orchestrator send "existing_account": { "action": { "type": "run_command", - "command": "CI= npx -y neon auth", + "command": "CI= neon auth", "description": "This will open your browser for Neon OAuth sign-in.", "timeout": 120000, "onSuccess": { diff --git a/packages/cli/src/init/neonctl.ts b/packages/cli/src/init/neonctl.ts index 0ae98d52..7d62477c 100644 --- a/packages/cli/src/init/neonctl.ts +++ b/packages/cli/src/init/neonctl.ts @@ -1,22 +1,32 @@ import { lstatSync } from "node:fs"; import { execa } from "execa"; +import which from "which"; import { globalInstallCommand, resolveInvokingPackageManager, } from "../utils/package_manager.js"; /** - * Returns the Neon CLI command prefix: "CI= npx -y neon". + * Returns the Neon CLI command prefix the flow emits to agents/users, e.g. + * `"CI= neon"` or `"CI= npx -y neon"`. * - * The CLI reads NEON_API_HOST and NEON_OAUTH_HOST from the environment - * directly, so no extra flags are needed. The `neon` package ships both the - * `neon` and `neonctl` binaries; we surface the cleaner `neon` command in the - * examples emitted to users and agents. + * `neon init` installs the Neon CLI (see {@link ensureNeonctl}), so once a `neon` + * binary is on PATH we emit it directly — the agent drives the installed CLI + * instead of paying npx's resolution cost on every command. When nothing is + * installed yet — a first run started with `npx neon init`, before setup has + * installed the CLI — we fall back to `npx -y neon` so the emitted command still + * works. Detection happens per call (at response-build time) so the prefix + * reflects the machine's current state. + * + * The CLI reads NEON_API_HOST and NEON_OAUTH_HOST from the environment directly, + * so no extra flags are needed. The `neon` package ships both the `neon` and + * `neonctl` binaries; we surface the cleaner `neon` command. * * Usage: `${neonctlCmd()} orgs list --output json` */ export function neonctlCmd(): string { - return "CI= npx -y neon"; + const installed = which.sync("neon", { nothrow: true }); + return installed ? "CI= neon" : "CI= npx -y neon"; } type NeonctlStatus = { From 4c813a3f9b041a7282421851a75fcdb0550bfd0c Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Fri, 14 Aug 2026 11:23:39 -0600 Subject: [PATCH 05/13] Install the `neon` package, not `neonctl` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `neon init` now installs and version-checks the `neon` npm package (its binary, `npm view`, and the local-dev-symlink guard all target `neon`) instead of `neonctl`. This lines up with the command prefix the flow emits (`neon`), so the CLI it installs is the one the agent is told to run. Updated the agent_snapshot PATH stub to version `neon` (rather than `neonctl`) and regenerated the fixtures — the only change is the recorded subprocesses (`neon --version` / `npm view neon version`). Co-authored-by: Isaac --- .../__snapshots__/agent_snapshot.test.ts.snap | 64 +++++++++---------- packages/cli/src/init/agent_snapshot.test.ts | 6 +- packages/cli/src/init/neonctl.ts | 19 +++--- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap b/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap index c2383fe2..b55906ef 100644 --- a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap +++ b/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap @@ -436,8 +436,8 @@ exports[`neon init --agent: every step, against every project shape > connected --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -475,8 +475,8 @@ exports[`neon init --agent: every step, against every project shape > connected --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -911,8 +911,8 @@ exports[`neon init --agent: every step, against every project shape > drizzle > --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -950,8 +950,8 @@ exports[`neon init --agent: every step, against every project shape > drizzle > --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -1386,8 +1386,8 @@ exports[`neon init --agent: every step, against every project shape > fully-conf --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version" `; @@ -1423,8 +1423,8 @@ exports[`neon init --agent: every step, against every project shape > fully-conf --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version" `; @@ -1857,8 +1857,8 @@ exports[`neon init --agent: every step, against every project shape > greenfield --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -1896,8 +1896,8 @@ exports[`neon init --agent: every step, against every project shape > greenfield --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -2332,8 +2332,8 @@ exports[`neon init --agent: every step, against every project shape > next-prism --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -2371,8 +2371,8 @@ exports[`neon init --agent: every step, against every project shape > next-prism --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -2807,8 +2807,8 @@ exports[`neon init --agent: every step, against every project shape > next-prism --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -2846,8 +2846,8 @@ exports[`neon init --agent: every step, against every project shape > next-prism --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -3282,8 +3282,8 @@ exports[`neon init --agent: every step, against every project shape > node-app > --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -3321,8 +3321,8 @@ exports[`neon init --agent: every step, against every project shape > node-app > --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -3757,8 +3757,8 @@ exports[`neon init --agent: every step, against every project shape > other-data --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y @@ -3796,8 +3796,8 @@ exports[`neon init --agent: every step, against every project shape > other-data --- stderr --- --- subprocesses --- -/neonctl --version -/npm view neonctl version +/neon --version +/npm view neon version /npx -y add-mcp https://mcp.neon.tech/mcp -g -n Neon -y -a cursor /skills --version /skills add neondatabase/agent-skills --skill neon --agent cursor -y diff --git a/packages/cli/src/init/agent_snapshot.test.ts b/packages/cli/src/init/agent_snapshot.test.ts index 8d6b0380..a069fa25 100644 --- a/packages/cli/src/init/agent_snapshot.test.ts +++ b/packages/cli/src/init/agent_snapshot.test.ts @@ -62,16 +62,16 @@ const CLI = /** Every executable the init flow shells out to, with a fixed answer. */ const STUBS: Record = { - // `neonctl --version` and `npm view neonctl version` agreeing is what makes + // `neon --version` and `npm view neon version` agreeing is what makes // `ensureNeonctl` report `already_current` instead of trying to install. - neonctl: `case "$1" in + neon: `case "$1" in --version) echo "2.45.0" ;; *) exit 0 ;; esac`, npm: `if [ "$1" = "view" ]; then echo "2.45.0"; fi exit 0`, npx: "exit 0", - neon: "exit 0", + neonctl: "exit 0", skills: `if [ "$1" = "--version" ]; then echo "1.0.0"; fi exit 0`, claude: `if [ "$1" = "--version" ]; then echo "1.0.0 (Claude Code)"; fi diff --git a/packages/cli/src/init/neonctl.ts b/packages/cli/src/init/neonctl.ts index 7d62477c..2a18c7d4 100644 --- a/packages/cli/src/init/neonctl.ts +++ b/packages/cli/src/init/neonctl.ts @@ -37,13 +37,12 @@ type NeonctlStatus = { }; /** - * Gets the currently available neonctl version. - * Tries the global binary first, then falls back to npx. + * Gets the currently installed `neon` CLI version, or null if it isn't on PATH. */ async function getNeonctlVersion(): Promise { // Try global binary first (fast path) try { - const result = await execa("neonctl", ["--version"], { + const result = await execa("neon", ["--version"], { stdio: "pipe", timeout: 5000, }); @@ -56,7 +55,7 @@ async function getNeonctlVersion(): Promise { } /** - * Checks whether the neonctl CLI is globally installed and whether it's up to date. + * Checks whether the neon CLI is globally installed and whether it's up to date. */ export async function checkNeonctl(): Promise { const currentVersion = await getNeonctlVersion(); @@ -73,7 +72,7 @@ export async function checkNeonctl(): Promise { // Check latest version from npm registry let latestVersion: string | null = null; try { - const result = await execa("npm", ["view", "neonctl", "version"], { + const result = await execa("npm", ["view", "neon", "version"], { stdio: "pipe", timeout: 10000, }); @@ -114,7 +113,7 @@ export type EnsureNeonctlResult = { }; /** - * Checks if neonctl is installed via a local dev symlink. + * Checks if the neon CLI is installed via a local dev symlink. * If so, skip install/update — the developer manages it manually. */ function isLocalDevSymlink(): boolean { @@ -123,8 +122,8 @@ function isLocalDevSymlink(): boolean { const nvmDir = process.env.NVM_DIR || `${home}/.nvm`; // Check common global module locations for a symlink const candidates = [ - `${nvmDir}/versions/node/${process.version}/lib/node_modules/neonctl`, - `${home}/.nvm/versions/node/${process.version}/lib/node_modules/neonctl`, + `${nvmDir}/versions/node/${process.version}/lib/node_modules/neon`, + `${home}/.nvm/versions/node/${process.version}/lib/node_modules/neon`, ]; for (const candidate of candidates) { try { @@ -141,7 +140,7 @@ function isLocalDevSymlink(): boolean { } /** - * Ensures neonctl is globally installed and up to date. + * Ensures the `neon` CLI is globally installed and up to date. * Uses the same package manager that invoked the init command. */ export async function ensureNeonctl(): Promise { @@ -164,7 +163,7 @@ export async function ensureNeonctl(): Promise { } const pm = resolveInvokingPackageManager(); - const install = globalInstallCommand(pm, "neonctl"); + const install = globalInstallCommand(pm, "neon"); if (!install) { // The next step is installing a package manager, not falling back to // npx: npx ships with npm, so it is missing in exactly this case. From d22b66805771e9c945fc58cd9ab11d166bb8e629 Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Fri, 14 Aug 2026 11:34:29 -0600 Subject: [PATCH 06/13] Reword the setup defaults option label The defaults option now reads "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)". Regenerated the agent_snapshot fixtures for the new label. Co-authored-by: Isaac --- .../__snapshots__/agent_snapshot.test.ts.snap | 48 +++++++++---------- packages/cli/src/init/phases/setup.ts | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap b/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap index b55906ef..b445cbfe 100644 --- a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap +++ b/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap @@ -313,7 +313,7 @@ exports[`neon init --agent: every step, against every project shape > connected "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -788,7 +788,7 @@ exports[`neon init --agent: every step, against every project shape > drizzle > "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -1263,7 +1263,7 @@ exports[`neon init --agent: every step, against every project shape > fully-conf "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -1734,7 +1734,7 @@ exports[`neon init --agent: every step, against every project shape > greenfield "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -2209,7 +2209,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -2684,7 +2684,7 @@ exports[`neon init --agent: every step, against every project shape > next-prism "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -3159,7 +3159,7 @@ exports[`neon init --agent: every step, against every project shape > node-app > "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -3634,7 +3634,7 @@ exports[`neon init --agent: every step, against every project shape > other-data "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -4025,7 +4025,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > connected "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -4170,7 +4170,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > drizzle — "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -4332,7 +4332,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > greenfield "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -4477,7 +4477,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -4622,7 +4622,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > next-prisma- "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -4767,7 +4767,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > node-app — "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -4912,7 +4912,7 @@ exports[`neon init --agent: the orchestrator picks the next phase > other-databa "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -5095,7 +5095,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cla "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -5232,7 +5232,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cla "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -5445,7 +5445,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cod "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -5582,7 +5582,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cod "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -5803,7 +5803,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cur "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -5948,7 +5948,7 @@ exports[`neon init --agent: the response depends on which agent is driving > cur "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -6161,7 +6161,7 @@ exports[`neon init --agent: the response depends on which agent is driving > non "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -6298,7 +6298,7 @@ exports[`neon init --agent: the response depends on which agent is driving > non "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", @@ -6746,7 +6746,7 @@ exports[`neon init: failure output > --data without a step falls through to the "options": [ { "value": "defaults", - "label": "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" + "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" }, { "value": "customize", diff --git a/packages/cli/src/init/phases/setup.ts b/packages/cli/src/init/phases/setup.ts index def3e45b..f3d8e78d 100644 --- a/packages/cli/src/init/phases/setup.ts +++ b/packages/cli/src/init/phases/setup.ts @@ -180,7 +180,7 @@ async function buildBulkInspection( options: [ { value: "defaults", - label: "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)", + label: "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)", }, { value: "customize", From c32989ad5397346eb9f7f1fbb235bfad2dac6879 Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Fri, 14 Aug 2026 12:34:25 -0600 Subject: [PATCH 07/13] Apply installed-vs-npx fallback to init chaining; fix dev-symlink guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Factor the `neon` vs `npx -y neon` choice into a shared neonBin() helper and use it for the `neon init` chaining commands too (enrich_output), not just neonctlCmd — so a first run started with `npx neon init` doesn't emit chaining commands that fail before the CLI is installed. - Correct the neonctlCmd doc (the neon package ships only the `neon` binary). - Recognize a bare bin symlink in isLocalDevSymlink: a `neon` symlink resolving outside node_modules is a developer build, so skip the global install instead of failing with EEXIST when npm can't overwrite it. Gated on the bin being a symlink so real installs and test stubs still take the normal path. Co-authored-by: Isaac --- packages/cli/src/init/enrich_output.ts | 10 ++++-- packages/cli/src/init/neon_bin.ts | 16 +++++++++ packages/cli/src/init/neonctl.ts | 46 +++++++++++++++++--------- 3 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 packages/cli/src/init/neon_bin.ts diff --git a/packages/cli/src/init/enrich_output.ts b/packages/cli/src/init/enrich_output.ts index bacc5c0a..98aa3b0a 100644 --- a/packages/cli/src/init/enrich_output.ts +++ b/packages/cli/src/init/enrich_output.ts @@ -1,6 +1,10 @@ +import { neonBin } from "./neon_bin.js"; + /** - * Converts a phase's `args` (e.g. ["neon-auth", "--json", "--setup"]) into the - * `neon init --agent --data` invocation that reaches the same handler. + * Converts a phase's `args` (e.g. ["setup", "--json", "--data", …]) into the + * `neon init --agent --data` invocation that reaches the same handler. Uses the + * installed `neon` binary when present, else `npx -y neon` (see {@link neonBin}), + * so the chaining commands work even on a first run started with `npx neon init`. */ function argsToCommand(args: string[]): string { const data: Record = {}; @@ -35,7 +39,7 @@ function argsToCommand(args: string[]): string { } } - return `neon init --agent --data '${JSON.stringify(data)}'`; + return `${neonBin()} init --agent --data '${JSON.stringify(data)}'`; } /** diff --git a/packages/cli/src/init/neon_bin.ts b/packages/cli/src/init/neon_bin.ts new file mode 100644 index 00000000..98e20a6e --- /dev/null +++ b/packages/cli/src/init/neon_bin.ts @@ -0,0 +1,16 @@ +import which from "which"; + +/** + * How to invoke the `neon` CLI in the commands the init flow hands to agents and + * users: the installed binary when one is on PATH, otherwise `npx -y neon`. + * + * `neon init` installs the Neon CLI (see `ensureNeonctl`), so once that has run + * the bare binary is used — no per-command npx resolution cost. A first run + * started with `npx neon init`, before the CLI is installed, has no `neon` on + * PATH yet, so the emitted commands fall back to npx and still work. Detection + * happens per call (at response-build time) so the prefix reflects the machine's + * current state. + */ +export function neonBin(): string { + return which.sync("neon", { nothrow: true }) ? "neon" : "npx -y neon"; +} diff --git a/packages/cli/src/init/neonctl.ts b/packages/cli/src/init/neonctl.ts index 2a18c7d4..e35e3157 100644 --- a/packages/cli/src/init/neonctl.ts +++ b/packages/cli/src/init/neonctl.ts @@ -1,32 +1,26 @@ -import { lstatSync } from "node:fs"; +import { lstatSync, realpathSync } from "node:fs"; +import { sep } from "node:path"; import { execa } from "execa"; import which from "which"; import { globalInstallCommand, resolveInvokingPackageManager, } from "../utils/package_manager.js"; +import { neonBin } from "./neon_bin.js"; /** * Returns the Neon CLI command prefix the flow emits to agents/users, e.g. - * `"CI= neon"` or `"CI= npx -y neon"`. + * `"CI= neon"` or `"CI= npx -y neon"` (see {@link neonBin} for how the `neon` + * vs `npx -y neon` choice is made). * - * `neon init` installs the Neon CLI (see {@link ensureNeonctl}), so once a `neon` - * binary is on PATH we emit it directly — the agent drives the installed CLI - * instead of paying npx's resolution cost on every command. When nothing is - * installed yet — a first run started with `npx neon init`, before setup has - * installed the CLI — we fall back to `npx -y neon` so the emitted command still - * works. Detection happens per call (at response-build time) so the prefix - * reflects the machine's current state. - * - * The CLI reads NEON_API_HOST and NEON_OAUTH_HOST from the environment directly, - * so no extra flags are needed. The `neon` package ships both the `neon` and - * `neonctl` binaries; we surface the cleaner `neon` command. + * The `CI=` prefix keeps the emitted subcommands (`orgs list`, `auth`, …) + * non-interactive. The CLI reads NEON_API_HOST and NEON_OAUTH_HOST from the + * environment directly, so no extra flags are needed. * * Usage: `${neonctlCmd()} orgs list --output json` */ export function neonctlCmd(): string { - const installed = which.sync("neon", { nothrow: true }); - return installed ? "CI= neon" : "CI= npx -y neon"; + return `CI= ${neonBin()}`; } type NeonctlStatus = { @@ -120,7 +114,7 @@ function isLocalDevSymlink(): boolean { try { const home = process.env.HOME || process.env.USERPROFILE || ""; const nvmDir = process.env.NVM_DIR || `${home}/.nvm`; - // Check common global module locations for a symlink + // `pnpm link` / `npm link` create a package symlink under global node_modules. const candidates = [ `${nvmDir}/versions/node/${process.version}/lib/node_modules/neon`, `${home}/.nvm/versions/node/${process.version}/lib/node_modules/neon`, @@ -133,6 +127,26 @@ function isLocalDevSymlink(): boolean { // path doesn't exist } } + + // A bare bin symlink (e.g. `ln -s /dist/cli.js /neon`) points the + // `neon` binary straight at a working tree. A real global install is also a + // bin symlink, but one that resolves into a `node_modules` directory — so a + // `neon` symlink resolving anywhere else is a developer's own build. Gated on + // the bin being a symlink so a plain-file `neon` (a real install elsewhere, or + // a test stub) is left to the normal install/update path. + const bin = which.sync("neon", { nothrow: true }); + if (bin) { + try { + if ( + lstatSync(bin).isSymbolicLink() && + !realpathSync(bin).includes(`${sep}node_modules${sep}`) + ) { + return true; + } + } catch { + // unreadable — fall through + } + } return false; } catch { return false; From 97894350e9f7a227527dc8229e410b6e699a3128 Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Tue, 18 Aug 2026 09:33:36 -0600 Subject: [PATCH 08/13] chore: trigger CI Co-authored-by: Isaac From f59d4e36930ca909a54954bb868889ae2c65e95e Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Tue, 18 Aug 2026 14:46:24 -0600 Subject: [PATCH 09/13] Pin `neon` off PATH in init.test.ts for deterministic command `enrich_output` stays real in this test, so the emitted command runs through `neonBin()`, which prefers an installed `neon` on PATH and falls back to `npx -y neon`. Mock `which` so the assertion is stable whether or not the test machine has `neon` installed globally (it passed locally with a dev symlink on PATH, but CI has none), and expect the `npx -y neon` form to match. Co-authored-by: Isaac --- packages/cli/src/commands/init.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/init.test.ts b/packages/cli/src/commands/init.test.ts index 7adf6356..dc9df76c 100644 --- a/packages/cli/src/commands/init.test.ts +++ b/packages/cli/src/commands/init.test.ts @@ -33,6 +33,14 @@ vi.mock("../utils/write_sync.js", () => ({ writeAllSync: vi.fn(), })); +// `enrich_output` stays real, so the emitted command runs through `neonBin()`, which +// prefers an installed `neon` on PATH and falls back to `npx -y neon`. Pin it to the +// fallback so the asserted command is the same whether or not the test machine has +// `neon` installed globally. +vi.mock("which", () => ({ + default: { sync: () => null }, +})); + // `enrich_output` is a pure transform and stays real, so the stdout assertions below // exercise the payload an agent actually receives rather than a stand-in for it. @@ -102,7 +110,7 @@ describe("init", () => { nextAction: { type: "run_shell_command", command: - 'neon init --agent --data \'{"step":"auth","verify":true}\'', + 'npx -y neon init --agent --data \'{"step":"auth","verify":true}\'', }, }); }); From 176661abeac690124b46f87d4f122d655fdea006 Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Wed, 19 Aug 2026 08:57:34 -0600 Subject: [PATCH 10/13] Review cleanup: drop dead homeDir var, fix stale test comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - interactive.ts: remove the unused `homeDir` guard carried over from main's version — the minimal flow never reads it (agent selection uses detectInstalledAgents, skills install reads HOME internally). - route_command.test.ts: the setup phase installs MCP through the add-mcp library now, not `npx add-mcp`; update the mock comment accordingly. Co-authored-by: Isaac --- packages/cli/src/init/interactive.ts | 7 ------- packages/cli/src/init/route_command.test.ts | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/cli/src/init/interactive.ts b/packages/cli/src/init/interactive.ts index bea94bf3..a161aacd 100644 --- a/packages/cli/src/init/interactive.ts +++ b/packages/cli/src/init/interactive.ts @@ -186,13 +186,6 @@ async function interactiveInitInner(): Promise { // ----------------------------------------------------------------------- // Step 2: Install what's missing // ----------------------------------------------------------------------- - const homeDir = process.env.HOME || process.env.USERPROFILE; - if (!homeDir) { - log.error("Could not determine home directory."); - outro("Setup failed."); - return; - } - let selectedAgents: AgentType[]; if (detectedAgent) { selectedAgents = [detectedAgent]; diff --git a/packages/cli/src/init/route_command.test.ts b/packages/cli/src/init/route_command.test.ts index f4c37c3a..c4f640a8 100644 --- a/packages/cli/src/init/route_command.test.ts +++ b/packages/cli/src/init/route_command.test.ts @@ -1,7 +1,7 @@ import { describe, expect, test, vi } from "vitest"; import { routeDataStep } from "./route_command.js"; -// Mock execa for setup phase (it runs add-mcp, skills install, etc.) +// Mock execa so phase handlers never shell out (skills install, extension install, etc.) vi.mock("execa", () => ({ execa: vi.fn().mockResolvedValue({ stdout: "", stderr: "", exitCode: 0 }), })); From ee79e54dc6e4cde14e5abf06e053a64dc9ca11fd Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Wed, 19 Aug 2026 11:54:42 -0600 Subject: [PATCH 11/13] Review fixes: correct skills fallback agent name; re-check extension on editor change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - setup.ts: the manual skills-install fallback (shown when auto-install fails) built `skills add --agent `, but the skills CLI expects the mapped skills-agent name (e.g. vscode → github-copilot). Use getSkillsAgentName(agentId) so the printed commands actually work — matching the mapping the real install (ensureSkillsUpToDate) and phases/skills.ts use. - interactive.ts: when the user picks "Configure a different agent", re-check whether the extension is installed for the newly chosen editors. Previously `extensionAlreadyInstalled` kept the value from the initial agent set, so the extension prompt could be wrongly shown or suppressed after switching. Co-authored-by: Isaac --- packages/cli/src/init/interactive.ts | 9 +++++++++ packages/cli/src/init/phases/setup.ts | 13 ++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/init/interactive.ts b/packages/cli/src/init/interactive.ts index a161aacd..c1cafe8b 100644 --- a/packages/cli/src/init/interactive.ts +++ b/packages/cli/src/init/interactive.ts @@ -275,6 +275,15 @@ async function interactiveInitInner(): Promise { } selectedAgents = picked; vscodeEditors = extensionEditorsFor(selectedAgents); + // Re-check extension status for the newly chosen editors — otherwise + // `canInstallExtension` would reflect the previously selected set. + extensionAlreadyInstalled = + vscodeEditors.length > 0 && + ( + await Promise.all( + vscodeEditors.map((e) => isExtensionInstalled(e)), + ) + ).every(Boolean); canInstallExtension = vscodeEditors.length > 0 && !extensionAlreadyInstalled; continue; diff --git a/packages/cli/src/init/phases/setup.ts b/packages/cli/src/init/phases/setup.ts index 0bbe6ba7..ad61b914 100644 --- a/packages/cli/src/init/phases/setup.ts +++ b/packages/cli/src/init/phases/setup.ts @@ -1,6 +1,10 @@ import { unlink } from "node:fs/promises"; import { execa } from "execa"; -import { agentSupportsProjectMcp, resolveAddMcpAgentId } from "../agents.js"; +import { + agentSupportsProjectMcp, + getSkillsAgentName, + resolveAddMcpAgentId, +} from "../agents.js"; import { detectIde, isCursorInstalled, @@ -441,12 +445,15 @@ async function executeBatchedInstallation( }); } else { // Build the install commands for the agent to run directly - // (sandboxed environments may block child process writes) + // (sandboxed environments may block child process writes). Use the + // skills-CLI agent name (e.g. vscode → github-copilot), which is what + // `skills add --agent` expects — not the raw agent id. const { getSkillList } = await import("../skills.js"); const skillList = getSkillList(); + const skillsAgentName = getSkillsAgentName(agentId) ?? agentId; const cmds = skillList.map( (s) => - `skills add neondatabase/agent-skills --skill ${s} --agent ${agentId}${skillsScope === "global" ? " -g" : ""} -y`, + `skills add neondatabase/agent-skills --skill ${s} --agent ${skillsAgentName}${skillsScope === "global" ? " -g" : ""} -y`, ); results.push({ id: "install_skills", From a1be724684bbddab56f6468cf6900ff398716d8b Mon Sep 17 00:00:00 2001 From: Jeff Christoffersen Date: Tue, 25 Aug 2026 11:18:20 -0600 Subject: [PATCH 12/13] Remove VSCode extension installation from neon init Drops vsix.ts, extension.ts, scripts/set-vsx-gallery.mjs, and the generated build_config.ts entirely. Removes the installExtension option, extension_installed check, and the install step from the agent setup flow. Interactive mode no longer offers the extension prompt or install loop. Collapses the now-identical defaults/customize branches in handleSetupPhase and simplifies mergeCliInspection (no longer needs inspectProject). Stale snapshot deleted; will regenerate on next build+test. Co-authored-by: Isaac --- packages/cli/package.json | 16 +- packages/cli/scripts/set-vsx-gallery.mjs | 15 - .../__snapshots__/agent_snapshot.test.ts.snap | 6677 ----------------- packages/cli/src/init/agent_snapshot.test.ts | 5 +- packages/cli/src/init/agents.ts | 3 - packages/cli/src/init/detect_agent.ts | 8 +- packages/cli/src/init/extension.ts | 213 - packages/cli/src/init/interactive.ts | 93 +- packages/cli/src/init/phases/setup.test.ts | 115 +- packages/cli/src/init/phases/setup.ts | 243 +- packages/cli/src/init/route_command.test.ts | 7 +- packages/cli/src/init/types.ts | 2 - packages/cli/src/init/vsix.ts | 142 - 13 files changed, 30 insertions(+), 7509 deletions(-) delete mode 100644 packages/cli/scripts/set-vsx-gallery.mjs delete mode 100644 packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap delete mode 100644 packages/cli/src/init/extension.ts delete mode 100644 packages/cli/src/init/vsix.ts diff --git a/packages/cli/package.json b/packages/cli/package.json index 183d271b..bf06bc95 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -43,18 +43,16 @@ }, "scripts": { "generateParams": "tsx generateOptionsFromSpec.ts", - "codegen": "node scripts/set-vsx-gallery.mjs", "clean": "rm -rf dist", - "build": "pnpm codegen && pnpm generateParams && tsc --noEmit && tsdown && cp src/*.html ./dist", - "build:internal": "node scripts/set-vsx-gallery.mjs https://cursor-vsx-proxy.cloud.databricks.com/gallery && pnpm generateParams && tsc --noEmit && tsdown && cp src/*.html ./dist", + "build": "pnpm generateParams && tsc --noEmit && tsdown && cp src/*.html ./dist", "bundle": "node pkg.js", - "typecheck": "pnpm codegen && tsc --noEmit", - "lint": "pnpm codegen && pnpm typecheck && biome check src", + "typecheck": "tsc --noEmit", + "lint": "pnpm typecheck && biome check src", "lint:fix": "pnpm typecheck && biome check src --write", - "test": "pnpm codegen && pnpm --filter neonctl... build && vitest run", - "test:ci": "pnpm codegen && pnpm build && vitest run", - "test:e2e": "pnpm codegen && pnpm build && vitest run --config vitest.e2e.config.ts", - "test:conformance": "pnpm codegen && vitest run --config tests/psql-conformance/vitest.config.ts" + "test": "pnpm --filter neonctl... build && vitest run", + "test:ci": "pnpm build && vitest run", + "test:e2e": "pnpm build && vitest run --config vitest.e2e.config.ts", + "test:conformance": "vitest run --config tests/psql-conformance/vitest.config.ts" }, "dependencies": { "@clack/core": "0.4.2", diff --git a/packages/cli/scripts/set-vsx-gallery.mjs b/packages/cli/scripts/set-vsx-gallery.mjs deleted file mode 100644 index 3ba899bd..00000000 --- a/packages/cli/scripts/set-vsx-gallery.mjs +++ /dev/null @@ -1,15 +0,0 @@ -// Generates src/init/build_config.ts with the VSX gallery URL baked in. -// Usage: -// node scripts/set-vsx-gallery.mjs → empty (external release) -// node scripts/set-vsx-gallery.mjs https://proxy.example.com/gallery → baked in (internal) -// -// Every script that compiles or type-checks `src/` must run this first: the -// generated file is gitignored, so a fresh clone has none and `tsc` fails on -// the import in src/init/vsix.ts. -import { writeFileSync } from "node:fs"; - -const url = process.argv[2] || ""; -writeFileSync( - "src/init/build_config.ts", - `// Auto-generated by scripts/set-vsx-gallery.mjs — do not edit\nexport const INTERNAL_VSX_GALLERY = ${JSON.stringify(url)};\n`, -); diff --git a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap b/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap deleted file mode 100644 index 1e54575b..00000000 --- a/packages/cli/src/init/__snapshots__/agent_snapshot.test.ts.snap +++ /dev/null @@ -1,6677 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`neon init --agent: agentId in the payload overrides detection > claude-code 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"claude-code\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: agentId in the payload overrides detection > cursor 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: agentId in the payload overrides detection > vscode 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"vscode\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: agentId in the payload overrides detection > windsurf 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"windsurf\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > auth-existing 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > auth-new 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > auth-verify 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > mcp-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installed", - "path": "/.cursor/mcp.json", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "message": "Installed Neon MCP server (global scope) for cursor." -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > setup-customize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > connected > setup-defaults 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > connected > skills-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > connected > skills-update 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > auth-existing 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > auth-new 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > auth-verify 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > mcp-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installed", - "path": "/.cursor/mcp.json", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "message": "Installed Neon MCP server (global scope) for cursor." -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > setup-customize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > setup-defaults 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > skills-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > drizzle > skills-update 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > auth-existing 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > auth-new 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > auth-verify 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > mcp-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installed", - "path": "/.cursor/mcp.json", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "message": "Installed Neon MCP server (global scope) for cursor." -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > setup-customize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > setup-defaults 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > skills-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > fully-configured > skills-update 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > auth-existing 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > auth-new 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > auth-verify 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > mcp-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installed", - "path": "/.cursor/mcp.json", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "message": "Installed Neon MCP server (global scope) for cursor." -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > setup-customize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > setup-defaults 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > skills-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > greenfield > skills-update 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > auth-existing 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > auth-new 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > auth-verify 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > mcp-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installed", - "path": "/.cursor/mcp.json", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "message": "Installed Neon MCP server (global scope) for cursor." -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > setup-customize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > setup-defaults 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > skills-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma > skills-update 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth-existing 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth-new 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > auth-verify 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > mcp-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installed", - "path": "/.cursor/mcp.json", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "message": "Installed Neon MCP server (global scope) for cursor." -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > setup-customize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > setup-defaults 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > skills-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > next-prisma-migrated > skills-update 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > auth-existing 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > auth-new 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > auth-verify 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > mcp-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installed", - "path": "/.cursor/mcp.json", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "message": "Installed Neon MCP server (global scope) for cursor." -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > setup-customize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > setup-defaults 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > skills-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > node-app > skills-update 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > auth-existing 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > auth-new 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > auth-verify 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > mcp-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installed", - "path": "/.cursor/mcp.json", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - }, - "message": "Installed Neon MCP server (global scope) for cursor." -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > setup-customize 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > setup-defaults 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "installed", - "results": [ - { - "id": "neonctl", - "description": "Neon CLI is up to date (v2.45.0)", - "status": "success" - }, - { - "id": "install_mcp", - "description": "Installed Neon MCP server (global scope)", - "status": "success" - }, - { - "id": "install_skills", - "description": "Neon agent skills installed", - "status": "success" - } - ], - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -/neon --version -/npm view neon version -/skills --version -/skills add neondatabase/agent-skills --skill neon --agent cursor -y -/skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > skills-install 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: every step, against every project shape > other-database > skills-update 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "installing_skills", - "nextAction": { - "type": "run_command", - "command": "npx -y skills add neondatabase/agent-skills --skill neon --agent cursor -y && npx -y skills add neondatabase/agent-skills --skill neon-postgres --agent cursor -y", - "description": "Installing Neon agent skills for cursor.", - "timeout": 30000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - }, - "onFailure": { - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } - } - }, - "skillReferences": { - "gettingStarted": "https://neon.com/docs/ai/skills/neon-postgres/references/getting-started.md", - "connectionMethods": "https://neon.com/docs/ai/skills/neon-postgres/references/connection-methods.md", - "neonAuth": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-auth.md", - "serverlessDriver": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-serverless.md", - "neonCli": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-cli.md", - "devtools": "https://neon.com/docs/ai/skills/neon-postgres/references/devtools.md", - "branching": "https://neon.com/docs/ai/skills/neon-postgres/references/branching.md", - "neonJs": "https://neon.com/docs/ai/skills/neon-postgres/references/neon-js.md" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: nested and string-encoded --data payloads > JSON-encoded string under a data key 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: nested and string-encoded --data payloads > nested object under a data key 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > connected — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > drizzle — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > fully-configured — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "complete", - "nextAction": { - "type": "complete", - "message": "Neon tooling is installed — the MCP server and agent skills are available. Ask the user which features they want (Postgres, Auth, Object Storage, Functions, AI Gateway), then follow the Neon skill to connect the database and configure the selected features." - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > greenfield — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > next-prisma — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > next-prisma-migrated — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > node-app — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the orchestrator picks the next phase > other-database — no --data 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > claude-code > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"claude-code\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > claude-code > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"claude-code\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > claude-code > orchestrator 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": null, - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > claude-code > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": null, - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > claude-code > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"claude-code\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > codex > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"codex\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > codex > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"codex\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > codex > orchestrator 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": null, - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > codex > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": null, - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > codex > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"codex\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > cursor > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"agent\\":\\"cursor\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > cursor > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"agent\\":\\"cursor\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > cursor > orchestrator 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > cursor > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > cursor > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"agent\\":\\"cursor\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > none > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "verified", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > none > mcp-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "status", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"mcp\\",\\"mcpConfigured\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > none > orchestrator 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": null, - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > none > setup 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": null, - "installedEditors": , - "mcpConfigured": null, - "mcpScope": null, - "skillsInstalled": null, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\n\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the response depends on which agent is driving > none > skills-status 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "tooling", - "status": "skills_check", - "nextAction": { - "type": "agent_check", - "checks": [ - { - "id": "skills", - "description": "Check if Neon agent skills are installed in this project", - "lookFor": [ - "A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", - "A .skills/ directory with neon-related content" - ] - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"skills\\",\\"install\\":true}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the signed-out auth branches > a damaged credentials file is an error, not a fresh machine 1`] = ` -"exit: 1 ---- stdout --- - ---- stderr --- -ERROR: /credentials.json is not valid JSON, so the credential in it cannot be read. Replace it deliberately with \`neon profile create DEFAULT\`, or delete the file. ---- subprocesses --- -" -`; - -exports[`neon init --agent: the signed-out auth branches > auth 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "required", - "nextAction": { - "type": "ask_user", - "instructions": "IMPORTANT: You MUST present this question to the user and WAIT for their response before proceeding. Do NOT assume the answer or auto-select an option. The user's answer determines the next step.", - "question": "Do you have an existing Neon account, or do you need to create one?", - "options": [ - { - "value": "existing_account", - "label": "I have an existing Neon account" - }, - { - "value": "new_account", - "label": "I need to create a new account" - } - ], - "context": "Neon is a complete set of cloud backend primitives built around Lakebase Postgres. A free account is required to continue.", - "responseMapping": { - "existing_account": { - "action": { - "type": "run_command", - "command": "CI= neon auth", - "description": "This will open your browser for Neon OAuth sign-in.", - "timeout": 120000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"verify\\":true}'" - }, - "onFailure": { - "2": { - "type": "ask_user", - "question": "The sign-in timed out. Did you complete the sign-in in your browser?", - "options": [ - "yes_retry", - "need_help" - ], - "responseMapping": { - "yes_retry": { - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"method\\":\\"existing\\"}'" - }, - "need_help": { - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"method\\":\\"new\\"}'" - } - } - }, - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"auth\\"}'" - } - } - } - }, - "new_account": { - "action": { - "type": "agent_action", - "steps": [ - { - "id": "open_signup", - "description": "Open the Neon sign-up page in the user's browser", - "command": " https://console.neon.tech/signup" - }, - { - "id": "wait_for_signup", - "description": "Tell the user: 'I've opened the Neon sign-up page. Create your account and verify your email, then let me know when you're ready.'" - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"method\\":\\"existing\\"}'" - } - } - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the signed-out auth branches > auth-existing-account 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "in_progress", - "nextAction": { - "type": "run_command", - "command": "CI= neon auth", - "description": "This will open your browser for Neon OAuth sign-in.", - "timeout": 120000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"verify\\":true}'" - }, - "onFailure": { - "2": { - "type": "ask_user", - "question": "The sign-in timed out. Did you complete the sign-in in your browser?", - "options": [ - "yes_retry", - "need_help" - ], - "responseMapping": { - "yes_retry": { - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"method\\":\\"existing\\"}'" - }, - "need_help": { - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"method\\":\\"new\\"}'" - } - } - }, - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"auth\\"}'" - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the signed-out auth branches > auth-new-account 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "in_progress", - "nextAction": { - "type": "agent_action", - "steps": [ - { - "id": "open_signup", - "description": "Open the Neon sign-up page in the user's browser", - "command": " https://console.neon.tech/signup" - }, - { - "id": "wait_for_signup", - "description": "Tell the user: 'I've opened the Neon sign-up page. Create your account and verify your email, then let me know when you're ready.'" - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"method\\":\\"existing\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the signed-out auth branches > auth-verify 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "not_authenticated", - "nextAction": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"auth\\"}'" - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init --agent: the signed-out auth branches > the orchestrator sends a signed-out caller to the auth phase 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "auth", - "status": "required", - "nextAction": { - "type": "ask_user", - "instructions": "IMPORTANT: You MUST present this question to the user and WAIT for their response before proceeding. Do NOT assume the answer or auto-select an option. The user's answer determines the next step.", - "question": "Do you have an existing Neon account, or do you need to create one?", - "options": [ - { - "value": "existing_account", - "label": "I have an existing Neon account" - }, - { - "value": "new_account", - "label": "I need to create a new account" - } - ], - "context": "Neon is a complete set of cloud backend primitives built around Lakebase Postgres. A free account is required to continue.", - "responseMapping": { - "existing_account": { - "action": { - "type": "run_command", - "command": "CI= neon auth", - "description": "This will open your browser for Neon OAuth sign-in.", - "timeout": 120000, - "onSuccess": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"verify\\":true}'" - }, - "onFailure": { - "2": { - "type": "ask_user", - "question": "The sign-in timed out. Did you complete the sign-in in your browser?", - "options": [ - "yes_retry", - "need_help" - ], - "responseMapping": { - "yes_retry": { - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"method\\":\\"existing\\"}'" - }, - "need_help": { - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"method\\":\\"new\\"}'" - } - } - }, - "other": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"auth\\"}'" - } - } - } - }, - "new_account": { - "action": { - "type": "agent_action", - "steps": [ - { - "id": "open_signup", - "description": "Open the Neon sign-up page in the user's browser", - "command": " https://console.neon.tech/signup" - }, - { - "id": "wait_for_signup", - "description": "Tell the user: 'I've opened the Neon sign-up page. Create your account and verify your email, then let me know when you're ready.'" - } - ], - "onComplete": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"auth\\",\\"method\\":\\"existing\\"}'" - } - } - } - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init: failure output > --data without a step falls through to the orchestrator 1`] = ` -"exit: 0 ---- stdout --- -{ - "phase": "setup", - "status": "pending", - "detectedIde": "cursor", - "installedEditors": , - "mcpConfigured": false, - "mcpScope": null, - "skillsInstalled": false, - "skillsScope": null, - "nextAction": { - "type": "agent_check", - "instructions": "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the agent checks and the first userPreference question.\\n\\nPerform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the \`condition\` field — only show a question if its condition is met.\\n\\nThe CLI has pre-detected the following from the filesystem: MCP server: not configured. Agent skills: not installed. Report these findings to the user before asking preferences. Only ask about scope/options for components that are NOT already configured. Do NOT ask about skills scope if skills are partially installed — they will be completed automatically.\\n\\nIMPORTANT (Cursor users): Cursor disables project-level MCP servers by default as a security measure. If the user is in Cursor and chooses project-level MCP scope, warn them that they will need to manually enable the Neon server in Cursor Settings > MCP after installation. Recommend global scope for Cursor to avoid this extra step.\\n\\nGROUPING: Preferences that share the same \`group\` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a \`group\` must be asked individually.\\n\\nThe CLI has detected the IDE as: cursor. Include this as the \\"ide\\" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.\\n\\nAfter all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", - "checks": [ - { - "id": "neonctl", - "description": "The Neon CLI will be installed or updated automatically (no action needed from the agent)", - "lookFor": [] - }, - { - "id": "mcp_server", - "description": "Check if the Neon MCP server is already configured in your MCP server list", - "lookFor": [ - "An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'" - ] - }, - { - "id": "agent_type", - "description": "Identify which coding agent is running this command", - "lookFor": [ - "Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity", - "Report your own agent identifier — this is used to configure the MCP server for the correct tool" - ] - }, - { - "id": "extension_installed", - "description": "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - "lookFor": [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question" - ] - } - ], - "userPreferences": [ - { - "id": "mode", - "question": "Use default settings or customize?", - "phase": "after_checks", - "options": [ - { - "value": "defaults", - "label": "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)" - }, - { - "value": "customize", - "label": "Customize installation settings" - } - ], - "default": "defaults" - }, - { - "id": "mcpScope", - "question": "Where should the Neon MCP server be configured?", - "context": "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - }, - { - "value": "none", - "label": "Skip — do not install the MCP server" - } - ], - "default": "global", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "skillsScope", - "question": "Where should Neon agent skills be installed?", - "context": "Only ask if skills are not already installed.", - "phase": "after_checks", - "options": [ - { - "value": "global", - "label": "Global (available in all projects)" - }, - { - "value": "project", - "label": "Project-level (scoped to this project only)" - } - ], - "default": "project", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - }, - { - "id": "installExtension", - "question": "Install the Neon editor extension for local database browsing?", - "phase": "after_checks", - "options": [ - { - "value": "true", - "label": "Yes" - }, - { - "value": "false", - "label": "No" - } - ], - "default": "true", - "context": "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - "condition": { - "preferenceId": "mode", - "equals": "customize" - }, - "group": "customize" - } - ], - "reportBack": { - "type": "run_shell_command", - "command": "neon init --agent --data '{\\"step\\":\\"setup\\",\\"data\\":\\"\\"}'" - } - } -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init: failure output > --help 1`] = ` -"exit: 0 ---- stdout --- - ---- stderr --- -neon init - -Initialize a project with Neon using your AI coding assistant - -Global options: --o, --output -└────────────────> Set output format [string] [choices: "json", "yaml", "table" - ] [default: "table"] ---config-dir -└────────────────> Path to config directory [string] [default: ""] ---profile -└────────────────> Named credentials to use, from profiles.json (default: NEON_ - PROFILE, else DEFAULT) [string] ---api-key -└────────────────> API key [string] [default: NEON_API_KEY] ---color -└────────────────> Colorize the output. Example: --no-color, --color false [boo - lean] [default: true] ---analytics -└────────────────> Manage analytics. Example: --no-analytics, --analytics false - [boolean] [default: true] --h, --help -└────────────────> Show help [boolean] [default: false] --v, --version -└────────────────> Show version number [boolean] - -Options: --a, --agent -└────────────────> Enable agent/JSON mode (agent type is auto-detected). [boole - an] [default: false] ---data -└────────────────> JSON object with a "step" field to route to a specific phase - and phase-specific options. [string] ---- subprocesses --- -" -`; - -exports[`neon init: failure output > an unknown profile is refused before the command runs 1`] = ` -"exit: 1 ---- stdout --- - ---- stderr --- -ERROR: Unknown profile "ghost". Known profiles: DEFAULT. Create it with \`neon profile create ghost\`. ---- subprocesses --- -" -`; - -exports[`neon init: failure output > malformed --data 1`] = ` -"exit: 1 ---- stdout --- -{ - "success": false, - "error": "Invalid JSON in --data flag at position 1. Expected a JSON object." -} ---- stderr --- - ---- subprocesses --- -" -`; - -exports[`neon init: failure output > unknown step 1`] = ` -"exit: 1 ---- stdout --- -{ - "success": false, - "error": "Unknown step: \\"not-a-step\\". Valid steps: auth, setup, mcp, skills" -} ---- stderr --- - ---- subprocesses --- -" -`; diff --git a/packages/cli/src/init/agent_snapshot.test.ts b/packages/cli/src/init/agent_snapshot.test.ts index 25f011b2..ce7b1040 100644 --- a/packages/cli/src/init/agent_snapshot.test.ts +++ b/packages/cli/src/init/agent_snapshot.test.ts @@ -439,7 +439,7 @@ function normalise( // editing the guidance around it stops the match and shows up as a diff // rather than being silently absorbed. .replace( - /No IDE detected, but the following editors are installed: [^.]*\. The \\"installedEditors\\" field in this response lists them\. If the user wants the extension installed, ask which editor to install it for and include that as the \\"ide\\" field in your reportBack data\. If not, set \\"ide\\" to \\"none\\"\.|No IDE or supported editors detected\. Set \\"ide\\" to \\"none\\" in your reportBack data\./g, + /No IDE detected, but the following editors are installed: [^.]*\. The \\"installedEditors\\" field in this response lists them\. Set \\"ide\\" to the editor name or \\"none\\" in your reportBack data\.|No IDE or supported editors detected\. Set \\"ide\\" to \\"none\\" in your reportBack data\./g, "", ) .split(paths.configDir) @@ -493,8 +493,6 @@ const STEPS: { name: string; data: Record }[] = [ step: "setup", mode: "defaults", mcpConfigured: false, - isVscodeIde: true, - installExtension: false, }, }, { @@ -504,7 +502,6 @@ const STEPS: { name: string; data: Record }[] = [ mode: "customize", mcpScope: "global", skillsScope: "project", - installExtension: false, }, }, { name: "mcp-status", data: { step: "mcp", status: true } }, diff --git a/packages/cli/src/init/agents.ts b/packages/cli/src/init/agents.ts index 2d255de3..58cf2a73 100644 --- a/packages/cli/src/init/agents.ts +++ b/packages/cli/src/init/agents.ts @@ -100,9 +100,6 @@ export function supportsSkills(agent: string): boolean { } export function agentPickerHint(id: AgentType): string { - if (id === "cursor" || id === "vscode") { - return "Neon Local Connect extension"; - } if (id === "claude-desktop") { return supportsSkills(id) ? "Connectors in the app, skills" diff --git a/packages/cli/src/init/detect_agent.ts b/packages/cli/src/init/detect_agent.ts index 3acc6f09..dcba971f 100644 --- a/packages/cli/src/init/detect_agent.ts +++ b/packages/cli/src/init/detect_agent.ts @@ -1,12 +1,6 @@ import { existsSync } from "node:fs"; -/** - * Detects the IDE/editor the terminal is running in, regardless of which - * agent is active. Used for extension installation — the extension goes - * into the IDE, not the agent. - * - * Returns an Editor-compatible string: "Cursor", "VS Code", "Windsurf", or null. - */ +/** Detects the IDE/editor the terminal is running in, regardless of which agent is active. */ export function detectIde(): "Cursor" | "VS Code" | "Windsurf" | null { const env = process.env; diff --git a/packages/cli/src/init/extension.ts b/packages/cli/src/init/extension.ts deleted file mode 100644 index 8a9c6bd1..00000000 --- a/packages/cli/src/init/extension.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { existsSync } from "node:fs"; -import { unlink } from "node:fs/promises"; -import { execa } from "execa"; -import type { Editor } from "./types.js"; -import { downloadVsix, NEON_EXTENSION_ID } from "./vsix.js"; - -/** - * Uses macOS mdfind to locate an app by bundle identifier - */ -async function findAppWithMdfind(bundleId: string): Promise { - try { - const result = await execa( - "mdfind", - [`kMDItemCFBundleIdentifier == '${bundleId}'`], - { timeout: 5000 }, - ); - const paths = result.stdout.trim().split("\n").filter(Boolean); - return paths[0] || null; - } catch { - return null; - } -} - -/** - * Known installation paths for VS Code CLI - */ -function getVSCodePaths(): string[] { - const platform = process.platform; - const home = process.env.HOME || ""; - - if (platform === "darwin") { - return [ - "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code", - "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code-insiders", - `${home}/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code`, - `${home}/Downloads/Visual Studio Code.app/Contents/Resources/app/bin/code`, - ]; - } - - if (platform === "linux") { - return [ - "/usr/share/code/bin/code", - "/usr/bin/code", - "/snap/bin/code", - "/usr/share/code-insiders/bin/code-insiders", - ]; - } - - if (platform === "win32") { - const localAppData = process.env.LOCALAPPDATA || ""; - const programFiles = process.env.PROGRAMFILES || "C:\\Program Files"; - return [ - `${localAppData}\\Programs\\Microsoft VS Code\\bin\\code.cmd`, - `${programFiles}\\Microsoft VS Code\\bin\\code.cmd`, - `${localAppData}\\Programs\\Microsoft VS Code Insiders\\bin\\code-insiders.cmd`, - ]; - } - - return []; -} - -/** - * Known installation paths for Cursor CLI - */ -function getCursorPaths(): string[] { - const platform = process.platform; - const home = process.env.HOME || ""; - - if (platform === "darwin") { - return [ - "/Applications/Cursor.app/Contents/Resources/app/bin/cursor", - `${home}/Applications/Cursor.app/Contents/Resources/app/bin/cursor`, - `${home}/Downloads/Cursor.app/Contents/Resources/app/bin/cursor`, - ]; - } - - if (platform === "linux") { - return [ - "/usr/share/cursor/bin/cursor", - "/usr/bin/cursor", - `${home}/.local/bin/cursor`, - "/opt/cursor/bin/cursor", - ]; - } - - if (platform === "win32") { - const localAppData = process.env.LOCALAPPDATA || ""; - const programFiles = process.env.PROGRAMFILES || "C:\\Program Files"; - return [ - `${localAppData}\\Programs\\Cursor\\resources\\app\\bin\\cursor.cmd`, - `${localAppData}\\cursor\\Cursor.exe`, - `${programFiles}\\Cursor\\resources\\app\\bin\\cursor.cmd`, - ]; - } - - return []; -} - -/** - * Finds the CLI command for an editor by checking known installation paths - * On macOS, also uses mdfind to locate the app if standard paths fail - * Falls back to the simple command name if no full path is found (in case it's in PATH) - */ -export async function findEditorCommand( - editor: Editor, -): Promise { - let paths: string[]; - let fallbackCommand: string; - let bundleId: string | null = null; - - if (editor === "VS Code") { - paths = getVSCodePaths(); - fallbackCommand = "code"; - bundleId = "com.microsoft.VSCode"; - } else if (editor === "Cursor") { - paths = getCursorPaths(); - fallbackCommand = "cursor"; - bundleId = "com.todesktop.230313mzl4w4u92"; - } else { - return null; - } - - for (const path of paths) { - if (existsSync(path)) { - return path; - } - } - - // On macOS, try mdfind to locate the app dynamically - if (process.platform === "darwin" && bundleId) { - const appPath = await findAppWithMdfind(bundleId); - if (appPath) { - const cliPath = `${appPath}/Contents/Resources/app/bin/${fallbackCommand}`; - if (existsSync(cliPath)) { - return cliPath; - } - } - } - - return fallbackCommand; -} - -/** - * Checks if the extension is installed by querying the editor's extension list - */ -export async function isExtensionInstalled(editor: Editor): Promise { - const command = await findEditorCommand(editor); - if (!command) { - return false; - } - - try { - const result = await execa(command, ["--list-extensions"], { - timeout: 5000, - }); - return result.stdout.includes(NEON_EXTENSION_ID); - } catch { - return false; - } -} - -/** - * Installs the Neon Local Connect extension for VS Code or Cursor. - * - * Strategy: - * 1. Try `--install-extension ` directly (uses the editor's configured marketplace) - * 2. If that fails, download .vsix (from proxy or Open VSX) and install locally - * 3. Set NEON_VSX_GALLERY_URL to use a corporate proxy for the download - */ -export async function installExtension(editor: Editor): Promise { - const command = await findEditorCommand(editor); - if (!command) { - return false; - } - - // Try direct marketplace install first - try { - await execa(command, ["--install-extension", NEON_EXTENSION_ID], { - stdio: "pipe", - timeout: 60000, - }); - return true; - } catch { - // Fall through to VSIX download - } - - // Download .vsix and install locally - const vsixPath = await downloadVsix(); - if (!vsixPath) { - return false; - } - - try { - await execa(command, ["--install-extension", vsixPath], { - stdio: "pipe", - timeout: 60000, - }); - return true; - } catch { - return false; - } finally { - try { - await unlink(vsixPath); - } catch {} - } -} - -/** - * Returns the editor types that should use extension installation (vs MCP) - */ -export function usesExtension(editor: Editor): boolean { - return editor === "VS Code" || editor === "Cursor"; -} diff --git a/packages/cli/src/init/interactive.ts b/packages/cli/src/init/interactive.ts index c1cafe8b..dc1aeafd 100644 --- a/packages/cli/src/init/interactive.ts +++ b/packages/cli/src/init/interactive.ts @@ -1,13 +1,12 @@ /** * Interactive v2 CLI — purpose-built guided flow for humans. * - * Installs the Neon tooling (MCP server, agent skills, IDE extension) and stops - * there. Connecting a database and configuring features is left to the user's + * Installs the Neon tooling (MCP server, agent skills) and stops there. + * Connecting a database and configuring features is left to the user's * agent — which now has the Neon skill — or to a manual `neon link`. */ import { - confirm, isCancel, log, multiselect, @@ -28,7 +27,6 @@ import { } from "./agents.js"; import { ensureNeonctlAuth, isAuthenticated } from "./auth.js"; import { detectAgent, detectIde } from "./detect_agent.js"; -import { installExtension, isExtensionInstalled } from "./extension.js"; import { inspectProject } from "./inspect.js"; import { installNeonMcpServer } from "./install_mcp.js"; import { ensureNeonctl } from "./neonctl.js"; @@ -37,7 +35,6 @@ import { installAgentSkills, skillsInstalledForAgent, } from "./skills.js"; -import type { Editor } from "./types.js"; function wordWrap(text: string, width: number): string { return text @@ -108,7 +105,7 @@ async function interactiveInitInner(): Promise { process.stdout.write( `${dim( wordWrap( - "\nLet's set up Neon for your AI coding assistant. We'll install the MCP server, agent skills, and IDE extension so your agent can take it from here.\n", + "\nLet's set up Neon for your AI coding assistant. We'll install the MCP server and agent skills so your agent can take it from here.\n", process.stdout.columns || 80, ), )}\n`, @@ -145,12 +142,6 @@ async function interactiveInitInner(): Promise { const needsSkills = !skillsAlready; const needsInstall = needsMcp || needsSkills; - let extensionAlready = false; - const detectedIdeEditor = extensionEditorForAgent(detectedAgent); - if (detectedIdeEditor) { - extensionAlready = await isExtensionInstalled(detectedIdeEditor); - } - // Everything already in place — nothing to install. if (!needsInstall) { log.step( @@ -163,8 +154,6 @@ async function interactiveInitInner(): Promise { `Neon agent skills already installed (${inspection.skillsScope || "detected"}) ✓`, ), ); - if (extensionAlready) - log.step(dim("Neon editor extension installed ✓")); printNextSteps(); return; } @@ -198,21 +187,6 @@ async function interactiveInitInner(): Promise { selectedAgents = picked; } - let vscodeEditors = extensionEditorsFor(selectedAgents); - let extensionAlreadyInstalled = false; - if (vscodeEditors.length > 0) { - const checks = await Promise.all( - vscodeEditors.map((e) => isExtensionInstalled(e)), - ); - extensionAlreadyInstalled = checks.every(Boolean); - if (extensionAlreadyInstalled) { - log.step(dim("Neon editor extension already installed ✓")); - } - } - let canInstallExtension = - vscodeEditors.length > 0 && !extensionAlreadyInstalled; - let doInstallExtension = false; - // Installation preferences let mcpScope: "global" | "project" | "none" = "global"; let skillsScope: "global" | "project" = "project"; @@ -250,9 +224,7 @@ async function interactiveInitInner(): Promise { { value: "customize", label: "Customize installation", - hint: canInstallExtension - ? "choose scopes and optional editor extension" - : "choose scopes and options", + hint: "choose scopes and options", }, { value: "change_editor", @@ -274,18 +246,6 @@ async function interactiveInitInner(): Promise { return; } selectedAgents = picked; - vscodeEditors = extensionEditorsFor(selectedAgents); - // Re-check extension status for the newly chosen editors — otherwise - // `canInstallExtension` would reflect the previously selected set. - extensionAlreadyInstalled = - vscodeEditors.length > 0 && - ( - await Promise.all( - vscodeEditors.map((e) => isExtensionInstalled(e)), - ) - ).every(Boolean); - canInstallExtension = - vscodeEditors.length > 0 && !extensionAlreadyInstalled; continue; } @@ -353,17 +313,6 @@ async function interactiveInitInner(): Promise { } skillsScope = skillsScopeResult as "global" | "project"; } - - if (canInstallExtension) { - const extResult = await confirm({ - message: `Install the Neon extension for ${vscodeEditors.join(", ")}?`, - }); - if (isCancel(extResult)) { - outro("Setup cancelled."); - return; - } - doInstallExtension = extResult; - } } // Auth check before install @@ -460,21 +409,6 @@ async function interactiveInitInner(): Promise { } } - if (doInstallExtension) { - for (const editor of vscodeEditors) { - const extS = spinner(); - extS.start(`Installing Neon extension for ${editor}...`); - const extOk = await installExtension(editor); - if (extOk) { - extS.stop(dim(`Neon extension installed for ${editor} ✓`)); - } else { - extS.stop( - `Extension install failed — install manually from the extensions panel.`, - ); - } - } - } - // Ensure all required skills are present (fills in any missing ones). // detectAgent() returns null in a human terminal (TTY), so fall back // to IDE detection which works regardless of TTY. @@ -517,25 +451,6 @@ function printNextSteps(): void { outro(dim("Have feedback? Email us at feedback@neon.tech")); } -function extensionEditorForAgent(agent: AgentType | undefined): Editor | null { - if (agent === "cursor") return "Cursor"; - if (agent === "vscode") return "VS Code"; - const ide = detectIde(); - if (ide === "Cursor" || ide === "VS Code") return ide; - return null; -} - -function extensionEditorsFor(selected: AgentType[]): Editor[] { - const out: Editor[] = []; - if (selected.includes("cursor")) out.push("Cursor"); - if (selected.includes("vscode")) out.push("VS Code"); - const ide = detectIde(); - if ((ide === "Cursor" || ide === "VS Code") && !out.includes(ide)) { - out.push(ide); - } - return out; -} - async function pickAgents(): Promise { const options = mcpPickerOptions(); const installed = await detectInstalledAgents(); diff --git a/packages/cli/src/init/phases/setup.test.ts b/packages/cli/src/init/phases/setup.test.ts index b001648a..f7dfefec 100644 --- a/packages/cli/src/init/phases/setup.test.ts +++ b/packages/cli/src/init/phases/setup.test.ts @@ -4,27 +4,11 @@ vi.mock("../auth.js", () => ({ isAuthenticated: vi.fn().mockResolvedValue(true), })); -const mockFindEditorCommand = vi.fn().mockResolvedValue("/usr/bin/cursor"); -vi.mock("../extension.js", () => ({ - findEditorCommand: (...args: unknown[]) => mockFindEditorCommand(...args), -})); - const mockExeca = vi.fn().mockResolvedValue({ stdout: "", stderr: "" }); vi.mock("execa", () => ({ execa: (...args: unknown[]) => mockExeca(...args), })); -vi.mock("../inspect.js", () => ({ - inspectProject: vi.fn().mockResolvedValue({ - isVscodeIde: true, - }), -})); - -vi.mock("../vsix.js", () => ({ - NEON_EXTENSION_ID: "databricks.neon-local-connect", - downloadVsix: vi.fn().mockResolvedValue(null), -})); - const mockEnsureNeonctl = vi.fn(); vi.mock("../neonctl.js", () => ({ ensureNeonctl: (...args: unknown[]) => mockEnsureNeonctl(...args), @@ -44,14 +28,6 @@ vi.mock("../skills.js", async (importOriginal) => { }; }); -vi.mock("node:fs/promises", () => ({ - unlink: vi.fn().mockReturnValue(Promise.resolve()), -})); - -// Mock global fetch for Open VSX VSIX download -const mockFetch = vi.fn(); -vi.stubGlobal("fetch", mockFetch); - import { handleSetupPhase } from "./setup.js"; describe("setup phase", () => { @@ -66,19 +42,6 @@ describe("setup phase", () => { path: "/tmp/mcp.json", }); mockEnsureSkills.mockReset().mockResolvedValue(true); - mockFindEditorCommand.mockReset().mockResolvedValue("/usr/bin/cursor"); - mockFetch.mockReset().mockResolvedValue({ - ok: true, - json: () => - Promise.resolve({ - version: "1.0.0", - files: { - download: - "https://open-vsx.org/api/databricks/neon-local-connect/1.0.0/file/databricks.neon-local-connect-1.0.0.vsix", - }, - }), - body: "mock-stream", - }); }); test("returns inspection checks with phased userPreferences and instructions", async () => { @@ -101,6 +64,7 @@ describe("setup phase", () => { expect(checkIds).toContain("agent_type"); expect(checkIds).not.toContain("connection_string"); expect(checkIds).not.toContain("project_stack"); + expect(checkIds).not.toContain("extension_installed"); // reportBack data schema should only require agent-provided fields const dataPlaceholder = result.nextAction.reportBack.args.find( @@ -111,6 +75,7 @@ describe("setup phase", () => { // Should NOT ask for features or stack info anymore. expect(dataPlaceholder).not.toContain("framework"); expect(dataPlaceholder).not.toContain("features"); + expect(dataPlaceholder).not.toContain("installExtension"); // reportBack should use --data flag expect(result.nextAction.reportBack.args[0]).toBe("setup"); @@ -121,6 +86,9 @@ describe("setup phase", () => { expect(prefs.find((p) => p.id === "consent")).toBeUndefined(); expect(prefs.find((p) => p.id === "features")).toBeUndefined(); expect(prefs.find((p) => p.id === "template")).toBeUndefined(); + expect( + prefs.find((p) => p.id === "installExtension"), + ).toBeUndefined(); const modePref = prefs.find((p) => p.id === "mode"); expect(modePref?.phase).toBe("after_checks"); @@ -131,14 +99,6 @@ describe("setup phase", () => { preferenceId: "mode", equals: "customize", }); - - const extensionPref = prefs.find( - (p) => p.id === "installExtension", - ); - expect(extensionPref?.condition).toEqual({ - preferenceId: "mode", - equals: "customize", - }); } }); @@ -146,12 +106,10 @@ describe("setup phase", () => { const result = await handleSetupPhase({ agent: "vscode", mcpConfigured: false, - isVscodeIde: true, mode: "defaults", }); expect(result.phase).toBe("setup"); - // CLI executed the installs expect(result.status).toBe("installed"); expect(result.results).toBeDefined(); @@ -160,7 +118,6 @@ describe("setup phase", () => { expect(resultIds).toContain("neonctl"); expect(resultIds).toContain("install_mcp"); expect(resultIds).toContain("install_skills"); - expect(resultIds).not.toContain("install_extension"); expect(results.every((r) => r.status === "success")).toBe(true); // nextAction hands off to the agent — no more neon init commands. @@ -170,11 +127,9 @@ describe("setup phase", () => { } // MCP is installed via the add-mcp library (not execa); skills via ensureSkillsUpToDate. - // No extension in defaults mode, so execa is never called. expect(mockExeca).not.toHaveBeenCalled(); expect(mockEnsureSkills).toHaveBeenCalled(); - // MCP should be installed at global scope expect(mockInstallMcp).toHaveBeenCalledTimes(1); expect(mockInstallMcp.mock.calls[0][0]).toMatchObject({ scope: "global", @@ -185,7 +140,6 @@ describe("setup phase", () => { const result = await handleSetupPhase({ agent: "claude", mcpConfigured: true, - isVscodeIde: false, mode: "defaults", }); @@ -193,8 +147,6 @@ describe("setup phase", () => { const resultIds = results.map((r) => r.id); expect(resultIds).toContain("skip_mcp"); expect(resultIds).not.toContain("install_mcp"); - // Should not have install_extension since not a vscode IDE - expect(resultIds).not.toContain("install_extension"); expect(result.nextAction.type).toBe("complete"); }); @@ -203,7 +155,6 @@ describe("setup phase", () => { const result = await handleSetupPhase({ agent: "claude", mcpConfigured: false, - isVscodeIde: true, mode: "customize", }); @@ -212,65 +163,13 @@ describe("setup phase", () => { expect(result.nextAction.type).toBe("complete"); }); - test("defaults mode does not install the Cursor extension", async () => { - mockFindEditorCommand.mockResolvedValue( - "/Applications/Cursor.app/Contents/Resources/app/bin/cursor", - ); - - const result = await handleSetupPhase({ - agent: "cursor", - mcpConfigured: false, - isVscodeIde: true, - mode: "defaults", - }); - - const results = result.results as { - id: string; - status: string; - description: string; - manualAction?: boolean; - }[]; - const extResult = results.find((r) => r.id === "install_extension"); - expect(extResult).toBeUndefined(); - }); - - test("custom install falls back to manual when extension installation fails", async () => { - mockFindEditorCommand.mockResolvedValue("/usr/bin/cursor"); - // Make direct install fail - mockExeca.mockImplementation((_cmd: string, args: string[]) => { - if (args?.includes?.("--install-extension")) { - return Promise.reject(new Error("install failed")); - } - return Promise.resolve({ stdout: "", stderr: "" }); - }); - - const result = await handleSetupPhase({ - agent: "cursor", - mcpConfigured: false, - isVscodeIde: true, - mode: "customize", - installExtension: true, - }); - - const results = result.results as { - id: string; - status: string; - manualAction?: boolean; - }[]; - const extResult = results.find((r) => r.id === "install_extension"); - expect(extResult?.status).toBe("success"); - expect(extResult?.manualAction).toBe(true); - }); - test("customize mode with scopes installs MCP at project scope", async () => { const result = await handleSetupPhase({ agent: "claude", mcpConfigured: false, - isVscodeIde: true, mode: "customize", mcpScope: "project", skillsScope: "global", - installExtension: false, }); expect(result.phase).toBe("setup"); @@ -280,8 +179,6 @@ describe("setup phase", () => { const resultIds = results.map((r) => r.id); expect(resultIds).toContain("install_mcp"); expect(resultIds).toContain("install_skills"); - // Extension should NOT be installed since installExtension=false - expect(resultIds).not.toContain("install_extension"); // MCP should be installed at project scope expect(mockInstallMcp).toHaveBeenCalledWith( @@ -293,7 +190,6 @@ describe("setup phase", () => { const result = await handleSetupPhase({ agent: "claude", mcpConfigured: false, - isVscodeIde: false, mcpScope: "project", skillsScope: "project", execute: true, @@ -315,7 +211,6 @@ describe("setup phase", () => { const result = await handleSetupPhase({ agent: "claude", mcpConfigured: false, - isVscodeIde: false, mode: "defaults", }); diff --git a/packages/cli/src/init/phases/setup.ts b/packages/cli/src/init/phases/setup.ts index ad61b914..28123c66 100644 --- a/packages/cli/src/init/phases/setup.ts +++ b/packages/cli/src/init/phases/setup.ts @@ -1,5 +1,3 @@ -import { unlink } from "node:fs/promises"; -import { execa } from "execa"; import { agentSupportsProjectMcp, getSkillsAgentName, @@ -10,14 +8,11 @@ import { isCursorInstalled, isVSCodeInstalled, } from "../detect_agent.js"; -import { findEditorCommand } from "../extension.js"; import { handoffAction } from "../handoff.js"; -import { inspectProject } from "../inspect.js"; import { installNeonMcpServer } from "../install_mcp.js"; import { ensureNeonctl } from "../neonctl.js"; import { ensureSkillsUpToDate } from "../skills.js"; -import type { Editor, PhaseResponse } from "../types.js"; -import { downloadVsix, NEON_EXTENSION_ID } from "../vsix.js"; +import type { PhaseResponse } from "../types.js"; export type SetupPhaseOptions = { agent?: string; @@ -26,19 +21,17 @@ export type SetupPhaseOptions = { // Inspection results — pre-filled by orchestrator or reported by agent mcpConfigured?: boolean | null; skillsInstalled?: boolean | null; - isVscodeIde?: boolean | null; // User preferences (also used for pre-detected scope from inspection) mode?: string; mcpScope?: string; skillsScope?: string; - installExtension?: boolean; // Execution flags execute?: boolean; }; /** * Setup phase: inspects tooling state, collects install preferences, then - * batches the MCP server / skills / extension installs together. + * batches the MCP server / skills installs together. * * With --data JSON, the agent sends inspection results AND user preferences in * a single call, so the CLI can go straight to installation. Once install @@ -48,40 +41,15 @@ export type SetupPhaseOptions = { export async function handleSetupPhase( options: SetupPhaseOptions, ): Promise { - // --execute: run the batched installation (legacy path) - if (options.execute) { - return executeBatchedInstallation(await mergeCliInspection(options)); - } - - // Treat any explicit mode value that isn't "customize"/"custom" as defaults. - if ( - options.mode && - options.mode !== "customize" && - options.mode !== "custom" - ) { - const merged = await mergeCliInspection(options); - return executeBatchedInstallation({ - ...merged, - mcpScope: merged.mcpScope ?? "global", - skillsScope: merged.skillsScope ?? "project", - installExtension: false, - }); - } - - // User chose "customize" (also accept "custom" — agents sometimes truncate) - if (options.mode === "customize" || options.mode === "custom") { + if (options.execute || options.mode) { const merged = await mergeCliInspection(options); - const shouldInstallExt = - merged.installExtension ?? isVscodeBasedIde(merged); return executeBatchedInstallation({ ...merged, mcpScope: merged.mcpScope ?? "global", skillsScope: merged.skillsScope ?? "project", - installExtension: shouldInstallExt, }); } - // Default: send inspection checks with install preferences (all in one response) return buildBulkInspection(options); } @@ -128,9 +96,9 @@ async function buildBulkInspection( "GROUPING: Preferences that share the same `group` field should be presented together in a single message (e.g. list all customize options at once and let the user answer them together). Preferences without a `group` must be asked individually.", "", detectedIde - ? `The CLI has detected the IDE as: ${detectedIde.toLowerCase()}. Include this as the "ide" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor/VS Code/Windsurf, the extension IS applicable even if you are Claude Code.` + ? `The CLI has detected the IDE as: ${detectedIde.toLowerCase()}. Include this as the "ide" field in your reportBack data.` : installedEditors.length > 0 - ? `No IDE detected, but the following editors are installed: ${installedEditors.join(", ")}. The "installedEditors" field in this response lists them. If the user wants the extension installed, ask which editor to install it for and include that as the "ide" field in your reportBack data. If not, set "ide" to "none".` + ? `No IDE detected, but the following editors are installed: ${installedEditors.join(", ")}. The "installedEditors" field in this response lists them. Set "ide" to the editor name or "none" in your reportBack data.` : `No IDE or supported editors detected. Set "ide" to "none" in your reportBack data.`, "", "After all questions are answered, call reportBack with a single --data JSON containing: agent, ide, mcpConfigured, and all preference answers. The CLI will inspect the project and merge results automatically.", @@ -159,19 +127,6 @@ async function buildBulkInspection( "Report your own agent identifier — this is used to configure the MCP server for the correct tool", ], }, - ...(detectedIde - ? [ - { - id: "extension_installed", - description: - "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)", - lookFor: [ - "Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", - "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question", - ], - }, - ] - : []), ], userPreferences: [ // Only show defaults/customize when there's something to customize: @@ -185,7 +140,7 @@ async function buildBulkInspection( options: [ { value: "defaults", - label: "Use defaults (MCP: global, skills:project-level, install extension later) (Recommended)", + label: "Use defaults (MCP: global, skills: project-level) (Recommended)", }, { value: "customize", @@ -250,21 +205,6 @@ async function buildBulkInspection( }, ] : []), - { - id: "installExtension", - question: - "Install the Neon editor extension for local database browsing?", - phase: "after_checks" as const, - options: [ - { value: "true", label: "Yes" }, - { value: "false", label: "No" }, - ], - default: "true", - context: - "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code-based IDEs (VS Code, Cursor, Windsurf). SKIP this question if the user is NOT in a VS Code-based IDE, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.", - condition: { preferenceId: "mode", equals: "customize" }, - group: "customize", - }, ], reportBack: { type: "run_neon_init", @@ -285,10 +225,7 @@ async function buildBulkInspection( const mcpField = hasModeQuestion ? ", mcpScope?: 'global'|'project'|'none'" : ""; - const extField = hasModeQuestion - ? ", installExtension?: bool" - : ""; - return ``; + return ``; })(), ], }, @@ -308,7 +245,7 @@ type InstallResult = { }; /** - * Executes the batched installation of MCP server, skills, and extension. + * Executes the batched installation of MCP server and skills. * Runs commands directly in the CLI process — the agent does NOT run these. * Once install finishes, hands off to the agent (see {@link handoffAction}). */ @@ -318,11 +255,9 @@ async function executeBatchedInstallation( const mcpScope = options.mcpScope ?? "global"; const agentId = options.agent ?? "cursor"; const mcpAgentId = resolveAddMcpAgentId(agentId); - const installExt = options.installExtension === true; const results: InstallResult[] = []; - // Step 1: Ensure the Neon CLI is installed and up to date const neonctlResult = await ensureNeonctl(); switch (neonctlResult.status) { case "already_current": @@ -356,7 +291,6 @@ async function executeBatchedInstallation( break; } - // Step 2: Install MCP server (skip if already configured) const isCursor = mcpAgentId === "cursor" || options.ide?.toLowerCase() === "cursor" || @@ -432,7 +366,6 @@ async function executeBatchedInstallation( } } - // Step 3: Install/update skills const skillsScope = (options.skillsScope ?? "project") as | "global" | "project"; @@ -464,14 +397,6 @@ async function executeBatchedInstallation( }); } - // Step 4: Install editor extension if requested - // Use the agent-reported IDE (not agent identity) — e.g. Claude Code running in - // Cursor should install the extension for Cursor, not skip it. - if (installExt) { - const extResult = await installExtensionForIde(options.ide ?? agentId); - results.push(extResult); - } - const allSucceeded = results.every((r) => r.status === "success"); return { @@ -482,156 +407,10 @@ async function executeBatchedInstallation( }; } -/** - * Fills in the IDE-related inspection fields when the agent didn't report them. - * Agent-reported data (mcpConfigured, agent, mode, scopes) is preserved. - */ async function mergeCliInspection( options: SetupPhaseOptions, ): Promise { - // If the agent already told us the IDE, no need to re-inspect. - if (options.ide !== undefined && options.isVscodeIde !== undefined) { - return options; - } - - const inspection = await inspectProject([ - { id: "ide_type", description: "", lookFor: [] }, - ]); - - const ide = - options.ide?.toLowerCase().replace(/\s+/g, "-") || - detectIde()?.toLowerCase().replace(/\s+/g, "-") || - undefined; - - return { - ...options, - ide, - isVscodeIde: - options.isVscodeIde ?? - (inspection.isVscodeIde as boolean | undefined), - }; -} - -function isVscodeBasedIde(options: SetupPhaseOptions): boolean { - if (options.ide) { - const ide = options.ide.toLowerCase(); - return ( - ide === "cursor" || - ide === "vscode" || - ide === "vs-code" || - ide === "windsurf" - ); - } - return options.isVscodeIde === true; -} - -/** - * Resolves which IDE to install the extension for. - * Accepts the agent-reported IDE value (preferred), the agent ID, or - * falls back to env-var detection. - */ -function resolveEditorForExtension(ideOrAgentId: string): Editor | null { - // Map known IDE/agent identifiers to Editor types - switch (ideOrAgentId.toLowerCase()) { - case "cursor": - return "Cursor"; - case "vscode": - case "vs-code": - case "copilot": - case "github-copilot": - case "github-copilot-cli": - return "VS Code"; - default: - break; - } - - // Fall back to env-var detection - const ide = detectIde(); - if (ide === "Cursor" || ide === "VS Code") return ide; - - return null; -} - -const MANUAL_INSTALL_MSG = `Search for "Neon" in the extensions panel (Cmd+Shift+X / Ctrl+Shift+X) and install "Neon Local Connect" by Databricks.`; - -/** - * Installs the Neon extension for the detected IDE. - * - * Uses env-var detection to determine the IDE (not the agent identity), - * so Claude Code running in Cursor correctly installs for Cursor. - * - * Strategy: - * 1. Try ` --install-extension ` directly (uses editor's configured marketplace) - * 2. If that fails, download .vsix (from proxy or Open VSX) and install via local file - * 3. If all else fails: return manual install instructions - */ -async function installExtensionForIde(agentId: string): Promise { - const editorType = resolveEditorForExtension(agentId); - if (!editorType) { - return { - id: "install_extension", - description: MANUAL_INSTALL_MSG, - status: "success", - manualAction: true, - }; - } - - const editorCmd = await findEditorCommand(editorType); - if (!editorCmd) { - return { - id: "install_extension", - description: MANUAL_INSTALL_MSG, - status: "success", - manualAction: true, - }; - } - - // Try direct marketplace install first (works if editor has marketplace configured) - try { - await execa(editorCmd, ["--install-extension", NEON_EXTENSION_ID], { - stdio: "pipe", - timeout: 60000, - }); - return { - id: "install_extension", - description: `Installed Neon extension for ${editorType}`, - status: "success", - }; - } catch { - // Fall through to VSIX download approach - } - - // Download .vsix and install locally - const vsixPath = await downloadVsix(); - if (!vsixPath) { - return { - id: "install_extension", - description: MANUAL_INSTALL_MSG, - status: "success", - manualAction: true, - }; - } - - try { - await execa(editorCmd, ["--install-extension", vsixPath], { - stdio: "pipe", - timeout: 60000, - }); - return { - id: "install_extension", - description: `Installed Neon extension for ${editorType}`, - status: "success", - }; - } catch { - return { - id: "install_extension", - description: MANUAL_INSTALL_MSG, - status: "success", - manualAction: true, - }; - } finally { - try { - await unlink(vsixPath); - } catch {} - } + if (options.ide !== undefined) return options; + const ide = detectIde()?.toLowerCase().replace(/\s+/g, "-") || undefined; + return { ...options, ide }; } diff --git a/packages/cli/src/init/route_command.test.ts b/packages/cli/src/init/route_command.test.ts index c4f640a8..948955c6 100644 --- a/packages/cli/src/init/route_command.test.ts +++ b/packages/cli/src/init/route_command.test.ts @@ -1,16 +1,11 @@ import { describe, expect, test, vi } from "vitest"; import { routeDataStep } from "./route_command.js"; -// Mock execa so phase handlers never shell out (skills install, extension install, etc.) +// Mock execa so phase handlers never shell out (skills install, etc.) vi.mock("execa", () => ({ execa: vi.fn().mockResolvedValue({ stdout: "", stderr: "", exitCode: 0 }), })); -// Mock extension helpers -vi.mock("./extension.js", () => ({ - findEditorCommand: vi.fn().mockResolvedValue(null), -})); - // Mock skills evergreen check vi.mock("./skills.js", async (importOriginal) => { const actual = (await importOriginal()) as Record; diff --git a/packages/cli/src/init/types.ts b/packages/cli/src/init/types.ts index 11e44cf8..f1a4c4df 100644 --- a/packages/cli/src/init/types.ts +++ b/packages/cli/src/init/types.ts @@ -1,5 +1,3 @@ -export type Editor = "Cursor" | "VS Code"; - // --------------------------------------------------------------------------- // Agent-driven state machine protocol // --------------------------------------------------------------------------- diff --git a/packages/cli/src/init/vsix.ts b/packages/cli/src/init/vsix.ts deleted file mode 100644 index 0332fa9f..00000000 --- a/packages/cli/src/init/vsix.ts +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Shared VSIX download utilities for extension installation. - * Supports corporate proxy via NEON_VSX_GALLERY_URL env var. - */ -import { createWriteStream } from "node:fs"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; -import { pipeline } from "node:stream/promises"; -import { INTERNAL_VSX_GALLERY } from "./build_config.js"; - -export const NEON_EXTENSION_ID = "databricks.neon-local-connect"; -const OPEN_VSX_API = "https://open-vsx.org/api"; - -/** - * Downloads a .vsix file for the Neon extension. - * - * Strategy: - * 1. If NEON_VSX_GALLERY_URL is set, download from the corporate proxy gallery - * 2. Otherwise, download from the public Open VSX API - * - * Returns the path to the temp .vsix file, or null on failure. - */ -export async function downloadVsix(): Promise { - // Runtime env var takes priority, then build-time baked value - const proxyGallery = - process.env.NEON_VSX_GALLERY_URL || INTERNAL_VSX_GALLERY || ""; - if (proxyGallery) { - return downloadFromGallery(proxyGallery); - } - return downloadFromOpenVsx(); -} - -/** - * Downloads from a VS Code marketplace-compatible gallery API (corporate proxy). - * Uses the VS Code extensionquery POST API to find the VSIX download URL. - */ -async function downloadFromGallery(galleryUrl: string): Promise { - const [publisher, name] = NEON_EXTENSION_ID.split("."); - const baseUrl = galleryUrl.replace(/\/+$/, ""); - const queryUrl = `${baseUrl}/extensionquery`; - - try { - // Query the marketplace API for the extension - const queryRes = await fetch(queryUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json;api-version=6.1-preview.1", - }, - body: JSON.stringify({ - filters: [ - { - criteria: [ - { filterType: 7, value: `${publisher}.${name}` }, - ], - }, - ], - flags: 914, - }), - signal: AbortSignal.timeout(15000), - }); - - if (!queryRes.ok) return null; - - const data = (await queryRes.json()) as { - results?: { - extensions?: { - versions?: { - files?: { assetType: string; source: string }[]; - }[]; - }[]; - }[]; - }; - - // Find the VSIX download URL from the response - const extension = data.results?.[0]?.extensions?.[0]; - const latestVersion = extension?.versions?.[0]; - const vsixFile = latestVersion?.files?.find( - (f) => - f.assetType === "Microsoft.VisualStudio.Services.VSIXPackage", - ); - - if (!vsixFile?.source) return null; - - // Download the VSIX - const vsixRes = await fetch(vsixFile.source, { - signal: AbortSignal.timeout(30000), - redirect: "follow", - }); - if (!vsixRes.ok || !vsixRes.body) return null; - - const tmpPath = join(tmpdir(), `${NEON_EXTENSION_ID}-proxy.vsix`); - const fileStream = createWriteStream(tmpPath); - await pipeline( - vsixRes.body as unknown as NodeJS.ReadableStream, - fileStream, - ); - return tmpPath; - } catch { - return null; - } -} - -/** - * Downloads from the public Open VSX API. - */ -async function downloadFromOpenVsx(): Promise { - const [publisher, name] = NEON_EXTENSION_ID.split("."); - const metaUrl = `${OPEN_VSX_API}/${publisher}/${name}/latest`; - - try { - const metaRes = await fetch(metaUrl, { - signal: AbortSignal.timeout(10000), - }); - if (!metaRes.ok) return null; - - const meta = (await metaRes.json()) as { - files?: { download?: string }; - version?: string; - }; - const downloadUrl = meta.files?.download; - if (!downloadUrl) return null; - - const vsixRes = await fetch(downloadUrl, { - signal: AbortSignal.timeout(30000), - }); - if (!vsixRes.ok || !vsixRes.body) return null; - - const tmpPath = join( - tmpdir(), - `${NEON_EXTENSION_ID}-${meta.version ?? "latest"}.vsix`, - ); - const fileStream = createWriteStream(tmpPath); - await pipeline( - vsixRes.body as unknown as NodeJS.ReadableStream, - fileStream, - ); - return tmpPath; - } catch { - return null; - } -} From 05562363004f2b048524eb03290ca6c25946107e Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Tue, 25 Aug 2026 21:04:08 -0700 Subject: [PATCH 13/13] Pin init e2e to the remaining agent protocol Init skips global auth, so inspect-only cases must not send the harness API key. Ask setup without installing, and keep getting-started unknown. --- packages/cli/e2e/init.e2e.test.ts | 90 ++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/packages/cli/e2e/init.e2e.test.ts b/packages/cli/e2e/init.e2e.test.ts index c45b8a58..c062a688 100644 --- a/packages/cli/e2e/init.e2e.test.ts +++ b/packages/cli/e2e/init.e2e.test.ts @@ -11,23 +11,19 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { runCli } from "./helpers.js"; /** - * `neon init --agent` is a protocol rather than a command that does the work: it answers with - * what an agent should do next. `neon init` now stops at installing the Neon tooling (MCP - * server, agent skills, editor extension) and hands the rest off to the agent — it no longer - * selects an org/project, links, or pulls env. The snapshot tests pin the emitted strings - * exactly; this suite guards the two things a live run must still get right: the CLI accepts - * the steps it hands out, and merely *asking* what to do installs nothing into the project. + * `neon init --agent` answers with the next step. Snapshot tests pin the + * strings; this suite checks that asking does not install into the project, + * and that deleted steps stay unknown. */ /** - * Every variable `detectAgent` and `detectIde` read (`init/detect_agent.ts`). Clearing them - * keeps the run agentless — which matters for determinism, and because with an agent id the - * setup execution path would fetch the skills CLI and install skills into the home and working - * directories. The default `setup` step under test never executes installs, but the scrub - * keeps that guarantee honest if that ever changes. + * Every variable `detectAgent` and `detectIde` read. Clearing them keeps the + * run agentless: with an agent id the setup execution path would fetch the + * skills CLI and install into home and cwd. The default `setup` step under + * test never executes installs; the scrub keeps that honest if that changes. * - * The value scan at `detect_agent.ts:48` only runs inside the VS Code branch, which these - * variables gate, so clearing them is sufficient. + * The value scan in `detectIde` only runs inside the VS Code branch, which + * these variables gate. */ const NO_AGENT_ENV: Record = { CLAUDECODE: undefined, @@ -51,55 +47,89 @@ type PhaseResponse = { status: string; nextAction?: { type: string; + reportBack?: { type?: string; command?: string }; [key: string]: unknown; }; }; describe.sequential("e2e — neon init emits a protocol the CLI still accepts", () => { - /** - * A throwaway working directory: the phase must not write anything here just for being - * asked what to do. Removed in teardown. - */ - const workdir = mkdtempSync(join(tmpdir(), "neon-init-e2e-")); + const root = mkdtempSync(join(tmpdir(), "neon-init-e2e-")); + const workdir = join(root, "app"); const contextFile = join(workdir, ".neon"); + const home = join(root, "home"); + const isolated = { + env: { ...NO_AGENT_ENV, HOME: home }, + cwd: workdir, + contextFile, + json: false, + apiKey: null, + }; beforeAll(() => { - // Make the workdir a project root so inspection doesn't climb into $TMPDIR's ancestors. + mkdirSync(workdir); + mkdirSync(home); mkdirSync(join(workdir, ".git")); writeFileSync(join(workdir, "pnpm-lock.yaml"), ""); }); afterAll(() => { - rmSync(workdir, { recursive: true, force: true }); + rmSync(root, { recursive: true, force: true }); }); it("answers the setup step with an agent_check and installs nothing by asking", async () => { const phase = await runCli( ["init", "--agent", "--data", JSON.stringify({ step: "setup" })], - { env: NO_AGENT_ENV, cwd: workdir, contextFile }, + isolated, ); expect(phase.code, phase.stderr).toBe(0); const response = JSON.parse(phase.stdout) as PhaseResponse; expect(response.phase).toBe("setup"); expect(response.nextAction?.type).toBe("agent_check"); + expect(response.nextAction?.reportBack?.type).toBe("run_shell_command"); + expect(response.nextAction?.reportBack?.command).toMatch( + /init --agent/, + ); + expect(response.nextAction?.reportBack?.command).toContain( + '"step":"setup"', + ); - // Asking the phase what to do must not install anything into the working directory. expect(existsSync(join(workdir, ".agents"))).toBe(false); expect(existsSync(join(workdir, "skills-lock.json"))).toBe(false); }); + it("rejects the removed getting-started step", async () => { + const result = await runCli( + [ + "init", + "--agent", + "--data", + JSON.stringify({ step: "getting-started" }), + ], + isolated, + ); + + expect(result.code).not.toBe(0); + const failure = JSON.parse(result.stdout) as { + success: boolean; + error: string; + }; + expect(failure.success).toBe(false); + expect(failure.error).toContain('Unknown step: "getting-started"'); + }); + it("reports an unknown step instead of guessing", async () => { - const result = await runCli([ - "init", - "--agent", - "--data", - JSON.stringify({ step: "not-a-real-step" }), - ]); + const result = await runCli( + [ + "init", + "--agent", + "--data", + JSON.stringify({ step: "not-a-real-step" }), + ], + isolated, + ); expect(result.code).not.toBe(0); - // Agent mode answers in JSON even when it fails, so an agent parsing stdout gets a - // reason rather than a stack trace on stderr. const failure = JSON.parse(result.stdout) as { success: boolean; error: string;