code-graph-builder 0.22.0 → 0.25.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 +185 -139
  2. package/package.json +1 -1
package/bin/cli.mjs CHANGED
@@ -21,7 +21,8 @@ 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
23
  const IS_WIN = platform() === "win32";
24
- const PYPI_INDEX = "https://pypi.org/simple";
24
+ // Removed hardcoded PyPI index — pip will use the user's configured source
25
+ // (e.g. mirrors in pip.conf / pip.ini)
25
26
 
26
27
  // ---------------------------------------------------------------------------
27
28
  // Tree-style UI helpers
@@ -51,14 +52,16 @@ const T = {
51
52
  /**
52
53
  * Interactive single-select menu.
53
54
  * Arrow keys to navigate, Space to select, Enter to confirm.
54
- * Returns the index of the selected option, or -1 if cancelled (Ctrl+C).
55
+ * Returns the index of the selected option, -1 if cancelled (Ctrl+C),
56
+ * or -2 if the user pressed ← (back to previous step).
55
57
  *
56
58
  * @param {string[]} options - Display labels for each option
57
59
  * @param {string} prefix - Tree prefix for each line (e.g. " │ ")
58
60
  * @param {number} defaultIndex - Initially highlighted index
61
+ * @param {boolean} allowBack - Whether ← arrow triggers back (-2)
59
62
  * @returns {Promise<number>}
60
63
  */
61
- function selectMenu(options, prefix = " ", defaultIndex = 0) {
64
+ function selectMenu(options, prefix = " ", defaultIndex = 0, allowBack = false) {
62
65
  return new Promise((resolve) => {
63
66
  const out = process.stderr;
64
67
  let cursor = defaultIndex;
@@ -71,10 +74,12 @@ function selectMenu(options, prefix = " ", defaultIndex = 0) {
71
74
  const CYAN = "\x1b[36m";
72
75
  const RESET = "\x1b[0m";
73
76
 
77
+ const backHint = allowBack ? `${DIM} ← back${RESET}` : "";
78
+
74
79
  function render(initial = false) {
75
80
  // Move cursor up to overwrite previous render (skip on first draw)
76
81
  if (!initial) {
77
- out.write(`\x1b[${options.length}A`);
82
+ out.write(`\x1b[${options.length + (allowBack ? 1 : 0)}A`);
78
83
  }
79
84
  for (let i = 0; i < options.length; i++) {
80
85
  const isActive = i === cursor;
@@ -88,6 +93,9 @@ function selectMenu(options, prefix = " ", defaultIndex = 0) {
88
93
  // Clear line then write
89
94
  out.write(`\x1b[2K${prefix}${radio} ${label}\n`);
90
95
  }
96
+ if (allowBack) {
97
+ out.write(`\x1b[2K${prefix}${DIM}← Back to previous step${RESET}\n`);
98
+ }
91
99
  }
92
100
 
93
101
  // Hide cursor
@@ -116,6 +124,13 @@ function selectMenu(options, prefix = " ", defaultIndex = 0) {
116
124
  return;
117
125
  }
118
126
 
127
+ // Arrow left — back to previous step
128
+ if (key === "\x1b[D" && allowBack) {
129
+ cleanup();
130
+ resolve(-2);
131
+ return;
132
+ }
133
+
119
134
  // Arrow up / k
120
135
  if (key === "\x1b[A" || key === "k") {
121
136
  cursor = (cursor - 1 + options.length) % options.length;
@@ -328,28 +343,18 @@ async function runSetup() {
328
343
  // Load existing config
329
344
  const existing = loadEnvFile();
330
345
 
331
- // --- Step 1: Workspace ---
332
- log(` ${T.DOT} Step 1/3 Workspace`);
333
- log(` ${T.SIDE}`);
334
- log(` ${T.BRANCH} Stores indexed repos, graphs, and embeddings`);
335
-
336
- const workspace =
337
- (await ask(` ${T.SIDE} Path [${WORKSPACE_DIR}]: `)).trim() || WORKSPACE_DIR;
338
-
339
- log(` ${T.LAST} ${T.OK} ${workspace}`);
340
- log();
341
-
342
- // --- Step 2: LLM Provider ---
343
- log(` ${T.DOT} Step 2/3 LLM Provider`);
344
- log(` ${T.SIDE}`);
345
- log(` ${T.BRANCH} For natural language queries & descriptions`);
346
- log(` ${T.SIDE} Use ↑↓ to navigate, Space to select, Enter to confirm`);
347
- log(` ${T.SIDE}`);
348
-
349
- if (existing.LLM_API_KEY) {
350
- log(` ${T.SIDE} Current: ${mask(existing.LLM_API_KEY)} → ${existing.LLM_BASE_URL || "?"}`);
351
- log(` ${T.SIDE}`);
352
- }
346
+ // Step results preserved across back/forward navigation
347
+ let workspace = existing.CGB_WORKSPACE || WORKSPACE_DIR;
348
+ let llmKey = existing.LLM_API_KEY || "";
349
+ let llmBaseUrl = existing.LLM_BASE_URL || "";
350
+ let llmModel = existing.LLM_MODEL || "";
351
+ let llmProviderName = "skipped";
352
+ let embedKey = "";
353
+ let embedUrl = "";
354
+ let embedModel = "";
355
+ let embedKeyEnv = "DASHSCOPE_API_KEY";
356
+ let embedUrlEnv = "DASHSCOPE_BASE_URL";
357
+ let embedProviderName = "skipped";
353
358
 
354
359
  const llmOptions = [
355
360
  "Moonshot / Kimi platform.moonshot.cn",
@@ -369,66 +374,6 @@ async function runSetup() {
369
374
  { name: "LiteLLM", url: "http://localhost:4000/v1", model: "gpt-4o" },
370
375
  ];
371
376
 
372
- // Close readline before raw mode menu, reopen after
373
- rl.close();
374
- const llmChoice = await selectMenu(llmOptions, ` ${T.SIDE} `, 6);
375
- rl = createInterface({ input: process.stdin, output: process.stderr });
376
- ask = (q) => new Promise((resolve) => rl.question(q, resolve));
377
-
378
- let llmKey = existing.LLM_API_KEY || "";
379
- let llmBaseUrl = existing.LLM_BASE_URL || "";
380
- let llmModel = existing.LLM_MODEL || "";
381
- let llmProviderName = "skipped";
382
-
383
- if (llmChoice >= 0 && llmChoice < 5) {
384
- // Known provider
385
- const provider = llmProviders[llmChoice];
386
- llmBaseUrl = provider.url;
387
- llmModel = provider.model;
388
- llmProviderName = provider.name;
389
-
390
- log(` ${T.SIDE}`);
391
- llmKey = (await ask(` ${T.SIDE} API Key (sk-...): `)).trim() || existing.LLM_API_KEY || "";
392
-
393
- if (llmKey) {
394
- const urlOverride = (await ask(` ${T.SIDE} Base URL [${llmBaseUrl}]: `)).trim();
395
- if (urlOverride) llmBaseUrl = urlOverride;
396
- const modelOverride = (await ask(` ${T.SIDE} Model [${llmModel}]: `)).trim();
397
- if (modelOverride) llmModel = modelOverride;
398
- }
399
- } else if (llmChoice === 5) {
400
- // Custom
401
- llmProviderName = "Custom";
402
- const defUrl = llmBaseUrl || existing.LLM_BASE_URL || "";
403
- const defModel = llmModel || existing.LLM_MODEL || "gpt-4o";
404
- const defKey = existing.LLM_API_KEY || "";
405
- log(` ${T.SIDE}`);
406
- llmBaseUrl = (await ask(` ${T.SIDE} API Base URL${defUrl ? ` [${defUrl}]` : ""}: `)).trim() || defUrl;
407
- llmModel = (await ask(` ${T.SIDE} Model${defModel ? ` [${defModel}]` : ""}: `)).trim() || defModel;
408
- llmKey = (await ask(` ${T.SIDE} API Key${defKey ? ` [${mask(defKey)}]` : " (sk-...)"}: `)).trim() || defKey;
409
- }
410
- // llmChoice === 6 or -1 → skip
411
-
412
- if (llmKey) {
413
- log(` ${T.LAST} ${T.OK} ${llmProviderName} / ${llmModel}`);
414
- } else {
415
- log(` ${T.LAST} ${T.WARN} Skipped (configure later in ${ENV_FILE})`);
416
- }
417
- log();
418
-
419
- // --- Step 3: Embedding Provider ---
420
- log(` ${T.DOT} Step 3/3 Embedding Provider`);
421
- log(` ${T.SIDE}`);
422
- log(` ${T.BRANCH} For semantic code search`);
423
- log(` ${T.SIDE} Use ↑↓ to navigate, Space to select, Enter to confirm`);
424
- log(` ${T.SIDE}`);
425
-
426
- if (existing.DASHSCOPE_API_KEY || existing.EMBED_API_KEY) {
427
- const ek = existing.DASHSCOPE_API_KEY || existing.EMBED_API_KEY;
428
- log(` ${T.SIDE} Current: ${mask(ek)} → ${existing.DASHSCOPE_BASE_URL || existing.EMBED_BASE_URL || "?"}`);
429
- log(` ${T.SIDE}`);
430
- }
431
-
432
377
  const embedOptions = [
433
378
  "DashScope / Qwen dashscope.console.aliyun.com (free tier)",
434
379
  "OpenAI Embeddings platform.openai.com",
@@ -441,56 +386,157 @@ async function runSetup() {
441
386
  { name: "OpenAI", url: "https://api.openai.com/v1", model: "text-embedding-3-small", keyEnv: "OPENAI_API_KEY", urlEnv: "OPENAI_BASE_URL" },
442
387
  ];
443
388
 
444
- rl.close();
445
- const embedChoice = await selectMenu(embedOptions, ` ${T.SIDE} `, 3);
446
- rl = createInterface({ input: process.stdin, output: process.stderr });
447
- ask = (q) => new Promise((resolve) => rl.question(q, resolve));
389
+ // --- Step-based wizard with ← back support ---
390
+ let step = 1;
448
391
 
449
- let embedKey = "";
450
- let embedUrl = "";
451
- let embedModel = "";
452
- let embedKeyEnv = "DASHSCOPE_API_KEY";
453
- let embedUrlEnv = "DASHSCOPE_BASE_URL";
454
- let embedProviderName = "skipped";
392
+ while (step >= 1 && step <= 3) {
393
+
394
+ // ─── Step 1: Workspace ───
395
+ if (step === 1) {
396
+ log(` ${T.DOT} Step 1/3 Workspace`);
397
+ log(` ${T.SIDE}`);
398
+ log(` ${T.BRANCH} Stores indexed repos, graphs, and embeddings`);
455
399
 
456
- if (embedChoice >= 0 && embedChoice < 2) {
457
- // Known provider
458
- const ep = embedProvidersList[embedChoice];
459
- embedUrl = ep.url;
460
- embedModel = ep.model;
461
- embedKeyEnv = ep.keyEnv;
462
- embedUrlEnv = ep.urlEnv;
463
- embedProviderName = ep.name;
464
-
465
- log(` ${T.SIDE}`);
466
- embedKey = (await ask(` ${T.SIDE} API Key: `)).trim() ||
467
- existing[embedKeyEnv] || existing.DASHSCOPE_API_KEY || "";
468
-
469
- if (embedKey) {
470
- const urlOverride = (await ask(` ${T.SIDE} Base URL [${embedUrl}]: `)).trim();
471
- if (urlOverride) embedUrl = urlOverride;
472
- const modelOverride = (await ask(` ${T.SIDE} Model [${embedModel}]: `)).trim();
473
- if (modelOverride) embedModel = modelOverride;
400
+ workspace =
401
+ (await ask(` ${T.SIDE} Path [${WORKSPACE_DIR}]: `)).trim() || WORKSPACE_DIR;
402
+
403
+ log(` ${T.LAST} ${T.OK} ${workspace}`);
404
+ log();
405
+ step = 2;
406
+ continue;
474
407
  }
475
- } else if (embedChoice === 2) {
476
- // Custom
477
- embedProviderName = "Custom";
478
- const defEmbedUrl = existing.EMBED_BASE_URL || existing.DASHSCOPE_BASE_URL || "";
479
- const defEmbedModel = existing.EMBED_MODEL || "text-embedding-3-small";
480
- const defEmbedKey = existing.EMBED_API_KEY || existing.DASHSCOPE_API_KEY || "";
481
- log(` ${T.SIDE}`);
482
- embedUrl = (await ask(` ${T.SIDE} API Base URL${defEmbedUrl ? ` [${defEmbedUrl}]` : ""}: `)).trim() || defEmbedUrl;
483
- embedModel = (await ask(` ${T.SIDE} Model${defEmbedModel ? ` [${defEmbedModel}]` : ""}: `)).trim() || defEmbedModel;
484
- embedKey = (await ask(` ${T.SIDE} API Key${defEmbedKey ? ` [${mask(defEmbedKey)}]` : ""}: `)).trim() || defEmbedKey;
485
- embedKeyEnv = "EMBED_API_KEY";
486
- embedUrlEnv = "EMBED_BASE_URL";
487
- }
488
- // embedChoice === 3 or -1 → skip
489
408
 
490
- if (embedKey) {
491
- log(` ${T.LAST} ${T.OK} ${embedProviderName} / ${embedModel}`);
492
- } else {
493
- log(` ${T.LAST} ${T.WARN} Skipped (configure later in ${ENV_FILE})`);
409
+ // ─── Step 2: LLM Provider ───
410
+ if (step === 2) {
411
+ log(` ${T.DOT} Step 2/3 LLM Provider`);
412
+ log(` ${T.SIDE}`);
413
+ log(` ${T.BRANCH} For natural language queries & descriptions`);
414
+ log(` ${T.SIDE} Use ↑↓ navigate, Enter confirm, ← back`);
415
+ log(` ${T.SIDE}`);
416
+
417
+ if (existing.LLM_API_KEY) {
418
+ log(` ${T.SIDE} Current: ${mask(existing.LLM_API_KEY)} → ${existing.LLM_BASE_URL || "?"}`);
419
+ log(` ${T.SIDE}`);
420
+ }
421
+
422
+ rl.close();
423
+ const llmChoice = await selectMenu(llmOptions, ` ${T.SIDE} `, 6, true);
424
+ rl = createInterface({ input: process.stdin, output: process.stderr });
425
+ ask = (q) => new Promise((resolve) => rl.question(q, resolve));
426
+
427
+ if (llmChoice === -2) { log(); step = 1; continue; }
428
+ if (llmChoice === -1) { rl.close(); return; }
429
+
430
+ llmKey = existing.LLM_API_KEY || "";
431
+ llmBaseUrl = existing.LLM_BASE_URL || "";
432
+ llmModel = existing.LLM_MODEL || "";
433
+ llmProviderName = "skipped";
434
+
435
+ if (llmChoice >= 0 && llmChoice < 5) {
436
+ const provider = llmProviders[llmChoice];
437
+ llmBaseUrl = provider.url;
438
+ llmModel = provider.model;
439
+ llmProviderName = provider.name;
440
+
441
+ log(` ${T.SIDE}`);
442
+ llmKey = (await ask(` ${T.SIDE} API Key (sk-...): `)).trim() || existing.LLM_API_KEY || "";
443
+
444
+ if (llmKey) {
445
+ const urlOverride = (await ask(` ${T.SIDE} Base URL [${llmBaseUrl}]: `)).trim();
446
+ if (urlOverride) llmBaseUrl = urlOverride;
447
+ const modelOverride = (await ask(` ${T.SIDE} Model [${llmModel}]: `)).trim();
448
+ if (modelOverride) llmModel = modelOverride;
449
+ }
450
+ } else if (llmChoice === 5) {
451
+ llmProviderName = "Custom";
452
+ const defUrl = llmBaseUrl || existing.LLM_BASE_URL || "";
453
+ const defModel = llmModel || existing.LLM_MODEL || "gpt-4o";
454
+ const defKey = existing.LLM_API_KEY || "";
455
+ log(` ${T.SIDE}`);
456
+ llmBaseUrl = (await ask(` ${T.SIDE} API Base URL${defUrl ? ` [${defUrl}]` : ""}: `)).trim() || defUrl;
457
+ llmModel = (await ask(` ${T.SIDE} Model${defModel ? ` [${defModel}]` : ""}: `)).trim() || defModel;
458
+ llmKey = (await ask(` ${T.SIDE} API Key${defKey ? ` [${mask(defKey)}]` : " (sk-...)"}: `)).trim() || defKey;
459
+ }
460
+
461
+ if (llmKey) {
462
+ log(` ${T.LAST} ${T.OK} ${llmProviderName} / ${llmModel}`);
463
+ } else {
464
+ log(` ${T.LAST} ${T.WARN} Skipped (configure later in ${ENV_FILE})`);
465
+ }
466
+ log();
467
+ step = 3;
468
+ continue;
469
+ }
470
+
471
+ // ─── Step 3: Embedding Provider ───
472
+ if (step === 3) {
473
+ log(` ${T.DOT} Step 3/3 Embedding Provider`);
474
+ log(` ${T.SIDE}`);
475
+ log(` ${T.BRANCH} For semantic code search`);
476
+ log(` ${T.SIDE} Use ↑↓ navigate, Enter confirm, ← back`);
477
+ log(` ${T.SIDE}`);
478
+
479
+ if (existing.DASHSCOPE_API_KEY || existing.EMBED_API_KEY) {
480
+ const ek = existing.DASHSCOPE_API_KEY || existing.EMBED_API_KEY;
481
+ log(` ${T.SIDE} Current: ${mask(ek)} → ${existing.DASHSCOPE_BASE_URL || existing.EMBED_BASE_URL || "?"}`);
482
+ log(` ${T.SIDE}`);
483
+ }
484
+
485
+ rl.close();
486
+ const embedChoice = await selectMenu(embedOptions, ` ${T.SIDE} `, 3, true);
487
+ rl = createInterface({ input: process.stdin, output: process.stderr });
488
+ ask = (q) => new Promise((resolve) => rl.question(q, resolve));
489
+
490
+ if (embedChoice === -2) { log(); step = 2; continue; }
491
+ if (embedChoice === -1) { rl.close(); return; }
492
+
493
+ embedKey = "";
494
+ embedUrl = "";
495
+ embedModel = "";
496
+ embedKeyEnv = "DASHSCOPE_API_KEY";
497
+ embedUrlEnv = "DASHSCOPE_BASE_URL";
498
+ embedProviderName = "skipped";
499
+
500
+ if (embedChoice >= 0 && embedChoice < 2) {
501
+ const ep = embedProvidersList[embedChoice];
502
+ embedUrl = ep.url;
503
+ embedModel = ep.model;
504
+ embedKeyEnv = ep.keyEnv;
505
+ embedUrlEnv = ep.urlEnv;
506
+ embedProviderName = ep.name;
507
+
508
+ log(` ${T.SIDE}`);
509
+ embedKey = (await ask(` ${T.SIDE} API Key: `)).trim() ||
510
+ existing[embedKeyEnv] || existing.DASHSCOPE_API_KEY || "";
511
+
512
+ if (embedKey) {
513
+ const urlOverride = (await ask(` ${T.SIDE} Base URL [${embedUrl}]: `)).trim();
514
+ if (urlOverride) embedUrl = urlOverride;
515
+ const modelOverride = (await ask(` ${T.SIDE} Model [${embedModel}]: `)).trim();
516
+ if (modelOverride) embedModel = modelOverride;
517
+ }
518
+ } else if (embedChoice === 2) {
519
+ embedProviderName = "Custom";
520
+ const defEmbedUrl = existing.EMBED_BASE_URL || existing.DASHSCOPE_BASE_URL || "";
521
+ const defEmbedModel = existing.EMBED_MODEL || "text-embedding-3-small";
522
+ const defEmbedKey = existing.EMBED_API_KEY || existing.DASHSCOPE_API_KEY || "";
523
+ log(` ${T.SIDE}`);
524
+ embedUrl = (await ask(` ${T.SIDE} API Base URL${defEmbedUrl ? ` [${defEmbedUrl}]` : ""}: `)).trim() || defEmbedUrl;
525
+ embedModel = (await ask(` ${T.SIDE} Model${defEmbedModel ? ` [${defEmbedModel}]` : ""}: `)).trim() || defEmbedModel;
526
+ embedKey = (await ask(` ${T.SIDE} API Key${defEmbedKey ? ` [${mask(defEmbedKey)}]` : ""}: `)).trim() || defEmbedKey;
527
+ embedKeyEnv = "EMBED_API_KEY";
528
+ embedUrlEnv = "EMBED_BASE_URL";
529
+ }
530
+
531
+ if (embedKey) {
532
+ log(` ${T.LAST} ${T.OK} ${embedProviderName} / ${embedModel}`);
533
+ } else {
534
+ log(` ${T.LAST} ${T.WARN} Skipped (configure later in ${ENV_FILE})`);
535
+ }
536
+
537
+ step = 4; // done
538
+ continue;
539
+ }
494
540
  }
495
541
 
496
542
  rl.close();
@@ -540,7 +586,7 @@ async function runSetup() {
540
586
  if (pip) {
541
587
  try {
542
588
  execSync(
543
- [...pip, "install", "--upgrade", "-i", PYPI_INDEX, PYTHON_PACKAGE].map(s => `"${s}"`).join(" "),
589
+ [...pip, "install", "--upgrade", PYTHON_PACKAGE].map(s => `"${s}"`).join(" "),
544
590
  { stdio: "pipe", shell: true }
545
591
  );
546
592
  } catch { /* handled below */ }
@@ -551,7 +597,7 @@ async function runSetup() {
551
597
  if (pip) {
552
598
  try {
553
599
  execSync(
554
- [...pip, "install", "--upgrade", "-i", PYPI_INDEX, PYTHON_PACKAGE].map(s => `"${s}"`).join(" "),
600
+ [...pip, "install", "--upgrade", PYTHON_PACKAGE].map(s => `"${s}"`).join(" "),
555
601
  { stdio: "pipe", shell: true }
556
602
  );
557
603
  } catch { /* upgrade is best-effort */ }
@@ -563,7 +609,7 @@ async function runSetup() {
563
609
  log(` ${T.BRANCH} ${T.OK} ${PYTHON_PACKAGE} ${ver || ""}`);
564
610
  } else {
565
611
  log(` ${T.BRANCH} ${T.FAIL} Package not installed`);
566
- log(` ${T.LAST} Run manually: pip install -i ${PYPI_INDEX} ${PYTHON_PACKAGE}`);
612
+ log(` ${T.LAST} Run manually: pip install ${PYTHON_PACKAGE}`);
567
613
  log();
568
614
  return;
569
615
  }
@@ -738,13 +784,13 @@ function autoInstallAndStart(extraArgs) {
738
784
 
739
785
  try {
740
786
  execSync(
741
- [...pip, "install", "-i", PYPI_INDEX, PYTHON_PACKAGE].map(s => `"${s}"`).join(" "),
787
+ [...pip, "install", PYTHON_PACKAGE].map(s => `"${s}"`).join(" "),
742
788
  { stdio: "inherit", shell: true }
743
789
  );
744
790
  } catch (err) {
745
791
  process.stderr.write(
746
792
  `\nFailed to install ${PYTHON_PACKAGE}.\n` +
747
- `Try manually: ${pip.join(" ")} install -i ${PYPI_INDEX} ${PYTHON_PACKAGE}\n`
793
+ `Try manually: ${pip.join(" ")} install ${PYTHON_PACKAGE}\n`
748
794
  );
749
795
  process.exit(1);
750
796
  }
@@ -752,7 +798,7 @@ function autoInstallAndStart(extraArgs) {
752
798
  if (!pythonPackageInstalled()) {
753
799
  process.stderr.write(
754
800
  `\nInstallation completed but package not importable.\n` +
755
- `Try manually: ${pip.join(" ")} install -i ${PYPI_INDEX} ${PYTHON_PACKAGE}\n`
801
+ `Try manually: ${pip.join(" ")} install ${PYTHON_PACKAGE}\n`
756
802
  );
757
803
  process.exit(1);
758
804
  }
@@ -879,7 +925,7 @@ if (mode === "--setup") {
879
925
  if (!PYTHON_CMD || !pythonPackageInstalled()) {
880
926
  process.stderr.write(
881
927
  `Error: Python package '${PYTHON_PACKAGE}' is not installed.\n` +
882
- `Run: pip install -i ${PYPI_INDEX} ${PYTHON_PACKAGE}\n`
928
+ `Run: pip install ${PYTHON_PACKAGE}\n`
883
929
  );
884
930
  process.exit(1);
885
931
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-graph-builder",
3
- "version": "0.22.0",
3
+ "version": "0.25.0",
4
4
  "description": "Code knowledge graph builder with MCP server for AI-assisted code navigation",
5
5
  "license": "MIT",
6
6
  "bin": {