neon 6.0.0 → 6.2.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,84 @@
1
+ import { isNeonApiError } from "../api.js";
2
+ import { r as defaultDir } from "../_chunks/paths-DMq0Lt7a.js";
3
+ import "../config.js";
4
+ import "../auth.js";
5
+ import { ensureAuth } from "../commands/auth.js";
6
+ import { recoverFrom401 } from "../auth_recovery.js";
7
+ import { pull } from "../commands/env.js";
8
+ import { setupNeonMcp } from "../commands/mcp.js";
9
+ //#region src/init/auth.ts
10
+ const isUnauthorized = (error) => isNeonApiError(error) && error.status === 401;
11
+ const authenticationFailed = () => /* @__PURE__ */ new Error("Authentication failed.");
12
+ /**
13
+ * Fills in the fields `ensureAuth` requires beyond `InitAuthOptions`, and sets `_` so
14
+ * `ensureAuth`'s per-command branches (`isMcpCommand`, `isSkillsCommand`, ...) treat this
15
+ * exactly like the standalone command it names — mirrors `init/link.ts`'s `runAuthenticatedLink`.
16
+ */
17
+ const authContext = (options, command) => ({
18
+ ...options,
19
+ _: [...command],
20
+ configDir: options.configDir ?? defaultDir,
21
+ oauthHost: options.oauthHost ?? process.env.NEON_OAUTH_HOST ?? "https://oauth2.neon.tech",
22
+ clientId: options.clientId ?? "neonctl",
23
+ help: false
24
+ });
25
+ /**
26
+ * Resolves Neon auth the same way a standalone `neon mcp` invocation would, then runs
27
+ * `setupNeonMcp`. `init`/`bootstrap` never resolve a shared `apiClient` of their own for
28
+ * this (see `InitAuthOptions` above), so this is what does it, per call.
29
+ *
30
+ * Skips `ensureAuth` entirely when `options.oauth` is true: `setupNeonMcp`'s oauth branch
31
+ * never reads `apiClient`/`apiKey`, and `ensureAuth`'s own `isMcpOauth` skip reads the real
32
+ * `process.argv` for `--oauth` (documented there as required because that middleware runs
33
+ * before flag parsing) — which never contains it for a `neon init`/`bootstrap` process, so
34
+ * `ensureAuth` would otherwise try to resolve credentials it doesn't need and can fail on
35
+ * an unrelated bad profile or expired session.
36
+ */
37
+ const runAuthenticatedMcp = async (options) => {
38
+ if (options.oauth === true) return setupNeonMcp(options);
39
+ const authenticated = authContext(options, ["mcp"]);
40
+ await ensureAuth(authenticated);
41
+ try {
42
+ return await setupNeonMcp(authenticated);
43
+ } catch (error) {
44
+ if (!isUnauthorized(error)) throw error;
45
+ if (!await recoverFrom401(true)) throw authenticationFailed();
46
+ }
47
+ await ensureAuth(authenticated);
48
+ try {
49
+ return await setupNeonMcp(authenticated);
50
+ } catch (error) {
51
+ if (!isUnauthorized(error)) throw error;
52
+ await recoverFrom401(false);
53
+ throw authenticationFailed();
54
+ }
55
+ };
56
+ /**
57
+ * Resolves Neon auth the same way a standalone `neon env pull` invocation would, then runs
58
+ * `pull` — the same function `link`/`checkout` already call directly via
59
+ * `autoPullEnvAfterPin` (see `commands/env.ts`).
60
+ */
61
+ const pullInitEnv = async (props) => {
62
+ const authenticated = authContext(props, ["env", "pull"]);
63
+ await ensureAuth(authenticated);
64
+ const opts = {
65
+ announce: true,
66
+ implyAiGateway: true
67
+ };
68
+ try {
69
+ return await pull(authenticated, opts);
70
+ } catch (error) {
71
+ if (!isUnauthorized(error)) throw error;
72
+ if (!await recoverFrom401(true)) throw authenticationFailed();
73
+ }
74
+ await ensureAuth(authenticated);
75
+ try {
76
+ return await pull(authenticated, opts);
77
+ } catch (error) {
78
+ if (!isUnauthorized(error)) throw error;
79
+ await recoverFrom401(false);
80
+ throw authenticationFailed();
81
+ }
82
+ };
83
+ //#endregion
84
+ export { pullInitEnv, runAuthenticatedMcp };
@@ -5,6 +5,31 @@ var InitCancelled = class extends Error {
5
5
  this.name = "InitCancelled";
6
6
  }
7
7
  };
8
+ /**
9
+ * Rejects with {@link InitCancelled} on SIGINT while `work` is pending, so a step that runs
10
+ * fully in-process (no spawned child of its own to die and report the signal back) still
11
+ * unwinds through `runInit`'s normal cancellation handling — the "Neon setup cancelled."
12
+ * summary and the funnel's final telemetry — instead of Node's default SIGINT disposition
13
+ * killing the process with no chance to run either.
14
+ *
15
+ * Does not cancel `work` itself: there is no cooperative cancellation to ask for here, so a
16
+ * lost race leaves it running in the background, its eventual settlement discarded. That is
17
+ * fine — the process is already unwinding to exit on the `InitCancelled` path.
18
+ */
19
+ const raceSigint = (work) => new Promise((resolve, reject) => {
20
+ const onSigint = () => reject(new InitCancelled());
21
+ process.once("SIGINT", onSigint);
22
+ const stop = () => {
23
+ process.removeListener("SIGINT", onSigint);
24
+ };
25
+ work.then((value) => {
26
+ stop();
27
+ resolve(value);
28
+ }, (error) => {
29
+ stop();
30
+ reject(error);
31
+ });
32
+ });
8
33
  const restoreCursor = () => {
9
34
  process.stdout.write("\x1B[?25h");
10
35
  process.stdout.write("\n");
@@ -13,4 +38,4 @@ const restoreCursorVisibilityOnAbort = (state) => {
13
38
  if (state.aborted) process.stdout.write("\x1B[?25h");
14
39
  };
15
40
  //#endregion
16
- export { InitCancelled, restoreCursor, restoreCursorVisibilityOnAbort };
41
+ export { InitCancelled, raceSigint, restoreCursor, restoreCursorVisibilityOnAbort };
@@ -1,5 +1,5 @@
1
1
  import { isCi } from "../env.js";
2
- import { HEADING_CANCELLED, HEADING_COMPLETE, HEADING_FAILED, HEADING_PENDING, INIT_SUBTITLE, PROGRESS } from "./copy.js";
2
+ import { HEADING_CANCELLED, HEADING_COMPLETE, HEADING_FAILED, HEADING_PENDING, INIT_SUBTITLE } from "./copy.js";
3
3
  import chalk from "chalk";
4
4
  //#region src/init/chrome.ts
5
5
  const NEON_GREEN = "#4BB578";
@@ -89,23 +89,6 @@ const configRowValue = (config, filename) => {
89
89
  if (config === "skipped" || config === null) return "skipped";
90
90
  return config;
91
91
  };
92
- const INIT_STEP_LABELS = {
93
- bootstrap: "Creating the app from the selected template...",
94
- plugins: PROGRESS.plugins,
95
- skills: PROGRESS.skills,
96
- mcp: PROGRESS.mcp,
97
- auth: PROGRESS.auth,
98
- link: PROGRESS.link,
99
- claim: PROGRESS.claim,
100
- config: PROGRESS.config,
101
- env: PROGRESS.env
102
- };
103
- const initStepLabel = (step) => {
104
- const command = step[0];
105
- if (command === void 0) return;
106
- if (command === "config") return INIT_STEP_LABELS.config;
107
- return INIT_STEP_LABELS[command];
108
- };
109
92
  const headingForKind = (kind) => {
110
93
  switch (kind) {
111
94
  case "success": return HEADING_COMPLETE;
@@ -116,4 +99,4 @@ const headingForKind = (kind) => {
116
99
  }
117
100
  };
118
101
  //#endregion
119
- export { INIT_BANNER_LINES, INIT_STEP_LABELS, NEON_GREEN, agentSetupDoneLabel, agentSetupLabel, agentsRowValue, configRowValue, configSummaryLabel, formatInitBanner, formatInitDone, headingForKind, initStepLabel, printInitBanner, printInitDone, printInitProgress, projectRowValue, shouldPrintInitBanner };
102
+ export { INIT_BANNER_LINES, NEON_GREEN, agentSetupDoneLabel, agentSetupLabel, agentsRowValue, configRowValue, configSummaryLabel, formatInitBanner, formatInitDone, headingForKind, printInitBanner, printInitDone, printInitProgress, projectRowValue, shouldPrintInitBanner };
@@ -5,8 +5,8 @@ import { i as selectCredential, t as credentialInputs } from "../_chunks/auth_se
5
5
  import { detectInstalledAgents, uniqueAgentIds } from "../mcp/agents.js";
6
6
  import "./agents.js";
7
7
  import { directoryIsEmpty } from "./plan.js";
8
- import { canPickAgentsInteractively } from "../utils/agent_picker.js";
9
8
  import { detectAgent } from "./detect_host.js";
9
+ import { canPickAgentsInteractively } from "../utils/agent_picker.js";
10
10
  import { existsSync, readdirSync } from "node:fs";
11
11
  import { detectProjectAgents } from "add-mcp";
12
12
  //#region src/init/detect.ts
package/dist/init/plan.js CHANGED
@@ -36,21 +36,47 @@ const postScaffoldActions = (input) => {
36
36
  return actions;
37
37
  };
38
38
  const planAgentSteps = (input) => {
39
- const y = input.yes ? ["-y"] : [];
40
- if (input.agentSetup === "plugin") return [["plugins", ...y]];
41
- if (input.agentSetup === "skills-mcp") return [["skills", ...y], ["mcp", ...y]];
39
+ if (input.agentSetup === "plugin") return [{
40
+ kind: "plugins",
41
+ options: { yes: input.yes }
42
+ }];
43
+ if (input.agentSetup === "skills-mcp") return [{
44
+ kind: "skills",
45
+ options: { yes: input.yes }
46
+ }, {
47
+ kind: "mcp",
48
+ options: { yes: input.yes }
49
+ }];
42
50
  return [];
43
51
  };
44
52
  const planToolingSteps = (tooling, options) => {
45
- const prefix = options.yes ? ["-y"] : [];
46
- const flagsFor = (ids) => options.named ? [...prefix, ...agentArgv(ids)] : [...prefix];
53
+ const agentsFor = (ids) => options.named ? { agents: ids } : {};
54
+ const agentFor = (ids) => options.named ? { agent: [...ids] } : {};
47
55
  switch (tooling.setup) {
48
56
  case "skip": return [];
49
- case "plugin": return [["plugins", ...flagsFor(tooling.agents)]];
57
+ case "plugin": return [{
58
+ kind: "plugins",
59
+ options: {
60
+ yes: options.yes,
61
+ ...agentsFor(tooling.agents)
62
+ }
63
+ }];
50
64
  case "skills-mcp": {
51
65
  const steps = [];
52
- if (tooling.skillsAgents.length > 0) steps.push(["skills", ...flagsFor(tooling.skillsAgents)]);
53
- if (tooling.mcpAgents.length > 0) steps.push(["mcp", ...flagsFor(tooling.mcpAgents)]);
66
+ if (tooling.skillsAgents.length > 0) steps.push({
67
+ kind: "skills",
68
+ options: {
69
+ yes: options.yes,
70
+ ...agentsFor(tooling.skillsAgents)
71
+ }
72
+ });
73
+ if (tooling.mcpAgents.length > 0) steps.push({
74
+ kind: "mcp",
75
+ options: {
76
+ yes: options.yes,
77
+ ...agentFor(tooling.mcpAgents)
78
+ }
79
+ });
54
80
  return steps;
55
81
  }
56
82
  default: return tooling;
@@ -209,46 +235,63 @@ const recommendedTooling = (detected) => {
209
235
  return split;
210
236
  };
211
237
  const planInitToolingSteps = (input) => {
212
- const y = input.yes ? ["-y"] : [];
213
- const pluginPrefix = [
214
- "plugins",
215
- ...input.pluginScope === "global" ? ["--global"] : [],
216
- ...y
217
- ];
218
- const skillFlags = (input.skills ?? []).flatMap((skill) => ["--skill", skill]);
219
- const skillsPrefix = [
220
- "skills",
221
- ...input.skillsGlobal ? ["--global"] : [],
222
- ...y,
223
- ...skillFlags
224
- ];
225
- const mcpPrefix = [
226
- "mcp",
227
- ...y,
228
- ...input.mcpOauth ? ["--oauth"] : [],
229
- ...input.mcpProject === true ? ["--project"] : [],
230
- ...input.mcpProjectId !== void 0 ? ["--project-id", input.mcpProjectId] : []
231
- ];
232
- const named = (ids) => agentArgv(ids);
238
+ const pluginOptions = (agents) => ({
239
+ yes: input.yes,
240
+ global: input.pluginScope === "global",
241
+ agents
242
+ });
243
+ const skillsOptions = (agents) => ({
244
+ yes: input.yes,
245
+ global: input.skillsGlobal,
246
+ agents,
247
+ ...input.skills !== void 0 ? { skills: input.skills } : {}
248
+ });
249
+ const mcpOptions = (agents) => ({
250
+ yes: input.yes,
251
+ oauth: input.mcpOauth,
252
+ project: input.mcpProject === true,
253
+ agent: [...agents],
254
+ ...input.mcpProjectId !== void 0 ? { projectId: input.mcpProjectId } : {}
255
+ });
233
256
  switch (input.tooling.setup) {
234
257
  case "skip": return [];
235
- case "plugin": return [[...pluginPrefix, ...named(input.tooling.agents)]];
236
- case "skills": return [[
237
- "skills",
238
- ...y,
239
- ...skillFlags,
240
- ...named(input.tooling.agents)
241
- ]];
258
+ case "plugin": return [{
259
+ kind: "plugins",
260
+ options: pluginOptions(input.tooling.agents)
261
+ }];
262
+ case "skills": return [{
263
+ kind: "skills",
264
+ options: {
265
+ yes: input.yes,
266
+ agents: input.tooling.agents,
267
+ ...input.skills !== void 0 ? { skills: input.skills } : {}
268
+ }
269
+ }];
242
270
  case "skills-mcp": {
243
271
  const steps = [];
244
- if (input.tooling.skillsAgents.length > 0) steps.push([...skillsPrefix, ...named(input.tooling.skillsAgents)]);
245
- if (input.tooling.mcpAgents.length > 0) steps.push([...mcpPrefix, ...named(input.tooling.mcpAgents)]);
272
+ if (input.tooling.skillsAgents.length > 0) steps.push({
273
+ kind: "skills",
274
+ options: skillsOptions(input.tooling.skillsAgents)
275
+ });
276
+ if (input.tooling.mcpAgents.length > 0) steps.push({
277
+ kind: "mcp",
278
+ options: mcpOptions(input.tooling.mcpAgents)
279
+ });
246
280
  return steps;
247
281
  }
248
282
  case "mixed": {
249
- const steps = [[...pluginPrefix, ...named(input.tooling.pluginAgents)]];
250
- if (input.tooling.skillsAgents.length > 0) steps.push([...skillsPrefix, ...named(input.tooling.skillsAgents)]);
251
- if (input.tooling.mcpAgents.length > 0) steps.push([...mcpPrefix, ...named(input.tooling.mcpAgents)]);
283
+ const steps = [{
284
+ kind: "plugins",
285
+ options: pluginOptions(input.tooling.pluginAgents)
286
+ }];
287
+ if (input.tooling.skillsAgents.length > 0) steps.push({
288
+ kind: "skills",
289
+ options: skillsOptions(input.tooling.skillsAgents)
290
+ });
291
+ if (input.tooling.mcpAgents.length > 0) steps.push({
292
+ kind: "mcp",
293
+ options: mcpOptions(input.tooling.mcpAgents)
294
+ });
252
295
  return steps;
253
296
  }
254
297
  default: return input.tooling;
@@ -333,30 +376,9 @@ const linkInputArgv = (input) => {
333
376
  if (input.branch) args.push("--branch", input.branch);
334
377
  return args;
335
378
  };
336
- const planExistingInit = (input) => {
337
- const steps = [...planAgentSteps(input)];
338
- if (!input.linked) steps.push(planLinkStep({
339
- yes: input.yes,
340
- extra: input.linkExtra
341
- }));
342
- if (input.config.kind === "write") steps.push(planConfigInitStep({
343
- yes: input.yes,
344
- services: input.config.services
345
- }));
346
- return steps;
347
- };
348
379
  const resolveInitAgentSetup = async (input) => {
349
380
  if (input.interactive) return input.pick();
350
381
  throw new Error(INIT_NEEDS_YES_OR_TERMINAL);
351
382
  };
352
- const childArgv = (step, forward) => {
353
- const args = [...step];
354
- if (forward.configDir) args.push("--config-dir", forward.configDir);
355
- if (forward.profile) args.push("--profile", forward.profile);
356
- args.push("--api-host", forward.apiHost);
357
- args.push("--context-file", forward.contextFile);
358
- if (forward.analytics === false) args.push("--no-analytics");
359
- return args;
360
- };
361
383
  //#endregion
362
- export { FALLBACK_SKILLS_AGENTS, INIT_NEEDS_YES_OR_TERMINAL, NAMED_AGENTS_MIXED, NAMED_AGENTS_UNSUPPORTED, assertNamedAgentTooling, bootstrapInitStep, childArgv, chooseYesAgentTooling, collectYesAgents, directoryIsEmpty, funnelAgentSetup, initPluginAgents, initSkillsMcpAgents, initYesSupportedAgents, linkInputArgv, namedAgentsMixedMessage, namedAgentsNeedSplit, noDetectedAgentsMessage, planAgentSteps, planConfigInitStep, planExistingInit, planInitToolingSteps, planLinkStep, planToolingSteps, planYesAgentSteps, postScaffoldActions, projectContextFile, recommendedTooling, resolveInitAgentSetup, resolveNamedAgents, resolveYesAgentList, rewriteUnknownAgentArg, splitInitTooling };
384
+ export { FALLBACK_SKILLS_AGENTS, INIT_NEEDS_YES_OR_TERMINAL, NAMED_AGENTS_MIXED, NAMED_AGENTS_UNSUPPORTED, assertNamedAgentTooling, bootstrapInitStep, chooseYesAgentTooling, collectYesAgents, directoryIsEmpty, funnelAgentSetup, initPluginAgents, initSkillsMcpAgents, initYesSupportedAgents, linkInputArgv, namedAgentsMixedMessage, namedAgentsNeedSplit, noDetectedAgentsMessage, planAgentSteps, planConfigInitStep, planInitToolingSteps, planLinkStep, planToolingSteps, planYesAgentSteps, postScaffoldActions, projectContextFile, recommendedTooling, resolveInitAgentSetup, resolveNamedAgents, resolveYesAgentList, rewriteUnknownAgentArg, splitInitTooling };
package/dist/init/run.js CHANGED
@@ -3,14 +3,11 @@ import { r as defaultDir } from "../_chunks/paths-DMq0Lt7a.js";
3
3
  import "../config.js";
4
4
  import { contextBranch, readContextFile } from "../context.js";
5
5
  import { recordCommandSuccessExtras, takeCommandSuccessExtras } from "../analytics.js";
6
- import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
7
6
  import "../claimable/api.js";
8
7
  import { readLinkedClaimableCredentials } from "../claimable/state.js";
9
8
  import { getCliName } from "../utils/cli_name.js";
10
9
  import { deprecatedServiceMessage, parseServices, servicesFlagValue } from "../neon_services.js";
11
10
  import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES, CONFIG_INIT_UNAVAILABLE } from "../config_template.js";
12
- import { InitCancelled, restoreCursor } from "./cancelled.js";
13
- import { initChildEnv, spawnCliChild } from "./child.js";
14
11
  import { CANCELLED_BODY, CLAIMABLE_ACCOUNT_FLAGS, CLAIMABLE_ALREADY_LINKED, CLAIMABLE_MCP_API_KEY, CLAIMABLE_NO_LINK, MCP_SCOPED_NEEDS_PROJECT, NON_TTY_LINK_NEEDS_AUTH, NO_AGENTS_FALLBACK_STATUS, PROGRESS, YES_LINK_NEEDS_AUTH, claimableNext, claimableThenServicesNext, envPullFailedNext, existingConfigNext, extraServicesNext, installFailedNext, mcpConfigLocationSkipped, mcpConfigLocationUnavailable, namedAgentsUnavailable, skippedLinkNext, unattendedUnauthedNext } from "./copy.js";
15
12
  import { agentsRowValue, configRowValue, formatInitDone, headingForKind, printInitBanner, printInitDone, printInitProgress, projectRowValue, shouldPrintInitBanner } from "./chrome.js";
16
13
  import { inferPackageManager, resolvePackageManager } from "../utils/package_manager.js";
@@ -20,8 +17,10 @@ import { mcpInstallableAgents } from "../mcp/targets.js";
20
17
  import { pluginsInstallableAgents } from "../plugins/targets.js";
21
18
  import { skillsInstallableAgents } from "../skills/targets.js";
22
19
  import { FALLBACK_SKILLS_AGENTS, assertNamedAgentTooling, funnelAgentSetup, planInitToolingSteps, recommendedTooling, resolveNamedAgents, splitInitTooling } from "./plan.js";
20
+ import { pullInitEnv } from "./auth.js";
21
+ import { InitCancelled, raceSigint, restoreCursor } from "./cancelled.js";
23
22
  import { pickAgentSetupInteractively, pickInitAgentsInteractively, pickInitConfigInteractively, pickInitMcpAuthInteractively, pickInitMcpConfigLocationInteractively, pickInitModeInteractively, pickInitPackageManagerInteractively, pickInitProjectSetupInteractively, pickInitServicesInteractively, pickInitSkillsInteractively } from "./wizard.js";
24
- import { runInitSteps } from "./tooling.js";
23
+ import { runToolingSteps } from "./tooling.js";
25
24
  import { create } from "../commands/claim.js";
26
25
  import { INIT_CONFIG_SERVICES_CONFLICT, configPlanFromResolution, isBareInitServices, resolveInitConfigChoice, shouldPullEnvAfterInitConfig } from "./choices.js";
27
26
  import { detectInitEnvironment } from "./detect.js";
@@ -153,16 +152,18 @@ const runInit = async (props) => {
153
152
  ...props.mcpAuth !== void 0 ? { mcpAuth: props.mcpAuth } : {},
154
153
  ...props.mcpProjectScoped === true ? { mcpProjectScoped: true } : {}
155
154
  });
156
- const run = props.run ?? spawnCliChild;
157
- const explicitKey = props.profile ? "" : credentialInputs().apiKeyFlag;
158
- const authEnv = explicitKey ? { NEON_API_KEY: explicitKey } : void 0;
159
155
  const configDir = props.configDir ?? defaultDir;
160
- const forward = {
161
- ...props.configDir ? { configDir: props.configDir } : {},
162
- ...props.profile ? { profile: props.profile } : {},
156
+ const auth = {
157
+ apiClient: props.apiClient,
158
+ apiKey: props.apiKey,
163
159
  apiHost: props.apiHost,
164
160
  contextFile,
165
- ...props.analytics === false ? { analytics: false } : {}
161
+ ...props.configDir ? { configDir: props.configDir } : {},
162
+ ...props.profile ? { profile: props.profile } : {},
163
+ ...props.oauthHost ? { oauthHost: props.oauthHost } : {},
164
+ ...props.clientId ? { clientId: props.clientId } : {},
165
+ ...props.forceAuth !== void 0 ? { forceAuth: props.forceAuth } : {},
166
+ ...props.allowUnsafeTls !== void 0 ? { allowUnsafeTls: props.allowUnsafeTls } : {}
166
167
  };
167
168
  const servicesFlag = parsedInitServices(props.services);
168
169
  if (props.config === false && servicesFlag !== void 0) throw new Error(INIT_CONFIG_SERVICES_CONFLICT);
@@ -320,11 +321,11 @@ const runInit = async (props) => {
320
321
  ...selectedSkills !== void 0 ? { skills: selectedSkills } : {}
321
322
  });
322
323
  if (toolingSteps.length > 0) {
323
- await runInitSteps(toolingSteps, {
324
+ await runToolingSteps(toolingSteps, {
324
325
  cwd,
325
- run,
326
- forward,
327
- authEnv,
326
+ output: props.output,
327
+ auth,
328
+ operations: props.operations,
328
329
  narrate: "human"
329
330
  });
330
331
  funnel.agentsInstalled = agentsFromTooling(earlyTooling);
@@ -420,11 +421,11 @@ const runInit = async (props) => {
420
421
  ...pinId !== void 0 ? { mcpProjectId: pinId } : {}
421
422
  });
422
423
  if (mcpSteps.length > 0) {
423
- await runInitSteps(mcpSteps, {
424
+ await runToolingSteps(mcpSteps, {
424
425
  cwd,
425
- run,
426
- forward,
427
- authEnv,
426
+ output: props.output,
427
+ auth,
428
+ operations: props.operations,
428
429
  narrate: "human"
429
430
  });
430
431
  funnel.agentsInstalled = uniqueAgents([...funnel.agentsInstalled, ...agentsFromTooling(mcpOnly)]);
@@ -509,14 +510,17 @@ const runInit = async (props) => {
509
510
  ...typeof context.projectId === "string" ? { projectId: context.projectId } : {},
510
511
  ...branch !== void 0 ? { branch } : {}
511
512
  })) try {
512
- await runInitSteps([["env", "pull"]], {
513
+ if (typeof context.projectId !== "string" || typeof branch !== "string") throw new Error("Env pull requires a linked project and branch.");
514
+ printInitProgress(PROGRESS.env);
515
+ await raceSigint((props.envPull ?? pullInitEnv)({
516
+ ...auth,
517
+ output: props.output,
513
518
  cwd,
514
- run,
515
- forward,
516
- authEnv,
517
- narrate: "human"
518
- });
519
+ projectId: context.projectId,
520
+ branch
521
+ }));
519
522
  } catch (error) {
523
+ if (error instanceof InitCancelled) throw error;
520
524
  const failed = envPullFailedNext();
521
525
  printInitDone(formatInitDone({
522
526
  heading: failed.heading,
@@ -638,4 +642,4 @@ const agentsFromTooling = (tooling) => {
638
642
  }
639
643
  };
640
644
  //#endregion
641
- export { initChildEnv, runInit };
645
+ export { runInit };
@@ -1,23 +1,109 @@
1
1
  import { log } from "../log.js";
2
2
  import { getCliName } from "../utils/cli_name.js";
3
- import { AUTH_CHILD } from "./child.js";
4
- import { initStepLabel } from "./chrome.js";
5
- import { assertNamedAgentTooling, childArgv, chooseYesAgentTooling, collectYesAgents, initYesSupportedAgents, noDetectedAgentsMessage, planAgentSteps, planToolingSteps, planYesAgentSteps, resolveInitAgentSetup } from "./plan.js";
6
- import { canPickAgentsInteractively } from "../utils/agent_picker.js";
3
+ import { PROGRESS } from "./copy.js";
4
+ import { assertNamedAgentTooling, chooseYesAgentTooling, collectYesAgents, initYesSupportedAgents, noDetectedAgentsMessage, planAgentSteps, planToolingSteps, planYesAgentSteps, resolveInitAgentSetup } from "./plan.js";
7
5
  import { detectAgent } from "./detect_host.js";
6
+ import { canPickAgentsInteractively } from "../utils/agent_picker.js";
7
+ import { mcpInstallError, reportMcpInstall } from "../commands/mcp.js";
8
+ import { installPlugins, pluginsInstallError, reportPluginsInstall } from "../commands/plugins.js";
9
+ import { installSkills, reportSkillsInstall, skillsInstallError } from "../commands/skills.js";
10
+ import { runAuthenticatedMcp } from "./auth.js";
11
+ import { raceSigint } from "./cancelled.js";
8
12
  import { pickAgentSetupInteractively } from "./wizard.js";
9
13
  import { detectProjectAgents } from "add-mcp";
10
14
  //#region src/init/tooling.ts
11
15
  const defaultProjectAgents = (cwd) => detectProjectAgents(cwd);
12
- const runInitSteps = async (steps, options) => {
16
+ const TOOLING_STEP_LABELS = {
17
+ plugins: PROGRESS.plugins,
18
+ skills: PROGRESS.skills,
19
+ mcp: PROGRESS.mcp
20
+ };
21
+ /** Reconstructs a display-only, `neon <command> <flags>` style string for a step. Never executed — only ever logged. */
22
+ const toolingStepCommandText = (step) => {
23
+ const flags = [];
24
+ switch (step.kind) {
25
+ case "plugins": {
26
+ const { options } = step;
27
+ if (options.yes) flags.push("-y");
28
+ if (options.global) flags.push("--global");
29
+ for (const agent of options.agents ?? []) flags.push("--agent", agent);
30
+ break;
31
+ }
32
+ case "skills": {
33
+ const { options } = step;
34
+ if (options.yes) flags.push("-y");
35
+ if (options.global) flags.push("--global");
36
+ for (const skill of options.skills ?? []) flags.push("--skill", skill);
37
+ for (const agent of options.agents ?? []) flags.push("--agent", agent);
38
+ break;
39
+ }
40
+ case "mcp": {
41
+ const { options } = step;
42
+ if (options.yes) flags.push("-y");
43
+ if (options.oauth) flags.push("--oauth");
44
+ if (options.project) flags.push("--project");
45
+ if (options.projectId !== void 0) flags.push("--project-id", options.projectId);
46
+ for (const agent of options.agent ?? []) flags.push("--agent", agent);
47
+ break;
48
+ }
49
+ default: return step;
50
+ }
51
+ return [step.kind, ...flags].join(" ");
52
+ };
53
+ /**
54
+ * Runs plugins/skills/mcp installs in-process, in order, via {@link ToolingOperations}. This
55
+ * replaces re-executing the `neon` binary as a child process (`neon plugins`, `neon skills`,
56
+ * `neon mcp`) to reuse those commands — see `packages/cli/AGENTS.md`.
57
+ */
58
+ const runToolingSteps = async (steps, options) => {
59
+ const ops = {
60
+ installPlugins,
61
+ installSkills,
62
+ installMcp: (stepOptions) => runAuthenticatedMcp({
63
+ ...stepOptions,
64
+ ...options.auth
65
+ }),
66
+ ...options.operations
67
+ };
68
+ const reportProps = { output: options.output };
13
69
  for (const step of steps) {
14
- const label = options.narrate === "human" ? initStepLabel(step) : void 0;
70
+ const label = options.narrate === "human" ? TOOLING_STEP_LABELS[step.kind] : void 0;
15
71
  if (label !== void 0) process.stdout.write(`${label}\n`);
16
- else log.info("Running `%s %s`", getCliName(), step.join(" "));
17
- log.debug("Running `%s %s`", getCliName(), step.join(" "));
18
- const argv = childArgv(step, options.forward);
19
- const command = step[0];
20
- if (!await options.run(argv, options.cwd, command !== void 0 && AUTH_CHILD.has(command) ? options.authEnv : void 0)) throw new Error(`\`${getCliName()} ${step.join(" ")}\` failed.`);
72
+ else log.info("Running `%s %s`", getCliName(), toolingStepCommandText(step));
73
+ log.debug("Running `%s %s`", getCliName(), toolingStepCommandText(step));
74
+ switch (step.kind) {
75
+ case "plugins": {
76
+ const outcome = await raceSigint(ops.installPlugins({
77
+ ...step.options,
78
+ cwd: options.cwd
79
+ }));
80
+ reportPluginsInstall(reportProps, outcome);
81
+ const error = pluginsInstallError(outcome);
82
+ if (error) throw error;
83
+ break;
84
+ }
85
+ case "skills": {
86
+ const outcome = await raceSigint(ops.installSkills({
87
+ ...step.options,
88
+ cwd: options.cwd
89
+ }));
90
+ reportSkillsInstall(reportProps, outcome);
91
+ const error = skillsInstallError(outcome);
92
+ if (error) throw error;
93
+ break;
94
+ }
95
+ case "mcp": {
96
+ const outcome = await raceSigint(ops.installMcp({
97
+ ...step.options,
98
+ cwd: options.cwd
99
+ }));
100
+ reportMcpInstall(reportProps, outcome);
101
+ const error = mcpInstallError(outcome);
102
+ if (error) throw error;
103
+ break;
104
+ }
105
+ default: return step;
106
+ }
21
107
  }
22
108
  };
23
109
  const yesAgentsFromOptions = async (options) => collectYesAgents({
@@ -36,14 +122,14 @@ const runAgentTooling = async (options) => {
36
122
  if (named.length > 0) {
37
123
  assertNamedAgentTooling(named, options.command ?? "init");
38
124
  const tooling = chooseYesAgentTooling(named);
39
- await runInitSteps(planToolingSteps(tooling, {
125
+ await runToolingSteps(planToolingSteps(tooling, {
40
126
  yes,
41
127
  named: true
42
128
  }), options);
43
129
  return tooling.setup;
44
130
  }
45
131
  if (options.agentSetup !== void 0) {
46
- await runInitSteps(planAgentSteps({
132
+ await runToolingSteps(planAgentSteps({
47
133
  yes,
48
134
  agentSetup: options.agentSetup
49
135
  }), options);
@@ -52,7 +138,7 @@ const runAgentTooling = async (options) => {
52
138
  if (yes) {
53
139
  if (options.hasProjectPlugins !== void 0) {
54
140
  const agentSetup = await options.hasProjectPlugins(options.cwd) ? "plugin" : "skills-mcp";
55
- await runInitSteps(planAgentSteps({
141
+ await runToolingSteps(planAgentSteps({
56
142
  yes: true,
57
143
  agentSetup
58
144
  }), options);
@@ -61,7 +147,7 @@ const runAgentTooling = async (options) => {
61
147
  const agents = await yesAgentsFromOptions(options);
62
148
  const tooling = chooseYesAgentTooling(agents);
63
149
  if (tooling.setup === "skip") throw yesMiss();
64
- await runInitSteps(planYesAgentSteps(tooling), options);
150
+ await runToolingSteps(planYesAgentSteps(tooling), options);
65
151
  return tooling.setup;
66
152
  }
67
153
  const interactive = options.pickAgentSetup !== void 0 || canPickAgentsInteractively();
@@ -69,15 +155,11 @@ const runAgentTooling = async (options) => {
69
155
  interactive,
70
156
  pick: options.pickAgentSetup ?? pickAgentSetupInteractively
71
157
  });
72
- await runInitSteps(planAgentSteps({
158
+ await runToolingSteps(planAgentSteps({
73
159
  yes,
74
160
  agentSetup
75
161
  }), options);
76
162
  return agentSetup;
77
163
  };
78
- const runScaffoldFollowUp = async (options) => {
79
- if (!options.skipAgentSetup) await runAgentTooling(options);
80
- if (options.shouldLink) await runInitSteps([options.linkYes ? ["link", "--yes"] : ["link"]], options);
81
- };
82
164
  //#endregion
83
- export { runAgentTooling, runInitSteps, runScaffoldFollowUp };
165
+ export { runAgentTooling, runToolingSteps };
@@ -1,5 +1,4 @@
1
1
  import { CONFIG_INIT_SERVICES } from "../config_template.js";
2
- import { InitCancelled, restoreCursorVisibilityOnAbort } from "./cancelled.js";
3
2
  import { AGENT_DETECTED_DESCRIPTION, AGENT_OTHER_DESCRIPTION, AGENT_PICKER_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, 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 } from "./copy.js";
4
3
  import { installedPackageManagers } from "../utils/package_manager.js";
5
4
  import { POSTGRES_SERVICE_CHOICE, keepPostgresSelected } from "../utils/service_picker.js";
@@ -7,6 +6,7 @@ import { getAgentDisplayName, listMcpAgentIds } from "../mcp/agents.js";
7
6
  import { INIT_NEEDS_YES_OR_TERMINAL } from "./plan.js";
8
7
  import { canPickAgentsInteractively } from "../utils/agent_picker.js";
9
8
  import { NEON_SKILL_CATALOG, listSkillIds } from "../skills/catalog.js";
9
+ import { InitCancelled, restoreCursorVisibilityOnAbort } from "./cancelled.js";
10
10
  import chalk from "chalk";
11
11
  import prompts from "prompts";
12
12
  //#region src/init/wizard.ts