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