code-graph-builder 0.7.0 → 0.8.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/bin/cli.mjs +100 -17
- package/package.json +1 -1
package/bin/cli.mjs
CHANGED
|
@@ -10,16 +10,17 @@
|
|
|
10
10
|
* npx code-graph-builder --pip # force python3 direct mode
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { spawn, execFileSync } from "node:child_process";
|
|
13
|
+
import { spawn, execFileSync, execSync } from "node:child_process";
|
|
14
14
|
import { createInterface } from "node:readline";
|
|
15
15
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
16
|
-
import { homedir } from "node:os";
|
|
16
|
+
import { homedir, platform } from "node:os";
|
|
17
17
|
import { join } from "node:path";
|
|
18
18
|
|
|
19
19
|
const PYTHON_PACKAGE = "code-graph-builder";
|
|
20
20
|
const MODULE_PATH = "code_graph_builder.mcp.server";
|
|
21
21
|
const WORKSPACE_DIR = join(homedir(), ".code-graph-builder");
|
|
22
22
|
const ENV_FILE = join(WORKSPACE_DIR, ".env");
|
|
23
|
+
const IS_WIN = platform() === "win32";
|
|
23
24
|
|
|
24
25
|
// ---------------------------------------------------------------------------
|
|
25
26
|
// Utilities
|
|
@@ -27,16 +28,40 @@ const ENV_FILE = join(WORKSPACE_DIR, ".env");
|
|
|
27
28
|
|
|
28
29
|
function commandExists(cmd) {
|
|
29
30
|
try {
|
|
30
|
-
|
|
31
|
+
// "which" on Unix/macOS, "where" on Windows
|
|
32
|
+
const checker = IS_WIN ? "where" : "which";
|
|
33
|
+
execFileSync(checker, [cmd], { stdio: "pipe" });
|
|
31
34
|
return true;
|
|
32
35
|
} catch {
|
|
33
36
|
return false;
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Find a working Python command. On Windows the command is typically
|
|
42
|
+
* "python" (the py-launcher or Store stub), while on Unix it is "python3".
|
|
43
|
+
* Returns the command string or null if none is found.
|
|
44
|
+
*/
|
|
45
|
+
function findPython() {
|
|
46
|
+
const candidates = IS_WIN
|
|
47
|
+
? ["python", "python3", "py"]
|
|
48
|
+
: ["python3", "python"];
|
|
49
|
+
for (const cmd of candidates) {
|
|
50
|
+
try {
|
|
51
|
+
const ver = execFileSync(cmd, ["--version"], { stdio: "pipe" }).toString().trim();
|
|
52
|
+
// Ensure it is Python 3.x
|
|
53
|
+
if (ver.includes("3.")) return cmd;
|
|
54
|
+
} catch { /* skip */ }
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const PYTHON_CMD = findPython();
|
|
60
|
+
|
|
37
61
|
function pythonPackageInstalled() {
|
|
62
|
+
if (!PYTHON_CMD) return false;
|
|
38
63
|
try {
|
|
39
|
-
execFileSync(
|
|
64
|
+
execFileSync(PYTHON_CMD, ["-c", `import ${MODULE_PATH.split(".")[0]}`], {
|
|
40
65
|
stdio: "pipe",
|
|
41
66
|
});
|
|
42
67
|
return true;
|
|
@@ -268,7 +293,7 @@ async function runSetup() {
|
|
|
268
293
|
log(' "mcpServers": {');
|
|
269
294
|
log(' "code-graph-builder": {');
|
|
270
295
|
log(' "command": "npx",');
|
|
271
|
-
log(' "args": ["-y", "code-graph-builder", "--server"]');
|
|
296
|
+
log(' "args": ["-y", "code-graph-builder@latest", "--server"]');
|
|
272
297
|
log(" }");
|
|
273
298
|
log(" }");
|
|
274
299
|
log(" }");
|
|
@@ -294,6 +319,7 @@ function runServer(cmd, args) {
|
|
|
294
319
|
const child = spawn(cmd, args, {
|
|
295
320
|
stdio: "inherit",
|
|
296
321
|
env: mergedEnv,
|
|
322
|
+
shell: IS_WIN, // Windows needs shell for .cmd/.ps1 scripts (uvx, pipx, etc.)
|
|
297
323
|
});
|
|
298
324
|
|
|
299
325
|
child.on("error", (err) => {
|
|
@@ -306,6 +332,70 @@ function runServer(cmd, args) {
|
|
|
306
332
|
});
|
|
307
333
|
}
|
|
308
334
|
|
|
335
|
+
/**
|
|
336
|
+
* Find a working pip command. Returns [cmd, ...prefixArgs] or null.
|
|
337
|
+
* Tries: pip3, pip, python3 -m pip, python -m pip
|
|
338
|
+
*/
|
|
339
|
+
function findPip() {
|
|
340
|
+
// Standalone pip
|
|
341
|
+
for (const cmd of IS_WIN ? ["pip", "pip3"] : ["pip3", "pip"]) {
|
|
342
|
+
if (commandExists(cmd)) return [cmd];
|
|
343
|
+
}
|
|
344
|
+
// python -m pip fallback
|
|
345
|
+
if (PYTHON_CMD) {
|
|
346
|
+
try {
|
|
347
|
+
execFileSync(PYTHON_CMD, ["-m", "pip", "--version"], { stdio: "pipe" });
|
|
348
|
+
return [PYTHON_CMD, "-m", "pip"];
|
|
349
|
+
} catch { /* skip */ }
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Auto-install the Python package via pip, then start the server.
|
|
356
|
+
*/
|
|
357
|
+
function autoInstallAndStart(extraArgs) {
|
|
358
|
+
const pip = findPip();
|
|
359
|
+
if (!pip) {
|
|
360
|
+
process.stderr.write(
|
|
361
|
+
`code-graph-builder requires Python 3.10+ with pip.\n\n` +
|
|
362
|
+
(PYTHON_CMD
|
|
363
|
+
? `Python found (${PYTHON_CMD}) but pip is not available.\n\n`
|
|
364
|
+
: `Python 3 not found on PATH.\n\n`) +
|
|
365
|
+
`Please install Python 3.10+ first, then run:\n` +
|
|
366
|
+
` npx code-graph-builder --server\n`
|
|
367
|
+
);
|
|
368
|
+
process.exit(1);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
process.stderr.write(`Installing ${PYTHON_PACKAGE}...\n`);
|
|
372
|
+
|
|
373
|
+
try {
|
|
374
|
+
execSync(
|
|
375
|
+
[...pip, "install", PYTHON_PACKAGE].map(s => `"${s}"`).join(" "),
|
|
376
|
+
{ stdio: "inherit", shell: true }
|
|
377
|
+
);
|
|
378
|
+
} catch (err) {
|
|
379
|
+
process.stderr.write(
|
|
380
|
+
`\nFailed to install ${PYTHON_PACKAGE}.\n` +
|
|
381
|
+
`Try manually: ${pip.join(" ")} install ${PYTHON_PACKAGE}\n`
|
|
382
|
+
);
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Verify installation succeeded
|
|
387
|
+
if (!pythonPackageInstalled()) {
|
|
388
|
+
process.stderr.write(
|
|
389
|
+
`\nInstallation completed but package not importable.\n` +
|
|
390
|
+
`Try manually: ${pip.join(" ")} install ${PYTHON_PACKAGE}\n`
|
|
391
|
+
);
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
process.stderr.write(`${PYTHON_PACKAGE} installed successfully.\n`);
|
|
396
|
+
runServer(PYTHON_CMD, ["-m", MODULE_PATH]);
|
|
397
|
+
}
|
|
398
|
+
|
|
309
399
|
function startServer(extraArgs = []) {
|
|
310
400
|
if (commandExists("uvx")) {
|
|
311
401
|
runServer("uvx", [PYTHON_PACKAGE, ...extraArgs]);
|
|
@@ -314,17 +404,10 @@ function startServer(extraArgs = []) {
|
|
|
314
404
|
} else if (commandExists("pipx")) {
|
|
315
405
|
runServer("pipx", ["run", PYTHON_PACKAGE, ...extraArgs]);
|
|
316
406
|
} else if (pythonPackageInstalled()) {
|
|
317
|
-
runServer(
|
|
407
|
+
runServer(PYTHON_CMD, ["-m", MODULE_PATH]);
|
|
318
408
|
} else {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
`Install options:\n` +
|
|
322
|
-
` 1. pip install ${PYTHON_PACKAGE}\n` +
|
|
323
|
-
` 2. curl -LsSf https://astral.sh/uv/install.sh | sh (installs uv)\n` +
|
|
324
|
-
` 3. pip install pipx\n\n` +
|
|
325
|
-
`Then run: npx code-graph-builder --server\n`
|
|
326
|
-
);
|
|
327
|
-
process.exit(1);
|
|
409
|
+
// Auto-install via pip
|
|
410
|
+
autoInstallAndStart(extraArgs);
|
|
328
411
|
}
|
|
329
412
|
}
|
|
330
413
|
|
|
@@ -341,14 +424,14 @@ if (mode === "--setup") {
|
|
|
341
424
|
} else if (mode === "--server" || mode === "--pip" || mode === "--python") {
|
|
342
425
|
// Start MCP server directly
|
|
343
426
|
if (mode === "--pip" || mode === "--python") {
|
|
344
|
-
if (!pythonPackageInstalled()) {
|
|
427
|
+
if (!PYTHON_CMD || !pythonPackageInstalled()) {
|
|
345
428
|
process.stderr.write(
|
|
346
429
|
`Error: Python package '${PYTHON_PACKAGE}' is not installed.\n` +
|
|
347
430
|
`Run: pip install ${PYTHON_PACKAGE}\n`
|
|
348
431
|
);
|
|
349
432
|
process.exit(1);
|
|
350
433
|
}
|
|
351
|
-
runServer(
|
|
434
|
+
runServer(PYTHON_CMD, ["-m", MODULE_PATH]);
|
|
352
435
|
} else {
|
|
353
436
|
startServer(args.slice(1));
|
|
354
437
|
}
|