neon 8.15.0 → 8.15.1
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.
- package/README.md +3 -1
- package/dist/_chunks/{docs-CClmgj_Z.js → docs-BpLiSTmm.js} +1 -1
- package/dist/_chunks/{plugins-CKOcBuuP.js → plugins-B2Gym8Lw.js} +2 -2
- package/dist/analytics.js +4 -2
- package/dist/commands/docs.js +1 -1
- package/dist/commands/functions_new.js +2 -3
- package/dist/commands/index.js +2 -2
- package/dist/commands/plugins.js +1 -1
- package/dist/env.js +19 -4
- package/dist/init/copy.js +3 -2
- package/dist/init/run.js +22 -13
- package/dist/init/tooling.js +1 -1
- package/dist/init/wizard.js +4 -4
- package/dist/plugins/child_env.js +10 -0
- package/dist/plugins/plan.js +14 -14
- package/dist/plugins/run.js +2 -8
- package/dist/plugins/targets.js +129 -11
- package/dist/update_notifier.js +7 -4
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1056,6 +1056,8 @@ On a TTY the command asks which agents, then installs. Detected agents start sel
|
|
|
1056
1056
|
|
|
1057
1057
|
Default scope is `project`. `--global` is `user`. On macOS and Linux, Cursor and Claude Code store the plugin cache under `~/.claude/plugins`; on Windows, Cursor installs into Cursor extensions. `project` vs `user` is the scope field the plugins CLI records, not a directory in the repo. VS Code, GitHub Copilot CLI, and Grok Build only install user-level: they are skipped at the default scope with a warning, and a VS Code-only project fails if nothing else is selected. Pass `--global` for those.
|
|
1058
1058
|
|
|
1059
|
+
GitHub Copilot CLI and Grok Build install through their own CLIs (`copilot`, `grok`), so `neon plugins` runs `copilot --version` or `grok --version` first. A command that is missing, fails, or (for Copilot) is older than 0.0.400 is skipped with a warning under `-y`, fails an explicit `--agent`, and in `neon init` gets Neon skills and MCP instead of the plugin. The other agents install by writing files, so their detected config directory is enough.
|
|
1060
|
+
|
|
1059
1061
|
Supported agents with a plugins mapping: `claude-code`, `claude-desktop`, `codex`, `cursor`, `github-copilot-cli`, `grok-build`, `vscode`. `claude-desktop` installs as Claude Code; detecting both produces one install and lists both names in the table. `mcporter` is a known MCP name that is then skipped.
|
|
1060
1062
|
|
|
1061
1063
|
The plugins CLI installs every plugin it finds in the Neon plugin package. Today that is `neon-postgres` from `neondatabase/agent-skills`. It includes the Neon MCP server (`https://mcp.neon.tech/mcp`) and these skills: `neon`, `neon-ai-gateway`, `neon-auth`, `neon-functions`, `neon-object-storage`, `neon-postgres`, `neon-postgres-branches`, `neon-postgres-egress-optimizer`, `neon-realtime`. It does not include `neon-postgres-agent-platforms`.
|
|
@@ -1122,7 +1124,7 @@ Without a template, a terminal asks which one to use; anything else exits with t
|
|
|
1122
1124
|
|
|
1123
1125
|
A template's `layout` decides what you get. `router` writes one module per operation plus a generated `index.ts` that dispatches on the URL path, and declares one function. `separate` declares each operation as its own function under the slug the template gives it. Without `--operation` or `--all-operations`, a TTY asks which operations to include and anything else uses the template's recommended ones.
|
|
1124
1126
|
|
|
1125
|
-
Variables a template needs are declared on each function in `neon.ts` (`env: { API_KEY: process.env.API_KEY! }`) and added, without values, to `.env.example` next to it. Values are never written to `.env` or `.env.local` for you: a blank placeholder there would deploy as an empty string and delete the live value. Put the real value in the file `env pull` uses and deploy with `neon config apply --env .env.local`. `neon dev`
|
|
1127
|
+
Variables a template needs are declared on each function in `neon.ts` (`env: { API_KEY: process.env.API_KEY! }`) and added, without values, to `.env.example` next to it. Values are never written to `.env` or `.env.local` for you: a blank placeholder there would deploy as an empty string and delete the live value. Put the real value in the file `env pull` uses and deploy with `neon config apply --env .env.local`. `neon dev` loads it automatically.
|
|
1126
1128
|
|
|
1127
1129
|
Nothing is written until every check passes: a file that already exists with different contents needs `--force`, and a slug `neon.ts` already declares needs `--slug`, `--force`, or `--no-add-to-config`. A `separate` template sets its own slugs, so `--slug` only changes its directory; remove the existing declarations from `neon.ts`, or use `--force` or `--no-add-to-config`.
|
|
1128
1130
|
|
|
@@ -128,7 +128,7 @@ function siteSearchHeaders(opts) {
|
|
|
128
128
|
"Content-Type": "application/json",
|
|
129
129
|
"User-Agent": `neon/${opts.version}`,
|
|
130
130
|
"x-caller-source": "cli",
|
|
131
|
-
...opts.agent ? { "x-caller-client": encodeURIComponent(opts.agent) } : {},
|
|
131
|
+
...opts.analytics && opts.agent ? { "x-caller-client": encodeURIComponent(opts.agent) } : {},
|
|
132
132
|
...opts.analytics ? {} : { "x-caller-analytics": "off" }
|
|
133
133
|
};
|
|
134
134
|
}
|
|
@@ -5,7 +5,7 @@ import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
|
5
5
|
import { getAgentDisplayName } from "../mcp/agents.js";
|
|
6
6
|
import { NEON_MCP_URL } from "../mcp/install.js";
|
|
7
7
|
import "../init/agents.js";
|
|
8
|
-
import { pluginsInstallableAgents } from "../plugins/targets.js";
|
|
8
|
+
import { pluginsCommandProblemText, pluginsInstallableAgents } from "../plugins/targets.js";
|
|
9
9
|
import { writer } from "../writer.js";
|
|
10
10
|
import { noPassthrough } from "../utils/flags.js";
|
|
11
11
|
import { AGENT_PLUGIN_SKIP_MESSAGE } from "../init/copy.js";
|
|
@@ -77,7 +77,7 @@ const installPlugins = async (options) => {
|
|
|
77
77
|
});
|
|
78
78
|
for (const agent of plan.skipped) log.warning("Skipping %s: no plugins mapping.", getAgentDisplayName(agent));
|
|
79
79
|
for (const agent of plan.userScopeSkipped) log.warning("Skipping %s: plugins are user-level. Pass --global.", getAgentDisplayName(agent));
|
|
80
|
-
for (const
|
|
80
|
+
for (const row of plan.unusableCommands) log.warning("Skipping %s: the plugin installs through \"%s\", which %s.", getAgentDisplayName(row.agent), row.command.name, pluginsCommandProblemText(row));
|
|
81
81
|
const rows = [];
|
|
82
82
|
const failed = [];
|
|
83
83
|
const scope = scopeLabel(plan.scope);
|
package/dist/analytics.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as OAUTH, t as storeFor } from "./_chunks/credential_io-DbeSh3Pl.js";
|
|
2
|
-
import { getCliAgent, getGithubEnvVars, isCi } from "./env.js";
|
|
2
|
+
import { getCliAgent, getGithubEnvVars, getTty, isCi } from "./env.js";
|
|
3
3
|
import { log } from "./log.js";
|
|
4
4
|
import { getOrCreateAnonymousId } from "./anonymous_id.js";
|
|
5
5
|
import pkg_default from "./pkg.js";
|
|
@@ -248,7 +248,8 @@ const settleErrorReports = async (deadline) => {
|
|
|
248
248
|
const getErrorAnalyticsEventContext = (_args) => ({
|
|
249
249
|
version: pkg_default.version,
|
|
250
250
|
ci: isCi(),
|
|
251
|
-
agent: getCliAgent(process.env)
|
|
251
|
+
agent: getCliAgent(process.env),
|
|
252
|
+
tty: getTty()
|
|
252
253
|
});
|
|
253
254
|
const CREDENTIAL_TOKEN_ID = /\bnak_live_[0-9a-f]+\b/gi;
|
|
254
255
|
const redactCredentialTokenIds = (text) => text.replace(CREDENTIAL_TOKEN_ID, "nak_live_<redacted>");
|
|
@@ -353,6 +354,7 @@ const getAnalyticsEventProperties = (args) => ({
|
|
|
353
354
|
flags: { output: args.output },
|
|
354
355
|
ci: isCi(),
|
|
355
356
|
agent: getCliAgent(process.env),
|
|
357
|
+
tty: getTty(),
|
|
356
358
|
githubEnvVars: getGithubEnvVars(process.env)
|
|
357
359
|
});
|
|
358
360
|
export { analyticsIdentity, analyticsMiddleware, closeAnalytics, commandSuccessProperties, getAnalyticsEventProperties, getErrorAnalyticsEventProperties, initAnalyticsClientMiddleware, isAnalyticsActive, recordCommandSuccessExtras, recordScaffoldedTemplate, redactCredentialTokenIds, sendError, storedCredentialAttribution, takeCommandSuccessExtras, telemetryCredential, trackCommandSuccess, trackEvent, useAnalyticsClientForTests };
|
package/dist/commands/docs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as DEFAULT_SITE_SEARCH_URL, n as MAX_LIMIT, r as MAX_QUERY_LENGTH, i as SITE_SEARCH_CATEGORIES, a as builder, o as command, s as describe, l as formatSearchResults, u as parseSearchHits, d as resolveSiteSearchUrl, f as siteSearchHeaders } from "../_chunks/docs-
|
|
1
|
+
export { t as DEFAULT_SITE_SEARCH_URL, n as MAX_LIMIT, r as MAX_QUERY_LENGTH, i as SITE_SEARCH_CATEGORIES, a as builder, o as command, s as describe, l as formatSearchResults, u as parseSearchHits, d as resolveSiteSearchUrl, f as siteSearchHeaders } from "../_chunks/docs-BpLiSTmm.js";
|
|
@@ -169,9 +169,8 @@ const scaffoldFromTemplate = async (options) => {
|
|
|
169
169
|
const envFile = displayPath(resolveEnvFilePath(location), cwd);
|
|
170
170
|
const names = environment.join(", ");
|
|
171
171
|
if (variables && register) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
next.push(`Set ${names} in ${envFile}, and export ${them} for ${cli} dev`);
|
|
172
|
+
nextSteps.push(`Set ${variables} in ${envFile} (\`${cli} dev\` loads it automatically).`);
|
|
173
|
+
next.push(`Set ${names} in ${envFile} (${cli} dev loads it automatically)`);
|
|
175
174
|
} else if (variables) {
|
|
176
175
|
nextSteps.push(`Set ${variables} in your shell.`);
|
|
177
176
|
next.push(`Set ${names} in your shell`);
|
package/dist/commands/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { c as config_exports } from "../_chunks/config-BjCm5cfr.js";
|
|
|
7
7
|
import { u as projects_exports } from "../_chunks/projects-a3X4OFEJ.js";
|
|
8
8
|
import { a as link_exports } from "../_chunks/link-nAiuWKfg.js";
|
|
9
9
|
import { o as mcp_exports } from "../_chunks/mcp-BznTRK3w.js";
|
|
10
|
-
import { c as plugins_exports } from "../_chunks/plugins-
|
|
10
|
+
import { c as plugins_exports } from "../_chunks/plugins-B2Gym8Lw.js";
|
|
11
11
|
import { l as skills_exports } from "../_chunks/skills-DKgafm65.js";
|
|
12
12
|
import { t as bootstrap_exports } from "../_chunks/bootstrap-BDkvsAVJ.js";
|
|
13
13
|
import { r as branches_exports } from "../_chunks/branches-Bp-5z1lM.js";
|
|
@@ -21,7 +21,7 @@ import { o as databases_exports } from "../_chunks/databases-DC2h6IW2.js";
|
|
|
21
21
|
import { r as deploy_exports } from "../_chunks/deploy-DmEEhux0.js";
|
|
22
22
|
import { s as dev_exports } from "../_chunks/dev-DlUJFzRs.js";
|
|
23
23
|
import { i as diff_exports } from "../_chunks/diff-dnxneH0u.js";
|
|
24
|
-
import { c as docs_exports } from "../_chunks/docs-
|
|
24
|
+
import { c as docs_exports } from "../_chunks/docs-BpLiSTmm.js";
|
|
25
25
|
import { o as feedback_exports } from "../_chunks/feedback-mQmdTRFn.js";
|
|
26
26
|
import { o as functions_exports } from "../_chunks/functions-3ypiRwZK.js";
|
|
27
27
|
import { o as git_exports } from "../_chunks/git-j6xSVo3e.js";
|
package/dist/commands/plugins.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as aliases, n as builder, r as command, i as describe, a as handler, o as installPlugins, s as pluginsInstallError, l as reportPluginsInstall } from "../_chunks/plugins-
|
|
1
|
+
export { t as aliases, n as builder, r as command, i as describe, a as handler, o as installPlugins, s as pluginsInstallError, l as reportPluginsInstall } from "../_chunks/plugins-B2Gym8Lw.js";
|
package/dist/env.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isatty } from "node:tty";
|
|
1
2
|
// src/env.ts
|
|
2
3
|
const isCi = () => {
|
|
3
4
|
return process.env.CI !== "false" && Boolean(process.env.CI);
|
|
@@ -6,16 +7,30 @@ const isInteractiveTerminal = () => !isCi() && Boolean(process.stdin.isTTY) && B
|
|
|
6
7
|
const isDebug = () => {
|
|
7
8
|
return Boolean(process.env.DEBUG);
|
|
8
9
|
};
|
|
10
|
+
const getTty = () => ({
|
|
11
|
+
stdin: isatty(0),
|
|
12
|
+
stdout: isatty(1)
|
|
13
|
+
});
|
|
14
|
+
const AI_AGENT_NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
15
|
+
const isNonEmpty = (value) => Boolean(value?.trim());
|
|
9
16
|
const getCliAgent = (env) => {
|
|
10
17
|
const active = [
|
|
11
18
|
["claude-code", env.CLAUDE_CODE_CHILD_SESSION === "1"],
|
|
12
19
|
["codex", env.CODEX_CI === "1" && (Boolean(env.CODEX_THREAD_ID?.trim()) || Boolean(env.CODEX_SESSION_ID?.trim()))],
|
|
13
20
|
["cursor", env.CURSOR_AGENT === "1"],
|
|
14
|
-
["opencode", env.OPENCODE === "1"],
|
|
21
|
+
["opencode", env.OPENCODE === "1" && env.KILO !== "1"],
|
|
22
|
+
["kilo", env.KILO === "1"],
|
|
15
23
|
["gemini-cli", env.GEMINI_CLI === "1"],
|
|
16
|
-
["
|
|
24
|
+
["qwen-code", env.QWEN_CODE === "1"],
|
|
25
|
+
["github-copilot-cli", env.COPILOT_CLI === "1"],
|
|
26
|
+
["antigravity", isNonEmpty(env.ANTIGRAVITY_AGENT)],
|
|
27
|
+
["amp", isNonEmpty(env.AMP_CURRENT_THREAD_ID)],
|
|
28
|
+
["cline", env.CLINE_ACTIVE === "true"],
|
|
29
|
+
["openclaw", env.OPENCLAW_SHELL === "exec"]
|
|
17
30
|
].filter(([, present]) => present);
|
|
18
|
-
return active.length === 1 ? active[0][0] : void 0;
|
|
31
|
+
if (active.length > 0) return active.length === 1 ? active[0][0] : void 0;
|
|
32
|
+
const declared = env.AI_AGENT?.trim().toLowerCase();
|
|
33
|
+
return declared && AI_AGENT_NAME.test(declared) ? declared : void 0;
|
|
19
34
|
};
|
|
20
35
|
const getGithubEnvVars = (env) => {
|
|
21
36
|
const vars = [
|
|
@@ -38,4 +53,4 @@ const getGithubEnvVars = (env) => {
|
|
|
38
53
|
});
|
|
39
54
|
return Object.fromEntries(map);
|
|
40
55
|
};
|
|
41
|
-
export { getCliAgent, getGithubEnvVars, isCi, isDebug, isInteractiveTerminal };
|
|
56
|
+
export { getCliAgent, getGithubEnvVars, getTty, isCi, isDebug, isInteractiveTerminal };
|
package/dist/init/copy.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getCliName } from "../utils/cli_name.js";
|
|
2
|
+
import { pluginsCommandProblemText } from "../plugins/targets.js";
|
|
2
3
|
// src/init/copy.ts
|
|
3
4
|
const INIT_SUBTITLE = "Set up coding agents and this directory for Neon.";
|
|
4
5
|
const MODE_MESSAGE = "How would you like to set up Neon?";
|
|
@@ -16,7 +17,7 @@ const AGENT_SETUP_SKIP_DESCRIPTION = "Continue to project setup without changing
|
|
|
16
17
|
const AGENT_PICKER_MESSAGE = "Which coding agents should receive Neon tooling? (space to toggle, enter to confirm)";
|
|
17
18
|
const AGENT_DETECTED_DESCRIPTION = "Detected on this machine or in this directory.";
|
|
18
19
|
const AGENT_OTHER_DESCRIPTION = "Install for this agent.";
|
|
19
|
-
const
|
|
20
|
+
const agentUnusableCommandDescription = (row) => `Detected, but "${row.command.name}" ${pluginsCommandProblemText(row)}. Its plugin cannot install.`;
|
|
20
21
|
const AGENT_PLUGIN_SKIP_MESSAGE = "No agents selected. Continue without installing the Neon plugin?";
|
|
21
22
|
const AGENT_SKILLS_PICKER_MESSAGE = "Which coding agents should receive Neon skills? (space to toggle, enter to confirm)";
|
|
22
23
|
const AGENT_MCP_PICKER_MESSAGE = "Which coding agents should receive the Neon MCP server? (space to toggle, enter to confirm)";
|
|
@@ -184,4 +185,4 @@ const headingForOutcome = (outcome) => {
|
|
|
184
185
|
default: return outcome;
|
|
185
186
|
}
|
|
186
187
|
};
|
|
187
|
-
export { AGENTS_NOT_INSTALLED, AGENT_DETECTED_DESCRIPTION, AGENT_MCP_PICKER_MESSAGE, AGENT_MCP_SKIP_MESSAGE, AGENT_OTHER_DESCRIPTION, AGENT_PICKER_MESSAGE, AGENT_PLUGIN_SKIP_MESSAGE, AGENT_SETUP_MESSAGE, AGENT_SETUP_PLUGIN_DESCRIPTION, AGENT_SETUP_PLUGIN_TITLE, AGENT_SETUP_SKILLS_DESCRIPTION, AGENT_SETUP_SKILLS_TITLE, AGENT_SETUP_SKIP_DESCRIPTION, AGENT_SETUP_SKIP_TITLE, AGENT_SKILLS_PICKER_MESSAGE, AGENT_SKILLS_SKIP_MESSAGE, CANCELLED_BODY, CLAIMABLE_ACCOUNT_FLAGS, CLAIMABLE_ALREADY_LINKED, CLAIMABLE_MCP_API_KEY, CLAIMABLE_NO_LINK, CONFIG_CONFIRM_HINT, CONFIG_CONFIRM_MESSAGE, HEADING_CANCELLED, HEADING_COMPLETE, HEADING_FAILED, HEADING_PENDING, INIT_NEEDS_YES, INIT_SUBTITLE, MCP_API_KEY_NEEDS_AUTH, MCP_AUTH_API_KEY_DESCRIPTION, MCP_AUTH_API_KEY_TITLE, MCP_AUTH_MESSAGE, MCP_AUTH_OAUTH_DESCRIPTION, MCP_AUTH_OAUTH_TITLE, MCP_OAUTH_FALLBACK, MCP_SCOPED_NEEDS_PROJECT, MCP_SCOPE_GLOBAL_DESCRIPTION, MCP_SCOPE_GLOBAL_TITLE, MCP_SCOPE_MESSAGE, MCP_SCOPE_PROJECT_DESCRIPTION, MCP_SCOPE_PROJECT_TITLE, MODE_CUSTOM_DESCRIPTION, MODE_CUSTOM_TITLE, MODE_MESSAGE, MODE_RECOMMENDED_DESCRIPTION, MODE_RECOMMENDED_TITLE, NON_TTY_AGENT_SETUP, NON_TTY_LINK_NEEDS_AUTH, NO_AGENTS_FALLBACK_STATUS, NO_AGENT_SETUP_CONFLICT, PACKAGE_MANAGER_MESSAGE, PROGRESS, PROJECT_PINNED_OFFLINE, PROJECT_SETUP_CLAIMABLE_DESCRIPTION, PROJECT_SETUP_CLAIMABLE_TITLE, PROJECT_SETUP_LINK_DESCRIPTION, PROJECT_SETUP_LINK_TITLE, PROJECT_SETUP_MESSAGE, SERVICES_HINT, SERVICES_MESSAGE, SKILLS_PICKER_MESSAGE,
|
|
188
|
+
export { AGENTS_NOT_INSTALLED, AGENT_DETECTED_DESCRIPTION, AGENT_MCP_PICKER_MESSAGE, AGENT_MCP_SKIP_MESSAGE, AGENT_OTHER_DESCRIPTION, AGENT_PICKER_MESSAGE, AGENT_PLUGIN_SKIP_MESSAGE, AGENT_SETUP_MESSAGE, AGENT_SETUP_PLUGIN_DESCRIPTION, AGENT_SETUP_PLUGIN_TITLE, AGENT_SETUP_SKILLS_DESCRIPTION, AGENT_SETUP_SKILLS_TITLE, AGENT_SETUP_SKIP_DESCRIPTION, AGENT_SETUP_SKIP_TITLE, AGENT_SKILLS_PICKER_MESSAGE, AGENT_SKILLS_SKIP_MESSAGE, CANCELLED_BODY, CLAIMABLE_ACCOUNT_FLAGS, CLAIMABLE_ALREADY_LINKED, CLAIMABLE_MCP_API_KEY, CLAIMABLE_NO_LINK, CONFIG_CONFIRM_HINT, CONFIG_CONFIRM_MESSAGE, HEADING_CANCELLED, HEADING_COMPLETE, HEADING_FAILED, HEADING_PENDING, INIT_NEEDS_YES, INIT_SUBTITLE, MCP_API_KEY_NEEDS_AUTH, MCP_AUTH_API_KEY_DESCRIPTION, MCP_AUTH_API_KEY_TITLE, MCP_AUTH_MESSAGE, MCP_AUTH_OAUTH_DESCRIPTION, MCP_AUTH_OAUTH_TITLE, MCP_OAUTH_FALLBACK, MCP_SCOPED_NEEDS_PROJECT, MCP_SCOPE_GLOBAL_DESCRIPTION, MCP_SCOPE_GLOBAL_TITLE, MCP_SCOPE_MESSAGE, MCP_SCOPE_PROJECT_DESCRIPTION, MCP_SCOPE_PROJECT_TITLE, MODE_CUSTOM_DESCRIPTION, MODE_CUSTOM_TITLE, MODE_MESSAGE, MODE_RECOMMENDED_DESCRIPTION, MODE_RECOMMENDED_TITLE, NON_TTY_AGENT_SETUP, NON_TTY_LINK_NEEDS_AUTH, NO_AGENTS_FALLBACK_STATUS, NO_AGENT_SETUP_CONFLICT, PACKAGE_MANAGER_MESSAGE, PROGRESS, PROJECT_PINNED_OFFLINE, PROJECT_SETUP_CLAIMABLE_DESCRIPTION, PROJECT_SETUP_CLAIMABLE_TITLE, PROJECT_SETUP_LINK_DESCRIPTION, PROJECT_SETUP_LINK_TITLE, PROJECT_SETUP_MESSAGE, SERVICES_HINT, SERVICES_MESSAGE, SKILLS_PICKER_MESSAGE, agentUnusableCommandDescription, claimableNext, claimableThenServicesNext, configInitNext, envPullFailedNext, existingConfigNext, headingForOutcome, installFailedNext, mcpAfterLinkNext, mcpConfigLocationSkipped, mcpConfigLocationUnavailable, mcpKeyFailedNext, namedAgentsUnavailable, newConfigNext, offlinePinNext, skippedLinkNext, toolingFailedNext, unattendedUnauthedNext };
|
package/dist/init/run.js
CHANGED
|
@@ -11,7 +11,7 @@ import { CONFIG_INIT_SERVICES, parseConfigInitServices } from "../config_templat
|
|
|
11
11
|
import { hasNeonConfigFile, neonConfigFilename } from "../project.js";
|
|
12
12
|
import { getAgentDisplayName } from "../mcp/agents.js";
|
|
13
13
|
import { mcpInstallableAgents } from "../mcp/targets.js";
|
|
14
|
-
import { missingPluginsCommands, pluginsInstallableAgents } from "../plugins/targets.js";
|
|
14
|
+
import { missingPluginsCommands, pluginsCommandProblemText, pluginsInstallableAgents, unusablePluginsCommands } from "../plugins/targets.js";
|
|
15
15
|
import { skillsInstallableAgents } from "../skills/targets.js";
|
|
16
16
|
import { FALLBACK_SKILLS_AGENTS, assertNamedAgentTooling, funnelAgentSetup, planInitToolingSteps, recommendedTooling, resolveNamedAgents, splitInitTooling } from "./plan.js";
|
|
17
17
|
import { inferPackageManager, resolvePackageManager } from "../utils/package_manager.js";
|
|
@@ -67,10 +67,10 @@ const pluginScopeFor = (agents, preferred) => {
|
|
|
67
67
|
const project = new Set(pluginsInstallableAgents("project"));
|
|
68
68
|
return agents.some((id) => !project.has(id) && pluginsInstallableAgents("global").includes(id)) ? "global" : "project";
|
|
69
69
|
};
|
|
70
|
-
const
|
|
70
|
+
const warnUnusablePluginCommands = (unusable, tooling) => {
|
|
71
71
|
if (tooling.setup !== "skills-mcp" && tooling.setup !== "mixed") return;
|
|
72
72
|
const routed = /* @__PURE__ */ new Set([...tooling.skillsAgents, ...tooling.mcpAgents]);
|
|
73
|
-
for (const
|
|
73
|
+
for (const row of unusable) if (routed.has(row.agent)) log.warning("%s: \"%s\" %s, so its plugin cannot install. Installing Neon skills and MCP instead.", getAgentDisplayName(row.agent), row.command.name, pluginsCommandProblemText(row));
|
|
74
74
|
};
|
|
75
75
|
const nonEmptyAgents = (ids, fallback) => {
|
|
76
76
|
const [first, ...rest] = ids;
|
|
@@ -124,7 +124,7 @@ const pickOrDetectAgents = async (input) => {
|
|
|
124
124
|
available: [...input.available],
|
|
125
125
|
detected,
|
|
126
126
|
preselected,
|
|
127
|
-
...input.
|
|
127
|
+
...input.unusableCommands !== void 0 ? { unusableCommands: input.unusableCommands } : {},
|
|
128
128
|
setup: input.setup
|
|
129
129
|
});
|
|
130
130
|
return detected;
|
|
@@ -265,9 +265,18 @@ const runInit = async (props) => {
|
|
|
265
265
|
}
|
|
266
266
|
const recommended = mode === "recommended";
|
|
267
267
|
const targets = named.length > 0 ? named : detection.detectedAgents;
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
268
|
+
let probedPluginCommands;
|
|
269
|
+
const unusablePluginCommands = () => {
|
|
270
|
+
probedPluginCommands ??= unusablePluginsCommands(targets, { cwd });
|
|
271
|
+
return probedPluginCommands;
|
|
272
|
+
};
|
|
273
|
+
let missingCommands;
|
|
274
|
+
const missingPluginCommands = () => {
|
|
275
|
+
missingCommands ??= missingPluginsCommands(targets, { cwd });
|
|
276
|
+
return missingCommands;
|
|
277
|
+
};
|
|
278
|
+
const unavailablePluginAgents = () => unusablePluginCommands().map((row) => row.agent);
|
|
279
|
+
const pluginReady = (agents) => agents.filter((id) => !unavailablePluginAgents().includes(id));
|
|
271
280
|
let tooling = { setup: "skip" };
|
|
272
281
|
let mcpAuth = props.mcpAuth;
|
|
273
282
|
let mcpAuthChosen = props.mcpAuth !== void 0;
|
|
@@ -277,9 +286,9 @@ const runInit = async (props) => {
|
|
|
277
286
|
let pluginScope = recommended ? "global" : "project";
|
|
278
287
|
if (recommended) {
|
|
279
288
|
pluginScope = "global";
|
|
280
|
-
tooling = named.length > 0 ? splitInitTooling(named, "global", "global", unavailablePluginAgents) : recommendedTooling(detection.detectedAgents, unavailablePluginAgents);
|
|
289
|
+
tooling = named.length > 0 ? splitInitTooling(named, "global", "global", unavailablePluginAgents()) : recommendedTooling(detection.detectedAgents, unavailablePluginAgents());
|
|
281
290
|
if (named.length === 0 && detection.detectedAgents.length === 0) printInitProgress(NO_AGENTS_FALLBACK_STATUS);
|
|
282
|
-
|
|
291
|
+
warnUnusablePluginCommands(unusablePluginCommands(), tooling);
|
|
283
292
|
mcpAuth = !detection.authenticated && (tooling.setup === "skills-mcp" || tooling.setup === "mixed") ? "oauth" : mcpAuth;
|
|
284
293
|
} else {
|
|
285
294
|
const inferred = inferInitAgentSetup({
|
|
@@ -294,8 +303,8 @@ const runInit = async (props) => {
|
|
|
294
303
|
else if (inferred.kind === "auto") {
|
|
295
304
|
const autoAgents = named.length > 0 ? named : detection.detectedAgents;
|
|
296
305
|
pluginScope = pluginScopeFor(pluginReady(autoAgents), "project");
|
|
297
|
-
tooling = splitInitTooling(autoAgents, pluginScope, mcpConfigLocation, unavailablePluginAgents);
|
|
298
|
-
|
|
306
|
+
tooling = splitInitTooling(autoAgents, pluginScope, mcpConfigLocation, unavailablePluginAgents());
|
|
307
|
+
warnUnusablePluginCommands(unusablePluginCommands(), tooling);
|
|
299
308
|
if (tooling.setup === "skip" && yes && named.length === 0) {
|
|
300
309
|
tooling = recommendedTooling([]);
|
|
301
310
|
printInitProgress(NO_AGENTS_FALLBACK_STATUS);
|
|
@@ -310,8 +319,8 @@ const runInit = async (props) => {
|
|
|
310
319
|
available,
|
|
311
320
|
detected: detection.detectedAgents,
|
|
312
321
|
...setup === "plugin" ? {
|
|
313
|
-
preselected:
|
|
314
|
-
|
|
322
|
+
preselected: detection.detectedAgents.filter((id) => !missingPluginCommands().some((row) => row.agent === id)),
|
|
323
|
+
unusableCommands: missingPluginCommands()
|
|
315
324
|
} : {},
|
|
316
325
|
interactive: detection.interactive,
|
|
317
326
|
setup,
|
package/dist/init/tooling.js
CHANGED
|
@@ -6,7 +6,7 @@ import { CannotMintApiKeyError } from "../mcp/mint.js";
|
|
|
6
6
|
import { detectAgent } from "./detect_host.js";
|
|
7
7
|
import { AgentSelectionSkipped, canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
8
8
|
import { a as mcpInstallError, s as reportMcpInstall } from "../_chunks/mcp-BznTRK3w.js";
|
|
9
|
-
import { l as reportPluginsInstall, o as installPlugins, s as pluginsInstallError } from "../_chunks/plugins-
|
|
9
|
+
import { l as reportPluginsInstall, o as installPlugins, s as pluginsInstallError } from "../_chunks/plugins-B2Gym8Lw.js";
|
|
10
10
|
import { c as skillsInstallError, o as installSkills, s as reportSkillsInstall } from "../_chunks/skills-DKgafm65.js";
|
|
11
11
|
import { runAuthenticatedMcp } from "./auth.js";
|
|
12
12
|
import { raceSigint } from "./cancelled.js";
|
package/dist/init/wizard.js
CHANGED
|
@@ -2,7 +2,7 @@ import { CONFIG_INIT_SERVICES } from "../config_template.js";
|
|
|
2
2
|
import { getAgentDisplayName, listMcpAgentIds } from "../mcp/agents.js";
|
|
3
3
|
import { INIT_NEEDS_YES_OR_TERMINAL } from "./plan.js";
|
|
4
4
|
import { installedPackageManagers } from "../utils/package_manager.js";
|
|
5
|
-
import { AGENT_DETECTED_DESCRIPTION, AGENT_MCP_PICKER_MESSAGE, AGENT_MCP_SKIP_MESSAGE, AGENT_OTHER_DESCRIPTION, AGENT_PICKER_MESSAGE, AGENT_PLUGIN_SKIP_MESSAGE, AGENT_SETUP_MESSAGE, AGENT_SETUP_PLUGIN_DESCRIPTION, AGENT_SETUP_PLUGIN_TITLE, AGENT_SETUP_SKILLS_DESCRIPTION, AGENT_SETUP_SKILLS_TITLE, AGENT_SETUP_SKIP_DESCRIPTION, AGENT_SETUP_SKIP_TITLE, AGENT_SKILLS_PICKER_MESSAGE, AGENT_SKILLS_SKIP_MESSAGE, CONFIG_CONFIRM_HINT, CONFIG_CONFIRM_MESSAGE, MCP_AUTH_API_KEY_DESCRIPTION, MCP_AUTH_API_KEY_TITLE, MCP_AUTH_MESSAGE, MCP_AUTH_OAUTH_DESCRIPTION, MCP_AUTH_OAUTH_TITLE, MCP_SCOPE_GLOBAL_DESCRIPTION, MCP_SCOPE_GLOBAL_TITLE, MCP_SCOPE_MESSAGE, MCP_SCOPE_PROJECT_DESCRIPTION, MCP_SCOPE_PROJECT_TITLE, MODE_CUSTOM_DESCRIPTION, MODE_CUSTOM_TITLE, MODE_MESSAGE, MODE_RECOMMENDED_DESCRIPTION, MODE_RECOMMENDED_TITLE, PACKAGE_MANAGER_MESSAGE, PROJECT_SETUP_CLAIMABLE_DESCRIPTION, PROJECT_SETUP_CLAIMABLE_TITLE, PROJECT_SETUP_LINK_DESCRIPTION, PROJECT_SETUP_LINK_TITLE, PROJECT_SETUP_MESSAGE, SERVICES_HINT, SERVICES_MESSAGE, SKILLS_PICKER_MESSAGE,
|
|
5
|
+
import { AGENT_DETECTED_DESCRIPTION, AGENT_MCP_PICKER_MESSAGE, AGENT_MCP_SKIP_MESSAGE, AGENT_OTHER_DESCRIPTION, AGENT_PICKER_MESSAGE, AGENT_PLUGIN_SKIP_MESSAGE, AGENT_SETUP_MESSAGE, AGENT_SETUP_PLUGIN_DESCRIPTION, AGENT_SETUP_PLUGIN_TITLE, AGENT_SETUP_SKILLS_DESCRIPTION, AGENT_SETUP_SKILLS_TITLE, AGENT_SETUP_SKIP_DESCRIPTION, AGENT_SETUP_SKIP_TITLE, AGENT_SKILLS_PICKER_MESSAGE, AGENT_SKILLS_SKIP_MESSAGE, CONFIG_CONFIRM_HINT, CONFIG_CONFIRM_MESSAGE, MCP_AUTH_API_KEY_DESCRIPTION, MCP_AUTH_API_KEY_TITLE, MCP_AUTH_MESSAGE, MCP_AUTH_OAUTH_DESCRIPTION, MCP_AUTH_OAUTH_TITLE, MCP_SCOPE_GLOBAL_DESCRIPTION, MCP_SCOPE_GLOBAL_TITLE, MCP_SCOPE_MESSAGE, MCP_SCOPE_PROJECT_DESCRIPTION, MCP_SCOPE_PROJECT_TITLE, MODE_CUSTOM_DESCRIPTION, MODE_CUSTOM_TITLE, MODE_MESSAGE, MODE_RECOMMENDED_DESCRIPTION, MODE_RECOMMENDED_TITLE, PACKAGE_MANAGER_MESSAGE, PROJECT_SETUP_CLAIMABLE_DESCRIPTION, PROJECT_SETUP_CLAIMABLE_TITLE, PROJECT_SETUP_LINK_DESCRIPTION, PROJECT_SETUP_LINK_TITLE, PROJECT_SETUP_MESSAGE, SERVICES_HINT, SERVICES_MESSAGE, SKILLS_PICKER_MESSAGE, agentUnusableCommandDescription } from "./copy.js";
|
|
6
6
|
import { POSTGRES_SERVICE_CHOICE, keepPostgresSelected } from "../utils/service_picker.js";
|
|
7
7
|
import { canPickAgentsInteractively, pickAgentsInteractively } from "../utils/agent_picker.js";
|
|
8
8
|
import { NEON_SKILL_CATALOG, listSkillIds } from "../skills/catalog.js";
|
|
@@ -72,18 +72,18 @@ const pickAgentSetupInteractively = async () => {
|
|
|
72
72
|
const pickInitAgentsInteractively = async (input) => {
|
|
73
73
|
requireInteractive();
|
|
74
74
|
const detected = new Set(input.detected);
|
|
75
|
-
const
|
|
75
|
+
const unusable = new Map((input.unusableCommands ?? []).map((row) => [row.agent, row]));
|
|
76
76
|
const selected = await pickAgentsInteractively({
|
|
77
77
|
onCancel: aborted,
|
|
78
78
|
message: input.setup === "skills" ? AGENT_SKILLS_PICKER_MESSAGE : input.setup === "mcp" ? AGENT_MCP_PICKER_MESSAGE : AGENT_PICKER_MESSAGE,
|
|
79
79
|
skipMessage: input.setup === "plugin" ? AGENT_PLUGIN_SKIP_MESSAGE : input.setup === "skills" ? AGENT_SKILLS_SKIP_MESSAGE : AGENT_MCP_SKIP_MESSAGE,
|
|
80
80
|
selected: input.preselected ?? input.detected,
|
|
81
81
|
choices: input.available.map((id) => {
|
|
82
|
-
const
|
|
82
|
+
const row = unusable.get(id);
|
|
83
83
|
return {
|
|
84
84
|
id,
|
|
85
85
|
title: `${getAgentDisplayName(id)} (${id})`,
|
|
86
|
-
description:
|
|
86
|
+
description: row !== void 0 ? agentUnusableCommandDescription(row) : detected.has(id) ? AGENT_DETECTED_DESCRIPTION : AGENT_OTHER_DESCRIPTION
|
|
87
87
|
};
|
|
88
88
|
})
|
|
89
89
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// src/plugins/child_env.ts
|
|
2
|
+
const TELEMETRY_BLOCKERS = ["DISABLE_TELEMETRY", "DO_NOT_TRACK"];
|
|
3
|
+
/** The env the plugins CLI and the agent CLIs it calls run with. */
|
|
4
|
+
const pluginsChildEnv = (base = process.env) => {
|
|
5
|
+
const env = { ...base };
|
|
6
|
+
for (const key of TELEMETRY_BLOCKERS) delete env[key];
|
|
7
|
+
for (const key of Object.keys(env)) if (key.toUpperCase() === "NEON_API_KEY") delete env[key];
|
|
8
|
+
return env;
|
|
9
|
+
};
|
|
10
|
+
export { pluginsChildEnv };
|
package/dist/plugins/plan.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { getAgentDisplayName, tryResolveAddMcpAgentId } from "../mcp/agents.js";
|
|
2
2
|
import "../init/agents.js";
|
|
3
|
-
import { detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, missingPluginsCommands, pluginsInstallableAgents } from "./targets.js";
|
|
3
|
+
import { detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, missingPluginsCommands, pluginsCommandFix, pluginsCommandProblemText, pluginsInstallableAgents, unusablePluginsCommands } from "./targets.js";
|
|
4
4
|
import { collectYesAgents, noDetectedAgentsMessage } from "../init/plan.js";
|
|
5
5
|
import { detectAgent } from "../init/detect_host.js";
|
|
6
6
|
import { agentChoicesFrom, pickAgentsInteractively, resolveAgentSelection } from "../utils/agent_picker.js";
|
|
7
7
|
// src/plugins/plan.ts
|
|
8
|
-
const
|
|
8
|
+
const unusablePluginsCommandsError = (unusable) => new Error(unusable.map((row) => `Cannot install the Neon plugin for ${getAgentDisplayName(row.agent)}: "${row.command.name}" ${pluginsCommandProblemText(row)}. ${pluginsCommandFix(row)}, then retry.`).join("\n"));
|
|
9
9
|
const assertPluginsCanRun = (options) => {
|
|
10
10
|
if (options.yes || options.interactive || options.hasAgents) return;
|
|
11
11
|
throw new Error("No interactive terminal. Pass -y to install into detected agents, or --agent <name> to name them.");
|
|
@@ -34,18 +34,18 @@ async function resolvePluginsPlan(options) {
|
|
|
34
34
|
detectAgent: options.detectAgent ?? detectAgent,
|
|
35
35
|
acceptHost: (id) => availableSet.has(id) || getPluginsTargetName(id) !== void 0
|
|
36
36
|
}) : scoped;
|
|
37
|
-
const
|
|
37
|
+
const unusableDetected = new Map((prompt && options.agents.length === 0 ? missingPluginsCommands(detected, commandLookup(options)) : []).map((row) => [row.agent, row]));
|
|
38
38
|
const choices = agentChoicesFrom(available, detected).map((choice) => {
|
|
39
|
-
const
|
|
40
|
-
return
|
|
39
|
+
const row = unusableDetected.get(choice.id);
|
|
40
|
+
return row === void 0 ? choice : {
|
|
41
41
|
...choice,
|
|
42
|
-
description: `detected, but "${command}"
|
|
42
|
+
description: `detected, but "${row.command.name}" ${pluginsCommandProblemText(row)}`
|
|
43
43
|
};
|
|
44
44
|
});
|
|
45
45
|
const selected = await resolveAgentSelection({
|
|
46
46
|
specified: options.agents,
|
|
47
47
|
choices,
|
|
48
|
-
detected: detected.filter((id) => !
|
|
48
|
+
detected: detected.filter((id) => !unusableDetected.has(id)),
|
|
49
49
|
message: "Which coding agents should get the Neon plugin? (space to toggle, enter to confirm)",
|
|
50
50
|
nonInteractiveMessage: noDetectedAgentsMessage({
|
|
51
51
|
scope,
|
|
@@ -77,17 +77,17 @@ async function resolvePluginsPlan(options) {
|
|
|
77
77
|
}
|
|
78
78
|
agents.push(id);
|
|
79
79
|
}
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
82
|
-
if (!(options.agents.length === 0 && !prompt)) throw
|
|
83
|
-
const missingAgents = new Set(
|
|
80
|
+
const unusableCommands = unusablePluginsCommands(agents, commandLookup(options));
|
|
81
|
+
if (unusableCommands.length > 0) {
|
|
82
|
+
if (!(options.agents.length === 0 && !prompt)) throw unusablePluginsCommandsError(unusableCommands);
|
|
83
|
+
const missingAgents = new Set(unusableCommands.map((row) => row.agent));
|
|
84
84
|
const installable = agents.filter((id) => !missingAgents.has(id));
|
|
85
85
|
return {
|
|
86
86
|
scope,
|
|
87
87
|
agents: installable,
|
|
88
88
|
skipped,
|
|
89
89
|
userScopeSkipped,
|
|
90
|
-
|
|
90
|
+
unusableCommands,
|
|
91
91
|
targets: installable.length === 0 ? [] : mappedPluginsTargets(installable, scope)
|
|
92
92
|
};
|
|
93
93
|
}
|
|
@@ -103,8 +103,8 @@ async function resolvePluginsPlan(options) {
|
|
|
103
103
|
agents,
|
|
104
104
|
skipped,
|
|
105
105
|
userScopeSkipped,
|
|
106
|
-
|
|
106
|
+
unusableCommands,
|
|
107
107
|
targets: mappedPluginsTargets(agents, scope)
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
|
-
export { assertPluginsCanRun,
|
|
110
|
+
export { assertPluginsCanRun, resolvePluginsPlan, unusablePluginsCommandsError };
|
package/dist/plugins/run.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { pluginsChildEnv } from "./child_env.js";
|
|
1
2
|
import { spawnSync } from "node:child_process";
|
|
2
3
|
import { execa } from "execa";
|
|
3
4
|
// src/plugins/run.ts
|
|
4
|
-
const TELEMETRY_BLOCKERS = ["DISABLE_TELEMETRY", "DO_NOT_TRACK"];
|
|
5
5
|
const PLUGIN_SOURCE = "neondatabase/agent-skills";
|
|
6
6
|
const NEON_PLUGIN_NAME = "neon-postgres";
|
|
7
7
|
const PLUGIN_SKILLS = [
|
|
@@ -16,12 +16,6 @@ const PLUGIN_SKILLS = [
|
|
|
16
16
|
"neon-realtime"
|
|
17
17
|
];
|
|
18
18
|
const PLUGINS_CLI_TIMEOUT_MS = 12e4;
|
|
19
|
-
const pluginsChildEnv = (base = process.env) => {
|
|
20
|
-
const env = { ...base };
|
|
21
|
-
for (const key of TELEMETRY_BLOCKERS) delete env[key];
|
|
22
|
-
for (const key of Object.keys(env)) if (key.toUpperCase() === "NEON_API_KEY") delete env[key];
|
|
23
|
-
return env;
|
|
24
|
-
};
|
|
25
19
|
const pluginsScopeFlag = (global) => global ? "user" : "project";
|
|
26
20
|
const pluginsAddArgs = (options) => {
|
|
27
21
|
if (options.target.length === 0) throw new Error("plugins add needs a -t target.");
|
|
@@ -130,4 +124,4 @@ const killProcessTree = (pid) => {
|
|
|
130
124
|
};
|
|
131
125
|
const isCommandMissing = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
132
126
|
const isExecaFailure = (error) => typeof error === "object" && error !== null && "shortMessage" in error && typeof error.shortMessage === "string";
|
|
133
|
-
export { NEON_PLUGIN_NAME, PLUGINS_CLI_TIMEOUT_MS, PLUGIN_SKILLS, PLUGIN_SOURCE, neonPluginsRetryCommand, pluginsAddArgs,
|
|
127
|
+
export { NEON_PLUGIN_NAME, PLUGINS_CLI_TIMEOUT_MS, PLUGIN_SKILLS, PLUGIN_SOURCE, neonPluginsRetryCommand, pluginsAddArgs, pluginsCliFailureMessage, pluginsScopeFlag, quoteNpxArg, runPluginsCli };
|
package/dist/plugins/targets.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { detectInstalledAgents, listMcpAgentIds, tryResolveAddMcpAgentId, uniqueAgentIds } from "../mcp/agents.js";
|
|
2
2
|
import "../init/agents.js";
|
|
3
|
+
import { pluginsChildEnv } from "./child_env.js";
|
|
3
4
|
import { delimiter, dirname, join, resolve } from "node:path";
|
|
4
5
|
import { detectProjectAgents } from "add-mcp";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
5
7
|
import which from "which";
|
|
6
8
|
// src/plugins/targets.ts
|
|
7
9
|
const CLAUDE_CODE = { target: "claude-code" };
|
|
@@ -13,11 +15,20 @@ const PLUGINS_TARGET_BY_TYPE = {
|
|
|
13
15
|
codex: { target: "codex" },
|
|
14
16
|
"github-copilot-cli": {
|
|
15
17
|
target: "github-copilot",
|
|
16
|
-
command:
|
|
18
|
+
command: {
|
|
19
|
+
name: "copilot",
|
|
20
|
+
product: "GitHub Copilot CLI",
|
|
21
|
+
versionPrefix: "GitHub Copilot CLI",
|
|
22
|
+
minVersion: "0.0.400"
|
|
23
|
+
}
|
|
17
24
|
},
|
|
18
25
|
"grok-build": {
|
|
19
26
|
target: "grok",
|
|
20
|
-
command:
|
|
27
|
+
command: {
|
|
28
|
+
name: "grok",
|
|
29
|
+
product: "Grok Build",
|
|
30
|
+
versionPrefix: "grok"
|
|
31
|
+
}
|
|
21
32
|
}
|
|
22
33
|
};
|
|
23
34
|
const pluginsTargets = () => [...new Set(Object.values(PLUGINS_TARGET_BY_TYPE))];
|
|
@@ -38,20 +49,127 @@ const pluginsCommandSearchPath = (cwd, path) => {
|
|
|
38
49
|
}
|
|
39
50
|
return [...bins, path].join(delimiter);
|
|
40
51
|
};
|
|
41
|
-
const
|
|
52
|
+
const VERSION_TIMEOUT_MS = 1e4;
|
|
53
|
+
const versionParts = (version) => version.split(".").map((part) => Number.parseInt(part, 10));
|
|
54
|
+
/** Exported for tests. */
|
|
55
|
+
const versionAtLeast = (version, minimum) => {
|
|
56
|
+
const have = versionParts(version);
|
|
57
|
+
const need = versionParts(minimum);
|
|
58
|
+
for (let i = 0; i < 3; i++) {
|
|
59
|
+
const a = have[i] ?? 0;
|
|
60
|
+
const b = need[i] ?? 0;
|
|
61
|
+
if (a !== b) return a > b;
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
};
|
|
65
|
+
/** Exported for tests. */
|
|
66
|
+
const pluginsCommandVersionProblem = (command, output) => {
|
|
67
|
+
const label = `${command.versionPrefix} `;
|
|
68
|
+
const at = output.indexOf(label);
|
|
69
|
+
const version = at === -1 ? void 0 : output.slice(at + label.length).match(/^\d+\.\d+\.\d+/)?.[0];
|
|
70
|
+
if (version === void 0) return { kind: "unrecognized" };
|
|
71
|
+
if (command.minVersion !== void 0 && !versionAtLeast(version, command.minVersion)) return {
|
|
72
|
+
kind: "outdated",
|
|
73
|
+
version
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
const firstLine = (text) => text.split("\n").map((line) => line.trim()).find((line) => line.length > 0) ?? "";
|
|
77
|
+
/** The child sees the same PATH the command was resolved on, as under npx. */
|
|
78
|
+
const withPath = (env, path) => {
|
|
79
|
+
const key = Object.keys(env).find((name) => name.toUpperCase() === "PATH");
|
|
80
|
+
return {
|
|
81
|
+
...env,
|
|
82
|
+
[key ?? "PATH"]: path
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Runs `<command> --version` the way the plugins CLI spawns the command (no shell, the npx
|
|
87
|
+
* search PATH, its child env), so a command it could not run is caught before it runs.
|
|
88
|
+
*/
|
|
89
|
+
const probePluginsCommand = (command, options) => {
|
|
90
|
+
const resolved = which.sync(command.name, {
|
|
91
|
+
nothrow: true,
|
|
92
|
+
path: options.searchPath
|
|
93
|
+
});
|
|
94
|
+
if (resolved === null) return { kind: "missing" };
|
|
95
|
+
const result = spawnSync(resolved, ["--version"], {
|
|
96
|
+
cwd: options.cwd,
|
|
97
|
+
env: withPath(pluginsChildEnv(), options.searchPath),
|
|
98
|
+
encoding: "utf8",
|
|
99
|
+
stdio: [
|
|
100
|
+
"ignore",
|
|
101
|
+
"pipe",
|
|
102
|
+
"pipe"
|
|
103
|
+
],
|
|
104
|
+
timeout: VERSION_TIMEOUT_MS,
|
|
105
|
+
windowsHide: true
|
|
106
|
+
});
|
|
107
|
+
if (result.error !== void 0) {
|
|
108
|
+
const code = "code" in result.error && typeof result.error.code === "string" ? result.error.code : void 0;
|
|
109
|
+
return {
|
|
110
|
+
kind: "broken",
|
|
111
|
+
detail: code === "ETIMEDOUT" ? `timed out after ${VERSION_TIMEOUT_MS / 1e3} seconds` : code ?? result.error.message
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
115
|
+
if (result.status !== 0) {
|
|
116
|
+
const line = firstLine(result.stderr) || firstLine(result.stdout);
|
|
117
|
+
return {
|
|
118
|
+
kind: "broken",
|
|
119
|
+
detail: line.length > 0 ? line.slice(0, 160).replace(/\.$/, "") : result.signal !== null ? `killed by ${result.signal}` : `exit code ${result.status}`
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return pluginsCommandVersionProblem(command, output);
|
|
123
|
+
};
|
|
124
|
+
const commandRows = (agents, options, check) => {
|
|
42
125
|
const searchPath = pluginsCommandSearchPath(options.cwd, options.path ?? process.env.PATH ?? "");
|
|
43
|
-
const
|
|
126
|
+
const rows = [];
|
|
44
127
|
for (const agent of uniqueAgentIds(agents)) {
|
|
45
128
|
const command = getPluginsTarget(agent)?.command;
|
|
46
|
-
if (command
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}) === null) missing.push({
|
|
129
|
+
if (command === void 0) continue;
|
|
130
|
+
const problem = check(command, searchPath);
|
|
131
|
+
if (problem !== void 0) rows.push({
|
|
50
132
|
agent,
|
|
51
|
-
command
|
|
133
|
+
command,
|
|
134
|
+
problem
|
|
52
135
|
});
|
|
53
136
|
}
|
|
54
|
-
return
|
|
137
|
+
return rows;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Agents whose plugin command is not on the search PATH. Runs nothing, so it is safe for
|
|
141
|
+
* agents that were only detected and may not be installed.
|
|
142
|
+
*/
|
|
143
|
+
const missingPluginsCommands = (agents, options) => commandRows(agents, options, (command, searchPath) => which.sync(command.name, {
|
|
144
|
+
nothrow: true,
|
|
145
|
+
path: searchPath
|
|
146
|
+
}) === null ? { kind: "missing" } : void 0);
|
|
147
|
+
/**
|
|
148
|
+
* Agents whose plugin installs through a CLI that is missing, does not run, is not the
|
|
149
|
+
* expected product, or is too old. Runs `<command> --version`, so pass only agents the
|
|
150
|
+
* plugin is about to install into; file-based targets never spawn anything.
|
|
151
|
+
*/
|
|
152
|
+
const unusablePluginsCommands = (agents, options) => commandRows(agents, options, (command, searchPath) => probePluginsCommand(command, {
|
|
153
|
+
cwd: options.cwd,
|
|
154
|
+
searchPath
|
|
155
|
+
}));
|
|
156
|
+
/** Completes `"<command>" …` in a warning or error. */
|
|
157
|
+
const pluginsCommandProblemText = (row) => {
|
|
158
|
+
const { command, problem } = row;
|
|
159
|
+
const product = command.product;
|
|
160
|
+
switch (problem.kind) {
|
|
161
|
+
case "missing": return "was not found on PATH";
|
|
162
|
+
case "broken": return `did not run ("${command.name} --version" failed: ${problem.detail})`;
|
|
163
|
+
case "unrecognized": return `is not ${product} ("${command.name} --version" printed no ${product} version)`;
|
|
164
|
+
case "outdated": return `is ${product} ${problem.version}, older than the ${command.minVersion} its plugin needs`;
|
|
165
|
+
default: return problem;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
/** What to do about the problem, as an imperative clause. */
|
|
169
|
+
const pluginsCommandFix = (row) => {
|
|
170
|
+
const { command, problem } = row;
|
|
171
|
+
if (problem.kind === "missing") return "Install it or add it to PATH";
|
|
172
|
+
return `Install or update ${command.product}`;
|
|
55
173
|
};
|
|
56
174
|
function getPluginsTarget(agent) {
|
|
57
175
|
if (Object.prototype.hasOwnProperty.call(PLUGINS_TARGET_BY_TYPE, agent)) return PLUGINS_TARGET_BY_TYPE[agent];
|
|
@@ -104,4 +222,4 @@ const mappedPluginsTargets = (agents, scope) => {
|
|
|
104
222
|
if (mapped.length === 0) throw new Error(`None of the selected agents can install plugins. Supported agents: ${pluginsInstallableAgents(scope).join(", ")}`);
|
|
105
223
|
return mapped;
|
|
106
224
|
};
|
|
107
|
-
export { detectInstallablePluginsAgents, detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, missingPluginsCommands, pluginsInstallableAgents, pluginsMappedAgents, pluginsTargets, supportsPlugins };
|
|
225
|
+
export { detectInstallablePluginsAgents, detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, missingPluginsCommands, pluginsCommandFix, pluginsCommandProblemText, pluginsCommandVersionProblem, pluginsInstallableAgents, pluginsMappedAgents, pluginsTargets, supportsPlugins, unusablePluginsCommands, versionAtLeast };
|
package/dist/update_notifier.js
CHANGED
|
@@ -141,10 +141,11 @@ const duplicateInstallNotice = () => {
|
|
|
141
141
|
}
|
|
142
142
|
};
|
|
143
143
|
const isPackagedExecutable = () => "pkg" in process;
|
|
144
|
-
const
|
|
144
|
+
const commandSucceeded = () => (process.exitCode ?? 0) === 0;
|
|
145
|
+
const shouldRunUpdateNotifier = ({ commandPath, currentBranch, disabled, isCi: runningInCi, isPackaged, output, stderrIsTty, stdoutIsTty, succeeded }) => !runningInCi && !disabled && !isPackaged && output === "table" && stdoutIsTty && stderrIsTty && commandPath.length > 0 && commandPath[0] !== "completion" && !isCurrentBranchProbe({
|
|
145
146
|
commandPath,
|
|
146
147
|
currentBranch
|
|
147
|
-
});
|
|
148
|
+
}) && succeeded;
|
|
148
149
|
/**
|
|
149
150
|
* The skills notice drops the terminal checks: coding agents run the CLI without a TTY,
|
|
150
151
|
* and they are the ones that act on it. `neon skills` itself reports skill state.
|
|
@@ -157,7 +158,8 @@ const shouldRunSkillsNotifier = ({ commandPath, currentBranch, output }) => comm
|
|
|
157
158
|
isPackaged: isPackagedExecutable(),
|
|
158
159
|
output,
|
|
159
160
|
stderrIsTty: true,
|
|
160
|
-
stdoutIsTty: true
|
|
161
|
+
stdoutIsTty: true,
|
|
162
|
+
succeeded: commandSucceeded()
|
|
161
163
|
});
|
|
162
164
|
const notifyIfUpdateAvailable = ({ commandPath, configDir, currentBranch, currentVersion, output }) => {
|
|
163
165
|
if (!shouldRunUpdateNotifier({
|
|
@@ -168,7 +170,8 @@ const notifyIfUpdateAvailable = ({ commandPath, configDir, currentBranch, curren
|
|
|
168
170
|
isPackaged: isPackagedExecutable(),
|
|
169
171
|
output,
|
|
170
172
|
stderrIsTty: process.stderr.isTTY === true,
|
|
171
|
-
stdoutIsTty: process.stdout.isTTY === true
|
|
173
|
+
stdoutIsTty: process.stdout.isTTY === true,
|
|
174
|
+
succeeded: commandSucceeded()
|
|
172
175
|
})) return;
|
|
173
176
|
const now = Date.now();
|
|
174
177
|
if (process.platform !== "win32") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neon",
|
|
3
|
-
"version": "8.15.
|
|
3
|
+
"version": "8.15.1",
|
|
4
4
|
"description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -73,9 +73,9 @@
|
|
|
73
73
|
"yargs": "17.7.2",
|
|
74
74
|
"yoctocolors": "^2.1.2",
|
|
75
75
|
"@neon/config": "1.10.0",
|
|
76
|
-
"@neon/config-runtime": "1.11.0",
|
|
77
76
|
"@neon/realtime": "0.1.2",
|
|
78
|
-
"@neon/sdk": "7.7.0"
|
|
77
|
+
"@neon/sdk": "7.7.0",
|
|
78
|
+
"@neon/config-runtime": "1.11.0"
|
|
79
79
|
},
|
|
80
80
|
"optionalDependencies": {
|
|
81
81
|
"@napi-rs/keyring": "1.3.0",
|
|
@@ -114,8 +114,8 @@
|
|
|
114
114
|
"@neon-internals/cli-core": "0.0.0",
|
|
115
115
|
"@neon-internals/env-core": "0.0.38",
|
|
116
116
|
"@neon/env": "1.6.8",
|
|
117
|
-
"@neon/
|
|
118
|
-
"@neon/
|
|
117
|
+
"@neon/e2e-harness": "0.0.0",
|
|
118
|
+
"@neon/functions": "0.11.0"
|
|
119
119
|
},
|
|
120
120
|
"publishConfig": {
|
|
121
121
|
"access": "public",
|