neon 6.2.4 → 6.4.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 +32 -2
- package/dist/commands/auth.js +2 -1
- package/dist/commands/claim.js +1 -1
- package/dist/commands/config.js +204 -24
- package/dist/commands/mcp.js +4 -2
- package/dist/commands/plugins.js +7 -3
- package/dist/commands/skills.js +4 -2
- package/dist/config_edit.js +237 -0
- package/dist/config_source.js +270 -0
- package/dist/config_template.js +10 -2
- package/dist/context.js +6 -1
- package/dist/index.js +6 -1
- package/dist/init/chrome.js +26 -6
- package/dist/init/copy.js +7 -1
- package/dist/init/mode.js +1 -3
- package/dist/init/plan.js +11 -5
- package/dist/init/run.js +63 -50
- package/dist/init/tooling.js +63 -50
- package/dist/init/wizard.js +17 -18
- package/dist/plugins/plan.js +32 -4
- package/dist/plugins/targets.js +49 -9
- package/dist/sso_recovery.js +92 -0
- package/dist/utils/agent_picker.js +44 -18
- package/dist/utils/enrichers.js +2 -1
- package/package.json +1 -1
package/dist/init/plan.js
CHANGED
|
@@ -201,8 +201,9 @@ const chooseYesAgentTooling = (agents) => {
|
|
|
201
201
|
};
|
|
202
202
|
};
|
|
203
203
|
const FALLBACK_SKILLS_AGENTS = ["cursor", "codex"];
|
|
204
|
-
const splitInitTooling = (agents, pluginScope, mcpConfigLocation = pluginScope) => {
|
|
205
|
-
const
|
|
204
|
+
const splitInitTooling = (agents, pluginScope, mcpConfigLocation = pluginScope, unavailablePluginAgents = []) => {
|
|
205
|
+
const unavailable = new Set(unavailablePluginAgents);
|
|
206
|
+
const pluginSet = new Set(pluginsInstallableAgents(pluginScope).filter((id) => !unavailable.has(id)));
|
|
206
207
|
const mcpSet = new Set(mcpInstallableAgents(mcpConfigLocation));
|
|
207
208
|
const pluginAgents = uniqueAgentIds(agents.filter((id) => pluginSet.has(id)));
|
|
208
209
|
const rest = uniqueAgentIds(agents.filter((id) => !pluginSet.has(id)));
|
|
@@ -226,8 +227,8 @@ const splitInitTooling = (agents, pluginScope, mcpConfigLocation = pluginScope)
|
|
|
226
227
|
mcpAgents
|
|
227
228
|
};
|
|
228
229
|
};
|
|
229
|
-
const recommendedTooling = (detected) => {
|
|
230
|
-
const split = splitInitTooling(detected, "global");
|
|
230
|
+
const recommendedTooling = (detected, unavailablePluginAgents = []) => {
|
|
231
|
+
const split = splitInitTooling(detected, "global", "global", unavailablePluginAgents);
|
|
231
232
|
if (split.setup === "skip") return {
|
|
232
233
|
setup: "skills",
|
|
233
234
|
agents: FALLBACK_SKILLS_AGENTS
|
|
@@ -297,7 +298,12 @@ const planInitToolingSteps = (input) => {
|
|
|
297
298
|
default: return input.tooling;
|
|
298
299
|
}
|
|
299
300
|
};
|
|
300
|
-
const funnelAgentSetup = (tooling) =>
|
|
301
|
+
const funnelAgentSetup = (tooling) => {
|
|
302
|
+
if (tooling.setup !== "skills-mcp") return tooling.setup;
|
|
303
|
+
if (tooling.skillsAgents.length === 0) return "mcp";
|
|
304
|
+
if (tooling.mcpAgents.length === 0) return "skills";
|
|
305
|
+
return "skills-mcp";
|
|
306
|
+
};
|
|
301
307
|
const planYesAgentSteps = (tooling) => planToolingSteps(tooling, {
|
|
302
308
|
yes: true,
|
|
303
309
|
named: false
|
package/dist/init/run.js
CHANGED
|
@@ -13,8 +13,9 @@ import { agentsRowValue, configRowValue, formatInitDone, headingForKind, printIn
|
|
|
13
13
|
import { inferPackageManager, resolvePackageManager } from "../utils/package_manager.js";
|
|
14
14
|
import { ConfigInstallFailed, hasNeonConfigFile, initCmd, neonConfigFilename } from "../commands/config.js";
|
|
15
15
|
import { runAuthenticatedLink } from "./link.js";
|
|
16
|
+
import { getAgentDisplayName } from "../mcp/agents.js";
|
|
16
17
|
import { mcpInstallableAgents } from "../mcp/targets.js";
|
|
17
|
-
import { pluginsInstallableAgents } from "../plugins/targets.js";
|
|
18
|
+
import { missingPluginsCommands, pluginsInstallableAgents } from "../plugins/targets.js";
|
|
18
19
|
import { skillsInstallableAgents } from "../skills/targets.js";
|
|
19
20
|
import { FALLBACK_SKILLS_AGENTS, assertNamedAgentTooling, funnelAgentSetup, planInitToolingSteps, recommendedTooling, resolveNamedAgents, splitInitTooling } from "./plan.js";
|
|
20
21
|
import { pullInitEnv } from "./auth.js";
|
|
@@ -56,11 +57,10 @@ const pluginScopeFor = (agents, preferred) => {
|
|
|
56
57
|
const project = new Set(pluginsInstallableAgents("project"));
|
|
57
58
|
return agents.some((id) => !project.has(id) && pluginsInstallableAgents("global").includes(id)) ? "global" : "project";
|
|
58
59
|
};
|
|
59
|
-
const
|
|
60
|
-
if (setup
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
return [.../* @__PURE__ */ new Set([...skillsInstallableAgents(), ...mcpInstallableAgents(scope === "project" ? "project" : "global")])];
|
|
60
|
+
const warnMissingPluginCommands = (missing, tooling) => {
|
|
61
|
+
if (tooling.setup !== "skills-mcp" && tooling.setup !== "mixed") return;
|
|
62
|
+
const routed = /* @__PURE__ */ new Set([...tooling.skillsAgents, ...tooling.mcpAgents]);
|
|
63
|
+
for (const { agent, command } of missing) if (routed.has(agent)) log.warning("%s: \"%s\" was not found on PATH, so its plugin cannot install. Installing Neon skills and MCP instead.", getAgentDisplayName(agent), command);
|
|
64
64
|
};
|
|
65
65
|
const nonEmptyAgents = (ids, fallback) => {
|
|
66
66
|
const [first, ...rest] = ids;
|
|
@@ -75,14 +75,15 @@ const skillsTooling = (ids, fallback) => {
|
|
|
75
75
|
agents
|
|
76
76
|
};
|
|
77
77
|
};
|
|
78
|
-
const skillsMcpTooling = (
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
const skillsMcpTooling = (skillsSelection, mcpSelection, mcpConfigLocation, fallback) => {
|
|
79
|
+
const useFallback = fallback && skillsSelection?.length === 0 && mcpSelection?.length === 0;
|
|
80
|
+
const selectedSkills = (useFallback ? FALLBACK_SKILLS_AGENTS : skillsSelection ?? []).filter((id) => skillsInstallableAgents().includes(id));
|
|
81
|
+
const selectedMcp = (useFallback ? FALLBACK_SKILLS_AGENTS : mcpSelection ?? []).filter((id) => mcpInstallableAgents(mcpConfigLocation).includes(id));
|
|
82
|
+
if (selectedMcp.length === 0) return skillsTooling(selectedSkills, false);
|
|
82
83
|
return {
|
|
83
84
|
setup: "skills-mcp",
|
|
84
|
-
skillsAgents:
|
|
85
|
-
mcpAgents:
|
|
85
|
+
skillsAgents: selectedSkills,
|
|
86
|
+
mcpAgents: selectedMcp
|
|
86
87
|
};
|
|
87
88
|
};
|
|
88
89
|
const validateMcpConfigLocationSupport = (ids, location) => {
|
|
@@ -103,13 +104,18 @@ const pickOrDetectAgents = async (input) => {
|
|
|
103
104
|
return selected;
|
|
104
105
|
}
|
|
105
106
|
const detected = input.detected.filter((id) => input.available.includes(id));
|
|
107
|
+
const preselected = (input.preselected ?? input.detected).filter((id) => input.available.includes(id));
|
|
106
108
|
if (input.pickAgents !== void 0) return input.pickAgents({
|
|
107
109
|
available: [...input.available],
|
|
108
|
-
detected
|
|
110
|
+
detected: preselected,
|
|
111
|
+
setup: input.setup
|
|
109
112
|
});
|
|
110
113
|
if (input.interactive) return pickInitAgentsInteractively({
|
|
111
114
|
available: [...input.available],
|
|
112
|
-
detected
|
|
115
|
+
detected,
|
|
116
|
+
preselected,
|
|
117
|
+
...input.missingCommands !== void 0 ? { missingCommands: input.missingCommands } : {},
|
|
118
|
+
setup: input.setup
|
|
113
119
|
});
|
|
114
120
|
return detected;
|
|
115
121
|
};
|
|
@@ -229,7 +235,9 @@ const runInit = async (props) => {
|
|
|
229
235
|
}
|
|
230
236
|
const recommended = mode === "recommended";
|
|
231
237
|
const targets = named.length > 0 ? named : detection.detectedAgents;
|
|
232
|
-
|
|
238
|
+
const missingPluginCommands = missingPluginsCommands(targets, { cwd });
|
|
239
|
+
const unavailablePluginAgents = missingPluginCommands.map((row) => row.agent);
|
|
240
|
+
const pluginReady = (agents) => agents.filter((id) => !unavailablePluginAgents.includes(id));
|
|
233
241
|
let tooling = { setup: "skip" };
|
|
234
242
|
let mcpAuth = props.mcpAuth;
|
|
235
243
|
let mcpConfigLocation = props.mcpConfigLocation ?? "global";
|
|
@@ -238,9 +246,9 @@ const runInit = async (props) => {
|
|
|
238
246
|
let pluginScope = recommended ? "global" : "project";
|
|
239
247
|
if (recommended) {
|
|
240
248
|
pluginScope = "global";
|
|
241
|
-
tooling = named.length > 0 ? splitInitTooling(named, "global") : recommendedTooling(detection.detectedAgents);
|
|
249
|
+
tooling = named.length > 0 ? splitInitTooling(named, "global", "global", unavailablePluginAgents) : recommendedTooling(detection.detectedAgents, unavailablePluginAgents);
|
|
242
250
|
if (named.length === 0 && detection.detectedAgents.length === 0) printInitProgress(NO_AGENTS_FALLBACK_STATUS);
|
|
243
|
-
|
|
251
|
+
warnMissingPluginCommands(missingPluginCommands, tooling);
|
|
244
252
|
mcpAuth = !detection.authenticated && (tooling.setup === "skills-mcp" || tooling.setup === "mixed") ? "oauth" : mcpAuth;
|
|
245
253
|
} else {
|
|
246
254
|
const inferred = inferInitAgentSetup({
|
|
@@ -251,53 +259,58 @@ const runInit = async (props) => {
|
|
|
251
259
|
canAsk: detection.interactive || props.pickAgentSetup !== void 0,
|
|
252
260
|
...props.skill !== void 0 ? { skills: props.skill } : {}
|
|
253
261
|
});
|
|
254
|
-
if (inferred.kind === "skip") {
|
|
255
|
-
|
|
256
|
-
agentSetupChoice = "skip";
|
|
257
|
-
} else if (inferred.kind === "auto") {
|
|
262
|
+
if (inferred.kind === "skip") tooling = { setup: "skip" };
|
|
263
|
+
else if (inferred.kind === "auto") {
|
|
258
264
|
const autoAgents = named.length > 0 ? named : detection.detectedAgents;
|
|
259
|
-
pluginScope = pluginScopeFor(autoAgents, "project");
|
|
260
|
-
tooling = splitInitTooling(autoAgents, pluginScope, mcpConfigLocation);
|
|
265
|
+
pluginScope = pluginScopeFor(pluginReady(autoAgents), "project");
|
|
266
|
+
tooling = splitInitTooling(autoAgents, pluginScope, mcpConfigLocation, unavailablePluginAgents);
|
|
267
|
+
warnMissingPluginCommands(missingPluginCommands, tooling);
|
|
261
268
|
if (tooling.setup === "skip" && yes && named.length === 0) {
|
|
262
269
|
tooling = recommendedTooling([]);
|
|
263
270
|
printInitProgress(NO_AGENTS_FALLBACK_STATUS);
|
|
264
271
|
}
|
|
265
|
-
agentSetupChoice = funnelAgentSetup(tooling);
|
|
266
272
|
} else {
|
|
267
273
|
const setup = inferred.kind === "ask" ? await (props.pickAgentSetup ?? pickAgentSetupInteractively)() : inferred.kind;
|
|
268
|
-
agentSetupChoice = setup === "skills" ? "skills" : setup;
|
|
269
274
|
if (setup !== "skip") {
|
|
270
275
|
pluginScope = pluginScopeFor(targets, "project");
|
|
271
|
-
const
|
|
272
|
-
const
|
|
276
|
+
const namedFallback = named.length === 0;
|
|
277
|
+
const pickAgents = (setup, available) => pickOrDetectAgents({
|
|
273
278
|
named,
|
|
274
279
|
available,
|
|
275
280
|
detected: detection.detectedAgents,
|
|
281
|
+
...setup === "plugin" ? {
|
|
282
|
+
preselected: pluginReady(detection.detectedAgents),
|
|
283
|
+
missingCommands: missingPluginCommands
|
|
284
|
+
} : {},
|
|
276
285
|
interactive: detection.interactive,
|
|
277
|
-
|
|
286
|
+
setup,
|
|
287
|
+
pickAgents: props.pickAgents
|
|
278
288
|
});
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
tooling = skillsTooling(selected, namedFallback);
|
|
287
|
-
agentSetupChoice = funnelAgentSetup(tooling);
|
|
289
|
+
if (setup === "plugin" || setup === "skills") {
|
|
290
|
+
const selected = await pickAgents(setup, setup === "plugin" ? uniqueAgents([...pluginsInstallableAgents("project"), ...pluginsInstallableAgents("global")]) : [...skillsInstallableAgents()]);
|
|
291
|
+
if (selected === void 0) tooling = { setup: "skip" };
|
|
292
|
+
else if (setup === "plugin") {
|
|
293
|
+
pluginScope = pluginScopeFor(selected, pluginScope);
|
|
294
|
+
tooling = splitInitTooling(selected.filter((id) => pluginsInstallableAgents(pluginScope).includes(id)), pluginScope);
|
|
295
|
+
} else tooling = skillsTooling(selected, namedFallback);
|
|
288
296
|
} else {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
297
|
+
const skillsAvailable = skillsInstallableAgents();
|
|
298
|
+
const skillsSelection = named.length > 0 ? named.filter((id) => skillsAvailable.includes(id)) : await pickAgents("skills", skillsAvailable);
|
|
299
|
+
if (skillsSelection !== void 0 && selectedSkills === void 0 && inferred.kind === "ask") selectedSkills = props.pickSkills !== void 0 ? await props.pickSkills() : detection.interactive ? await pickInitSkillsInteractively() : void 0;
|
|
300
|
+
const mcpSelection = named.length > 0 ? named : await pickAgents("mcp", mcpInstallableAgents("global"));
|
|
301
|
+
if (mcpSelection !== void 0) {
|
|
302
|
+
mcpConfigLocation = props.mcpConfigLocation ?? (yes ? "global" : props.pickMcpConfigLocation !== void 0 ? await props.pickMcpConfigLocation() : detection.interactive ? await pickInitMcpConfigLocationInteractively() : "global");
|
|
303
|
+
if (mcpSelection.length > 0) validateMcpConfigLocationSupport(mcpSelection, mcpConfigLocation);
|
|
304
|
+
const pickAuth = props.pickMcpAuth ?? (detection.interactive ? pickInitMcpAuthInteractively : void 0);
|
|
305
|
+
mcpAuth = props.mcpAuth ?? (yes ? void 0 : await pickAuth?.({ authenticated: detection.authenticated })) ?? (detection.authenticated && props.claimable !== true ? "api-key" : "oauth");
|
|
306
|
+
}
|
|
307
|
+
tooling = skillsMcpTooling(skillsSelection, mcpSelection, mcpConfigLocation, namedFallback);
|
|
295
308
|
}
|
|
296
309
|
}
|
|
297
310
|
}
|
|
298
311
|
}
|
|
299
|
-
funnel.agentSetup =
|
|
300
|
-
const usesMcp = tooling.setup === "skills-mcp" || tooling.setup === "mixed";
|
|
312
|
+
funnel.agentSetup = funnelAgentSetup(tooling);
|
|
313
|
+
const usesMcp = (tooling.setup === "skills-mcp" || tooling.setup === "mixed") && tooling.mcpAgents.length > 0;
|
|
301
314
|
if (mcpAuth === void 0 && usesMcp && (!detection.authenticated || props.claimable === true)) mcpAuth = "oauth";
|
|
302
315
|
delayMcp = usesMcp && (mcpAuth === "api-key" || props.mcpProjectScoped === true);
|
|
303
316
|
const mcpOauth = mcpAuth === "oauth";
|
|
@@ -355,7 +368,7 @@ const runInit = async (props) => {
|
|
|
355
368
|
funnel.link = "skipped";
|
|
356
369
|
} else if (props.pickProjectSetup !== void 0 || detection.interactive) projectSetup = await (props.pickProjectSetup ?? pickInitProjectSetupInteractively)();
|
|
357
370
|
else throw new Error(NON_TTY_LINK_NEEDS_AUTH);
|
|
358
|
-
if (mcpAuth === "api-key" && (projectSetup === "claimable" || props.claimable === true)) throw new Error(CLAIMABLE_MCP_API_KEY);
|
|
371
|
+
if (usesMcp && mcpAuth === "api-key" && (projectSetup === "claimable" || props.claimable === true)) throw new Error(CLAIMABLE_MCP_API_KEY);
|
|
359
372
|
if (projectSetup === "claimable") {
|
|
360
373
|
printInitProgress(PROGRESS.claim);
|
|
361
374
|
await (props.createClaimable ?? defaultCreateClaimable)({
|
|
@@ -529,8 +542,8 @@ const runInit = async (props) => {
|
|
|
529
542
|
{
|
|
530
543
|
label: "Agents",
|
|
531
544
|
value: agentsRowValue({
|
|
532
|
-
|
|
533
|
-
|
|
545
|
+
tooling,
|
|
546
|
+
installed: funnel.agentsInstalled
|
|
534
547
|
})
|
|
535
548
|
},
|
|
536
549
|
{
|
|
@@ -578,8 +591,8 @@ const runInit = async (props) => {
|
|
|
578
591
|
{
|
|
579
592
|
label: "Agents",
|
|
580
593
|
value: agentsRowValue({
|
|
581
|
-
|
|
582
|
-
|
|
594
|
+
tooling,
|
|
595
|
+
installed: funnel.agentsInstalled
|
|
583
596
|
})
|
|
584
597
|
},
|
|
585
598
|
{
|
package/dist/init/tooling.js
CHANGED
|
@@ -3,7 +3,7 @@ import { getCliName } from "../utils/cli_name.js";
|
|
|
3
3
|
import { PROGRESS } from "./copy.js";
|
|
4
4
|
import { assertNamedAgentTooling, chooseYesAgentTooling, collectYesAgents, initYesSupportedAgents, noDetectedAgentsMessage, planAgentSteps, planToolingSteps, planYesAgentSteps, resolveInitAgentSetup } from "./plan.js";
|
|
5
5
|
import { detectAgent } from "./detect_host.js";
|
|
6
|
-
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
6
|
+
import { AgentSelectionSkipped, canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
7
7
|
import { mcpInstallError, reportMcpInstall } from "../commands/mcp.js";
|
|
8
8
|
import { installPlugins, pluginsInstallError, reportPluginsInstall } from "../commands/plugins.js";
|
|
9
9
|
import { installSkills, reportSkillsInstall, skillsInstallError } from "../commands/skills.js";
|
|
@@ -66,45 +66,61 @@ const runToolingSteps = async (steps, options) => {
|
|
|
66
66
|
...options.operations
|
|
67
67
|
};
|
|
68
68
|
const reportProps = { output: options.output };
|
|
69
|
+
const sharedOptions = {
|
|
70
|
+
cwd: options.cwd,
|
|
71
|
+
allowAgentSkip: options.allowAgentSkip
|
|
72
|
+
};
|
|
73
|
+
const completed = [];
|
|
69
74
|
for (const step of steps) {
|
|
70
75
|
const label = options.narrate === "human" ? TOOLING_STEP_LABELS[step.kind] : void 0;
|
|
71
76
|
if (label !== void 0) process.stdout.write(`${label}\n`);
|
|
72
77
|
else log.info("Running `%s %s`", getCliName(), toolingStepCommandText(step));
|
|
73
78
|
log.debug("Running `%s %s`", getCliName(), toolingStepCommandText(step));
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
79
|
+
try {
|
|
80
|
+
switch (step.kind) {
|
|
81
|
+
case "plugins": {
|
|
82
|
+
const outcome = await raceSigint(ops.installPlugins({
|
|
83
|
+
...step.options,
|
|
84
|
+
...sharedOptions
|
|
85
|
+
}));
|
|
86
|
+
reportPluginsInstall(reportProps, outcome);
|
|
87
|
+
const error = pluginsInstallError(outcome);
|
|
88
|
+
if (error) throw error;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
case "skills": {
|
|
92
|
+
const outcome = await raceSigint(ops.installSkills({
|
|
93
|
+
...step.options,
|
|
94
|
+
...sharedOptions
|
|
95
|
+
}));
|
|
96
|
+
reportSkillsInstall(reportProps, outcome);
|
|
97
|
+
const error = skillsInstallError(outcome);
|
|
98
|
+
if (error) throw error;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case "mcp": {
|
|
102
|
+
const outcome = await raceSigint(ops.installMcp({
|
|
103
|
+
...step.options,
|
|
104
|
+
...sharedOptions
|
|
105
|
+
}));
|
|
106
|
+
reportMcpInstall(reportProps, outcome);
|
|
107
|
+
const error = mcpInstallError(outcome);
|
|
108
|
+
if (error) throw error;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
default: return step;
|
|
94
112
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
cwd: options.cwd
|
|
99
|
-
}));
|
|
100
|
-
reportMcpInstall(reportProps, outcome);
|
|
101
|
-
const error = mcpInstallError(outcome);
|
|
102
|
-
if (error) throw error;
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
default: return step;
|
|
113
|
+
completed.push(step.kind);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (!(error instanceof AgentSelectionSkipped)) throw error;
|
|
106
116
|
}
|
|
107
117
|
}
|
|
118
|
+
return completed;
|
|
119
|
+
};
|
|
120
|
+
const agentSetupResult = (setup, completed) => {
|
|
121
|
+
if (completed.length === 0) return "skip";
|
|
122
|
+
if (setup === "skills-mcp" && completed.length === 1) return completed[0] === "skills" ? "skills" : "mcp";
|
|
123
|
+
return setup;
|
|
108
124
|
};
|
|
109
125
|
const yesAgentsFromOptions = async (options) => collectYesAgents({
|
|
110
126
|
detected: () => (options.detectProjectAgents ?? defaultProjectAgents)(options.cwd),
|
|
@@ -119,47 +135,44 @@ const yesMiss = () => new Error(noDetectedAgentsMessage({
|
|
|
119
135
|
const runAgentTooling = async (options) => {
|
|
120
136
|
const yes = options.yes;
|
|
121
137
|
const named = options.agents ?? [];
|
|
138
|
+
const runSetup = async (setup, steps) => agentSetupResult(setup, await runToolingSteps(steps, {
|
|
139
|
+
...options,
|
|
140
|
+
allowAgentSkip: !yes
|
|
141
|
+
}));
|
|
122
142
|
if (named.length > 0) {
|
|
123
143
|
assertNamedAgentTooling(named, options.command ?? "init");
|
|
124
144
|
const tooling = chooseYesAgentTooling(named);
|
|
125
|
-
|
|
145
|
+
return runSetup(tooling.setup, planToolingSteps(tooling, {
|
|
126
146
|
yes,
|
|
127
147
|
named: true
|
|
128
|
-
})
|
|
129
|
-
return tooling.setup;
|
|
130
|
-
}
|
|
131
|
-
if (options.agentSetup !== void 0) {
|
|
132
|
-
await runToolingSteps(planAgentSteps({
|
|
133
|
-
yes,
|
|
134
|
-
agentSetup: options.agentSetup
|
|
135
|
-
}), options);
|
|
136
|
-
return options.agentSetup;
|
|
148
|
+
}));
|
|
137
149
|
}
|
|
150
|
+
if (options.agentSetup !== void 0) return runSetup(options.agentSetup, planAgentSteps({
|
|
151
|
+
yes,
|
|
152
|
+
agentSetup: options.agentSetup
|
|
153
|
+
}));
|
|
138
154
|
if (yes) {
|
|
139
155
|
if (options.hasProjectPlugins !== void 0) {
|
|
140
156
|
const agentSetup = await options.hasProjectPlugins(options.cwd) ? "plugin" : "skills-mcp";
|
|
141
|
-
|
|
157
|
+
return runSetup(agentSetup, planAgentSteps({
|
|
142
158
|
yes: true,
|
|
143
159
|
agentSetup
|
|
144
|
-
})
|
|
145
|
-
return agentSetup;
|
|
160
|
+
}));
|
|
146
161
|
}
|
|
147
162
|
const agents = await yesAgentsFromOptions(options);
|
|
148
163
|
const tooling = chooseYesAgentTooling(agents);
|
|
149
164
|
if (tooling.setup === "skip") throw yesMiss();
|
|
150
|
-
|
|
151
|
-
return tooling.setup;
|
|
165
|
+
return runSetup(tooling.setup, planYesAgentSteps(tooling));
|
|
152
166
|
}
|
|
153
167
|
const interactive = options.pickAgentSetup !== void 0 || canPickAgentsInteractively();
|
|
154
168
|
const agentSetup = await resolveInitAgentSetup({
|
|
155
169
|
interactive,
|
|
156
170
|
pick: options.pickAgentSetup ?? pickAgentSetupInteractively
|
|
157
171
|
});
|
|
158
|
-
|
|
172
|
+
return runSetup(agentSetup, planAgentSteps({
|
|
159
173
|
yes,
|
|
160
174
|
agentSetup
|
|
161
|
-
})
|
|
162
|
-
return agentSetup;
|
|
175
|
+
}));
|
|
163
176
|
};
|
|
164
177
|
//#endregion
|
|
165
178
|
export { runAgentTooling, runToolingSteps };
|
package/dist/init/wizard.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { CONFIG_INIT_SERVICES } from "../config_template.js";
|
|
2
|
-
import { 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, CONFIG_CONFIRM_HINT, CONFIG_CONFIRM_MESSAGE, MCP_AUTH_API_KEY_DESCRIPTION, MCP_AUTH_API_KEY_TITLE, MCP_AUTH_MESSAGE, MCP_AUTH_OAUTH_DESCRIPTION, MCP_AUTH_OAUTH_TITLE, 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, PACKAGE_MANAGER_MESSAGE, 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 } from "./copy.js";
|
|
2
|
+
import { AGENT_DETECTED_DESCRIPTION, AGENT_MCP_PICKER_MESSAGE, AGENT_MCP_SKIP_MESSAGE, AGENT_OTHER_DESCRIPTION, AGENT_PICKER_MESSAGE, AGENT_PLUGIN_SKIP_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, AGENT_SKILLS_PICKER_MESSAGE, AGENT_SKILLS_SKIP_MESSAGE, CONFIG_CONFIRM_HINT, CONFIG_CONFIRM_MESSAGE, MCP_AUTH_API_KEY_DESCRIPTION, MCP_AUTH_API_KEY_TITLE, MCP_AUTH_MESSAGE, MCP_AUTH_OAUTH_DESCRIPTION, MCP_AUTH_OAUTH_TITLE, 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, PACKAGE_MANAGER_MESSAGE, 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, agentMissingCommandDescription } from "./copy.js";
|
|
3
3
|
import { installedPackageManagers } from "../utils/package_manager.js";
|
|
4
4
|
import { POSTGRES_SERVICE_CHOICE, keepPostgresSelected } from "../utils/service_picker.js";
|
|
5
5
|
import { getAgentDisplayName, listMcpAgentIds } from "../mcp/agents.js";
|
|
6
6
|
import { INIT_NEEDS_YES_OR_TERMINAL } from "./plan.js";
|
|
7
|
-
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
7
|
+
import { canPickAgentsInteractively, pickAgentsInteractively } from "../utils/agent_picker.js";
|
|
8
8
|
import { NEON_SKILL_CATALOG, listSkillIds } from "../skills/catalog.js";
|
|
9
9
|
import { InitCancelled, restoreCursorVisibilityOnAbort } from "./cancelled.js";
|
|
10
10
|
import chalk from "chalk";
|
|
@@ -71,24 +71,23 @@ const pickAgentSetupInteractively = async () => {
|
|
|
71
71
|
};
|
|
72
72
|
const pickInitAgentsInteractively = async (input) => {
|
|
73
73
|
requireInteractive();
|
|
74
|
-
if (input.available.length === 0) throw new Error("No coding agents are available to pick.");
|
|
75
74
|
const detected = new Set(input.detected);
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
75
|
+
const missing = new Map((input.missingCommands ?? []).map((row) => [row.agent, row.command]));
|
|
76
|
+
const selected = await pickAgentsInteractively({
|
|
77
|
+
onCancel: aborted,
|
|
78
|
+
message: input.setup === "skills" ? AGENT_SKILLS_PICKER_MESSAGE : input.setup === "mcp" ? AGENT_MCP_PICKER_MESSAGE : AGENT_PICKER_MESSAGE,
|
|
79
|
+
skipMessage: input.setup === "plugin" ? AGENT_PLUGIN_SKIP_MESSAGE : input.setup === "skills" ? AGENT_SKILLS_SKIP_MESSAGE : AGENT_MCP_SKIP_MESSAGE,
|
|
80
|
+
selected: input.preselected ?? input.detected,
|
|
81
|
+
choices: input.available.map((id) => {
|
|
82
|
+
const command = missing.get(id);
|
|
83
|
+
return {
|
|
84
|
+
id,
|
|
85
|
+
title: `${getAgentDisplayName(id)} (${id})`,
|
|
86
|
+
description: command !== void 0 ? agentMissingCommandDescription(command) : detected.has(id) ? AGENT_DETECTED_DESCRIPTION : AGENT_OTHER_DESCRIPTION
|
|
87
|
+
};
|
|
88
|
+
})
|
|
89
89
|
});
|
|
90
|
-
|
|
91
|
-
return agents.filter((id) => typeof id === "string");
|
|
90
|
+
return selected.length > 0 ? selected : void 0;
|
|
92
91
|
};
|
|
93
92
|
const pickInitSkillsInteractively = async () => {
|
|
94
93
|
requireInteractive();
|
package/dist/plugins/plan.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { getAgentDisplayName, tryResolveAddMcpAgentId } from "../mcp/agents.js";
|
|
2
2
|
import "../init/agents.js";
|
|
3
|
-
import { detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, pluginsInstallableAgents } from "./targets.js";
|
|
3
|
+
import { detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, missingPluginsCommands, pluginsInstallableAgents } from "./targets.js";
|
|
4
4
|
import { collectYesAgents, noDetectedAgentsMessage } from "../init/plan.js";
|
|
5
5
|
import { detectAgent } from "../init/detect_host.js";
|
|
6
6
|
import { agentChoicesFrom, pickAgentsInteractively, resolveAgentSelection } from "../utils/agent_picker.js";
|
|
7
7
|
//#region src/plugins/plan.ts
|
|
8
|
+
const missingPluginsCommandsError = (missing) => new Error(missing.map(({ agent, command }) => `Cannot install the Neon plugin for ${getAgentDisplayName(agent)}: "${command}" was not found on PATH. Install it or add it to PATH, then retry.`).join("\n"));
|
|
8
9
|
const assertPluginsCanRun = (options) => {
|
|
9
10
|
if (options.yes || options.interactive || options.hasAgents) return;
|
|
10
11
|
throw new Error("No interactive terminal. Pass -y to install into detected agents, or --agent <name> to name them.");
|
|
11
12
|
};
|
|
13
|
+
const commandLookup = (options) => ({
|
|
14
|
+
cwd: options.cwd,
|
|
15
|
+
...options.commandPath !== void 0 ? { path: options.commandPath } : {}
|
|
16
|
+
});
|
|
12
17
|
async function resolvePluginsPlan(options) {
|
|
13
18
|
assertPluginsCanRun({
|
|
14
19
|
yes: options.yes,
|
|
@@ -29,10 +34,18 @@ async function resolvePluginsPlan(options) {
|
|
|
29
34
|
detectAgent: options.detectAgent ?? detectAgent,
|
|
30
35
|
acceptHost: (id) => availableSet.has(id) || getPluginsTargetName(id) !== void 0
|
|
31
36
|
}) : scoped;
|
|
37
|
+
const missingDetected = new Map((prompt ? missingPluginsCommands(detected, commandLookup(options)) : []).map((row) => [row.agent, row.command]));
|
|
38
|
+
const choices = agentChoicesFrom(available, detected).map((choice) => {
|
|
39
|
+
const command = missingDetected.get(choice.id);
|
|
40
|
+
return command === void 0 ? choice : {
|
|
41
|
+
...choice,
|
|
42
|
+
description: `detected, but "${command}" is not on PATH`
|
|
43
|
+
};
|
|
44
|
+
});
|
|
32
45
|
const selected = await resolveAgentSelection({
|
|
33
46
|
specified: options.agents,
|
|
34
|
-
choices
|
|
35
|
-
detected,
|
|
47
|
+
choices,
|
|
48
|
+
detected: detected.filter((id) => !missingDetected.has(id)),
|
|
36
49
|
message: "Which coding agents should get the Neon plugin? (space to toggle, enter to confirm)",
|
|
37
50
|
nonInteractiveMessage: noDetectedAgentsMessage({
|
|
38
51
|
scope,
|
|
@@ -64,6 +77,20 @@ async function resolvePluginsPlan(options) {
|
|
|
64
77
|
}
|
|
65
78
|
agents.push(id);
|
|
66
79
|
}
|
|
80
|
+
const missingCommands = missingPluginsCommands(agents, commandLookup(options));
|
|
81
|
+
if (missingCommands.length > 0) {
|
|
82
|
+
if (!(options.agents.length === 0 && !prompt)) throw missingPluginsCommandsError(missingCommands);
|
|
83
|
+
const missingAgents = new Set(missingCommands.map((row) => row.agent));
|
|
84
|
+
const installable = agents.filter((id) => !missingAgents.has(id));
|
|
85
|
+
return {
|
|
86
|
+
scope,
|
|
87
|
+
agents: installable,
|
|
88
|
+
skipped,
|
|
89
|
+
userScopeSkipped,
|
|
90
|
+
missingCommands,
|
|
91
|
+
targets: installable.length === 0 ? [] : mappedPluginsTargets(installable, scope)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
67
94
|
if (agents.length === 0) {
|
|
68
95
|
if (userScopeSkipped.length > 0 && skipped.length === 0) {
|
|
69
96
|
const names = userScopeSkipped.map((id) => getAgentDisplayName(id)).join(", ");
|
|
@@ -76,8 +103,9 @@ async function resolvePluginsPlan(options) {
|
|
|
76
103
|
agents,
|
|
77
104
|
skipped,
|
|
78
105
|
userScopeSkipped,
|
|
106
|
+
missingCommands,
|
|
79
107
|
targets: mappedPluginsTargets(agents, scope)
|
|
80
108
|
};
|
|
81
109
|
}
|
|
82
110
|
//#endregion
|
|
83
|
-
export { assertPluginsCanRun, resolvePluginsPlan };
|
|
111
|
+
export { assertPluginsCanRun, missingPluginsCommandsError, resolvePluginsPlan };
|
package/dist/plugins/targets.js
CHANGED
|
@@ -1,27 +1,67 @@
|
|
|
1
1
|
import { detectInstalledAgents, listMcpAgentIds, tryResolveAddMcpAgentId, uniqueAgentIds } from "../mcp/agents.js";
|
|
2
2
|
import "../init/agents.js";
|
|
3
|
+
import { delimiter, dirname, join, resolve } from "node:path";
|
|
4
|
+
import which from "which";
|
|
3
5
|
import { detectProjectAgents } from "add-mcp";
|
|
4
6
|
//#region src/plugins/targets.ts
|
|
7
|
+
const CLAUDE_CODE = { target: "claude-code" };
|
|
5
8
|
const PLUGINS_TARGET_BY_TYPE = {
|
|
6
|
-
cursor: "cursor",
|
|
7
|
-
vscode: "vscode",
|
|
8
|
-
"claude-code":
|
|
9
|
-
"claude-desktop":
|
|
10
|
-
codex: "codex",
|
|
11
|
-
"github-copilot-cli":
|
|
12
|
-
|
|
9
|
+
cursor: { target: "cursor" },
|
|
10
|
+
vscode: { target: "vscode" },
|
|
11
|
+
"claude-code": CLAUDE_CODE,
|
|
12
|
+
"claude-desktop": CLAUDE_CODE,
|
|
13
|
+
codex: { target: "codex" },
|
|
14
|
+
"github-copilot-cli": {
|
|
15
|
+
target: "github-copilot",
|
|
16
|
+
command: "copilot"
|
|
17
|
+
},
|
|
18
|
+
"grok-build": {
|
|
19
|
+
target: "grok",
|
|
20
|
+
command: "grok"
|
|
21
|
+
}
|
|
13
22
|
};
|
|
23
|
+
const pluginsTargets = () => [...new Set(Object.values(PLUGINS_TARGET_BY_TYPE))];
|
|
14
24
|
const USER_SCOPE_ONLY_TARGETS = /* @__PURE__ */ new Set([
|
|
15
25
|
"vscode",
|
|
16
26
|
"github-copilot",
|
|
17
27
|
"grok"
|
|
18
28
|
]);
|
|
19
|
-
|
|
29
|
+
/** npx runs the plugins CLI with every `node_modules/.bin` from `cwd` up to the root ahead of PATH. */
|
|
30
|
+
const pluginsCommandSearchPath = (cwd, path) => {
|
|
31
|
+
const bins = [];
|
|
32
|
+
let dir = resolve(cwd);
|
|
33
|
+
for (;;) {
|
|
34
|
+
bins.push(join(dir, "node_modules", ".bin"));
|
|
35
|
+
const parent = dirname(dir);
|
|
36
|
+
if (parent === dir) break;
|
|
37
|
+
dir = parent;
|
|
38
|
+
}
|
|
39
|
+
return [...bins, path].join(delimiter);
|
|
40
|
+
};
|
|
41
|
+
const missingPluginsCommands = (agents, options) => {
|
|
42
|
+
const searchPath = pluginsCommandSearchPath(options.cwd, options.path ?? process.env.PATH ?? "");
|
|
43
|
+
const missing = [];
|
|
44
|
+
for (const agent of uniqueAgentIds(agents)) {
|
|
45
|
+
const command = getPluginsTarget(agent)?.command;
|
|
46
|
+
if (command !== void 0 && which.sync(command, {
|
|
47
|
+
nothrow: true,
|
|
48
|
+
path: searchPath
|
|
49
|
+
}) === null) missing.push({
|
|
50
|
+
agent,
|
|
51
|
+
command
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return missing;
|
|
55
|
+
};
|
|
56
|
+
function getPluginsTarget(agent) {
|
|
20
57
|
if (Object.prototype.hasOwnProperty.call(PLUGINS_TARGET_BY_TYPE, agent)) return PLUGINS_TARGET_BY_TYPE[agent];
|
|
21
58
|
const id = tryResolveAddMcpAgentId(agent);
|
|
22
59
|
if (!id) return void 0;
|
|
23
60
|
return PLUGINS_TARGET_BY_TYPE[id];
|
|
24
61
|
}
|
|
62
|
+
function getPluginsTargetName(agent) {
|
|
63
|
+
return getPluginsTarget(agent)?.target;
|
|
64
|
+
}
|
|
25
65
|
function supportsPlugins(agent) {
|
|
26
66
|
return getPluginsTargetName(agent) !== void 0;
|
|
27
67
|
}
|
|
@@ -65,4 +105,4 @@ const mappedPluginsTargets = (agents, scope) => {
|
|
|
65
105
|
return mapped;
|
|
66
106
|
};
|
|
67
107
|
//#endregion
|
|
68
|
-
export { detectInstallablePluginsAgents, detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, pluginsInstallableAgents, pluginsMappedAgents, supportsPlugins };
|
|
108
|
+
export { detectInstallablePluginsAgents, detectPluginsAgents, getPluginsTargetName, isUserScopeOnlyPluginsTarget, mappedPluginsTargets, missingPluginsCommands, pluginsInstallableAgents, pluginsMappedAgents, pluginsTargets, supportsPlugins };
|