neon 4.10.1 → 4.11.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 +33 -22
- package/dist/commands/bootstrap.js +201 -89
- package/dist/commands/checkout.js +0 -1
- package/dist/commands/init.js +45 -15
- package/dist/commands/link.js +2 -10
- package/dist/commands/mcp.js +4 -4
- package/dist/commands/plugins.js +4 -4
- package/dist/commands/skills.js +12 -9
- package/dist/errors.js +2 -1
- package/dist/index.js +17 -2
- package/dist/init/chrome.js +44 -0
- package/dist/init/detect_host.js +38 -0
- package/dist/init/plan.js +204 -5
- package/dist/init/tooling.js +48 -12
- package/dist/init/wizard.js +2 -1
- package/dist/mcp/plan.js +16 -3
- package/dist/plugins/plan.js +18 -4
- package/dist/plugins/targets.js +1 -1
- package/dist/skills/catalog.js +0 -5
- package/dist/skills/plan.js +19 -5
- package/dist/skills/targets.js +1 -1
- package/dist/skills/wizard.js +1 -1
- package/dist/utils/agent_flag.js +13 -0
- package/dist/utils/agent_picker.js +2 -2
- package/package.json +3 -3
package/dist/init/plan.js
CHANGED
|
@@ -1,23 +1,223 @@
|
|
|
1
|
+
import { getCliName } from "../utils/cli_name.js";
|
|
2
|
+
import { tryResolveAddMcpAgentId, uniqueAgentIds } from "../mcp/agents.js";
|
|
3
|
+
import { mcpInstallableAgents } from "../mcp/targets.js";
|
|
4
|
+
import { supportsSkills } from "./agents.js";
|
|
5
|
+
import { pluginsInstallableAgents } from "../plugins/targets.js";
|
|
6
|
+
import { skillsInstallableAgents } from "../skills/targets.js";
|
|
7
|
+
import { agentArgv } from "../utils/agent_flag.js";
|
|
1
8
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
2
9
|
//#region src/init/plan.ts
|
|
3
10
|
const directoryIsEmpty = (names) => names.filter((name) => name !== ".git").length === 0;
|
|
4
|
-
const bootstrapInitStep = (yes) => yes ? [
|
|
11
|
+
const bootstrapInitStep = (yes, agents = []) => [...yes ? [
|
|
5
12
|
"bootstrap",
|
|
6
13
|
".",
|
|
7
14
|
"--default"
|
|
8
|
-
] : ["bootstrap", "."];
|
|
15
|
+
] : ["bootstrap", "."], ...agentArgv(agents)];
|
|
9
16
|
const projectContextFile = (projectDir, contextFile) => {
|
|
10
17
|
const resolved = isAbsolute(contextFile) ? contextFile : resolve(projectDir, contextFile);
|
|
11
18
|
const rel = relative(projectDir, resolved);
|
|
12
19
|
if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) return join(projectDir, ".neon");
|
|
13
20
|
return resolved;
|
|
14
21
|
};
|
|
22
|
+
/** Linking a template with neon.ts requires installed dependencies because env pull evaluates it. */
|
|
23
|
+
const postScaffoldActions = (input) => {
|
|
24
|
+
const actions = [];
|
|
25
|
+
if (input.git) actions.push("git");
|
|
26
|
+
if (input.agentSetup !== "skip") actions.push("agent");
|
|
27
|
+
const canLink = input.link && !(input.hasNeonConfig && !input.install);
|
|
28
|
+
if (canLink && input.hasNeonConfig && input.install) {
|
|
29
|
+
actions.push("install");
|
|
30
|
+
actions.push("link");
|
|
31
|
+
return actions;
|
|
32
|
+
}
|
|
33
|
+
if (canLink) actions.push("link");
|
|
34
|
+
if (input.install) actions.push("install");
|
|
35
|
+
return actions;
|
|
36
|
+
};
|
|
15
37
|
const planAgentSteps = (input) => {
|
|
16
38
|
const y = input.yes ? ["-y"] : [];
|
|
17
39
|
if (input.agentSetup === "plugin") return [["plugins", ...y]];
|
|
18
40
|
if (input.agentSetup === "skills-mcp") return [["skills", ...y], ["mcp", ...y]];
|
|
19
41
|
return [];
|
|
20
42
|
};
|
|
43
|
+
const planToolingSteps = (tooling, options) => {
|
|
44
|
+
const prefix = options.yes ? ["-y"] : [];
|
|
45
|
+
const flagsFor = (ids) => options.named ? [...prefix, ...agentArgv(ids)] : [...prefix];
|
|
46
|
+
switch (tooling.setup) {
|
|
47
|
+
case "skip": return [];
|
|
48
|
+
case "plugin": return [["plugins", ...flagsFor(tooling.agents)]];
|
|
49
|
+
case "skills-mcp": {
|
|
50
|
+
const steps = [];
|
|
51
|
+
if (tooling.skillsAgents.length > 0) steps.push(["skills", ...flagsFor(tooling.skillsAgents)]);
|
|
52
|
+
if (tooling.mcpAgents.length > 0) steps.push(["mcp", ...flagsFor(tooling.mcpAgents)]);
|
|
53
|
+
return steps;
|
|
54
|
+
}
|
|
55
|
+
default: return tooling;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const noDetectedAgentsMessage = (input) => {
|
|
59
|
+
const lead = input.scope === "project" ? "No coding agents detected in this project." : "No coding agents detected.";
|
|
60
|
+
const name = input.nameAgent === true;
|
|
61
|
+
let hint;
|
|
62
|
+
if (input.fix === "pass-yes") {
|
|
63
|
+
const detected = input.scope === "project" ? "Pass -y to use project folders, else the host CLI agent" : "Pass -y to use installed apps, else the host CLI agent";
|
|
64
|
+
hint = name ? `${detected}, --agent <name> to name them, or run from a terminal to pick one.` : `${detected}, or run from a terminal to pick one.`;
|
|
65
|
+
} else hint = name ? "Pass --agent <name>, run this command from a supported agent, or omit -y in a terminal to pick one." : "Run this command from a supported agent, or omit -y in a terminal to pick one.";
|
|
66
|
+
return `${lead} ${hint} Supported agents: ${input.supported.join(", ")}`;
|
|
67
|
+
};
|
|
68
|
+
const INIT_NEEDS_YES_OR_TERMINAL = "No interactive terminal. Pass -y to use defaults, or run this command in a terminal to pick.";
|
|
69
|
+
const UNKNOWN_AGENT_COMMANDS = ["link"];
|
|
70
|
+
const unknownAgentArgHint = (command, cliName) => `${cliName} ${command} has no --agent. Pass --project-id <id> to link without a TTY, or run ${cliName} ${command} in a terminal.`;
|
|
71
|
+
const BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
72
|
+
"-y",
|
|
73
|
+
"--yes",
|
|
74
|
+
"--default",
|
|
75
|
+
"--force",
|
|
76
|
+
"-h",
|
|
77
|
+
"--help",
|
|
78
|
+
"-v",
|
|
79
|
+
"--version",
|
|
80
|
+
"--color",
|
|
81
|
+
"--no-color",
|
|
82
|
+
"--analytics",
|
|
83
|
+
"--no-analytics",
|
|
84
|
+
"--oauth",
|
|
85
|
+
"--project",
|
|
86
|
+
"--global",
|
|
87
|
+
"--read-only",
|
|
88
|
+
"--readonly",
|
|
89
|
+
"--clear",
|
|
90
|
+
"--checks",
|
|
91
|
+
"--no-checks",
|
|
92
|
+
"--env-pull",
|
|
93
|
+
"--no-env-pull",
|
|
94
|
+
"--no-agent-setup",
|
|
95
|
+
"--no-link",
|
|
96
|
+
"--list",
|
|
97
|
+
"--list-templates",
|
|
98
|
+
"--force-auth",
|
|
99
|
+
"--no-force-auth"
|
|
100
|
+
]);
|
|
101
|
+
const isUnknownAgentCommand = (token) => {
|
|
102
|
+
for (const command of UNKNOWN_AGENT_COMMANDS) if (command === token) return true;
|
|
103
|
+
return false;
|
|
104
|
+
};
|
|
105
|
+
const commandFromArgv = (argv) => {
|
|
106
|
+
let skipValue = false;
|
|
107
|
+
for (const token of argv) {
|
|
108
|
+
if (skipValue) {
|
|
109
|
+
skipValue = false;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (token === "--") break;
|
|
113
|
+
if (token.startsWith("-")) {
|
|
114
|
+
const flag = token.split("=")[0];
|
|
115
|
+
if (flag !== void 0 && !token.includes("=") && !BOOLEAN_FLAGS.has(flag)) skipValue = true;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (isUnknownAgentCommand(token)) return token;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const rewriteUnknownAgentArg = (input) => {
|
|
122
|
+
if (input.message !== "Unknown argument: agent" && input.message !== "Unknown argument: a") return;
|
|
123
|
+
const command = commandFromArgv(input.argv);
|
|
124
|
+
if (command === void 0) return;
|
|
125
|
+
return unknownAgentArgHint(command, input.cliName);
|
|
126
|
+
};
|
|
127
|
+
const initYesSupportedAgents = () => uniqueAgentIds([
|
|
128
|
+
...pluginsInstallableAgents("project"),
|
|
129
|
+
...skillsInstallableAgents(),
|
|
130
|
+
...mcpInstallableAgents("global")
|
|
131
|
+
]);
|
|
132
|
+
const initPluginAgents = () => pluginsInstallableAgents("project");
|
|
133
|
+
const initSkillsMcpAgents = () => {
|
|
134
|
+
const plugin = new Set(initPluginAgents());
|
|
135
|
+
return initYesSupportedAgents().filter((id) => !plugin.has(id));
|
|
136
|
+
};
|
|
137
|
+
const resolveYesAgentList = (input) => {
|
|
138
|
+
if (input.detected.length > 0) return uniqueAgentIds(input.detected);
|
|
139
|
+
if (input.host !== null) return [input.host];
|
|
140
|
+
return [];
|
|
141
|
+
};
|
|
142
|
+
async function collectYesAgents(sources) {
|
|
143
|
+
const detected = await sources.detected();
|
|
144
|
+
if (detected.length > 0) return resolveYesAgentList({
|
|
145
|
+
detected,
|
|
146
|
+
host: null
|
|
147
|
+
});
|
|
148
|
+
const rawHost = sources.detectAgent();
|
|
149
|
+
const accept = sources.acceptHost ?? (() => true);
|
|
150
|
+
const host = rawHost !== null && accept(rawHost) ? rawHost : null;
|
|
151
|
+
return resolveYesAgentList({
|
|
152
|
+
detected,
|
|
153
|
+
host
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const chooseYesAgentTooling = (agents) => {
|
|
157
|
+
const projectPlugin = new Set(pluginsInstallableAgents("project"));
|
|
158
|
+
const [pluginFirst, ...pluginRest] = agents.filter((id) => projectPlugin.has(id));
|
|
159
|
+
if (pluginFirst !== void 0) return {
|
|
160
|
+
setup: "plugin",
|
|
161
|
+
agents: [pluginFirst, ...pluginRest]
|
|
162
|
+
};
|
|
163
|
+
const globalMcp = new Set(mcpInstallableAgents("global"));
|
|
164
|
+
const skillsAgents = agents.filter((id) => supportsSkills(id));
|
|
165
|
+
const mcpAgents = agents.filter((id) => globalMcp.has(id));
|
|
166
|
+
if (skillsAgents.length === 0 && mcpAgents.length === 0) return { setup: "skip" };
|
|
167
|
+
return {
|
|
168
|
+
setup: "skills-mcp",
|
|
169
|
+
skillsAgents,
|
|
170
|
+
mcpAgents
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
const planYesAgentSteps = (tooling) => planToolingSteps(tooling, {
|
|
174
|
+
yes: true,
|
|
175
|
+
named: false
|
|
176
|
+
});
|
|
177
|
+
const NAMED_AGENTS_UNSUPPORTED = "None of the selected agents can install the Neon plugin, skills, or MCP.";
|
|
178
|
+
const NAMED_AGENTS_MIXED = "Cannot install the plugin and skills/MCP in one run.";
|
|
179
|
+
const quoteIfNeeded = (value) => /[\s"'\\]/.test(value) ? `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"` : value;
|
|
180
|
+
const mixedRerun = (command, ids, context) => {
|
|
181
|
+
const tokens = [getCliName(), command];
|
|
182
|
+
if (command === "bootstrap" && context?.directory !== void 0 && context.directory.length > 0) tokens.push(quoteIfNeeded(context.directory));
|
|
183
|
+
if (context?.yes === true) tokens.push(command === "bootstrap" ? "--default" : "-y");
|
|
184
|
+
tokens.push(...ids.flatMap((id) => ["--agent", id]));
|
|
185
|
+
return `\`${tokens.join(" ")}\``;
|
|
186
|
+
};
|
|
187
|
+
const namedAgentsMixedMessage = (named, command, context) => {
|
|
188
|
+
const plugin = new Set(initPluginAgents());
|
|
189
|
+
const pluginNamed = uniqueAgentIds(named.filter((id) => plugin.has(id)));
|
|
190
|
+
const otherNamed = uniqueAgentIds(named.filter((id) => !plugin.has(id)));
|
|
191
|
+
return `${NAMED_AGENTS_MIXED} Plugin: ${pluginNamed.join(", ")}. Skills/MCP: ${otherNamed.join(", ")}. Re-run ${mixedRerun(command, pluginNamed, context)} or ${mixedRerun(command, otherNamed, context)}.`;
|
|
192
|
+
};
|
|
193
|
+
const namedAgentsNeedSplit = (named, tooling) => {
|
|
194
|
+
if (tooling.setup === "skip") return false;
|
|
195
|
+
if (tooling.setup === "plugin") {
|
|
196
|
+
const used = new Set(tooling.agents);
|
|
197
|
+
const leftover = named.filter((id) => !used.has(id));
|
|
198
|
+
return chooseYesAgentTooling(leftover).setup !== "skip";
|
|
199
|
+
}
|
|
200
|
+
const used = /* @__PURE__ */ new Set([...tooling.skillsAgents, ...tooling.mcpAgents]);
|
|
201
|
+
return named.some((id) => !used.has(id));
|
|
202
|
+
};
|
|
203
|
+
const assertNamedAgentTooling = (named, command = "init", context) => {
|
|
204
|
+
if (named.length === 0) return;
|
|
205
|
+
const tooling = chooseYesAgentTooling(named);
|
|
206
|
+
if (tooling.setup === "skip") throw new Error(`${NAMED_AGENTS_UNSUPPORTED} Supported agents: ${initYesSupportedAgents().join(", ")}`);
|
|
207
|
+
if (namedAgentsNeedSplit(named, tooling)) throw new Error(namedAgentsMixedMessage(named, command, context));
|
|
208
|
+
};
|
|
209
|
+
const resolveNamedAgents = (raw) => {
|
|
210
|
+
if (raw.length === 0) return [];
|
|
211
|
+
const available = initYesSupportedAgents();
|
|
212
|
+
const resolved = [];
|
|
213
|
+
for (const item of raw) {
|
|
214
|
+
if (item === "*") throw new Error("--agent * is not accepted. Pass --agent <name> for each coding agent, or omit --agent to detect or pick.");
|
|
215
|
+
const id = tryResolveAddMcpAgentId(item);
|
|
216
|
+
if (!id) throw new Error(`Unknown agent: "${item}". Supported agents: ${available.join(", ")}`);
|
|
217
|
+
resolved.push(id);
|
|
218
|
+
}
|
|
219
|
+
return uniqueAgentIds(resolved);
|
|
220
|
+
};
|
|
21
221
|
const planExistingInit = (input) => {
|
|
22
222
|
const steps = [...planAgentSteps(input)];
|
|
23
223
|
if (!input.linked) steps.push(input.yes ? ["link", "--yes"] : ["link"]);
|
|
@@ -30,9 +230,8 @@ const planExistingInit = (input) => {
|
|
|
30
230
|
return steps;
|
|
31
231
|
};
|
|
32
232
|
const resolveInitAgentSetup = async (input) => {
|
|
33
|
-
if (input.yes) return input.hasProjectPlugins ? "plugin" : "skills-mcp";
|
|
34
233
|
if (input.interactive) return input.pick();
|
|
35
|
-
|
|
234
|
+
throw new Error(INIT_NEEDS_YES_OR_TERMINAL);
|
|
36
235
|
};
|
|
37
236
|
const childArgv = (step, forward) => {
|
|
38
237
|
const args = [...step];
|
|
@@ -44,4 +243,4 @@ const childArgv = (step, forward) => {
|
|
|
44
243
|
return args;
|
|
45
244
|
};
|
|
46
245
|
//#endregion
|
|
47
|
-
export { bootstrapInitStep, childArgv, directoryIsEmpty, planAgentSteps, planExistingInit, projectContextFile, resolveInitAgentSetup };
|
|
246
|
+
export { INIT_NEEDS_YES_OR_TERMINAL, NAMED_AGENTS_MIXED, NAMED_AGENTS_UNSUPPORTED, assertNamedAgentTooling, bootstrapInitStep, childArgv, chooseYesAgentTooling, collectYesAgents, directoryIsEmpty, initPluginAgents, initSkillsMcpAgents, initYesSupportedAgents, namedAgentsMixedMessage, namedAgentsNeedSplit, noDetectedAgentsMessage, planAgentSteps, planExistingInit, planToolingSteps, planYesAgentSteps, postScaffoldActions, projectContextFile, resolveInitAgentSetup, resolveNamedAgents, resolveYesAgentList, rewriteUnknownAgentArg };
|
package/dist/init/tooling.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { log } from "../log.js";
|
|
2
2
|
import { getCliName } from "../utils/cli_name.js";
|
|
3
3
|
import { AUTH_CHILD } from "./child.js";
|
|
4
|
-
import { childArgv, planAgentSteps, resolveInitAgentSetup } from "./plan.js";
|
|
5
|
-
import { detectInstallablePluginsAgents } from "../plugins/targets.js";
|
|
4
|
+
import { assertNamedAgentTooling, childArgv, chooseYesAgentTooling, collectYesAgents, initYesSupportedAgents, noDetectedAgentsMessage, planAgentSteps, planToolingSteps, planYesAgentSteps, resolveInitAgentSetup } from "./plan.js";
|
|
6
5
|
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
6
|
+
import { detectAgent } from "./detect_host.js";
|
|
7
7
|
import { pickAgentSetupInteractively } from "./wizard.js";
|
|
8
|
+
import { detectProjectAgents } from "add-mcp";
|
|
8
9
|
//#region src/init/tooling.ts
|
|
9
|
-
const
|
|
10
|
-
return (await detectInstallablePluginsAgents({
|
|
11
|
-
scope: "project",
|
|
12
|
-
cwd
|
|
13
|
-
})).length > 0;
|
|
14
|
-
};
|
|
10
|
+
const defaultProjectAgents = (cwd) => detectProjectAgents(cwd);
|
|
15
11
|
const runInitSteps = async (steps, options) => {
|
|
16
12
|
for (const step of steps) {
|
|
17
13
|
log.info("Running `%s %s`", getCliName(), step.join(" "));
|
|
@@ -20,20 +16,60 @@ const runInitSteps = async (steps, options) => {
|
|
|
20
16
|
if (!await options.run(argv, options.cwd, command !== void 0 && AUTH_CHILD.has(command) ? options.authEnv : void 0)) throw new Error(`\`${getCliName()} ${step.join(" ")}\` failed.`);
|
|
21
17
|
}
|
|
22
18
|
};
|
|
19
|
+
const yesAgentsFromOptions = async (options) => collectYesAgents({
|
|
20
|
+
detected: () => (options.detectProjectAgents ?? defaultProjectAgents)(options.cwd),
|
|
21
|
+
detectAgent: options.detectAgent ?? detectAgent
|
|
22
|
+
});
|
|
23
|
+
const yesMiss = () => new Error(noDetectedAgentsMessage({
|
|
24
|
+
scope: "project",
|
|
25
|
+
supported: initYesSupportedAgents(),
|
|
26
|
+
fix: "run-without-yes",
|
|
27
|
+
nameAgent: true
|
|
28
|
+
}));
|
|
23
29
|
const runAgentTooling = async (options) => {
|
|
24
30
|
const yes = options.yes;
|
|
25
|
-
const
|
|
26
|
-
|
|
31
|
+
const named = options.agents ?? [];
|
|
32
|
+
if (named.length > 0) {
|
|
33
|
+
assertNamedAgentTooling(named, options.command ?? "init");
|
|
34
|
+
const tooling = chooseYesAgentTooling(named);
|
|
35
|
+
await runInitSteps(planToolingSteps(tooling, {
|
|
36
|
+
yes,
|
|
37
|
+
named: true
|
|
38
|
+
}), options);
|
|
39
|
+
return tooling.setup;
|
|
40
|
+
}
|
|
41
|
+
if (options.agentSetup !== void 0) {
|
|
42
|
+
await runInitSteps(planAgentSteps({
|
|
43
|
+
yes,
|
|
44
|
+
agentSetup: options.agentSetup
|
|
45
|
+
}), options);
|
|
46
|
+
return options.agentSetup;
|
|
47
|
+
}
|
|
48
|
+
if (yes) {
|
|
49
|
+
if (options.hasProjectPlugins !== void 0) {
|
|
50
|
+
const agentSetup = await options.hasProjectPlugins(options.cwd) ? "plugin" : "skills-mcp";
|
|
51
|
+
await runInitSteps(planAgentSteps({
|
|
52
|
+
yes: true,
|
|
53
|
+
agentSetup
|
|
54
|
+
}), options);
|
|
55
|
+
return agentSetup;
|
|
56
|
+
}
|
|
57
|
+
const agents = await yesAgentsFromOptions(options);
|
|
58
|
+
const tooling = chooseYesAgentTooling(agents);
|
|
59
|
+
if (tooling.setup === "skip") throw yesMiss();
|
|
60
|
+
await runInitSteps(planYesAgentSteps(tooling), options);
|
|
61
|
+
return tooling.setup;
|
|
62
|
+
}
|
|
63
|
+
const interactive = options.pickAgentSetup !== void 0 || canPickAgentsInteractively();
|
|
27
64
|
const agentSetup = await resolveInitAgentSetup({
|
|
28
|
-
yes,
|
|
29
65
|
interactive,
|
|
30
|
-
hasProjectPlugins,
|
|
31
66
|
pick: options.pickAgentSetup ?? pickAgentSetupInteractively
|
|
32
67
|
});
|
|
33
68
|
await runInitSteps(planAgentSteps({
|
|
34
69
|
yes,
|
|
35
70
|
agentSetup
|
|
36
71
|
}), options);
|
|
72
|
+
return agentSetup;
|
|
37
73
|
};
|
|
38
74
|
const runScaffoldFollowUp = async (options) => {
|
|
39
75
|
if (!options.skipAgentSetup) await runAgentTooling(options);
|
package/dist/init/wizard.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { INIT_NEEDS_YES_OR_TERMINAL } from "./plan.js";
|
|
1
2
|
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
2
3
|
import { NEON_PLUGIN_NAME } from "../plugins/run.js";
|
|
3
4
|
import prompts from "prompts";
|
|
@@ -10,7 +11,7 @@ const restoreCursorOnAbort = (state) => {
|
|
|
10
11
|
}
|
|
11
12
|
};
|
|
12
13
|
const pickAgentSetupInteractively = async () => {
|
|
13
|
-
if (!canPickAgentsInteractively()) throw new Error(
|
|
14
|
+
if (!canPickAgentsInteractively()) throw new Error(INIT_NEEDS_YES_OR_TERMINAL);
|
|
14
15
|
const { setup } = await prompts({
|
|
15
16
|
onState: restoreCursorOnAbort,
|
|
16
17
|
type: "select",
|
package/dist/mcp/plan.js
CHANGED
|
@@ -1,22 +1,35 @@
|
|
|
1
1
|
import { tryResolveAddMcpAgentId } from "./agents.js";
|
|
2
|
-
import { agentChoicesFrom, pickAgentsInteractively, resolveAgentSelection } from "../utils/agent_picker.js";
|
|
3
2
|
import { detectMcpAgents, mcpInstallableAgents } from "./targets.js";
|
|
3
|
+
import { collectYesAgents, noDetectedAgentsMessage } from "../init/plan.js";
|
|
4
|
+
import { agentChoicesFrom, pickAgentsInteractively, resolveAgentSelection } from "../utils/agent_picker.js";
|
|
5
|
+
import { detectAgent } from "../init/detect_host.js";
|
|
4
6
|
import { pickMcpAuth, pickMcpProjectPin, pickMcpScope } from "./wizard.js";
|
|
5
7
|
//#region src/mcp/plan.ts
|
|
6
8
|
async function resolveMcpPlan(options) {
|
|
7
9
|
const prompt = options.interactive && !options.yes;
|
|
8
10
|
const scope = options.project ? "project" : prompt ? await (options.pickScope ?? pickMcpScope)() : "global";
|
|
9
11
|
const available = mcpInstallableAgents(scope);
|
|
10
|
-
const
|
|
12
|
+
const availableSet = new Set(available);
|
|
13
|
+
const scoped = await detectMcpAgents({
|
|
11
14
|
scope,
|
|
12
15
|
cwd: options.cwd
|
|
13
16
|
});
|
|
17
|
+
const detected = options.yes && options.agents.length === 0 ? await collectYesAgents({
|
|
18
|
+
detected: () => scoped,
|
|
19
|
+
detectAgent: options.detectAgent ?? detectAgent,
|
|
20
|
+
acceptHost: (id) => availableSet.has(id)
|
|
21
|
+
}) : scoped;
|
|
14
22
|
const agents = await resolveAgentSelection({
|
|
15
23
|
specified: options.agents,
|
|
16
24
|
choices: agentChoicesFrom(available, detected),
|
|
17
25
|
detected,
|
|
18
26
|
message: "Which coding agents should get the Neon MCP server? (space to toggle, enter to confirm)",
|
|
19
|
-
nonInteractiveMessage:
|
|
27
|
+
nonInteractiveMessage: noDetectedAgentsMessage({
|
|
28
|
+
scope,
|
|
29
|
+
supported: available,
|
|
30
|
+
fix: options.yes ? "run-without-yes" : "pass-yes",
|
|
31
|
+
nameAgent: true
|
|
32
|
+
}),
|
|
20
33
|
resolveSpecified: (raw) => {
|
|
21
34
|
const id = tryResolveAddMcpAgentId(raw);
|
|
22
35
|
if (!id) throw new Error(`Unknown agent: "${raw}". Supported agents: ${available.join(", ")}`);
|
package/dist/plugins/plan.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { getAgentDisplayName, tryResolveAddMcpAgentId } from "../mcp/agents.js";
|
|
2
2
|
import "../init/agents.js";
|
|
3
3
|
import { detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, pluginsInstallableAgents } from "./targets.js";
|
|
4
|
+
import { collectYesAgents, noDetectedAgentsMessage } from "../init/plan.js";
|
|
4
5
|
import { agentChoicesFrom, pickAgentsInteractively, resolveAgentSelection } from "../utils/agent_picker.js";
|
|
6
|
+
import { detectAgent } from "../init/detect_host.js";
|
|
5
7
|
//#region src/plugins/plan.ts
|
|
6
8
|
const assertPluginsCanRun = (options) => {
|
|
7
9
|
if (options.yes || options.interactive || options.hasAgents) return;
|
|
8
|
-
throw new Error("No interactive terminal. Pass -y to install into detected agents, or --agent <name
|
|
10
|
+
throw new Error("No interactive terminal. Pass -y to install into detected agents, or --agent <name> to name them.");
|
|
9
11
|
};
|
|
10
12
|
async function resolvePluginsPlan(options) {
|
|
11
13
|
assertPluginsCanRun({
|
|
@@ -16,16 +18,28 @@ async function resolvePluginsPlan(options) {
|
|
|
16
18
|
const prompt = options.interactive && !options.yes;
|
|
17
19
|
const scope = options.global ? "global" : "project";
|
|
18
20
|
const available = pluginsInstallableAgents(scope);
|
|
19
|
-
const
|
|
21
|
+
const availableSet = new Set(available);
|
|
22
|
+
const scoped = await detectPluginsAgents({
|
|
20
23
|
scope,
|
|
21
|
-
cwd: options.cwd
|
|
24
|
+
cwd: options.cwd,
|
|
25
|
+
...options.detectInstalledAgents ? { detectInstalledAgents: options.detectInstalledAgents } : {}
|
|
22
26
|
});
|
|
27
|
+
const detected = options.yes && options.agents.length === 0 ? await collectYesAgents({
|
|
28
|
+
detected: () => scoped,
|
|
29
|
+
detectAgent: options.detectAgent ?? detectAgent,
|
|
30
|
+
acceptHost: (id) => availableSet.has(id) || getPluginsTargetName(id) !== void 0
|
|
31
|
+
}) : scoped;
|
|
23
32
|
const selected = await resolveAgentSelection({
|
|
24
33
|
specified: options.agents,
|
|
25
34
|
choices: agentChoicesFrom(available, detected),
|
|
26
35
|
detected,
|
|
27
36
|
message: "Which coding agents should get the Neon plugin? (space to toggle, enter to confirm)",
|
|
28
|
-
nonInteractiveMessage:
|
|
37
|
+
nonInteractiveMessage: noDetectedAgentsMessage({
|
|
38
|
+
scope,
|
|
39
|
+
supported: available,
|
|
40
|
+
fix: options.yes ? "run-without-yes" : "pass-yes",
|
|
41
|
+
nameAgent: true
|
|
42
|
+
}),
|
|
29
43
|
resolveSpecified: (raw) => {
|
|
30
44
|
if (raw === "*") throw new Error("neon plugins does not accept --agent *. Pass --agent <name> for each coding agent, or omit --agent to use detected agents.");
|
|
31
45
|
const id = tryResolveAddMcpAgentId(raw);
|
package/dist/plugins/targets.js
CHANGED
|
@@ -35,7 +35,7 @@ const pluginsInstallableAgents = (scope) => pluginsMappedAgents().filter((id) =>
|
|
|
35
35
|
return target !== void 0 && !isUserScopeOnlyPluginsTarget(target);
|
|
36
36
|
});
|
|
37
37
|
const detectPluginsAgents = async (options) => {
|
|
38
|
-
const detected = options.scope === "project" ? detectProjectAgents(options.cwd) : await detectInstalledAgents();
|
|
38
|
+
const detected = options.scope === "project" ? detectProjectAgents(options.cwd) : await (options.detectInstalledAgents ?? detectInstalledAgents)();
|
|
39
39
|
return uniqueAgentIds(detected).filter((id) => supportsPlugins(id));
|
|
40
40
|
};
|
|
41
41
|
const detectInstallablePluginsAgents = async (options) => {
|
package/dist/skills/catalog.js
CHANGED
|
@@ -3,11 +3,6 @@ import { helpCsv } from "../utils/help_text.js";
|
|
|
3
3
|
const AGENT_SKILLS_SOURCE = "neondatabase/agent-skills";
|
|
4
4
|
const PLATFORMS_SKILLS_SOURCE = "neondatabase/neon-for-agent-platforms";
|
|
5
5
|
const NEON_SKILL_CATALOG = [
|
|
6
|
-
{
|
|
7
|
-
skill: "claimable-postgres",
|
|
8
|
-
source: AGENT_SKILLS_SOURCE,
|
|
9
|
-
defaultSelected: true
|
|
10
|
-
},
|
|
11
6
|
{
|
|
12
7
|
skill: "neon",
|
|
13
8
|
source: AGENT_SKILLS_SOURCE,
|
package/dist/skills/plan.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { tryResolveAddMcpAgentId } from "../mcp/agents.js";
|
|
2
2
|
import "../init/agents.js";
|
|
3
|
+
import { detectSkillsAgents, skillsInstallableAgents } from "./targets.js";
|
|
4
|
+
import { collectYesAgents, noDetectedAgentsMessage } from "../init/plan.js";
|
|
3
5
|
import { agentChoicesFrom, pickAgentsInteractively, resolveAgentSelection } from "../utils/agent_picker.js";
|
|
6
|
+
import { detectAgent } from "../init/detect_host.js";
|
|
4
7
|
import { invocationsForSelection, resolveSkillId, uniqueSkillEntries, yesInstallInvocations } from "./catalog.js";
|
|
5
|
-
import { detectSkillsAgents, skillsInstallableAgents } from "./targets.js";
|
|
6
8
|
import { pickSkillsInteractively } from "./wizard.js";
|
|
7
9
|
//#region src/skills/plan.ts
|
|
8
10
|
const assertSkillsCanRun = (options) => {
|
|
@@ -11,7 +13,7 @@ const assertSkillsCanRun = (options) => {
|
|
|
11
13
|
throw new Error("No interactive terminal. Pass -y to update installed skills.");
|
|
12
14
|
}
|
|
13
15
|
if (options.yes || options.interactive || options.hasSkills === true) return;
|
|
14
|
-
throw new Error("No interactive terminal. Pass -y to install the default skills, or --skill <name>.");
|
|
16
|
+
throw new Error("No interactive terminal. Pass -y to install the default skills into detected agents, or --skill <name>.");
|
|
15
17
|
};
|
|
16
18
|
async function resolveSkillsPlan(options) {
|
|
17
19
|
assertSkillsCanRun({
|
|
@@ -24,16 +26,28 @@ async function resolveSkillsPlan(options) {
|
|
|
24
26
|
const prompt = options.interactive && !options.yes;
|
|
25
27
|
const scope = options.global ? "global" : "project";
|
|
26
28
|
const available = skillsInstallableAgents();
|
|
27
|
-
const
|
|
29
|
+
const availableSet = new Set(available);
|
|
30
|
+
const scoped = await detectSkillsAgents({
|
|
28
31
|
scope,
|
|
29
|
-
cwd: options.cwd
|
|
32
|
+
cwd: options.cwd,
|
|
33
|
+
...options.detectInstalledAgents ? { detectInstalledAgents: options.detectInstalledAgents } : {}
|
|
30
34
|
});
|
|
35
|
+
const detected = options.yes && options.agents.length === 0 ? await collectYesAgents({
|
|
36
|
+
detected: () => scoped,
|
|
37
|
+
detectAgent: options.detectAgent ?? detectAgent,
|
|
38
|
+
acceptHost: (id) => availableSet.has(id)
|
|
39
|
+
}) : scoped;
|
|
31
40
|
const selected = await resolveAgentSelection({
|
|
32
41
|
specified: options.agents,
|
|
33
42
|
choices: agentChoicesFrom(available, detected),
|
|
34
43
|
detected,
|
|
35
44
|
message: "Which coding agents should get Neon agent skills? (space to toggle, enter to confirm)",
|
|
36
|
-
nonInteractiveMessage:
|
|
45
|
+
nonInteractiveMessage: noDetectedAgentsMessage({
|
|
46
|
+
scope,
|
|
47
|
+
supported: available,
|
|
48
|
+
fix: options.yes ? "run-without-yes" : "pass-yes",
|
|
49
|
+
nameAgent: true
|
|
50
|
+
}),
|
|
37
51
|
resolveSpecified: (raw) => {
|
|
38
52
|
if (raw === "*") throw new Error("neon skills does not accept --agent *. Pass --agent <name> for each coding agent, or omit --agent to use detected agents.");
|
|
39
53
|
const id = tryResolveAddMcpAgentId(raw);
|
package/dist/skills/targets.js
CHANGED
|
@@ -4,7 +4,7 @@ import { detectProjectAgents } from "add-mcp";
|
|
|
4
4
|
//#region src/skills/targets.ts
|
|
5
5
|
const skillsInstallableAgents = () => listMcpAgentIds().filter((id) => supportsSkills(id));
|
|
6
6
|
const detectSkillsAgents = async (options) => {
|
|
7
|
-
const detected = options.scope === "project" ? detectProjectAgents(options.cwd) : await detectInstalledAgents();
|
|
7
|
+
const detected = options.scope === "project" ? detectProjectAgents(options.cwd) : await (options.detectInstalledAgents ?? detectInstalledAgents)();
|
|
8
8
|
return uniqueAgentIds(detected).filter((id) => supportsSkills(id));
|
|
9
9
|
};
|
|
10
10
|
const mappedSkillsAgentNames = (agents) => {
|
package/dist/skills/wizard.js
CHANGED
|
@@ -12,7 +12,7 @@ const restoreCursorOnAbort = (state) => {
|
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
const pickSkillsInteractively = async () => {
|
|
15
|
-
if (!canPickAgentsInteractively()) throw new Error("No interactive terminal. Pass -y to install the default skills
|
|
15
|
+
if (!canPickAgentsInteractively()) throw new Error("No interactive terminal. Pass -y to install the default skills into detected agents.");
|
|
16
16
|
const { skills } = await prompts({
|
|
17
17
|
onState: restoreCursorOnAbort,
|
|
18
18
|
type: "multiselect",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/utils/agent_flag.ts
|
|
2
|
+
const coerceAgentFlag = (value) => {
|
|
3
|
+
if (value === void 0) return [];
|
|
4
|
+
const list = Array.isArray(value) ? value : [value];
|
|
5
|
+
if (list.length === 0) throw new Error("--agent needs a value. Pass one, or omit the flag entirely.");
|
|
6
|
+
return list.map((item) => {
|
|
7
|
+
if (typeof item !== "string" || item.trim() === "") throw new Error("--agent needs a value. Pass one, or omit the flag entirely.");
|
|
8
|
+
return item;
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
const agentArgv = (agents) => agents.flatMap((id) => ["--agent", id]);
|
|
12
|
+
//#endregion
|
|
13
|
+
export { agentArgv, coerceAgentFlag };
|
|
@@ -4,7 +4,7 @@ import prompts from "prompts";
|
|
|
4
4
|
//#region src/utils/agent_picker.ts
|
|
5
5
|
const canPickAgentsInteractively = () => !isCi() && Boolean(process.stdout.isTTY) && Boolean(process.stdin.isTTY);
|
|
6
6
|
const pickAgentsInteractively = async (options) => {
|
|
7
|
-
if (!canPickAgentsInteractively()) throw new Error("No interactive terminal. Pass
|
|
7
|
+
if (!canPickAgentsInteractively()) throw new Error("No interactive terminal. Pass -y, or run this command in a terminal to pick agents.");
|
|
8
8
|
if (options.choices.length === 0) throw new Error("No coding agents are available to pick.");
|
|
9
9
|
const selected = new Set(options.selected ?? []);
|
|
10
10
|
const { agents } = await prompts({
|
|
@@ -48,7 +48,7 @@ const resolveAgentSelection = async (options) => {
|
|
|
48
48
|
choices: options.choices,
|
|
49
49
|
selected: options.detected.filter((id) => options.choices.some((choice) => choice.id === id))
|
|
50
50
|
});
|
|
51
|
-
if (selected.length === 0) throw new Error("No agents selected.
|
|
51
|
+
if (selected.length === 0) throw new Error("No agents selected. Pick at least one agent, or pass -y to use detected agents.");
|
|
52
52
|
return uniqueAgentIds(selected);
|
|
53
53
|
}
|
|
54
54
|
if (options.detected.length > 0) return uniqueAgentIds(options.detected);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neon",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.11.0",
|
|
4
4
|
"description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"yaml": "^2.9.0",
|
|
64
64
|
"yargs": "17.7.2",
|
|
65
65
|
"yoctocolors": "^2.1.2",
|
|
66
|
-
"@neon/config": "1.0.5",
|
|
66
|
+
"@neon/config-runtime": "1.0.5",
|
|
67
67
|
"@neon/sdk": "3.0.0",
|
|
68
|
-
"@neon/config
|
|
68
|
+
"@neon/config": "1.0.5"
|
|
69
69
|
},
|
|
70
70
|
"optionalDependencies": {
|
|
71
71
|
"@napi-rs/keyring": "1.3.0",
|