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.
@@ -1,31 +1,46 @@
1
1
  import { isCi } from "../env.js";
2
+ import { HEADING_CANCELLED, HEADING_COMPLETE, HEADING_FAILED, HEADING_PENDING, INIT_SUBTITLE } from "./copy.js";
2
3
  import chalk from "chalk";
3
4
  //#region src/init/chrome.ts
4
5
  const NEON_GREEN = "#4BB578";
5
6
  const INIT_BANNER_LINES = [
6
- " ██╗ ██╗██████╗ ██████╗ ██╗ ██╗",
7
- " ███╗ ██║██╔═══╝ ██╔═══██╗███╗ ██║",
8
- " ████╗██║██████╗ ██║ ██║████╗██║",
9
- " ██╔████║██╔═══╝ ██║ ██║██╔████║",
10
- " ██║╚███║██████╗ ╚██████╔╝██║╚███║",
11
- " ╚═╝ ╚══╝╚═════╝ ╚═════╝ ╚═╝ ╚══╝"
7
+ " ██╗ ██╗██████╗ ██████╗ ██╗ ██╗",
8
+ " ███╗ ██║██╔═══╝██╔═══██╗███╗ ██║",
9
+ " ████╗██║██████╗██║ ██║████╗██║",
10
+ " ██╔████║██╔═══╝██║ ██║██╔████║",
11
+ " ██║╚███║██████╗╚██████╔╝██║╚███║",
12
+ " ╚═╝ ╚══╝╚═════╝ ╚═════╝ ╚═╝ ╚══╝"
12
13
  ];
13
14
  const formatInitBanner = () => INIT_BANNER_LINES.join("\n");
14
15
  const shouldPrintInitBanner = (yes) => !yes && !isCi() && Boolean(process.stdout.isTTY);
15
16
  const printInitBanner = () => {
16
- process.stdout.write(`\n${chalk.hex(NEON_GREEN)(formatInitBanner())}\n\n${chalk.dim("Set up this directory for Neon.")}\n\n`);
17
+ process.stdout.write(`\n${chalk.hex(NEON_GREEN)(formatInitBanner())}\n\n${chalk.dim(INIT_SUBTITLE)}\n\n`);
18
+ };
19
+ const printInitProgress = (message) => {
20
+ process.stdout.write(`${message}\n`);
17
21
  };
18
22
  const formatInitDone = (input) => {
19
23
  const labelWidth = Math.max(0, ...input.rows.map((row) => row.label.length));
24
+ const intro = input.body !== void 0 && input.body.length > 0 ? `\n\n${input.body}` : "";
20
25
  const body = input.rows.length === 0 ? "" : `\n\n${input.rows.map((row) => ` ${row.label.padEnd(labelWidth)} ${row.value}`).join("\n")}`;
21
26
  const next = input.next.length === 0 ? "" : `\n\nNext:\n${input.next.map((line) => ` ${line}`).join("\n")}`;
22
27
  const rule = "-".repeat(input.heading.length);
23
- return `${input.heading}\n${rule}${body}${next}\n`;
28
+ return `${input.heading}\n${rule}${intro}${body}${next}\n`;
29
+ };
30
+ const headingColor = (heading) => {
31
+ if (heading === "Neon setup complete.") return chalk.hex(NEON_GREEN).bold;
32
+ if (heading === "Neon setup needs a next step.") return chalk.yellow.bold;
33
+ if (heading === "Neon setup failed.") return chalk.red.bold;
34
+ if (heading === "Neon setup cancelled.") return chalk.dim;
35
+ return chalk.hex(NEON_GREEN).bold;
24
36
  };
25
37
  const printInitDone = (text) => {
26
- const painted = (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n").map((line, index) => {
27
- if (index === 0) return chalk.hex(NEON_GREEN).bold(line);
28
- if (/^-{3,}$/.test(line)) return chalk.hex(NEON_GREEN)(line);
38
+ const lines = (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n");
39
+ const heading = lines[0] ?? "";
40
+ const paintHeading = headingColor(heading);
41
+ const painted = lines.map((line, index) => {
42
+ if (index === 0) return paintHeading(line);
43
+ if (/^-{3,}$/.test(line)) return heading === "Neon setup failed." ? chalk.red(line) : heading === "Neon setup needs a next step." ? chalk.yellow(line) : heading === "Neon setup cancelled." ? chalk.dim(line) : chalk.hex(NEON_GREEN)(line);
29
44
  if (line === "Next:") return chalk.bold(line);
30
45
  return line;
31
46
  });
@@ -40,6 +55,25 @@ const agentSetupDoneLabel = (input) => {
40
55
  if (!input.ran && input.setup !== "skip") return "not run";
41
56
  return agentSetupLabel(input.setup);
42
57
  };
58
+ const formatAgentIds = (agents) => agents.join(", ");
59
+ const agentsRowValue = (input) => {
60
+ if (input.setup === null || input.setup === "skip") return "skipped";
61
+ if (input.setup === "skills") return "default Neon skills in this directory";
62
+ const ids = formatAgentIds(input.agents);
63
+ if (input.setup === "plugin") return ids.length > 0 ? `Neon plugin: ${ids}` : "Neon plugin";
64
+ if (input.setup === "skills-mcp") return ids.length > 0 ? `skills and MCP: ${ids}` : "skills and MCP";
65
+ return ids.length > 0 ? `plugin and skills/MCP: ${ids}` : "plugin and skills/MCP";
66
+ };
67
+ const projectRowValue = (link) => {
68
+ switch (link) {
69
+ case "already_linked": return "already linked";
70
+ case "linked": return "linked";
71
+ case "claimable": return "claimable";
72
+ case "skipped": return "skipped";
73
+ case null: return "not linked";
74
+ default: return link;
75
+ }
76
+ };
43
77
  const configSummaryLabel = (summary) => {
44
78
  switch (summary) {
45
79
  case "created": return "neon.ts created";
@@ -49,19 +83,20 @@ const configSummaryLabel = (summary) => {
49
83
  default: return summary;
50
84
  }
51
85
  };
52
- const INIT_STEP_LABELS = {
53
- bootstrap: "Creating the app from the selected template…",
54
- plugins: "Installing the Neon plugin…",
55
- skills: "Installing Neon agent skills…",
56
- mcp: "Setting up the Neon MCP server…",
57
- link: "Linking a Neon project…",
58
- config: "Setting up neon.ts…",
59
- env: "Refreshing Neon env vars…"
86
+ const configRowValue = (config, filename) => {
87
+ if (config === "created") return "neon.ts created";
88
+ if (config === "existing") return `existing ${filename ?? "Neon config"} preserved`;
89
+ if (config === "skipped" || config === null) return "skipped";
90
+ return config;
60
91
  };
61
- const initStepLabel = (step) => {
62
- const command = step[0];
63
- if (command === void 0) return;
64
- return INIT_STEP_LABELS[command];
92
+ const headingForKind = (kind) => {
93
+ switch (kind) {
94
+ case "success": return HEADING_COMPLETE;
95
+ case "pending": return HEADING_PENDING;
96
+ case "error": return HEADING_FAILED;
97
+ case "aborted": return HEADING_CANCELLED;
98
+ default: return kind;
99
+ }
65
100
  };
66
101
  //#endregion
67
- export { INIT_BANNER_LINES, INIT_STEP_LABELS, NEON_GREEN, agentSetupDoneLabel, agentSetupLabel, configSummaryLabel, formatInitBanner, formatInitDone, initStepLabel, printInitBanner, printInitDone, shouldPrintInitBanner };
102
+ export { INIT_BANNER_LINES, NEON_GREEN, agentSetupDoneLabel, agentSetupLabel, agentsRowValue, configRowValue, configSummaryLabel, formatInitBanner, formatInitDone, headingForKind, printInitBanner, printInitDone, printInitProgress, projectRowValue, shouldPrintInitBanner };
@@ -0,0 +1,133 @@
1
+ import { getCliName } from "../utils/cli_name.js";
2
+ //#region src/init/copy.ts
3
+ const INIT_SUBTITLE = "Set up coding agents and this directory for Neon.";
4
+ const MODE_MESSAGE = "How would you like to set up Neon?";
5
+ const MODE_RECOMMENDED_TITLE = "Recommended setup";
6
+ const MODE_RECOMMENDED_DESCRIPTION = "Set up detected agents, link a project, and add a default neon.ts in this directory.";
7
+ const MODE_CUSTOM_TITLE = "Custom setup";
8
+ const MODE_CUSTOM_DESCRIPTION = "Choose agent tooling, project setup, and the services declared in neon.ts.";
9
+ const AGENT_SETUP_MESSAGE = "How should Neon be added to your coding agents?";
10
+ const AGENT_SETUP_PLUGIN_TITLE = "Neon plugin";
11
+ const AGENT_SETUP_PLUGIN_DESCRIPTION = "Install Neon skills and the MCP server together.";
12
+ const AGENT_SETUP_SKILLS_TITLE = "Skills and MCP separately";
13
+ const AGENT_SETUP_SKILLS_DESCRIPTION = "Install Neon skills and configure the MCP server as separate steps.";
14
+ const AGENT_SETUP_SKIP_TITLE = "Skip agent setup";
15
+ const AGENT_SETUP_SKIP_DESCRIPTION = "Continue to project setup without changing agent configuration.";
16
+ const AGENT_PICKER_MESSAGE = "Which coding agents should receive Neon tooling? (space to toggle, enter to confirm)";
17
+ const AGENT_DETECTED_DESCRIPTION = "Detected on this machine or in this directory.";
18
+ const AGENT_OTHER_DESCRIPTION = "Install for this agent.";
19
+ const SKILLS_PICKER_MESSAGE = "Which Neon skills should be installed? (space to toggle, enter to confirm)";
20
+ const MCP_SCOPE_MESSAGE = "Where should the Neon MCP server be configured?";
21
+ const MCP_SCOPE_GLOBAL_TITLE = "User configuration";
22
+ const MCP_SCOPE_GLOBAL_DESCRIPTION = "Make the MCP server available across projects.";
23
+ const MCP_SCOPE_PROJECT_TITLE = "This directory";
24
+ const MCP_SCOPE_PROJECT_DESCRIPTION = "Add the MCP server to this project's agent configuration.";
25
+ const MCP_AUTH_MESSAGE = "How should the MCP server authenticate to Neon?";
26
+ const MCP_AUTH_OAUTH_TITLE = "Sign in from the coding agent";
27
+ const MCP_AUTH_OAUTH_DESCRIPTION = "Configure OAuth. The agent asks you to sign in on first use.";
28
+ const MCP_AUTH_API_KEY_TITLE = "Use a Neon API key";
29
+ const MCP_AUTH_API_KEY_DESCRIPTION = "Reuse an existing MCP key or create a key after CLI authentication.";
30
+ const CLAIMABLE_MCP_API_KEY = "Claimable project setup cannot create an account API key for MCP. Use --mcp-auth oauth.";
31
+ const CLAIMABLE_NO_LINK = "--claimable cannot be combined with --no-link.";
32
+ const CLAIMABLE_ACCOUNT_FLAGS = "--claimable cannot be combined with --org-id, --project-id, --project-name, --region-id, or --branch.";
33
+ const CLAIMABLE_ALREADY_LINKED = "--claimable cannot run in a directory that is already linked. Omit --claimable, or unlink first.";
34
+ const PROJECT_SETUP_MESSAGE = "How would you like to get a Neon project?";
35
+ const PROJECT_SETUP_LINK_TITLE = "Sign in to Neon";
36
+ const PROJECT_SETUP_LINK_DESCRIPTION = "Link an existing project or create a new one in your account.";
37
+ const PROJECT_SETUP_CLAIMABLE_TITLE = "Create a claimable project";
38
+ const PROJECT_SETUP_CLAIMABLE_DESCRIPTION = "Start without an account. The project expires in 72 hours unless claimed.";
39
+ const CONFIG_CONFIRM_HINT = "Declare Neon services in a file you can review before deploying.";
40
+ const CONFIG_CONFIRM_MESSAGE = "Create neon.ts in this directory?";
41
+ const SERVICES_HINT = "Leave the optional services unselected for the default neon.ts.";
42
+ const SERVICES_MESSAGE = "Which services should neon.ts declare? (space to toggle, enter to confirm)";
43
+ const PACKAGE_MANAGER_MESSAGE = "Which package manager should install the Neon dependencies?";
44
+ const NO_AGENTS_FALLBACK_STATUS = "No coding agents detected. Installing the default Neon skills for Cursor and Codex in this directory.";
45
+ const NO_AGENT_SETUP_CONFLICT = "--no-agent-setup cannot be combined with --agent, --skill, or MCP flags.";
46
+ const NON_TTY_LINK_NEEDS_AUTH = "No interactive terminal. Sign in with `neon auth`, then re-run, or pass --claimable.";
47
+ const YES_LINK_NEEDS_AUTH = "-y cannot sign in. Sign in with `neon auth`, then re-run, or pass --claimable.";
48
+ const MCP_SCOPED_NEEDS_PROJECT = "--mcp-project-scoped requires a linked project. Link a project first, or omit --mcp-project-scoped.";
49
+ const namedAgentsUnavailable = (ids) => `--agent ${ids.join(", ")} cannot install this tooling. Pass agents that support it, or omit --agent.`;
50
+ const mcpConfigLocationUnavailable = (ids, location) => location === "project" ? `--agent ${ids.join(", ")} cannot install project-level MCP config. Use --mcp-config-location global, or choose an agent that supports project config.` : `--agent ${ids.join(", ")} cannot install global MCP config. Choose an agent that supports global config.`;
51
+ const mcpConfigLocationSkipped = (ids, location) => {
52
+ const subject = ids.length === 1 ? "that agent does" : "those agents do";
53
+ const skills = ids.length === 1 ? "Its skills" : "Their skills";
54
+ const target = ids.length === 1 ? "it" : "them";
55
+ return location === "project" ? `Skipped project-level MCP config for ${ids.join(", ")} because ${subject} not support it. ${skills} will still be installed. Use --mcp-config-location global to configure MCP for ${target}.` : `Skipped global MCP config for ${ids.join(", ")} because ${subject} not support it. ${skills} will still be installed.`;
56
+ };
57
+ const NON_TTY_AGENT_SETUP = "No interactive terminal. Pass -y for Recommended, or pass --skill, --mcp-auth, --agent, or --no-agent-setup.";
58
+ const HEADING_COMPLETE = "Neon setup complete.";
59
+ const HEADING_PENDING = "Neon setup needs a next step.";
60
+ const HEADING_FAILED = "Neon setup failed.";
61
+ const HEADING_CANCELLED = "Neon setup cancelled.";
62
+ const CANCELLED_BODY = "Changes completed before cancellation were kept.";
63
+ const PROGRESS = {
64
+ plugins: "Installing the Neon plugin...",
65
+ skills: "Installing Neon skills...",
66
+ mcp: "Configuring the Neon MCP server...",
67
+ auth: "Signing in to Neon...",
68
+ link: "Linking a Neon project...",
69
+ claim: "Creating a claimable project...",
70
+ config: "Creating neon.ts...",
71
+ install: (pm) => `Installing Neon dependencies with ${pm}...`,
72
+ env: "Pulling Neon environment variables..."
73
+ };
74
+ const unattendedUnauthedNext = () => [
75
+ "Link a project with a Neon account and an authenticated CLI.",
76
+ "Sign up: https://neon.com/signup",
77
+ `${getCliName()} auth`,
78
+ `${getCliName()} link`,
79
+ "",
80
+ "Or create a claimable project without an account. It expires in 72 hours unless claimed.",
81
+ `${getCliName()} claim create`
82
+ ];
83
+ const skippedLinkNext = () => ["To connect this directory to a Neon project:", `${getCliName()} link`];
84
+ const extraServicesNext = () => [
85
+ "Review neon.ts and confirm the linked project and branch before deploying.",
86
+ "Preview the changes:",
87
+ `${getCliName()} config plan`,
88
+ "",
89
+ "Deploy when the changes and target are correct:",
90
+ `${getCliName()} deploy`,
91
+ "",
92
+ "Refresh local environment variables after deployment:",
93
+ `${getCliName()} env pull`
94
+ ];
95
+ const existingConfigNext = () => ["The existing Neon config was preserved. Refresh environment variables when its declared services are available:", `${getCliName()} env pull`];
96
+ const claimableNext = (expiresAt) => [
97
+ `This project expires at ${expiresAt}.`,
98
+ "To keep it, open the claim flow and sign in to Neon:",
99
+ `${getCliName()} claim accept`
100
+ ];
101
+ const claimableThenServicesNext = (expiresAt) => [
102
+ ...claimableNext(expiresAt),
103
+ "",
104
+ "After claiming the project, review neon.ts and preview the changes:",
105
+ `${getCliName()} config plan`,
106
+ "",
107
+ "Deploy when the changes and target are correct:",
108
+ `${getCliName()} deploy`,
109
+ "",
110
+ "Refresh local environment variables:",
111
+ `${getCliName()} env pull`
112
+ ];
113
+ const installFailedNext = (command) => ({
114
+ heading: HEADING_FAILED,
115
+ body: "Could not install the Neon dependencies.",
116
+ next: ["Install the dependencies, then run neon init again:", command]
117
+ });
118
+ const envPullFailedNext = () => ({
119
+ heading: HEADING_FAILED,
120
+ body: "The project is linked, but its environment variables could not be pulled.",
121
+ next: ["Resolve the error above, then retry:", `${getCliName()} env pull`]
122
+ });
123
+ const headingForOutcome = (outcome) => {
124
+ switch (outcome) {
125
+ case "success": return HEADING_COMPLETE;
126
+ case "pending": return HEADING_PENDING;
127
+ case "error": return HEADING_FAILED;
128
+ case "aborted": return HEADING_CANCELLED;
129
+ default: return outcome;
130
+ }
131
+ };
132
+ //#endregion
133
+ export { 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, CANCELLED_BODY, CLAIMABLE_ACCOUNT_FLAGS, CLAIMABLE_ALREADY_LINKED, CLAIMABLE_MCP_API_KEY, CLAIMABLE_NO_LINK, CONFIG_CONFIRM_HINT, CONFIG_CONFIRM_MESSAGE, HEADING_CANCELLED, HEADING_COMPLETE, HEADING_FAILED, HEADING_PENDING, INIT_SUBTITLE, MCP_AUTH_API_KEY_DESCRIPTION, MCP_AUTH_API_KEY_TITLE, MCP_AUTH_MESSAGE, MCP_AUTH_OAUTH_DESCRIPTION, MCP_AUTH_OAUTH_TITLE, MCP_SCOPED_NEEDS_PROJECT, MCP_SCOPE_GLOBAL_DESCRIPTION, MCP_SCOPE_GLOBAL_TITLE, MCP_SCOPE_MESSAGE, MCP_SCOPE_PROJECT_DESCRIPTION, MCP_SCOPE_PROJECT_TITLE, MODE_CUSTOM_DESCRIPTION, MODE_CUSTOM_TITLE, MODE_MESSAGE, MODE_RECOMMENDED_DESCRIPTION, MODE_RECOMMENDED_TITLE, NON_TTY_AGENT_SETUP, NON_TTY_LINK_NEEDS_AUTH, NO_AGENTS_FALLBACK_STATUS, NO_AGENT_SETUP_CONFLICT, PACKAGE_MANAGER_MESSAGE, PROGRESS, PROJECT_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, YES_LINK_NEEDS_AUTH, claimableNext, claimableThenServicesNext, envPullFailedNext, existingConfigNext, extraServicesNext, headingForOutcome, installFailedNext, mcpConfigLocationSkipped, mcpConfigLocationUnavailable, namedAgentsUnavailable, skippedLinkNext, unattendedUnauthedNext };
@@ -0,0 +1,58 @@
1
+ import { t as storeFor } from "../_chunks/credential_io-D8AR-jvB.js";
2
+ import { isCi } from "../env.js";
3
+ import { h as resolveProfile, u as locationOf } from "../_chunks/profiles-CvnFEQyd.js";
4
+ import { i as selectCredential, t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
5
+ import { detectInstalledAgents, uniqueAgentIds } from "../mcp/agents.js";
6
+ import "./agents.js";
7
+ import { directoryIsEmpty } from "./plan.js";
8
+ import { detectAgent } from "./detect_host.js";
9
+ import { canPickAgentsInteractively } from "../utils/agent_picker.js";
10
+ import { existsSync, readdirSync } from "node:fs";
11
+ import { detectProjectAgents } from "add-mcp";
12
+ //#region src/init/detect.ts
13
+ const listingHasCredentials = (configDir, profile) => {
14
+ try {
15
+ return storeFor(configDir).inspect(locationOf(resolveProfile(configDir, profile))).credentials !== null;
16
+ } catch {
17
+ return false;
18
+ }
19
+ };
20
+ const hasLocalCredentials = (configDir) => {
21
+ const inputs = credentialInputs();
22
+ const selection = selectCredential({
23
+ apiKeyFlag: inputs.apiKeyFlag,
24
+ profileFlag: inputs.profileFlag,
25
+ apiKeyEnv: inputs.apiKeyEnv,
26
+ profileEnv: inputs.profileEnv
27
+ });
28
+ if (selection.source === "explicit-api-key" || selection.source === "ambient-api-key") return true;
29
+ const dir = configDir || inputs.configDir;
30
+ if (!dir) return false;
31
+ return listingHasCredentials(dir, selection.profile || "DEFAULT");
32
+ };
33
+ const collectDetectedAgents = (input) => uniqueAgentIds([
34
+ ...input.global,
35
+ ...input.project,
36
+ ...input.host === null ? [] : [input.host]
37
+ ]);
38
+ const detectInitEnvironment = async (options) => {
39
+ const names = options.directoryNames ?? (existsSync(options.cwd) ? readdirSync(options.cwd) : []);
40
+ const host = (options.detectAgent ?? detectAgent)();
41
+ const project = await (options.detectProjectAgents ?? detectProjectAgents)(options.cwd);
42
+ const globalAgents = await (options.detectInstalledAgents ?? detectInstalledAgents)();
43
+ const detectedAgents = collectDetectedAgents({
44
+ host,
45
+ project,
46
+ global: globalAgents
47
+ });
48
+ return {
49
+ authenticated: (options.hasLocalCredentials ?? hasLocalCredentials)(options.configDir),
50
+ emptyDirectory: directoryIsEmpty(names),
51
+ interactive: options.interactive ?? canPickAgentsInteractively(),
52
+ hostAgent: host,
53
+ detectedAgents,
54
+ ci: isCi()
55
+ };
56
+ };
57
+ //#endregion
58
+ export { collectDetectedAgents, detectInitEnvironment, hasLocalCredentials };
@@ -0,0 +1,115 @@
1
+ import pkg_default from "../pkg.js";
2
+ import { trackEvent } from "../analytics.js";
3
+ import { randomUUID } from "node:crypto";
4
+ //#region src/init/funnel.ts
5
+ const CLI_INIT_START = "cli_init_start";
6
+ const CLI_INIT_END = "cli_init_end";
7
+ const FLAG_ALIASES = {
8
+ "-y": "--yes",
9
+ "--yes": "--yes",
10
+ "--default": "--yes",
11
+ "-a": "--agent",
12
+ "--agent": "--agent",
13
+ "--no-link": "--no-link",
14
+ "--link": "--link",
15
+ "--no-config": "--no-config",
16
+ "--config": "--config",
17
+ "--services": "--services",
18
+ "--org-id": "--org-id",
19
+ "--project-id": "--project-id",
20
+ "--project-name": "--project-name",
21
+ "--region-id": "--region-id",
22
+ "--branch": "--branch",
23
+ "--branch-id": "--branch",
24
+ "--claimable": "--claimable",
25
+ "--package-manager": "--package-manager",
26
+ "--skill": "--skill",
27
+ "--mcp-config-location": "--mcp-config-location",
28
+ "--mcp-auth": "--mcp-auth",
29
+ "--mcp-project-scoped": "--mcp-project-scoped",
30
+ "--no-agent-setup": "--no-agent-setup",
31
+ "--agent-setup": "--agent-setup",
32
+ "--no-analytics": "--no-analytics",
33
+ "--analytics": "--analytics",
34
+ "--profile": "--profile",
35
+ "--config-dir": "--config-dir",
36
+ "--api-host": "--api-host",
37
+ "--context-file": "--context-file",
38
+ "--force-auth": "--force-auth",
39
+ "--no-force-auth": "--no-force-auth"
40
+ };
41
+ const flagName = (token) => {
42
+ const raw = token.split("=")[0];
43
+ if (raw === void 0) return;
44
+ return FLAG_ALIASES[raw];
45
+ };
46
+ const initFlagsFromArgv = (argv) => {
47
+ const initAt = argv.lastIndexOf("init");
48
+ const slice = initAt >= 0 ? argv.slice(initAt + 1) : argv;
49
+ const flags = [];
50
+ const seen = /* @__PURE__ */ new Set();
51
+ for (const token of slice) {
52
+ if (token === "--") break;
53
+ if (!token.startsWith("-")) continue;
54
+ const name = flagName(token);
55
+ if (name === void 0 || seen.has(name)) continue;
56
+ seen.add(name);
57
+ flags.push(name);
58
+ }
59
+ return flags;
60
+ };
61
+ const createInitRunId = () => randomUUID();
62
+ const initStartProperties = (input) => ({
63
+ init_run_id: input.initRunId,
64
+ cli_version: pkg_default.version,
65
+ flags: initFlagsFromArgv(input.argv ?? process.argv),
66
+ authenticated: input.detection.authenticated,
67
+ empty_directory: input.detection.emptyDirectory,
68
+ interactive: input.detection.interactive,
69
+ host_agent: input.detection.hostAgent,
70
+ detected_agents: input.detection.detectedAgents,
71
+ ci: input.detection.ci
72
+ });
73
+ const initEndProperties = (input) => ({
74
+ init_run_id: input.initRunId,
75
+ cli_version: pkg_default.version,
76
+ mode: input.mode,
77
+ agent_setup: input.agentSetup,
78
+ agents_installed: input.agentsInstalled,
79
+ link: input.link,
80
+ config: input.config,
81
+ services: input.services,
82
+ outcome: input.outcome
83
+ });
84
+ const emitInitStart = (properties) => {
85
+ trackEvent(CLI_INIT_START, properties);
86
+ };
87
+ const emitInitEnd = (properties) => {
88
+ trackEvent(CLI_INIT_END, properties);
89
+ };
90
+ const createInitFunnel = (initRunId = createInitRunId()) => ({
91
+ initRunId,
92
+ mode: null,
93
+ agentSetup: null,
94
+ agentsInstalled: [],
95
+ link: null,
96
+ config: null,
97
+ services: null,
98
+ ended: false
99
+ });
100
+ const finishInitFunnel = (state, outcome, analytics) => {
101
+ if (state.ended || !analytics) return;
102
+ state.ended = true;
103
+ emitInitEnd(initEndProperties({
104
+ initRunId: state.initRunId,
105
+ mode: state.mode,
106
+ agentSetup: state.agentSetup,
107
+ agentsInstalled: state.agentsInstalled,
108
+ link: state.link,
109
+ config: state.config,
110
+ services: state.services,
111
+ outcome
112
+ }));
113
+ };
114
+ //#endregion
115
+ export { CLI_INIT_END, CLI_INIT_START, createInitFunnel, createInitRunId, emitInitEnd, emitInitStart, finishInitFunnel, initEndProperties, initFlagsFromArgv, initStartProperties };
@@ -0,0 +1,30 @@
1
+ import { NON_TTY_AGENT_SETUP, NO_AGENT_SETUP_CONFLICT } from "./copy.js";
2
+ import { INIT_NEEDS_YES_OR_TERMINAL } from "./plan.js";
3
+ //#region src/init/mode.ts
4
+ const hasInitMcpFlags = (input) => input.mcpConfigLocation !== void 0 || input.mcpAuth !== void 0 || input.mcpProjectScoped === true;
5
+ const hasSkillsFlag = (skills) => skills !== void 0 && skills.length > 0;
6
+ const hasCustomizingFlags = (input) => input.skipAgents || input.claimable || hasSkillsFlag(input.skills) || hasInitMcpFlags(input);
7
+ const hasExistingSetupFlags = (input) => input.namedAgents || input.noLink || input.configFlag !== void 0 || input.services !== void 0 || input.hasLinkInputs;
8
+ const resolveInitMode = (input) => {
9
+ if (hasCustomizingFlags(input)) return { kind: "custom" };
10
+ if (input.yes) return { kind: "recommended" };
11
+ if (hasExistingSetupFlags(input)) return { kind: "custom" };
12
+ if (input.interactive) return { kind: "ask" };
13
+ throw new Error(INIT_NEEDS_YES_OR_TERMINAL);
14
+ };
15
+ const assertAgentSetupFlags = (input) => {
16
+ if (!input.skipAgents) return;
17
+ if (input.namedAgents || hasSkillsFlag(input.skills) || hasInitMcpFlags(input)) throw new Error(NO_AGENT_SETUP_CONFLICT);
18
+ };
19
+ const inferInitAgentSetup = (input) => {
20
+ if (input.skipAgents) return { kind: "skip" };
21
+ const hasSkills = hasSkillsFlag(input.skills);
22
+ if (hasSkills && input.hasMcpFlags) return { kind: "skills-mcp" };
23
+ if (hasSkills) return { kind: "skills" };
24
+ if (input.hasMcpFlags) return { kind: "skills-mcp" };
25
+ if (input.namedAgents || input.yes) return { kind: "auto" };
26
+ if (input.canAsk) return { kind: "ask" };
27
+ throw new Error(NON_TTY_AGENT_SETUP);
28
+ };
29
+ //#endregion
30
+ export { assertAgentSetupFlags, hasInitMcpFlags, inferInitAgentSetup, resolveInitMode };
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;
@@ -95,7 +121,7 @@ const BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
95
121
  "--no-agent-setup",
96
122
  "--no-link",
97
123
  "--no-config",
98
- "--skip-template",
124
+ "--claimable",
99
125
  "--config",
100
126
  "--list",
101
127
  "--list-templates",
@@ -174,6 +200,104 @@ const chooseYesAgentTooling = (agents) => {
174
200
  mcpAgents
175
201
  };
176
202
  };
203
+ const FALLBACK_SKILLS_AGENTS = ["cursor", "codex"];
204
+ const splitInitTooling = (agents, pluginScope, mcpConfigLocation = pluginScope) => {
205
+ const pluginSet = new Set(pluginsInstallableAgents(pluginScope));
206
+ const mcpSet = new Set(mcpInstallableAgents(mcpConfigLocation));
207
+ const pluginAgents = uniqueAgentIds(agents.filter((id) => pluginSet.has(id)));
208
+ const rest = uniqueAgentIds(agents.filter((id) => !pluginSet.has(id)));
209
+ const skillsAgents = rest.filter((id) => supportsSkills(id));
210
+ const mcpAgents = rest.filter((id) => mcpSet.has(id));
211
+ const [pluginFirst, ...pluginRest] = pluginAgents;
212
+ if (pluginFirst !== void 0 && (skillsAgents.length > 0 || mcpAgents.length > 0)) return {
213
+ setup: "mixed",
214
+ pluginAgents: [pluginFirst, ...pluginRest],
215
+ skillsAgents,
216
+ mcpAgents
217
+ };
218
+ if (pluginFirst !== void 0) return {
219
+ setup: "plugin",
220
+ agents: [pluginFirst, ...pluginRest]
221
+ };
222
+ if (skillsAgents.length === 0 && mcpAgents.length === 0) return { setup: "skip" };
223
+ return {
224
+ setup: "skills-mcp",
225
+ skillsAgents,
226
+ mcpAgents
227
+ };
228
+ };
229
+ const recommendedTooling = (detected) => {
230
+ const split = splitInitTooling(detected, "global");
231
+ if (split.setup === "skip") return {
232
+ setup: "skills",
233
+ agents: FALLBACK_SKILLS_AGENTS
234
+ };
235
+ return split;
236
+ };
237
+ const planInitToolingSteps = (input) => {
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
+ });
256
+ switch (input.tooling.setup) {
257
+ case "skip": return [];
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
+ }];
270
+ case "skills-mcp": {
271
+ const steps = [];
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
+ });
280
+ return steps;
281
+ }
282
+ case "mixed": {
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
+ });
295
+ return steps;
296
+ }
297
+ default: return input.tooling;
298
+ }
299
+ };
300
+ const funnelAgentSetup = (tooling) => tooling.setup;
177
301
  const planYesAgentSteps = (tooling) => planToolingSteps(tooling, {
178
302
  yes: true,
179
303
  named: false
@@ -208,7 +332,7 @@ const assertNamedAgentTooling = (named, command = "init", context) => {
208
332
  if (named.length === 0) return;
209
333
  const tooling = chooseYesAgentTooling(named);
210
334
  if (tooling.setup === "skip") throw new Error(`${NAMED_AGENTS_UNSUPPORTED} Supported agents: ${initYesSupportedAgents().join(", ")}`);
211
- if (namedAgentsNeedSplit(named, tooling)) throw new Error(namedAgentsMixedMessage(named, command, context));
335
+ if (command === "bootstrap" && namedAgentsNeedSplit(named, tooling)) throw new Error(namedAgentsMixedMessage(named, command, context));
212
336
  };
213
337
  const resolveNamedAgents = (raw) => {
214
338
  if (raw.length === 0) return [];
@@ -252,30 +376,9 @@ const linkInputArgv = (input) => {
252
376
  if (input.branch) args.push("--branch", input.branch);
253
377
  return args;
254
378
  };
255
- const planExistingInit = (input) => {
256
- const steps = [...planAgentSteps(input)];
257
- if (!input.linked) steps.push(planLinkStep({
258
- yes: input.yes,
259
- extra: input.linkExtra
260
- }));
261
- if (input.config.kind === "write") steps.push(planConfigInitStep({
262
- yes: input.yes,
263
- services: input.config.services
264
- }));
265
- return steps;
266
- };
267
379
  const resolveInitAgentSetup = async (input) => {
268
380
  if (input.interactive) return input.pick();
269
381
  throw new Error(INIT_NEEDS_YES_OR_TERMINAL);
270
382
  };
271
- const childArgv = (step, forward) => {
272
- const args = [...step];
273
- if (forward.configDir) args.push("--config-dir", forward.configDir);
274
- if (forward.profile) args.push("--profile", forward.profile);
275
- args.push("--api-host", forward.apiHost);
276
- args.push("--context-file", forward.contextFile);
277
- if (forward.analytics === false) args.push("--no-analytics");
278
- return args;
279
- };
280
383
  //#endregion
281
- export { INIT_NEEDS_YES_OR_TERMINAL, NAMED_AGENTS_MIXED, NAMED_AGENTS_UNSUPPORTED, assertNamedAgentTooling, bootstrapInitStep, childArgv, chooseYesAgentTooling, collectYesAgents, directoryIsEmpty, initPluginAgents, initSkillsMcpAgents, initYesSupportedAgents, linkInputArgv, namedAgentsMixedMessage, namedAgentsNeedSplit, noDetectedAgentsMessage, planAgentSteps, planConfigInitStep, planExistingInit, planLinkStep, planToolingSteps, planYesAgentSteps, postScaffoldActions, projectContextFile, resolveInitAgentSetup, resolveNamedAgents, resolveYesAgentList, rewriteUnknownAgentArg };
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 };