neon 4.3.1 → 4.5.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.
@@ -0,0 +1,135 @@
1
+ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
+ import { log } from "../log.js";
3
+ import { writer } from "../writer.js";
4
+ import { noPassthrough } from "../utils/flags.js";
5
+ import { getAgentDisplayName } from "../mcp/agents.js";
6
+ import "../init/agents.js";
7
+ import { canPickAgentsInteractively } from "../utils/agent_picker.js";
8
+ import { resolvePluginsPlan } from "../plugins/plan.js";
9
+ import { NEON_PLUGIN_NAME, neonPluginsRetryCommand, pluginsAddArgs, runPluginsCli } from "../plugins/run.js";
10
+ import { confirmPluginsInstall } from "../plugins/wizard.js";
11
+ //#region src/commands/plugins.ts
12
+ var plugins_exports = /* @__PURE__ */ __exportAll({
13
+ builder: () => builder,
14
+ command: () => command,
15
+ describe: () => describe,
16
+ handler: () => handler
17
+ });
18
+ const scopeLabel = (scope) => scope === "project" ? "project" : "user";
19
+ const coerceAgents = (value) => {
20
+ if (value === void 0) return [];
21
+ const list = Array.isArray(value) ? value : [value];
22
+ if (list.length === 0) throw new Error("--agent needs a value. Pass one, or omit the flag entirely.");
23
+ return list.map((item) => {
24
+ if (typeof item !== "string" || item.trim() === "") throw new Error("--agent needs a value. Pass one, or omit the flag entirely.");
25
+ return item;
26
+ });
27
+ };
28
+ const command = "plugins";
29
+ const describe = "Install the Neon plugin into coding agents";
30
+ const builder = (argv) => argv.usage("$0 plugins [options]").options({
31
+ yes: {
32
+ alias: "y",
33
+ type: "boolean",
34
+ default: false,
35
+ describe: "Skip prompts"
36
+ },
37
+ global: {
38
+ type: "boolean",
39
+ default: false,
40
+ describe: "Install user-level. Default is project"
41
+ },
42
+ agent: {
43
+ alias: "a",
44
+ type: "array",
45
+ string: true,
46
+ describe: "Coding agent to install into (repeatable). Skips the agent picker",
47
+ coerce: coerceAgents
48
+ }
49
+ }).example("$0 plugins", "Interactive: agents, then confirm").example("$0 plugins -y", "Detected agents, skip prompts").example("$0 plugins --agent cursor --agent claude-code", "Install into specific agents").example("$0 plugins --global", "Install user-level").strict().check(noPassthrough("plugins"));
50
+ const handler = async (props) => {
51
+ const cwd = process.cwd();
52
+ const yes = props.yes === true;
53
+ const interactive = canPickAgentsInteractively() && !yes;
54
+ const plan = await resolvePluginsPlan({
55
+ global: props.global === true,
56
+ agents: props.agent ?? [],
57
+ yes,
58
+ cwd,
59
+ interactive
60
+ });
61
+ for (const agent of plan.skipped) log.warning("Skipping %s: no plugins mapping.", getAgentDisplayName(agent));
62
+ for (const agent of plan.userScopeSkipped) log.warning("Skipping %s: plugins are user-level. Pass --global.", getAgentDisplayName(agent));
63
+ if (interactive) {
64
+ if (!await confirmPluginsInstall({
65
+ scope: plan.scope,
66
+ agents: plan.agents
67
+ })) {
68
+ log.info("Aborted. Nothing was written.");
69
+ return;
70
+ }
71
+ }
72
+ const rows = [];
73
+ const failed = [];
74
+ const scope = scopeLabel(plan.scope);
75
+ for (const mapped of plan.targets) {
76
+ const args = pluginsAddArgs({
77
+ target: mapped.target,
78
+ global: plan.scope === "global"
79
+ });
80
+ const agent = mapped.agents.join(", ");
81
+ try {
82
+ await runPluginsCli({
83
+ args,
84
+ cwd
85
+ });
86
+ rows.push({
87
+ scope,
88
+ plugin: NEON_PLUGIN_NAME,
89
+ agent,
90
+ status: "installed"
91
+ });
92
+ } catch (error) {
93
+ const message = error instanceof Error ? error.message : String(error);
94
+ rows.push({
95
+ scope,
96
+ plugin: NEON_PLUGIN_NAME,
97
+ agent,
98
+ status: "failed",
99
+ error: "plugins CLI failed"
100
+ });
101
+ failed.push({
102
+ agents: mapped.agents,
103
+ message
104
+ });
105
+ }
106
+ }
107
+ const out = writer(props);
108
+ out.write(rows, {
109
+ fields: [
110
+ "scope",
111
+ "plugin",
112
+ "agent",
113
+ "status",
114
+ "error"
115
+ ],
116
+ title: "Plugins"
117
+ });
118
+ out.end();
119
+ if (failed.length === 0) {
120
+ log.info(plan.scope === "project" ? "Installed the Neon plugin (project)." : "Installed the Neon plugin (user).");
121
+ return;
122
+ }
123
+ const first = failed[0];
124
+ if (first === void 0) throw new Error("Failed to install the Neon plugin.");
125
+ const retry = neonPluginsRetryCommand({
126
+ agents: failed.flatMap((row) => row.agents),
127
+ global: plan.scope === "global"
128
+ });
129
+ if (first.message.includes("needs npx (Node.js)")) throw new Error(first.message);
130
+ const detail = failed.map((row) => row.message).join("\n");
131
+ if (failed.length === rows.length) throw new Error(`${detail}\nRetry with: ${retry}`);
132
+ throw new Error(`Failed to install the Neon plugin for: ${failed.flatMap((row) => row.agents).join(", ")}.\n${detail}\nRetry with: ${retry}`);
133
+ };
134
+ //#endregion
135
+ export { builder, command, describe, handler, plugins_exports as t };
@@ -1,13 +1,14 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
- import { _ as upsertProfile, a as canDropProfilesFile, b as writeSecretFile, c as listProfiles, d as newProfileLocation, f as profilesFilePath, g as selectProfileName, h as resolveProfile, i as assertValidProfileName, l as locationForName, m as readProfiles, n as KEYRING_CREDENTIALS, o as credentialsDisplay, p as profilesUsingPath, r as assertProfilesUsable, s as isKeyringPointer, u as locationOf, v as CRED_STORAGE_FILE, y as CRED_STORAGE_KEYRING } from "../_chunks/profiles-CVXNvU2r.js";
3
- import { c as describeScope, d as scopeOf, l as interpretCredentials, n as KEYRING_SERVICE, o as apiKeyCredentials, r as keyringAccount, s as credentialLabel, t as storeFor } from "../_chunks/credential_io-D2vw1UOw.js";
2
+ import { t as writeSecretFile } from "../_chunks/secure_file-BucZj4yQ.js";
3
+ import { c as describeScope, d as scopeOf, l as interpretCredentials, n as KEYRING_SERVICE, o as apiKeyCredentials, r as keyringAccount, s as credentialLabel, t as storeFor } from "../_chunks/credential_io-B1JxWELN.js";
4
4
  import { isCi } from "../env.js";
5
5
  import { log } from "../log.js";
6
6
  import { getApiClient, isNeonApiError } from "../api.js";
7
7
  import { o as isOwnedCredentialPath, r as credentialsPath } from "../_chunks/paths-g3DRqJiD.js";
8
8
  import { setAuthContext } from "../auth_context.js";
9
+ import { _ as upsertProfile, a as canDropProfilesFile, c as listProfiles, d as newProfileLocation, f as profilesFilePath, g as selectProfileName, h as resolveProfile, i as assertValidProfileName, l as locationForName, m as readProfiles, n as KEYRING_CREDENTIALS, o as credentialsDisplay, p as profilesUsingPath, r as assertProfilesUsable, s as isKeyringPointer, u as locationOf, v as CRED_STORAGE_FILE, y as CRED_STORAGE_KEYRING } from "../_chunks/profiles-D95AeZ5v.js";
9
10
  import { auth, revokeToken } from "../auth.js";
10
- import { t as credentialInputs } from "../_chunks/auth_selection-Bx2NAgkk.js";
11
+ import { t as credentialInputs } from "../_chunks/auth_selection-ktL6uoEe.js";
11
12
  import { readOutgoingCredential, retirePreviousCredential, revokeTokenSet, withdrawKey } from "../retire_credential.js";
12
13
  import { authFlow, usableCredential } from "./auth.js";
13
14
  import { writer } from "../writer.js";
@@ -0,0 +1,18 @@
1
+ //#region src/config_services.ts
2
+ const isToggleEnabled = (toggle) => {
3
+ if (toggle === void 0) return false;
4
+ if (typeof toggle === "boolean") return toggle;
5
+ return toggle.enabled !== false;
6
+ };
7
+ /** Postgres is omitted because every project includes it. */
8
+ const declaredNeonServices = (config) => {
9
+ const services = [];
10
+ if (isToggleEnabled(config.auth)) services.push("auth");
11
+ if (isToggleEnabled(config.dataApi)) services.push("data-api");
12
+ if (Object.keys(config.preview?.buckets ?? {}).length > 0) services.push("object-storage");
13
+ if (Object.keys(config.preview?.functions ?? {}).length > 0) services.push("functions");
14
+ if (isToggleEnabled(config.preview?.aiGateway)) services.push("ai-gateway");
15
+ return services;
16
+ };
17
+ //#endregion
18
+ export { declaredNeonServices };
package/dist/context.js CHANGED
@@ -45,6 +45,7 @@ const isConfigInit = (args) => args._[0] === "config" && args._[1] === "init" &&
45
45
  * access has already lapsed is the main reason to remove one.
46
46
  */
47
47
  const isProfileCommand = (args) => args._[0] === "profile" || args._[0] === "profiles";
48
+ const isClaimCommand = (args) => args._[0] === "claim" || args._[0] === "claimable";
48
49
  /**
49
50
  * `neon api-keys …`, under either spelling. Exempts the group from context enrichment: how
50
51
  * far a credential reaches must come from an explicit flag, never from `.neon`.
@@ -52,6 +53,7 @@ const isProfileCommand = (args) => args._[0] === "profile" || args._[0] === "pro
52
53
  const isApiKeysCommand = (args) => args._[0] === "api-keys" || args._[0] === "api-key";
53
54
  const isMcpCommand = (args) => args._[0] === "mcp";
54
55
  const isSkillsCommand = (args) => args._[0] === "skills";
56
+ const isPluginsCommand = (args) => args._[0] === "plugins";
55
57
  /** Raw argv is required because auth middleware runs before MCP flags are parsed. */
56
58
  const isMcpOauth = (args) => isMcpCommand(args) && argvEnablesMcpOauth(process.argv);
57
59
  const OAUTH_FALSE = /* @__PURE__ */ new Set([
@@ -131,7 +133,8 @@ const enrichFromContext = (args) => {
131
133
  if (isApiKeysCommand(args)) return;
132
134
  if (isProfileCommand(args)) return;
133
135
  if (isMcpCommand(args)) return;
134
- if (isSkillsCommand(args)) return;
136
+ if (isSkillsCommand(args) || isPluginsCommand(args)) return;
137
+ if (isClaimCommand(args)) return;
135
138
  const context = readContextFile(args.contextFile);
136
139
  if (!args.orgId) args.orgId = context.orgId;
137
140
  if (!args.projectId) args.projectId = context.projectId;
@@ -237,4 +240,4 @@ const globToRegExp = (pattern) => {
237
240
  return new RegExp(`^${source}$`);
238
241
  };
239
242
  //#endregion
240
- export { applyContext, contextBranch, currentContextFile, enrichFromContext, ensureGitignored, isApiKeysCommand, isConfigInit, isCurrentBranchProbe, isMcpCommand, isMcpOauth, isProfileCommand, isSkillsCommand, readContextFile, setContext, updateContextFile, walkContextFile };
243
+ export { applyContext, contextBranch, currentContextFile, enrichFromContext, ensureGitignored, isApiKeysCommand, isClaimCommand, isConfigInit, isCurrentBranchProbe, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, readContextFile, setContext, updateContextFile, walkContextFile };
@@ -1,2 +1,2 @@
1
- import { t as storeFor } from "./_chunks/credential_io-D2vw1UOw.js";
1
+ import { t as storeFor } from "./_chunks/credential_io-B1JxWELN.js";
2
2
  export { storeFor };
package/dist/env.js CHANGED
@@ -5,6 +5,11 @@ const isCi = () => {
5
5
  const isDebug = () => {
6
6
  return Boolean(process.env.DEBUG);
7
7
  };
8
+ const getCliAgent = (env) => {
9
+ const claudeCode = env.CLAUDE_CODE_CHILD_SESSION === "1";
10
+ if (claudeCode === (env.CODEX_CI === "1" && (Boolean(env.CODEX_THREAD_ID?.trim()) || Boolean(env.CODEX_SESSION_ID?.trim())))) return void 0;
11
+ return claudeCode ? "claude-code" : "codex";
12
+ };
8
13
  const getGithubEnvVars = (env) => {
9
14
  const vars = [
10
15
  "GITHUB_ACTION_PATH",
@@ -27,4 +32,4 @@ const getGithubEnvVars = (env) => {
27
32
  return Object.fromEntries(map);
28
33
  };
29
34
  //#endregion
30
- export { getGithubEnvVars, isCi, isDebug };
35
+ export { getCliAgent, getGithubEnvVars, isCi, isDebug };
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ const NO_SUBCOMMANDS_VERBS = [
30
30
  "open",
31
31
  "init",
32
32
  "mcp",
33
+ "plugins",
33
34
  "skills",
34
35
  "dev",
35
36
  "deploy",
package/dist/init/auth.js CHANGED
@@ -1,5 +1,5 @@
1
- import { l as locationForName } from "../_chunks/profiles-CVXNvU2r.js";
2
- import { l as interpretCredentials, t as storeFor } from "../_chunks/credential_io-D2vw1UOw.js";
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
3
  import { npxNeonArgs, selectedConfigDir, selectedProfileName } from "./profile_cli.js";
4
4
  import { log } from "@clack/prompts";
5
5
  import { execa } from "execa";
@@ -1,6 +1,6 @@
1
- import { t as DEFAULT_PROFILE } from "../_chunks/profiles-CVXNvU2r.js";
2
1
  import { n as configDir } from "../_chunks/paths-g3DRqJiD.js";
3
- import { i as selectCredential, t as credentialInputs } from "../_chunks/auth_selection-Bx2NAgkk.js";
2
+ import { t as DEFAULT_PROFILE } from "../_chunks/profiles-D95AeZ5v.js";
3
+ import { i as selectCredential, t as credentialInputs } from "../_chunks/auth_selection-ktL6uoEe.js";
4
4
  //#region src/init/profile_cli.ts
5
5
  /**
6
6
  * API-key selections fall back to DEFAULT because init authenticates from stored credentials.
@@ -0,0 +1,69 @@
1
+ import { getAgentDisplayName, tryResolveAddMcpAgentId } from "../mcp/agents.js";
2
+ import "../init/agents.js";
3
+ import { agentChoicesFrom, pickAgentsInteractively, resolveAgentSelection } from "../utils/agent_picker.js";
4
+ import { detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, pluginsInstallableAgents } from "./targets.js";
5
+ //#region src/plugins/plan.ts
6
+ const assertPluginsCanRun = (options) => {
7
+ if (options.yes || options.interactive || options.hasAgents) return;
8
+ throw new Error("No interactive terminal. Pass -y to install into detected agents, or --agent <name>.");
9
+ };
10
+ async function resolvePluginsPlan(options) {
11
+ assertPluginsCanRun({
12
+ yes: options.yes,
13
+ interactive: options.interactive,
14
+ hasAgents: options.agents.length > 0
15
+ });
16
+ const prompt = options.interactive && !options.yes;
17
+ const scope = options.global ? "global" : "project";
18
+ const available = pluginsInstallableAgents(scope);
19
+ const detected = await detectPluginsAgents({
20
+ scope,
21
+ cwd: options.cwd
22
+ });
23
+ const selected = await resolveAgentSelection({
24
+ specified: options.agents,
25
+ choices: agentChoicesFrom(available, detected),
26
+ detected,
27
+ message: "Which coding agents should get the Neon plugin? (space to toggle, enter to confirm)",
28
+ nonInteractiveMessage: scope === "project" ? `No coding agents detected in this project. Pass --agent <name>. Supported agents: ${available.join(", ")}` : `No coding agents detected. Pass --agent <name>. Supported agents: ${available.join(", ")}`,
29
+ resolveSpecified: (raw) => {
30
+ if (raw === "*") throw new Error("neon plugins does not accept --agent *. Pass --agent <name> for each coding agent, or omit --agent to use detected agents.");
31
+ const id = tryResolveAddMcpAgentId(raw);
32
+ if (!id) throw new Error(`Unknown agent: "${raw}". Supported agents: ${available.join(", ")}`);
33
+ return id;
34
+ },
35
+ pick: prompt ? options.pickAgents ?? pickAgentsInteractively : void 0,
36
+ interactive: prompt
37
+ });
38
+ const agents = [];
39
+ const skipped = [];
40
+ const userScopeSkipped = [];
41
+ for (const id of selected) {
42
+ const target = getPluginsTargetName(id);
43
+ if (target === void 0) {
44
+ skipped.push(id);
45
+ continue;
46
+ }
47
+ if (scope === "project" && isUserScopeOnlyPluginsTarget(target)) {
48
+ userScopeSkipped.push(id);
49
+ continue;
50
+ }
51
+ agents.push(id);
52
+ }
53
+ if (agents.length === 0) {
54
+ if (userScopeSkipped.length > 0 && skipped.length === 0) {
55
+ const names = userScopeSkipped.map((id) => getAgentDisplayName(id)).join(", ");
56
+ throw new Error(`${names}: plugins are user-level. Pass --global. Without --global: ${pluginsInstallableAgents("project").join(", ")}`);
57
+ }
58
+ throw new Error(`None of the selected agents can install plugins. Supported agents: ${available.join(", ")}`);
59
+ }
60
+ return {
61
+ scope,
62
+ agents,
63
+ skipped,
64
+ userScopeSkipped,
65
+ targets: mappedPluginsTargets(agents, scope)
66
+ };
67
+ }
68
+ //#endregion
69
+ export { assertPluginsCanRun, resolvePluginsPlan };
@@ -0,0 +1,123 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { execa } from "execa";
3
+ //#region src/plugins/run.ts
4
+ const TELEMETRY_BLOCKERS = ["DISABLE_TELEMETRY", "DO_NOT_TRACK"];
5
+ const PLUGIN_SOURCE = "neondatabase/agent-skills";
6
+ const NEON_PLUGIN_NAME = "neon-postgres";
7
+ const PLUGINS_CLI_TIMEOUT_MS = 12e4;
8
+ const pluginsChildEnv = (base = process.env) => {
9
+ const env = { ...base };
10
+ for (const key of TELEMETRY_BLOCKERS) delete env[key];
11
+ for (const key of Object.keys(env)) if (key.toUpperCase() === "NEON_API_KEY") delete env[key];
12
+ return env;
13
+ };
14
+ const pluginsScopeFlag = (global) => global ? "user" : "project";
15
+ const pluginsAddArgs = (options) => {
16
+ if (options.target.length === 0) throw new Error("plugins add needs a -t target.");
17
+ return [
18
+ "-y",
19
+ "plugins",
20
+ "add",
21
+ PLUGIN_SOURCE,
22
+ "-t",
23
+ options.target,
24
+ "-s",
25
+ pluginsScopeFlag(options.global),
26
+ "-y"
27
+ ];
28
+ };
29
+ const neonPluginsRetryCommand = (options) => {
30
+ const args = ["plugins"];
31
+ for (const agent of options.agents) args.push("--agent", agent);
32
+ if (options.global) args.push("--global");
33
+ args.push("-y");
34
+ return `neon ${args.map(quoteNpxArg).join(" ")}`;
35
+ };
36
+ const quoteNpxArg = (part) => /[\s"'\\*]/.test(part) ? `'${part.replace(/'/g, `'\\''`)}'` : part;
37
+ const runPluginsCli = async (options) => {
38
+ const timeoutMs = options.timeoutMs ?? 12e4;
39
+ const subprocess = execa("npx", options.args, {
40
+ cwd: options.cwd,
41
+ env: pluginsChildEnv(options.env ?? process.env),
42
+ extendEnv: false,
43
+ stdio: "pipe",
44
+ detached: true
45
+ });
46
+ let timedOut = false;
47
+ let stopped = false;
48
+ const stop = () => {
49
+ if (stopped) return;
50
+ stopped = true;
51
+ if (subprocess.pid !== void 0) killProcessTree(subprocess.pid);
52
+ };
53
+ const onSigint = () => {
54
+ stop();
55
+ process.exit(130);
56
+ };
57
+ const onSigterm = () => {
58
+ stop();
59
+ process.exit(143);
60
+ };
61
+ process.once("SIGINT", onSigint);
62
+ process.once("SIGTERM", onSigterm);
63
+ process.once("exit", stop);
64
+ const timer = setTimeout(() => {
65
+ timedOut = true;
66
+ stop();
67
+ }, timeoutMs);
68
+ try {
69
+ const result = await subprocess;
70
+ return {
71
+ stdout: result.stdout,
72
+ stderr: result.stderr
73
+ };
74
+ } catch (error) {
75
+ if (isCommandMissing(error)) throw new Error("neon plugins needs npx (Node.js) to run the plugins CLI. Install Node.js, then retry.");
76
+ if (isExecaFailure(error)) throw new Error(pluginsCliFailureMessage({
77
+ stderr: typeof error.stderr === "string" ? error.stderr : "",
78
+ stdout: typeof error.stdout === "string" ? error.stdout : "",
79
+ timedOut,
80
+ timeoutMs
81
+ }));
82
+ throw error;
83
+ } finally {
84
+ clearTimeout(timer);
85
+ process.removeListener("SIGINT", onSigint);
86
+ process.removeListener("SIGTERM", onSigterm);
87
+ process.removeListener("exit", stop);
88
+ }
89
+ };
90
+ const pluginsCliFailureMessage = (error) => {
91
+ const childOut = [error.stderr, error.stdout].filter((part) => typeof part === "string" && part.length > 0).join("\n");
92
+ if (error.timedOut === true) {
93
+ const seconds = Math.max(1, Math.ceil((error.timeoutMs ?? 12e4) / 1e3));
94
+ const headline = `plugins CLI timed out after ${seconds} ${seconds === 1 ? "second" : "seconds"}`;
95
+ return childOut.length > 0 ? `${headline}:\n${childOut}` : `${headline}.`;
96
+ }
97
+ if (childOut.length > 0) return `plugins CLI failed:\n${childOut}`;
98
+ return "plugins CLI failed.";
99
+ };
100
+ const killProcessTree = (pid) => {
101
+ if (process.platform === "win32") {
102
+ spawnSync("taskkill", [
103
+ "/pid",
104
+ String(pid),
105
+ "/T",
106
+ "/F"
107
+ ], { stdio: "ignore" });
108
+ return;
109
+ }
110
+ try {
111
+ process.kill(-pid, "SIGKILL");
112
+ } catch {
113
+ try {
114
+ process.kill(pid, "SIGKILL");
115
+ } catch {
116
+ return;
117
+ }
118
+ }
119
+ };
120
+ const isCommandMissing = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
121
+ const isExecaFailure = (error) => typeof error === "object" && error !== null && "shortMessage" in error && typeof error.shortMessage === "string";
122
+ //#endregion
123
+ export { NEON_PLUGIN_NAME, PLUGINS_CLI_TIMEOUT_MS, PLUGIN_SOURCE, neonPluginsRetryCommand, pluginsAddArgs, pluginsChildEnv, pluginsCliFailureMessage, pluginsScopeFlag, quoteNpxArg, runPluginsCli };
@@ -0,0 +1,64 @@
1
+ import { detectInstalledAgents, listMcpAgentIds, tryResolveAddMcpAgentId, uniqueAgentIds } from "../mcp/agents.js";
2
+ import "../init/agents.js";
3
+ import { detectProjectAgents } from "add-mcp";
4
+ //#region src/plugins/targets.ts
5
+ const PLUGINS_TARGET_BY_TYPE = {
6
+ cursor: "cursor",
7
+ vscode: "vscode",
8
+ "claude-code": "claude-code",
9
+ "claude-desktop": "claude-code",
10
+ codex: "codex",
11
+ "github-copilot-cli": "github-copilot",
12
+ "grok-build": "grok"
13
+ };
14
+ const USER_SCOPE_ONLY_TARGETS = /* @__PURE__ */ new Set([
15
+ "vscode",
16
+ "github-copilot",
17
+ "grok"
18
+ ]);
19
+ function getPluginsTargetName(agent) {
20
+ if (Object.prototype.hasOwnProperty.call(PLUGINS_TARGET_BY_TYPE, agent)) return PLUGINS_TARGET_BY_TYPE[agent];
21
+ const id = tryResolveAddMcpAgentId(agent);
22
+ if (!id) return void 0;
23
+ return PLUGINS_TARGET_BY_TYPE[id];
24
+ }
25
+ function supportsPlugins(agent) {
26
+ return getPluginsTargetName(agent) !== void 0;
27
+ }
28
+ function isUserScopeOnlyPluginsTarget(target) {
29
+ return USER_SCOPE_ONLY_TARGETS.has(target);
30
+ }
31
+ const pluginsMappedAgents = () => listMcpAgentIds().filter((id) => supportsPlugins(id));
32
+ const pluginsInstallableAgents = (scope) => pluginsMappedAgents().filter((id) => {
33
+ if (scope === "global") return true;
34
+ const target = getPluginsTargetName(id);
35
+ return target !== void 0 && !isUserScopeOnlyPluginsTarget(target);
36
+ });
37
+ const detectPluginsAgents = async (options) => {
38
+ const detected = options.scope === "project" ? detectProjectAgents(options.cwd) : await detectInstalledAgents();
39
+ return uniqueAgentIds(detected).filter((id) => supportsPlugins(id));
40
+ };
41
+ const mappedPluginsTargets = (agents, scope) => {
42
+ const mapped = [];
43
+ const byTarget = /* @__PURE__ */ new Map();
44
+ for (const agent of agents) {
45
+ const target = getPluginsTargetName(agent);
46
+ if (target === void 0) continue;
47
+ if (scope === "project" && isUserScopeOnlyPluginsTarget(target)) continue;
48
+ const existing = byTarget.get(target);
49
+ if (existing !== void 0) {
50
+ if (!existing.agents.includes(agent)) existing.agents.push(agent);
51
+ continue;
52
+ }
53
+ const row = {
54
+ agents: [agent],
55
+ target
56
+ };
57
+ byTarget.set(target, row);
58
+ mapped.push(row);
59
+ }
60
+ if (mapped.length === 0) throw new Error(`None of the selected agents can install plugins. Supported agents: ${pluginsInstallableAgents(scope).join(", ")}`);
61
+ return mapped;
62
+ };
63
+ //#endregion
64
+ export { detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, pluginsInstallableAgents, pluginsMappedAgents, supportsPlugins };
@@ -0,0 +1,36 @@
1
+ import { getAgentDisplayName } from "../mcp/agents.js";
2
+ import "../init/agents.js";
3
+ import { canPickAgentsInteractively } from "../utils/agent_picker.js";
4
+ import { NEON_PLUGIN_NAME } from "./run.js";
5
+ import prompts from "prompts";
6
+ //#region src/plugins/wizard.ts
7
+ const restoreCursorOnAbort = (state) => {
8
+ if (state.aborted) {
9
+ process.stdout.write("\x1B[?25h");
10
+ process.stdout.write("\n");
11
+ process.exit(1);
12
+ }
13
+ };
14
+ const pluginsInstallSummary = (options) => {
15
+ const rows = [
16
+ ["Scope", options.scope === "project" ? "project" : "user"],
17
+ ["Agents", options.agents.map(getAgentDisplayName).join(", ")],
18
+ ["Plugin", NEON_PLUGIN_NAME]
19
+ ];
20
+ const labelWidth = Math.max(...rows.map(([label]) => label.length));
21
+ return rows.map(([label, value]) => `${label.padEnd(labelWidth)} ${value}`).join("\n");
22
+ };
23
+ const confirmPluginsInstall = async (options) => {
24
+ if (!canPickAgentsInteractively()) return true;
25
+ process.stdout.write(`\n${pluginsInstallSummary(options)}\n\n`);
26
+ const { ok } = await prompts({
27
+ onState: restoreCursorOnAbort,
28
+ type: "confirm",
29
+ name: "ok",
30
+ message: "Install the Neon plugin into these agents?",
31
+ initial: true
32
+ });
33
+ return ok === true;
34
+ };
35
+ //#endregion
36
+ export { confirmPluginsInstall, pluginsInstallSummary };
@@ -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-D2vw1UOw.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-B1JxWELN.js";
2
2
  import { log } from "./log.js";
3
3
  import { getApiClient } from "./api.js";
4
4
  import { revokeToken } from "./auth.js";
@@ -1,4 +1,4 @@
1
- import { r as recordCredentialInputs } from "../_chunks/auth_selection-Bx2NAgkk.js";
1
+ import { r as recordCredentialInputs } from "../_chunks/auth_selection-ktL6uoEe.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon",
3
- "version": "4.3.1",
3
+ "version": "4.5.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.2",
66
+ "@neon/config-runtime": "1.0.3",
67
67
  "@neon/sdk": "2.2.0",
68
- "@neon/config-runtime": "1.0.2"
68
+ "@neon/config": "1.0.3"
69
69
  },
70
70
  "optionalDependencies": {
71
71
  "@napi-rs/keyring": "1.3.0",
@@ -97,8 +97,8 @@
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.2",
101
- "@neon/e2e-harness": "0.0.0"
100
+ "@neon/e2e-harness": "0.0.0",
101
+ "@neon-internals/env-core": "0.0.3"
102
102
  },
103
103
  "publishConfig": {
104
104
  "access": "public",