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/commands/init.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import { log } from "../log.js";
|
|
3
2
|
import { readContextFile } from "../context.js";
|
|
4
3
|
import { getCliName } from "../utils/cli_name.js";
|
|
5
4
|
import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
6
5
|
import { initChildEnv, spawnCliChild } from "../init/child.js";
|
|
7
|
-
import {
|
|
6
|
+
import { agentSetupLabel, formatInitDone, printInitBanner, printInitDone, shouldPrintInitBanner } from "../init/chrome.js";
|
|
7
|
+
import { coerceAgentFlag } from "../utils/agent_flag.js";
|
|
8
|
+
import { INIT_NEEDS_YES_OR_TERMINAL, assertNamedAgentTooling, bootstrapInitStep, directoryIsEmpty, initPluginAgents, initSkillsMcpAgents, planExistingInit, resolveNamedAgents } from "../init/plan.js";
|
|
9
|
+
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
8
10
|
import { runAgentTooling, runInitSteps } from "../init/tooling.js";
|
|
9
|
-
import { helpEpilogue } from "../utils/help_text.js";
|
|
11
|
+
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
10
12
|
import { existsSync, readdirSync } from "node:fs";
|
|
11
13
|
import { resolve } from "node:path";
|
|
12
14
|
//#region src/commands/init.ts
|
|
@@ -19,15 +21,18 @@ var init_exports = /* @__PURE__ */ __exportAll({
|
|
|
19
21
|
});
|
|
20
22
|
const command = "init";
|
|
21
23
|
const describe = "Set up this directory for Neon: agent tooling, a linked project, and neon.ts. In an empty directory, scaffolds a template first. Skills needs Node.js 22.20 or newer.";
|
|
22
|
-
const removedProtocol = () => `\`${getCliName()} init --
|
|
24
|
+
const removedProtocol = () => `\`${getCliName()} init --data\` was removed. Run \`${getCliName()} init\` or \`${getCliName()} init -y\`.`;
|
|
23
25
|
const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file", { hidden: true }).option("yes", {
|
|
24
26
|
alias: "y",
|
|
25
27
|
type: "boolean",
|
|
26
28
|
default: false,
|
|
27
|
-
describe: "Empty dir: bootstrap --default. Otherwise plugin
|
|
29
|
+
describe: "Empty dir: bootstrap --default. Otherwise plugin, or skills and MCP, for project folders, else the host CLI agent. Exits if none. Then link --yes and config init --services none. link --yes still asks for a project unless one is already linked"
|
|
28
30
|
}).option("agent", {
|
|
29
|
-
|
|
30
|
-
type: "
|
|
31
|
+
alias: "a",
|
|
32
|
+
type: "array",
|
|
33
|
+
string: true,
|
|
34
|
+
describe: "Coding agent to install into (repeatable). Forwarded to plugins, or to skills and mcp. Skips agent selection. Values listed below",
|
|
35
|
+
coerce: coerceAgentFlag
|
|
31
36
|
}).option("data", {
|
|
32
37
|
hidden: true,
|
|
33
38
|
type: "string"
|
|
@@ -35,8 +40,8 @@ const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file
|
|
|
35
40
|
alias: "o",
|
|
36
41
|
hidden: true,
|
|
37
42
|
describe: "Not supported; the commands init runs print their own output"
|
|
38
|
-
}).example("$0 init", "Empty dir: bootstrap (scaffold, agent tooling, link). Existing app: agent tooling, link, config init").example("$0 init -y", "Same steps, using each child's defaults").epilogue(helpEpilogue("Interactive agent setup: plugin (recommended), skills and MCP separately, or skip agent setup. Never both plugin and skills+MCP.", "-y installs the plugin when Cursor, Claude Code, or Codex is
|
|
39
|
-
if (argv.
|
|
43
|
+
}).example("$0 init", "Empty dir: bootstrap (scaffold, agent tooling, link). Existing app: agent tooling, link, config init").example("$0 init -y", "Same steps, using each child's defaults").example("$0 init --agent cursor --agent claude-code", "Skip agent selection; install the plugin for those agents").epilogue(helpEpilogue("Interactive agent setup: plugin (recommended), skills and MCP separately, or skip agent setup. Never both plugin and skills+MCP.", "-y installs the plugin when Cursor, Claude Code, or Codex is in project folders, else the host CLI agent. Otherwise skills and MCP. If none are found, it exits: pass --agent <name>, run from a supported agent, or omit -y in a terminal to pick. Then link unless already linked. link --yes may still ask for a project.", "--agent / -a is forwarded to plugins, or to skills and mcp, not both. It skips agent selection, including with -y.", helpCsv("Plugin agents", initPluginAgents()), helpCsv("Skills and MCP agents", initSkillsMcpAgents()))).check((argv) => {
|
|
44
|
+
if (argv.data !== void 0) throw new Error(removedProtocol());
|
|
40
45
|
if (argv.help !== true && (argv.output === "json" || argv.output === "yaml")) throw new Error(`\`${getCliName()} init\` does not support --output. The commands it runs print their own output.`);
|
|
41
46
|
return true;
|
|
42
47
|
}).strict();
|
|
@@ -50,6 +55,8 @@ const handler = async (props) => {
|
|
|
50
55
|
const names = existsSync(cwd) ? readdirSync(cwd) : [];
|
|
51
56
|
const contextFile = resolve(cwd, props.contextFile);
|
|
52
57
|
const yes = props.yes === true;
|
|
58
|
+
const named = resolveNamedAgents(props.agent ?? []);
|
|
59
|
+
assertNamedAgentTooling(named, "init", yes ? { yes: true } : void 0);
|
|
53
60
|
const run = props.run ?? spawnCliChild;
|
|
54
61
|
const explicitKey = props.profile ? "" : credentialInputs().apiKeyFlag;
|
|
55
62
|
const authEnv = explicitKey ? { NEON_API_KEY: explicitKey } : void 0;
|
|
@@ -61,26 +68,32 @@ const handler = async (props) => {
|
|
|
61
68
|
...props.analytics === false ? { analytics: false } : {}
|
|
62
69
|
};
|
|
63
70
|
if (directoryIsEmpty(names)) {
|
|
64
|
-
|
|
71
|
+
if (!yes && !canPickAgentsInteractively()) throw new Error(INIT_NEEDS_YES_OR_TERMINAL);
|
|
72
|
+
await runInitSteps([bootstrapInitStep(yes, named)], {
|
|
65
73
|
cwd,
|
|
66
74
|
run,
|
|
67
75
|
forward,
|
|
68
76
|
authEnv
|
|
69
77
|
});
|
|
70
|
-
log.info("Done.");
|
|
71
78
|
return;
|
|
72
79
|
}
|
|
73
|
-
|
|
80
|
+
if (shouldPrintInitBanner(yes)) printInitBanner();
|
|
81
|
+
const alreadyLinked = isLinked(contextFile);
|
|
82
|
+
const agentSetup = await runAgentTooling({
|
|
74
83
|
cwd,
|
|
75
84
|
yes,
|
|
76
85
|
run,
|
|
77
86
|
forward,
|
|
78
87
|
authEnv,
|
|
88
|
+
...named.length > 0 ? { agents: named } : {},
|
|
79
89
|
...props.pickAgentSetup ? { pickAgentSetup: props.pickAgentSetup } : {},
|
|
80
|
-
...props.
|
|
90
|
+
...props.detectProjectAgents ? { detectProjectAgents: props.detectProjectAgents } : {},
|
|
91
|
+
...props.detectAgent ? { detectAgent: props.detectAgent } : {},
|
|
92
|
+
...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {},
|
|
93
|
+
command: "init"
|
|
81
94
|
});
|
|
82
95
|
await runInitSteps(planExistingInit({
|
|
83
|
-
linked:
|
|
96
|
+
linked: alreadyLinked,
|
|
84
97
|
yes,
|
|
85
98
|
agentSetup: "skip"
|
|
86
99
|
}), {
|
|
@@ -89,7 +102,24 @@ const handler = async (props) => {
|
|
|
89
102
|
forward,
|
|
90
103
|
authEnv
|
|
91
104
|
});
|
|
92
|
-
|
|
105
|
+
printInitDone(formatInitDone({
|
|
106
|
+
heading: "Configured this directory for Neon.",
|
|
107
|
+
rows: [
|
|
108
|
+
{
|
|
109
|
+
label: "Agents",
|
|
110
|
+
value: agentSetupLabel(agentSetup)
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
label: "Project",
|
|
114
|
+
value: alreadyLinked ? "already linked" : "linked"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
label: "Config",
|
|
118
|
+
value: "neon.ts"
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
next: []
|
|
122
|
+
}));
|
|
93
123
|
};
|
|
94
124
|
//#endregion
|
|
95
125
|
export { builder, command, describe, handler, initChildEnv, init_exports as t };
|
package/dist/commands/link.js
CHANGED
|
@@ -4,10 +4,10 @@ import { log } from "../log.js";
|
|
|
4
4
|
import { isNeonApiError, messageFromBody } from "../api.js";
|
|
5
5
|
import { applyContext, contextBranch, readContextFile, setContext, updateContextFile } from "../context.js";
|
|
6
6
|
import { getCliName } from "../utils/cli_name.js";
|
|
7
|
+
import { helpEpilogue } from "../utils/help_text.js";
|
|
7
8
|
import { createBranch, pickBranchInteractively } from "../utils/branch_picker.js";
|
|
8
9
|
import { autoPullEnvAfterPin } from "./env.js";
|
|
9
10
|
import { hasNeonConfigFile, initCmd } from "./config.js";
|
|
10
|
-
import { helpEpilogue } from "../utils/help_text.js";
|
|
11
11
|
import { REGIONS } from "./projects.js";
|
|
12
12
|
import prompts from "prompts";
|
|
13
13
|
//#region src/commands/link.ts
|
|
@@ -31,7 +31,6 @@ const nonInteractiveLinkCommands = () => {
|
|
|
31
31
|
};
|
|
32
32
|
const nonInteractiveLinkHelp = () => nonInteractiveLinkCommands().map((command) => ` ${command}`).join("\n");
|
|
33
33
|
const orgScopedKeyHint = "Organization-scoped API keys cannot list orgs; pass --org-id.";
|
|
34
|
-
const removedAgent = () => `\`${getCliName()} link --agent\` was removed.\nUse:\n${nonInteractiveLinkHelp()}\n${orgScopedKeyHint}`;
|
|
35
34
|
const command = "link";
|
|
36
35
|
const describe = "Link the current directory to a Neon project";
|
|
37
36
|
const builder = (argv) => argv.usage("$0 link [options]").options({
|
|
@@ -60,10 +59,6 @@ const builder = (argv) => argv.usage("$0 link [options]").options({
|
|
|
60
59
|
describe: "JSON object with link parameters, e.g. '{\"orgId\":\"...\",\"projectId\":\"...\"}' or '{\"orgId\":\"...\",\"projectName\":\"...\",\"regionId\":\"...\"}'. Flags take precedence over fields in --params.",
|
|
61
60
|
type: "string"
|
|
62
61
|
},
|
|
63
|
-
agent: {
|
|
64
|
-
hidden: true,
|
|
65
|
-
type: "boolean"
|
|
66
|
-
},
|
|
67
62
|
yes: {
|
|
68
63
|
alias: "y",
|
|
69
64
|
describe: "Skip the \"already linked\" confirmation, and pin the project's default branch when linking a project that has more than one.",
|
|
@@ -92,10 +87,7 @@ const builder = (argv) => argv.usage("$0 link [options]").options({
|
|
|
92
87
|
["$0 link --branch-id br-…", "Pin a branch in the already-linked project"],
|
|
93
88
|
["$0 link --no-checks --org-id org-… --project-id polished-snowflake-12345678", "Write the context offline (no API calls, no verification)"],
|
|
94
89
|
["$0 link --clear", "Forget the current org/project/branch context"]
|
|
95
|
-
]).epilogue(helpEpilogue("Non-interactive (CI, scripts, agents):", ...nonInteractiveLinkCommands().map((command) => ` ${command}`), orgScopedKeyHint)).
|
|
96
|
-
if (argv.agent === true) throw new Error(removedAgent());
|
|
97
|
-
return true;
|
|
98
|
-
});
|
|
90
|
+
]).epilogue(helpEpilogue("Non-interactive (CI, scripts, agents):", ...nonInteractiveLinkCommands().map((command) => ` ${command}`), orgScopedKeyHint)).strict();
|
|
99
91
|
const handler = async (props) => {
|
|
100
92
|
if (props.clear) {
|
|
101
93
|
clearContext(props.contextFile);
|
package/dist/commands/mcp.js
CHANGED
|
@@ -4,11 +4,11 @@ import { readContextFile } from "../context.js";
|
|
|
4
4
|
import { getCliName } from "../utils/cli_name.js";
|
|
5
5
|
import { writer } from "../writer.js";
|
|
6
6
|
import { noPassthrough, single } from "../utils/flags.js";
|
|
7
|
+
import { NEON_MCP_CATEGORIES, NEON_MCP_URL, existingNeonApiKey, installNeonMcpServer, neonMcpUrl, parseMcpCategories, trackedProjectMcpConfig } from "../mcp/install.js";
|
|
8
|
+
import { mcpInstallableAgents, resolveInstallTargets } from "../mcp/targets.js";
|
|
7
9
|
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
8
10
|
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
9
|
-
import { NEON_MCP_CATEGORIES, NEON_MCP_URL, existingNeonApiKey, installNeonMcpServer, neonMcpUrl, parseMcpCategories, trackedProjectMcpConfig } from "../mcp/install.js";
|
|
10
11
|
import { mintMcpApiKey, mintedKeyRevokeCommand, withdrawMintedKey } from "../mcp/mint.js";
|
|
11
|
-
import { mcpInstallableAgents, resolveInstallTargets } from "../mcp/targets.js";
|
|
12
12
|
import { confirmMcpInstall } from "../mcp/wizard.js";
|
|
13
13
|
import { resolveMcpPlan } from "../mcp/plan.js";
|
|
14
14
|
//#region src/commands/mcp.ts
|
|
@@ -38,7 +38,7 @@ const builder = (argv) => argv.usage("$0 mcp [options]").options({
|
|
|
38
38
|
alias: "y",
|
|
39
39
|
type: "boolean",
|
|
40
40
|
default: false,
|
|
41
|
-
describe: "Skip prompts. Defaults listed below. --
|
|
41
|
+
describe: "Skip prompts. Defaults listed below. --agent, --project, --oauth, --read-only, --project-id and --category still apply"
|
|
42
42
|
},
|
|
43
43
|
agent: {
|
|
44
44
|
alias: "a",
|
|
@@ -86,7 +86,7 @@ const builder = (argv) => argv.usage("$0 mcp [options]").options({
|
|
|
86
86
|
return parseMcpCategories(parts);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
-
}).example("$0 mcp", "Interactive: config location, agents, auth, then confirm").example("$0 mcp -y", "Global config,
|
|
89
|
+
}).example("$0 mcp", "Interactive: config location, agents, auth, then confirm").example("$0 mcp -y", "Global config, installed apps else the host CLI agent, reuse or mint an API key").example("$0 mcp --oauth", "Install with OAuth; the agent signs in on first use").example("$0 mcp --agent cursor --agent claude-code", "Install into specific agents").example("$0 mcp --project", "Write project-level config").example("$0 mcp --read-only", "Hide write tools via ?readonly=true").example("$0 mcp --project-id <id>", "Pin tools to one project via ?projectId=").example("$0 mcp --category querying --category schema", "Limit tools to those categories").epilogue(helpEpilogue(`Installs ${NEON_MCP_URL}`, helpCsv("Supported agents at global scope", mcpGlobalAgents), helpCsv("--project does not support", mcpProjectDroppedAgents), helpCsv("Supported categories", NEON_MCP_CATEGORIES), "neon mcp -y:", " global config", " globally installed apps, else the host CLI agent", " reuse an existing Neon MCP API key, else mint an account-wide key", " write tools on, all categories", " no project pin (including from .neon)")).strict().check(noPassthrough("mcp"));
|
|
90
90
|
const handler = async (props) => {
|
|
91
91
|
const cwd = process.cwd();
|
|
92
92
|
const interactive = canPickAgentsInteractively() && props.yes !== true;
|
package/dist/commands/plugins.js
CHANGED
|
@@ -3,12 +3,12 @@ import { log } from "../log.js";
|
|
|
3
3
|
import { writer } from "../writer.js";
|
|
4
4
|
import { noPassthrough } from "../utils/flags.js";
|
|
5
5
|
import { getAgentDisplayName } from "../mcp/agents.js";
|
|
6
|
+
import { NEON_MCP_URL } from "../mcp/install.js";
|
|
6
7
|
import "../init/agents.js";
|
|
7
8
|
import { pluginsInstallableAgents } from "../plugins/targets.js";
|
|
8
9
|
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
9
10
|
import { NEON_PLUGIN_NAME, PLUGIN_SKILLS, PLUGIN_SOURCE, neonPluginsRetryCommand, pluginsAddArgs, runPluginsCli } from "../plugins/run.js";
|
|
10
11
|
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
11
|
-
import { NEON_MCP_URL } from "../mcp/install.js";
|
|
12
12
|
import { resolvePluginsPlan } from "../plugins/plan.js";
|
|
13
13
|
import { confirmPluginsInstall } from "../plugins/wizard.js";
|
|
14
14
|
//#region src/commands/plugins.ts
|
|
@@ -18,7 +18,6 @@ var plugins_exports = /* @__PURE__ */ __exportAll({
|
|
|
18
18
|
describe: () => describe,
|
|
19
19
|
handler: () => handler
|
|
20
20
|
});
|
|
21
|
-
const scopeLabel = (scope) => scope === "project" ? "project" : "user";
|
|
22
21
|
const coerceAgents = (value) => {
|
|
23
22
|
if (value === void 0) return [];
|
|
24
23
|
const list = Array.isArray(value) ? value : [value];
|
|
@@ -28,6 +27,7 @@ const coerceAgents = (value) => {
|
|
|
28
27
|
return item;
|
|
29
28
|
});
|
|
30
29
|
};
|
|
30
|
+
const scopeLabel = (scope) => scope === "project" ? "project" : "user";
|
|
31
31
|
const command = "plugins";
|
|
32
32
|
const describe = "Install the Neon plugin into coding agents";
|
|
33
33
|
const pluginProjectAgents = pluginsInstallableAgents("project");
|
|
@@ -37,7 +37,7 @@ const builder = (argv) => argv.usage("$0 plugins [options]").options({
|
|
|
37
37
|
alias: "y",
|
|
38
38
|
type: "boolean",
|
|
39
39
|
default: false,
|
|
40
|
-
describe: "Skip prompts"
|
|
40
|
+
describe: "Skip prompts. Detected agents (project folders, else the host CLI agent). --global uses installed apps, else the host CLI agent"
|
|
41
41
|
},
|
|
42
42
|
global: {
|
|
43
43
|
type: "boolean",
|
|
@@ -51,7 +51,7 @@ const builder = (argv) => argv.usage("$0 plugins [options]").options({
|
|
|
51
51
|
describe: "Coding agent to install into (repeatable). Skips the agent picker. Values listed below",
|
|
52
52
|
coerce: coerceAgents
|
|
53
53
|
}
|
|
54
|
-
}).example("$0 plugins", "Interactive: agents, then confirm").example("$0 plugins -y", "Detected agents, skip prompts").example("$0 plugins --agent cursor --agent claude-code", "Install into specific agents").example("$0 plugins --global", "Install user-level").epilogue(helpEpilogue(helpCsv("Supported agents at project scope", pluginProjectAgents), helpCsv("Also with --global", pluginGlobalOnlyAgents), `Currently one plugin: ${NEON_PLUGIN_NAME} from ${PLUGIN_SOURCE}.`, `It includes the Neon MCP server (${NEON_MCP_URL})`, helpCsv("and these skills", PLUGIN_SKILLS))).strict().check(noPassthrough("plugins"));
|
|
54
|
+
}).example("$0 plugins", "Interactive: agents, then confirm").example("$0 plugins -y", "Detected agents (project folders, else the host CLI agent), skip prompts").example("$0 plugins --agent cursor --agent claude-code", "Install into specific agents").example("$0 plugins --global", "Install user-level").epilogue(helpEpilogue(helpCsv("Supported agents at project scope", pluginProjectAgents), helpCsv("Also with --global", pluginGlobalOnlyAgents), `Currently one plugin: ${NEON_PLUGIN_NAME} from ${PLUGIN_SOURCE}.`, `It includes the Neon MCP server (${NEON_MCP_URL})`, helpCsv("and these skills", PLUGIN_SKILLS))).strict().check(noPassthrough("plugins"));
|
|
55
55
|
const handler = async (props) => {
|
|
56
56
|
const cwd = process.cwd();
|
|
57
57
|
const yes = props.yes === true;
|
package/dist/commands/skills.js
CHANGED
|
@@ -4,10 +4,10 @@ import { writer } from "../writer.js";
|
|
|
4
4
|
import { noPassthrough } from "../utils/flags.js";
|
|
5
5
|
import { getAgentDisplayName } from "../mcp/agents.js";
|
|
6
6
|
import "../init/agents.js";
|
|
7
|
+
import { mappedSkillsAgentNames, skillsInstallableAgents } from "../skills/targets.js";
|
|
7
8
|
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
8
9
|
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
9
10
|
import { skillsHelpValues, skillsYesHelp } from "../skills/catalog.js";
|
|
10
|
-
import { mappedSkillsAgentNames, skillsInstallableAgents } from "../skills/targets.js";
|
|
11
11
|
import { confirmSkillsInstall, confirmSkillsUpdate } from "../skills/wizard.js";
|
|
12
12
|
import { assertSkillsCanRun, resolveSkillsPlan } from "../skills/plan.js";
|
|
13
13
|
import { assertSkillsNode, neonSkillsRetryCommand, neonSkillsUpdateRetryCommand, runSkillsCli, skillsAddArgs, skillsMetadata, skillsUpdateArgs, skillsUpdateDetail, skillsUpdateHadNothing } from "../skills/run.js";
|
|
@@ -39,11 +39,14 @@ const coerceSkills = (value) => {
|
|
|
39
39
|
};
|
|
40
40
|
const command = "skills";
|
|
41
41
|
const describe = "Install Neon agent skills into coding agents";
|
|
42
|
-
const builder = (argv) => argv.usage("$0 skills [command] [options]").command("update", "Update installed skills to the latest versions", (yargs) => yargs.usage("$0 skills update [options]").options({
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
const builder = (argv) => argv.usage("$0 skills [command] [options]").command("update", "Update installed skills to the latest versions", (yargs) => yargs.usage("$0 skills update [options]").options({
|
|
43
|
+
yes: { describe: "Skip the confirm prompt" },
|
|
44
|
+
global: {
|
|
45
|
+
type: "boolean",
|
|
46
|
+
default: false,
|
|
47
|
+
describe: "Update user-level skills (skills CLI -g). Default is this directory"
|
|
48
|
+
}
|
|
49
|
+
}).example("$0 skills update", "Interactive: confirm, then update skills in this directory").example("$0 skills update -y", "Update skills in this directory without prompting").example("$0 skills update --global -y", "Update user-level skills").strict().hide("agent").hide("skill").check((args) => {
|
|
47
50
|
noPassthrough("skills update")(args);
|
|
48
51
|
const agents = args.agent;
|
|
49
52
|
if (Array.isArray(agents) && agents.length > 0) throw new Error("neon skills update does not take --agent. It refreshes every installed skill in this directory (or --global).");
|
|
@@ -55,7 +58,7 @@ const builder = (argv) => argv.usage("$0 skills [command] [options]").command("u
|
|
|
55
58
|
alias: "y",
|
|
56
59
|
type: "boolean",
|
|
57
60
|
default: false,
|
|
58
|
-
describe: "Skip prompts"
|
|
61
|
+
describe: "Skip prompts. Detected agents (project folders, else the host CLI agent). --global uses installed apps, else the host CLI agent"
|
|
59
62
|
},
|
|
60
63
|
global: {
|
|
61
64
|
type: "boolean",
|
|
@@ -73,10 +76,10 @@ const builder = (argv) => argv.usage("$0 skills [command] [options]").command("u
|
|
|
73
76
|
alias: "s",
|
|
74
77
|
type: "array",
|
|
75
78
|
string: true,
|
|
76
|
-
describe: "Skill to install (repeatable). Skips the skill picker. Values listed below",
|
|
79
|
+
describe: "Skill to install (repeatable). Skips the skill picker. Does not select agents. Values listed below",
|
|
77
80
|
coerce: coerceSkills
|
|
78
81
|
}
|
|
79
|
-
}).example("$0 skills", "Interactive: this directory, agents, skills, then confirm").example("$0 skills -y", "This directory, detected agents, the default skills").example("$0 skills -s neon -s neon-ai-gateway --agent cursor", "
|
|
82
|
+
}).example("$0 skills", "Interactive: this directory, agents, skills, then confirm").example("$0 skills -y", "This directory, detected agents (project folders, else the host CLI agent), the default skills").example("$0 skills -y -s neon -s neon-ai-gateway", "Named skills into detected agents").example("$0 skills -s neon -s neon-ai-gateway --agent cursor", "Named skills into a named agent").example("$0 skills --global", "Install user-level skills").epilogue(helpEpilogue(helpCsv("Supported agents", skillsInstallableAgents()), "Supported skills, by source repo:", skillsHelpValues(), skillsYesHelp())).strict().check(noPassthrough("skills"));
|
|
80
83
|
const handler = async (props) => {
|
|
81
84
|
assertSkillsNode();
|
|
82
85
|
const cwd = process.cwd();
|
package/dist/errors.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
const ERROR_MATCHERS = [
|
|
3
3
|
[/^Unknown command: (.*)$/, "UNKNOWN_COMMAND"],
|
|
4
4
|
[/^Missing required argument: (.*)$/, "MISSING_ARGUMENT"],
|
|
5
|
-
[/^Failed to open web browser. (.*)$/, "AUTH_BROWSER_FAILED"]
|
|
5
|
+
[/^Failed to open web browser. (.*)$/, "AUTH_BROWSER_FAILED"],
|
|
6
|
+
[/^Setup did not finish\.$/, "NEON_INIT_FAILED"]
|
|
6
7
|
];
|
|
7
8
|
const matchErrorCode = (message) => {
|
|
8
9
|
if (!message) return "UNKNOWN_ERROR";
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { defaultClientID } from "./auth.js";
|
|
|
11
11
|
import { getCliName } from "./utils/cli_name.js";
|
|
12
12
|
import { showHelp } from "./help.js";
|
|
13
13
|
import { deleteCredentialsAt, ensureAuth } from "./commands/auth.js";
|
|
14
|
+
import { rewriteUnknownAgentArg } from "./init/plan.js";
|
|
14
15
|
import commands_default from "./commands/index.js";
|
|
15
16
|
import { fillInArgs, resolveApiKeyFromEnv } from "./utils/middlewares.js";
|
|
16
17
|
import yargs from "yargs";
|
|
@@ -125,6 +126,19 @@ async function handleError(msg, err) {
|
|
|
125
126
|
process.exit(0);
|
|
126
127
|
}
|
|
127
128
|
if (err instanceof Error && err.stack) log.debug("Stack: %s", err.stack);
|
|
129
|
+
if (err instanceof Error) {
|
|
130
|
+
const rewritten = rewriteUnknownAgentArg({
|
|
131
|
+
message: err.message,
|
|
132
|
+
argv: process.argv,
|
|
133
|
+
cliName: getCliName()
|
|
134
|
+
});
|
|
135
|
+
if (rewritten !== void 0) {
|
|
136
|
+
const error = new Error(rewritten);
|
|
137
|
+
sendError(error, matchErrorCode(error.message));
|
|
138
|
+
log.error(error.message);
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
128
142
|
if (isNetworkError(err)) {
|
|
129
143
|
log.error(NETWORK_ERROR_MESSAGE);
|
|
130
144
|
const error = err instanceof Error ? err : new Error(NETWORK_ERROR_MESSAGE);
|
|
@@ -162,8 +176,9 @@ async function handleError(msg, err) {
|
|
|
162
176
|
}
|
|
163
177
|
} else {
|
|
164
178
|
const error = err instanceof Error ? err : new Error(msg || "Unknown error");
|
|
165
|
-
|
|
166
|
-
|
|
179
|
+
const code = matchErrorCode(error.message);
|
|
180
|
+
sendError(error, code);
|
|
181
|
+
if (code !== "NEON_INIT_FAILED") log.error(error.message);
|
|
167
182
|
return false;
|
|
168
183
|
}
|
|
169
184
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { isCi } from "../env.js";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
//#region src/init/chrome.ts
|
|
4
|
+
const NEON_GREEN = "#4BB578";
|
|
5
|
+
const INIT_BANNER_LINES = [
|
|
6
|
+
" ██╗ ██╗██████╗ ██████╗ ██╗ ██╗",
|
|
7
|
+
" ███╗ ██║██╔═══╝ ██╔═══██╗███╗ ██║",
|
|
8
|
+
" ████╗██║██████╗ ██║ ██║████╗██║",
|
|
9
|
+
" ██╔████║██╔═══╝ ██║ ██║██╔████║",
|
|
10
|
+
" ██║╚███║██████╗ ╚██████╔╝██║╚███║",
|
|
11
|
+
" ╚═╝ ╚══╝╚═════╝ ╚═════╝ ╚═╝ ╚══╝"
|
|
12
|
+
];
|
|
13
|
+
const formatInitBanner = () => INIT_BANNER_LINES.join("\n");
|
|
14
|
+
const shouldPrintInitBanner = (yes) => !yes && !isCi() && Boolean(process.stdout.isTTY);
|
|
15
|
+
const printInitBanner = () => {
|
|
16
|
+
process.stdout.write(`\n${chalk.hex(NEON_GREEN)(formatInitBanner())}\n\n${chalk.dim("Let's get this directory set up with Neon.")}\n\n`);
|
|
17
|
+
};
|
|
18
|
+
const formatInitDone = (input) => {
|
|
19
|
+
const labelWidth = Math.max(0, ...input.rows.map((row) => row.label.length));
|
|
20
|
+
const body = input.rows.length === 0 ? "" : `\n\n${input.rows.map((row) => ` ${row.label.padEnd(labelWidth)} ${row.value}`).join("\n")}`;
|
|
21
|
+
const next = input.next.length === 0 ? "" : `\n\nNext:\n${input.next.map((line) => ` ${line}`).join("\n")}`;
|
|
22
|
+
const rule = "-".repeat(input.heading.length);
|
|
23
|
+
return `${input.heading}\n${rule}${body}${next}\n`;
|
|
24
|
+
};
|
|
25
|
+
const printInitDone = (text) => {
|
|
26
|
+
const painted = (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n").map((line, index) => {
|
|
27
|
+
if (index === 0) return chalk.hex(NEON_GREEN).bold(line);
|
|
28
|
+
if (/^-{3,}$/.test(line)) return chalk.hex(NEON_GREEN)(line);
|
|
29
|
+
if (line === "Next:") return chalk.bold(line);
|
|
30
|
+
return line;
|
|
31
|
+
});
|
|
32
|
+
process.stdout.write(`\n${painted.join("\n")}\n\n`);
|
|
33
|
+
};
|
|
34
|
+
const agentSetupLabel = (setup) => {
|
|
35
|
+
if (setup === "plugin") return "plugin";
|
|
36
|
+
if (setup === "skills-mcp") return "skills and MCP";
|
|
37
|
+
return "skipped";
|
|
38
|
+
};
|
|
39
|
+
const agentSetupDoneLabel = (input) => {
|
|
40
|
+
if (!input.ran && input.setup !== "skip") return "not run";
|
|
41
|
+
return agentSetupLabel(input.setup);
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
export { INIT_BANNER_LINES, NEON_GREEN, agentSetupDoneLabel, agentSetupLabel, formatInitBanner, formatInitDone, printInitBanner, printInitDone, shouldPrintInitBanner };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/init/detect_host.ts
|
|
2
|
+
const CLAUDE_ENV = ["CLAUDECODE", "CLAUDE_CODE_CHILD_SESSION"];
|
|
3
|
+
const CURSOR_ENV = [
|
|
4
|
+
"CURSOR_TRACE_ID",
|
|
5
|
+
"CURSOR_EXTENSION_HOST_ROLE",
|
|
6
|
+
"CURSOR_LAYOUT",
|
|
7
|
+
"CURSOR_SPAWNED_BY_EXTENSION_ID"
|
|
8
|
+
];
|
|
9
|
+
const CURSOR_PATH_ENV = [
|
|
10
|
+
"GIT_ASKPASS",
|
|
11
|
+
"VSCODE_GIT_ASKPASS_NODE",
|
|
12
|
+
"VSCODE_GIT_ASKPASS_MAIN",
|
|
13
|
+
"VSCODE_IPC_HOOK_CLI"
|
|
14
|
+
];
|
|
15
|
+
const looksLikeCursor = (value) => {
|
|
16
|
+
const lower = value.toLowerCase();
|
|
17
|
+
return lower.includes("cursor.app") || lower.includes("/cursor/") || lower.includes("/.cursor/");
|
|
18
|
+
};
|
|
19
|
+
const isSet = (value) => value !== void 0;
|
|
20
|
+
const isNonEmpty = (value) => typeof value === "string" && value.trim() !== "";
|
|
21
|
+
function detectAgent(env = process.env) {
|
|
22
|
+
if (CLAUDE_ENV.some((key) => env[key] === "1")) return "claude-code";
|
|
23
|
+
if (env.CODEX === "1") return "codex";
|
|
24
|
+
if (isNonEmpty(env.CODEX_THREAD_ID) || isNonEmpty(env.CODEX_SESSION_ID)) return "codex";
|
|
25
|
+
if (env.GEMINI_CLI === "1") return "gemini-cli";
|
|
26
|
+
if (env.OPENCODE === "1") return "opencode";
|
|
27
|
+
if (env.GOOSE_TERMINAL === "1" || env.AGENT === "goose") return "goose";
|
|
28
|
+
if (env.TERM_PROGRAM === "cursor" || CURSOR_ENV.some((key) => isSet(env[key])) || CURSOR_PATH_ENV.some((key) => {
|
|
29
|
+
const value = env[key];
|
|
30
|
+
return typeof value === "string" && looksLikeCursor(value);
|
|
31
|
+
})) return "cursor";
|
|
32
|
+
if (env.TERM_PROGRAM === "windsurf") return "windsurf";
|
|
33
|
+
if (env.TERM_PROGRAM === "zed") return "zed";
|
|
34
|
+
if (env.TERM_PROGRAM === "vscode" || isSet(env.VSCODE_PID) || isSet(env.VSCODE_CWD)) return "vscode";
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { detectAgent };
|