@xevy/cli 0.1.1 → 0.1.3
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 +27 -48
- package/bin/xevy.mjs +158 -141
- package/package.json +4 -10
package/README.md
CHANGED
|
@@ -1,71 +1,50 @@
|
|
|
1
1
|
# @xevy/cli
|
|
2
2
|
|
|
3
|
-
The **`xevy`** command —
|
|
3
|
+
The **`xevy`** command — set up and launch the [pi](https://github.com/badlogic/pi-mono) agent with Xevy.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npx @xevy/cli setup
|
|
6
|
+
npx @xevy/cli setup # one-time: install pi + the Xevy extension + your API key
|
|
7
|
+
npx @xevy/cli # open pi with the Xevy extension loaded
|
|
7
8
|
```
|
|
8
9
|
|
|
9
|
-
`xevy setup`
|
|
10
|
-
into your user-global agent config, for [Cursor, Claude Code, and 50+ other
|
|
11
|
-
agents](https://github.com/vercel-labs/skills#supported-agents).
|
|
10
|
+
## `xevy setup`
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
1. Installs the **pi agent** if it isn't already on your PATH.
|
|
13
|
+
2. Downloads the Xevy pi extension (`xevy.ts`) into **`~/.xevy/extensions/`**.
|
|
14
|
+
3. Asks for your **API key** and prints a **direct link to the API-keys tab** so
|
|
15
|
+
you can generate one in a couple of clicks, then stores it in
|
|
16
|
+
`~/.config/xevy/credentials` (mode `600`, never printed).
|
|
14
17
|
|
|
15
|
-
The
|
|
16
|
-
|
|
17
|
-
store on disk.
|
|
18
|
+
The API-keys link opens **Settings → Profile → API keys** directly:
|
|
19
|
+
`https://xevy.ai/settings?section=profile&tab=api`.
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
- **Claude Code:** browser sign-in on the first MCP call
|
|
21
|
+
## `xevy`
|
|
21
22
|
|
|
22
|
-
|
|
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
|
|
23
|
+
Opens pi, loading every `*.ts` extension in `~/.xevy/extensions` (`pi -e …`).
|
|
24
|
+
Any extra arguments are forwarded to pi:
|
|
32
25
|
|
|
33
26
|
```bash
|
|
34
|
-
|
|
35
|
-
xevy
|
|
27
|
+
xevy # interactive pi session with Xevy
|
|
28
|
+
xevy "summarise this repo" # forwards the prompt to pi
|
|
36
29
|
```
|
|
37
30
|
|
|
31
|
+
## `xevy pi` (alternative)
|
|
32
|
+
|
|
33
|
+
Installs the split **model + MCP** extensions (`xevy-model.ts`, `xevy-mcp.ts`)
|
|
34
|
+
into pi's own extensions dir and authenticates over **OAuth** (`/login xevy`) —
|
|
35
|
+
no API key. Use this instead of `setup` if you prefer OAuth and the hosted
|
|
36
|
+
`xevy/*` models + MCP tool bridge.
|
|
37
|
+
|
|
38
38
|
## Options
|
|
39
39
|
|
|
40
40
|
```
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
-
|
|
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
|
|
41
|
+
--base <url> Xevy host (default: XEVY_BASE or https://xevy.ai)
|
|
42
|
+
-v, --version Print the CLI version
|
|
43
|
+
-h, --help Show help
|
|
50
44
|
```
|
|
51
45
|
|
|
52
|
-
### Examples
|
|
53
|
-
|
|
54
46
|
```bash
|
|
55
|
-
npx @xevy/cli setup
|
|
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
|
|
47
|
+
XEVY_BASE=https://xevy.vyte.dev npx @xevy/cli setup
|
|
59
48
|
```
|
|
60
49
|
|
|
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
50
|
Docs: <https://xevy.ai/docs/mcp>
|
package/bin/xevy.mjs
CHANGED
|
@@ -2,47 +2,57 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @xevy/cli — the `xevy` command.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* xevy setup Install the pi agent (if missing), download the Xevy pi
|
|
6
|
+
* extension into ~/.xevy/extensions, and store your API key.
|
|
7
|
+
* xevy Open pi with the Xevy extensions from ~/.xevy/extensions.
|
|
8
|
+
* xevy pi (alt) Install the split model + MCP extensions into pi's own
|
|
9
|
+
* extensions dir, authenticated via OAuth.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
|
11
|
+
* `xevy setup` is the default flow: it fetches `xevy.ts` from the server and
|
|
12
|
+
* asks for an API key (with a direct link to the exact page to generate one),
|
|
13
|
+
* stored in ~/.config/xevy/credentials. Then `xevy` launches pi with it.
|
|
21
14
|
*/
|
|
22
15
|
import { spawnSync } from "node:child_process";
|
|
23
|
-
import { createRequire } from "node:module";
|
|
24
16
|
import fs from "node:fs";
|
|
25
17
|
import os from "node:os";
|
|
26
18
|
import path from "node:path";
|
|
19
|
+
import readline from "node:readline";
|
|
27
20
|
import { fileURLToPath } from "node:url";
|
|
28
21
|
|
|
29
|
-
const require = createRequire(import.meta.url);
|
|
30
22
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
31
23
|
const pkgRoot = path.resolve(here, "..");
|
|
32
24
|
|
|
33
25
|
const DEFAULT_BASE = "https://xevy.ai";
|
|
34
|
-
const DEFAULT_AGENTS = ["cursor", "claude-code"];
|
|
35
26
|
const isWindows = process.platform === "win32";
|
|
36
27
|
|
|
37
28
|
const PI_PACKAGE = "@mariozechner/pi-coding-agent";
|
|
29
|
+
const PI_BIN = isWindows ? "pi.cmd" : "pi";
|
|
30
|
+
|
|
31
|
+
// Where `xevy setup` installs the extension and `xevy` loads it from.
|
|
32
|
+
const XEVY_EXTENSIONS_DIR = path.join(os.homedir(), ".xevy", "extensions");
|
|
33
|
+
const XEVY_EXTENSION_FILE = "xevy.ts";
|
|
34
|
+
const API_KEY_PREFIX = "xevy_pk_";
|
|
35
|
+
|
|
36
|
+
// Credentials file the xevy.ts extension reads (baseUrl + token).
|
|
37
|
+
const CREDENTIALS_DIR = path.join(
|
|
38
|
+
process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"),
|
|
39
|
+
"xevy",
|
|
40
|
+
);
|
|
41
|
+
const CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, "credentials");
|
|
42
|
+
|
|
43
|
+
// `xevy pi` (alt flow) installs these into pi's own extensions dir over OAuth.
|
|
38
44
|
const PI_EXTENSION_DIR = path.join(os.homedir(), ".pi", "agent", "extensions");
|
|
39
|
-
// pi extensions installed by `xevy pi` — file name + a marker string used to
|
|
40
|
-
// sanity-check the download isn't SPA HTML (wrong --base).
|
|
41
45
|
const PI_EXTENSIONS = [
|
|
42
46
|
{ file: "xevy-model.ts", marker: "xevyModelExtension" },
|
|
43
47
|
{ file: "xevy-mcp.ts", marker: "xevyMcpExtension" },
|
|
44
48
|
];
|
|
45
49
|
|
|
50
|
+
function apiKeysUrl(base) {
|
|
51
|
+
// Direct link to the API keys tab (Profile → API keys), reachable without a
|
|
52
|
+
// project via the root Settings app.
|
|
53
|
+
return `${base}/settings?section=profile&tab=api`;
|
|
54
|
+
}
|
|
55
|
+
|
|
46
56
|
function readCliVersion() {
|
|
47
57
|
try {
|
|
48
58
|
const pkg = JSON.parse(fs.readFileSync(path.join(pkgRoot, "package.json"), "utf-8"));
|
|
@@ -58,144 +68,65 @@ function die(msg) {
|
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
function usage() {
|
|
61
|
-
console.log(`Xevy CLI
|
|
71
|
+
console.log(`Xevy CLI
|
|
62
72
|
|
|
63
|
-
xevy
|
|
64
|
-
xevy
|
|
73
|
+
xevy Open pi with the Xevy extensions in ~/.xevy/extensions
|
|
74
|
+
xevy setup [--base u] Install pi (if missing) + the Xevy pi extension, store your API key
|
|
75
|
+
xevy pi [--base u] (alt) Install the split model + MCP extensions over OAuth
|
|
65
76
|
|
|
66
77
|
Options:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
--mcp-only Configure the OAuth MCP server only (skip skills)
|
|
71
|
-
--copy Copy skill files instead of symlinking
|
|
72
|
-
-v, --version Print the CLI version
|
|
73
|
-
-h, --help Show this help
|
|
74
|
-
|
|
75
|
-
Notes:
|
|
76
|
-
• Everything installs to your user-global scope — no per-repo files, no
|
|
77
|
-
project detection or linking.
|
|
78
|
-
• The MCP server signs in with OAuth from inside your agent on first
|
|
79
|
-
connect. There is no API key to paste.
|
|
78
|
+
--base <url> Xevy host (default: XEVY_BASE or ${DEFAULT_BASE})
|
|
79
|
+
-v, --version Print the CLI version
|
|
80
|
+
-h, --help Show this help
|
|
80
81
|
|
|
81
82
|
Examples:
|
|
82
83
|
npx @xevy/cli setup
|
|
83
|
-
npx @xevy/cli
|
|
84
|
-
npx @xevy/cli pi
|
|
84
|
+
npx @xevy/cli
|
|
85
85
|
XEVY_BASE=https://xevy.vyte.dev npx @xevy/cli setup`);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
function parseArgv(argv) {
|
|
89
|
-
const
|
|
90
|
-
command: "setup",
|
|
91
|
-
agents: [],
|
|
92
|
-
base: process.env.XEVY_BASE?.replace(/\/$/, "") || DEFAULT_BASE,
|
|
93
|
-
skillsOnly: false,
|
|
94
|
-
mcpOnly: false,
|
|
95
|
-
copy: false,
|
|
96
|
-
};
|
|
97
|
-
|
|
89
|
+
const base = process.env.XEVY_BASE?.replace(/\/$/, "") || DEFAULT_BASE;
|
|
98
90
|
const args = [...argv];
|
|
99
|
-
if (args.length > 0 && !args[0].startsWith("-")) {
|
|
100
|
-
const cmd = args.shift();
|
|
101
|
-
if (cmd === "setup" || cmd === "pi") {
|
|
102
|
-
opts.command = cmd;
|
|
103
|
-
} else if (cmd === "help") {
|
|
104
|
-
opts.command = "help";
|
|
105
|
-
return opts;
|
|
106
|
-
} else {
|
|
107
|
-
die(`Unknown command: ${cmd}. Try \`xevy --help\`.`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
91
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
92
|
+
if (args.length === 0) return { command: "open", base, piArgs: [] };
|
|
93
|
+
|
|
94
|
+
const first = args[0];
|
|
95
|
+
if (first === "-h" || first === "--help" || first === "help") return { command: "help", base };
|
|
96
|
+
if (first === "-v" || first === "--version") return { command: "version", base };
|
|
97
|
+
|
|
98
|
+
if (first === "setup" || first === "pi") {
|
|
99
|
+
args.shift();
|
|
100
|
+
const opts = { command: first, base, piArgs: [] };
|
|
101
|
+
while (args.length > 0) {
|
|
102
|
+
const a = args.shift();
|
|
103
|
+
if (a === "--base") {
|
|
104
|
+
const v = args.shift();
|
|
105
|
+
if (!v) die("Missing value for --base");
|
|
106
|
+
opts.base = v.replace(/\/$/, "");
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
die(`Unknown option for \`xevy ${first}\`: ${a}. Try \`xevy --help\`.`);
|
|
126
110
|
}
|
|
127
|
-
|
|
128
|
-
if (a === "--mcp-only") { opts.mcpOnly = true; continue; }
|
|
129
|
-
if (a === "--copy") { opts.copy = true; continue; }
|
|
130
|
-
die(`Unknown option: ${a}. Try \`xevy --help\`.`);
|
|
111
|
+
return opts;
|
|
131
112
|
}
|
|
132
113
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
if (opts.agents.length === 0) opts.agents = [...DEFAULT_AGENTS];
|
|
137
|
-
return opts;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function quoteArg(a) {
|
|
141
|
-
return /[\s"]/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a;
|
|
114
|
+
// Anything else → open pi, forwarding the args through.
|
|
115
|
+
return { command: "open", base, piArgs: args };
|
|
142
116
|
}
|
|
143
117
|
|
|
144
118
|
function run(cmd, args, { label } = {}) {
|
|
145
119
|
const title = label || [cmd, ...args].join(" ");
|
|
146
120
|
console.log(`→ ${title}`);
|
|
147
121
|
const command = isWindows && cmd === "npx" ? "npx.cmd" : cmd;
|
|
148
|
-
const
|
|
149
|
-
const r = spawnSync(command, finalArgs, { stdio: "inherit", shell: isWindows, env: process.env });
|
|
122
|
+
const r = spawnSync(command, args, { stdio: "inherit", shell: isWindows, env: process.env });
|
|
150
123
|
if (r.error) die(r.error.message);
|
|
151
124
|
if (r.status !== 0) die(`Command failed (${r.status}): ${title}`);
|
|
152
125
|
return r;
|
|
153
126
|
}
|
|
154
127
|
|
|
155
|
-
/** Resolve the on-disk root of the pinned @xevy/skills dependency. */
|
|
156
|
-
function resolveSkillsRoot() {
|
|
157
|
-
try {
|
|
158
|
-
return path.dirname(require.resolve("@xevy/skills/package.json"));
|
|
159
|
-
} catch {
|
|
160
|
-
die("Could not locate the bundled @xevy/skills package. Reinstall @xevy/cli and try again.");
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function installSkills(opts) {
|
|
165
|
-
const skillsRoot = resolveSkillsRoot();
|
|
166
|
-
const args = ["--yes", "skills", "add", skillsRoot, "-g", "-y", "-s", "*"];
|
|
167
|
-
if (opts.copy) args.push("--copy");
|
|
168
|
-
for (const a of opts.agents) args.push("-a", a);
|
|
169
|
-
run("npx", args, { label: "npx skills add (Xevy skills, global)" });
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function installMcp(opts) {
|
|
173
|
-
const mcpUrl = `${opts.base}/mcp`;
|
|
174
|
-
const args = ["--yes", "add-mcp", mcpUrl, "-n", "xevy", "-g", "-y"];
|
|
175
|
-
for (const a of opts.agents) args.push("-a", a);
|
|
176
|
-
run("npx", args, { label: `npx add-mcp ${mcpUrl} (OAuth, global)` });
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function setup(opts) {
|
|
180
|
-
console.log("Setting up Xevy (global, OAuth — no API key)\n");
|
|
181
|
-
|
|
182
|
-
if (!opts.mcpOnly) installSkills(opts);
|
|
183
|
-
if (!opts.skillsOnly) installMcp(opts);
|
|
184
|
-
|
|
185
|
-
console.log("\n✓ Xevy setup complete.");
|
|
186
|
-
if (!opts.skillsOnly) {
|
|
187
|
-
console.log(" • Complete OAuth in your agent on first connect:");
|
|
188
|
-
console.log(" Cursor: Settings → MCP → xevy → connect");
|
|
189
|
-
console.log(" Claude Code: browser sign-in on first MCP call");
|
|
190
|
-
}
|
|
191
|
-
if (opts.base !== DEFAULT_BASE) {
|
|
192
|
-
console.log(` • Host: ${opts.base} (set XEVY_BASE in your agent env too, then restart).`);
|
|
193
|
-
}
|
|
194
|
-
console.log(` • Docs: ${opts.base}/docs/mcp`);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
128
|
function hasPi() {
|
|
198
|
-
const r = spawnSync(
|
|
129
|
+
const r = spawnSync(PI_BIN, ["--version"], { stdio: "ignore", shell: isWindows });
|
|
199
130
|
return !r.error && r.status === 0;
|
|
200
131
|
}
|
|
201
132
|
|
|
@@ -207,12 +138,12 @@ function ensurePi() {
|
|
|
207
138
|
console.log(`→ pi agent not found — installing ${PI_PACKAGE} globally…`);
|
|
208
139
|
run("npm", ["install", "-g", PI_PACKAGE], { label: `npm install -g ${PI_PACKAGE}` });
|
|
209
140
|
if (!hasPi()) {
|
|
210
|
-
die("pi is still not on PATH after install. Ensure your npm global bin dir is on PATH, then re-run
|
|
141
|
+
die("pi is still not on PATH after install. Ensure your npm global bin dir is on PATH, then re-run.");
|
|
211
142
|
}
|
|
212
143
|
}
|
|
213
144
|
|
|
214
|
-
async function
|
|
215
|
-
const url = `${
|
|
145
|
+
async function downloadExtension(base, file, destDir, markers) {
|
|
146
|
+
const url = `${base}/${file}`;
|
|
216
147
|
console.log(`→ Downloading ${url}`);
|
|
217
148
|
|
|
218
149
|
let source;
|
|
@@ -225,14 +156,101 @@ async function installExtension(opts, { file, marker }) {
|
|
|
225
156
|
}
|
|
226
157
|
|
|
227
158
|
// Guard against a wrong --base returning the SPA HTML instead of the extension.
|
|
228
|
-
if (!
|
|
159
|
+
if (!markers.every((m) => source.includes(m))) {
|
|
229
160
|
die(`Downloaded ${url} does not look like the ${file} extension (wrong --base?).`);
|
|
230
161
|
}
|
|
231
162
|
|
|
232
|
-
fs.mkdirSync(
|
|
233
|
-
const dest = path.join(
|
|
163
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
164
|
+
const dest = path.join(destDir, file);
|
|
234
165
|
fs.writeFileSync(dest, source, "utf-8");
|
|
235
166
|
console.log(`✓ Installed ${dest}`);
|
|
167
|
+
return dest;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function promptLine(question) {
|
|
171
|
+
return new Promise((resolve) => {
|
|
172
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
173
|
+
rl.question(question, (answer) => {
|
|
174
|
+
rl.close();
|
|
175
|
+
resolve(String(answer || "").trim());
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function writeCredentials(base, token) {
|
|
181
|
+
fs.mkdirSync(CREDENTIALS_DIR, { recursive: true });
|
|
182
|
+
fs.writeFileSync(
|
|
183
|
+
CREDENTIALS_FILE,
|
|
184
|
+
`baseUrl=${base}\ntoken=${token}\nupdatedAt=${new Date().toISOString()}\n`,
|
|
185
|
+
{ mode: 0o600 },
|
|
186
|
+
);
|
|
187
|
+
try {
|
|
188
|
+
fs.chmodSync(CREDENTIALS_FILE, 0o600);
|
|
189
|
+
} catch {
|
|
190
|
+
// best-effort on platforms without chmod
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function setup(opts) {
|
|
195
|
+
console.log("Setting up Xevy for pi\n");
|
|
196
|
+
|
|
197
|
+
// 1. Ensure pi is installed (installs it if missing).
|
|
198
|
+
ensurePi();
|
|
199
|
+
|
|
200
|
+
// 2. Extensions:
|
|
201
|
+
// • xevy.ts → ~/.xevy/extensions (tools/workflow; loaded by `xevy`)
|
|
202
|
+
// • xevy-model.ts → pi's GLOBAL extensions dir, so the xevy/* models are
|
|
203
|
+
// available in EVERY pi session (plain `pi` too), not just via `xevy`.
|
|
204
|
+
await downloadExtension(opts.base, XEVY_EXTENSION_FILE, XEVY_EXTENSIONS_DIR, [
|
|
205
|
+
"EXTENSION_VERSION",
|
|
206
|
+
"registerCommand",
|
|
207
|
+
]);
|
|
208
|
+
await downloadExtension(opts.base, "xevy-model.ts", PI_EXTENSION_DIR, [
|
|
209
|
+
"xevyModelExtension",
|
|
210
|
+
"registerCommand",
|
|
211
|
+
]);
|
|
212
|
+
|
|
213
|
+
// 2. API key — with a direct link to the exact page to generate one.
|
|
214
|
+
console.log("\nGet your API key (this link opens the API keys tab directly):");
|
|
215
|
+
console.log(` ${apiKeysUrl(opts.base)}`);
|
|
216
|
+
console.log(" Sign in if prompted, then click “Create API key” and copy it.\n");
|
|
217
|
+
|
|
218
|
+
const token = await promptLine(`Paste your Xevy API key (${API_KEY_PREFIX}…): `);
|
|
219
|
+
if (!token) die("API key is required. Re-run `xevy setup` when you have one.");
|
|
220
|
+
if (!token.startsWith(API_KEY_PREFIX)) {
|
|
221
|
+
die(`That doesn't look like a Xevy API key (expected '${API_KEY_PREFIX}…'). Not saved.`);
|
|
222
|
+
}
|
|
223
|
+
writeCredentials(opts.base, token);
|
|
224
|
+
console.log(`✓ API key stored at ${CREDENTIALS_FILE} (key not shown).`);
|
|
225
|
+
|
|
226
|
+
console.log("\n✓ Setup complete.");
|
|
227
|
+
console.log(" • Xevy models (xevy/auto, xevy/fast, …) are available in any pi session — no /login needed.");
|
|
228
|
+
console.log(" • Run `xevy` to open pi with the Xevy tools + models loaded.");
|
|
229
|
+
if (opts.base !== DEFAULT_BASE) console.log(` • Host: ${opts.base}`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function open(opts) {
|
|
233
|
+
if (!hasPi()) {
|
|
234
|
+
die("pi is not installed. Run `xevy setup` first.");
|
|
235
|
+
}
|
|
236
|
+
let files = [];
|
|
237
|
+
if (fs.existsSync(XEVY_EXTENSIONS_DIR)) {
|
|
238
|
+
files = fs
|
|
239
|
+
.readdirSync(XEVY_EXTENSIONS_DIR)
|
|
240
|
+
.filter((f) => f.endsWith(".ts"))
|
|
241
|
+
.map((f) => path.join(XEVY_EXTENSIONS_DIR, f));
|
|
242
|
+
}
|
|
243
|
+
if (files.length === 0) {
|
|
244
|
+
die(`No Xevy extensions in ${XEVY_EXTENSIONS_DIR}. Run \`xevy setup\` first.`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const piArgs = [];
|
|
248
|
+
for (const f of files) piArgs.push("-e", f);
|
|
249
|
+
piArgs.push(...opts.piArgs);
|
|
250
|
+
|
|
251
|
+
const r = spawnSync(PI_BIN, piArgs, { stdio: "inherit", shell: isWindows, env: process.env });
|
|
252
|
+
if (r.error) die(r.error.message);
|
|
253
|
+
process.exit(r.status ?? 0);
|
|
236
254
|
}
|
|
237
255
|
|
|
238
256
|
async function piSetup(opts) {
|
|
@@ -240,7 +258,7 @@ async function piSetup(opts) {
|
|
|
240
258
|
|
|
241
259
|
ensurePi();
|
|
242
260
|
for (const ext of PI_EXTENSIONS) {
|
|
243
|
-
await
|
|
261
|
+
await downloadExtension(opts.base, ext.file, PI_EXTENSION_DIR, [ext.marker, "registerCommand"]);
|
|
244
262
|
}
|
|
245
263
|
|
|
246
264
|
console.log("\n✓ pi + Xevy extensions ready.");
|
|
@@ -248,9 +266,7 @@ async function piSetup(opts) {
|
|
|
248
266
|
console.log(" • Xevy models: xevy/auto, xevy/fast, xevy/advance, xevy/min, xevy/max, xevy/vision.");
|
|
249
267
|
console.log(" • MCP tools bridge automatically on session start (or run /xevy-mcp).");
|
|
250
268
|
console.log(" • If pi is already running, use /reload to load the extensions.");
|
|
251
|
-
if (opts.base !== DEFAULT_BASE) {
|
|
252
|
-
console.log(` • Source host: ${opts.base}`);
|
|
253
|
-
}
|
|
269
|
+
if (opts.base !== DEFAULT_BASE) console.log(` • Source host: ${opts.base}`);
|
|
254
270
|
}
|
|
255
271
|
|
|
256
272
|
async function main() {
|
|
@@ -260,6 +276,7 @@ async function main() {
|
|
|
260
276
|
if (opts.command === "version") return console.log(readCliVersion());
|
|
261
277
|
if (opts.command === "setup") return setup(opts);
|
|
262
278
|
if (opts.command === "pi") return piSetup(opts);
|
|
279
|
+
if (opts.command === "open") return open(opts);
|
|
263
280
|
}
|
|
264
281
|
|
|
265
282
|
main().catch((error) => die(error instanceof Error ? error.message : String(error)));
|
package/package.json
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xevy/cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Xevy CLI —
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Xevy CLI — `xevy setup` installs the pi agent + the Xevy pi extension and stores your API key; `xevy` opens pi with it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"xevy",
|
|
8
8
|
"cli",
|
|
9
9
|
"setup",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"agent-skills",
|
|
13
|
-
"cursor",
|
|
14
|
-
"claude-code",
|
|
10
|
+
"pi",
|
|
11
|
+
"pi-agent",
|
|
15
12
|
"sdlc"
|
|
16
13
|
],
|
|
17
14
|
"homepage": "https://xevy.ai/docs/mcp",
|
|
@@ -26,9 +23,6 @@
|
|
|
26
23
|
"engines": {
|
|
27
24
|
"node": ">=18"
|
|
28
25
|
},
|
|
29
|
-
"dependencies": {
|
|
30
|
-
"@xevy/skills": "^0.1.18"
|
|
31
|
-
},
|
|
32
26
|
"publishConfig": {
|
|
33
27
|
"access": "public"
|
|
34
28
|
}
|