neon-init 0.20.0 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"setup.js","names":[],"sources":["../../../src/lib/phases/setup.ts"],"sourcesContent":["import { writeFileSync } from \"node:fs\";\nimport { unlink } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { execa } from \"execa\";\nimport { resolveAddMcpAgentId } from \"../agents.js\";\nimport {\n\tFALLBACK_TEMPLATES,\n\tfetchTemplates,\n\tfindTemplate,\n\ttype NeonFeature,\n\tscaffoldTemplate,\n} from \"../bootstrap.js\";\nimport {\n\tdetectIde,\n\tisCursorInstalled,\n\tisVSCodeInstalled,\n} from \"../detect-agent.js\";\nimport { findEditorCommand } from \"../extension.js\";\nimport { inspectProject } from \"../inspect.js\";\nimport { ensureNeonctl } from \"../neonctl.js\";\nimport { ensureSkillsUpToDate } from \"../skills.js\";\nimport type { Editor, PhaseResponse } from \"../types.js\";\nimport { downloadVsix, NEON_EXTENSION_ID } from \"../vsix.js\";\n\nexport interface SetupPhaseOptions {\n\tagent?: string;\n\t/** The IDE/editor the user is running in (e.g. \"cursor\", \"vscode\") — reported by agent */\n\tide?: string;\n\t/** Enable preview skills (neon-object-storage, neon-functions, neon-ai-gateway) */\n\tpreview?: boolean;\n\t/** Whether the directory already contains an application */\n\thasApp?: boolean;\n\t/** Template ID to scaffold (when bootstrapping a new project) */\n\ttemplate?: string;\n\t/** Neon features required by the selected template */\n\ttemplateRequires?: NeonFeature[];\n\t/** Neon features selected by the user (brownfield flows) */\n\tfeatures?: NeonFeature[];\n\t// Inspection results — pre-filled by orchestrator or reported by agent\n\tmcpConfigured?: boolean | null;\n\tskillsInstalled?: boolean | null;\n\tconnectionString?: boolean | null;\n\tconnectionParams?: string; // JSON with host/dbname/etc if found\n\tframework?: string;\n\torm?: string;\n\tmigrationTool?: string;\n\tmigrationDir?: string;\n\tisVscodeIde?: boolean | null;\n\t// User preferences (also used for pre-detected scope from inspection)\n\tmode?: string;\n\tmcpScope?: string;\n\tskillsScope?: string;\n\tinstallExtension?: boolean;\n\t// Execution flags\n\texecute?: boolean;\n}\n\n/**\n * Comprehensive setup phase: inspects repo state, collects user preferences,\n * then batches all installation commands together.\n *\n * With --data JSON, the agent sends inspection results AND user preferences\n * in a single call, so the CLI can go straight to installation.\n */\nexport async function handleSetupPhase(\n\toptions: SetupPhaseOptions,\n): Promise<PhaseResponse> {\n\t// Parse features from comma-separated string (e.g. \"database,auth\" from agent --data)\n\tif (typeof options.features === \"string\") {\n\t\toptions.features = (options.features as unknown as string)\n\t\t\t.split(\",\")\n\t\t\t.map((f) => f.trim()) as NeonFeature[];\n\t}\n\n\t// Treat \"none\" as no template selected\n\tconst templateWasAnswered = options.template !== undefined;\n\tif (options.template === \"none\") {\n\t\toptions.template = undefined;\n\t}\n\n\t// Resolve template requirements if a template was selected but requires not yet populated\n\tif (options.template && !options.templateRequires) {\n\t\tconst templates = await fetchTemplates();\n\t\tconst selected = templates.find((t) => t.id === options.template);\n\t\tif (selected) {\n\t\t\toptions.templateRequires = selected.requires;\n\t\t}\n\t}\n\n\t// --execute: run the batched installation (legacy path)\n\tif (options.execute) {\n\t\treturn executeBatchedInstallation(await mergeCliInspection(options));\n\t}\n\n\t// Treat any explicit mode value that isn't \"customize\"/\"custom\" as defaults.\n\t// Also treat as defaults when mode is missing but agent reported back\n\t// (template was answered, nothing left to customize).\n\tconst hasReportedBack = options.mode || templateWasAnswered;\n\tif (\n\t\thasReportedBack &&\n\t\toptions.mode !== \"customize\" &&\n\t\toptions.mode !== \"custom\"\n\t) {\n\t\tconst merged = await mergeCliInspection(options);\n\t\tconst shouldInstallExt =\n\t\t\tmerged.installExtension ?? isVscodeBasedIde(merged);\n\t\treturn executeBatchedInstallation({\n\t\t\t...merged,\n\t\t\tmcpScope: merged.mcpScope ?? \"global\",\n\t\t\tskillsScope: merged.skillsScope ?? \"project\",\n\t\t\tinstallExtension: shouldInstallExt,\n\t\t});\n\t}\n\n\t// User chose \"customize\" (also accept \"custom\" — agents sometimes truncate)\n\tif (options.mode === \"customize\" || options.mode === \"custom\") {\n\t\tconst merged = await mergeCliInspection(options);\n\t\tconst shouldInstallExt =\n\t\t\tmerged.installExtension ?? isVscodeBasedIde(merged);\n\t\treturn executeBatchedInstallation({\n\t\t\t...merged,\n\t\t\tmcpScope: merged.mcpScope ?? \"global\",\n\t\t\tskillsScope: merged.skillsScope ?? \"project\",\n\t\t\tinstallExtension: shouldInstallExt,\n\t\t});\n\t}\n\n\t// Default: send inspection checks with user preferences (all in one response)\n\treturn buildBulkInspection(options);\n}\n\nfunction buildTemplatePreference(\n\ttemplates: {\n\t\tid: string;\n\t\ttitle: string;\n\t\tdescription: string;\n\t\ttools?: string[];\n\t}[],\n) {\n\treturn [\n\t\t{\n\t\t\tid: \"template\",\n\t\t\tquestion:\n\t\t\t\t\"No application was detected in this directory. Would you like to scaffold a new project from a template?\",\n\t\t\tphase: \"before_checks\" as const,\n\t\t\toptions: [\n\t\t\t\t...templates.map((t) => {\n\t\t\t\t\tconst tools =\n\t\t\t\t\t\tt.tools && t.tools.length > 0\n\t\t\t\t\t\t\t? ` (${t.tools.join(\", \")})`\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\treturn {\n\t\t\t\t\t\tvalue: t.id,\n\t\t\t\t\t\tlabel: `${t.title}${tools} — ${t.description}`,\n\t\t\t\t\t};\n\t\t\t\t}),\n\t\t\t\t{\n\t\t\t\t\tvalue: \"none\",\n\t\t\t\t\tlabel: \"No thanks — continue without scaffolding\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tdefault: \"none\",\n\t\t},\n\t];\n}\n\nasync function buildBulkInspection(\n\toptions: SetupPhaseOptions,\n): Promise<PhaseResponse> {\n\tconst hasApp = options.hasApp !== false;\n\tconst detectedIde = detectIde();\n\n\t// If no IDE detected (e.g. standalone terminal), check what's installed\n\tconst installedEditors: string[] = [];\n\tif (!detectedIde) {\n\t\tif (isCursorInstalled()) installedEditors.push(\"cursor\");\n\t\tif (isVSCodeInstalled()) installedEditors.push(\"vscode\");\n\t}\n\n\t// Fetch available templates when no app is detected\n\tlet templatePreferences: ReturnType<typeof buildTemplatePreference> = [];\n\tif (!hasApp) {\n\t\tlet templates = FALLBACK_TEMPLATES;\n\t\ttry {\n\t\t\tconst fetched = await fetchTemplates();\n\t\t\tif (fetched && fetched.length > 0) templates = fetched;\n\t\t} catch {}\n\t\ttemplatePreferences = buildTemplatePreference(templates);\n\t}\n\n\treturn {\n\t\tphase: \"setup\",\n\t\tstatus: hasApp ? \"pending\" : \"bootstrap_needed\",\n\t\tdetectedIde: detectedIde?.toLowerCase() ?? null,\n\t\tinstalledEditors: installedEditors.length > 0 ? installedEditors : null,\n\t\t// Pre-detected state from filesystem inspection\n\t\tmcpConfigured: options.mcpConfigured ?? null,\n\t\tmcpScope: options.mcpScope ?? null,\n\t\tskillsInstalled: options.skillsInstalled ?? null,\n\t\tskillsScope: options.skillsScope ?? null,\n\t\tnextAction: {\n\t\t\ttype: \"agent_check\",\n\t\t\tinstructions: [\n\t\t\t\t\"IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\",\n\t\t\t\t\"\",\n\t\t\t\thasApp\n\t\t\t\t\t? \"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.\"\n\t\t\t\t\t: \"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.\",\n\t\t\t\t\"\",\n\t\t\t\t`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.`,\n\t\t\t\t\"\",\n\t\t\t\t\"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.\",\n\t\t\t\t\"\",\n\t\t\t\t\"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.\",\n\t\t\t\t\"\",\n\t\t\t\tdetectedIde\n\t\t\t\t\t? `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.`\n\t\t\t\t\t: installedEditors.length > 0\n\t\t\t\t\t\t? `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\".`\n\t\t\t\t\t\t: `No IDE or supported editors detected. Set \"ide\" to \"none\" in your reportBack data.`,\n\t\t\t\t\"\",\n\t\t\t\t\"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.\",\n\t\t\t].join(\"\\n\"),\n\t\t\tchecks: [\n\t\t\t\t{\n\t\t\t\t\tid: \"neonctl\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"The neonctl CLI will be installed or updated automatically (no action needed from the agent)\",\n\t\t\t\t\tlookFor: [],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"mcp_server\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Check if the Neon MCP server is already configured in your MCP server list\",\n\t\t\t\t\tlookFor: [\n\t\t\t\t\t\t\"An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"agent_type\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Identify which coding agent is running this command\",\n\t\t\t\t\tlookFor: [\n\t\t\t\t\t\t\"Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity\",\n\t\t\t\t\t\t\"Report your own agent identifier — this is used to configure the MCP server for the correct tool\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\t...(detectedIde\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tid: \"extension_installed\",\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\"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)\",\n\t\t\t\t\t\t\t\tlookFor: [\n\t\t\t\t\t\t\t\t\t\"Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'\",\n\t\t\t\t\t\t\t\t\t\"If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question\",\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: []),\n\t\t\t],\n\t\t\tuserPreferences: [\n\t\t\t\t...templatePreferences,\n\t\t\t\t// For brownfield flows, ask which Neon features to enable\n\t\t\t\t...(hasApp\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tid: \"features\",\n\t\t\t\t\t\t\t\tquestion:\n\t\t\t\t\t\t\t\t\t\"Which Neon features would you like to enable for this project?\",\n\t\t\t\t\t\t\t\tphase: \"after_checks\" as const,\n\t\t\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvalue: \"database\",\n\t\t\t\t\t\t\t\t\t\tlabel: \"Database (always included)\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvalue: \"database,auth\",\n\t\t\t\t\t\t\t\t\t\tlabel: \"Database + Neon Auth (adds authentication via Neon)\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdefault: \"database\",\n\t\t\t\t\t\t\t\tcontext:\n\t\t\t\t\t\t\t\t\t\"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.\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: []),\n\t\t\t\t// Only show defaults/customize when there's something to customize:\n\t\t\t\t// MCP not configured, skills need scope choice, or extension not detected.\n\t\t\t\t...(() => {\n\t\t\t\t\tconst isPartialSkills = String(\n\t\t\t\t\t\toptions.skillsScope ?? \"\",\n\t\t\t\t\t).includes(\"partial\");\n\t\t\t\t\tconst needsMcpChoice = !options.mcpConfigured;\n\t\t\t\t\tconst needsSkillsChoice =\n\t\t\t\t\t\t!options.skillsInstalled && !isPartialSkills;\n\t\t\t\t\tconst hasCustomizableOptions =\n\t\t\t\t\t\tneedsMcpChoice || needsSkillsChoice;\n\t\t\t\t\tif (!hasCustomizableOptions) return [];\n\t\t\t\t\treturn [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: \"mode\",\n\t\t\t\t\t\t\tquestion: \"Use default settings or customize?\",\n\t\t\t\t\t\t\tphase: \"after_checks\" as const,\n\t\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tvalue: \"defaults\",\n\t\t\t\t\t\t\t\t\tlabel: hasApp\n\t\t\t\t\t\t\t\t\t\t? \"Use defaults (neonctl CLI, MCP: global, skills: project-level, extension if applicable — already-configured components will be skipped)\"\n\t\t\t\t\t\t\t\t\t\t: \"Use defaults (neonctl CLI, MCP: global, extension if applicable — skills included in template)\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tvalue: \"customize\",\n\t\t\t\t\t\t\t\t\tlabel: \"Customize installation settings\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdefault: \"defaults\",\n\t\t\t\t\t\t},\n\t\t\t\t\t];\n\t\t\t\t})(),\n\t\t\t\t{\n\t\t\t\t\tid: \"mcpScope\",\n\t\t\t\t\tquestion: \"Where should the Neon MCP server be configured?\",\n\t\t\t\t\tcontext:\n\t\t\t\t\t\t\"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.\",\n\t\t\t\t\tphase: \"after_checks\",\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"global\",\n\t\t\t\t\t\t\tlabel: \"Global (available in all projects)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"project\",\n\t\t\t\t\t\t\tlabel: \"Project-level (scoped to this project only)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"none\",\n\t\t\t\t\t\t\tlabel: \"Skip — do not install the MCP server\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdefault: \"global\",\n\t\t\t\t\tcondition: { preferenceId: \"mode\", equals: \"customize\" },\n\t\t\t\t\tgroup: \"customize\",\n\t\t\t\t},\n\t\t\t\t// Show skills scope when skills aren't detected and no partial install exists.\n\t\t\t\t// Partial installations are auto-completed to the same scope silently.\n\t\t\t\t...(!options.skillsInstalled &&\n\t\t\t\t!String(options.skillsScope ?? \"\").includes(\"partial\")\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tid: \"skillsScope\",\n\t\t\t\t\t\t\t\tquestion:\n\t\t\t\t\t\t\t\t\t\"Where should Neon agent skills be installed?\",\n\t\t\t\t\t\t\t\tcontext:\n\t\t\t\t\t\t\t\t\t\"Only ask if skills are not already installed.\",\n\t\t\t\t\t\t\t\tphase: \"after_checks\" as const,\n\t\t\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvalue: \"global\",\n\t\t\t\t\t\t\t\t\t\tlabel: \"Global (available in all projects)\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvalue: \"project\",\n\t\t\t\t\t\t\t\t\t\tlabel: \"Project-level (scoped to this project only)\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdefault: \"project\",\n\t\t\t\t\t\t\t\tcondition: {\n\t\t\t\t\t\t\t\t\tpreferenceId: \"mode\",\n\t\t\t\t\t\t\t\t\tequals: \"customize\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tgroup: \"customize\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: []),\n\t\t\t\t{\n\t\t\t\t\tid: \"installExtension\",\n\t\t\t\t\tquestion:\n\t\t\t\t\t\t\"Install the Neon editor extension for local database browsing?\",\n\t\t\t\t\tphase: \"after_checks\",\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t{ value: \"true\", label: \"Yes\" },\n\t\t\t\t\t\t{ value: \"false\", label: \"No\" },\n\t\t\t\t\t],\n\t\t\t\t\tdefault: \"true\",\n\t\t\t\t\tcontext:\n\t\t\t\t\t\t\"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.\",\n\t\t\t\t\tcondition: { preferenceId: \"mode\", equals: \"customize\" },\n\t\t\t\t\tgroup: \"customize\",\n\t\t\t\t},\n\t\t\t],\n\t\t\treportBack: {\n\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\targs: [\n\t\t\t\t\t\"setup\",\n\t\t\t\t\t\"--json\",\n\t\t\t\t\t\"--data\",\n\t\t\t\t\t(() => {\n\t\t\t\t\t\tconst partialScope = String(\n\t\t\t\t\t\t\toptions.skillsScope ?? \"\",\n\t\t\t\t\t\t).replace(\"-partial\", \"\");\n\t\t\t\t\t\tconst hasPartial = String(\n\t\t\t\t\t\t\toptions.skillsScope ?? \"\",\n\t\t\t\t\t\t).includes(\"partial\");\n\t\t\t\t\t\tconst previewFlag = options.preview\n\t\t\t\t\t\t\t? \", preview: true\"\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\tconst needsMcpChoice = !options.mcpConfigured;\n\t\t\t\t\t\tconst needsSkillsChoice =\n\t\t\t\t\t\t\t!options.skillsInstalled && !hasPartial;\n\t\t\t\t\t\tconst hasModeQuestion =\n\t\t\t\t\t\t\tneedsMcpChoice || needsSkillsChoice;\n\t\t\t\t\t\tconst modeField = hasModeQuestion\n\t\t\t\t\t\t\t? \", mode: string\"\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\tconst mcpField = hasModeQuestion\n\t\t\t\t\t\t\t? \", mcpScope?: 'global'|'project'|'none'\"\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\tconst skillsField = needsSkillsChoice\n\t\t\t\t\t\t\t? \", skillsScope?: string\"\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\tconst extField = hasModeQuestion\n\t\t\t\t\t\t\t? \", installExtension?: bool\"\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\tconst prefilledSkills =\n\t\t\t\t\t\t\toptions.skillsInstalled || hasPartial\n\t\t\t\t\t\t\t\t? `, skillsScope: \"${options.skillsInstalled ? options.skillsScope || \"project\" : partialScope}\"`\n\t\t\t\t\t\t\t\t: skillsField;\n\t\t\t\t\t\treturn `<json: { agent: string, ide: string, mcpConfigured: bool${prefilledSkills}${previewFlag}${modeField}${mcpField}${extField}${hasApp ? \", features?: string\" : \", template: string\"} }>`;\n\t\t\t\t\t})(),\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction _buildModeQuestion(options: SetupPhaseOptions): PhaseResponse {\n\tconst agentArgs = options.agent ? [\"--agent\", options.agent] : [];\n\n\t// Build a context summary from what the agent found\n\tconst findings: string[] = [];\n\tif (options.mcpConfigured) {\n\t\tfindings.push(\n\t\t\t\"Neon MCP server is already configured (will be upgraded to evergreen)\",\n\t\t);\n\t} else {\n\t\tfindings.push(\"Neon MCP server is not configured\");\n\t}\n\tif (options.connectionString) {\n\t\tfindings.push(\"A Neon connection string was found in the project\");\n\t} else {\n\t\tfindings.push(\"No Neon connection string found — will need to add one\");\n\t}\n\tif (options.framework && options.framework !== \"none\") {\n\t\tfindings.push(`Framework detected: ${options.framework}`);\n\t}\n\tif (options.orm && options.orm !== \"none\") {\n\t\tfindings.push(`ORM detected: ${options.orm}`);\n\t}\n\tif (options.migrationTool && options.migrationTool !== \"none\") {\n\t\tfindings.push(`Migration tool detected: ${options.migrationTool}`);\n\t}\n\tif (options.isVscodeIde) {\n\t\tfindings.push(\"VS Code-based IDE detected — Neon extension available\");\n\t}\n\n\tconst inspectionArgs = buildInspectionArgs(options);\n\n\t// Build defaults label showing only what will be installed\n\tconst defaultsParts: string[] = [\"neonctl CLI\"];\n\tif (!options.mcpConfigured) defaultsParts.push(\"MCP global\");\n\tdefaultsParts.push(\"skills in project\");\n\tif (options.isVscodeIde) defaultsParts.push(\"install extension\");\n\tconst defaultsLabel =\n\t\tdefaultsParts.length > 0\n\t\t\t? `Use defaults (${defaultsParts.join(\", \")})`\n\t\t\t: \"Use defaults\";\n\n\treturn {\n\t\tphase: \"setup\",\n\t\tstatus: \"preferences_needed\",\n\t\tinspection: {\n\t\t\tmcpConfigured: options.mcpConfigured,\n\t\t\tconnectionString: options.connectionString,\n\t\t\tframework: options.framework,\n\t\t\torm: options.orm,\n\t\t\tmigrationTool: options.migrationTool,\n\t\t\tmigrationDir: options.migrationDir,\n\t\t\tisVscodeIde: options.isVscodeIde,\n\t\t},\n\t\tnextAction: {\n\t\t\ttype: \"ask_user\",\n\t\t\tquestion: \"Use default settings or customize?\",\n\t\t\toptions: [\n\t\t\t\t{\n\t\t\t\t\tvalue: \"defaults\",\n\t\t\t\t\tlabel: defaultsLabel,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tvalue: \"customize\",\n\t\t\t\t\tlabel: \"Customize installation settings\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tcontext: `Project inspection results:\\n${findings.map((f) => `- ${f}`).join(\"\\n\")}`,\n\t\t\tresponseMapping: {\n\t\t\t\tdefaults: {\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"setup\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...agentArgs,\n\t\t\t\t\t\t...inspectionArgs,\n\t\t\t\t\t\t\"--mode\",\n\t\t\t\t\t\t\"defaults\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tcustomize: {\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"setup\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...agentArgs,\n\t\t\t\t\t\t...inspectionArgs,\n\t\t\t\t\t\t\"--mode\",\n\t\t\t\t\t\t\"customize\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction _buildCustomizeQuestions(options: SetupPhaseOptions): PhaseResponse {\n\tconst agentArgs = options.agent ? [\"--agent\", options.agent] : [];\n\tconst inspectionArgs = buildInspectionArgs(options);\n\n\tconst needsMcp = !options.mcpConfigured;\n\tconst mcpScopes = needsMcp ? [\"global\", \"project\", \"none\"] : [\"skip\"];\n\tconst skillsScopes = [\"global\", \"project\"];\n\tconst extOptions = options.isVscodeIde ? [\"ext\", \"noext\"] : [\"ext\"];\n\n\t// Build all combinations of configurable options\n\tconst customOptions: { value: string; label: string }[] = [];\n\tfor (const mcp of mcpScopes) {\n\t\tfor (const skills of skillsScopes) {\n\t\t\tfor (const ext of extOptions) {\n\t\t\t\tconst parts: string[] = [];\n\t\t\t\tif (mcp === \"none\") parts.push(\"Skip MCP\");\n\t\t\t\telse if (mcp !== \"skip\") parts.push(`MCP: ${mcp}`);\n\t\t\t\tif (skills !== \"skip\")\n\t\t\t\t\tparts.push(\n\t\t\t\t\t\t`Skills: ${skills === \"project\" ? \"project-level\" : skills}`,\n\t\t\t\t\t);\n\t\t\t\tif (options.isVscodeIde) {\n\t\t\t\t\tparts.push(\n\t\t\t\t\t\text === \"ext\" ? \"Install extension\" : \"Skip extension\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcustomOptions.push({\n\t\t\t\t\tvalue: `${mcp}_${skills}_${ext}`,\n\t\t\t\t\tlabel: parts.join(\", \"),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tconst responseMapping: Record<string, { args: string[] }> = {};\n\n\tfor (const opt of customOptions) {\n\t\tconst parts = opt.value.split(\"_\");\n\t\tconst mcpScope = parts[0] === \"skip\" ? \"global\" : parts[0]; // \"none\" passes through\n\t\tconst skillsScope = parts[1] === \"skip\" ? \"project\" : parts[1];\n\t\tconst installExt = parts[2] === \"ext\";\n\n\t\tresponseMapping[opt.value] = {\n\t\t\targs: [\n\t\t\t\t\"setup\",\n\t\t\t\t\"--json\",\n\t\t\t\t...agentArgs,\n\t\t\t\t...inspectionArgs,\n\t\t\t\t\"--mode\",\n\t\t\t\t\"customize\",\n\t\t\t\t\"--mcp-scope\",\n\t\t\t\tmcpScope,\n\t\t\t\t\"--skills-scope\",\n\t\t\t\tskillsScope,\n\t\t\t\t...(options.isVscodeIde\n\t\t\t\t\t? [\"--install-extension\", installExt ? \"true\" : \"false\"]\n\t\t\t\t\t: []),\n\t\t\t\t\"--execute\",\n\t\t\t],\n\t\t};\n\t}\n\n\treturn {\n\t\tphase: \"setup\",\n\t\tstatus: \"customizing\",\n\t\tnextAction: {\n\t\t\ttype: \"ask_user\",\n\t\t\tquestion: \"Choose your installation configuration:\",\n\t\t\toptions: customOptions,\n\t\t\tcontext:\n\t\t\t\t\"Global scope means settings apply across all your projects. Project-level means settings are scoped to this project only.\" +\n\t\t\t\t(options.mcpConfigured\n\t\t\t\t\t? \"\\nSince Neon tools are already installed, they will be upgraded to the latest evergreen version.\"\n\t\t\t\t\t: \"\") +\n\t\t\t\t(isCursorAgent(options)\n\t\t\t\t\t? \"\\nNote: Cursor disables project-level MCP servers by default. If you choose project scope, you will need to manually enable the Neon server in Cursor Settings > MCP.\"\n\t\t\t\t\t: \"\"),\n\t\t\tresponseMapping,\n\t\t},\n\t};\n}\n\ninterface InstallResult {\n\tid: string;\n\tdescription: string;\n\tstatus: \"success\" | \"failed\";\n\terror?: string;\n\t/** True when the step wasn't automated — the description contains manual instructions for the user */\n\tmanualAction?: boolean;\n\t/** Shell commands the agent can run to complete this step manually */\n\tcommands?: string[];\n}\n\n/**\n * Executes the batched installation of MCP server, skills, and extension.\n * Runs commands directly in the CLI process — the agent does NOT run these.\n * Returns results and chains to the getting-started phase.\n */\nasync function executeBatchedInstallation(\n\toptions: SetupPhaseOptions,\n): Promise<PhaseResponse> {\n\tconst mcpScope = options.mcpScope ?? \"global\";\n\tconst agentId = options.agent ?? \"cursor\";\n\tconst mcpAgentId = resolveAddMcpAgentId(agentId);\n\tconst installExt = options.installExtension === true;\n\n\tconst results: InstallResult[] = [];\n\tconst isBootstrap = !!options.template;\n\n\t// Step 0: Bootstrap project from template if specified\n\tif (isBootstrap && options.template) {\n\t\ttry {\n\t\t\tconst templates = await fetchTemplates();\n\t\t\tconst template =\n\t\t\t\tfindTemplate(templates, options.template) ??\n\t\t\t\tfindTemplate(FALLBACK_TEMPLATES, options.template);\n\t\t\tif (!template) {\n\t\t\t\tthrow new Error(`Unknown template \"${options.template}\".`);\n\t\t\t}\n\t\t\tawait scaffoldTemplate(template, \".\");\n\t\t\tresults.push({\n\t\t\t\tid: \"bootstrap\",\n\t\t\t\tdescription: `Scaffolded project from template \"${options.template}\"`,\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\n\t\t\t// Write template features to .neon under _init (ephemeral, cleaned up when init completes)\n\t\t\tif (options.templateRequires) {\n\t\t\t\tconst neonContextPath = resolve(process.cwd(), \".neon\");\n\t\t\t\tconst context: Record<string, unknown> = {\n\t\t\t\t\t_init: { features: options.templateRequires },\n\t\t\t\t};\n\t\t\t\twriteFileSync(\n\t\t\t\t\tneonContextPath,\n\t\t\t\t\t`${JSON.stringify(context, null, 2)}\\n`,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tresults.push({\n\t\t\t\tid: \"bootstrap\",\n\t\t\t\tdescription: `Failed to scaffold project from template \"${options.template}\"`,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: err instanceof Error ? err.message : \"Unknown error\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// Step 1: Ensure neonctl CLI is installed and up to date\n\tconst neonctlResult = await ensureNeonctl();\n\tswitch (neonctlResult.status) {\n\t\tcase \"already_current\":\n\t\t\tresults.push({\n\t\t\t\tid: \"neonctl\",\n\t\t\t\tdescription: `neonctl CLI is up to date (v${neonctlResult.version})`,\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\t\t\tbreak;\n\t\tcase \"installed\":\n\t\t\tresults.push({\n\t\t\t\tid: \"neonctl\",\n\t\t\t\tdescription: `Installed neonctl CLI (v${neonctlResult.version})`,\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\t\t\tbreak;\n\t\tcase \"updated\":\n\t\t\tresults.push({\n\t\t\t\tid: \"neonctl\",\n\t\t\t\tdescription: `Updated neonctl CLI to v${neonctlResult.version}`,\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tresults.push({\n\t\t\t\tid: \"neonctl\",\n\t\t\t\tdescription: \"Failed to install neonctl CLI\",\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: neonctlResult.error,\n\t\t\t});\n\t\t\tbreak;\n\t}\n\n\t// Step 2: Install MCP server (skip if already configured)\n\tconst isCursor =\n\t\tmcpAgentId === \"cursor\" ||\n\t\toptions.ide?.toLowerCase() === \"cursor\" ||\n\t\toptions.agent?.toLowerCase() === \"cursor\";\n\n\tif (mcpScope === \"none\") {\n\t\tresults.push({\n\t\t\tid: \"skip_mcp\",\n\t\t\tdescription: \"Neon MCP server installation skipped by user\",\n\t\t\tstatus: \"success\",\n\t\t});\n\t} else if (options.mcpConfigured) {\n\t\tresults.push({\n\t\t\tid: \"skip_mcp\",\n\t\t\tdescription: \"Neon MCP server already configured\",\n\t\t\tstatus: \"success\",\n\t\t});\n\t} else {\n\t\tconst mcpArgs = [\n\t\t\t\"-y\",\n\t\t\t\"add-mcp\",\n\t\t\t\"https://mcp.neon.tech/mcp\",\n\t\t\t...(mcpScope === \"global\" ? [\"-g\"] : []),\n\t\t\t\"-n\",\n\t\t\t\"Neon\",\n\t\t\t\"-y\",\n\t\t\t\"-a\",\n\t\t\tmcpAgentId,\n\t\t];\n\t\ttry {\n\t\t\tawait execa(\"npx\", mcpArgs, { stdio: \"pipe\", timeout: 60000 });\n\t\t\tresults.push({\n\t\t\t\tid: \"install_mcp\",\n\t\t\t\tdescription: `Installed Neon MCP server (${mcpScope} scope)`,\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\n\t\t\t// Some editors disable newly added MCP servers by default.\n\t\t\t// Cursor: project-level servers are always disabled initially.\n\t\t\t// Claude Code: newly added servers require user approval.\n\t\t\tconst isClaudeCode =\n\t\t\t\tmcpAgentId === \"claude-code\" ||\n\t\t\t\toptions.agent?.toLowerCase() === \"claude-code\";\n\n\t\t\tif (isCursor && mcpScope === \"project\") {\n\t\t\t\tresults.push({\n\t\t\t\t\tid: \"enable_mcp\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t'Cursor disables project-level MCP servers by default. Open Cursor Settings > MCP and toggle the \"Neon\" server on.',\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\tmanualAction: true,\n\t\t\t\t});\n\t\t\t} else if (isClaudeCode) {\n\t\t\t\tresults.push({\n\t\t\t\t\tid: \"enable_mcp\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t'Claude Code requires approval for newly added MCP servers. When prompted, approve the \"Neon\" MCP server to enable it. You can check MCP server status with /mcp in Claude Code.',\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\tmanualAction: true,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tresults.push({\n\t\t\t\tid: \"install_mcp\",\n\t\t\t\tdescription: \"Failed to install Neon MCP server\",\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: err instanceof Error ? err.message : \"Unknown error\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// Step 3: Install/update skills (skip when bootstrapping — templates bundle skills)\n\tif (isBootstrap) {\n\t\tresults.push({\n\t\t\tid: \"install_skills\",\n\t\t\tdescription: \"Neon agent skills included in template\",\n\t\t\tstatus: \"success\",\n\t\t});\n\t} else {\n\t\tconst skillsScope = (options.skillsScope ?? \"project\") as\n\t\t\t| \"global\"\n\t\t\t| \"project\";\n\t\tconst skillsOk = await ensureSkillsUpToDate(\n\t\t\tagentId,\n\t\t\tskillsScope,\n\t\t\toptions.preview,\n\t\t);\n\t\tif (skillsOk) {\n\t\t\tresults.push({\n\t\t\t\tid: \"install_skills\",\n\t\t\t\tdescription: \"Neon agent skills installed\",\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\t\t} else {\n\t\t\t// Build the install commands for the agent to run directly\n\t\t\t// (sandboxed environments may block child process writes)\n\t\t\tconst { getSkillList } = await import(\"../skills.js\");\n\t\t\tconst skillList = getSkillList(options.preview);\n\t\t\tconst cmds = skillList.map(\n\t\t\t\t(s) =>\n\t\t\t\t\t`skills add neondatabase/agent-skills --skill ${s} --agent ${agentId}${skillsScope === \"global\" ? \" -g\" : \"\"} -y`,\n\t\t\t);\n\t\t\tresults.push({\n\t\t\t\tid: \"install_skills\",\n\t\t\t\tdescription:\n\t\t\t\t\t\"Failed to install Neon agent skills automatically. Run these commands to install manually:\",\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tcommands: cmds,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Step 4: Install editor extension if requested\n\t// Use the agent-reported IDE (not agent identity) — e.g. Claude Code running in\n\t// Cursor should install the extension for Cursor, not skip it.\n\tif (installExt) {\n\t\tconst extResult = await installExtensionForIde(options.ide ?? agentId);\n\t\tresults.push(extResult);\n\t}\n\n\t// Step 5: Write selected features to .neon under _init for brownfield flows\n\t// (Bootstrap flows already wrote _init in step 0)\n\tif (!isBootstrap && options.features && options.features.length > 0) {\n\t\tconst neonContextPath = resolve(process.cwd(), \".neon\");\n\t\tconst context: Record<string, unknown> = {\n\t\t\t_init: { features: options.features },\n\t\t};\n\t\twriteFileSync(neonContextPath, `${JSON.stringify(context, null, 2)}\\n`);\n\t}\n\n\tconst allSucceeded = results.every((r) => r.status === \"success\");\n\n\t// Build args to chain to the getting-started phase as a separate CLI call.\n\t// This ensures the agent gets a clean response with ONLY the getting-started\n\t// action — no competing \"results\" array to distract it.\n\tconst gettingStartedData: Record<string, unknown> = {};\n\tif (options.connectionString) gettingStartedData.hasConnectionString = true;\n\tif (options.framework) gettingStartedData.framework = options.framework;\n\tif (options.orm) gettingStartedData.orm = options.orm;\n\tif (options.migrationTool)\n\t\tgettingStartedData.migrationTool = options.migrationTool;\n\tif (options.migrationDir)\n\t\tgettingStartedData.migrationDir = options.migrationDir;\n\t// Pass features so getting-started knows which phases to chain to\n\tconst resolvedFeatures = options.templateRequires ?? options.features;\n\tif (resolvedFeatures && resolvedFeatures.length > 0)\n\t\tgettingStartedData.features = resolvedFeatures;\n\t// Bootstrap implies preview mode (new project in us-east required)\n\tif (isBootstrap) gettingStartedData.preview = true;\n\tconst gettingStartedArgs = [\n\t\t\"getting-started\",\n\t\t\"--json\",\n\t\t\"--data\",\n\t\tJSON.stringify(gettingStartedData),\n\t];\n\n\treturn {\n\t\tphase: \"setup\",\n\t\tstatus: allSucceeded ? \"installed\" : \"partial\",\n\t\tresults,\n\t\tnextAction: {\n\t\t\ttype: \"run_neon_init\",\n\t\t\targs: gettingStartedArgs,\n\t\t},\n\t};\n}\n\nfunction buildInspectionArgs(options: SetupPhaseOptions): string[] {\n\tconst args: string[] = [];\n\tif (options.mcpConfigured !== null && options.mcpConfigured !== undefined) {\n\t\targs.push(\"--mcp-configured\", options.mcpConfigured ? \"true\" : \"false\");\n\t}\n\tif (\n\t\toptions.connectionString !== null &&\n\t\toptions.connectionString !== undefined\n\t) {\n\t\targs.push(\n\t\t\t\"--connection-string\",\n\t\t\toptions.connectionString ? \"true\" : \"false\",\n\t\t);\n\t}\n\tif (options.framework) {\n\t\targs.push(\"--framework\", options.framework);\n\t}\n\tif (options.orm) {\n\t\targs.push(\"--orm\", options.orm);\n\t}\n\tif (options.migrationTool) {\n\t\targs.push(\"--migration-tool\", options.migrationTool);\n\t}\n\tif (options.migrationDir) {\n\t\targs.push(\"--migration-dir\", options.migrationDir);\n\t}\n\tif (options.isVscodeIde !== null && options.isVscodeIde !== undefined) {\n\t\targs.push(\"--is-vscode-ide\", options.isVscodeIde ? \"true\" : \"false\");\n\t}\n\treturn args;\n}\n\n/**\n * Fills in missing filesystem inspection fields by running inspectProject().\n * Agent-reported data (mcpConfigured, agent, mode, scopes) is preserved.\n * CLI-detectable fields (framework, orm, migrations, connectionString, isVscodeIde)\n * are filled in only if not already present.\n */\nasync function mergeCliInspection(\n\toptions: SetupPhaseOptions,\n): Promise<SetupPhaseOptions> {\n\t// If the agent already provided these, no need to re-inspect\n\tif (options.framework !== undefined && options.orm !== undefined) {\n\t\treturn options;\n\t}\n\n\tconst inspection = await inspectProject([\n\t\t{ id: \"connection_string\", description: \"\", lookFor: [] },\n\t\t{ id: \"project_stack\", description: \"\", lookFor: [] },\n\t\t{ id: \"migrations\", description: \"\", lookFor: [] },\n\t\t{ id: \"ide_type\", description: \"\", lookFor: [] },\n\t]);\n\n\t// Also detect IDE if not already reported by the agent\n\tconst ide =\n\t\toptions.ide?.toLowerCase().replace(/\\s+/g, \"-\") ||\n\t\tdetectIde()?.toLowerCase().replace(/\\s+/g, \"-\") ||\n\t\tundefined;\n\n\treturn {\n\t\t...options,\n\t\tide,\n\t\tconnectionString:\n\t\t\toptions.connectionString ??\n\t\t\t(inspection.connectionString as boolean | undefined),\n\t\tframework:\n\t\t\toptions.framework ?? (inspection.framework as string | undefined),\n\t\torm: options.orm ?? (inspection.orm as string | undefined),\n\t\tmigrationTool:\n\t\t\toptions.migrationTool ??\n\t\t\t(inspection.migrationTool as string | undefined),\n\t\tmigrationDir:\n\t\t\toptions.migrationDir ??\n\t\t\t(inspection.migrationDir as string | undefined),\n\t\tisVscodeIde:\n\t\t\toptions.isVscodeIde ??\n\t\t\t(inspection.isVscodeIde as boolean | undefined),\n\t};\n}\n\n/**\n * Checks whether the user is in a VS Code-based IDE that supports extensions.\n * Uses agent-reported `ide` field first, then falls back to `isVscodeIde` from inspection.\n */\nfunction isCursorAgent(options: SetupPhaseOptions): boolean {\n\tconst ide = options.ide?.toLowerCase();\n\tif (ide === \"cursor\") return true;\n\tconst agent = options.agent?.toLowerCase();\n\tif (agent === \"cursor\") return true;\n\treturn false;\n}\n\nfunction isVscodeBasedIde(options: SetupPhaseOptions): boolean {\n\tif (options.ide) {\n\t\tconst ide = options.ide.toLowerCase();\n\t\treturn (\n\t\t\tide === \"cursor\" ||\n\t\t\tide === \"vscode\" ||\n\t\t\tide === \"vs-code\" ||\n\t\t\tide === \"windsurf\"\n\t\t);\n\t}\n\treturn options.isVscodeIde === true;\n}\n\n/**\n * Resolves which IDE to install the extension for.\n * Accepts the agent-reported IDE value (preferred), the agent ID, or\n * falls back to env-var detection.\n */\nfunction resolveEditorForExtension(ideOrAgentId: string): Editor | null {\n\t// Map known IDE/agent identifiers to Editor types\n\tswitch (ideOrAgentId.toLowerCase()) {\n\t\tcase \"cursor\":\n\t\t\treturn \"Cursor\";\n\t\tcase \"vscode\":\n\t\tcase \"vs-code\":\n\t\tcase \"copilot\":\n\t\tcase \"github-copilot\":\n\t\tcase \"github-copilot-cli\":\n\t\t\treturn \"VS Code\";\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\t// Fall back to env-var detection\n\tconst ide = detectIde();\n\tif (ide === \"Cursor\" || ide === \"VS Code\") return ide;\n\n\treturn null;\n}\n\nconst MANUAL_INSTALL_MSG = `Search for \"Neon\" in the extensions panel (Cmd+Shift+X / Ctrl+Shift+X) and install \"Neon Local Connect\" by Databricks.`;\n\n/**\n * Installs the Neon extension for the detected IDE.\n *\n * Uses env-var detection to determine the IDE (not the agent identity),\n * so Claude Code running in Cursor correctly installs for Cursor.\n *\n * Strategy:\n * 1. Try `<editor> --install-extension <id>` directly (uses editor's configured marketplace)\n * 2. If that fails, download .vsix (from proxy or Open VSX) and install via local file\n * 3. If all else fails: return manual install instructions\n */\nasync function installExtensionForIde(agentId: string): Promise<InstallResult> {\n\tconst editorType = resolveEditorForExtension(agentId);\n\tif (!editorType) {\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: MANUAL_INSTALL_MSG,\n\t\t\tstatus: \"success\",\n\t\t\tmanualAction: true,\n\t\t};\n\t}\n\n\tconst editorCmd = await findEditorCommand(editorType);\n\tif (!editorCmd) {\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: MANUAL_INSTALL_MSG,\n\t\t\tstatus: \"success\",\n\t\t\tmanualAction: true,\n\t\t};\n\t}\n\n\t// Try direct marketplace install first (works if editor has marketplace configured)\n\ttry {\n\t\tawait execa(editorCmd, [\"--install-extension\", NEON_EXTENSION_ID], {\n\t\t\tstdio: \"pipe\",\n\t\t\ttimeout: 60000,\n\t\t});\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: `Installed Neon extension for ${editorType}`,\n\t\t\tstatus: \"success\",\n\t\t};\n\t} catch {\n\t\t// Fall through to VSIX download approach\n\t}\n\n\t// Download .vsix and install locally\n\tconst vsixPath = await downloadVsix();\n\tif (!vsixPath) {\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: MANUAL_INSTALL_MSG,\n\t\t\tstatus: \"success\",\n\t\t\tmanualAction: true,\n\t\t};\n\t}\n\n\ttry {\n\t\tawait execa(editorCmd, [\"--install-extension\", vsixPath], {\n\t\t\tstdio: \"pipe\",\n\t\t\ttimeout: 60000,\n\t\t});\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: `Installed Neon extension for ${editorType}`,\n\t\t\tstatus: \"success\",\n\t\t};\n\t} catch {\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: MANUAL_INSTALL_MSG,\n\t\t\tstatus: \"success\",\n\t\t\tmanualAction: true,\n\t\t};\n\t} finally {\n\t\ttry {\n\t\t\tawait unlink(vsixPath);\n\t\t} catch {}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"setup.js","names":[],"sources":["../../../src/lib/phases/setup.ts"],"sourcesContent":["import { writeFileSync } from \"node:fs\";\nimport { unlink } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { execa } from \"execa\";\nimport { resolveAddMcpAgentId } from \"../agents.js\";\nimport {\n\tFALLBACK_TEMPLATES,\n\tfetchTemplates,\n\tfindTemplate,\n\ttype NeonFeature,\n\tscaffoldTemplate,\n} from \"../bootstrap.js\";\nimport {\n\tdetectIde,\n\tisCursorInstalled,\n\tisVSCodeInstalled,\n} from \"../detect-agent.js\";\nimport { findEditorCommand } from \"../extension.js\";\nimport { inspectProject } from \"../inspect.js\";\nimport { ensureNeonctl } from \"../neonctl.js\";\nimport { ensureSkillsUpToDate } from \"../skills.js\";\nimport type { Editor, PhaseResponse } from \"../types.js\";\nimport { downloadVsix, NEON_EXTENSION_ID } from \"../vsix.js\";\n\nexport interface SetupPhaseOptions {\n\tagent?: string;\n\t/** The IDE/editor the user is running in (e.g. \"cursor\", \"vscode\") — reported by agent */\n\tide?: string;\n\t/** Enable preview skills (neon-object-storage, neon-functions, neon-ai-gateway) */\n\tpreview?: boolean;\n\t/** Whether the directory already contains an application */\n\thasApp?: boolean;\n\t/** Template ID to scaffold (when bootstrapping a new project) */\n\ttemplate?: string;\n\t/** Neon features required by the selected template */\n\ttemplateRequires?: NeonFeature[];\n\t/** Neon features selected by the user (brownfield flows) */\n\tfeatures?: NeonFeature[];\n\t// Inspection results — pre-filled by orchestrator or reported by agent\n\tmcpConfigured?: boolean | null;\n\tskillsInstalled?: boolean | null;\n\tconnectionString?: boolean | null;\n\tconnectionParams?: string; // JSON with host/dbname/etc if found\n\tframework?: string;\n\torm?: string;\n\tmigrationTool?: string;\n\tmigrationDir?: string;\n\tisVscodeIde?: boolean | null;\n\t// User preferences (also used for pre-detected scope from inspection)\n\tmode?: string;\n\tmcpScope?: string;\n\tskillsScope?: string;\n\tinstallExtension?: boolean;\n\t// Execution flags\n\texecute?: boolean;\n}\n\n/**\n * Comprehensive setup phase: inspects repo state, collects user preferences,\n * then batches all installation commands together.\n *\n * With --data JSON, the agent sends inspection results AND user preferences\n * in a single call, so the CLI can go straight to installation.\n */\nexport async function handleSetupPhase(\n\toptions: SetupPhaseOptions,\n): Promise<PhaseResponse> {\n\t// Parse features from comma-separated string (e.g. \"database,auth\" from agent --data)\n\tif (typeof options.features === \"string\") {\n\t\toptions.features = (options.features as unknown as string)\n\t\t\t.split(\",\")\n\t\t\t.map((f) => f.trim()) as NeonFeature[];\n\t}\n\n\t// Treat \"none\" as no template selected\n\tconst templateWasAnswered = options.template !== undefined;\n\tif (options.template === \"none\") {\n\t\toptions.template = undefined;\n\t}\n\n\t// Resolve template requirements if a template was selected but requires not yet populated\n\tif (options.template && !options.templateRequires) {\n\t\tconst templates = await fetchTemplates();\n\t\tconst selected = templates.find((t) => t.id === options.template);\n\t\tif (selected) {\n\t\t\toptions.templateRequires = selected.requires;\n\t\t}\n\t}\n\n\t// --execute: run the batched installation (legacy path)\n\tif (options.execute) {\n\t\treturn executeBatchedInstallation(await mergeCliInspection(options));\n\t}\n\n\t// Treat any explicit mode value that isn't \"customize\"/\"custom\" as defaults.\n\t// Also treat as defaults when mode is missing but agent reported back\n\t// (template was answered, nothing left to customize).\n\tconst hasReportedBack = options.mode || templateWasAnswered;\n\tif (\n\t\thasReportedBack &&\n\t\toptions.mode !== \"customize\" &&\n\t\toptions.mode !== \"custom\"\n\t) {\n\t\tconst merged = await mergeCliInspection(options);\n\t\tconst shouldInstallExt =\n\t\t\tmerged.installExtension ?? isVscodeBasedIde(merged);\n\t\treturn executeBatchedInstallation({\n\t\t\t...merged,\n\t\t\tmcpScope: merged.mcpScope ?? \"global\",\n\t\t\tskillsScope: merged.skillsScope ?? \"project\",\n\t\t\tinstallExtension: shouldInstallExt,\n\t\t});\n\t}\n\n\t// User chose \"customize\" (also accept \"custom\" — agents sometimes truncate)\n\tif (options.mode === \"customize\" || options.mode === \"custom\") {\n\t\tconst merged = await mergeCliInspection(options);\n\t\tconst shouldInstallExt =\n\t\t\tmerged.installExtension ?? isVscodeBasedIde(merged);\n\t\treturn executeBatchedInstallation({\n\t\t\t...merged,\n\t\t\tmcpScope: merged.mcpScope ?? \"global\",\n\t\t\tskillsScope: merged.skillsScope ?? \"project\",\n\t\t\tinstallExtension: shouldInstallExt,\n\t\t});\n\t}\n\n\t// Default: send inspection checks with user preferences (all in one response)\n\treturn buildBulkInspection(options);\n}\n\nfunction buildTemplatePreference(\n\ttemplates: {\n\t\tid: string;\n\t\ttitle: string;\n\t\tdescription: string;\n\t\ttools?: string[];\n\t}[],\n) {\n\treturn [\n\t\t{\n\t\t\tid: \"template\",\n\t\t\tquestion:\n\t\t\t\t\"No application was detected in this directory. Would you like to scaffold a new project from a template?\",\n\t\t\tphase: \"before_checks\" as const,\n\t\t\toptions: [\n\t\t\t\t...templates.map((t) => {\n\t\t\t\t\tconst tools =\n\t\t\t\t\t\tt.tools && t.tools.length > 0\n\t\t\t\t\t\t\t? ` (${t.tools.join(\", \")})`\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\treturn {\n\t\t\t\t\t\tvalue: t.id,\n\t\t\t\t\t\tlabel: `${t.title}${tools} — ${t.description}`,\n\t\t\t\t\t};\n\t\t\t\t}),\n\t\t\t\t{\n\t\t\t\t\tvalue: \"none\",\n\t\t\t\t\tlabel: \"No thanks — continue without scaffolding\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tdefault: \"none\",\n\t\t},\n\t];\n}\n\nasync function buildBulkInspection(\n\toptions: SetupPhaseOptions,\n): Promise<PhaseResponse> {\n\tconst hasApp = options.hasApp !== false;\n\tconst detectedIde = detectIde();\n\n\t// If no IDE detected (e.g. standalone terminal), check what's installed\n\tconst installedEditors: string[] = [];\n\tif (!detectedIde) {\n\t\tif (isCursorInstalled()) installedEditors.push(\"cursor\");\n\t\tif (isVSCodeInstalled()) installedEditors.push(\"vscode\");\n\t}\n\n\t// Fetch available templates when no app is detected\n\tlet templatePreferences: ReturnType<typeof buildTemplatePreference> = [];\n\tif (!hasApp) {\n\t\tlet templates = FALLBACK_TEMPLATES;\n\t\ttry {\n\t\t\tconst fetched = await fetchTemplates();\n\t\t\tif (fetched && fetched.length > 0) templates = fetched;\n\t\t} catch {}\n\t\ttemplatePreferences = buildTemplatePreference(templates);\n\t}\n\n\treturn {\n\t\tphase: \"setup\",\n\t\tstatus: hasApp ? \"pending\" : \"bootstrap_needed\",\n\t\tdetectedIde: detectedIde?.toLowerCase() ?? null,\n\t\tinstalledEditors: installedEditors.length > 0 ? installedEditors : null,\n\t\t// Pre-detected state from filesystem inspection\n\t\tmcpConfigured: options.mcpConfigured ?? null,\n\t\tmcpScope: options.mcpScope ?? null,\n\t\tskillsInstalled: options.skillsInstalled ?? null,\n\t\tskillsScope: options.skillsScope ?? null,\n\t\tnextAction: {\n\t\t\ttype: \"agent_check\",\n\t\t\tinstructions: [\n\t\t\t\t\"IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.\",\n\t\t\t\t\"\",\n\t\t\t\thasApp\n\t\t\t\t\t? \"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.\"\n\t\t\t\t\t: \"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.\",\n\t\t\t\t\"\",\n\t\t\t\t`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.`,\n\t\t\t\t\"\",\n\t\t\t\t\"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.\",\n\t\t\t\t\"\",\n\t\t\t\t\"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.\",\n\t\t\t\t\"\",\n\t\t\t\tdetectedIde\n\t\t\t\t\t? `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.`\n\t\t\t\t\t: installedEditors.length > 0\n\t\t\t\t\t\t? `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\".`\n\t\t\t\t\t\t: `No IDE or supported editors detected. Set \"ide\" to \"none\" in your reportBack data.`,\n\t\t\t\t\"\",\n\t\t\t\t\"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.\",\n\t\t\t].join(\"\\n\"),\n\t\t\tchecks: [\n\t\t\t\t{\n\t\t\t\t\tid: \"neonctl\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"The Neon CLI will be installed or updated automatically (no action needed from the agent)\",\n\t\t\t\t\tlookFor: [],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"mcp_server\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Check if the Neon MCP server is already configured in your MCP server list\",\n\t\t\t\t\tlookFor: [\n\t\t\t\t\t\t\"An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"agent_type\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Identify which coding agent is running this command\",\n\t\t\t\t\tlookFor: [\n\t\t\t\t\t\t\"Determine which agent you are: cursor, claude-code, copilot, vscode, windsurf, codex, cline, gemini-cli, goose, opencode, or antigravity\",\n\t\t\t\t\t\t\"Report your own agent identifier — this is used to configure the MCP server for the correct tool\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\t...(detectedIde\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tid: \"extension_installed\",\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\"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)\",\n\t\t\t\t\t\t\t\tlookFor: [\n\t\t\t\t\t\t\t\t\t\"Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'\",\n\t\t\t\t\t\t\t\t\t\"If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question\",\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: []),\n\t\t\t],\n\t\t\tuserPreferences: [\n\t\t\t\t...templatePreferences,\n\t\t\t\t// For brownfield flows, ask which Neon features to enable\n\t\t\t\t...(hasApp\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tid: \"features\",\n\t\t\t\t\t\t\t\tquestion:\n\t\t\t\t\t\t\t\t\t\"Which Neon features would you like to enable for this project?\",\n\t\t\t\t\t\t\t\tphase: \"after_checks\" as const,\n\t\t\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvalue: \"database\",\n\t\t\t\t\t\t\t\t\t\tlabel: \"Database (always included)\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvalue: \"database,auth\",\n\t\t\t\t\t\t\t\t\t\tlabel: \"Database + Neon Auth (adds authentication via Neon)\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdefault: \"database\",\n\t\t\t\t\t\t\t\tcontext:\n\t\t\t\t\t\t\t\t\t\"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.\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: []),\n\t\t\t\t// Only show defaults/customize when there's something to customize:\n\t\t\t\t// MCP not configured, skills need scope choice, or extension not detected.\n\t\t\t\t...(() => {\n\t\t\t\t\tconst isPartialSkills = String(\n\t\t\t\t\t\toptions.skillsScope ?? \"\",\n\t\t\t\t\t).includes(\"partial\");\n\t\t\t\t\tconst needsMcpChoice = !options.mcpConfigured;\n\t\t\t\t\tconst needsSkillsChoice =\n\t\t\t\t\t\t!options.skillsInstalled && !isPartialSkills;\n\t\t\t\t\tconst hasCustomizableOptions =\n\t\t\t\t\t\tneedsMcpChoice || needsSkillsChoice;\n\t\t\t\t\tif (!hasCustomizableOptions) return [];\n\t\t\t\t\treturn [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: \"mode\",\n\t\t\t\t\t\t\tquestion: \"Use default settings or customize?\",\n\t\t\t\t\t\t\tphase: \"after_checks\" as const,\n\t\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tvalue: \"defaults\",\n\t\t\t\t\t\t\t\t\tlabel: hasApp\n\t\t\t\t\t\t\t\t\t\t? \"Use defaults (Neon CLI, MCP: global, skills: project-level, extension if applicable — already-configured components will be skipped)\"\n\t\t\t\t\t\t\t\t\t\t: \"Use defaults (Neon CLI, MCP: global, extension if applicable — skills included in template)\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tvalue: \"customize\",\n\t\t\t\t\t\t\t\t\tlabel: \"Customize installation settings\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdefault: \"defaults\",\n\t\t\t\t\t\t},\n\t\t\t\t\t];\n\t\t\t\t})(),\n\t\t\t\t{\n\t\t\t\t\tid: \"mcpScope\",\n\t\t\t\t\tquestion: \"Where should the Neon MCP server be configured?\",\n\t\t\t\t\tcontext:\n\t\t\t\t\t\t\"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.\",\n\t\t\t\t\tphase: \"after_checks\",\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"global\",\n\t\t\t\t\t\t\tlabel: \"Global (available in all projects)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"project\",\n\t\t\t\t\t\t\tlabel: \"Project-level (scoped to this project only)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"none\",\n\t\t\t\t\t\t\tlabel: \"Skip — do not install the MCP server\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdefault: \"global\",\n\t\t\t\t\tcondition: { preferenceId: \"mode\", equals: \"customize\" },\n\t\t\t\t\tgroup: \"customize\",\n\t\t\t\t},\n\t\t\t\t// Show skills scope when skills aren't detected and no partial install exists.\n\t\t\t\t// Partial installations are auto-completed to the same scope silently.\n\t\t\t\t...(!options.skillsInstalled &&\n\t\t\t\t!String(options.skillsScope ?? \"\").includes(\"partial\")\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tid: \"skillsScope\",\n\t\t\t\t\t\t\t\tquestion:\n\t\t\t\t\t\t\t\t\t\"Where should Neon agent skills be installed?\",\n\t\t\t\t\t\t\t\tcontext:\n\t\t\t\t\t\t\t\t\t\"Only ask if skills are not already installed.\",\n\t\t\t\t\t\t\t\tphase: \"after_checks\" as const,\n\t\t\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvalue: \"global\",\n\t\t\t\t\t\t\t\t\t\tlabel: \"Global (available in all projects)\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvalue: \"project\",\n\t\t\t\t\t\t\t\t\t\tlabel: \"Project-level (scoped to this project only)\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdefault: \"project\",\n\t\t\t\t\t\t\t\tcondition: {\n\t\t\t\t\t\t\t\t\tpreferenceId: \"mode\",\n\t\t\t\t\t\t\t\t\tequals: \"customize\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tgroup: \"customize\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: []),\n\t\t\t\t{\n\t\t\t\t\tid: \"installExtension\",\n\t\t\t\t\tquestion:\n\t\t\t\t\t\t\"Install the Neon editor extension for local database browsing?\",\n\t\t\t\t\tphase: \"after_checks\",\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t{ value: \"true\", label: \"Yes\" },\n\t\t\t\t\t\t{ value: \"false\", label: \"No\" },\n\t\t\t\t\t],\n\t\t\t\t\tdefault: \"true\",\n\t\t\t\t\tcontext:\n\t\t\t\t\t\t\"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.\",\n\t\t\t\t\tcondition: { preferenceId: \"mode\", equals: \"customize\" },\n\t\t\t\t\tgroup: \"customize\",\n\t\t\t\t},\n\t\t\t],\n\t\t\treportBack: {\n\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\targs: [\n\t\t\t\t\t\"setup\",\n\t\t\t\t\t\"--json\",\n\t\t\t\t\t\"--data\",\n\t\t\t\t\t(() => {\n\t\t\t\t\t\tconst partialScope = String(\n\t\t\t\t\t\t\toptions.skillsScope ?? \"\",\n\t\t\t\t\t\t).replace(\"-partial\", \"\");\n\t\t\t\t\t\tconst hasPartial = String(\n\t\t\t\t\t\t\toptions.skillsScope ?? \"\",\n\t\t\t\t\t\t).includes(\"partial\");\n\t\t\t\t\t\tconst previewFlag = options.preview\n\t\t\t\t\t\t\t? \", preview: true\"\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\tconst needsMcpChoice = !options.mcpConfigured;\n\t\t\t\t\t\tconst needsSkillsChoice =\n\t\t\t\t\t\t\t!options.skillsInstalled && !hasPartial;\n\t\t\t\t\t\tconst hasModeQuestion =\n\t\t\t\t\t\t\tneedsMcpChoice || needsSkillsChoice;\n\t\t\t\t\t\tconst modeField = hasModeQuestion\n\t\t\t\t\t\t\t? \", mode: string\"\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\tconst mcpField = hasModeQuestion\n\t\t\t\t\t\t\t? \", mcpScope?: 'global'|'project'|'none'\"\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\tconst skillsField = needsSkillsChoice\n\t\t\t\t\t\t\t? \", skillsScope?: string\"\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\tconst extField = hasModeQuestion\n\t\t\t\t\t\t\t? \", installExtension?: bool\"\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\tconst prefilledSkills =\n\t\t\t\t\t\t\toptions.skillsInstalled || hasPartial\n\t\t\t\t\t\t\t\t? `, skillsScope: \"${options.skillsInstalled ? options.skillsScope || \"project\" : partialScope}\"`\n\t\t\t\t\t\t\t\t: skillsField;\n\t\t\t\t\t\treturn `<json: { agent: string, ide: string, mcpConfigured: bool${prefilledSkills}${previewFlag}${modeField}${mcpField}${extField}${hasApp ? \", features?: string\" : \", template: string\"} }>`;\n\t\t\t\t\t})(),\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction _buildModeQuestion(options: SetupPhaseOptions): PhaseResponse {\n\tconst agentArgs = options.agent ? [\"--agent\", options.agent] : [];\n\n\t// Build a context summary from what the agent found\n\tconst findings: string[] = [];\n\tif (options.mcpConfigured) {\n\t\tfindings.push(\n\t\t\t\"Neon MCP server is already configured (will be upgraded to evergreen)\",\n\t\t);\n\t} else {\n\t\tfindings.push(\"Neon MCP server is not configured\");\n\t}\n\tif (options.connectionString) {\n\t\tfindings.push(\"A Neon connection string was found in the project\");\n\t} else {\n\t\tfindings.push(\"No Neon connection string found — will need to add one\");\n\t}\n\tif (options.framework && options.framework !== \"none\") {\n\t\tfindings.push(`Framework detected: ${options.framework}`);\n\t}\n\tif (options.orm && options.orm !== \"none\") {\n\t\tfindings.push(`ORM detected: ${options.orm}`);\n\t}\n\tif (options.migrationTool && options.migrationTool !== \"none\") {\n\t\tfindings.push(`Migration tool detected: ${options.migrationTool}`);\n\t}\n\tif (options.isVscodeIde) {\n\t\tfindings.push(\"VS Code-based IDE detected — Neon extension available\");\n\t}\n\n\tconst inspectionArgs = buildInspectionArgs(options);\n\n\t// Build defaults label showing only what will be installed\n\tconst defaultsParts: string[] = [\"Neon CLI\"];\n\tif (!options.mcpConfigured) defaultsParts.push(\"MCP global\");\n\tdefaultsParts.push(\"skills in project\");\n\tif (options.isVscodeIde) defaultsParts.push(\"install extension\");\n\tconst defaultsLabel =\n\t\tdefaultsParts.length > 0\n\t\t\t? `Use defaults (${defaultsParts.join(\", \")})`\n\t\t\t: \"Use defaults\";\n\n\treturn {\n\t\tphase: \"setup\",\n\t\tstatus: \"preferences_needed\",\n\t\tinspection: {\n\t\t\tmcpConfigured: options.mcpConfigured,\n\t\t\tconnectionString: options.connectionString,\n\t\t\tframework: options.framework,\n\t\t\torm: options.orm,\n\t\t\tmigrationTool: options.migrationTool,\n\t\t\tmigrationDir: options.migrationDir,\n\t\t\tisVscodeIde: options.isVscodeIde,\n\t\t},\n\t\tnextAction: {\n\t\t\ttype: \"ask_user\",\n\t\t\tquestion: \"Use default settings or customize?\",\n\t\t\toptions: [\n\t\t\t\t{\n\t\t\t\t\tvalue: \"defaults\",\n\t\t\t\t\tlabel: defaultsLabel,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tvalue: \"customize\",\n\t\t\t\t\tlabel: \"Customize installation settings\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tcontext: `Project inspection results:\\n${findings.map((f) => `- ${f}`).join(\"\\n\")}`,\n\t\t\tresponseMapping: {\n\t\t\t\tdefaults: {\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"setup\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...agentArgs,\n\t\t\t\t\t\t...inspectionArgs,\n\t\t\t\t\t\t\"--mode\",\n\t\t\t\t\t\t\"defaults\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tcustomize: {\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"setup\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...agentArgs,\n\t\t\t\t\t\t...inspectionArgs,\n\t\t\t\t\t\t\"--mode\",\n\t\t\t\t\t\t\"customize\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction _buildCustomizeQuestions(options: SetupPhaseOptions): PhaseResponse {\n\tconst agentArgs = options.agent ? [\"--agent\", options.agent] : [];\n\tconst inspectionArgs = buildInspectionArgs(options);\n\n\tconst needsMcp = !options.mcpConfigured;\n\tconst mcpScopes = needsMcp ? [\"global\", \"project\", \"none\"] : [\"skip\"];\n\tconst skillsScopes = [\"global\", \"project\"];\n\tconst extOptions = options.isVscodeIde ? [\"ext\", \"noext\"] : [\"ext\"];\n\n\t// Build all combinations of configurable options\n\tconst customOptions: { value: string; label: string }[] = [];\n\tfor (const mcp of mcpScopes) {\n\t\tfor (const skills of skillsScopes) {\n\t\t\tfor (const ext of extOptions) {\n\t\t\t\tconst parts: string[] = [];\n\t\t\t\tif (mcp === \"none\") parts.push(\"Skip MCP\");\n\t\t\t\telse if (mcp !== \"skip\") parts.push(`MCP: ${mcp}`);\n\t\t\t\tif (skills !== \"skip\")\n\t\t\t\t\tparts.push(\n\t\t\t\t\t\t`Skills: ${skills === \"project\" ? \"project-level\" : skills}`,\n\t\t\t\t\t);\n\t\t\t\tif (options.isVscodeIde) {\n\t\t\t\t\tparts.push(\n\t\t\t\t\t\text === \"ext\" ? \"Install extension\" : \"Skip extension\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcustomOptions.push({\n\t\t\t\t\tvalue: `${mcp}_${skills}_${ext}`,\n\t\t\t\t\tlabel: parts.join(\", \"),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tconst responseMapping: Record<string, { args: string[] }> = {};\n\n\tfor (const opt of customOptions) {\n\t\tconst parts = opt.value.split(\"_\");\n\t\tconst mcpScope = parts[0] === \"skip\" ? \"global\" : parts[0]; // \"none\" passes through\n\t\tconst skillsScope = parts[1] === \"skip\" ? \"project\" : parts[1];\n\t\tconst installExt = parts[2] === \"ext\";\n\n\t\tresponseMapping[opt.value] = {\n\t\t\targs: [\n\t\t\t\t\"setup\",\n\t\t\t\t\"--json\",\n\t\t\t\t...agentArgs,\n\t\t\t\t...inspectionArgs,\n\t\t\t\t\"--mode\",\n\t\t\t\t\"customize\",\n\t\t\t\t\"--mcp-scope\",\n\t\t\t\tmcpScope,\n\t\t\t\t\"--skills-scope\",\n\t\t\t\tskillsScope,\n\t\t\t\t...(options.isVscodeIde\n\t\t\t\t\t? [\"--install-extension\", installExt ? \"true\" : \"false\"]\n\t\t\t\t\t: []),\n\t\t\t\t\"--execute\",\n\t\t\t],\n\t\t};\n\t}\n\n\treturn {\n\t\tphase: \"setup\",\n\t\tstatus: \"customizing\",\n\t\tnextAction: {\n\t\t\ttype: \"ask_user\",\n\t\t\tquestion: \"Choose your installation configuration:\",\n\t\t\toptions: customOptions,\n\t\t\tcontext:\n\t\t\t\t\"Global scope means settings apply across all your projects. Project-level means settings are scoped to this project only.\" +\n\t\t\t\t(options.mcpConfigured\n\t\t\t\t\t? \"\\nSince Neon tools are already installed, they will be upgraded to the latest evergreen version.\"\n\t\t\t\t\t: \"\") +\n\t\t\t\t(isCursorAgent(options)\n\t\t\t\t\t? \"\\nNote: Cursor disables project-level MCP servers by default. If you choose project scope, you will need to manually enable the Neon server in Cursor Settings > MCP.\"\n\t\t\t\t\t: \"\"),\n\t\t\tresponseMapping,\n\t\t},\n\t};\n}\n\ninterface InstallResult {\n\tid: string;\n\tdescription: string;\n\tstatus: \"success\" | \"failed\";\n\terror?: string;\n\t/** True when the step wasn't automated — the description contains manual instructions for the user */\n\tmanualAction?: boolean;\n\t/** Shell commands the agent can run to complete this step manually */\n\tcommands?: string[];\n}\n\n/**\n * Executes the batched installation of MCP server, skills, and extension.\n * Runs commands directly in the CLI process — the agent does NOT run these.\n * Returns results and chains to the getting-started phase.\n */\nasync function executeBatchedInstallation(\n\toptions: SetupPhaseOptions,\n): Promise<PhaseResponse> {\n\tconst mcpScope = options.mcpScope ?? \"global\";\n\tconst agentId = options.agent ?? \"cursor\";\n\tconst mcpAgentId = resolveAddMcpAgentId(agentId);\n\tconst installExt = options.installExtension === true;\n\n\tconst results: InstallResult[] = [];\n\tconst isBootstrap = !!options.template;\n\n\t// Step 0: Bootstrap project from template if specified\n\tif (isBootstrap && options.template) {\n\t\ttry {\n\t\t\tconst templates = await fetchTemplates();\n\t\t\tconst template =\n\t\t\t\tfindTemplate(templates, options.template) ??\n\t\t\t\tfindTemplate(FALLBACK_TEMPLATES, options.template);\n\t\t\tif (!template) {\n\t\t\t\tthrow new Error(`Unknown template \"${options.template}\".`);\n\t\t\t}\n\t\t\tawait scaffoldTemplate(template, \".\");\n\t\t\tresults.push({\n\t\t\t\tid: \"bootstrap\",\n\t\t\t\tdescription: `Scaffolded project from template \"${options.template}\"`,\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\n\t\t\t// Write template features to .neon under _init (ephemeral, cleaned up when init completes)\n\t\t\tif (options.templateRequires) {\n\t\t\t\tconst neonContextPath = resolve(process.cwd(), \".neon\");\n\t\t\t\tconst context: Record<string, unknown> = {\n\t\t\t\t\t_init: { features: options.templateRequires },\n\t\t\t\t};\n\t\t\t\twriteFileSync(\n\t\t\t\t\tneonContextPath,\n\t\t\t\t\t`${JSON.stringify(context, null, 2)}\\n`,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tresults.push({\n\t\t\t\tid: \"bootstrap\",\n\t\t\t\tdescription: `Failed to scaffold project from template \"${options.template}\"`,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: err instanceof Error ? err.message : \"Unknown error\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// Step 1: Ensure the Neon CLI is installed and up to date\n\tconst neonctlResult = await ensureNeonctl();\n\tswitch (neonctlResult.status) {\n\t\tcase \"already_current\":\n\t\t\tresults.push({\n\t\t\t\tid: \"neonctl\",\n\t\t\t\tdescription: `Neon CLI is up to date (v${neonctlResult.version})`,\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\t\t\tbreak;\n\t\tcase \"installed\":\n\t\t\tresults.push({\n\t\t\t\tid: \"neonctl\",\n\t\t\t\tdescription: `Installed Neon CLI (v${neonctlResult.version})`,\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\t\t\tbreak;\n\t\tcase \"updated\":\n\t\t\tresults.push({\n\t\t\t\tid: \"neonctl\",\n\t\t\t\tdescription: `Updated Neon CLI to v${neonctlResult.version}`,\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tresults.push({\n\t\t\t\tid: \"neonctl\",\n\t\t\t\tdescription: \"Failed to install Neon CLI\",\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: neonctlResult.error,\n\t\t\t});\n\t\t\tbreak;\n\t}\n\n\t// Step 2: Install MCP server (skip if already configured)\n\tconst isCursor =\n\t\tmcpAgentId === \"cursor\" ||\n\t\toptions.ide?.toLowerCase() === \"cursor\" ||\n\t\toptions.agent?.toLowerCase() === \"cursor\";\n\n\tif (mcpScope === \"none\") {\n\t\tresults.push({\n\t\t\tid: \"skip_mcp\",\n\t\t\tdescription: \"Neon MCP server installation skipped by user\",\n\t\t\tstatus: \"success\",\n\t\t});\n\t} else if (options.mcpConfigured) {\n\t\tresults.push({\n\t\t\tid: \"skip_mcp\",\n\t\t\tdescription: \"Neon MCP server already configured\",\n\t\t\tstatus: \"success\",\n\t\t});\n\t} else {\n\t\tconst mcpArgs = [\n\t\t\t\"-y\",\n\t\t\t\"add-mcp\",\n\t\t\t\"https://mcp.neon.tech/mcp\",\n\t\t\t...(mcpScope === \"global\" ? [\"-g\"] : []),\n\t\t\t\"-n\",\n\t\t\t\"Neon\",\n\t\t\t\"-y\",\n\t\t\t\"-a\",\n\t\t\tmcpAgentId,\n\t\t];\n\t\ttry {\n\t\t\tawait execa(\"npx\", mcpArgs, { stdio: \"pipe\", timeout: 60000 });\n\t\t\tresults.push({\n\t\t\t\tid: \"install_mcp\",\n\t\t\t\tdescription: `Installed Neon MCP server (${mcpScope} scope)`,\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\n\t\t\t// Some editors disable newly added MCP servers by default.\n\t\t\t// Cursor: project-level servers are always disabled initially.\n\t\t\t// Claude Code: newly added servers require user approval.\n\t\t\tconst isClaudeCode =\n\t\t\t\tmcpAgentId === \"claude-code\" ||\n\t\t\t\toptions.agent?.toLowerCase() === \"claude-code\";\n\n\t\t\tif (isCursor && mcpScope === \"project\") {\n\t\t\t\tresults.push({\n\t\t\t\t\tid: \"enable_mcp\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t'Cursor disables project-level MCP servers by default. Open Cursor Settings > MCP and toggle the \"Neon\" server on.',\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\tmanualAction: true,\n\t\t\t\t});\n\t\t\t} else if (isClaudeCode) {\n\t\t\t\tresults.push({\n\t\t\t\t\tid: \"enable_mcp\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t'Claude Code requires approval for newly added MCP servers. When prompted, approve the \"Neon\" MCP server to enable it. You can check MCP server status with /mcp in Claude Code.',\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\tmanualAction: true,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tresults.push({\n\t\t\t\tid: \"install_mcp\",\n\t\t\t\tdescription: \"Failed to install Neon MCP server\",\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: err instanceof Error ? err.message : \"Unknown error\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// Step 3: Install/update skills (skip when bootstrapping — templates bundle skills)\n\tif (isBootstrap) {\n\t\tresults.push({\n\t\t\tid: \"install_skills\",\n\t\t\tdescription: \"Neon agent skills included in template\",\n\t\t\tstatus: \"success\",\n\t\t});\n\t} else {\n\t\tconst skillsScope = (options.skillsScope ?? \"project\") as\n\t\t\t| \"global\"\n\t\t\t| \"project\";\n\t\tconst skillsOk = await ensureSkillsUpToDate(\n\t\t\tagentId,\n\t\t\tskillsScope,\n\t\t\toptions.preview,\n\t\t);\n\t\tif (skillsOk) {\n\t\t\tresults.push({\n\t\t\t\tid: \"install_skills\",\n\t\t\t\tdescription: \"Neon agent skills installed\",\n\t\t\t\tstatus: \"success\",\n\t\t\t});\n\t\t} else {\n\t\t\t// Build the install commands for the agent to run directly\n\t\t\t// (sandboxed environments may block child process writes)\n\t\t\tconst { getSkillList } = await import(\"../skills.js\");\n\t\t\tconst skillList = getSkillList(options.preview);\n\t\t\tconst cmds = skillList.map(\n\t\t\t\t(s) =>\n\t\t\t\t\t`skills add neondatabase/agent-skills --skill ${s} --agent ${agentId}${skillsScope === \"global\" ? \" -g\" : \"\"} -y`,\n\t\t\t);\n\t\t\tresults.push({\n\t\t\t\tid: \"install_skills\",\n\t\t\t\tdescription:\n\t\t\t\t\t\"Failed to install Neon agent skills automatically. Run these commands to install manually:\",\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tcommands: cmds,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Step 4: Install editor extension if requested\n\t// Use the agent-reported IDE (not agent identity) — e.g. Claude Code running in\n\t// Cursor should install the extension for Cursor, not skip it.\n\tif (installExt) {\n\t\tconst extResult = await installExtensionForIde(options.ide ?? agentId);\n\t\tresults.push(extResult);\n\t}\n\n\t// Step 5: Write selected features to .neon under _init for brownfield flows\n\t// (Bootstrap flows already wrote _init in step 0)\n\tif (!isBootstrap && options.features && options.features.length > 0) {\n\t\tconst neonContextPath = resolve(process.cwd(), \".neon\");\n\t\tconst context: Record<string, unknown> = {\n\t\t\t_init: { features: options.features },\n\t\t};\n\t\twriteFileSync(neonContextPath, `${JSON.stringify(context, null, 2)}\\n`);\n\t}\n\n\tconst allSucceeded = results.every((r) => r.status === \"success\");\n\n\t// Build args to chain to the getting-started phase as a separate CLI call.\n\t// This ensures the agent gets a clean response with ONLY the getting-started\n\t// action — no competing \"results\" array to distract it.\n\tconst gettingStartedData: Record<string, unknown> = {};\n\tif (options.connectionString) gettingStartedData.hasConnectionString = true;\n\tif (options.framework) gettingStartedData.framework = options.framework;\n\tif (options.orm) gettingStartedData.orm = options.orm;\n\tif (options.migrationTool)\n\t\tgettingStartedData.migrationTool = options.migrationTool;\n\tif (options.migrationDir)\n\t\tgettingStartedData.migrationDir = options.migrationDir;\n\t// Pass features so getting-started knows which phases to chain to\n\tconst resolvedFeatures = options.templateRequires ?? options.features;\n\tif (resolvedFeatures && resolvedFeatures.length > 0)\n\t\tgettingStartedData.features = resolvedFeatures;\n\t// Bootstrap implies preview mode (new project in us-east required)\n\tif (isBootstrap) gettingStartedData.preview = true;\n\tconst gettingStartedArgs = [\n\t\t\"getting-started\",\n\t\t\"--json\",\n\t\t\"--data\",\n\t\tJSON.stringify(gettingStartedData),\n\t];\n\n\treturn {\n\t\tphase: \"setup\",\n\t\tstatus: allSucceeded ? \"installed\" : \"partial\",\n\t\tresults,\n\t\tnextAction: {\n\t\t\ttype: \"run_neon_init\",\n\t\t\targs: gettingStartedArgs,\n\t\t},\n\t};\n}\n\nfunction buildInspectionArgs(options: SetupPhaseOptions): string[] {\n\tconst args: string[] = [];\n\tif (options.mcpConfigured !== null && options.mcpConfigured !== undefined) {\n\t\targs.push(\"--mcp-configured\", options.mcpConfigured ? \"true\" : \"false\");\n\t}\n\tif (\n\t\toptions.connectionString !== null &&\n\t\toptions.connectionString !== undefined\n\t) {\n\t\targs.push(\n\t\t\t\"--connection-string\",\n\t\t\toptions.connectionString ? \"true\" : \"false\",\n\t\t);\n\t}\n\tif (options.framework) {\n\t\targs.push(\"--framework\", options.framework);\n\t}\n\tif (options.orm) {\n\t\targs.push(\"--orm\", options.orm);\n\t}\n\tif (options.migrationTool) {\n\t\targs.push(\"--migration-tool\", options.migrationTool);\n\t}\n\tif (options.migrationDir) {\n\t\targs.push(\"--migration-dir\", options.migrationDir);\n\t}\n\tif (options.isVscodeIde !== null && options.isVscodeIde !== undefined) {\n\t\targs.push(\"--is-vscode-ide\", options.isVscodeIde ? \"true\" : \"false\");\n\t}\n\treturn args;\n}\n\n/**\n * Fills in missing filesystem inspection fields by running inspectProject().\n * Agent-reported data (mcpConfigured, agent, mode, scopes) is preserved.\n * CLI-detectable fields (framework, orm, migrations, connectionString, isVscodeIde)\n * are filled in only if not already present.\n */\nasync function mergeCliInspection(\n\toptions: SetupPhaseOptions,\n): Promise<SetupPhaseOptions> {\n\t// If the agent already provided these, no need to re-inspect\n\tif (options.framework !== undefined && options.orm !== undefined) {\n\t\treturn options;\n\t}\n\n\tconst inspection = await inspectProject([\n\t\t{ id: \"connection_string\", description: \"\", lookFor: [] },\n\t\t{ id: \"project_stack\", description: \"\", lookFor: [] },\n\t\t{ id: \"migrations\", description: \"\", lookFor: [] },\n\t\t{ id: \"ide_type\", description: \"\", lookFor: [] },\n\t]);\n\n\t// Also detect IDE if not already reported by the agent\n\tconst ide =\n\t\toptions.ide?.toLowerCase().replace(/\\s+/g, \"-\") ||\n\t\tdetectIde()?.toLowerCase().replace(/\\s+/g, \"-\") ||\n\t\tundefined;\n\n\treturn {\n\t\t...options,\n\t\tide,\n\t\tconnectionString:\n\t\t\toptions.connectionString ??\n\t\t\t(inspection.connectionString as boolean | undefined),\n\t\tframework:\n\t\t\toptions.framework ?? (inspection.framework as string | undefined),\n\t\torm: options.orm ?? (inspection.orm as string | undefined),\n\t\tmigrationTool:\n\t\t\toptions.migrationTool ??\n\t\t\t(inspection.migrationTool as string | undefined),\n\t\tmigrationDir:\n\t\t\toptions.migrationDir ??\n\t\t\t(inspection.migrationDir as string | undefined),\n\t\tisVscodeIde:\n\t\t\toptions.isVscodeIde ??\n\t\t\t(inspection.isVscodeIde as boolean | undefined),\n\t};\n}\n\n/**\n * Checks whether the user is in a VS Code-based IDE that supports extensions.\n * Uses agent-reported `ide` field first, then falls back to `isVscodeIde` from inspection.\n */\nfunction isCursorAgent(options: SetupPhaseOptions): boolean {\n\tconst ide = options.ide?.toLowerCase();\n\tif (ide === \"cursor\") return true;\n\tconst agent = options.agent?.toLowerCase();\n\tif (agent === \"cursor\") return true;\n\treturn false;\n}\n\nfunction isVscodeBasedIde(options: SetupPhaseOptions): boolean {\n\tif (options.ide) {\n\t\tconst ide = options.ide.toLowerCase();\n\t\treturn (\n\t\t\tide === \"cursor\" ||\n\t\t\tide === \"vscode\" ||\n\t\t\tide === \"vs-code\" ||\n\t\t\tide === \"windsurf\"\n\t\t);\n\t}\n\treturn options.isVscodeIde === true;\n}\n\n/**\n * Resolves which IDE to install the extension for.\n * Accepts the agent-reported IDE value (preferred), the agent ID, or\n * falls back to env-var detection.\n */\nfunction resolveEditorForExtension(ideOrAgentId: string): Editor | null {\n\t// Map known IDE/agent identifiers to Editor types\n\tswitch (ideOrAgentId.toLowerCase()) {\n\t\tcase \"cursor\":\n\t\t\treturn \"Cursor\";\n\t\tcase \"vscode\":\n\t\tcase \"vs-code\":\n\t\tcase \"copilot\":\n\t\tcase \"github-copilot\":\n\t\tcase \"github-copilot-cli\":\n\t\t\treturn \"VS Code\";\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\t// Fall back to env-var detection\n\tconst ide = detectIde();\n\tif (ide === \"Cursor\" || ide === \"VS Code\") return ide;\n\n\treturn null;\n}\n\nconst MANUAL_INSTALL_MSG = `Search for \"Neon\" in the extensions panel (Cmd+Shift+X / Ctrl+Shift+X) and install \"Neon Local Connect\" by Databricks.`;\n\n/**\n * Installs the Neon extension for the detected IDE.\n *\n * Uses env-var detection to determine the IDE (not the agent identity),\n * so Claude Code running in Cursor correctly installs for Cursor.\n *\n * Strategy:\n * 1. Try `<editor> --install-extension <id>` directly (uses editor's configured marketplace)\n * 2. If that fails, download .vsix (from proxy or Open VSX) and install via local file\n * 3. If all else fails: return manual install instructions\n */\nasync function installExtensionForIde(agentId: string): Promise<InstallResult> {\n\tconst editorType = resolveEditorForExtension(agentId);\n\tif (!editorType) {\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: MANUAL_INSTALL_MSG,\n\t\t\tstatus: \"success\",\n\t\t\tmanualAction: true,\n\t\t};\n\t}\n\n\tconst editorCmd = await findEditorCommand(editorType);\n\tif (!editorCmd) {\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: MANUAL_INSTALL_MSG,\n\t\t\tstatus: \"success\",\n\t\t\tmanualAction: true,\n\t\t};\n\t}\n\n\t// Try direct marketplace install first (works if editor has marketplace configured)\n\ttry {\n\t\tawait execa(editorCmd, [\"--install-extension\", NEON_EXTENSION_ID], {\n\t\t\tstdio: \"pipe\",\n\t\t\ttimeout: 60000,\n\t\t});\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: `Installed Neon extension for ${editorType}`,\n\t\t\tstatus: \"success\",\n\t\t};\n\t} catch {\n\t\t// Fall through to VSIX download approach\n\t}\n\n\t// Download .vsix and install locally\n\tconst vsixPath = await downloadVsix();\n\tif (!vsixPath) {\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: MANUAL_INSTALL_MSG,\n\t\t\tstatus: \"success\",\n\t\t\tmanualAction: true,\n\t\t};\n\t}\n\n\ttry {\n\t\tawait execa(editorCmd, [\"--install-extension\", vsixPath], {\n\t\t\tstdio: \"pipe\",\n\t\t\ttimeout: 60000,\n\t\t});\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: `Installed Neon extension for ${editorType}`,\n\t\t\tstatus: \"success\",\n\t\t};\n\t} catch {\n\t\treturn {\n\t\t\tid: \"install_extension\",\n\t\t\tdescription: MANUAL_INSTALL_MSG,\n\t\t\tstatus: \"success\",\n\t\t\tmanualAction: true,\n\t\t};\n\t} finally {\n\t\ttry {\n\t\t\tawait unlink(vsixPath);\n\t\t} catch {}\n\t}\n}\n"],"mappings":"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"}
@@ -34,22 +34,22 @@ async function handleStatusPhase(_options) {
34
34
  if (!authed) recommendations.push({
35
35
  priority: "high",
36
36
  message: "Not authenticated with Neon",
37
- command: `neonctl init --agent --data '{"step":"auth"}'`
37
+ command: `neon init --agent --data '{"step":"auth"}'`
38
38
  });
39
39
  if (!hasDatabaseUrl) recommendations.push({
40
40
  priority: "high",
41
41
  message: "No DATABASE_URL found in .env",
42
- command: `neonctl init --agent --data '{"step":"db"}'`
42
+ command: `neon init --agent --data '{"step":"db"}'`
43
43
  });
44
44
  if (!skillsInstalled) recommendations.push({
45
45
  priority: "medium",
46
46
  message: "Neon agent skills not detected in this project",
47
- command: `neonctl init --agent --data '{"step":"skills","install":true}'`
47
+ command: `neon init --agent --data '{"step":"skills","install":true}'`
48
48
  });
49
49
  if (migrationTool && !hasMigrations) recommendations.push({
50
50
  priority: "medium",
51
51
  message: `${migrationTool} detected but no migrations found`,
52
- command: `neonctl init --agent --data '{"step":"migrations"}'`
52
+ command: `neon init --agent --data '{"step":"migrations"}'`
53
53
  });
54
54
  return {
55
55
  auth: { authenticated: authed },
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","names":[],"sources":["../../../src/lib/phases/status.ts"],"sourcesContent":["import { isAuthenticated } from \"../auth.js\";\nimport { inspectProject } from \"../inspect.js\";\nimport type { StatusResponse } from \"../types.js\";\n\nexport interface StatusOptions {\n\tagent?: string;\n}\n\nexport async function handleStatusPhase(\n\t_options: StatusOptions,\n): Promise<StatusResponse> {\n\tconst authed = await isAuthenticated();\n\n\tconst inspection = await inspectProject([\n\t\t{ id: \"mcp_server\", description: \"\", lookFor: [] },\n\t\t{ id: \"database_url\", description: \"\", lookFor: [] },\n\t\t{ id: \"skills\", description: \"\", lookFor: [] },\n\t\t{ id: \"migrations\", description: \"\", lookFor: [] },\n\t]);\n\n\tconst hasDatabaseUrl = inspection.databaseUrl === true;\n\tconst mcpConfigured = inspection.mcpConfigured === true;\n\tconst skillsInstalled = inspection.skillsInstalled === true;\n\tconst migrationTool = (inspection.migrationTool as string | null) ?? null;\n\tconst hasMigrations =\n\t\tmigrationTool !== null &&\n\t\tmigrationTool !== \"none\" &&\n\t\tinspection.migrationDir !== \"none\";\n\n\tconst recommendations: StatusResponse[\"recommendations\"] = [];\n\n\tif (!authed) {\n\t\trecommendations.push({\n\t\t\tpriority: \"high\",\n\t\t\tmessage: \"Not authenticated with Neon\",\n\t\t\tcommand: `neonctl init --agent --data '{\"step\":\"auth\"}'`,\n\t\t});\n\t}\n\n\tif (!hasDatabaseUrl) {\n\t\trecommendations.push({\n\t\t\tpriority: \"high\",\n\t\t\tmessage: \"No DATABASE_URL found in .env\",\n\t\t\tcommand: `neonctl init --agent --data '{\"step\":\"db\"}'`,\n\t\t});\n\t}\n\n\tif (!skillsInstalled) {\n\t\trecommendations.push({\n\t\t\tpriority: \"medium\",\n\t\t\tmessage: \"Neon agent skills not detected in this project\",\n\t\t\tcommand: `neonctl init --agent --data '{\"step\":\"skills\",\"install\":true}'`,\n\t\t});\n\t}\n\n\tif (migrationTool && !hasMigrations) {\n\t\trecommendations.push({\n\t\t\tpriority: \"medium\",\n\t\t\tmessage: `${migrationTool} detected but no migrations found`,\n\t\t\tcommand: `neonctl init --agent --data '{\"step\":\"migrations\"}'`,\n\t\t});\n\t}\n\n\treturn {\n\t\tauth: {\n\t\t\tauthenticated: authed,\n\t\t},\n\t\ttooling: {\n\t\t\tmcpServer: {\n\t\t\t\tconfigured: mcpConfigured,\n\t\t\t\tscope: inspection.mcpScope || null,\n\t\t\t},\n\t\t\tskills: {\n\t\t\t\tinstalled: skillsInstalled,\n\t\t\t\tscope: inspection.skillsScope || null,\n\t\t\t},\n\t\t},\n\t\tproject: {\n\t\t\tdatabaseUrl: hasDatabaseUrl,\n\t\t},\n\t\tmigrations: {\n\t\t\ttool: migrationTool,\n\t\t\thasMigrations,\n\t\t},\n\t\trecommendations,\n\t};\n}\n"],"mappings":";;;AAQA,eAAsB,kBACrB,UAC0B;CAC1B,MAAM,SAAS,MAAM,gBAAgB;CAErC,MAAM,aAAa,MAAM,eAAe;EACvC;GAAE,IAAI;GAAc,aAAa;GAAI,SAAS,CAAC;EAAE;EACjD;GAAE,IAAI;GAAgB,aAAa;GAAI,SAAS,CAAC;EAAE;EACnD;GAAE,IAAI;GAAU,aAAa;GAAI,SAAS,CAAC;EAAE;EAC7C;GAAE,IAAI;GAAc,aAAa;GAAI,SAAS,CAAC;EAAE;CAClD,CAAC;CAED,MAAM,iBAAiB,WAAW,gBAAgB;CAClD,MAAM,gBAAgB,WAAW,kBAAkB;CACnD,MAAM,kBAAkB,WAAW,oBAAoB;CACvD,MAAM,gBAAiB,WAAW,iBAAmC;CACrE,MAAM,gBACL,kBAAkB,QAClB,kBAAkB,UAClB,WAAW,iBAAiB;CAE7B,MAAM,kBAAqD,CAAC;CAE5D,IAAI,CAAC,QACJ,gBAAgB,KAAK;EACpB,UAAU;EACV,SAAS;EACT,SAAS;CACV,CAAC;CAGF,IAAI,CAAC,gBACJ,gBAAgB,KAAK;EACpB,UAAU;EACV,SAAS;EACT,SAAS;CACV,CAAC;CAGF,IAAI,CAAC,iBACJ,gBAAgB,KAAK;EACpB,UAAU;EACV,SAAS;EACT,SAAS;CACV,CAAC;CAGF,IAAI,iBAAiB,CAAC,eACrB,gBAAgB,KAAK;EACpB,UAAU;EACV,SAAS,GAAG,cAAc;EAC1B,SAAS;CACV,CAAC;CAGF,OAAO;EACN,MAAM,EACL,eAAe,OAChB;EACA,SAAS;GACR,WAAW;IACV,YAAY;IACZ,OAAO,WAAW,YAAY;GAC/B;GACA,QAAQ;IACP,WAAW;IACX,OAAO,WAAW,eAAe;GAClC;EACD;EACA,SAAS,EACR,aAAa,eACd;EACA,YAAY;GACX,MAAM;GACN;EACD;EACA;CACD;AACD"}
1
+ {"version":3,"file":"status.js","names":[],"sources":["../../../src/lib/phases/status.ts"],"sourcesContent":["import { isAuthenticated } from \"../auth.js\";\nimport { inspectProject } from \"../inspect.js\";\nimport type { StatusResponse } from \"../types.js\";\n\nexport interface StatusOptions {\n\tagent?: string;\n}\n\nexport async function handleStatusPhase(\n\t_options: StatusOptions,\n): Promise<StatusResponse> {\n\tconst authed = await isAuthenticated();\n\n\tconst inspection = await inspectProject([\n\t\t{ id: \"mcp_server\", description: \"\", lookFor: [] },\n\t\t{ id: \"database_url\", description: \"\", lookFor: [] },\n\t\t{ id: \"skills\", description: \"\", lookFor: [] },\n\t\t{ id: \"migrations\", description: \"\", lookFor: [] },\n\t]);\n\n\tconst hasDatabaseUrl = inspection.databaseUrl === true;\n\tconst mcpConfigured = inspection.mcpConfigured === true;\n\tconst skillsInstalled = inspection.skillsInstalled === true;\n\tconst migrationTool = (inspection.migrationTool as string | null) ?? null;\n\tconst hasMigrations =\n\t\tmigrationTool !== null &&\n\t\tmigrationTool !== \"none\" &&\n\t\tinspection.migrationDir !== \"none\";\n\n\tconst recommendations: StatusResponse[\"recommendations\"] = [];\n\n\tif (!authed) {\n\t\trecommendations.push({\n\t\t\tpriority: \"high\",\n\t\t\tmessage: \"Not authenticated with Neon\",\n\t\t\tcommand: `neon init --agent --data '{\"step\":\"auth\"}'`,\n\t\t});\n\t}\n\n\tif (!hasDatabaseUrl) {\n\t\trecommendations.push({\n\t\t\tpriority: \"high\",\n\t\t\tmessage: \"No DATABASE_URL found in .env\",\n\t\t\tcommand: `neon init --agent --data '{\"step\":\"db\"}'`,\n\t\t});\n\t}\n\n\tif (!skillsInstalled) {\n\t\trecommendations.push({\n\t\t\tpriority: \"medium\",\n\t\t\tmessage: \"Neon agent skills not detected in this project\",\n\t\t\tcommand: `neon init --agent --data '{\"step\":\"skills\",\"install\":true}'`,\n\t\t});\n\t}\n\n\tif (migrationTool && !hasMigrations) {\n\t\trecommendations.push({\n\t\t\tpriority: \"medium\",\n\t\t\tmessage: `${migrationTool} detected but no migrations found`,\n\t\t\tcommand: `neon init --agent --data '{\"step\":\"migrations\"}'`,\n\t\t});\n\t}\n\n\treturn {\n\t\tauth: {\n\t\t\tauthenticated: authed,\n\t\t},\n\t\ttooling: {\n\t\t\tmcpServer: {\n\t\t\t\tconfigured: mcpConfigured,\n\t\t\t\tscope: inspection.mcpScope || null,\n\t\t\t},\n\t\t\tskills: {\n\t\t\t\tinstalled: skillsInstalled,\n\t\t\t\tscope: inspection.skillsScope || null,\n\t\t\t},\n\t\t},\n\t\tproject: {\n\t\t\tdatabaseUrl: hasDatabaseUrl,\n\t\t},\n\t\tmigrations: {\n\t\t\ttool: migrationTool,\n\t\t\thasMigrations,\n\t\t},\n\t\trecommendations,\n\t};\n}\n"],"mappings":";;;AAQA,eAAsB,kBACrB,UAC0B;CAC1B,MAAM,SAAS,MAAM,gBAAgB;CAErC,MAAM,aAAa,MAAM,eAAe;EACvC;GAAE,IAAI;GAAc,aAAa;GAAI,SAAS,CAAC;EAAE;EACjD;GAAE,IAAI;GAAgB,aAAa;GAAI,SAAS,CAAC;EAAE;EACnD;GAAE,IAAI;GAAU,aAAa;GAAI,SAAS,CAAC;EAAE;EAC7C;GAAE,IAAI;GAAc,aAAa;GAAI,SAAS,CAAC;EAAE;CAClD,CAAC;CAED,MAAM,iBAAiB,WAAW,gBAAgB;CAClD,MAAM,gBAAgB,WAAW,kBAAkB;CACnD,MAAM,kBAAkB,WAAW,oBAAoB;CACvD,MAAM,gBAAiB,WAAW,iBAAmC;CACrE,MAAM,gBACL,kBAAkB,QAClB,kBAAkB,UAClB,WAAW,iBAAiB;CAE7B,MAAM,kBAAqD,CAAC;CAE5D,IAAI,CAAC,QACJ,gBAAgB,KAAK;EACpB,UAAU;EACV,SAAS;EACT,SAAS;CACV,CAAC;CAGF,IAAI,CAAC,gBACJ,gBAAgB,KAAK;EACpB,UAAU;EACV,SAAS;EACT,SAAS;CACV,CAAC;CAGF,IAAI,CAAC,iBACJ,gBAAgB,KAAK;EACpB,UAAU;EACV,SAAS;EACT,SAAS;CACV,CAAC;CAGF,IAAI,iBAAiB,CAAC,eACrB,gBAAgB,KAAK;EACpB,UAAU;EACV,SAAS,GAAG,cAAc;EAC1B,SAAS;CACV,CAAC;CAGF,OAAO;EACN,MAAM,EACL,eAAe,OAChB;EACA,SAAS;GACR,WAAW;IACV,YAAY;IACZ,OAAO,WAAW,YAAY;GAC/B;GACA,QAAQ;IACP,WAAW;IACX,OAAO,WAAW,eAAe;GAClC;EACD;EACA,SAAS,EACR,aAAa,eACd;EACA,YAAY;GACX,MAAM;GACN;EACD;EACA;CACD;AACD"}
@@ -162,7 +162,8 @@ function skillsAreFresh(agent, requiredSkills) {
162
162
  if (requiredSkills.every((skill) => projectSkillDirs.some((dir) => existsSync(resolve(cwd, dir, "skills", skill, "SKILL.md"))))) return true;
163
163
  }
164
164
  } catch {}
165
- const globalDirs = GLOBAL_SKILLS_DIRS[getSkillsAgentName(agent)] ?? [];
165
+ const agentName = getSkillsAgentName(agent);
166
+ const globalDirs = GLOBAL_SKILLS_DIRS[agentName] ?? [];
166
167
  for (const dir of globalDirs) if (requiredSkills.every((skill) => existsSync(resolve(dir, skill, "SKILL.md")))) try {
167
168
  if (now - statSync(resolve(dir, requiredSkills[0], "SKILL.md")).mtimeMs < SKILLS_FRESHNESS_MS) return true;
168
169
  } catch {}
@@ -1 +1 @@
1
- {"version":3,"file":"skills.js","names":["getSkillsAgentNameFromId"],"sources":["../../src/lib/skills.ts"],"sourcesContent":["import { existsSync, statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { log, spinner } from \"@clack/prompts\";\nimport { execa } from \"execa\";\nimport { dim } from \"yoctocolors\";\nimport { getSkillsAgentName as getSkillsAgentNameFromId } from \"./agents.js\";\nimport type { Editor } from \"./types.js\";\n\n/**\n * Ensures the `skills` CLI is globally installed so npx doesn't need\n * to download it (which can fail behind corporate proxies / sandboxes).\n */\nasync function ensureSkillsCli(): Promise<void> {\n\ttry {\n\t\tawait execa(\"skills\", [\"--version\"], { stdio: \"pipe\", timeout: 5000 });\n\t} catch {\n\t\t// Not installed — install it globally\n\t\ttry {\n\t\t\tawait execa(\"npm\", [\"install\", \"-g\", \"skills\"], {\n\t\t\t\tstdio: \"pipe\",\n\t\t\t\ttimeout: 60000,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Best effort — npx will fall back to downloading\n\t\t}\n\t}\n}\n\n/** Base skills installed for all invocations */\nconst BASE_SKILLS = [\"neon\", \"neon-postgres\"];\n\n/** Additional skills installed for preview (non-bootstrap) invocations */\nconst PREVIEW_SKILLS = [\n\t\"neon-object-storage\",\n\t\"neon-functions\",\n\t\"neon-ai-gateway\",\n];\n\n/** Returns the skill list based on whether preview mode is active */\nexport function getSkillList(preview?: boolean): string[] {\n\treturn preview ? [...BASE_SKILLS, ...PREVIEW_SKILLS] : BASE_SKILLS;\n}\n\nconst SKILL_BASE_URL =\n\t\"https://neon.com/docs/ai/skills/neon-postgres/references\";\n\nexport const SKILL_REFERENCE_URLS: Record<string, string> = {\n\tgettingStarted: `${SKILL_BASE_URL}/getting-started.md`,\n\tconnectionMethods: `${SKILL_BASE_URL}/connection-methods.md`,\n\tneonAuth: `${SKILL_BASE_URL}/neon-auth.md`,\n\tserverlessDriver: `${SKILL_BASE_URL}/neon-serverless.md`,\n\tneonCli: `${SKILL_BASE_URL}/neon-cli.md`,\n\tdevtools: `${SKILL_BASE_URL}/devtools.md`,\n\tbranching: `${SKILL_BASE_URL}/branching.md`,\n\tneonJs: `${SKILL_BASE_URL}/neon-js.md`,\n};\n\n/**\n * Fetches the \"Getting Started with Neon\" skill content from the public URL.\n * Returns the markdown content, or null if the fetch fails.\n */\nexport async function fetchSkillContent(): Promise<string | null> {\n\ttry {\n\t\tconst response = await fetch(SKILL_REFERENCE_URLS.gettingStarted, {\n\t\t\tsignal: AbortSignal.timeout(10000),\n\t\t});\n\t\tif (!response.ok) return null;\n\t\treturn await response.text();\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Maps Editor display names to the skills CLI agent name.\n * Used only by the v1 installAgentSkills function.\n */\nfunction editorToSkillsAgent(editor: Editor): string {\n\tswitch (editor) {\n\t\tcase \"Cursor\":\n\t\t\treturn \"cursor\";\n\t\tcase \"VS Code\":\n\t\tcase \"GitHub Copilot CLI\":\n\t\t\treturn \"github-copilot\";\n\t\tcase \"Claude CLI\":\n\t\t\treturn \"claude-code\";\n\t\tcase \"Codex\":\n\t\t\treturn \"codex\";\n\t\tcase \"OpenCode\":\n\t\t\treturn \"opencode\";\n\t\tcase \"Antigravity\":\n\t\t\treturn \"antigravity\";\n\t\tcase \"Cline\":\n\t\tcase \"Cline CLI\":\n\t\t\treturn \"cline\";\n\t\tcase \"Gemini CLI\":\n\t\t\treturn \"gemini-cli\";\n\t\tcase \"Goose\":\n\t\t\treturn \"goose\";\n\t\tcase \"Claude Desktop\":\n\t\t\treturn \"claude-code\";\n\t\tcase \"MCPorter\":\n\t\t\treturn \"mcporter\";\n\t\tcase \"Zed\":\n\t\t\treturn \"zed\";\n\t\tdefault:\n\t\t\treturn \"\";\n\t}\n}\n\nexport interface InstallSkillsOptions {\n\tjson?: boolean;\n\tscope?: \"global\" | \"project\";\n\tpreview?: boolean;\n}\n\n/**\n * Installs Neon agent skills using Vercel's skills CLI.\n */\nexport async function installAgentSkills(\n\tselectedEditors: Editor[],\n\toptions?: InstallSkillsOptions,\n): Promise<boolean> {\n\tconst quiet = options?.json === true;\n\n\tconst editorsWithSkills = selectedEditors.filter(\n\t\t(e) => editorToSkillsAgent(e) !== \"\",\n\t);\n\n\tif (editorsWithSkills.length === 0) {\n\t\treturn true;\n\t}\n\n\tconst skillsSpinner = quiet ? null : spinner();\n\tskillsSpinner?.start(\"Installing agent skills for Neon in this project...\");\n\n\tlet anyFailed = false;\n\n\tawait ensureSkillsCli();\n\tconst skills = getSkillList(options?.preview);\n\n\tfor (const editor of editorsWithSkills) {\n\t\tconst agentName = editorToSkillsAgent(editor);\n\n\t\t// Install one skill at a time — the skills CLI has a bug with multiple\n\t\t// --skill flags where it creates directories but doesn't copy all SKILL.md files.\n\t\tfor (const skill of skills) {\n\t\t\ttry {\n\t\t\t\tawait execa(\n\t\t\t\t\t\"skills\",\n\t\t\t\t\t[\n\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\"neondatabase/agent-skills\",\n\t\t\t\t\t\t\"--skill\",\n\t\t\t\t\t\tskill,\n\t\t\t\t\t\t\"--agent\",\n\t\t\t\t\t\tagentName,\n\t\t\t\t\t\t...(options?.scope === \"global\" ? [\"-g\"] : []),\n\t\t\t\t\t\t\"-y\",\n\t\t\t\t\t],\n\t\t\t\t\t{\n\t\t\t\t\t\tstdio: \"pipe\",\n\t\t\t\t\t\ttimeout: 120000,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!quiet)\n\t\t\t\t\tlog.error(\n\t\t\t\t\t\t`Failed to install skill ${skill} for ${editor}: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n\t\t\t\t\t);\n\t\t\t\tanyFailed = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (anyFailed) {\n\t\tskillsSpinner?.stop(\n\t\t\t\"Agent skills installation for this project completed with errors\",\n\t\t);\n\t\tif (!quiet)\n\t\t\tlog.info(\n\t\t\t\t\"You can manually install skills by running: npx skills add neondatabase/agent-skills --skill neon --skill neon-postgres\",\n\t\t\t);\n\t\treturn false;\n\t}\n\n\tskillsSpinner?.stop(dim(\"Agent skills installed ✓\"));\n\treturn true;\n}\n\n// ---------------------------------------------------------------------------\n// Evergreen skills: ensure skills are up to date (at most once per 12 hours)\n// ---------------------------------------------------------------------------\n\nconst SKILLS_FRESHNESS_MS = 12 * 60 * 60 * 1000; // 12 hours\n\n/**\n * Agent skills directory paths by agent, keyed by scope.\n * These are the directories that `skills add` writes to.\n */\nconst GLOBAL_SKILLS_DIRS: Record<string, string[]> = (() => {\n\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t// .agents/skills is the generic directory used by multiple agents\n\tconst agentsDir = resolve(home, \".agents\", \"skills\");\n\treturn {\n\t\tcursor: [resolve(home, \".cursor\", \"skills\"), agentsDir],\n\t\t\"claude-code\": [resolve(home, \".claude\", \"skills\"), agentsDir],\n\t\t\"github-copilot\": [resolve(home, \".vscode\", \"skills\"), agentsDir],\n\t\tcodex: [resolve(home, \".codex\", \"skills\"), agentsDir],\n\t\tcline: [resolve(home, \".cline\", \"skills\"), agentsDir],\n\t};\n})();\n\n/**\n * Checks whether skills were recently updated (within the freshness window).\n * Checks both project-level (skills-lock.json mtime) and global (skills dir mtime).\n */\nfunction skillsAreFresh(agent: string, requiredSkills: string[]): boolean {\n\tconst now = Date.now();\n\tconst cwd = process.cwd();\n\tconst projectSkillDirs = [\".agents\", \".cursor\", \".claude\"];\n\n\t// Check project-level: ALL required skills must exist on disk\n\t// and skills-lock.json must be recent\n\tconst lockPath = resolve(cwd, \"skills-lock.json\");\n\tif (existsSync(lockPath)) {\n\t\ttry {\n\t\t\tconst mtime = statSync(lockPath).mtimeMs;\n\t\t\tif (now - mtime < SKILLS_FRESHNESS_MS) {\n\t\t\t\tconst allExist = requiredSkills.every((skill) =>\n\t\t\t\t\tprojectSkillDirs.some((dir) =>\n\t\t\t\t\t\texistsSync(\n\t\t\t\t\t\t\tresolve(cwd, dir, \"skills\", skill, \"SKILL.md\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tif (allExist) return true;\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\t// Check global: ALL required skills must exist in agent-specific dirs\n\tconst agentName = getSkillsAgentNameFromId(agent);\n\tconst globalDirs = GLOBAL_SKILLS_DIRS[agentName] ?? [];\n\tfor (const dir of globalDirs) {\n\t\tconst allExist = requiredSkills.every((skill) =>\n\t\t\texistsSync(resolve(dir, skill, \"SKILL.md\")),\n\t\t);\n\t\tif (allExist) {\n\t\t\t// Check freshness of any one skill file\n\t\t\ttry {\n\t\t\t\tconst mtime = statSync(\n\t\t\t\t\tresolve(dir, requiredSkills[0], \"SKILL.md\"),\n\t\t\t\t).mtimeMs;\n\t\t\t\tif (now - mtime < SKILLS_FRESHNESS_MS) return true;\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Ensures Neon agent skills are up to date. Runs `skills add` if the skills\n * haven't been updated within the freshness window (12 hours).\n *\n * Designed to be called from any phase handler — cheap to call repeatedly\n * since it's a no-op when skills are fresh.\n */\nexport async function ensureSkillsUpToDate(\n\tagent: string | undefined,\n\tscope?: \"global\" | \"project\",\n\tpreview?: boolean,\n): Promise<boolean> {\n\tconst resolvedAgent = agent || \"cursor\";\n\tconst skills = getSkillList(preview);\n\tif (skillsAreFresh(resolvedAgent, skills)) return true;\n\n\tawait ensureSkillsCli();\n\tconst agentName = getSkillsAgentNameFromId(resolvedAgent);\n\tlet allOk = true;\n\n\t// Only install skills that don't already have SKILL.md on disk.\n\t// Re-installing existing skills can trigger sandbox permission prompts.\n\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\tconst cwd = process.cwd();\n\tconst checkDirs =\n\t\tscope === \"global\"\n\t\t\t? [\n\t\t\t\t\tresolve(home, \".cursor\", \"skills\"),\n\t\t\t\t\tresolve(home, \".claude\", \"skills\"),\n\t\t\t\t\tresolve(home, \".agents\", \"skills\"),\n\t\t\t\t]\n\t\t\t: [\n\t\t\t\t\tresolve(cwd, \".cursor\", \"skills\"),\n\t\t\t\t\tresolve(cwd, \".claude\", \"skills\"),\n\t\t\t\t\tresolve(cwd, \".agents\", \"skills\"),\n\t\t\t\t];\n\n\tconst missingSkills = skills.filter(\n\t\t(skill) =>\n\t\t\t!checkDirs.some((dir) =>\n\t\t\t\texistsSync(resolve(dir, skill, \"SKILL.md\")),\n\t\t\t),\n\t);\n\n\tif (missingSkills.length === 0) return true;\n\n\t// Install one skill at a time — the skills CLI has a bug with multiple\n\t// --skill flags where it creates directories but doesn't copy all SKILL.md files.\n\tfor (const skill of missingSkills) {\n\t\ttry {\n\t\t\tawait execa(\n\t\t\t\t\"skills\",\n\t\t\t\t[\n\t\t\t\t\t\"add\",\n\t\t\t\t\t\"neondatabase/agent-skills\",\n\t\t\t\t\t\"--skill\",\n\t\t\t\t\tskill,\n\t\t\t\t\t\"--agent\",\n\t\t\t\t\tagentName,\n\t\t\t\t\t...(scope === \"global\" ? [\"-g\"] : []),\n\t\t\t\t\t\"-y\",\n\t\t\t\t],\n\t\t\t\t{ stdio: \"pipe\", timeout: 120000 },\n\t\t\t);\n\t\t} catch {\n\t\t\tallOk = false;\n\t\t}\n\t}\n\n\treturn allOk;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"skills.js","names":["getSkillsAgentNameFromId"],"sources":["../../src/lib/skills.ts"],"sourcesContent":["import { existsSync, statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { log, spinner } from \"@clack/prompts\";\nimport { execa } from \"execa\";\nimport { dim } from \"yoctocolors\";\nimport { getSkillsAgentName as getSkillsAgentNameFromId } from \"./agents.js\";\nimport type { Editor } from \"./types.js\";\n\n/**\n * Ensures the `skills` CLI is globally installed so npx doesn't need\n * to download it (which can fail behind corporate proxies / sandboxes).\n */\nasync function ensureSkillsCli(): Promise<void> {\n\ttry {\n\t\tawait execa(\"skills\", [\"--version\"], { stdio: \"pipe\", timeout: 5000 });\n\t} catch {\n\t\t// Not installed — install it globally\n\t\ttry {\n\t\t\tawait execa(\"npm\", [\"install\", \"-g\", \"skills\"], {\n\t\t\t\tstdio: \"pipe\",\n\t\t\t\ttimeout: 60000,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Best effort — npx will fall back to downloading\n\t\t}\n\t}\n}\n\n/** Base skills installed for all invocations */\nconst BASE_SKILLS = [\"neon\", \"neon-postgres\"];\n\n/** Additional skills installed for preview (non-bootstrap) invocations */\nconst PREVIEW_SKILLS = [\n\t\"neon-object-storage\",\n\t\"neon-functions\",\n\t\"neon-ai-gateway\",\n];\n\n/** Returns the skill list based on whether preview mode is active */\nexport function getSkillList(preview?: boolean): string[] {\n\treturn preview ? [...BASE_SKILLS, ...PREVIEW_SKILLS] : BASE_SKILLS;\n}\n\nconst SKILL_BASE_URL =\n\t\"https://neon.com/docs/ai/skills/neon-postgres/references\";\n\nexport const SKILL_REFERENCE_URLS: Record<string, string> = {\n\tgettingStarted: `${SKILL_BASE_URL}/getting-started.md`,\n\tconnectionMethods: `${SKILL_BASE_URL}/connection-methods.md`,\n\tneonAuth: `${SKILL_BASE_URL}/neon-auth.md`,\n\tserverlessDriver: `${SKILL_BASE_URL}/neon-serverless.md`,\n\tneonCli: `${SKILL_BASE_URL}/neon-cli.md`,\n\tdevtools: `${SKILL_BASE_URL}/devtools.md`,\n\tbranching: `${SKILL_BASE_URL}/branching.md`,\n\tneonJs: `${SKILL_BASE_URL}/neon-js.md`,\n};\n\n/**\n * Fetches the \"Getting Started with Neon\" skill content from the public URL.\n * Returns the markdown content, or null if the fetch fails.\n */\nexport async function fetchSkillContent(): Promise<string | null> {\n\ttry {\n\t\tconst response = await fetch(SKILL_REFERENCE_URLS.gettingStarted, {\n\t\t\tsignal: AbortSignal.timeout(10000),\n\t\t});\n\t\tif (!response.ok) return null;\n\t\treturn await response.text();\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Maps Editor display names to the skills CLI agent name.\n * Used only by the v1 installAgentSkills function.\n */\nfunction editorToSkillsAgent(editor: Editor): string {\n\tswitch (editor) {\n\t\tcase \"Cursor\":\n\t\t\treturn \"cursor\";\n\t\tcase \"VS Code\":\n\t\tcase \"GitHub Copilot CLI\":\n\t\t\treturn \"github-copilot\";\n\t\tcase \"Claude CLI\":\n\t\t\treturn \"claude-code\";\n\t\tcase \"Codex\":\n\t\t\treturn \"codex\";\n\t\tcase \"OpenCode\":\n\t\t\treturn \"opencode\";\n\t\tcase \"Antigravity\":\n\t\t\treturn \"antigravity\";\n\t\tcase \"Cline\":\n\t\tcase \"Cline CLI\":\n\t\t\treturn \"cline\";\n\t\tcase \"Gemini CLI\":\n\t\t\treturn \"gemini-cli\";\n\t\tcase \"Goose\":\n\t\t\treturn \"goose\";\n\t\tcase \"Claude Desktop\":\n\t\t\treturn \"claude-code\";\n\t\tcase \"MCPorter\":\n\t\t\treturn \"mcporter\";\n\t\tcase \"Zed\":\n\t\t\treturn \"zed\";\n\t\tdefault:\n\t\t\treturn \"\";\n\t}\n}\n\nexport interface InstallSkillsOptions {\n\tjson?: boolean;\n\tscope?: \"global\" | \"project\";\n\tpreview?: boolean;\n}\n\n/**\n * Installs Neon agent skills using Vercel's skills CLI.\n */\nexport async function installAgentSkills(\n\tselectedEditors: Editor[],\n\toptions?: InstallSkillsOptions,\n): Promise<boolean> {\n\tconst quiet = options?.json === true;\n\n\tconst editorsWithSkills = selectedEditors.filter(\n\t\t(e) => editorToSkillsAgent(e) !== \"\",\n\t);\n\n\tif (editorsWithSkills.length === 0) {\n\t\treturn true;\n\t}\n\n\tconst skillsSpinner = quiet ? null : spinner();\n\tskillsSpinner?.start(\"Installing agent skills for Neon in this project...\");\n\n\tlet anyFailed = false;\n\n\tawait ensureSkillsCli();\n\tconst skills = getSkillList(options?.preview);\n\n\tfor (const editor of editorsWithSkills) {\n\t\tconst agentName = editorToSkillsAgent(editor);\n\n\t\t// Install one skill at a time — the skills CLI has a bug with multiple\n\t\t// --skill flags where it creates directories but doesn't copy all SKILL.md files.\n\t\tfor (const skill of skills) {\n\t\t\ttry {\n\t\t\t\tawait execa(\n\t\t\t\t\t\"skills\",\n\t\t\t\t\t[\n\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\"neondatabase/agent-skills\",\n\t\t\t\t\t\t\"--skill\",\n\t\t\t\t\t\tskill,\n\t\t\t\t\t\t\"--agent\",\n\t\t\t\t\t\tagentName,\n\t\t\t\t\t\t...(options?.scope === \"global\" ? [\"-g\"] : []),\n\t\t\t\t\t\t\"-y\",\n\t\t\t\t\t],\n\t\t\t\t\t{\n\t\t\t\t\t\tstdio: \"pipe\",\n\t\t\t\t\t\ttimeout: 120000,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!quiet)\n\t\t\t\t\tlog.error(\n\t\t\t\t\t\t`Failed to install skill ${skill} for ${editor}: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n\t\t\t\t\t);\n\t\t\t\tanyFailed = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (anyFailed) {\n\t\tskillsSpinner?.stop(\n\t\t\t\"Agent skills installation for this project completed with errors\",\n\t\t);\n\t\tif (!quiet)\n\t\t\tlog.info(\n\t\t\t\t\"You can manually install skills by running: npx skills add neondatabase/agent-skills --skill neon --skill neon-postgres\",\n\t\t\t);\n\t\treturn false;\n\t}\n\n\tskillsSpinner?.stop(dim(\"Agent skills installed ✓\"));\n\treturn true;\n}\n\n// ---------------------------------------------------------------------------\n// Evergreen skills: ensure skills are up to date (at most once per 12 hours)\n// ---------------------------------------------------------------------------\n\nconst SKILLS_FRESHNESS_MS = 12 * 60 * 60 * 1000; // 12 hours\n\n/**\n * Agent skills directory paths by agent, keyed by scope.\n * These are the directories that `skills add` writes to.\n */\nconst GLOBAL_SKILLS_DIRS: Record<string, string[]> = (() => {\n\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t// .agents/skills is the generic directory used by multiple agents\n\tconst agentsDir = resolve(home, \".agents\", \"skills\");\n\treturn {\n\t\tcursor: [resolve(home, \".cursor\", \"skills\"), agentsDir],\n\t\t\"claude-code\": [resolve(home, \".claude\", \"skills\"), agentsDir],\n\t\t\"github-copilot\": [resolve(home, \".vscode\", \"skills\"), agentsDir],\n\t\tcodex: [resolve(home, \".codex\", \"skills\"), agentsDir],\n\t\tcline: [resolve(home, \".cline\", \"skills\"), agentsDir],\n\t};\n})();\n\n/**\n * Checks whether skills were recently updated (within the freshness window).\n * Checks both project-level (skills-lock.json mtime) and global (skills dir mtime).\n */\nfunction skillsAreFresh(agent: string, requiredSkills: string[]): boolean {\n\tconst now = Date.now();\n\tconst cwd = process.cwd();\n\tconst projectSkillDirs = [\".agents\", \".cursor\", \".claude\"];\n\n\t// Check project-level: ALL required skills must exist on disk\n\t// and skills-lock.json must be recent\n\tconst lockPath = resolve(cwd, \"skills-lock.json\");\n\tif (existsSync(lockPath)) {\n\t\ttry {\n\t\t\tconst mtime = statSync(lockPath).mtimeMs;\n\t\t\tif (now - mtime < SKILLS_FRESHNESS_MS) {\n\t\t\t\tconst allExist = requiredSkills.every((skill) =>\n\t\t\t\t\tprojectSkillDirs.some((dir) =>\n\t\t\t\t\t\texistsSync(\n\t\t\t\t\t\t\tresolve(cwd, dir, \"skills\", skill, \"SKILL.md\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tif (allExist) return true;\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\t// Check global: ALL required skills must exist in agent-specific dirs\n\tconst agentName = getSkillsAgentNameFromId(agent);\n\tconst globalDirs = GLOBAL_SKILLS_DIRS[agentName] ?? [];\n\tfor (const dir of globalDirs) {\n\t\tconst allExist = requiredSkills.every((skill) =>\n\t\t\texistsSync(resolve(dir, skill, \"SKILL.md\")),\n\t\t);\n\t\tif (allExist) {\n\t\t\t// Check freshness of any one skill file\n\t\t\ttry {\n\t\t\t\tconst mtime = statSync(\n\t\t\t\t\tresolve(dir, requiredSkills[0], \"SKILL.md\"),\n\t\t\t\t).mtimeMs;\n\t\t\t\tif (now - mtime < SKILLS_FRESHNESS_MS) return true;\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Ensures Neon agent skills are up to date. Runs `skills add` if the skills\n * haven't been updated within the freshness window (12 hours).\n *\n * Designed to be called from any phase handler — cheap to call repeatedly\n * since it's a no-op when skills are fresh.\n */\nexport async function ensureSkillsUpToDate(\n\tagent: string | undefined,\n\tscope?: \"global\" | \"project\",\n\tpreview?: boolean,\n): Promise<boolean> {\n\tconst resolvedAgent = agent || \"cursor\";\n\tconst skills = getSkillList(preview);\n\tif (skillsAreFresh(resolvedAgent, skills)) return true;\n\n\tawait ensureSkillsCli();\n\tconst agentName = getSkillsAgentNameFromId(resolvedAgent);\n\tlet allOk = true;\n\n\t// Only install skills that don't already have SKILL.md on disk.\n\t// Re-installing existing skills can trigger sandbox permission prompts.\n\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\tconst cwd = process.cwd();\n\tconst checkDirs =\n\t\tscope === \"global\"\n\t\t\t? [\n\t\t\t\t\tresolve(home, \".cursor\", \"skills\"),\n\t\t\t\t\tresolve(home, \".claude\", \"skills\"),\n\t\t\t\t\tresolve(home, \".agents\", \"skills\"),\n\t\t\t\t]\n\t\t\t: [\n\t\t\t\t\tresolve(cwd, \".cursor\", \"skills\"),\n\t\t\t\t\tresolve(cwd, \".claude\", \"skills\"),\n\t\t\t\t\tresolve(cwd, \".agents\", \"skills\"),\n\t\t\t\t];\n\n\tconst missingSkills = skills.filter(\n\t\t(skill) =>\n\t\t\t!checkDirs.some((dir) =>\n\t\t\t\texistsSync(resolve(dir, skill, \"SKILL.md\")),\n\t\t\t),\n\t);\n\n\tif (missingSkills.length === 0) return true;\n\n\t// Install one skill at a time — the skills CLI has a bug with multiple\n\t// --skill flags where it creates directories but doesn't copy all SKILL.md files.\n\tfor (const skill of missingSkills) {\n\t\ttry {\n\t\t\tawait execa(\n\t\t\t\t\"skills\",\n\t\t\t\t[\n\t\t\t\t\t\"add\",\n\t\t\t\t\t\"neondatabase/agent-skills\",\n\t\t\t\t\t\"--skill\",\n\t\t\t\t\tskill,\n\t\t\t\t\t\"--agent\",\n\t\t\t\t\tagentName,\n\t\t\t\t\t...(scope === \"global\" ? [\"-g\"] : []),\n\t\t\t\t\t\"-y\",\n\t\t\t\t],\n\t\t\t\t{ stdio: \"pipe\", timeout: 120000 },\n\t\t\t);\n\t\t} catch {\n\t\t\tallOk = false;\n\t\t}\n\t}\n\n\treturn allOk;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon-init",
3
- "version": "0.20.0",
3
+ "version": "0.20.2",
4
4
  "description": "Initialize Neon projects",
5
5
  "keywords": [
6
6
  "neon",
@@ -40,10 +40,10 @@
40
40
  "devDependencies": {
41
41
  "@types/node": "^20.19.0",
42
42
  "@types/yargs": "^17.0.35",
43
- "@vitest/coverage-v8": "3.0.9",
43
+ "@vitest/coverage-v8": "^3.0.9",
44
44
  "console-fail-test": "0.5.0",
45
45
  "tsdown": "^0.14.1",
46
- "typescript": "^5.8.2",
46
+ "typescript": "^5.9.0",
47
47
  "vitest": "^3.0.9"
48
48
  },
49
49
  "dependencies": {