neon 5.0.1 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -26
- package/dist/commands/config.js +38 -17
- package/dist/commands/init.js +44 -299
- package/dist/commands/mcp.js +1 -16
- package/dist/commands/plugins.js +2 -12
- package/dist/commands/skills.js +2 -12
- package/dist/index.js +8 -0
- package/dist/init/cancelled.js +16 -0
- package/dist/init/child.js +16 -4
- package/dist/init/choices.js +10 -21
- package/dist/init/chrome.js +71 -19
- package/dist/init/copy.js +133 -0
- package/dist/init/detect.js +58 -0
- package/dist/init/funnel.js +115 -0
- package/dist/init/mode.js +30 -0
- package/dist/init/plan.js +84 -3
- package/dist/init/run.js +641 -0
- package/dist/init/tooling.js +1 -1
- package/dist/init/wizard.js +207 -48
- package/dist/mcp/wizard.js +1 -27
- package/dist/skills/wizard.js +1 -25
- package/dist/test_utils/npm_env.js +26 -0
- package/dist/update_check_worker.js +47 -0
- package/dist/update_notifier.js +145 -0
- package/dist/utils/package_manager.js +8 -1
- package/dist/utils/service_picker.js +34 -8
- package/package.json +7 -5
- package/dist/plugins/wizard.js +0 -36
package/dist/init/chrome.js
CHANGED
|
@@ -1,31 +1,46 @@
|
|
|
1
1
|
import { isCi } from "../env.js";
|
|
2
|
+
import { HEADING_CANCELLED, HEADING_COMPLETE, HEADING_FAILED, HEADING_PENDING, INIT_SUBTITLE, PROGRESS } 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(
|
|
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
|
|
27
|
-
|
|
28
|
-
|
|
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,37 @@ const configSummaryLabel = (summary) => {
|
|
|
49
83
|
default: return summary;
|
|
50
84
|
}
|
|
51
85
|
};
|
|
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;
|
|
91
|
+
};
|
|
52
92
|
const INIT_STEP_LABELS = {
|
|
53
|
-
bootstrap: "Creating the app from the selected template
|
|
54
|
-
plugins:
|
|
55
|
-
skills:
|
|
56
|
-
mcp:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
93
|
+
bootstrap: "Creating the app from the selected template...",
|
|
94
|
+
plugins: PROGRESS.plugins,
|
|
95
|
+
skills: PROGRESS.skills,
|
|
96
|
+
mcp: PROGRESS.mcp,
|
|
97
|
+
auth: PROGRESS.auth,
|
|
98
|
+
link: PROGRESS.link,
|
|
99
|
+
claim: PROGRESS.claim,
|
|
100
|
+
config: PROGRESS.config,
|
|
101
|
+
env: PROGRESS.env
|
|
60
102
|
};
|
|
61
103
|
const initStepLabel = (step) => {
|
|
62
104
|
const command = step[0];
|
|
63
105
|
if (command === void 0) return;
|
|
106
|
+
if (command === "config") return INIT_STEP_LABELS.config;
|
|
64
107
|
return INIT_STEP_LABELS[command];
|
|
65
108
|
};
|
|
109
|
+
const headingForKind = (kind) => {
|
|
110
|
+
switch (kind) {
|
|
111
|
+
case "success": return HEADING_COMPLETE;
|
|
112
|
+
case "pending": return HEADING_PENDING;
|
|
113
|
+
case "error": return HEADING_FAILED;
|
|
114
|
+
case "aborted": return HEADING_CANCELLED;
|
|
115
|
+
default: return kind;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
66
118
|
//#endregion
|
|
67
|
-
export { INIT_BANNER_LINES, INIT_STEP_LABELS, NEON_GREEN, agentSetupDoneLabel, agentSetupLabel, configSummaryLabel, formatInitBanner, formatInitDone, initStepLabel, printInitBanner, printInitDone, shouldPrintInitBanner };
|
|
119
|
+
export { INIT_BANNER_LINES, INIT_STEP_LABELS, NEON_GREEN, agentSetupDoneLabel, agentSetupLabel, agentsRowValue, configRowValue, configSummaryLabel, formatInitBanner, formatInitDone, headingForKind, initStepLabel, 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 { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
9
|
+
import { detectAgent } from "./detect_host.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
|
@@ -95,7 +95,7 @@ const BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
95
95
|
"--no-agent-setup",
|
|
96
96
|
"--no-link",
|
|
97
97
|
"--no-config",
|
|
98
|
-
"--
|
|
98
|
+
"--claimable",
|
|
99
99
|
"--config",
|
|
100
100
|
"--list",
|
|
101
101
|
"--list-templates",
|
|
@@ -174,6 +174,87 @@ const chooseYesAgentTooling = (agents) => {
|
|
|
174
174
|
mcpAgents
|
|
175
175
|
};
|
|
176
176
|
};
|
|
177
|
+
const FALLBACK_SKILLS_AGENTS = ["cursor", "codex"];
|
|
178
|
+
const splitInitTooling = (agents, pluginScope, mcpConfigLocation = pluginScope) => {
|
|
179
|
+
const pluginSet = new Set(pluginsInstallableAgents(pluginScope));
|
|
180
|
+
const mcpSet = new Set(mcpInstallableAgents(mcpConfigLocation));
|
|
181
|
+
const pluginAgents = uniqueAgentIds(agents.filter((id) => pluginSet.has(id)));
|
|
182
|
+
const rest = uniqueAgentIds(agents.filter((id) => !pluginSet.has(id)));
|
|
183
|
+
const skillsAgents = rest.filter((id) => supportsSkills(id));
|
|
184
|
+
const mcpAgents = rest.filter((id) => mcpSet.has(id));
|
|
185
|
+
const [pluginFirst, ...pluginRest] = pluginAgents;
|
|
186
|
+
if (pluginFirst !== void 0 && (skillsAgents.length > 0 || mcpAgents.length > 0)) return {
|
|
187
|
+
setup: "mixed",
|
|
188
|
+
pluginAgents: [pluginFirst, ...pluginRest],
|
|
189
|
+
skillsAgents,
|
|
190
|
+
mcpAgents
|
|
191
|
+
};
|
|
192
|
+
if (pluginFirst !== void 0) return {
|
|
193
|
+
setup: "plugin",
|
|
194
|
+
agents: [pluginFirst, ...pluginRest]
|
|
195
|
+
};
|
|
196
|
+
if (skillsAgents.length === 0 && mcpAgents.length === 0) return { setup: "skip" };
|
|
197
|
+
return {
|
|
198
|
+
setup: "skills-mcp",
|
|
199
|
+
skillsAgents,
|
|
200
|
+
mcpAgents
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
const recommendedTooling = (detected) => {
|
|
204
|
+
const split = splitInitTooling(detected, "global");
|
|
205
|
+
if (split.setup === "skip") return {
|
|
206
|
+
setup: "skills",
|
|
207
|
+
agents: FALLBACK_SKILLS_AGENTS
|
|
208
|
+
};
|
|
209
|
+
return split;
|
|
210
|
+
};
|
|
211
|
+
const planInitToolingSteps = (input) => {
|
|
212
|
+
const y = input.yes ? ["-y"] : [];
|
|
213
|
+
const pluginPrefix = [
|
|
214
|
+
"plugins",
|
|
215
|
+
...input.pluginScope === "global" ? ["--global"] : [],
|
|
216
|
+
...y
|
|
217
|
+
];
|
|
218
|
+
const skillFlags = (input.skills ?? []).flatMap((skill) => ["--skill", skill]);
|
|
219
|
+
const skillsPrefix = [
|
|
220
|
+
"skills",
|
|
221
|
+
...input.skillsGlobal ? ["--global"] : [],
|
|
222
|
+
...y,
|
|
223
|
+
...skillFlags
|
|
224
|
+
];
|
|
225
|
+
const mcpPrefix = [
|
|
226
|
+
"mcp",
|
|
227
|
+
...y,
|
|
228
|
+
...input.mcpOauth ? ["--oauth"] : [],
|
|
229
|
+
...input.mcpProject === true ? ["--project"] : [],
|
|
230
|
+
...input.mcpProjectId !== void 0 ? ["--project-id", input.mcpProjectId] : []
|
|
231
|
+
];
|
|
232
|
+
const named = (ids) => agentArgv(ids);
|
|
233
|
+
switch (input.tooling.setup) {
|
|
234
|
+
case "skip": return [];
|
|
235
|
+
case "plugin": return [[...pluginPrefix, ...named(input.tooling.agents)]];
|
|
236
|
+
case "skills": return [[
|
|
237
|
+
"skills",
|
|
238
|
+
...y,
|
|
239
|
+
...skillFlags,
|
|
240
|
+
...named(input.tooling.agents)
|
|
241
|
+
]];
|
|
242
|
+
case "skills-mcp": {
|
|
243
|
+
const steps = [];
|
|
244
|
+
if (input.tooling.skillsAgents.length > 0) steps.push([...skillsPrefix, ...named(input.tooling.skillsAgents)]);
|
|
245
|
+
if (input.tooling.mcpAgents.length > 0) steps.push([...mcpPrefix, ...named(input.tooling.mcpAgents)]);
|
|
246
|
+
return steps;
|
|
247
|
+
}
|
|
248
|
+
case "mixed": {
|
|
249
|
+
const steps = [[...pluginPrefix, ...named(input.tooling.pluginAgents)]];
|
|
250
|
+
if (input.tooling.skillsAgents.length > 0) steps.push([...skillsPrefix, ...named(input.tooling.skillsAgents)]);
|
|
251
|
+
if (input.tooling.mcpAgents.length > 0) steps.push([...mcpPrefix, ...named(input.tooling.mcpAgents)]);
|
|
252
|
+
return steps;
|
|
253
|
+
}
|
|
254
|
+
default: return input.tooling;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
const funnelAgentSetup = (tooling) => tooling.setup;
|
|
177
258
|
const planYesAgentSteps = (tooling) => planToolingSteps(tooling, {
|
|
178
259
|
yes: true,
|
|
179
260
|
named: false
|
|
@@ -208,7 +289,7 @@ const assertNamedAgentTooling = (named, command = "init", context) => {
|
|
|
208
289
|
if (named.length === 0) return;
|
|
209
290
|
const tooling = chooseYesAgentTooling(named);
|
|
210
291
|
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));
|
|
292
|
+
if (command === "bootstrap" && namedAgentsNeedSplit(named, tooling)) throw new Error(namedAgentsMixedMessage(named, command, context));
|
|
212
293
|
};
|
|
213
294
|
const resolveNamedAgents = (raw) => {
|
|
214
295
|
if (raw.length === 0) return [];
|
|
@@ -278,4 +359,4 @@ const childArgv = (step, forward) => {
|
|
|
278
359
|
return args;
|
|
279
360
|
};
|
|
280
361
|
//#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 };
|
|
362
|
+
export { FALLBACK_SKILLS_AGENTS, INIT_NEEDS_YES_OR_TERMINAL, NAMED_AGENTS_MIXED, NAMED_AGENTS_UNSUPPORTED, assertNamedAgentTooling, bootstrapInitStep, childArgv, chooseYesAgentTooling, collectYesAgents, directoryIsEmpty, funnelAgentSetup, initPluginAgents, initSkillsMcpAgents, initYesSupportedAgents, linkInputArgv, namedAgentsMixedMessage, namedAgentsNeedSplit, noDetectedAgentsMessage, planAgentSteps, planConfigInitStep, planExistingInit, planInitToolingSteps, planLinkStep, planToolingSteps, planYesAgentSteps, postScaffoldActions, projectContextFile, recommendedTooling, resolveInitAgentSetup, resolveNamedAgents, resolveYesAgentList, rewriteUnknownAgentArg, splitInitTooling };
|