code-graph-builder 0.8.0 → 0.9.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 (2) hide show
  1. package/bin/cli.mjs +227 -8
  2. package/package.json +1 -1
package/bin/cli.mjs CHANGED
@@ -285,7 +285,134 @@ async function runSetup() {
285
285
  log(" Embedding: " + embedDisplay);
286
286
  log(" Workspace: " + workspace);
287
287
  log("");
288
- log("── Next steps ──────────────────────────────────────────────");
288
+
289
+ // --- Verify installation ---
290
+ log("── Verifying installation ──────────────────────────────────");
291
+ log("");
292
+
293
+ // Step 1: Python available?
294
+ if (!PYTHON_CMD) {
295
+ log(" ✗ Python 3 not found on PATH");
296
+ log(" Install Python 3.10+ and re-run: npx code-graph-builder --setup");
297
+ log("");
298
+ rl.close();
299
+ return;
300
+ }
301
+ log(` ✓ Python found: ${PYTHON_CMD}`);
302
+
303
+ // Step 2: Package installed? If not, auto-install.
304
+ if (!pythonPackageInstalled()) {
305
+ log(` … Installing ${PYTHON_PACKAGE} via pip...`);
306
+ const pip = findPip();
307
+ if (pip) {
308
+ try {
309
+ execSync(
310
+ [...pip, "install", PYTHON_PACKAGE].map(s => `"${s}"`).join(" "),
311
+ { stdio: "pipe", shell: true }
312
+ );
313
+ } catch { /* handled below */ }
314
+ }
315
+ }
316
+
317
+ if (pythonPackageInstalled()) {
318
+ log(` ✓ Python package installed: ${PYTHON_PACKAGE}`);
319
+ } else {
320
+ log(` ✗ Python package not installed`);
321
+ log(` Run manually: pip install ${PYTHON_PACKAGE}`);
322
+ log("");
323
+ rl.close();
324
+ return;
325
+ }
326
+
327
+ // Step 3: MCP server smoke test — spawn server, send initialize, check tools/list
328
+ log(" … Starting MCP server smoke test...");
329
+
330
+ const verified = await new Promise((resolve) => {
331
+ const envVars = loadEnvFile();
332
+ const mergedEnv = { ...process.env, ...envVars };
333
+ if (!mergedEnv.CGB_WORKSPACE) mergedEnv.CGB_WORKSPACE = WORKSPACE_DIR;
334
+
335
+ const child = spawn(PYTHON_CMD, ["-m", MODULE_PATH], {
336
+ stdio: ["pipe", "pipe", "pipe"],
337
+ env: mergedEnv,
338
+ shell: IS_WIN,
339
+ });
340
+
341
+ let stdout = "";
342
+ let resolved = false;
343
+
344
+ const finish = (success, detail) => {
345
+ if (resolved) return;
346
+ resolved = true;
347
+ try { child.kill(); } catch {}
348
+ resolve({ success, detail });
349
+ };
350
+
351
+ // Timeout after 15s
352
+ const timer = setTimeout(() => finish(false, "Server did not respond within 15s"), 15000);
353
+
354
+ child.stdout.on("data", (chunk) => {
355
+ stdout += chunk.toString();
356
+ // Look for a valid JSON-RPC response
357
+ const lines = stdout.split("\n");
358
+ for (const line of lines) {
359
+ // MCP uses Content-Length header framing
360
+ if (line.startsWith("{")) {
361
+ try {
362
+ const msg = JSON.parse(line);
363
+ if (msg.result && msg.result.capabilities) {
364
+ // Got initialize response, now request tools/list
365
+ const toolsReq =
366
+ `Content-Length: 80\r\n\r\n` +
367
+ JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} });
368
+ child.stdin.write(toolsReq);
369
+ stdout = "";
370
+ return;
371
+ }
372
+ if (msg.result && msg.result.tools) {
373
+ clearTimeout(timer);
374
+ finish(true, `${msg.result.tools.length} tools available`);
375
+ return;
376
+ }
377
+ } catch { /* partial JSON, wait for more */ }
378
+ }
379
+ }
380
+ });
381
+
382
+ child.on("error", (err) => {
383
+ clearTimeout(timer);
384
+ finish(false, err.message);
385
+ });
386
+
387
+ child.on("exit", (code) => {
388
+ clearTimeout(timer);
389
+ if (!resolved) finish(false, `Server exited with code ${code}`);
390
+ });
391
+
392
+ // Send MCP initialize request
393
+ const initReq = JSON.stringify({
394
+ jsonrpc: "2.0",
395
+ id: 1,
396
+ method: "initialize",
397
+ params: {
398
+ protocolVersion: "2024-11-05",
399
+ capabilities: {},
400
+ clientInfo: { name: "setup-verify", version: "1.0.0" },
401
+ },
402
+ });
403
+ const header = `Content-Length: ${Buffer.byteLength(initReq)}\r\n\r\n`;
404
+ child.stdin.write(header + initReq);
405
+ });
406
+
407
+ if (verified.success) {
408
+ log(` ✓ MCP server started successfully (${verified.detail})`);
409
+ } else {
410
+ log(` ✗ MCP server smoke test failed: ${verified.detail}`);
411
+ log(" The server may still work — try: npx code-graph-builder --server");
412
+ }
413
+
414
+ log("");
415
+ log("── Setup complete ─────────────────────────────────────────");
289
416
  log("");
290
417
  log(" Add to your MCP client config:");
291
418
  log("");
@@ -396,15 +523,104 @@ function autoInstallAndStart(extraArgs) {
396
523
  runServer(PYTHON_CMD, ["-m", MODULE_PATH]);
397
524
  }
398
525
 
526
+ // ---------------------------------------------------------------------------
527
+ // Uninstall — remove Python package, config, workspace data, Claude MCP entry
528
+ // ---------------------------------------------------------------------------
529
+
530
+ async function runUninstall() {
531
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
532
+ const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
533
+ const log = (msg) => process.stderr.write(msg + "\n");
534
+
535
+ log("");
536
+ log("╔══════════════════════════════════════════════════════════╗");
537
+ log("║ code-graph-builder Uninstall ║");
538
+ log("╚══════════════════════════════════════════════════════════╝");
539
+ log("");
540
+
541
+ // 1. Show what will be removed
542
+ const pip = findPip();
543
+ const hasPythonPkg = pythonPackageInstalled();
544
+ const hasWorkspace = existsSync(WORKSPACE_DIR);
545
+ const hasEnv = existsSync(ENV_FILE);
546
+
547
+ // Check Claude Code MCP config
548
+ let hasClaudeConfig = false;
549
+ try {
550
+ execFileSync("claude", ["mcp", "list"], { stdio: "pipe" });
551
+ hasClaudeConfig = true;
552
+ } catch { /* claude CLI not available */ }
553
+
554
+ log(" The following will be removed:");
555
+ log("");
556
+ if (hasPythonPkg) log(" ✓ Python package: code-graph-builder");
557
+ else log(" - Python package: not installed");
558
+ if (hasWorkspace) log(` ✓ Workspace data: ${WORKSPACE_DIR}`);
559
+ else log(" - Workspace data: not found");
560
+ if (hasEnv) log(` ✓ Config file: ${ENV_FILE}`);
561
+ if (hasClaudeConfig) log(" ✓ Claude Code MCP server entry");
562
+ log("");
563
+
564
+ const answer = (await ask(" Proceed with uninstall? [y/N]: ")).trim().toLowerCase();
565
+ rl.close();
566
+
567
+ if (answer !== "y" && answer !== "yes") {
568
+ log("\n Uninstall cancelled.\n");
569
+ process.exit(0);
570
+ }
571
+
572
+ log("");
573
+
574
+ // 2. Remove Claude Code MCP entry
575
+ if (hasClaudeConfig) {
576
+ try {
577
+ execSync("claude mcp remove code-graph-builder", { stdio: "pipe", shell: true });
578
+ log(" ✓ Removed Claude Code MCP entry");
579
+ } catch {
580
+ log(" ⚠ Could not remove Claude Code MCP entry (may not exist)");
581
+ }
582
+ }
583
+
584
+ // 3. Uninstall Python package
585
+ if (hasPythonPkg && pip) {
586
+ try {
587
+ execSync(
588
+ [...pip, "uninstall", "-y", PYTHON_PACKAGE].map(s => `"${s}"`).join(" "),
589
+ { stdio: "inherit", shell: true }
590
+ );
591
+ log(" ✓ Uninstalled Python package");
592
+ } catch {
593
+ log(" ⚠ Failed to uninstall Python package. Try manually: pip uninstall code-graph-builder");
594
+ }
595
+ }
596
+
597
+ // 4. Remove workspace data
598
+ if (hasWorkspace) {
599
+ const { rmSync } = await import("node:fs");
600
+ try {
601
+ rmSync(WORKSPACE_DIR, { recursive: true, force: true });
602
+ log(` ✓ Removed workspace: ${WORKSPACE_DIR}`);
603
+ } catch (err) {
604
+ log(` ⚠ Failed to remove workspace: ${err.message}`);
605
+ }
606
+ }
607
+
608
+ log("");
609
+ log(" Uninstall complete.");
610
+ log(" To also clear the npx cache: npx clear-npx-cache");
611
+ log("");
612
+ }
613
+
399
614
  function startServer(extraArgs = []) {
400
- if (commandExists("uvx")) {
615
+ // Prefer pip-installed package first (most reliable, includes all deps)
616
+ if (pythonPackageInstalled()) {
617
+ runServer(PYTHON_CMD, ["-m", MODULE_PATH]);
618
+ } else if (commandExists("uvx")) {
401
619
  runServer("uvx", [PYTHON_PACKAGE, ...extraArgs]);
402
620
  } else if (commandExists("uv")) {
403
621
  runServer("uv", ["tool", "run", PYTHON_PACKAGE, ...extraArgs]);
404
622
  } else if (commandExists("pipx")) {
405
623
  runServer("pipx", ["run", PYTHON_PACKAGE, ...extraArgs]);
406
- } else if (pythonPackageInstalled()) {
407
- runServer(PYTHON_CMD, ["-m", MODULE_PATH]);
408
624
  } else {
409
625
  // Auto-install via pip
410
626
  autoInstallAndStart(extraArgs);
@@ -435,14 +651,17 @@ if (mode === "--setup") {
435
651
  } else {
436
652
  startServer(args.slice(1));
437
653
  }
654
+ } else if (mode === "--uninstall") {
655
+ runUninstall();
438
656
  } else if (mode === "--help" || mode === "-h") {
439
657
  process.stderr.write(
440
658
  `code-graph-builder - Code knowledge graph MCP server\n\n` +
441
659
  `Usage:\n` +
442
- ` npx code-graph-builder Interactive setup wizard\n` +
443
- ` npx code-graph-builder --server Start MCP server\n` +
444
- ` npx code-graph-builder --setup Re-run setup wizard\n` +
445
- ` npx code-graph-builder --help Show this help\n\n` +
660
+ ` npx code-graph-builder Interactive setup wizard\n` +
661
+ ` npx code-graph-builder --server Start MCP server\n` +
662
+ ` npx code-graph-builder --setup Re-run setup wizard\n` +
663
+ ` npx code-graph-builder --uninstall Completely uninstall\n` +
664
+ ` npx code-graph-builder --help Show this help\n\n` +
446
665
  `Config: ${ENV_FILE}\n`
447
666
  );
448
667
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-graph-builder",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Code knowledge graph builder with MCP server for AI-assisted code navigation",
5
5
  "license": "MIT",
6
6
  "bin": {