neon 4.7.0 → 4.9.0

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.
Files changed (58) hide show
  1. package/README.md +49 -59
  2. package/dist/_chunks/{auth_selection-ktL6uoEe.js → auth_selection-pitMFzDe.js} +1 -1
  3. package/dist/_chunks/{credential_io-B1JxWELN.js → credential_io-YeAxg9Xn.js} +2 -2
  4. package/dist/_chunks/{paths-g3DRqJiD.js → paths-DMq0Lt7a.js} +1 -1
  5. package/dist/_chunks/{profiles-D95AeZ5v.js → profiles-CvnFEQyd.js} +1 -1
  6. package/dist/analytics.js +2 -2
  7. package/dist/auth_context.js +1 -1
  8. package/dist/commands/auth.js +12 -9
  9. package/dist/commands/bootstrap.js +72 -43
  10. package/dist/commands/branches.js +29 -14
  11. package/dist/commands/claim.js +1 -1
  12. package/dist/commands/init.js +80 -79
  13. package/dist/commands/link.js +78 -63
  14. package/dist/commands/mcp.js +1 -1
  15. package/dist/commands/plugins.js +4 -4
  16. package/dist/commands/profile.js +4 -4
  17. package/dist/commands/projects.js +17 -6
  18. package/dist/commands/skills.js +1 -1
  19. package/dist/config.js +1 -1
  20. package/dist/credential_io.js +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/init/child.js +36 -0
  23. package/dist/init/plan.js +47 -0
  24. package/dist/init/tooling.js +43 -0
  25. package/dist/init/wizard.js +42 -0
  26. package/dist/mcp/targets.js +1 -1
  27. package/dist/plugins/plan.js +1 -1
  28. package/dist/plugins/targets.js +5 -1
  29. package/dist/retire_credential.js +1 -1
  30. package/dist/utils/middlewares.js +1 -1
  31. package/dist/utils/package_manager.js +10 -5
  32. package/package.json +15 -16
  33. package/dist/init/auth.js +0 -53
  34. package/dist/init/build_config.js +0 -4
  35. package/dist/init/detect_agent.js +0 -63
  36. package/dist/init/editors.js +0 -19
  37. package/dist/init/enrich_output.js +0 -61
  38. package/dist/init/extension.js +0 -154
  39. package/dist/init/inspect.js +0 -249
  40. package/dist/init/interactive.js +0 -529
  41. package/dist/init/neonctl.js +0 -139
  42. package/dist/init/orchestrate.js +0 -189
  43. package/dist/init/phases/auth.js +0 -195
  44. package/dist/init/phases/cleanup.js +0 -27
  45. package/dist/init/phases/db.js +0 -257
  46. package/dist/init/phases/getting_started.js +0 -218
  47. package/dist/init/phases/mcp.js +0 -219
  48. package/dist/init/phases/migrations.js +0 -250
  49. package/dist/init/phases/neon_auth.js +0 -116
  50. package/dist/init/phases/setup.js +0 -582
  51. package/dist/init/phases/skills.js +0 -86
  52. package/dist/init/phases/status.js +0 -82
  53. package/dist/init/profile_cli.js +0 -56
  54. package/dist/init/resolve_context.js +0 -105
  55. package/dist/init/route_command.js +0 -93
  56. package/dist/init/skills.js +0 -206
  57. package/dist/init/types.js +0 -1
  58. package/dist/init/vsix.js +0 -89
@@ -1,582 +0,0 @@
1
- import { FALLBACK_TEMPLATES, fetchTemplates, findTemplate, scaffoldTemplate } from "../bootstrap.js";
2
- import { detectIde, isCursorInstalled, isVSCodeInstalled } from "../detect_agent.js";
3
- import { installNeonMcpServer } from "../../mcp/install.js";
4
- import { agentSupportsProjectMcp, listMcpAgentIds, resolveAddMcpAgentId, tryResolveAddMcpAgentId } from "../../mcp/agents.js";
5
- import { getSkillsAgentName, supportsSkills } from "../agents.js";
6
- import { NEON_EXTENSION_ID, downloadVsix } from "../vsix.js";
7
- import { findEditorCommand } from "../extension.js";
8
- import { inspectProject } from "../inspect.js";
9
- import { ensureNeonctl } from "../neonctl.js";
10
- import { ensureSkillsUpToDate } from "../skills.js";
11
- import { writeFileSync } from "node:fs";
12
- import { resolve } from "node:path";
13
- import { unlink } from "node:fs/promises";
14
- import { execa } from "execa";
15
- //#region src/init/phases/setup.ts
16
- /**
17
- * Comprehensive setup phase: inspects repo state, collects user preferences,
18
- * then batches all installation commands together.
19
- *
20
- * With --data JSON, the agent sends inspection results AND user preferences
21
- * in a single call, so the CLI can go straight to installation.
22
- */
23
- async function handleSetupPhase(options) {
24
- if (typeof options.features === "string") options.features = options.features.split(",").map((f) => f.trim());
25
- const templateWasAnswered = options.template !== void 0;
26
- if (options.template === "none") options.template = void 0;
27
- if (options.template && !options.templateRequires) {
28
- const selected = (await fetchTemplates()).find((t) => t.id === options.template);
29
- if (selected) options.templateRequires = selected.requires;
30
- }
31
- if (options.execute) return executeBatchedInstallation(await mergeCliInspection(options));
32
- if ((options.mode || templateWasAnswered) && options.mode !== "customize" && options.mode !== "custom") {
33
- const merged = await mergeCliInspection(options);
34
- return executeBatchedInstallation({
35
- ...merged,
36
- mcpScope: merged.mcpScope ?? "global",
37
- skillsScope: merged.skillsScope ?? "project",
38
- installExtension: false
39
- });
40
- }
41
- if (options.mode === "customize" || options.mode === "custom") {
42
- const merged = await mergeCliInspection(options);
43
- const shouldInstallExt = merged.installExtension ?? isVscodeBasedIde(merged);
44
- return executeBatchedInstallation({
45
- ...merged,
46
- mcpScope: merged.mcpScope ?? "global",
47
- skillsScope: merged.skillsScope ?? "project",
48
- installExtension: shouldInstallExt
49
- });
50
- }
51
- return buildBulkInspection(options);
52
- }
53
- function buildTemplatePreference(templates) {
54
- return [{
55
- id: "template",
56
- question: "No application was detected in this directory. Would you like to scaffold a new project from a template?",
57
- phase: "before_checks",
58
- options: [...templates.map((t) => {
59
- const tools = t.tools && t.tools.length > 0 ? ` (${t.tools.join(", ")})` : "";
60
- return {
61
- value: t.id,
62
- label: `${t.title}${tools} — ${t.description}`
63
- };
64
- }), {
65
- value: "none",
66
- label: "No thanks — continue without scaffolding"
67
- }],
68
- default: "none"
69
- }];
70
- }
71
- async function buildBulkInspection(options) {
72
- const hasApp = options.hasApp !== false;
73
- const detectedIde = detectIde();
74
- const installedEditors = [];
75
- if (!detectedIde) {
76
- if (isCursorInstalled()) installedEditors.push("cursor");
77
- if (isVSCodeInstalled()) installedEditors.push("vscode");
78
- }
79
- let templatePreferences = [];
80
- if (!hasApp) {
81
- let templates = FALLBACK_TEMPLATES;
82
- try {
83
- const fetched = await fetchTemplates();
84
- if (fetched && fetched.length > 0) templates = fetched;
85
- } catch {}
86
- templatePreferences = buildTemplatePreference(templates);
87
- }
88
- return {
89
- phase: "setup",
90
- status: hasApp ? "pending" : "bootstrap_needed",
91
- detectedIde: detectedIde?.toLowerCase() ?? null,
92
- installedEditors: installedEditors.length > 0 ? installedEditors : null,
93
- mcpConfigured: options.mcpConfigured ?? null,
94
- mcpScope: options.mcpScope ?? null,
95
- skillsInstalled: options.skillsInstalled ?? null,
96
- skillsScope: options.skillsScope ?? null,
97
- nextAction: {
98
- type: "agent_check",
99
- instructions: [
100
- "IMPORTANT: Do NOT summarize this response or ask the user for consent/confirmation before starting. Proceed IMMEDIATELY with the first userPreference question.",
101
- "",
102
- hasApp ? "Perform the agent checks listed above (MCP server status and your agent identity), then present each userPreference question to the user ONE AT A TIME, in order. Wait for the user's answer before showing the next question. Respect the `condition` field — only show a question if its condition is met." : "No application was detected in this directory. Ask the user if they'd like to scaffold a new project from a template (the `template` preference). Present ALL template options and the 'No thanks' option — do NOT auto-select even if there is only one template. If the user selects a template, the scaffolded template includes agent skills so skills installation will be skipped. If the user chooses 'none', continue with the remaining setup preferences normally. Then perform the agent checks and present the remaining preferences ONE AT A TIME.",
103
- "",
104
- `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.`,
105
- "",
106
- "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.",
107
- "",
108
- "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.",
109
- "",
110
- detectedIde ? `The CLI has detected the IDE as: ${detectedIde.toLowerCase()}. Include this as the "ide" field in your reportBack data. IMPORTANT: The IDE and the agent are different — you may be Claude Code (agent) running inside Cursor (IDE). The extension installs into the IDE, so if the IDE is Cursor or VS Code, the extension IS applicable even if you are Claude Code.` : installedEditors.length > 0 ? `No IDE detected, but the following editors are installed: ${installedEditors.join(", ")}. The "installedEditors" field in this response lists them. If the user wants the extension installed, ask which editor to install it for and include that as the "ide" field in your reportBack data. If not, set "ide" to "none".` : `No IDE or supported editors detected. Set "ide" to "none" in your reportBack data.`,
111
- "",
112
- "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."
113
- ].join("\n"),
114
- checks: [
115
- {
116
- id: "neon",
117
- description: "The Neon CLI will be installed or updated automatically (no action needed from the agent)",
118
- lookFor: []
119
- },
120
- {
121
- id: "mcp_server",
122
- description: "Check if the Neon MCP server is already configured in your MCP server list",
123
- lookFor: ["An MCP server entry named 'Neon' or with URL containing 'mcp.neon.tech'"]
124
- },
125
- {
126
- id: "agent_type",
127
- description: "Identify which coding agent is running this command",
128
- lookFor: [`Determine which agent you are: ${listMcpAgentIds().join(", ")}`, "Report your own agent identifier — this is used to configure the MCP server for the correct tool"]
129
- },
130
- ...detectedIde ? [{
131
- id: "extension_installed",
132
- description: "Check if the Neon editor extension (databricks.neon-local-connect) is already installed in the IDE (NOT the agent — e.g. if you are Claude Code running inside Cursor, check Cursor's extensions)",
133
- lookFor: ["Run the IDE's --list-extensions command or check installed extensions for 'databricks.neon-local-connect' or 'Neon Local Connect'", "If the extension is found, set installExtension to false in your reportBack data and SKIP the installExtension question"]
134
- }] : []
135
- ],
136
- userPreferences: [
137
- ...templatePreferences,
138
- ...hasApp ? [{
139
- id: "features",
140
- question: "Which Neon features would you like to enable for this project?",
141
- phase: "after_checks",
142
- options: [{
143
- value: "database",
144
- label: "Database (always included)"
145
- }, {
146
- value: "database,auth",
147
- label: "Database + Neon Auth (adds authentication via Neon)"
148
- }],
149
- default: "database",
150
- context: "Database connectivity is always set up. Neon Auth adds user authentication powered by Neon. More features (Functions, AI Gateway, Object Storage) will be available soon."
151
- }] : [],
152
- ...(() => {
153
- const isPartialSkills = String(options.skillsScope ?? "").includes("partial");
154
- const needsMcpChoice = !options.mcpConfigured;
155
- const needsSkillsChoice = !options.skillsInstalled && !isPartialSkills && !(options.agent && !supportsSkills(options.agent));
156
- if (!(needsMcpChoice || needsSkillsChoice)) return [];
157
- return [{
158
- id: "mode",
159
- question: "Use default settings or customize?",
160
- phase: "after_checks",
161
- options: [{
162
- value: "defaults",
163
- label: hasApp ? "Use defaults (Neon CLI, MCP: global, skills: project-level — already-configured components will be skipped)" : "Use defaults (Neon CLI, MCP: global — skills included in template)"
164
- }, {
165
- value: "customize",
166
- label: "Customize installation settings"
167
- }],
168
- default: "defaults"
169
- }];
170
- })(),
171
- {
172
- id: "mcpScope",
173
- question: "Where should the Neon MCP server be configured?",
174
- context: "SKIP this question entirely if the mcp_server check found it is already configured. Only ask if MCP is NOT yet configured. NOTE: Cursor disables project-level MCP servers by default — if the user is in Cursor, recommend global scope or warn that they will need to manually enable the server in Cursor Settings > MCP.",
175
- phase: "after_checks",
176
- options: [
177
- {
178
- value: "global",
179
- label: "Global (available in all projects)"
180
- },
181
- ...(() => {
182
- const known = options.agent ? tryResolveAddMcpAgentId(options.agent) : void 0;
183
- if (known && !agentSupportsProjectMcp(known)) return [];
184
- return [{
185
- value: "project",
186
- label: "Project-level (scoped to this project only)"
187
- }];
188
- })(),
189
- {
190
- value: "none",
191
- label: "Skip — do not install the MCP server"
192
- }
193
- ],
194
- default: "global",
195
- condition: {
196
- preferenceId: "mode",
197
- equals: "customize"
198
- },
199
- group: "customize"
200
- },
201
- ...!options.skillsInstalled && !String(options.skillsScope ?? "").includes("partial") && !(options.agent && !supportsSkills(options.agent)) ? [{
202
- id: "skillsScope",
203
- question: "Where should Neon agent skills be installed?",
204
- context: "Only ask if skills are not already installed.",
205
- phase: "after_checks",
206
- options: [{
207
- value: "global",
208
- label: "Global (available in all projects)"
209
- }, {
210
- value: "project",
211
- label: "Project-level (scoped to this project only)"
212
- }],
213
- default: "project",
214
- condition: {
215
- preferenceId: "mode",
216
- equals: "customize"
217
- },
218
- group: "customize"
219
- }] : [],
220
- {
221
- id: "installExtension",
222
- question: "Install the Neon editor extension for local database browsing?",
223
- phase: "after_checks",
224
- options: [{
225
- value: "true",
226
- label: "Yes"
227
- }, {
228
- value: "false",
229
- label: "No"
230
- }],
231
- default: "true",
232
- context: "The extension installs into the IDE, NOT the agent. If the CLI detected the IDE (see detectedIde field), use that — e.g. Claude Code running inside Cursor means the IDE is Cursor and the extension IS applicable. Only applicable for VS Code and Cursor. SKIP this question if the user is NOT in VS Code or Cursor, or if the extension_installed check found it is already installed. Set installExtension to false in reportBack if skipped.",
233
- condition: {
234
- preferenceId: "mode",
235
- equals: "customize"
236
- },
237
- group: "customize"
238
- }
239
- ],
240
- reportBack: {
241
- type: "run_neon_init",
242
- args: [
243
- "setup",
244
- "--json",
245
- "--data",
246
- (() => {
247
- const partialScope = String(options.skillsScope ?? "").replace("-partial", "");
248
- const hasPartial = String(options.skillsScope ?? "").includes("partial");
249
- const previewFlag = options.preview ? ", preview: true" : "";
250
- const needsMcpChoice = !options.mcpConfigured;
251
- const needsSkillsChoice = !options.skillsInstalled && !hasPartial;
252
- const hasModeQuestion = needsMcpChoice || needsSkillsChoice;
253
- const modeField = hasModeQuestion ? ", mode: string" : "";
254
- const knownAgent = options.agent ? tryResolveAddMcpAgentId(options.agent) : void 0;
255
- const mcpScopeUnion = knownAgent && !agentSupportsProjectMcp(knownAgent) ? "'global'|'none'" : "'global'|'project'|'none'";
256
- const mcpField = hasModeQuestion ? `, mcpScope?: ${mcpScopeUnion}` : "";
257
- const skillsField = needsSkillsChoice ? ", skillsScope?: string" : "";
258
- const extField = hasModeQuestion ? ", installExtension?: bool" : "";
259
- return `<json: { agent: string, ide: string, mcpConfigured: bool${options.skillsInstalled || hasPartial ? `, skillsScope: "${options.skillsInstalled ? options.skillsScope || "project" : partialScope}"` : skillsField}${previewFlag}${modeField}${mcpField}${extField}${hasApp ? ", features?: string" : ", template: string"} }>`;
260
- })()
261
- ]
262
- }
263
- }
264
- };
265
- }
266
- /**
267
- * Executes the batched installation of MCP server, skills, and extension.
268
- * Runs commands directly in the CLI process — the agent does NOT run these.
269
- * Returns results and chains to the getting-started phase.
270
- */
271
- async function executeBatchedInstallation(options) {
272
- const mcpScope = options.mcpScope ?? "global";
273
- const agentId = options.agent ?? "cursor";
274
- const mcpAgentId = resolveAddMcpAgentId(agentId);
275
- const installExt = options.installExtension === true;
276
- const results = [];
277
- const isBootstrap = !!options.template;
278
- if (isBootstrap && options.template) try {
279
- const templates = await fetchTemplates();
280
- const template = findTemplate(templates, options.template) ?? findTemplate(FALLBACK_TEMPLATES, options.template);
281
- if (!template) throw new Error(`Unknown template "${options.template}".`);
282
- await scaffoldTemplate(template, ".");
283
- results.push({
284
- id: "bootstrap",
285
- description: `Scaffolded project from template "${options.template}"`,
286
- status: "success"
287
- });
288
- if (options.templateRequires) {
289
- const neonContextPath = resolve(process.cwd(), ".neon");
290
- const context = { _init: { features: options.templateRequires } };
291
- writeFileSync(neonContextPath, `${JSON.stringify(context, null, 2)}\n`);
292
- }
293
- } catch (err) {
294
- results.push({
295
- id: "bootstrap",
296
- description: `Failed to scaffold project from template "${options.template}"`,
297
- status: "failed",
298
- error: err instanceof Error ? err.message : "Unknown error"
299
- });
300
- }
301
- const neonctlResult = await ensureNeonctl();
302
- switch (neonctlResult.status) {
303
- case "already_current":
304
- results.push({
305
- id: "neon",
306
- description: `Neon CLI is up to date (v${neonctlResult.version})`,
307
- status: "success"
308
- });
309
- break;
310
- case "installed":
311
- results.push({
312
- id: "neon",
313
- description: `Installed Neon CLI (v${neonctlResult.version})`,
314
- status: "success"
315
- });
316
- break;
317
- case "updated":
318
- results.push({
319
- id: "neon",
320
- description: `Updated Neon CLI to v${neonctlResult.version}`,
321
- status: "success"
322
- });
323
- break;
324
- case "failed": results.push({
325
- id: "neon",
326
- description: "Failed to install Neon CLI",
327
- status: "failed",
328
- error: neonctlResult.error
329
- });
330
- }
331
- const isCursor = mcpAgentId === "cursor" || options.ide?.toLowerCase() === "cursor" || options.agent?.toLowerCase() === "cursor";
332
- if (mcpScope === "none") results.push({
333
- id: "skip_mcp",
334
- description: "Neon MCP server installation skipped by user",
335
- status: "success"
336
- });
337
- else if (options.mcpConfigured) results.push({
338
- id: "skip_mcp",
339
- description: "Neon MCP server already configured",
340
- status: "success"
341
- });
342
- else {
343
- const installed = installNeonMcpServer({
344
- agent: mcpAgentId,
345
- scope: mcpScope === "project" ? "project" : "global",
346
- cwd: process.cwd()
347
- });
348
- if (installed.ok) {
349
- results.push({
350
- id: "install_mcp",
351
- description: `Installed Neon MCP server (${mcpScope} scope)`,
352
- status: "success"
353
- });
354
- const isClaudeCode = mcpAgentId === "claude-code" || options.agent?.toLowerCase() === "claude-code";
355
- if (isCursor && mcpScope === "project") results.push({
356
- id: "enable_mcp",
357
- description: "Cursor disables project-level MCP servers by default. Open Cursor Settings > MCP and toggle the \"Neon\" server on.",
358
- status: "success",
359
- manualAction: true
360
- });
361
- else if (isClaudeCode) results.push({
362
- id: "enable_mcp",
363
- description: "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.",
364
- status: "success",
365
- manualAction: true
366
- });
367
- } else if (installed.unsupported) {
368
- const projectUnsupported = mcpScope === "project" && !agentSupportsProjectMcp(mcpAgentId);
369
- results.push({
370
- id: "install_mcp",
371
- description: installed.error,
372
- status: projectUnsupported ? "failed" : "success",
373
- ...projectUnsupported ? { error: installed.error } : { manualAction: true }
374
- });
375
- } else results.push({
376
- id: "install_mcp",
377
- description: "Failed to install Neon MCP server",
378
- status: "failed",
379
- error: installed.error
380
- });
381
- }
382
- const skillsAgent = getSkillsAgentName(agentId);
383
- if (isBootstrap) results.push({
384
- id: "install_skills",
385
- description: "Neon agent skills included in template",
386
- status: "success"
387
- });
388
- else if (!skillsAgent) results.push({
389
- id: "skip_skills",
390
- description: "No Neon agent skills target for this agent",
391
- status: "success"
392
- });
393
- else {
394
- const skillsScope = options.skillsScope ?? "project";
395
- if (await ensureSkillsUpToDate(agentId, skillsScope, options.preview)) results.push({
396
- id: "install_skills",
397
- description: "Neon agent skills installed",
398
- status: "success"
399
- });
400
- else {
401
- const { getSkillList } = await import("../skills.js");
402
- const cmds = getSkillList(options.preview).map((s) => `skills add neondatabase/agent-skills --skill ${s} --agent ${skillsAgent}${skillsScope === "global" ? " -g" : ""} -y`);
403
- results.push({
404
- id: "install_skills",
405
- description: "Failed to install Neon agent skills automatically. Run these commands to install manually:",
406
- status: "failed",
407
- commands: cmds
408
- });
409
- }
410
- }
411
- if (installExt) {
412
- const extResult = await installExtensionForIde(options.ide ?? agentId);
413
- results.push(extResult);
414
- }
415
- if (!isBootstrap && options.features && options.features.length > 0) {
416
- const neonContextPath = resolve(process.cwd(), ".neon");
417
- const context = { _init: { features: options.features } };
418
- writeFileSync(neonContextPath, `${JSON.stringify(context, null, 2)}\n`);
419
- }
420
- const allSucceeded = results.every((r) => r.status === "success");
421
- const gettingStartedData = {};
422
- if (options.connectionString) gettingStartedData.hasConnectionString = true;
423
- if (options.framework) gettingStartedData.framework = options.framework;
424
- if (options.orm) gettingStartedData.orm = options.orm;
425
- if (options.migrationTool) gettingStartedData.migrationTool = options.migrationTool;
426
- if (options.migrationDir) gettingStartedData.migrationDir = options.migrationDir;
427
- const resolvedFeatures = options.templateRequires ?? options.features;
428
- if (resolvedFeatures && resolvedFeatures.length > 0) gettingStartedData.features = resolvedFeatures;
429
- if (isBootstrap) gettingStartedData.preview = true;
430
- const gettingStartedArgs = [
431
- "getting-started",
432
- "--json",
433
- "--data",
434
- JSON.stringify(gettingStartedData)
435
- ];
436
- return {
437
- phase: "setup",
438
- status: allSucceeded ? "installed" : "partial",
439
- results,
440
- nextAction: {
441
- type: "run_neon_init",
442
- args: gettingStartedArgs
443
- }
444
- };
445
- }
446
- /**
447
- * Fills in missing filesystem inspection fields by running inspectProject().
448
- * Agent-reported data (mcpConfigured, agent, mode, scopes) is preserved.
449
- * CLI-detectable fields (framework, orm, migrations, connectionString, isVscodeIde)
450
- * are filled in only if not already present.
451
- */
452
- async function mergeCliInspection(options) {
453
- if (options.framework !== void 0 && options.orm !== void 0) return options;
454
- const inspection = await inspectProject([
455
- {
456
- id: "connection_string",
457
- description: "",
458
- lookFor: []
459
- },
460
- {
461
- id: "project_stack",
462
- description: "",
463
- lookFor: []
464
- },
465
- {
466
- id: "migrations",
467
- description: "",
468
- lookFor: []
469
- },
470
- {
471
- id: "ide_type",
472
- description: "",
473
- lookFor: []
474
- }
475
- ]);
476
- const ide = options.ide?.toLowerCase().replace(/\s+/g, "-") || detectIde()?.toLowerCase().replace(/\s+/g, "-") || void 0;
477
- return {
478
- ...options,
479
- ide,
480
- connectionString: options.connectionString ?? inspection.connectionString,
481
- framework: options.framework ?? inspection.framework,
482
- orm: options.orm ?? inspection.orm,
483
- migrationTool: options.migrationTool ?? inspection.migrationTool,
484
- migrationDir: options.migrationDir ?? inspection.migrationDir,
485
- isVscodeIde: options.isVscodeIde ?? inspection.isVscodeIde
486
- };
487
- }
488
- function isVscodeBasedIde(options) {
489
- if (options.ide) {
490
- const ide = options.ide.toLowerCase();
491
- return ide === "cursor" || ide === "vscode" || ide === "vs-code";
492
- }
493
- return options.isVscodeIde === true;
494
- }
495
- /**
496
- * Resolves which IDE to install the extension for.
497
- * Accepts the agent-reported IDE value (preferred), the agent ID, or
498
- * falls back to env-var detection.
499
- */
500
- function resolveEditorForExtension(ideOrAgentId) {
501
- switch (ideOrAgentId.toLowerCase()) {
502
- case "cursor": return "Cursor";
503
- case "vscode":
504
- case "vs-code":
505
- case "copilot":
506
- case "github-copilot":
507
- case "github-copilot-cli": return "VS Code";
508
- }
509
- const ide = detectIde();
510
- if (ide === "Cursor" || ide === "VS Code") return ide;
511
- return null;
512
- }
513
- const MANUAL_INSTALL_MSG = `Search for "Neon" in the extensions panel (Cmd+Shift+X / Ctrl+Shift+X) and install "Neon Local Connect" by Databricks.`;
514
- /**
515
- * Installs the Neon extension for the detected IDE.
516
- *
517
- * Uses env-var detection to determine the IDE (not the agent identity),
518
- * so Claude Code running in Cursor correctly installs for Cursor.
519
- *
520
- * Strategy:
521
- * 1. Try `<editor> --install-extension <id>` directly (uses editor's configured marketplace)
522
- * 2. If that fails, download .vsix (from proxy or Open VSX) and install via local file
523
- * 3. If all else fails: return manual install instructions
524
- */
525
- async function installExtensionForIde(agentId) {
526
- const editorType = resolveEditorForExtension(agentId);
527
- if (!editorType) return {
528
- id: "install_extension",
529
- description: MANUAL_INSTALL_MSG,
530
- status: "success",
531
- manualAction: true
532
- };
533
- const editorCmd = await findEditorCommand(editorType);
534
- if (!editorCmd) return {
535
- id: "install_extension",
536
- description: MANUAL_INSTALL_MSG,
537
- status: "success",
538
- manualAction: true
539
- };
540
- try {
541
- await execa(editorCmd, ["--install-extension", NEON_EXTENSION_ID], {
542
- stdio: "pipe",
543
- timeout: 6e4
544
- });
545
- return {
546
- id: "install_extension",
547
- description: `Installed Neon extension for ${editorType}`,
548
- status: "success"
549
- };
550
- } catch {}
551
- const vsixPath = await downloadVsix();
552
- if (!vsixPath) return {
553
- id: "install_extension",
554
- description: MANUAL_INSTALL_MSG,
555
- status: "success",
556
- manualAction: true
557
- };
558
- try {
559
- await execa(editorCmd, ["--install-extension", vsixPath], {
560
- stdio: "pipe",
561
- timeout: 6e4
562
- });
563
- return {
564
- id: "install_extension",
565
- description: `Installed Neon extension for ${editorType}`,
566
- status: "success"
567
- };
568
- } catch {
569
- return {
570
- id: "install_extension",
571
- description: MANUAL_INSTALL_MSG,
572
- status: "success",
573
- manualAction: true
574
- };
575
- } finally {
576
- try {
577
- await unlink(vsixPath);
578
- } catch {}
579
- }
580
- }
581
- //#endregion
582
- export { handleSetupPhase };
@@ -1,86 +0,0 @@
1
- import { getSkillsAgentName } from "../agents.js";
2
- import { SKILL_REFERENCE_URLS, getSkillList } from "../skills.js";
3
- //#region src/init/phases/skills.ts
4
- async function handleSkillsPhase(options) {
5
- const agentArgs = options.agent ? [
6
- "--agent",
7
- options.agent,
8
- "--json"
9
- ] : ["--json"];
10
- const skillsAgent = options.agent ? getSkillsAgentName(options.agent) : "claude-code";
11
- if (options.agent && !skillsAgent) return {
12
- phase: "tooling",
13
- status: "skipped",
14
- nextAction: {
15
- type: "run_neon_init",
16
- args: agentArgs
17
- }
18
- };
19
- if (options.status) return {
20
- phase: "tooling",
21
- status: "skills_check",
22
- nextAction: {
23
- type: "agent_check",
24
- checks: [{
25
- id: "skills",
26
- description: "Check if Neon agent skills are installed in this project",
27
- lookFor: ["A skill file referencing 'neon-postgres' (e.g. .cursor/skills/, CLAUDE.md, .cursorrules)", "A .skills/ directory with neon-related content"]
28
- }],
29
- reportBack: {
30
- type: "run_neon_init",
31
- args: [
32
- "skills",
33
- "--json",
34
- ...options.agent ? ["--agent", options.agent] : [],
35
- ...options.update ? ["--update"] : ["--install"]
36
- ]
37
- }
38
- }
39
- };
40
- if (options.install || options.update) return {
41
- phase: "tooling",
42
- status: "installing_skills",
43
- nextAction: {
44
- type: "run_command",
45
- command: getSkillList().map((skill) => `npx -y skills add neondatabase/agent-skills --skill ${skill} --agent ${skillsAgent ?? "claude-code"} -y`).join(" && "),
46
- description: `Installing Neon agent skills for ${skillsAgent}.`,
47
- timeout: 3e4,
48
- onSuccess: {
49
- type: "run_neon_init",
50
- args: agentArgs
51
- },
52
- onFailure: { other: {
53
- type: "run_neon_init",
54
- args: agentArgs
55
- } }
56
- },
57
- skillReferences: SKILL_REFERENCE_URLS
58
- };
59
- return {
60
- phase: "tooling",
61
- status: "skills_available",
62
- nextAction: {
63
- type: "ask_user",
64
- question: "Would you like to install Neon agent skills for this project? Skills provide your AI assistant with Neon-specific knowledge.",
65
- options: [{
66
- value: "yes",
67
- label: "Yes, install skills"
68
- }, {
69
- value: "skip",
70
- label: "Skip for now"
71
- }],
72
- context: "Agent skills include guides for connection patterns, Neon Auth setup, branching, migrations, and more.",
73
- responseMapping: {
74
- yes: { args: [
75
- "skills",
76
- "--json",
77
- ...options.agent ? ["--agent", options.agent] : [],
78
- "--install"
79
- ] },
80
- skip: { args: agentArgs }
81
- }
82
- }
83
- };
84
- }
85
- //#endregion
86
- export { handleSkillsPhase };