neon 5.0.0 → 6.0.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.
- package/README.md +29 -26
- package/dist/commands/auth.js +9 -2
- package/dist/commands/config.js +39 -18
- package/dist/commands/init.js +44 -299
- package/dist/commands/mcp.js +1 -16
- package/dist/commands/plugins.js +2 -12
- package/dist/commands/skills.js +2 -12
- package/dist/index.js +9 -1
- package/dist/init/cancelled.js +16 -0
- package/dist/init/child.js +16 -4
- package/dist/init/choices.js +10 -21
- package/dist/init/chrome.js +71 -19
- package/dist/init/copy.js +133 -0
- package/dist/init/detect.js +58 -0
- package/dist/init/funnel.js +115 -0
- package/dist/init/mode.js +30 -0
- package/dist/init/plan.js +84 -3
- package/dist/init/run.js +641 -0
- package/dist/init/tooling.js +1 -1
- package/dist/init/wizard.js +207 -48
- package/dist/mcp/wizard.js +1 -27
- package/dist/skills/wizard.js +1 -25
- package/dist/test_utils/npm_env.js +26 -0
- package/dist/update_check_worker.js +47 -0
- package/dist/update_notifier.js +145 -0
- package/dist/utils/package_manager.js +8 -1
- package/dist/utils/service_picker.js +34 -8
- package/package.json +7 -5
- package/dist/plugins/wizard.js +0 -36
package/dist/commands/init.js
CHANGED
|
@@ -1,26 +1,13 @@
|
|
|
1
1
|
import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import { log } from "../log.js";
|
|
3
|
-
import { contextBranch, readContextFile } from "../context.js";
|
|
4
|
-
import { recordCommandSuccessExtras } from "../analytics.js";
|
|
5
|
-
import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
6
2
|
import { getCliName } from "../utils/cli_name.js";
|
|
7
|
-
import { deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
|
|
8
|
-
import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES, CONFIG_INIT_UNAVAILABLE } from "../config_template.js";
|
|
9
3
|
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { hasNeonConfigFile } from "./config.js";
|
|
14
|
-
import { runAuthenticatedLink } from "../init/link.js";
|
|
4
|
+
import { servicesOption } from "../neon_services.js";
|
|
5
|
+
import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES } from "../config_template.js";
|
|
6
|
+
import { initChildEnv } from "../init/child.js";
|
|
15
7
|
import { coerceAgentFlag } from "../utils/agent_flag.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { runAgentTooling, runInitSteps } from "../init/tooling.js";
|
|
20
|
-
import { handler as handler$1 } from "./bootstrap.js";
|
|
21
|
-
import { INIT_CONFIG_SERVICES_CONFLICT, INIT_TEMPLATE_KEEPS_CONFIG, configPlanFromResolution, resolveInitConfigChoice, resolveInitTemplateChoice, shouldRefreshEnvAfterNewConfig } from "../init/choices.js";
|
|
22
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
23
|
-
import { resolve } from "node:path";
|
|
8
|
+
import { initPluginAgents, initSkillsMcpAgents } from "../init/plan.js";
|
|
9
|
+
import { listSkillIds } from "../skills/catalog.js";
|
|
10
|
+
import { runInit } from "../init/run.js";
|
|
24
11
|
//#region src/commands/init.ts
|
|
25
12
|
var init_exports = /* @__PURE__ */ __exportAll({
|
|
26
13
|
builder: () => builder,
|
|
@@ -30,33 +17,43 @@ var init_exports = /* @__PURE__ */ __exportAll({
|
|
|
30
17
|
initChildEnv: () => initChildEnv
|
|
31
18
|
});
|
|
32
19
|
const command = "init";
|
|
33
|
-
const describe = "Set up this directory for Neon
|
|
20
|
+
const describe = "Set up coding agents and this directory for Neon. -y is Recommended. -y with Custom flags applies those answers without prompts.";
|
|
34
21
|
const removedProtocol = () => `\`${getCliName()} init --data\` was removed. Run \`${getCliName()} init\` or \`${getCliName()} init -y\`.`;
|
|
35
22
|
const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file", { hidden: true }).option("yes", {
|
|
36
23
|
alias: "y",
|
|
37
24
|
type: "boolean",
|
|
38
25
|
default: false,
|
|
39
|
-
describe: "
|
|
40
|
-
}).option("
|
|
26
|
+
describe: "Skip prompts. Alone: Recommended (detected agents, link when authenticated, default neon.ts). With --skill, MCP flags, --no-agent-setup, or --claimable: Custom using those flags"
|
|
27
|
+
}).option("agent-setup", {
|
|
28
|
+
type: "boolean",
|
|
29
|
+
default: true,
|
|
30
|
+
describe: "Install Neon into coding agents. Use --no-agent-setup to skip. Plugin vs skills is inferred from --skill and MCP flags"
|
|
31
|
+
}).option("claimable", {
|
|
41
32
|
type: "boolean",
|
|
42
33
|
default: false,
|
|
43
|
-
describe: "
|
|
44
|
-
}).option("
|
|
34
|
+
describe: "Create a claimable project that expires in 72 hours unless claimed. Selects Custom"
|
|
35
|
+
}).option("package-manager", {
|
|
45
36
|
type: "string",
|
|
46
|
-
|
|
37
|
+
choices: [
|
|
38
|
+
"npm",
|
|
39
|
+
"pnpm",
|
|
40
|
+
"yarn",
|
|
41
|
+
"bun"
|
|
42
|
+
],
|
|
43
|
+
describe: "Package manager for neon.ts dependencies. -y uses this without asking. Custom asks only when none is detected"
|
|
47
44
|
}).option("link", {
|
|
48
45
|
type: "boolean",
|
|
49
46
|
default: true,
|
|
50
47
|
describe: "Link a Neon project during setup. Use --no-link to skip without being asked"
|
|
51
48
|
}).option("config", {
|
|
52
49
|
type: "boolean",
|
|
53
|
-
describe: "
|
|
50
|
+
describe: "Create neon.ts after linking. Use --no-config to skip. Omitted in Custom: you will be asked"
|
|
54
51
|
}).option("services", servicesOption({
|
|
55
52
|
key: "services",
|
|
56
53
|
allowed: CONFIG_INIT_SERVICES,
|
|
57
|
-
describe: "
|
|
54
|
+
describe: "Services to declare in neon.ts",
|
|
58
55
|
noneMeans: CONFIG_INIT_NONE_MEANS,
|
|
59
|
-
also: "Implies creating neon.ts. Cannot be combined with --no-config.
|
|
56
|
+
also: "Implies creating neon.ts. Cannot be combined with --no-config."
|
|
60
57
|
})).option("org-id", {
|
|
61
58
|
describe: "Forwarded to link: organization ID to link to",
|
|
62
59
|
type: "string"
|
|
@@ -77,8 +74,24 @@ const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file
|
|
|
77
74
|
alias: "a",
|
|
78
75
|
type: "array",
|
|
79
76
|
string: true,
|
|
80
|
-
describe: "Coding agent to install into (repeatable).
|
|
77
|
+
describe: "Coding agent to install into (repeatable). Skips agent selection. Values listed below",
|
|
81
78
|
coerce: coerceAgentFlag
|
|
79
|
+
}).option("skill", {
|
|
80
|
+
type: "array",
|
|
81
|
+
string: true,
|
|
82
|
+
describe: "Neon skill to install (repeatable). Selects skills setup (not the plugin) and skips the skills picker. With MCP flags, also configures MCP. Values listed below"
|
|
83
|
+
}).option("mcp-config-location", {
|
|
84
|
+
type: "string",
|
|
85
|
+
choices: ["global", "project"],
|
|
86
|
+
describe: "Where to configure the Neon MCP server: global (user config, same as neon mcp) or project (this directory, same as neon mcp --project). Selects skills and MCP setup"
|
|
87
|
+
}).option("mcp-auth", {
|
|
88
|
+
type: "string",
|
|
89
|
+
choices: ["oauth", "api-key"],
|
|
90
|
+
describe: "MCP authentication. Selects skills and MCP setup. oauth is neon mcp --oauth"
|
|
91
|
+
}).option("mcp-project-scoped", {
|
|
92
|
+
type: "boolean",
|
|
93
|
+
default: false,
|
|
94
|
+
describe: "Limit MCP tools to the linked project, same as neon mcp --project-id. Selects skills and MCP setup"
|
|
82
95
|
}).option("data", {
|
|
83
96
|
hidden: true,
|
|
84
97
|
type: "string"
|
|
@@ -86,281 +99,13 @@ const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file
|
|
|
86
99
|
alias: "o",
|
|
87
100
|
hidden: true,
|
|
88
101
|
describe: "Not supported; the commands init runs print their own output"
|
|
89
|
-
}).example("$0 init", "
|
|
102
|
+
}).example("$0 init", "Recommended or Custom setup").example("$0 init -y", "Recommended setup without prompts").example("$0 init -y --skill neon --mcp-auth oauth --mcp-config-location project", "Custom: those skills and MCP, Recommended defaults for the rest").example("$0 init --no-agent-setup --claimable", "Skip agent setup and create a claimable project").example("$0 init --agent cursor --agent claude-code", "Skip agent selection; install tooling for those agents").epilogue(helpEpilogue("Recommended setup installs tooling for detected coding agents, links a Neon project when the CLI is authenticated or the session is interactive, and writes a default neon.ts.", "init sets up the current directory in place. It does not scaffold starter files.", "-y alone is Recommended. -y with --skill, MCP flags, --no-agent-setup, or --claimable is Custom: those flags, Recommended defaults for unanswered questions. -y never opens a browser.", "--skill selects skills (not the plugin). MCP flags select skills and MCP. --no-agent-setup skips agent setup. --agent without those flags installs the plugin (and skills/MCP for agents the plugin cannot cover).", "Without a TTY, pass -y or enough flags to answer every question.", "--agent / -a is forwarded to plugins, or to skills and mcp. It skips agent selection, including with -y.", helpCsv("Plugin agents", initPluginAgents()), helpCsv("Skills and MCP agents", initSkillsMcpAgents()), helpCsv("Skills", listSkillIds()))).check((argv) => {
|
|
90
103
|
if (argv.data !== void 0) throw new Error(removedProtocol());
|
|
91
104
|
if (argv.help !== true && (argv.output === "json" || argv.output === "yaml")) throw new Error(`\`${getCliName()} init\` does not support --output. The commands it runs print their own output.`);
|
|
92
105
|
return true;
|
|
93
106
|
}).strict();
|
|
94
|
-
const isLinked = (contextFile) => {
|
|
95
|
-
const projectId = readContextFile(contextFile).projectId;
|
|
96
|
-
return typeof projectId === "string" && projectId.length > 0;
|
|
97
|
-
};
|
|
98
|
-
const nestedBootstrapDirectory = (cwd) => resolve(cwd) === resolve(process.cwd()) ? "." : cwd;
|
|
99
|
-
const parsedInitServices = (raw) => {
|
|
100
|
-
const values = servicesFlagValue(raw);
|
|
101
|
-
if (values === void 0) return;
|
|
102
|
-
return parseServices(values, {
|
|
103
|
-
allowed: CONFIG_INIT_SERVICES,
|
|
104
|
-
whyUnavailable: CONFIG_INIT_UNAVAILABLE,
|
|
105
|
-
flag: "--services",
|
|
106
|
-
noneMeans: CONFIG_INIT_NONE_MEANS,
|
|
107
|
-
onDeprecated: (used, canonical) => log.warning(deprecatedServiceMessage(used, canonical))
|
|
108
|
-
});
|
|
109
|
-
};
|
|
110
|
-
const nestedBootstrapProps = (props, cwd, contextFile, template, linkInputs) => ({
|
|
111
|
-
apiClient: props.apiClient,
|
|
112
|
-
apiKey: props.apiKey,
|
|
113
|
-
apiHost: props.apiHost,
|
|
114
|
-
output: props.output,
|
|
115
|
-
contextFile,
|
|
116
|
-
directory: nestedBootstrapDirectory(cwd),
|
|
117
|
-
force: false,
|
|
118
|
-
listTemplates: false,
|
|
119
|
-
default: template.useDefault,
|
|
120
|
-
install: true,
|
|
121
|
-
git: true,
|
|
122
|
-
link: props.link !== false,
|
|
123
|
-
printBanner: false,
|
|
124
|
-
skipDoneSummary: true,
|
|
125
|
-
linkNoConfig: true,
|
|
126
|
-
narrate: "human",
|
|
127
|
-
...template.selected ? { selectedTemplate: template.selected } : {},
|
|
128
|
-
...!template.selected && template.id !== void 0 ? { template: template.id } : {},
|
|
129
|
-
linkInputs,
|
|
130
|
-
...props.agent !== void 0 ? { agent: props.agent } : {},
|
|
131
|
-
...props.configDir ? { configDir: props.configDir } : {},
|
|
132
|
-
...props.profile ? { profile: props.profile } : {},
|
|
133
|
-
...props.oauthHost ? { oauthHost: props.oauthHost } : {},
|
|
134
|
-
...props.clientId ? { clientId: props.clientId } : {},
|
|
135
|
-
...props.forceAuth !== void 0 ? { forceAuth: props.forceAuth } : {},
|
|
136
|
-
...props.allowUnsafeTls !== void 0 ? { allowUnsafeTls: props.allowUnsafeTls } : {},
|
|
137
|
-
...props.analytics === false ? { analytics: false } : {},
|
|
138
|
-
...props.run ? { run: props.run } : {},
|
|
139
|
-
...props.linkProject ? { linkProject: props.linkProject } : {},
|
|
140
|
-
...props.pickLink ? { pickLink: props.pickLink } : {},
|
|
141
|
-
...props.pickAgentSetup ? { pickAgentSetup: props.pickAgentSetup } : {},
|
|
142
|
-
...props.detectProjectAgents ? { detectProjectAgents: props.detectProjectAgents } : {},
|
|
143
|
-
...props.detectAgent ? { detectAgent: props.detectAgent } : {},
|
|
144
|
-
...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {}
|
|
145
|
-
});
|
|
146
|
-
const projectRow = (input) => {
|
|
147
|
-
if (input.alreadyLinked) return "already linked";
|
|
148
|
-
if (input.linkedNow) return "linked";
|
|
149
|
-
return "not linked";
|
|
150
|
-
};
|
|
151
|
-
const configSummary = (input) => {
|
|
152
|
-
if (input.plan.kind === "skip") return "skipped";
|
|
153
|
-
if (input.existingConfig) return "existing";
|
|
154
|
-
return "created";
|
|
155
|
-
};
|
|
156
|
-
const printNestedBootstrapDone = (result, cwd, fallbackTitle) => {
|
|
157
|
-
const hasConfig = result?.hasNeonConfig === true || hasNeonConfigFile(cwd);
|
|
158
|
-
printInitDone(formatInitDone({
|
|
159
|
-
heading: "Neon setup complete.",
|
|
160
|
-
rows: [
|
|
161
|
-
{
|
|
162
|
-
label: "Template",
|
|
163
|
-
value: result?.templateTitle ?? fallbackTitle
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
label: "Agents",
|
|
167
|
-
value: agentSetupLabel(result?.agentSetup ?? "skip")
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
label: "Project",
|
|
171
|
-
value: projectRow({
|
|
172
|
-
alreadyLinked: false,
|
|
173
|
-
linkedNow: result?.linked === true
|
|
174
|
-
})
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
label: "Config",
|
|
178
|
-
value: configSummaryLabel(hasConfig ? "template" : "skipped")
|
|
179
|
-
}
|
|
180
|
-
],
|
|
181
|
-
next: []
|
|
182
|
-
}));
|
|
183
|
-
};
|
|
184
|
-
const noteTemplateKeepsShippedConfig = (config, services) => {
|
|
185
|
-
if (config === void 0 && services === void 0) return;
|
|
186
|
-
log.warning(INIT_TEMPLATE_KEEPS_CONFIG);
|
|
187
|
-
};
|
|
188
|
-
const recordInitSuccess = (kind, agentSetup) => {
|
|
189
|
-
recordCommandSuccessExtras({
|
|
190
|
-
init_kind: kind,
|
|
191
|
-
...agentSetup !== void 0 ? { agent_setup: agentSetup } : {}
|
|
192
|
-
});
|
|
193
|
-
};
|
|
194
107
|
const handler = async (props) => {
|
|
195
|
-
|
|
196
|
-
const cwd = props.cwd ?? process.cwd();
|
|
197
|
-
const names = existsSync(cwd) ? readdirSync(cwd) : [];
|
|
198
|
-
const contextFile = resolve(cwd, props.contextFile);
|
|
199
|
-
const yes = props.yes === true;
|
|
200
|
-
const named = resolveNamedAgents(props.agent ?? []);
|
|
201
|
-
assertNamedAgentTooling(named, "init", yes ? { yes: true } : void 0);
|
|
202
|
-
const run = props.run ?? spawnCliChild;
|
|
203
|
-
const explicitKey = props.profile ? "" : credentialInputs().apiKeyFlag;
|
|
204
|
-
const authEnv = explicitKey ? { NEON_API_KEY: explicitKey } : void 0;
|
|
205
|
-
const forward = {
|
|
206
|
-
...props.configDir ? { configDir: props.configDir } : {},
|
|
207
|
-
...props.profile ? { profile: props.profile } : {},
|
|
208
|
-
apiHost: props.apiHost,
|
|
209
|
-
contextFile,
|
|
210
|
-
...props.analytics === false ? { analytics: false } : {}
|
|
211
|
-
};
|
|
212
|
-
const services = parsedInitServices(props.services);
|
|
213
|
-
if (props.config === false && services !== void 0) throw new Error(INIT_CONFIG_SERVICES_CONFLICT);
|
|
214
|
-
const linkInputs = {
|
|
215
|
-
...props.orgId ? { orgId: props.orgId } : {},
|
|
216
|
-
...props.projectId ? { projectId: props.projectId } : {},
|
|
217
|
-
...props.projectName ? { projectName: props.projectName } : {},
|
|
218
|
-
...props.regionId ? { regionId: props.regionId } : {},
|
|
219
|
-
...props.branch ? { branch: props.branch } : {}
|
|
220
|
-
};
|
|
221
|
-
const hasExplicitLinkInputs = props.orgId !== void 0 || props.projectId !== void 0 || props.projectName !== void 0 || props.regionId !== void 0 || props.branch !== void 0;
|
|
222
|
-
const templateChoice = resolveInitTemplateChoice({
|
|
223
|
-
empty: directoryIsEmpty(names),
|
|
224
|
-
yes,
|
|
225
|
-
skipTemplate: props.skipTemplate === true,
|
|
226
|
-
template: props.template
|
|
227
|
-
});
|
|
228
|
-
if (shouldPrintInitBanner(yes)) printInitBanner();
|
|
229
|
-
if (templateChoice.kind === "default" || templateChoice.kind === "template") {
|
|
230
|
-
if (templateChoice.kind === "template" && !yes && props.runBootstrap === void 0 && !canPickAgentsInteractively()) throw new Error(INIT_NEEDS_YES_OR_TERMINAL);
|
|
231
|
-
const runBootstrap = props.runBootstrap ?? handler$1;
|
|
232
|
-
noteTemplateKeepsShippedConfig(props.config, services);
|
|
233
|
-
const result = await runBootstrap(nestedBootstrapProps(props, cwd, contextFile, {
|
|
234
|
-
useDefault: yes,
|
|
235
|
-
...templateChoice.kind === "template" ? { id: templateChoice.id } : {}
|
|
236
|
-
}, linkInputs));
|
|
237
|
-
printNestedBootstrapDone(result, cwd, templateChoice.kind === "template" ? templateChoice.id : "default");
|
|
238
|
-
recordInitSuccess("empty-template", result?.agentSetup);
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
if (templateChoice.kind === "ask") {
|
|
242
|
-
if (props.pickTemplate === void 0 && !canPickAgentsInteractively()) throw new Error(INIT_NEEDS_YES_OR_TERMINAL);
|
|
243
|
-
const templates = await (props.fetchTemplates ?? fetchTemplates)();
|
|
244
|
-
const picked = await (props.pickTemplate ?? pickInitTemplateInteractively)(templates);
|
|
245
|
-
if (picked.kind === "template") {
|
|
246
|
-
const runBootstrap = props.runBootstrap ?? handler$1;
|
|
247
|
-
noteTemplateKeepsShippedConfig(props.config, services);
|
|
248
|
-
const result = await runBootstrap(nestedBootstrapProps(props, cwd, contextFile, {
|
|
249
|
-
selected: picked.template,
|
|
250
|
-
useDefault: false
|
|
251
|
-
}, linkInputs));
|
|
252
|
-
printNestedBootstrapDone(result, cwd, picked.template.id);
|
|
253
|
-
recordInitSuccess("empty-template", result?.agentSetup);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
const alreadyLinked = isLinked(contextFile);
|
|
258
|
-
const shouldLink = props.link !== false && (!alreadyLinked || hasExplicitLinkInputs);
|
|
259
|
-
const existingConfig = hasNeonConfigFile(cwd);
|
|
260
|
-
const canAskConfig = props.pickConfig !== void 0 || canPickAgentsInteractively();
|
|
261
|
-
const agentSetup = await runAgentTooling({
|
|
262
|
-
cwd,
|
|
263
|
-
yes,
|
|
264
|
-
run,
|
|
265
|
-
forward,
|
|
266
|
-
authEnv,
|
|
267
|
-
narrate: "human",
|
|
268
|
-
command: "init",
|
|
269
|
-
...named.length > 0 ? { agents: named } : {},
|
|
270
|
-
...props.pickAgentSetup ? { pickAgentSetup: props.pickAgentSetup } : {},
|
|
271
|
-
...props.detectProjectAgents ? { detectProjectAgents: props.detectProjectAgents } : {},
|
|
272
|
-
...props.detectAgent ? { detectAgent: props.detectAgent } : {},
|
|
273
|
-
...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {}
|
|
274
|
-
});
|
|
275
|
-
const canAskLink = props.pickLink !== void 0 || canPickAgentsInteractively();
|
|
276
|
-
const acceptedLink = shouldLink && (yes || !canAskLink || await (props.pickLink ?? pickInitLinkInteractively)());
|
|
277
|
-
if (acceptedLink) await (props.linkProject ?? runAuthenticatedLink)({
|
|
278
|
-
apiClient: props.apiClient,
|
|
279
|
-
apiKey: props.apiKey,
|
|
280
|
-
apiHost: props.apiHost,
|
|
281
|
-
output: props.output,
|
|
282
|
-
contextFile,
|
|
283
|
-
yes,
|
|
284
|
-
clear: false,
|
|
285
|
-
checks: true,
|
|
286
|
-
envPull: true,
|
|
287
|
-
config: false,
|
|
288
|
-
cwd,
|
|
289
|
-
...linkInputs,
|
|
290
|
-
...props.configDir ? { configDir: props.configDir } : {},
|
|
291
|
-
...props.profile ? { profile: props.profile } : {},
|
|
292
|
-
...props.oauthHost ? { oauthHost: props.oauthHost } : {},
|
|
293
|
-
...props.clientId ? { clientId: props.clientId } : {},
|
|
294
|
-
...props.forceAuth !== void 0 ? { forceAuth: props.forceAuth } : {},
|
|
295
|
-
...props.allowUnsafeTls !== void 0 ? { allowUnsafeTls: props.allowUnsafeTls } : {}
|
|
296
|
-
});
|
|
297
|
-
const configResolution = resolveInitConfigChoice({
|
|
298
|
-
flag: props.config,
|
|
299
|
-
yes,
|
|
300
|
-
canAsk: canAskConfig,
|
|
301
|
-
existingConfig,
|
|
302
|
-
...services !== void 0 ? { services } : {}
|
|
303
|
-
});
|
|
304
|
-
const accepted = configResolution.kind === "ask" ? await (props.pickConfig ?? pickInitConfigInteractively)() : void 0;
|
|
305
|
-
const configPlan = configPlanFromResolution(configResolution, accepted);
|
|
306
|
-
if (configPlan.kind === "write") {
|
|
307
|
-
await runInitSteps(planExistingInit({
|
|
308
|
-
linked: true,
|
|
309
|
-
yes,
|
|
310
|
-
agentSetup: "skip",
|
|
311
|
-
config: configPlan
|
|
312
|
-
}), {
|
|
313
|
-
cwd,
|
|
314
|
-
run,
|
|
315
|
-
forward,
|
|
316
|
-
authEnv,
|
|
317
|
-
narrate: "human"
|
|
318
|
-
});
|
|
319
|
-
const context = readContextFile(contextFile);
|
|
320
|
-
const branch = contextBranch(context);
|
|
321
|
-
if (shouldRefreshEnvAfterNewConfig({
|
|
322
|
-
wroteNewFile: !existingConfig,
|
|
323
|
-
projectId: context.projectId,
|
|
324
|
-
branch
|
|
325
|
-
})) try {
|
|
326
|
-
await runInitSteps([["env", "pull"]], {
|
|
327
|
-
cwd,
|
|
328
|
-
run,
|
|
329
|
-
forward,
|
|
330
|
-
authEnv,
|
|
331
|
-
narrate: "human"
|
|
332
|
-
});
|
|
333
|
-
} catch (err) {
|
|
334
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
335
|
-
log.warning(`Created neon.ts, but pulling its Neon env vars failed: %s
|
|
336
|
-
Run \`${getCliName()} env pull\` once resolved.`, message);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
printInitDone(formatInitDone({
|
|
340
|
-
heading: "Neon setup complete.",
|
|
341
|
-
rows: [
|
|
342
|
-
{
|
|
343
|
-
label: "Agents",
|
|
344
|
-
value: agentSetupLabel(agentSetup)
|
|
345
|
-
},
|
|
346
|
-
{
|
|
347
|
-
label: "Project",
|
|
348
|
-
value: projectRow({
|
|
349
|
-
alreadyLinked,
|
|
350
|
-
linkedNow: acceptedLink
|
|
351
|
-
})
|
|
352
|
-
},
|
|
353
|
-
{
|
|
354
|
-
label: "Config",
|
|
355
|
-
value: configSummaryLabel(configSummary({
|
|
356
|
-
plan: configPlan,
|
|
357
|
-
existingConfig
|
|
358
|
-
}))
|
|
359
|
-
}
|
|
360
|
-
],
|
|
361
|
-
next: []
|
|
362
|
-
}));
|
|
363
|
-
recordInitSuccess(templateChoice.kind === "existing" ? "existing" : "empty-skip", agentSetup);
|
|
108
|
+
await runInit(props);
|
|
364
109
|
};
|
|
365
110
|
//#endregion
|
|
366
111
|
export { builder, command, describe, handler, initChildEnv, init_exports as t };
|
package/dist/commands/mcp.js
CHANGED
|
@@ -10,7 +10,6 @@ import { NEON_MCP_CATEGORIES, NEON_MCP_URL, existingNeonApiKey, installNeonMcpSe
|
|
|
10
10
|
import { mcpInstallableAgents, resolveInstallTargets } from "../mcp/targets.js";
|
|
11
11
|
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
12
12
|
import { mintMcpApiKey, mintedKeyRevokeCommand, withdrawMintedKey } from "../mcp/mint.js";
|
|
13
|
-
import { confirmMcpInstall } from "../mcp/wizard.js";
|
|
14
13
|
import { resolveMcpPlan } from "../mcp/plan.js";
|
|
15
14
|
//#region src/commands/mcp.ts
|
|
16
15
|
var mcp_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -87,7 +86,7 @@ const builder = (argv) => argv.usage("$0 mcp [options]").options({
|
|
|
87
86
|
return parseMcpCategories(parts);
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
|
-
}).example("$0 mcp", "Interactive: config location, agents,
|
|
89
|
+
}).example("$0 mcp", "Interactive: config location, agents, then auth").example("$0 mcp -y", "Global config, installed apps else the host CLI agent, reuse or mint an API key").example("$0 mcp --oauth", "Install with OAuth; the agent signs in on first use").example("$0 mcp --agent cursor --agent claude-code", "Install into specific agents").example("$0 mcp --project", "Write project-level config").example("$0 mcp --read-only", "Hide write tools via ?readonly=true").example("$0 mcp --project-id <id>", "Pin tools to one project via ?projectId=").example("$0 mcp --category querying --category schema", "Limit tools to those categories").epilogue(helpEpilogue(`Installs ${NEON_MCP_URL}`, helpCsv("Supported agents at global scope", mcpGlobalAgents), helpCsv("--project does not support", mcpProjectDroppedAgents), helpCsv("Supported categories", NEON_MCP_CATEGORIES), "neon mcp -y:", " global config", " globally installed apps, else the host CLI agent", " reuse an existing Neon MCP API key, else mint an account-wide key", " write tools on, all categories", " no project pin (including from .neon)")).strict().check(noPassthrough("mcp"));
|
|
91
90
|
const handler = async (props) => {
|
|
92
91
|
const cwd = process.cwd();
|
|
93
92
|
const interactive = canPickAgentsInteractively() && props.yes !== true;
|
|
@@ -129,20 +128,6 @@ const handler = async (props) => {
|
|
|
129
128
|
if (!props.apiClient || !props.apiKey) throw new Error(`Authentication required. Run \`${getCliName()} auth\`, pass --api-key or use --oauth to install without a Neon credential.`);
|
|
130
129
|
if (!canPickAgentsInteractively() && props.yes !== true && (props.agent ?? []).length === 0) throw new Error("No interactive terminal. Pass -y to mint into every detected agent, --agent <name> to name them or --oauth to install without minting.");
|
|
131
130
|
}
|
|
132
|
-
if (interactive) {
|
|
133
|
-
if (!await confirmMcpInstall({
|
|
134
|
-
scope: plan.scope,
|
|
135
|
-
install,
|
|
136
|
-
skipped,
|
|
137
|
-
auth: plan.auth,
|
|
138
|
-
reuse: existing !== void 0,
|
|
139
|
-
url,
|
|
140
|
-
mintProjectId: plan.urlProjectId
|
|
141
|
-
})) {
|
|
142
|
-
log.info("Aborted. Nothing was written.");
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
131
|
let auth;
|
|
147
132
|
let minted;
|
|
148
133
|
if (plan.auth === "oauth") auth = { kind: "oauth" };
|
package/dist/commands/plugins.js
CHANGED
|
@@ -9,9 +9,8 @@ import { NEON_MCP_URL } from "../mcp/install.js";
|
|
|
9
9
|
import "../init/agents.js";
|
|
10
10
|
import { pluginsInstallableAgents } from "../plugins/targets.js";
|
|
11
11
|
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
12
|
-
import { NEON_PLUGIN_NAME, PLUGIN_SKILLS, PLUGIN_SOURCE, neonPluginsRetryCommand, pluginsAddArgs, runPluginsCli } from "../plugins/run.js";
|
|
13
12
|
import { resolvePluginsPlan } from "../plugins/plan.js";
|
|
14
|
-
import {
|
|
13
|
+
import { NEON_PLUGIN_NAME, PLUGIN_SKILLS, PLUGIN_SOURCE, neonPluginsRetryCommand, pluginsAddArgs, runPluginsCli } from "../plugins/run.js";
|
|
15
14
|
//#region src/commands/plugins.ts
|
|
16
15
|
var plugins_exports = /* @__PURE__ */ __exportAll({
|
|
17
16
|
aliases: () => aliases,
|
|
@@ -54,7 +53,7 @@ const builder = (argv) => argv.usage("$0 plugins [options]").options({
|
|
|
54
53
|
describe: "Coding agent to install into (repeatable). Skips the agent picker. Values listed below",
|
|
55
54
|
coerce: coerceAgents
|
|
56
55
|
}
|
|
57
|
-
}).example("$0 plugins", "Interactive: agents, then
|
|
56
|
+
}).example("$0 plugins", "Interactive: pick agents, then install").example("$0 plugins -y", "Detected agents (project folders, else the host CLI agent), skip prompts").example("$0 plugins --agent cursor --agent claude-code", "Install into specific agents").example("$0 plugins --global", "Install user-level").epilogue(helpEpilogue(helpCsv("Supported agents at project scope", pluginProjectAgents), helpCsv("Also with --global", pluginGlobalOnlyAgents), `Currently one plugin: ${NEON_PLUGIN_NAME} from ${PLUGIN_SOURCE}.`, `It includes the Neon MCP server (${NEON_MCP_URL})`, helpCsv("and these skills", PLUGIN_SKILLS))).strict().check(noPassthrough("plugins"));
|
|
58
57
|
const handler = async (props) => {
|
|
59
58
|
const cwd = process.cwd();
|
|
60
59
|
const yes = props.yes === true;
|
|
@@ -68,15 +67,6 @@ const handler = async (props) => {
|
|
|
68
67
|
});
|
|
69
68
|
for (const agent of plan.skipped) log.warning("Skipping %s: no plugins mapping.", getAgentDisplayName(agent));
|
|
70
69
|
for (const agent of plan.userScopeSkipped) log.warning("Skipping %s: plugins are user-level. Pass --global.", getAgentDisplayName(agent));
|
|
71
|
-
if (interactive) {
|
|
72
|
-
if (!await confirmPluginsInstall({
|
|
73
|
-
scope: plan.scope,
|
|
74
|
-
agents: plan.agents
|
|
75
|
-
})) {
|
|
76
|
-
log.info("Aborted. Nothing was written.");
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
70
|
const rows = [];
|
|
81
71
|
const failed = [];
|
|
82
72
|
const scope = scopeLabel(plan.scope);
|
package/dist/commands/skills.js
CHANGED
|
@@ -9,7 +9,7 @@ import "../init/agents.js";
|
|
|
9
9
|
import { mappedSkillsAgentNames, skillsInstallableAgents } from "../skills/targets.js";
|
|
10
10
|
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
11
11
|
import { skillsHelpValues, skillsYesHelp } from "../skills/catalog.js";
|
|
12
|
-
import {
|
|
12
|
+
import { confirmSkillsUpdate } from "../skills/wizard.js";
|
|
13
13
|
import { assertSkillsCanRun, resolveSkillsPlan } from "../skills/plan.js";
|
|
14
14
|
import { assertSkillsNode, neonSkillsRetryCommand, neonSkillsUpdateRetryCommand, runSkillsCli, skillsAddArgs, skillsMetadata, skillsUpdateArgs, skillsUpdateDetail, skillsUpdateHadNothing } from "../skills/run.js";
|
|
15
15
|
//#region src/commands/skills.ts
|
|
@@ -82,7 +82,7 @@ const builder = (argv) => argv.usage("$0 skills [command] [options]").command("u
|
|
|
82
82
|
describe: "Skill to install (repeatable). Skips the skill picker. Does not select agents. Values listed below",
|
|
83
83
|
coerce: coerceSkills
|
|
84
84
|
}
|
|
85
|
-
}).example("$0 skills", "Interactive: this directory, agents,
|
|
85
|
+
}).example("$0 skills", "Interactive: this directory, then agents, then skills").example("$0 skills -y", "This directory, detected agents (project folders, else the host CLI agent), the default skills").example("$0 skills -y -s neon -s neon-ai-gateway", "Named skills into detected agents").example("$0 skills -s neon -s neon-ai-gateway --agent cursor", "Named skills into a named agent").example("$0 skills --global", "Install user-level skills").epilogue(helpEpilogue(helpCsv("Supported agents", skillsInstallableAgents()), "Supported skills, by source repo:", skillsHelpValues(), skillsYesHelp())).strict().check(noPassthrough("skills"));
|
|
86
86
|
const handler = async (props) => {
|
|
87
87
|
assertSkillsNode();
|
|
88
88
|
const cwd = process.cwd();
|
|
@@ -98,16 +98,6 @@ const handler = async (props) => {
|
|
|
98
98
|
});
|
|
99
99
|
const mapped = mappedSkillsAgentNames(plan.agents);
|
|
100
100
|
for (const agent of plan.skipped) log.warning("Skipping %s: no skills mapping.", getAgentDisplayName(agent));
|
|
101
|
-
if (interactive) {
|
|
102
|
-
if (!await confirmSkillsInstall({
|
|
103
|
-
scope: plan.scope,
|
|
104
|
-
agents: plan.agents,
|
|
105
|
-
invocations: plan.invocations
|
|
106
|
-
})) {
|
|
107
|
-
log.info("Aborted. Nothing was written.");
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
101
|
const metadata = skillsMetadata("skills");
|
|
112
102
|
const rows = [];
|
|
113
103
|
const failed = [];
|
package/dist/index.js
CHANGED
|
@@ -8,12 +8,13 @@ import { currentContextFile, enrichFromContext } from "./context.js";
|
|
|
8
8
|
import { analyticsMiddleware, closeAnalytics, commandSuccessProperties, initAnalyticsClientMiddleware, sendError, takeCommandSuccessExtras, trackEvent } from "./analytics.js";
|
|
9
9
|
import { NETWORK_ERROR_MESSAGE, isNetworkError, matchErrorCode } from "./errors.js";
|
|
10
10
|
import { AuthRefreshError, defaultClientID } from "./auth.js";
|
|
11
|
+
import { getCliName } from "./utils/cli_name.js";
|
|
11
12
|
import { ensureAuth } from "./commands/auth.js";
|
|
12
13
|
import { recoverFrom401 } from "./auth_recovery.js";
|
|
13
|
-
import { getCliName } from "./utils/cli_name.js";
|
|
14
14
|
import { showHelp } from "./help.js";
|
|
15
15
|
import { rewriteUnknownAgentArg } from "./init/plan.js";
|
|
16
16
|
import commands_default from "./commands/index.js";
|
|
17
|
+
import { notifyIfUpdateAvailable } from "./update_notifier.js";
|
|
17
18
|
import { fillInArgs, resolveApiKeyFromEnv } from "./utils/middlewares.js";
|
|
18
19
|
import yargs from "yargs";
|
|
19
20
|
import { hideBin } from "yargs/helpers";
|
|
@@ -193,6 +194,13 @@ async function handleError(msg, err, canRetry) {
|
|
|
193
194
|
process.exit(0);
|
|
194
195
|
}
|
|
195
196
|
await closeAnalytics();
|
|
197
|
+
notifyIfUpdateAvailable({
|
|
198
|
+
commandPath: args._.map(String),
|
|
199
|
+
configDir: typeof args.configDir === "string" ? args.configDir : defaultDir,
|
|
200
|
+
currentBranch: Boolean(args.currentBranch),
|
|
201
|
+
currentVersion: pkg_default.version,
|
|
202
|
+
output: typeof args.output === "string" ? args.output : "table"
|
|
203
|
+
});
|
|
196
204
|
break;
|
|
197
205
|
} catch (err) {
|
|
198
206
|
takeCommandSuccessExtras();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/init/cancelled.ts
|
|
2
|
+
var InitCancelled = class extends Error {
|
|
3
|
+
constructor() {
|
|
4
|
+
super("Aborted.");
|
|
5
|
+
this.name = "InitCancelled";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
const restoreCursor = () => {
|
|
9
|
+
process.stdout.write("\x1B[?25h");
|
|
10
|
+
process.stdout.write("\n");
|
|
11
|
+
};
|
|
12
|
+
const restoreCursorVisibilityOnAbort = (state) => {
|
|
13
|
+
if (state.aborted) process.stdout.write("\x1B[?25h");
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { InitCancelled, restoreCursor, restoreCursorVisibilityOnAbort };
|
package/dist/init/child.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { InitCancelled } from "./cancelled.js";
|
|
1
2
|
import { spawn } from "node:child_process";
|
|
2
3
|
//#region src/init/child.ts
|
|
3
4
|
const AUTH_CHILD = /* @__PURE__ */ new Set([
|
|
@@ -19,18 +20,29 @@ const initChildEnv = (command, overlay, base = process.env) => {
|
|
|
19
20
|
const spawnCliChild = async (argv, cwd, overlay) => {
|
|
20
21
|
const cli = process.argv[1];
|
|
21
22
|
if (!cli) throw new Error("Cannot re-exec the Neon CLI: process.argv[1] is missing.");
|
|
22
|
-
return new Promise((resolve) => {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
23
24
|
const child = spawn(process.execPath, [cli, ...argv], {
|
|
24
25
|
cwd,
|
|
25
26
|
stdio: "inherit",
|
|
26
27
|
env: initChildEnv(argv[0], overlay)
|
|
27
28
|
});
|
|
29
|
+
const onSigint = () => {
|
|
30
|
+
child.kill("SIGINT");
|
|
31
|
+
};
|
|
32
|
+
process.on("SIGINT", onSigint);
|
|
33
|
+
const finish = (code, signal) => {
|
|
34
|
+
process.off("SIGINT", onSigint);
|
|
35
|
+
if (signal === "SIGINT" || signal === "SIGTERM") {
|
|
36
|
+
reject(new InitCancelled());
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
resolve(code === 0);
|
|
40
|
+
};
|
|
28
41
|
child.on("error", () => {
|
|
42
|
+
process.off("SIGINT", onSigint);
|
|
29
43
|
resolve(false);
|
|
30
44
|
});
|
|
31
|
-
child.on("close",
|
|
32
|
-
resolve(code === 0);
|
|
33
|
-
});
|
|
45
|
+
child.on("close", finish);
|
|
34
46
|
});
|
|
35
47
|
};
|
|
36
48
|
//#endregion
|
package/dist/init/choices.js
CHANGED
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
import { getCliName } from "../utils/cli_name.js";
|
|
2
1
|
//#region src/init/choices.ts
|
|
3
|
-
const INIT_TEMPLATE_CONFLICT = "--skip-template cannot be combined with --template.";
|
|
4
2
|
const INIT_CONFIG_SERVICES_CONFLICT = "--no-config cannot be combined with --services.";
|
|
5
|
-
const INIT_TEMPLATE_KEEPS_CONFIG = "A scaffolded template keeps the neon.ts it ships. --config, --no-config, and --services apply when you skip the template or set up an existing app.";
|
|
6
|
-
const initTemplateInNonEmptyMessage = () => `--template is only for an empty directory. This directory already has files, so there is nothing to scaffold. Omit --template, or run \`${getCliName()} bootstrap\` in an empty folder.`;
|
|
7
|
-
const resolveInitTemplateChoice = (input) => {
|
|
8
|
-
const id = input.template?.trim();
|
|
9
|
-
const hasTemplate = id !== void 0 && id.length > 0;
|
|
10
|
-
if (input.skipTemplate && hasTemplate) throw new Error(INIT_TEMPLATE_CONFLICT);
|
|
11
|
-
if (!input.empty) {
|
|
12
|
-
if (hasTemplate) throw new Error(initTemplateInNonEmptyMessage());
|
|
13
|
-
return { kind: "existing" };
|
|
14
|
-
}
|
|
15
|
-
if (input.skipTemplate) return { kind: "skip" };
|
|
16
|
-
if (hasTemplate) return {
|
|
17
|
-
kind: "template",
|
|
18
|
-
id
|
|
19
|
-
};
|
|
20
|
-
if (input.yes) return { kind: "default" };
|
|
21
|
-
return { kind: "ask" };
|
|
22
|
-
};
|
|
23
3
|
const resolveInitConfigChoice = (input) => {
|
|
24
4
|
if (input.flag === false && input.services !== void 0) throw new Error(INIT_CONFIG_SERVICES_CONFLICT);
|
|
25
5
|
if (input.flag === false) return { kind: "skip" };
|
|
@@ -41,6 +21,15 @@ const configPlanFromResolution = (resolution, accepted) => {
|
|
|
41
21
|
if (resolution.kind === "skip") return { kind: "skip" };
|
|
42
22
|
return resolution;
|
|
43
23
|
};
|
|
24
|
+
const isBareInitServices = (services) => {
|
|
25
|
+
if (services === void 0 || services.length === 0) return true;
|
|
26
|
+
return services.every((service) => service === "none");
|
|
27
|
+
};
|
|
44
28
|
const shouldRefreshEnvAfterNewConfig = (input) => input.wroteNewFile && typeof input.projectId === "string" && input.projectId.length > 0 && typeof input.branch === "string" && input.branch.length > 0;
|
|
29
|
+
const shouldPullEnvAfterInitConfig = (input) => !input.extraServices && !input.alreadyPulled && shouldRefreshEnvAfterNewConfig({
|
|
30
|
+
wroteNewFile: input.wroteNewFile,
|
|
31
|
+
...input.projectId !== void 0 ? { projectId: input.projectId } : {},
|
|
32
|
+
...input.branch !== void 0 ? { branch: input.branch } : {}
|
|
33
|
+
});
|
|
45
34
|
//#endregion
|
|
46
|
-
export { INIT_CONFIG_SERVICES_CONFLICT,
|
|
35
|
+
export { INIT_CONFIG_SERVICES_CONFLICT, configPlanFromResolution, isBareInitServices, resolveInitConfigChoice, shouldPullEnvAfterInitConfig, shouldRefreshEnvAfterNewConfig };
|