neon 4.6.0 → 4.8.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 (57) hide show
  1. package/README.md +48 -113
  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/claim.js +1 -1
  11. package/dist/commands/env.js +3 -20
  12. package/dist/commands/init.js +80 -79
  13. package/dist/commands/link.js +110 -292
  14. package/dist/commands/mcp.js +2 -2
  15. package/dist/commands/plugins.js +3 -3
  16. package/dist/commands/profile.js +4 -4
  17. package/dist/config.js +1 -1
  18. package/dist/credential_io.js +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/init/child.js +36 -0
  21. package/dist/init/plan.js +47 -0
  22. package/dist/init/tooling.js +43 -0
  23. package/dist/init/wizard.js +42 -0
  24. package/dist/mcp/targets.js +1 -1
  25. package/dist/plugins/plan.js +1 -1
  26. package/dist/plugins/targets.js +5 -1
  27. package/dist/retire_credential.js +1 -1
  28. package/dist/utils/cli_name.js +1 -12
  29. package/dist/utils/middlewares.js +1 -1
  30. package/dist/utils/package_manager.js +10 -5
  31. package/package.json +13 -14
  32. package/dist/init/auth.js +0 -53
  33. package/dist/init/build_config.js +0 -4
  34. package/dist/init/detect_agent.js +0 -63
  35. package/dist/init/editors.js +0 -19
  36. package/dist/init/enrich_output.js +0 -61
  37. package/dist/init/extension.js +0 -154
  38. package/dist/init/inspect.js +0 -249
  39. package/dist/init/interactive.js +0 -529
  40. package/dist/init/neonctl.js +0 -139
  41. package/dist/init/orchestrate.js +0 -189
  42. package/dist/init/phases/auth.js +0 -195
  43. package/dist/init/phases/cleanup.js +0 -27
  44. package/dist/init/phases/db.js +0 -257
  45. package/dist/init/phases/getting_started.js +0 -218
  46. package/dist/init/phases/mcp.js +0 -219
  47. package/dist/init/phases/migrations.js +0 -250
  48. package/dist/init/phases/neon_auth.js +0 -116
  49. package/dist/init/phases/setup.js +0 -582
  50. package/dist/init/phases/skills.js +0 -86
  51. package/dist/init/phases/status.js +0 -82
  52. package/dist/init/profile_cli.js +0 -56
  53. package/dist/init/resolve_context.js +0 -105
  54. package/dist/init/route_command.js +0 -93
  55. package/dist/init/skills.js +0 -206
  56. package/dist/init/types.js +0 -1
  57. package/dist/init/vsix.js +0 -89
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { log } from "./log.js";
2
2
  import pkg_default from "./pkg.js";
3
3
  import { isNeonApiError, messageFromBody } from "./api.js";
4
- import { i as defaultDir } from "./_chunks/paths-g3DRqJiD.js";
4
+ import { r as defaultDir } from "./_chunks/paths-DMq0Lt7a.js";
5
5
  import { ensureConfigDir } from "./config.js";
6
6
  import { authFailureMessage, credentialsToClearOn401, getAuthContext } from "./auth_context.js";
7
7
  import { currentContextFile, enrichFromContext } from "./context.js";
@@ -0,0 +1,36 @@
1
+ import { spawn } from "node:child_process";
2
+ //#region src/init/child.ts
3
+ const AUTH_CHILD = /* @__PURE__ */ new Set([
4
+ "bootstrap",
5
+ "link",
6
+ "mcp"
7
+ ]);
8
+ const initChildEnv = (command, overlay, base = process.env) => {
9
+ const child = { ...base };
10
+ if (command === void 0 || !AUTH_CHILD.has(command)) {
11
+ for (const key of Object.keys(child)) if (key.toUpperCase() === "NEON_API_KEY") delete child[key];
12
+ }
13
+ return overlay ? {
14
+ ...child,
15
+ ...overlay
16
+ } : child;
17
+ };
18
+ const spawnCliChild = async (argv, cwd, overlay) => {
19
+ const cli = process.argv[1];
20
+ if (!cli) throw new Error("Cannot re-exec the Neon CLI: process.argv[1] is missing.");
21
+ return new Promise((resolve) => {
22
+ const child = spawn(process.execPath, [cli, ...argv], {
23
+ cwd,
24
+ stdio: "inherit",
25
+ env: initChildEnv(argv[0], overlay)
26
+ });
27
+ child.on("error", () => {
28
+ resolve(false);
29
+ });
30
+ child.on("close", (code) => {
31
+ resolve(code === 0);
32
+ });
33
+ });
34
+ };
35
+ //#endregion
36
+ export { AUTH_CHILD, initChildEnv, spawnCliChild };
@@ -0,0 +1,47 @@
1
+ import { isAbsolute, join, relative, resolve } from "node:path";
2
+ //#region src/init/plan.ts
3
+ const directoryIsEmpty = (names) => names.filter((name) => name !== ".git").length === 0;
4
+ const bootstrapInitStep = (yes) => yes ? [
5
+ "bootstrap",
6
+ ".",
7
+ "--default"
8
+ ] : ["bootstrap", "."];
9
+ const projectContextFile = (projectDir, contextFile) => {
10
+ const resolved = isAbsolute(contextFile) ? contextFile : resolve(projectDir, contextFile);
11
+ const rel = relative(projectDir, resolved);
12
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) return join(projectDir, ".neon");
13
+ return resolved;
14
+ };
15
+ const planAgentSteps = (input) => {
16
+ const y = input.yes ? ["-y"] : [];
17
+ if (input.agentSetup === "plugin") return [["plugins", ...y]];
18
+ if (input.agentSetup === "skills-mcp") return [["skills", ...y], ["mcp", ...y]];
19
+ return [];
20
+ };
21
+ const planExistingInit = (input) => {
22
+ const steps = [...planAgentSteps(input)];
23
+ if (!input.linked) steps.push(input.yes ? ["link", "--yes"] : ["link"]);
24
+ steps.push(input.yes ? [
25
+ "config",
26
+ "init",
27
+ "--services",
28
+ "none"
29
+ ] : ["config", "init"]);
30
+ return steps;
31
+ };
32
+ const resolveInitAgentSetup = async (input) => {
33
+ if (input.yes) return input.hasProjectPlugins ? "plugin" : "skills-mcp";
34
+ if (input.interactive) return input.pick();
35
+ return "skills-mcp";
36
+ };
37
+ const childArgv = (step, forward) => {
38
+ const args = [...step];
39
+ if (forward.configDir) args.push("--config-dir", forward.configDir);
40
+ if (forward.profile) args.push("--profile", forward.profile);
41
+ args.push("--api-host", forward.apiHost);
42
+ args.push("--context-file", forward.contextFile);
43
+ if (forward.analytics === false) args.push("--no-analytics");
44
+ return args;
45
+ };
46
+ //#endregion
47
+ export { bootstrapInitStep, childArgv, directoryIsEmpty, planAgentSteps, planExistingInit, projectContextFile, resolveInitAgentSetup };
@@ -0,0 +1,43 @@
1
+ import { log } from "../log.js";
2
+ import { getCliName } from "../utils/cli_name.js";
3
+ import { AUTH_CHILD } from "./child.js";
4
+ import { childArgv, planAgentSteps, resolveInitAgentSetup } from "./plan.js";
5
+ import { detectInstallablePluginsAgents } from "../plugins/targets.js";
6
+ import { canPickAgentsInteractively } from "../utils/agent_picker.js";
7
+ import { pickAgentSetupInteractively } from "./wizard.js";
8
+ //#region src/init/tooling.ts
9
+ const defaultHasProjectPlugins = async (cwd) => {
10
+ return (await detectInstallablePluginsAgents({
11
+ scope: "project",
12
+ cwd
13
+ })).length > 0;
14
+ };
15
+ const runInitSteps = async (steps, options) => {
16
+ for (const step of steps) {
17
+ log.info("Running `%s %s`", getCliName(), step.join(" "));
18
+ const argv = childArgv(step, options.forward);
19
+ const command = step[0];
20
+ if (!await options.run(argv, options.cwd, command !== void 0 && AUTH_CHILD.has(command) ? options.authEnv : void 0)) throw new Error(`\`${getCliName()} ${step.join(" ")}\` failed.`);
21
+ }
22
+ };
23
+ const runAgentTooling = async (options) => {
24
+ const yes = options.yes;
25
+ const hasProjectPlugins = yes ? await (options.hasProjectPlugins ?? defaultHasProjectPlugins)(options.cwd) : false;
26
+ const interactive = !yes && (options.pickAgentSetup !== void 0 || canPickAgentsInteractively());
27
+ const agentSetup = await resolveInitAgentSetup({
28
+ yes,
29
+ interactive,
30
+ hasProjectPlugins,
31
+ pick: options.pickAgentSetup ?? pickAgentSetupInteractively
32
+ });
33
+ await runInitSteps(planAgentSteps({
34
+ yes,
35
+ agentSetup
36
+ }), options);
37
+ };
38
+ const runScaffoldFollowUp = async (options) => {
39
+ if (!options.skipAgentSetup) await runAgentTooling(options);
40
+ if (options.shouldLink) await runInitSteps([options.linkYes ? ["link", "--yes"] : ["link"]], options);
41
+ };
42
+ //#endregion
43
+ export { runAgentTooling, runInitSteps, runScaffoldFollowUp };
@@ -0,0 +1,42 @@
1
+ import { canPickAgentsInteractively } from "../utils/agent_picker.js";
2
+ import { NEON_PLUGIN_NAME } from "../plugins/run.js";
3
+ import prompts from "prompts";
4
+ //#region src/init/wizard.ts
5
+ const restoreCursorOnAbort = (state) => {
6
+ if (state.aborted) {
7
+ process.stdout.write("\x1B[?25h");
8
+ process.stdout.write("\n");
9
+ process.exit(1);
10
+ }
11
+ };
12
+ const pickAgentSetupInteractively = async () => {
13
+ if (!canPickAgentsInteractively()) throw new Error("No interactive terminal. Pass -y to use defaults, or run this command in a terminal.");
14
+ const { setup } = await prompts({
15
+ onState: restoreCursorOnAbort,
16
+ type: "select",
17
+ name: "setup",
18
+ message: "How should coding agents get Neon in this project?",
19
+ initial: 0,
20
+ choices: [
21
+ {
22
+ title: "Plugin (recommended)",
23
+ value: "plugin",
24
+ description: `Install ${NEON_PLUGIN_NAME} (skills and MCP in one)`
25
+ },
26
+ {
27
+ title: "Skills and MCP separately",
28
+ value: "skills-mcp",
29
+ description: "Install skills, then the MCP server"
30
+ },
31
+ {
32
+ title: "Skip agent setup",
33
+ value: "skip",
34
+ description: "Continue without a plugin, skills, or MCP"
35
+ }
36
+ ]
37
+ });
38
+ if (setup !== "plugin" && setup !== "skills-mcp" && setup !== "skip") throw new Error("Aborted.");
39
+ return setup;
40
+ };
41
+ //#endregion
42
+ export { pickAgentSetupInteractively };
@@ -1,5 +1,5 @@
1
- import { mcpUnsupportedReason } from "./install.js";
2
1
  import { detectInstalledAgents, listMcpAgentIds, resolveAddMcpAgentId, uniqueAgentIds } from "./agents.js";
2
+ import { mcpUnsupportedReason } from "./install.js";
3
3
  import { detectProjectAgents } from "add-mcp";
4
4
  //#region src/mcp/targets.ts
5
5
  function mcpInstallableAgents(scope) {
@@ -1,7 +1,7 @@
1
1
  import { getAgentDisplayName, tryResolveAddMcpAgentId } from "../mcp/agents.js";
2
2
  import "../init/agents.js";
3
- import { agentChoicesFrom, pickAgentsInteractively, resolveAgentSelection } from "../utils/agent_picker.js";
4
3
  import { detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, pluginsInstallableAgents } from "./targets.js";
4
+ import { agentChoicesFrom, pickAgentsInteractively, resolveAgentSelection } from "../utils/agent_picker.js";
5
5
  //#region src/plugins/plan.ts
6
6
  const assertPluginsCanRun = (options) => {
7
7
  if (options.yes || options.interactive || options.hasAgents) return;
@@ -38,6 +38,10 @@ const detectPluginsAgents = async (options) => {
38
38
  const detected = options.scope === "project" ? detectProjectAgents(options.cwd) : await detectInstalledAgents();
39
39
  return uniqueAgentIds(detected).filter((id) => supportsPlugins(id));
40
40
  };
41
+ const detectInstallablePluginsAgents = async (options) => {
42
+ const installable = new Set(pluginsInstallableAgents(options.scope));
43
+ return (await detectPluginsAgents(options)).filter((id) => installable.has(id));
44
+ };
41
45
  const mappedPluginsTargets = (agents, scope) => {
42
46
  const mapped = [];
43
47
  const byTarget = /* @__PURE__ */ new Map();
@@ -61,4 +65,4 @@ const mappedPluginsTargets = (agents, scope) => {
61
65
  return mapped;
62
66
  };
63
67
  //#endregion
64
- export { detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, pluginsInstallableAgents, pluginsMappedAgents, supportsPlugins };
68
+ export { detectInstallablePluginsAgents, detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, pluginsInstallableAgents, pluginsMappedAgents, supportsPlugins };
@@ -1,4 +1,4 @@
1
- import { a as OAUTH, d as scopeOf, i as API_KEY, l as interpretCredentials, t as storeFor, u as isSameCredential } from "./_chunks/credential_io-B1JxWELN.js";
1
+ import { a as OAUTH, d as scopeOf, i as API_KEY, l as interpretCredentials, t as storeFor, u as isSameCredential } from "./_chunks/credential_io-YeAxg9Xn.js";
2
2
  import { log } from "./log.js";
3
3
  import { getApiClient } from "./api.js";
4
4
  import { revokeToken } from "./auth.js";
@@ -1,17 +1,6 @@
1
1
  import { basename } from "node:path";
2
2
  //#region src/utils/cli_name.ts
3
- /**
4
- * The name this CLI was invoked as: `neon` (current) or `neonctl` (legacy alias).
5
- *
6
- * Use this for any user-facing string that suggests a command to run — help text,
7
- * error hints, and especially `--agent` `next_command_template` values, which an agent
8
- * executes verbatim. Hardcoding `neonctl` breaks those on installs of the `neon` package,
9
- * which no longer ships a `neonctl` binary (removed in `neon@2.38.0`).
10
- *
11
- * Derived from `process.argv[1]`, which is fixed for the process lifetime, so the result
12
- * is stable whether this is called at module load or per invocation. Mirrors the name used
13
- * for yargs `.scriptName()` in `index.ts`.
14
- */
3
+ /** Command hints must use the invoked binary so users can run them verbatim. */
15
4
  const getCliName = () => basename(process.argv[1] ?? "") === "neonctl" ? "neonctl" : "neon";
16
5
  //#endregion
17
6
  export { getCliName };
@@ -1,4 +1,4 @@
1
- import { r as recordCredentialInputs } from "../_chunks/auth_selection-ktL6uoEe.js";
1
+ import { r as recordCredentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
2
2
  //#region src/utils/middlewares.ts
3
3
  /**
4
4
  * Forwarding Yargs's default would turn ambient configuration into an explicit child override.
@@ -295,6 +295,14 @@ const DO_NOT_SUBSTITUTE_HINT = "Run this step's `command` exactly as written —
295
295
  * All four managers spell the whole-manifest install `install`.
296
296
  */
297
297
  const formatInstallCommand = (pm, packages, options) => `${pm} ${installArgs(pm, packages, options).join(" ")}`;
298
+ const commandEnv = (overlay, base = process.env) => {
299
+ const child = { ...base };
300
+ for (const key of Object.keys(child)) if (key.toUpperCase() === "NEON_API_KEY") delete child[key];
301
+ return overlay ? {
302
+ ...child,
303
+ ...overlay
304
+ } : child;
305
+ };
298
306
  /**
299
307
  * Run a command inheriting our stdio so the user sees install / link output
300
308
  * live and can answer any prompts the child raises. Resolves to whether it
@@ -306,10 +314,7 @@ const runCommand = (cmd, args, cwd, env) => new Promise((resolvePromise) => {
306
314
  cwd,
307
315
  stdio: "inherit",
308
316
  shell: process.platform === "win32",
309
- ...env ? { env: {
310
- ...process.env,
311
- ...env
312
- } } : {}
317
+ env: commandEnv(env)
313
318
  });
314
319
  child.on("error", (err) => {
315
320
  log.warning("Could not run `%s %s`: %s", cmd, args.join(" "), err instanceof Error ? err.message : String(err));
@@ -321,4 +326,4 @@ const runCommand = (cmd, args, cwd, env) => new Promise((resolvePromise) => {
321
326
  });
322
327
  });
323
328
  //#endregion
324
- export { DO_NOT_SUBSTITUTE_HINT, MISSING_BINARY_HINT, detectInvokingPackageManager, detectProjectPackageManager, formatExecCommand, formatInstallCommand, globalInstallCommand, inferPackageManager, installArgs, installedPackageManagers, resolveInvokingPackageManager, resolvePackageManager, runCommand };
329
+ export { DO_NOT_SUBSTITUTE_HINT, MISSING_BINARY_HINT, commandEnv, detectInvokingPackageManager, detectProjectPackageManager, formatExecCommand, formatInstallCommand, globalInstallCommand, inferPackageManager, installArgs, installedPackageManagers, resolveInvokingPackageManager, resolvePackageManager, runCommand };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon",
3
- "version": "4.6.0",
3
+ "version": "4.8.0",
4
4
  "description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
5
5
  "keywords": [
6
6
  "neon",
@@ -63,9 +63,9 @@
63
63
  "yaml": "^2.9.0",
64
64
  "yargs": "17.7.2",
65
65
  "yoctocolors": "^2.1.2",
66
- "@neon/config": "1.0.4",
67
- "@neon/config-runtime": "1.0.4",
68
- "@neon/sdk": "2.3.0"
66
+ "@neon/config": "1.0.5",
67
+ "@neon/config-runtime": "1.0.5",
68
+ "@neon/sdk": "3.0.0"
69
69
  },
70
70
  "optionalDependencies": {
71
71
  "@napi-rs/keyring": "1.3.0",
@@ -97,7 +97,7 @@
97
97
  "typescript": "^5.9.0",
98
98
  "vitest": "^3.0.9",
99
99
  "@neon-internals/cli-core": "0.0.0",
100
- "@neon-internals/env-core": "0.0.4",
100
+ "@neon-internals/env-core": "0.0.5",
101
101
  "@neon/e2e-harness": "0.0.0"
102
102
  },
103
103
  "publishConfig": {
@@ -120,17 +120,16 @@
120
120
  },
121
121
  "scripts": {
122
122
  "generateParams": "tsx generateOptionsFromSpec.ts",
123
- "codegen": "node scripts/set-vsx-gallery.mjs",
124
123
  "clean": "rm -rf dist",
125
- "build": "pnpm codegen && pnpm generateParams && tsc --noEmit && tsdown && cp src/*.html ./dist",
126
- "build:internal": "node scripts/set-vsx-gallery.mjs https://cursor-vsx-proxy.cloud.databricks.com/gallery && pnpm generateParams && tsc --noEmit && tsdown && cp src/*.html ./dist",
124
+ "build": "pnpm generateParams && tsc --noEmit && tsdown && cp src/*.html ./dist",
125
+ "build:internal": "pnpm build",
127
126
  "bundle": "node pkg.js",
128
- "typecheck": "pnpm codegen && tsc --noEmit",
129
- "lint": "pnpm codegen && pnpm typecheck && biome check src",
127
+ "typecheck": "tsc --noEmit",
128
+ "lint": "pnpm typecheck && biome check src",
130
129
  "lint:fix": "pnpm typecheck && biome check src --write",
131
- "test": "pnpm codegen && pnpm --filter neonctl... build && vitest run",
132
- "test:ci": "pnpm codegen && pnpm build && vitest run",
133
- "test:e2e": "pnpm codegen && pnpm build && vitest run --config vitest.e2e.config.ts",
134
- "test:conformance": "pnpm codegen && vitest run --config tests/psql-conformance/vitest.config.ts"
130
+ "test": "pnpm --filter neonctl... build && vitest run",
131
+ "test:ci": "pnpm build && vitest run",
132
+ "test:e2e": "pnpm build && vitest run --config vitest.e2e.config.ts",
133
+ "test:conformance": "vitest run --config tests/psql-conformance/vitest.config.ts"
135
134
  }
136
135
  }
package/dist/init/auth.js DELETED
@@ -1,53 +0,0 @@
1
- import { l as interpretCredentials, t as storeFor } from "../_chunks/credential_io-B1JxWELN.js";
2
- import { l as locationForName } from "../_chunks/profiles-D95AeZ5v.js";
3
- import { npxNeonArgs, selectedConfigDir, selectedProfileName } from "./profile_cli.js";
4
- import { log } from "@clack/prompts";
5
- import { execa } from "execa";
6
- //#region src/init/auth.ts
7
- /**
8
- * Ensures the Neon CLI is authenticated by running a command that triggers auth if needed
9
- * This will automatically start the OAuth flow if the user isn't already authenticated
10
- */
11
- async function ensureNeonctlAuth(options) {
12
- const quiet = options?.json === true;
13
- if (await getNeonctlAccessToken()) return true;
14
- try {
15
- await execa("npx", npxNeonArgs(["me"]), {
16
- stdio: "inherit",
17
- env: {
18
- ...process.env,
19
- CI: void 0
20
- }
21
- });
22
- return true;
23
- } catch (error) {
24
- const msg = error instanceof Error ? error.message : "Unknown error";
25
- if (!quiet) {
26
- if (msg.includes("interactive auth") || msg.includes("CI")) log.error("Auth requires an interactive terminal. Run neon init in your system terminal (outside the chat) to sign in.");
27
- else log.error(`Authentication failed: ${msg}`);
28
- }
29
- return false;
30
- }
31
- }
32
- /**
33
- * Checks whether the Neon CLI has stored OAuth credentials.
34
- */
35
- async function isAuthenticated() {
36
- return await getNeonctlAccessToken() !== null;
37
- }
38
- /**
39
- * Sharing the reader keeps init aligned with CLI path and API-key handling.
40
- * Unreadable credentials throw so browser sign-in cannot overwrite them.
41
- */
42
- async function getNeonctlAccessToken() {
43
- const dir = selectedConfigDir();
44
- const at = locationForName(dir, selectedProfileName());
45
- const loaded = storeFor(dir).read(at);
46
- if (loaded === null) return null;
47
- const credential = interpretCredentials(loaded.credentials, at, loaded.backend);
48
- if (credential.kind === "api_key") return credential.apiKey;
49
- const token = loaded.credentials.access_token;
50
- return typeof token === "string" && token.trim() !== "" ? token : null;
51
- }
52
- //#endregion
53
- export { ensureNeonctlAuth, isAuthenticated };
@@ -1,4 +0,0 @@
1
- //#region src/init/build_config.ts
2
- const INTERNAL_VSX_GALLERY = "";
3
- //#endregion
4
- export { INTERNAL_VSX_GALLERY };
@@ -1,63 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- //#region src/init/detect_agent.ts
3
- /**
4
- * Detects the IDE/editor the terminal is running in, regardless of which
5
- * agent is active. Used for extension installation — the extension goes
6
- * into the IDE, not the agent.
7
- *
8
- * Returns an Editor-compatible string: "Cursor", "VS Code", "Windsurf", or null.
9
- */
10
- function detectIde() {
11
- const env = process.env;
12
- if (env.TERM_PROGRAM === "cursor" || env.CURSOR_TRACE_ID !== void 0 || env.CURSOR_EXTENSION_HOST_ROLE !== void 0 || env.CURSOR_LAYOUT !== void 0 || env.CURSOR_SPAWNED_BY_EXTENSION_ID !== void 0) return "Cursor";
13
- if (env.GIT_ASKPASS?.includes("Cursor.app") || env.VSCODE_GIT_ASKPASS_NODE?.includes("Cursor.app") || env.GIT_ASKPASS?.includes("cursor") || env.VSCODE_GIT_ASKPASS_NODE?.includes("cursor")) return "Cursor";
14
- if (env.TERM_PROGRAM === "windsurf") return "Windsurf";
15
- if (env.TERM_PROGRAM === "vscode" || env.VSCODE_PID !== void 0 || env.VSCODE_CWD !== void 0) {
16
- const envValues = Object.values(env).join("\n").toLowerCase();
17
- if (envValues.includes("cursor.app") || envValues.includes("/cursor/")) return "Cursor";
18
- if (isCursorInstalled() && !isVSCodeInstalled()) return "Cursor";
19
- return "VS Code";
20
- }
21
- return null;
22
- }
23
- function isCursorInstalled() {
24
- if (process.platform === "darwin") return existsSync("/Applications/Cursor.app");
25
- if (process.platform === "linux") return existsSync("/usr/share/cursor") || existsSync("/usr/bin/cursor");
26
- if (process.platform === "win32") {
27
- const localAppData = process.env.LOCALAPPDATA || "";
28
- return existsSync(`${localAppData}\\Programs\\Cursor\\Cursor.exe`) || existsSync(`${localAppData}\\cursor\\Cursor.exe`);
29
- }
30
- return false;
31
- }
32
- function isVSCodeInstalled() {
33
- if (process.platform === "darwin") return existsSync("/Applications/Visual Studio Code.app");
34
- if (process.platform === "linux") return existsSync("/usr/share/code") || existsSync("/usr/bin/code");
35
- if (process.platform === "win32") {
36
- const localAppData = process.env.LOCALAPPDATA || "";
37
- const programFiles = process.env.PROGRAMFILES || "C:\\Program Files";
38
- return existsSync(`${localAppData}\\Programs\\Microsoft VS Code\\Code.exe`) || existsSync(`${programFiles}\\Microsoft VS Code\\Code.exe`);
39
- }
40
- return false;
41
- }
42
- /**
43
- * Detects which agent/IDE is running the CLI from environment variables.
44
- * Returns the add-mcp compatible agent ID (e.g. "cursor", "claude-code").
45
- *
46
- * Delegates IDE detection to detectIde() to avoid duplicating Cursor/VS Code
47
- * heuristics, then checks for agent-specific env vars.
48
- */
49
- function detectAgent() {
50
- const env = process.env;
51
- if (env.CLAUDECODE === "1" || env.CLAUDE_CODE === "1" || env.CLAUDE_CLI === "1") return "claude-code";
52
- if (env.CODEX === "1") return "codex";
53
- if (env.CLINE === "1") return "cline";
54
- const ide = detectIde();
55
- if (ide) return {
56
- Cursor: "cursor",
57
- "VS Code": "vscode",
58
- Windsurf: "windsurf"
59
- }[ide] ?? null;
60
- return null;
61
- }
62
- //#endregion
63
- export { detectAgent, detectIde, isCursorInstalled, isVSCodeInstalled };
@@ -1,19 +0,0 @@
1
- import { detectInstalledAgents } from "../mcp/agents.js";
2
- import "./agents.js";
3
- import { resolve } from "node:path";
4
- //#region src/init/editors.ts
5
- function getVSCodeGlobalConfigDir(homeDir) {
6
- const platform = process.platform;
7
- if (platform === "darwin") return resolve(homeDir, "Library", "Application Support", "Code", "User");
8
- if (platform === "linux") return resolve(homeDir, ".config", "Code", "User");
9
- if (platform === "win32") {
10
- const appData = process.env.APPDATA;
11
- if (appData) return resolve(appData, "Code", "User");
12
- }
13
- return null;
14
- }
15
- async function detectAvailableEditors(_homeDir) {
16
- return detectInstalledAgents();
17
- }
18
- //#endregion
19
- export { detectAvailableEditors, getVSCodeGlobalConfigDir };
@@ -1,61 +0,0 @@
1
- import { neonInitAgentCmd } from "./profile_cli.js";
2
- //#region src/init/enrich_output.ts
3
- /**
4
- * Converts a phase's `args` (e.g. ["neon-auth", "--json", "--setup"]) into the
5
- * `neon init --agent --data` invocation that reaches the same handler.
6
- */
7
- function argsToCommand(args) {
8
- const data = {};
9
- let i = 0;
10
- if (args.length > 0 && !args[0].startsWith("-")) {
11
- data.step = args[0];
12
- i = 1;
13
- }
14
- while (i < args.length) {
15
- const arg = args[i];
16
- if (arg.startsWith("--")) {
17
- const key = arg.slice(2).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
18
- const next = args[i + 1];
19
- if (key === "json") {
20
- i += 1;
21
- continue;
22
- }
23
- if (key === "profile" || key === "configDir") {
24
- i += next !== void 0 && !next.startsWith("-") ? 2 : 1;
25
- continue;
26
- }
27
- if (next !== void 0 && !next.startsWith("-")) {
28
- data[key] = next;
29
- i += 2;
30
- } else {
31
- data[key] = true;
32
- i += 1;
33
- }
34
- } else i += 1;
35
- }
36
- return neonInitAgentCmd(data);
37
- }
38
- /**
39
- * Walks a phase response object and:
40
- * 1. Replaces `args` arrays with `command` strings (neon init --data format)
41
- * 2. Renames `run_neon_init` → `run_shell_command`
42
- * 3. Adds a description to finalize steps
43
- */
44
- function enrichResponse(obj) {
45
- if (obj === null || typeof obj !== "object") return obj;
46
- if (Array.isArray(obj)) return obj.map(enrichResponse);
47
- const record = obj;
48
- const result = {};
49
- for (const [key, value] of Object.entries(record)) result[key] = enrichResponse(value);
50
- if (Array.isArray(result.args)) {
51
- result.command = argsToCommand(result.args);
52
- delete result.args;
53
- }
54
- if (result.type === "run_neon_init") {
55
- result.type = "run_shell_command";
56
- if (typeof result.command === "string" && result.command.includes("\"step\":\"finalize\"")) result.description = "Run this command to complete the setup. This is the final step — do not run any other neon init commands after this.";
57
- }
58
- return result;
59
- }
60
- //#endregion
61
- export { enrichResponse };