@xevy/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +71 -0
  2. package/bin/xevy.mjs +256 -0
  3. package/package.json +35 -0
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # @xevy/cli
2
+
3
+ The **`xevy`** command — one step to get Xevy working in your AI agent.
4
+
5
+ ```bash
6
+ npx @xevy/cli setup
7
+ ```
8
+
9
+ `xevy setup` installs the **Xevy SDLC agent skills** and the **Xevy MCP server**
10
+ into your user-global agent config, for [Cursor, Claude Code, and 50+ other
11
+ agents](https://github.com/vercel-labs/skills#supported-agents).
12
+
13
+ ## No API key — OAuth like any MCP
14
+
15
+ The Xevy MCP server signs in with **OAuth**, completed inside your agent the
16
+ first time it connects. There is no API key to find in a dashboard, paste, or
17
+ store on disk.
18
+
19
+ - **Cursor:** Settings → MCP → `xevy` → connect
20
+ - **Claude Code:** browser sign-in on the first MCP call
21
+
22
+ ## What it does (and doesn't)
23
+
24
+ - ✅ Installs skills globally (`~/.cursor/skills`, `~/.claude/skills`, `~/.agents/skills`, …)
25
+ - ✅ Writes the MCP server to your agent's **global** config
26
+ - ❌ No per-repo files, no project detection, no repo linking
27
+
28
+ To link a specific repository to a Xevy project later, run the bundled
29
+ `xevy-setup` skill from inside your agent (it uses the MCP tools over OAuth).
30
+
31
+ ## Install permanently
32
+
33
+ ```bash
34
+ npm i -g @xevy/cli
35
+ xevy setup
36
+ ```
37
+
38
+ ## Options
39
+
40
+ ```
41
+ xevy setup [options]
42
+
43
+ -a, --agent <name> Target agent (repeatable). Default: cursor, claude-code
44
+ --base <url> Xevy host (default: XEVY_BASE or https://xevy.ai)
45
+ --skills-only Install skills only (skip MCP)
46
+ --mcp-only Configure the OAuth MCP server only (skip skills)
47
+ --copy Copy skill files instead of symlinking
48
+ -v, --version Print the CLI version
49
+ -h, --help Show help
50
+ ```
51
+
52
+ ### Examples
53
+
54
+ ```bash
55
+ npx @xevy/cli setup # skills + MCP, global
56
+ npx @xevy/cli setup -a cursor # Cursor only
57
+ npx @xevy/cli setup --mcp-only # just the OAuth MCP
58
+ XEVY_BASE=https://xevy.vyte.dev npx @xevy/cli setup # non-default host
59
+ ```
60
+
61
+ ## How it works
62
+
63
+ Under the hood `xevy setup` drives two well-known primitives:
64
+
65
+ - [`npx skills`](https://github.com/vercel-labs/skills) — installs the skills
66
+ - [`npx add-mcp`](https://github.com/neondatabase/add-mcp) — writes the MCP config
67
+
68
+ Skills are sourced from the pinned [`@xevy/skills`](https://www.npmjs.com/package/@xevy/skills)
69
+ dependency, so this CLI stays in step with the published skills.
70
+
71
+ Docs: <https://xevy.ai/docs/mcp>
package/bin/xevy.mjs ADDED
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @xevy/cli — the `xevy` command.
4
+ *
5
+ * `xevy setup` installs the Xevy SDLC agent skills and the Xevy MCP server
6
+ * globally, for Cursor / Claude Code / 50+ agents. The MCP server authenticates
7
+ * via OAuth (completed inside the agent on first connect) — there is NO API key
8
+ * to find, paste, or store. No repo/project detection or linking happens here.
9
+ *
10
+ * Under the hood it drives the same primitives the Xevy docs recommend:
11
+ * • `npx skills add <dir>` — copies/symlinks the skills into agent scopes
12
+ * • `npx add-mcp <url>` — writes the agent MCP config (OAuth on connect)
13
+ * Skills are sourced from the pinned `@xevy/skills` dependency, so this CLI
14
+ * stays in step with the published skills without duplicating them.
15
+ *
16
+ * Usage:
17
+ * npx @xevy/cli setup Install skills + MCP globally (OAuth)
18
+ * npx @xevy/cli setup -a cursor Only a specific agent (repeatable)
19
+ * npx @xevy/cli setup --mcp-only Configure the OAuth MCP server only
20
+ * npx @xevy/cli --version
21
+ */
22
+ import { spawnSync } from "node:child_process";
23
+ import { createRequire } from "node:module";
24
+ import fs from "node:fs";
25
+ import os from "node:os";
26
+ import path from "node:path";
27
+ import { fileURLToPath } from "node:url";
28
+
29
+ const require = createRequire(import.meta.url);
30
+ const here = path.dirname(fileURLToPath(import.meta.url));
31
+ const pkgRoot = path.resolve(here, "..");
32
+
33
+ const DEFAULT_BASE = "https://xevy.ai";
34
+ const DEFAULT_AGENTS = ["cursor", "claude-code"];
35
+ const isWindows = process.platform === "win32";
36
+
37
+ const PI_PACKAGE = "@mariozechner/pi-coding-agent";
38
+ const PI_EXTENSION_DIR = path.join(os.homedir(), ".pi", "agent", "extensions");
39
+ const MODEL_EXTENSION_FILE = "xevy-model.ts";
40
+
41
+ function readCliVersion() {
42
+ try {
43
+ const pkg = JSON.parse(fs.readFileSync(path.join(pkgRoot, "package.json"), "utf-8"));
44
+ return String(pkg.version || "0.0.0");
45
+ } catch {
46
+ return "0.0.0";
47
+ }
48
+ }
49
+
50
+ function die(msg) {
51
+ console.error(`✗ ${msg}`);
52
+ process.exit(1);
53
+ }
54
+
55
+ function usage() {
56
+ console.log(`Xevy CLI — install Xevy agent skills + OAuth MCP.
57
+
58
+ xevy setup [options] Install skills + MCP globally (OAuth, no API key)
59
+ xevy pi [options] Install the pi agent (if missing) + the xevy-model.ts extension
60
+
61
+ Options:
62
+ -a, --agent <name> Target agent (repeatable). Default: ${DEFAULT_AGENTS.join(", ")}
63
+ --base <url> Xevy host (default: XEVY_BASE or ${DEFAULT_BASE})
64
+ --skills-only Install skills only (skip MCP)
65
+ --mcp-only Configure the OAuth MCP server only (skip skills)
66
+ --copy Copy skill files instead of symlinking
67
+ -v, --version Print the CLI version
68
+ -h, --help Show this help
69
+
70
+ Notes:
71
+ • Everything installs to your user-global scope — no per-repo files, no
72
+ project detection or linking.
73
+ • The MCP server signs in with OAuth from inside your agent on first
74
+ connect. There is no API key to paste.
75
+
76
+ Examples:
77
+ npx @xevy/cli setup
78
+ npx @xevy/cli setup -a cursor
79
+ npx @xevy/cli pi
80
+ XEVY_BASE=https://xevy.vyte.dev npx @xevy/cli setup`);
81
+ }
82
+
83
+ function parseArgv(argv) {
84
+ const opts = {
85
+ command: "setup",
86
+ agents: [],
87
+ base: process.env.XEVY_BASE?.replace(/\/$/, "") || DEFAULT_BASE,
88
+ skillsOnly: false,
89
+ mcpOnly: false,
90
+ copy: false,
91
+ };
92
+
93
+ const args = [...argv];
94
+ if (args.length > 0 && !args[0].startsWith("-")) {
95
+ const cmd = args.shift();
96
+ if (cmd === "setup" || cmd === "pi") {
97
+ opts.command = cmd;
98
+ } else if (cmd === "help") {
99
+ opts.command = "help";
100
+ return opts;
101
+ } else {
102
+ die(`Unknown command: ${cmd}. Try \`xevy --help\`.`);
103
+ }
104
+ }
105
+
106
+ while (args.length > 0) {
107
+ const a = args.shift();
108
+ if (a === "-h" || a === "--help") return { ...opts, command: "help" };
109
+ if (a === "-v" || a === "--version") return { ...opts, command: "version" };
110
+ if (a === "-a" || a === "--agent") {
111
+ const v = args.shift();
112
+ if (!v) die("Missing value for --agent");
113
+ opts.agents.push(v);
114
+ continue;
115
+ }
116
+ if (a === "--base") {
117
+ const v = args.shift();
118
+ if (!v) die("Missing value for --base");
119
+ opts.base = v.replace(/\/$/, "");
120
+ continue;
121
+ }
122
+ if (a === "--skills-only") { opts.skillsOnly = true; continue; }
123
+ if (a === "--mcp-only") { opts.mcpOnly = true; continue; }
124
+ if (a === "--copy") { opts.copy = true; continue; }
125
+ die(`Unknown option: ${a}. Try \`xevy --help\`.`);
126
+ }
127
+
128
+ if (opts.skillsOnly && opts.mcpOnly) {
129
+ die("--skills-only and --mcp-only are mutually exclusive.");
130
+ }
131
+ if (opts.agents.length === 0) opts.agents = [...DEFAULT_AGENTS];
132
+ return opts;
133
+ }
134
+
135
+ function quoteArg(a) {
136
+ return /[\s"]/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a;
137
+ }
138
+
139
+ function run(cmd, args, { label } = {}) {
140
+ const title = label || [cmd, ...args].join(" ");
141
+ console.log(`→ ${title}`);
142
+ const command = isWindows && cmd === "npx" ? "npx.cmd" : cmd;
143
+ const finalArgs = isWindows ? args.map(quoteArg) : args;
144
+ const r = spawnSync(command, finalArgs, { stdio: "inherit", shell: isWindows, env: process.env });
145
+ if (r.error) die(r.error.message);
146
+ if (r.status !== 0) die(`Command failed (${r.status}): ${title}`);
147
+ return r;
148
+ }
149
+
150
+ /** Resolve the on-disk root of the pinned @xevy/skills dependency. */
151
+ function resolveSkillsRoot() {
152
+ try {
153
+ return path.dirname(require.resolve("@xevy/skills/package.json"));
154
+ } catch {
155
+ die("Could not locate the bundled @xevy/skills package. Reinstall @xevy/cli and try again.");
156
+ }
157
+ }
158
+
159
+ function installSkills(opts) {
160
+ const skillsRoot = resolveSkillsRoot();
161
+ const args = ["--yes", "skills", "add", skillsRoot, "-g", "-y", "-s", "*"];
162
+ if (opts.copy) args.push("--copy");
163
+ for (const a of opts.agents) args.push("-a", a);
164
+ run("npx", args, { label: "npx skills add (Xevy skills, global)" });
165
+ }
166
+
167
+ function installMcp(opts) {
168
+ const mcpUrl = `${opts.base}/mcp`;
169
+ const args = ["--yes", "add-mcp", mcpUrl, "-n", "xevy", "-g", "-y"];
170
+ for (const a of opts.agents) args.push("-a", a);
171
+ run("npx", args, { label: `npx add-mcp ${mcpUrl} (OAuth, global)` });
172
+ }
173
+
174
+ function setup(opts) {
175
+ console.log("Setting up Xevy (global, OAuth — no API key)\n");
176
+
177
+ if (!opts.mcpOnly) installSkills(opts);
178
+ if (!opts.skillsOnly) installMcp(opts);
179
+
180
+ console.log("\n✓ Xevy setup complete.");
181
+ if (!opts.skillsOnly) {
182
+ console.log(" • Complete OAuth in your agent on first connect:");
183
+ console.log(" Cursor: Settings → MCP → xevy → connect");
184
+ console.log(" Claude Code: browser sign-in on first MCP call");
185
+ }
186
+ if (opts.base !== DEFAULT_BASE) {
187
+ console.log(` • Host: ${opts.base} (set XEVY_BASE in your agent env too, then restart).`);
188
+ }
189
+ console.log(` • Docs: ${opts.base}/docs/mcp`);
190
+ }
191
+
192
+ function hasPi() {
193
+ const r = spawnSync(isWindows ? "pi.cmd" : "pi", ["--version"], { stdio: "ignore", shell: isWindows });
194
+ return !r.error && r.status === 0;
195
+ }
196
+
197
+ function ensurePi() {
198
+ if (hasPi()) {
199
+ console.log("→ pi agent already installed");
200
+ return;
201
+ }
202
+ console.log(`→ pi agent not found — installing ${PI_PACKAGE} globally…`);
203
+ run("npm", ["install", "-g", PI_PACKAGE], { label: `npm install -g ${PI_PACKAGE}` });
204
+ if (!hasPi()) {
205
+ die("pi is still not on PATH after install. Ensure your npm global bin dir is on PATH, then re-run `xevy pi`.");
206
+ }
207
+ }
208
+
209
+ async function installModelExtension(opts) {
210
+ const url = `${opts.base}/${MODEL_EXTENSION_FILE}`;
211
+ console.log(`→ Downloading model extension ${url}`);
212
+
213
+ let source;
214
+ try {
215
+ const res = await fetch(url);
216
+ if (!res.ok) die(`Failed to download ${url} (HTTP ${res.status}).`);
217
+ source = await res.text();
218
+ } catch (error) {
219
+ die(`Could not fetch ${url}: ${error instanceof Error ? error.message : String(error)}`);
220
+ }
221
+
222
+ // Guard against a wrong --base returning the SPA HTML instead of the extension.
223
+ if (!source.includes("xevyModelExtension") || !source.includes("registerCommand")) {
224
+ die(`Downloaded ${url} does not look like the xevy-model extension (wrong --base?).`);
225
+ }
226
+
227
+ fs.mkdirSync(PI_EXTENSION_DIR, { recursive: true });
228
+ const dest = path.join(PI_EXTENSION_DIR, MODEL_EXTENSION_FILE);
229
+ fs.writeFileSync(dest, source, "utf-8");
230
+ console.log(`✓ Installed ${dest}`);
231
+ }
232
+
233
+ async function piSetup(opts) {
234
+ console.log("Setting up the pi agent + Xevy model extension\n");
235
+
236
+ ensurePi();
237
+ await installModelExtension(opts);
238
+
239
+ console.log("\n✓ pi + xevy-model.ts ready.");
240
+ console.log(" • In pi, run /agent-model to set or clear a per-agent model.");
241
+ console.log(" • If pi is already running, use /reload to load the extension.");
242
+ if (opts.base !== DEFAULT_BASE) {
243
+ console.log(` • Source host: ${opts.base}`);
244
+ }
245
+ }
246
+
247
+ async function main() {
248
+ const opts = parseArgv(process.argv.slice(2));
249
+
250
+ if (opts.command === "help") return usage();
251
+ if (opts.command === "version") return console.log(readCliVersion());
252
+ if (opts.command === "setup") return setup(opts);
253
+ if (opts.command === "pi") return piSetup(opts);
254
+ }
255
+
256
+ main().catch((error) => die(error instanceof Error ? error.message : String(error)));
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@xevy/cli",
3
+ "version": "0.1.0",
4
+ "description": "Xevy CLI — one command (`xevy setup`) installs the Xevy SDLC agent skills and the OAuth MCP server globally for Cursor, Claude Code, and 50+ agents. No API key required.",
5
+ "type": "module",
6
+ "keywords": [
7
+ "xevy",
8
+ "cli",
9
+ "setup",
10
+ "mcp",
11
+ "oauth",
12
+ "agent-skills",
13
+ "cursor",
14
+ "claude-code",
15
+ "sdlc"
16
+ ],
17
+ "homepage": "https://xevy.ai/docs/mcp",
18
+ "license": "UNLICENSED",
19
+ "bin": {
20
+ "xevy": "bin/xevy.mjs"
21
+ },
22
+ "files": [
23
+ "bin",
24
+ "README.md"
25
+ ],
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "dependencies": {
30
+ "@xevy/skills": "^0.1.18"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ }
35
+ }