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.
@@ -0,0 +1,641 @@
1
+ import { log } from "../log.js";
2
+ import { r as defaultDir } from "../_chunks/paths-DMq0Lt7a.js";
3
+ import "../config.js";
4
+ import { contextBranch, readContextFile } from "../context.js";
5
+ import { recordCommandSuccessExtras, takeCommandSuccessExtras } from "../analytics.js";
6
+ import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
7
+ import "../claimable/api.js";
8
+ import { readLinkedClaimableCredentials } from "../claimable/state.js";
9
+ import { getCliName } from "../utils/cli_name.js";
10
+ import { deprecatedServiceMessage, parseServices, servicesFlagValue } from "../neon_services.js";
11
+ 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
+ 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
+ import { agentsRowValue, configRowValue, formatInitDone, headingForKind, printInitBanner, printInitDone, printInitProgress, projectRowValue, shouldPrintInitBanner } from "./chrome.js";
16
+ import { inferPackageManager, resolvePackageManager } from "../utils/package_manager.js";
17
+ import { ConfigInstallFailed, hasNeonConfigFile, initCmd, neonConfigFilename } from "../commands/config.js";
18
+ import { runAuthenticatedLink } from "./link.js";
19
+ import { mcpInstallableAgents } from "../mcp/targets.js";
20
+ import { pluginsInstallableAgents } from "../plugins/targets.js";
21
+ import { skillsInstallableAgents } from "../skills/targets.js";
22
+ import { FALLBACK_SKILLS_AGENTS, assertNamedAgentTooling, funnelAgentSetup, planInitToolingSteps, recommendedTooling, resolveNamedAgents, splitInitTooling } from "./plan.js";
23
+ import { pickAgentSetupInteractively, pickInitAgentsInteractively, pickInitConfigInteractively, pickInitMcpAuthInteractively, pickInitMcpConfigLocationInteractively, pickInitModeInteractively, pickInitPackageManagerInteractively, pickInitProjectSetupInteractively, pickInitServicesInteractively, pickInitSkillsInteractively } from "./wizard.js";
24
+ import { runInitSteps } from "./tooling.js";
25
+ import { create } from "../commands/claim.js";
26
+ import { INIT_CONFIG_SERVICES_CONFLICT, configPlanFromResolution, isBareInitServices, resolveInitConfigChoice, shouldPullEnvAfterInitConfig } from "./choices.js";
27
+ import { detectInitEnvironment } from "./detect.js";
28
+ import { createInitFunnel, emitInitStart, finishInitFunnel, initStartProperties } from "./funnel.js";
29
+ import { assertAgentSetupFlags, hasInitMcpFlags, inferInitAgentSetup, resolveInitMode } from "./mode.js";
30
+ import { resolve } from "node:path";
31
+ //#region src/init/run.ts
32
+ const isLinked = (contextFile) => {
33
+ const projectId = readContextFile(contextFile).projectId;
34
+ return typeof projectId === "string" && projectId.length > 0;
35
+ };
36
+ const parsedInitServices = (raw) => {
37
+ const values = servicesFlagValue(raw);
38
+ if (values === void 0) return;
39
+ return parseServices(values, {
40
+ allowed: CONFIG_INIT_SERVICES,
41
+ whyUnavailable: CONFIG_INIT_UNAVAILABLE,
42
+ flag: "--services",
43
+ noneMeans: CONFIG_INIT_NONE_MEANS,
44
+ onDeprecated: (used, canonical) => log.warning(deprecatedServiceMessage(used, canonical))
45
+ });
46
+ };
47
+ const expandTelemetryServices = (services) => {
48
+ if (services.includes("data-api") && !services.includes("auth")) return CONFIG_INIT_SERVICES.filter((service) => service === "auth" || services.includes(service));
49
+ return [...services];
50
+ };
51
+ const extrasSetup = (setup) => {
52
+ if (setup === null) return;
53
+ return setup;
54
+ };
55
+ const pluginScopeFor = (agents, preferred) => {
56
+ if (preferred === "global") return "global";
57
+ const project = new Set(pluginsInstallableAgents("project"));
58
+ return agents.some((id) => !project.has(id) && pluginsInstallableAgents("global").includes(id)) ? "global" : "project";
59
+ };
60
+ const availableForSetup = (setup, scope) => {
61
+ if (setup === "plugin") return pluginsInstallableAgents(scope);
62
+ if (setup === "skip") return [];
63
+ if (setup === "skills") return [...skillsInstallableAgents()];
64
+ return [.../* @__PURE__ */ new Set([...skillsInstallableAgents(), ...mcpInstallableAgents(scope === "project" ? "project" : "global")])];
65
+ };
66
+ const nonEmptyAgents = (ids, fallback) => {
67
+ const [first, ...rest] = ids;
68
+ if (first !== void 0) return [first, ...rest];
69
+ if (fallback) return FALLBACK_SKILLS_AGENTS;
70
+ };
71
+ const skillsTooling = (ids, fallback) => {
72
+ const agents = nonEmptyAgents(ids.filter((id) => skillsInstallableAgents().includes(id)), fallback);
73
+ if (agents === void 0) return { setup: "skip" };
74
+ return {
75
+ setup: "skills",
76
+ agents
77
+ };
78
+ };
79
+ const skillsMcpTooling = (ids, scope, fallback) => {
80
+ const mcpConfigLocation = scope === "project" ? "project" : "global";
81
+ let selected = ids;
82
+ if (selected.length === 0 && fallback) selected = FALLBACK_SKILLS_AGENTS;
83
+ return {
84
+ setup: "skills-mcp",
85
+ skillsAgents: selected.filter((id) => skillsInstallableAgents().includes(id)),
86
+ mcpAgents: selected.filter((id) => mcpInstallableAgents(mcpConfigLocation).includes(id))
87
+ };
88
+ };
89
+ const validateMcpConfigLocationSupport = (ids, location) => {
90
+ const supported = new Set(mcpInstallableAgents(location));
91
+ const unavailable = ids.filter((id) => !supported.has(id));
92
+ if (unavailable.length === 0) return;
93
+ if (unavailable.length < ids.length) {
94
+ log.warning(mcpConfigLocationSkipped(unavailable, location));
95
+ return;
96
+ }
97
+ throw new Error(mcpConfigLocationUnavailable(unavailable, location));
98
+ };
99
+ const pickOrDetectAgents = async (input) => {
100
+ if (input.named.length > 0) {
101
+ const selected = input.named.filter((id) => input.available.includes(id));
102
+ const dropped = input.named.filter((id) => !input.available.includes(id));
103
+ if (dropped.length > 0) throw new Error(namedAgentsUnavailable(dropped));
104
+ return selected;
105
+ }
106
+ const detected = input.detected.filter((id) => input.available.includes(id));
107
+ if (input.pickAgents !== void 0) return input.pickAgents({
108
+ available: [...input.available],
109
+ detected
110
+ });
111
+ if (input.interactive) return pickInitAgentsInteractively({
112
+ available: [...input.available],
113
+ detected
114
+ });
115
+ return detected;
116
+ };
117
+ const defaultInitConfig = async (input) => {
118
+ await initCmd({
119
+ cwd: input.cwd,
120
+ install: input.install,
121
+ silent: true,
122
+ requireInstall: true,
123
+ ...input.services !== void 0 ? { services: input.services } : {},
124
+ ...input.packageManager !== void 0 ? { packageManager: input.packageManager } : {}
125
+ });
126
+ };
127
+ const defaultCreateClaimable = async (input) => {
128
+ await create({
129
+ _: ["claim", "create"],
130
+ output: input.output,
131
+ configDir: input.configDir,
132
+ contextFile: input.contextFile,
133
+ claimableHost: process.env.CLAIMABLE_NEON_HOST ?? "https://claimable.neon.tech",
134
+ apiKey: input.apiKey,
135
+ envPull: false,
136
+ cwd: input.cwd,
137
+ ...input.profile !== void 0 ? { profile: input.profile } : {}
138
+ });
139
+ };
140
+ const runInit = async (props) => {
141
+ if (props.output === "json" || props.output === "yaml") throw new Error(`\`${getCliName()} init\` does not support --output. The commands it runs print their own output.`);
142
+ const cwd = props.cwd ?? process.cwd();
143
+ const contextFile = resolve(cwd, props.contextFile);
144
+ const yes = props.yes === true;
145
+ const analytics = props.analytics !== false;
146
+ const named = resolveNamedAgents(props.agent ?? []);
147
+ assertNamedAgentTooling(named, "init", yes ? { yes: true } : void 0);
148
+ assertAgentSetupFlags({
149
+ skipAgents: props.agentSetup === false,
150
+ namedAgents: named.length > 0,
151
+ ...props.skill !== void 0 ? { skills: props.skill } : {},
152
+ ...props.mcpConfigLocation !== void 0 ? { mcpConfigLocation: props.mcpConfigLocation } : {},
153
+ ...props.mcpAuth !== void 0 ? { mcpAuth: props.mcpAuth } : {},
154
+ ...props.mcpProjectScoped === true ? { mcpProjectScoped: true } : {}
155
+ });
156
+ const run = props.run ?? spawnCliChild;
157
+ const explicitKey = props.profile ? "" : credentialInputs().apiKeyFlag;
158
+ const authEnv = explicitKey ? { NEON_API_KEY: explicitKey } : void 0;
159
+ const configDir = props.configDir ?? defaultDir;
160
+ const forward = {
161
+ ...props.configDir ? { configDir: props.configDir } : {},
162
+ ...props.profile ? { profile: props.profile } : {},
163
+ apiHost: props.apiHost,
164
+ contextFile,
165
+ ...props.analytics === false ? { analytics: false } : {}
166
+ };
167
+ const servicesFlag = parsedInitServices(props.services);
168
+ if (props.config === false && servicesFlag !== void 0) throw new Error(INIT_CONFIG_SERVICES_CONFLICT);
169
+ const linkInputs = {
170
+ ...props.orgId ? { orgId: props.orgId } : {},
171
+ ...props.projectId ? { projectId: props.projectId } : {},
172
+ ...props.projectName ? { projectName: props.projectName } : {},
173
+ ...props.regionId ? { regionId: props.regionId } : {},
174
+ ...props.branch ? { branch: props.branch } : {}
175
+ };
176
+ const hasExplicitLinkInputs = props.orgId !== void 0 || props.projectId !== void 0 || props.projectName !== void 0 || props.regionId !== void 0 || props.branch !== void 0;
177
+ const funnel = createInitFunnel();
178
+ let outcome = "error";
179
+ let printed = false;
180
+ try {
181
+ const detectOpts = {
182
+ cwd,
183
+ configDir,
184
+ ...props.detectProjectAgents ? { detectProjectAgents: props.detectProjectAgents } : {},
185
+ ...props.detectInstalledAgents ? { detectInstalledAgents: props.detectInstalledAgents } : {},
186
+ ...props.detectAgent ? { detectAgent: props.detectAgent } : {},
187
+ ...props.hasLocalCredentials ? { hasLocalCredentials: props.hasLocalCredentials } : {},
188
+ interactive: yes ? false : void 0
189
+ };
190
+ const detection = await detectInitEnvironment(detectOpts);
191
+ if (yes) detection.interactive = false;
192
+ if (analytics) emitInitStart(initStartProperties({
193
+ initRunId: funnel.initRunId,
194
+ detection
195
+ }));
196
+ if (shouldPrintInitBanner(yes)) printInitBanner();
197
+ const skipAgents = props.agentSetup === false;
198
+ const modeArgs = {
199
+ yes,
200
+ interactive: detection.interactive || props.pickMode !== void 0 || props.pickAgentSetup !== void 0,
201
+ skipAgents,
202
+ namedAgents: named.length > 0,
203
+ noLink: props.link === false,
204
+ hasLinkInputs: hasExplicitLinkInputs,
205
+ claimable: props.claimable === true,
206
+ ...props.skill !== void 0 ? { skills: props.skill } : {},
207
+ ...props.mcpConfigLocation !== void 0 ? { mcpConfigLocation: props.mcpConfigLocation } : {},
208
+ ...props.mcpAuth !== void 0 ? { mcpAuth: props.mcpAuth } : {},
209
+ ...props.mcpProjectScoped === true ? { mcpProjectScoped: true } : {},
210
+ ...props.config !== void 0 ? { configFlag: props.config } : {},
211
+ ...servicesFlag !== void 0 ? { services: servicesFlag } : {}
212
+ };
213
+ resolveInitMode(modeArgs);
214
+ const modeResolution = resolveInitMode({
215
+ ...modeArgs,
216
+ interactive: detection.interactive || props.pickMode !== void 0 || props.pickAgentSetup !== void 0
217
+ });
218
+ const mode = modeResolution.kind === "ask" ? props.pickMode !== void 0 ? await props.pickMode() : props.pickAgentSetup !== void 0 || props.pickProjectSetup !== void 0 || props.pickConfig !== void 0 ? "custom" : await pickInitModeInteractively() : modeResolution.kind;
219
+ funnel.mode = mode;
220
+ const alreadyLinked = isLinked(contextFile);
221
+ const existingConfig = hasNeonConfigFile(cwd);
222
+ const existingFilename = neonConfigFilename(cwd);
223
+ const claimableExisting = alreadyLinked && readLinkedClaimableCredentials(configDir, readContextFile(contextFile)) !== null;
224
+ if (props.claimable === true) {
225
+ if (props.link === false) throw new Error(CLAIMABLE_NO_LINK);
226
+ if (hasExplicitLinkInputs) throw new Error(CLAIMABLE_ACCOUNT_FLAGS);
227
+ if (alreadyLinked) throw new Error(CLAIMABLE_ALREADY_LINKED);
228
+ }
229
+ const recommended = mode === "recommended";
230
+ const targets = named.length > 0 ? named : detection.detectedAgents;
231
+ let agentSetupChoice = "plugin";
232
+ let tooling = { setup: "skip" };
233
+ let mcpAuth = props.mcpAuth;
234
+ let mcpConfigLocation = props.mcpConfigLocation ?? "global";
235
+ let selectedSkills = props.skill;
236
+ let delayMcp = false;
237
+ let pluginScope = recommended ? "global" : "project";
238
+ if (recommended) {
239
+ pluginScope = "global";
240
+ tooling = named.length > 0 ? splitInitTooling(named, "global") : recommendedTooling(detection.detectedAgents);
241
+ if (named.length === 0 && detection.detectedAgents.length === 0) printInitProgress(NO_AGENTS_FALLBACK_STATUS);
242
+ agentSetupChoice = funnelAgentSetup(tooling);
243
+ mcpAuth = !detection.authenticated && (tooling.setup === "skills-mcp" || tooling.setup === "mixed") ? "oauth" : mcpAuth;
244
+ } else {
245
+ const inferred = inferInitAgentSetup({
246
+ skipAgents,
247
+ hasMcpFlags: hasInitMcpFlags(props),
248
+ namedAgents: named.length > 0,
249
+ yes,
250
+ canAsk: detection.interactive || props.pickAgentSetup !== void 0,
251
+ ...props.skill !== void 0 ? { skills: props.skill } : {}
252
+ });
253
+ if (inferred.kind === "skip") {
254
+ tooling = { setup: "skip" };
255
+ agentSetupChoice = "skip";
256
+ } else if (inferred.kind === "auto") {
257
+ const autoAgents = named.length > 0 ? named : detection.detectedAgents;
258
+ pluginScope = pluginScopeFor(autoAgents, "project");
259
+ tooling = splitInitTooling(autoAgents, pluginScope, mcpConfigLocation);
260
+ if (tooling.setup === "skip" && yes && named.length === 0) {
261
+ tooling = recommendedTooling([]);
262
+ printInitProgress(NO_AGENTS_FALLBACK_STATUS);
263
+ }
264
+ agentSetupChoice = funnelAgentSetup(tooling);
265
+ } else {
266
+ const setup = inferred.kind === "ask" ? await (props.pickAgentSetup ?? pickAgentSetupInteractively)() : inferred.kind;
267
+ agentSetupChoice = setup === "skills" ? "skills" : setup;
268
+ if (setup !== "skip") {
269
+ pluginScope = pluginScopeFor(targets, "project");
270
+ const available = setup === "plugin" ? uniqueAgents([...pluginsInstallableAgents("project"), ...pluginsInstallableAgents("global")]) : availableForSetup(setup, pluginScope);
271
+ const selected = await pickOrDetectAgents({
272
+ named,
273
+ available,
274
+ detected: detection.detectedAgents,
275
+ interactive: detection.interactive,
276
+ ...props.pickAgents !== void 0 ? { pickAgents: props.pickAgents } : {}
277
+ });
278
+ const namedFallback = named.length === 0;
279
+ if (setup === "plugin") {
280
+ pluginScope = pluginScopeFor(selected, pluginScope);
281
+ tooling = splitInitTooling(selected, pluginScope);
282
+ if (tooling.setup !== "plugin" && tooling.setup !== "skip") tooling = splitInitTooling(selected.filter((id) => pluginsInstallableAgents(pluginScope).includes(id)), pluginScope);
283
+ agentSetupChoice = funnelAgentSetup(tooling);
284
+ } else if (setup === "skills") {
285
+ tooling = skillsTooling(selected, namedFallback);
286
+ agentSetupChoice = funnelAgentSetup(tooling);
287
+ } else {
288
+ if (selectedSkills === void 0 && inferred.kind === "ask") selectedSkills = props.pickSkills !== void 0 ? await props.pickSkills() : detection.interactive ? await pickInitSkillsInteractively() : void 0;
289
+ mcpConfigLocation = props.mcpConfigLocation ?? (yes ? "global" : props.pickMcpConfigLocation !== void 0 ? await props.pickMcpConfigLocation() : detection.interactive ? await pickInitMcpConfigLocationInteractively() : "global");
290
+ mcpAuth = props.mcpAuth ?? (yes ? detection.authenticated && props.claimable !== true ? "api-key" : "oauth" : props.pickMcpAuth !== void 0 ? await props.pickMcpAuth({ authenticated: detection.authenticated }) : detection.interactive ? await pickInitMcpAuthInteractively({ authenticated: detection.authenticated }) : detection.authenticated && props.claimable !== true ? "api-key" : "oauth");
291
+ validateMcpConfigLocationSupport(selected, mcpConfigLocation);
292
+ tooling = skillsMcpTooling(selected, mcpConfigLocation, namedFallback);
293
+ agentSetupChoice = "skills-mcp";
294
+ }
295
+ }
296
+ }
297
+ }
298
+ funnel.agentSetup = agentSetupChoice === "plugin" || agentSetupChoice === "skills-mcp" || agentSetupChoice === "skills" || agentSetupChoice === "mixed" || agentSetupChoice === "skip" ? agentSetupChoice : funnelAgentSetup(tooling);
299
+ const usesMcp = tooling.setup === "skills-mcp" || tooling.setup === "mixed";
300
+ if (mcpAuth === void 0 && usesMcp && (!detection.authenticated || props.claimable === true)) mcpAuth = "oauth";
301
+ delayMcp = usesMcp && (mcpAuth === "api-key" || props.mcpProjectScoped === true);
302
+ const mcpOauth = mcpAuth === "oauth";
303
+ const earlyTooling = delayMcp ? tooling.setup === "skills-mcp" ? {
304
+ setup: "skills-mcp",
305
+ skillsAgents: tooling.skillsAgents,
306
+ mcpAgents: []
307
+ } : tooling.setup === "mixed" ? {
308
+ setup: "mixed",
309
+ pluginAgents: tooling.pluginAgents,
310
+ skillsAgents: tooling.skillsAgents,
311
+ mcpAgents: []
312
+ } : tooling : tooling;
313
+ const toolingSteps = planInitToolingSteps({
314
+ tooling: earlyTooling,
315
+ yes: true,
316
+ pluginScope,
317
+ skillsGlobal: recommended,
318
+ mcpOauth,
319
+ mcpProject: mcpConfigLocation === "project",
320
+ ...selectedSkills !== void 0 ? { skills: selectedSkills } : {}
321
+ });
322
+ if (toolingSteps.length > 0) {
323
+ await runInitSteps(toolingSteps, {
324
+ cwd,
325
+ run,
326
+ forward,
327
+ authEnv,
328
+ narrate: "human"
329
+ });
330
+ funnel.agentsInstalled = agentsFromTooling(earlyTooling);
331
+ }
332
+ let projectSetup = "skip";
333
+ let linkedNow = false;
334
+ let claimExpiresAt;
335
+ const noLink = props.link === false;
336
+ if (noLink) {
337
+ funnel.link = alreadyLinked ? "already_linked" : "skipped";
338
+ projectSetup = "skip";
339
+ } else if (alreadyLinked && !hasExplicitLinkInputs) {
340
+ funnel.link = claimableExisting ? "claimable" : "already_linked";
341
+ projectSetup = "already";
342
+ if (claimableExisting) claimExpiresAt = readLinkedClaimableCredentials(configDir, readContextFile(contextFile))?.expiresAt;
343
+ } else if (recommended) {
344
+ if (hasExplicitLinkInputs || detection.authenticated) projectSetup = "link";
345
+ else if (detection.interactive && !yes) projectSetup = "link";
346
+ else {
347
+ projectSetup = "skip";
348
+ funnel.link = "skipped";
349
+ }
350
+ } else if (props.claimable === true) projectSetup = "claimable";
351
+ else if (hasExplicitLinkInputs || detection.authenticated) projectSetup = "link";
352
+ else if (yes) {
353
+ projectSetup = "skip";
354
+ funnel.link = "skipped";
355
+ } else if (props.pickProjectSetup !== void 0 || detection.interactive) projectSetup = await (props.pickProjectSetup ?? pickInitProjectSetupInteractively)();
356
+ else throw new Error(NON_TTY_LINK_NEEDS_AUTH);
357
+ if (mcpAuth === "api-key" && (projectSetup === "claimable" || props.claimable === true)) throw new Error(CLAIMABLE_MCP_API_KEY);
358
+ if (projectSetup === "claimable") {
359
+ printInitProgress(PROGRESS.claim);
360
+ await (props.createClaimable ?? defaultCreateClaimable)({
361
+ cwd,
362
+ configDir,
363
+ contextFile,
364
+ output: props.output,
365
+ apiKey: props.apiKey,
366
+ ...props.profile !== void 0 ? { profile: props.profile } : {}
367
+ });
368
+ linkedNow = true;
369
+ funnel.link = "claimable";
370
+ claimExpiresAt = readLinkedClaimableCredentials(configDir, readContextFile(contextFile))?.expiresAt;
371
+ } else if (projectSetup === "link") {
372
+ if (yes && !detection.authenticated) throw new Error(YES_LINK_NEEDS_AUTH);
373
+ printInitProgress(detection.authenticated ? PROGRESS.link : PROGRESS.auth);
374
+ await (props.linkProject ?? runAuthenticatedLink)({
375
+ apiClient: props.apiClient,
376
+ apiKey: props.apiKey,
377
+ apiHost: props.apiHost,
378
+ output: props.output,
379
+ contextFile,
380
+ yes,
381
+ clear: false,
382
+ checks: true,
383
+ envPull: true,
384
+ config: false,
385
+ cwd,
386
+ ...linkInputs,
387
+ ...props.configDir ? { configDir: props.configDir } : {},
388
+ ...props.profile ? { profile: props.profile } : {},
389
+ ...props.oauthHost ? { oauthHost: props.oauthHost } : {},
390
+ ...props.clientId ? { clientId: props.clientId } : {},
391
+ ...props.forceAuth !== void 0 ? { forceAuth: props.forceAuth } : {},
392
+ ...props.allowUnsafeTls !== void 0 ? { allowUnsafeTls: props.allowUnsafeTls } : {}
393
+ });
394
+ linkedNow = true;
395
+ funnel.link = "linked";
396
+ }
397
+ if (delayMcp && tooling.setup !== "skip") {
398
+ const linkedId = readContextFile(contextFile).projectId;
399
+ let pinId;
400
+ if (props.mcpProjectScoped === true) {
401
+ if (typeof linkedId !== "string" || linkedId.length === 0) throw new Error(MCP_SCOPED_NEEDS_PROJECT);
402
+ pinId = linkedId;
403
+ }
404
+ const mcpOnly = tooling.setup === "skills-mcp" ? {
405
+ setup: "skills-mcp",
406
+ skillsAgents: [],
407
+ mcpAgents: tooling.mcpAgents
408
+ } : tooling.setup === "mixed" ? {
409
+ setup: "skills-mcp",
410
+ skillsAgents: [],
411
+ mcpAgents: tooling.mcpAgents
412
+ } : { setup: "skip" };
413
+ const mcpSteps = planInitToolingSteps({
414
+ tooling: mcpOnly,
415
+ yes: true,
416
+ pluginScope,
417
+ skillsGlobal: recommended,
418
+ mcpOauth,
419
+ mcpProject: mcpConfigLocation === "project",
420
+ ...pinId !== void 0 ? { mcpProjectId: pinId } : {}
421
+ });
422
+ if (mcpSteps.length > 0) {
423
+ await runInitSteps(mcpSteps, {
424
+ cwd,
425
+ run,
426
+ forward,
427
+ authEnv,
428
+ narrate: "human"
429
+ });
430
+ funnel.agentsInstalled = uniqueAgents([...funnel.agentsInstalled, ...agentsFromTooling(mcpOnly)]);
431
+ }
432
+ }
433
+ const canAskConfig = props.pickConfig !== void 0 || detection.interactive;
434
+ const configResolution = resolveInitConfigChoice({
435
+ flag: recommended ? props.config ?? true : props.config,
436
+ yes: recommended || yes,
437
+ canAsk: canAskConfig,
438
+ existingConfig,
439
+ ...servicesFlag !== void 0 ? { services: servicesFlag } : {}
440
+ });
441
+ const accepted = configResolution.kind === "ask" ? await (props.pickConfig ?? pickInitConfigInteractively)() : void 0;
442
+ let configPlan = configPlanFromResolution(configResolution, accepted);
443
+ if (configPlan.kind === "write" && !existingConfig && configPlan.services === void 0 && !recommended && servicesFlag === void 0 && (detection.interactive || props.pickServices !== void 0)) {
444
+ const picked = await (props.pickServices ?? pickInitServicesInteractively)();
445
+ configPlan = {
446
+ kind: "write",
447
+ services: picked.length === 0 ? ["none"] : picked
448
+ };
449
+ }
450
+ if (recommended && configPlan.kind === "write" && !existingConfig) configPlan = {
451
+ kind: "write",
452
+ services: servicesFlag ?? ["none"]
453
+ };
454
+ let wroteNewFile = false;
455
+ let extraServices = false;
456
+ let selectedServices = null;
457
+ if (configPlan.kind === "write") {
458
+ const planned = existingConfig ? void 0 : configPlan.services ?? ["none"];
459
+ if (planned !== void 0) {
460
+ extraServices = !isBareInitServices(planned);
461
+ selectedServices = extraServices ? expandTelemetryServices(parseServices(planned, {
462
+ allowed: CONFIG_INIT_SERVICES,
463
+ flag: "--services",
464
+ noneMeans: CONFIG_INIT_NONE_MEANS
465
+ })) : [];
466
+ funnel.services = selectedServices;
467
+ wroteNewFile = true;
468
+ } else selectedServices = null;
469
+ let pm = props.packageManager;
470
+ if (pm === void 0) pm = recommended ? resolvePackageManager(cwd) : inferPackageManager(cwd);
471
+ if (pm === void 0) {
472
+ if (props.pickPackageManager !== void 0 || detection.interactive) pm = await (props.pickPackageManager ?? pickInitPackageManagerInteractively)() ?? resolvePackageManager(cwd);
473
+ else pm = resolvePackageManager(cwd);
474
+ }
475
+ if (!existingConfig) printInitProgress(PROGRESS.config);
476
+ printInitProgress(PROGRESS.install(pm));
477
+ try {
478
+ await (props.initConfig ?? defaultInitConfig)({
479
+ cwd,
480
+ install: true,
481
+ packageManager: pm,
482
+ ...planned !== void 0 ? { services: planned } : {}
483
+ });
484
+ } catch (error) {
485
+ if (error instanceof ConfigInstallFailed) {
486
+ const failed = installFailedNext(error.command);
487
+ printInitDone(formatInitDone({
488
+ heading: failed.heading,
489
+ body: failed.body,
490
+ rows: [],
491
+ next: failed.next
492
+ }));
493
+ printed = true;
494
+ outcome = "error";
495
+ throw error;
496
+ }
497
+ throw error;
498
+ }
499
+ funnel.config = existingConfig ? "existing" : "created";
500
+ } else funnel.config = "skipped";
501
+ const context = readContextFile(contextFile);
502
+ const branch = contextBranch(context);
503
+ const linked = isLinked(contextFile);
504
+ const alreadyPulled = linkedNow && funnel.link === "linked";
505
+ if (shouldPullEnvAfterInitConfig({
506
+ wroteNewFile,
507
+ extraServices,
508
+ alreadyPulled,
509
+ ...typeof context.projectId === "string" ? { projectId: context.projectId } : {},
510
+ ...branch !== void 0 ? { branch } : {}
511
+ })) try {
512
+ await runInitSteps([["env", "pull"]], {
513
+ cwd,
514
+ run,
515
+ forward,
516
+ authEnv,
517
+ narrate: "human"
518
+ });
519
+ } catch (error) {
520
+ const failed = envPullFailedNext();
521
+ printInitDone(formatInitDone({
522
+ heading: failed.heading,
523
+ body: failed.body,
524
+ rows: [
525
+ {
526
+ label: "Agents",
527
+ value: agentsRowValue({
528
+ setup: funnel.agentSetup,
529
+ agents: funnel.agentsInstalled
530
+ })
531
+ },
532
+ {
533
+ label: "Project",
534
+ value: projectRowValue(funnel.link)
535
+ },
536
+ {
537
+ label: "Config",
538
+ value: configRowValue(funnel.config, existingFilename)
539
+ }
540
+ ],
541
+ next: failed.next
542
+ }));
543
+ printed = true;
544
+ outcome = "error";
545
+ throw error;
546
+ }
547
+ const pendingUnauthed = funnel.link === "skipped" && !noLink && !alreadyLinked;
548
+ const pendingServices = extraServices && linked;
549
+ const pendingClaimable = funnel.link === "claimable" && claimExpiresAt !== void 0;
550
+ const pendingExisting = funnel.config === "existing" && linked && !wroteNewFile;
551
+ const pending = pendingUnauthed || pendingServices || pendingClaimable && extraServices || pendingExisting;
552
+ const next = [];
553
+ if (pendingUnauthed) next.push(...unattendedUnauthedNext());
554
+ else if (noLink && !alreadyLinked) next.push(...skippedLinkNext());
555
+ if (pendingClaimable && extraServices && claimExpiresAt !== void 0) {
556
+ if (next.length > 0) next.push("");
557
+ next.push(...claimableThenServicesNext(claimExpiresAt));
558
+ } else if (pendingClaimable && claimExpiresAt !== void 0) {
559
+ if (next.length > 0) next.push("");
560
+ next.push(...claimableNext(claimExpiresAt));
561
+ } else if (pendingServices) {
562
+ if (next.length > 0) next.push("");
563
+ next.push(...extraServicesNext());
564
+ } else if (pendingExisting) {
565
+ if (next.length > 0) next.push("");
566
+ next.push(...existingConfigNext());
567
+ }
568
+ outcome = pending && !pendingUnauthed && noLink ? "success" : pending ? "pending" : "success";
569
+ if (pendingUnauthed) outcome = "pending";
570
+ if (noLink && !pendingUnauthed && !pendingServices && !pendingExisting) outcome = "success";
571
+ printInitDone(formatInitDone({
572
+ heading: headingForKind(outcome),
573
+ rows: [
574
+ {
575
+ label: "Agents",
576
+ value: agentsRowValue({
577
+ setup: funnel.agentSetup,
578
+ agents: funnel.agentsInstalled
579
+ })
580
+ },
581
+ {
582
+ label: "Project",
583
+ value: projectRowValue(funnel.link ?? (linked ? "already_linked" : null))
584
+ },
585
+ {
586
+ label: "Config",
587
+ value: configRowValue(funnel.config, existingFilename)
588
+ }
589
+ ],
590
+ next
591
+ }));
592
+ printed = true;
593
+ takeCommandSuccessExtras();
594
+ recordCommandSuccessExtras({
595
+ init_kind: detection.emptyDirectory ? "empty-skip" : "existing",
596
+ ...extrasSetup(funnel.agentSetup) !== void 0 ? { agent_setup: extrasSetup(funnel.agentSetup) } : {}
597
+ });
598
+ } catch (error) {
599
+ restoreCursor();
600
+ if (error instanceof InitCancelled) {
601
+ outcome = "aborted";
602
+ if (!printed) printInitDone(formatInitDone({
603
+ heading: headingForKind("aborted"),
604
+ body: CANCELLED_BODY,
605
+ rows: [],
606
+ next: []
607
+ }));
608
+ throw error;
609
+ }
610
+ outcome = "error";
611
+ throw error;
612
+ } finally {
613
+ finishInitFunnel(funnel, outcome, analytics);
614
+ }
615
+ };
616
+ const uniqueAgents = (ids) => {
617
+ const seen = /* @__PURE__ */ new Set();
618
+ const out = [];
619
+ for (const id of ids) {
620
+ if (seen.has(id)) continue;
621
+ seen.add(id);
622
+ out.push(id);
623
+ }
624
+ return out;
625
+ };
626
+ const agentsFromTooling = (tooling) => {
627
+ switch (tooling.setup) {
628
+ case "skip": return [];
629
+ case "plugin": return [...tooling.agents];
630
+ case "skills": return [...tooling.agents];
631
+ case "skills-mcp": return uniqueAgents([...tooling.skillsAgents, ...tooling.mcpAgents]);
632
+ case "mixed": return uniqueAgents([
633
+ ...tooling.pluginAgents,
634
+ ...tooling.skillsAgents,
635
+ ...tooling.mcpAgents
636
+ ]);
637
+ default: return tooling;
638
+ }
639
+ };
640
+ //#endregion
641
+ export { initChildEnv, runInit };
@@ -12,7 +12,7 @@ const defaultProjectAgents = (cwd) => detectProjectAgents(cwd);
12
12
  const runInitSteps = async (steps, options) => {
13
13
  for (const step of steps) {
14
14
  const label = options.narrate === "human" ? initStepLabel(step) : void 0;
15
- if (label !== void 0) log.info(label);
15
+ if (label !== void 0) process.stdout.write(`${label}\n`);
16
16
  else log.info("Running `%s %s`", getCliName(), step.join(" "));
17
17
  log.debug("Running `%s %s`", getCliName(), step.join(" "));
18
18
  const argv = childArgv(step, options.forward);