homegraph 1.5.2 → 1.5.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.
Files changed (150) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/README.md +15 -8
  3. package/dist/arkui/index.d.ts +4 -0
  4. package/dist/arkui/index.d.ts.map +1 -0
  5. package/dist/arkui/index.js +23 -0
  6. package/dist/arkui/index.js.map +1 -0
  7. package/dist/arkui/migrate-passage.d.ts +28 -0
  8. package/dist/arkui/migrate-passage.d.ts.map +1 -0
  9. package/dist/arkui/migrate-passage.js +310 -0
  10. package/dist/arkui/migrate-passage.js.map +1 -0
  11. package/dist/arkui/migrate-semantics.d.ts +47 -0
  12. package/dist/arkui/migrate-semantics.d.ts.map +1 -0
  13. package/dist/arkui/migrate-semantics.js +229 -0
  14. package/dist/arkui/migrate-semantics.js.map +1 -0
  15. package/dist/arkui/migrate-snapshot.d.ts +79 -0
  16. package/dist/arkui/migrate-snapshot.d.ts.map +1 -0
  17. package/dist/arkui/migrate-snapshot.js +340 -0
  18. package/dist/arkui/migrate-snapshot.js.map +1 -0
  19. package/dist/bin/fatal-handler.js +2 -2
  20. package/dist/bin/homegraph.js +105 -156
  21. package/dist/bin/homegraph.js.map +1 -1
  22. package/dist/bin/node-version-check.d.ts +6 -15
  23. package/dist/bin/node-version-check.d.ts.map +1 -1
  24. package/dist/bin/node-version-check.js +8 -37
  25. package/dist/bin/node-version-check.js.map +1 -1
  26. package/dist/db/index.d.ts +36 -1
  27. package/dist/db/index.d.ts.map +1 -1
  28. package/dist/db/index.js +92 -6
  29. package/dist/db/index.js.map +1 -1
  30. package/dist/db/migrations.d.ts +1 -1
  31. package/dist/db/migrations.d.ts.map +1 -1
  32. package/dist/db/migrations.js +15 -1
  33. package/dist/db/migrations.js.map +1 -1
  34. package/dist/db/queries.d.ts +42 -0
  35. package/dist/db/queries.d.ts.map +1 -1
  36. package/dist/db/queries.js +177 -10
  37. package/dist/db/queries.js.map +1 -1
  38. package/dist/db/schema.sql +6 -1
  39. package/dist/db/sqlite-adapter.d.ts +8 -3
  40. package/dist/db/sqlite-adapter.d.ts.map +1 -1
  41. package/dist/db/sqlite-adapter.js +45 -3
  42. package/dist/db/sqlite-adapter.js.map +1 -1
  43. package/dist/extraction/extraction-version.d.ts +1 -1
  44. package/dist/extraction/extraction-version.js +1 -1
  45. package/dist/extraction/generated-detection.d.ts +42 -11
  46. package/dist/extraction/generated-detection.d.ts.map +1 -1
  47. package/dist/extraction/generated-detection.js +141 -20
  48. package/dist/extraction/generated-detection.js.map +1 -1
  49. package/dist/extraction/index.d.ts +42 -0
  50. package/dist/extraction/index.d.ts.map +1 -1
  51. package/dist/extraction/index.js +96 -0
  52. package/dist/extraction/index.js.map +1 -1
  53. package/dist/extraction/languages/arkts.d.ts +19 -2
  54. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  55. package/dist/extraction/languages/arkts.js +460 -110
  56. package/dist/extraction/languages/arkts.js.map +1 -1
  57. package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -1
  58. package/dist/extraction/wasm-runtime-flags.js +9 -6
  59. package/dist/extraction/wasm-runtime-flags.js.map +1 -1
  60. package/dist/graph-sources.d.ts +41 -0
  61. package/dist/graph-sources.d.ts.map +1 -0
  62. package/dist/graph-sources.js +82 -0
  63. package/dist/graph-sources.js.map +1 -0
  64. package/dist/index.d.ts +19 -1
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +79 -9
  67. package/dist/index.js.map +1 -1
  68. package/dist/installer/index.d.ts.map +1 -1
  69. package/dist/installer/index.js +0 -48
  70. package/dist/installer/index.js.map +1 -1
  71. package/dist/installer/targets/antigravity.js +1 -1
  72. package/dist/installer/targets/antigravity.js.map +1 -1
  73. package/dist/installer/targets/deveco.js +1 -1
  74. package/dist/installer/targets/deveco.js.map +1 -1
  75. package/dist/installer/targets/hermes.d.ts +1 -1
  76. package/dist/installer/targets/hermes.js +2 -2
  77. package/dist/installer/targets/hermes.js.map +1 -1
  78. package/dist/installer/targets/opencode.js +1 -1
  79. package/dist/installer/targets/opencode.js.map +1 -1
  80. package/dist/installer/targets/shared.js +1 -1
  81. package/dist/installer/targets/shared.js.map +1 -1
  82. package/dist/mcp/daemon-paths.d.ts +5 -4
  83. package/dist/mcp/daemon-paths.d.ts.map +1 -1
  84. package/dist/mcp/daemon-paths.js +28 -13
  85. package/dist/mcp/daemon-paths.js.map +1 -1
  86. package/dist/mcp/daemon-registry.d.ts.map +1 -1
  87. package/dist/mcp/daemon-registry.js +4 -1
  88. package/dist/mcp/daemon-registry.js.map +1 -1
  89. package/dist/mcp/daemon.d.ts +1 -1
  90. package/dist/mcp/daemon.js +1 -1
  91. package/dist/mcp/engine.d.ts.map +1 -1
  92. package/dist/mcp/engine.js +12 -3
  93. package/dist/mcp/engine.js.map +1 -1
  94. package/dist/mcp/explore-dedup.d.ts +137 -0
  95. package/dist/mcp/explore-dedup.d.ts.map +1 -0
  96. package/dist/mcp/explore-dedup.js +236 -0
  97. package/dist/mcp/explore-dedup.js.map +1 -0
  98. package/dist/mcp/explore-session-state.d.ts +217 -0
  99. package/dist/mcp/explore-session-state.d.ts.map +1 -0
  100. package/dist/mcp/explore-session-state.js +322 -0
  101. package/dist/mcp/explore-session-state.js.map +1 -0
  102. package/dist/mcp/index.d.ts.map +1 -1
  103. package/dist/mcp/index.js +4 -7
  104. package/dist/mcp/index.js.map +1 -1
  105. package/dist/mcp/proxy.d.ts.map +1 -1
  106. package/dist/mcp/proxy.js +5 -14
  107. package/dist/mcp/proxy.js.map +1 -1
  108. package/dist/mcp/query-worker.d.ts +1 -1
  109. package/dist/mcp/query-worker.js +1 -1
  110. package/dist/mcp/server-instructions.d.ts +7 -2
  111. package/dist/mcp/server-instructions.d.ts.map +1 -1
  112. package/dist/mcp/server-instructions.js +104 -28
  113. package/dist/mcp/server-instructions.js.map +1 -1
  114. package/dist/mcp/session.d.ts +14 -3
  115. package/dist/mcp/session.d.ts.map +1 -1
  116. package/dist/mcp/session.js +17 -14
  117. package/dist/mcp/session.js.map +1 -1
  118. package/dist/mcp/startup-handshake.d.ts +1 -1
  119. package/dist/mcp/startup-handshake.js +1 -1
  120. package/dist/mcp/tools.d.ts +117 -7
  121. package/dist/mcp/tools.d.ts.map +1 -1
  122. package/dist/mcp/tools.js +3056 -503
  123. package/dist/mcp/tools.js.map +1 -1
  124. package/dist/resolution/frameworks/arkts-napi.d.ts +14 -0
  125. package/dist/resolution/frameworks/arkts-napi.d.ts.map +1 -1
  126. package/dist/resolution/frameworks/arkts-napi.js +169 -49
  127. package/dist/resolution/frameworks/arkts-napi.js.map +1 -1
  128. package/dist/search/query-utils.d.ts +116 -4
  129. package/dist/search/query-utils.d.ts.map +1 -1
  130. package/dist/search/query-utils.js +869 -60
  131. package/dist/search/query-utils.js.map +1 -1
  132. package/dist/types.d.ts +8 -0
  133. package/dist/types.d.ts.map +1 -1
  134. package/dist/upgrade/index.d.ts +14 -26
  135. package/dist/upgrade/index.d.ts.map +1 -1
  136. package/dist/upgrade/index.js +28 -123
  137. package/dist/upgrade/index.js.map +1 -1
  138. package/dist/upgrade/remove-binary.d.ts +19 -19
  139. package/dist/upgrade/remove-binary.d.ts.map +1 -1
  140. package/dist/upgrade/remove-binary.js +20 -21
  141. package/dist/upgrade/remove-binary.js.map +1 -1
  142. package/dist/upgrade/update-check.d.ts +4 -3
  143. package/dist/upgrade/update-check.d.ts.map +1 -1
  144. package/dist/upgrade/update-check.js +4 -3
  145. package/dist/upgrade/update-check.js.map +1 -1
  146. package/package.json +12 -8
  147. package/dist/telemetry/index.d.ts +0 -143
  148. package/dist/telemetry/index.d.ts.map +0 -1
  149. package/dist/telemetry/index.js +0 -541
  150. package/dist/telemetry/index.js.map +0 -1
@@ -71,7 +71,6 @@ const fatal_handler_1 = require("./fatal-handler");
71
71
  const wasm_runtime_flags_1 = require("../extraction/wasm-runtime-flags");
72
72
  const command_supervision_1 = require("./command-supervision");
73
73
  const extraction_version_1 = require("../extraction/extraction-version");
74
- const telemetry_1 = require("../telemetry");
75
74
  // Lazy-load heavy modules (HomeGraph, runInstaller) to keep CLI startup fast.
76
75
  async function loadHomeGraph() {
77
76
  try {
@@ -90,25 +89,13 @@ async function loadHomeGraph() {
90
89
  // which fails for ESM-only packages. This bypasses the transformation.
91
90
  // eslint-disable-next-line @typescript-eslint/no-implied-eval
92
91
  const importESM = new Function('specifier', 'return import(specifier)');
93
- // Block HomeGraph on Node.js 25.x — V8's turboshaft WASM JIT has a Zone
94
- // allocator bug that reliably crashes when compiling tree-sitter
95
- // grammars (see #54, #81, #140). The previous behaviour was a soft
96
- // console.warn that scrolls off-screen before the OOM crash 30 seconds
97
- // later, leading to a steady stream of "what is this OOM" reports.
98
- // Hard-exit before any WASM work; allow override via env var for users
99
- // who patched V8 themselves or want to test a future fix.
100
- const nodeVersion = process.versions.node;
101
- const nodeMajor = parseInt(nodeVersion.split('.')[0] ?? '0', 10);
102
- if (nodeMajor >= 25) {
103
- process.stderr.write((0, node_version_check_1.buildNode25BlockBanner)(nodeVersion) + '\n');
104
- if (!process.env.HOMEGRAPH_ALLOW_UNSAFE_NODE) {
105
- process.exit(1);
106
- }
107
- // Override active — banner shown for visibility, continuing.
108
- }
109
92
  // Enforce the supported Node floor. `engines` in package.json only *warns* on
110
93
  // install (unless engine-strict), so hard-block here to actually keep users off
111
- // unsupported versions. Mirrors the 25+ block above. See package.json `engines`.
94
+ // unsupported versions. See package.json `engines` and ./node-version-check.
95
+ // Node ≥22 WASM Zone OOM is mitigated by --liftoff-only relaunch below — not by
96
+ // blocking majors (Node 25+ is supported when that flag is applied).
97
+ const nodeVersion = process.versions.node;
98
+ const nodeMajor = parseInt(nodeVersion.split('.')[0] ?? '0', 10);
112
99
  if (nodeMajor < node_version_check_1.MIN_NODE_MAJOR) {
113
100
  process.stderr.write((0, node_version_check_1.buildNodeTooOldBanner)(nodeVersion) + '\n');
114
101
  if (!process.env.HOMEGRAPH_ALLOW_UNSAFE_NODE) {
@@ -184,30 +171,6 @@ function main() {
184
171
  .name('homegraph')
185
172
  .description('Code intelligence and knowledge graph for any codebase')
186
173
  .version(packageJson.version);
187
- // Anonymous usage telemetry (see TELEMETRY.md): record the invoked subcommand
188
- // NAME only — never arguments or paths. Counts buffer locally; network sends
189
- // piggyback on commands that run long anyway (quick commands only append to
190
- // the local buffer at exit, costing nothing).
191
- // install/uninstall are absent on purpose: the installer flushes at its own
192
- // end, AFTER its consent prompt — a flush here would fire the first-run
193
- // notice before the user ever sees the toggle.
194
- const TELEMETRY_FLUSH_COMMANDS = new Set(['init', 'uninit', 'index', 'sync', 'upgrade']);
195
- program.hook('preAction', (_thisCommand, actionCommand) => {
196
- try {
197
- // The detached daemon re-invokes `serve --mcp` internally — not a user action.
198
- if (process.env.HOMEGRAPH_DAEMON_INTERNAL)
199
- return;
200
- const name = actionCommand.name();
201
- if (name === 'telemetry')
202
- return; // managing telemetry is not usage
203
- (0, telemetry_1.getTelemetry)().recordUsage('cli_command', name, true);
204
- if (TELEMETRY_FLUSH_COMMANDS.has(name))
205
- (0, telemetry_1.getTelemetry)().maybeFlush();
206
- }
207
- catch {
208
- /* telemetry must never break the CLI */
209
- }
210
- });
211
174
  // =============================================================================
212
175
  // Helper Functions
213
176
  // =============================================================================
@@ -473,15 +436,6 @@ function main() {
473
436
  }
474
437
  fs.writeFileSync(logPath, lines.join('\n') + '\n');
475
438
  }
476
- /**
477
- * Telemetry for a completed full index (see TELEMETRY.md). The bounded flush
478
- * keeps init/index responsive (these commands just ran for seconds anyway)
479
- * while delivering the event promptly.
480
- */
481
- async function recordIndexTelemetry(cg, result) {
482
- (0, telemetry_1.recordIndexEvent)(cg, result);
483
- await (0, telemetry_1.getTelemetry)().flushNow();
484
- }
485
439
  // =============================================================================
486
440
  // Commands
487
441
  // =============================================================================
@@ -550,7 +504,6 @@ function main() {
550
504
  supervision.stop();
551
505
  }
552
506
  printIndexResult(clack, result, projectPath);
553
- await recordIndexTelemetry(cg, result);
554
507
  try {
555
508
  const { offerWatchFallback } = await Promise.resolve().then(() => __importStar(require('../installer')));
556
509
  await offerWatchFallback(clack, projectPath);
@@ -604,13 +557,6 @@ function main() {
604
557
  }
605
558
  catch { /* non-fatal */ }
606
559
  success(`Removed HomeGraph from ${projectPath}`);
607
- // Churn signal — and flush now, since after an uninit there may be no
608
- // "next run" to deliver it.
609
- try {
610
- (0, telemetry_1.getTelemetry)().recordLifecycle('uninstall', {});
611
- await (0, telemetry_1.getTelemetry)().flushNow();
612
- }
613
- catch { /* non-fatal */ }
614
560
  }
615
561
  catch (err) {
616
562
  error(`Failed to uninitialize: ${err instanceof Error ? err.message : String(err)}`);
@@ -682,7 +628,6 @@ function main() {
682
628
  await progress.stop();
683
629
  }
684
630
  printIndexResult(clack, result, projectPath);
685
- await recordIndexTelemetry(cg, result);
686
631
  if (!result.success) {
687
632
  process.exit(1);
688
633
  }
@@ -715,7 +660,7 @@ function main() {
715
660
  process.exit(1);
716
661
  }
717
662
  const { default: HomeGraph } = await loadHomeGraph();
718
- const cg = await HomeGraph.open(projectPath);
663
+ const cg = await HomeGraph.open(projectPath, { sources: 'both' });
719
664
  if (options.quiet) {
720
665
  await cg.sync();
721
666
  cg.destroy();
@@ -809,6 +754,7 @@ function main() {
809
754
  nodeCount: stats.nodeCount,
810
755
  edgeCount: stats.edgeCount,
811
756
  dbSizeBytes: stats.dbSizeBytes,
757
+ walSizeBytes: stats.walSizeBytes,
812
758
  backend,
813
759
  journalMode,
814
760
  nodesByKind: stats.nodesByKind,
@@ -824,6 +770,7 @@ function main() {
824
770
  ohosApi: ohosApi
825
771
  ? { version: ohosApi.version, packageName: ohosApi.packageName, dbPath: ohosApi.dbPath }
826
772
  : null,
773
+ graphSources: cg.getGraphSources(),
827
774
  index: {
828
775
  state: indexState,
829
776
  builtWithVersion: buildInfo.version,
@@ -848,6 +795,19 @@ function main() {
848
795
  console.log(` Nodes: ${formatNumber(stats.nodeCount)}`);
849
796
  console.log(` Edges: ${formatNumber(stats.edgeCount)}`);
850
797
  console.log(` DB Size: ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`);
798
+ // Surface the WAL sidecar (#1431): a WAL that dwarfs the DB at rest is
799
+ // the killed-session leak — invisible before this line, it only showed
800
+ // up as a mysteriously full disk. open() above already kicked off the
801
+ // automatic heal for the oversized case.
802
+ if (stats.walSizeBytes > 0) {
803
+ const { WAL_HEAL_THRESHOLD_BYTES } = await Promise.resolve().then(() => __importStar(require('../db/index')));
804
+ const oversized = stats.walSizeBytes > Math.max(WAL_HEAL_THRESHOLD_BYTES, stats.dbSizeBytes);
805
+ const walLabel = `${(stats.walSizeBytes / 1024 / 1024).toFixed(2)} MB`;
806
+ console.log(` WAL Size: ${oversized ? chalk.yellow(walLabel) : walLabel}`);
807
+ if (oversized) {
808
+ warn('The write-ahead log is larger than the database — killed sessions left it behind. It is reclaimed automatically on open; if it persists across runs, another live HomeGraph process is holding it.');
809
+ }
810
+ }
851
811
  // Prefer node:sqlite → better-sqlite3; wasm is last-resort (no WAL).
852
812
  const backendLabel = backend === 'node-sqlite' ? chalk.green('node-sqlite')
853
813
  : backend === 'native' ? chalk.green('native (better-sqlite3)')
@@ -857,6 +817,7 @@ function main() {
857
817
  ? chalk.green('wal')
858
818
  : chalk.yellow(`${journalMode || 'unknown'} ${(0, glyphs_1.getGlyphs)().dash} WAL inactive; reads can block on writes`);
859
819
  console.log(` Journal: ${journalLabel}`);
820
+ console.log(` Sources: ${cg.getGraphSources()} (project index / OHOS SDK API)`);
860
821
  console.log();
861
822
  // Node breakdown
862
823
  console.log(chalk.bold('Nodes by Kind:'));
@@ -1441,75 +1402,102 @@ function main() {
1441
1402
  });
1442
1403
  });
1443
1404
  /**
1444
- * homegraph serve
1405
+ * homegraph serve / homegraph serve mcp
1406
+ *
1407
+ * Preferred: `homegraph serve mcp`
1408
+ * Legacy (still supported): `homegraph serve --mcp`
1409
+ *
1410
+ * Hidden from `--help`: this is the stdio entry point an AI agent launches
1411
+ * for itself (the installer wires it into every agent's MCP config), not a
1412
+ * command a human runs. It still works when invoked — hiding only removes it
1413
+ * from the listing. See the interactive-TTY guard in `runServeMcp`, which
1414
+ * explains this to anyone who runs it by hand.
1445
1415
  */
1446
- program
1447
- // Hidden from `--help`: this is the stdio entry point an AI agent launches
1448
- // for itself (the installer wires `args: ['serve','--mcp']` into every
1449
- // agent's MCP config), not a command a human runs. It still works when
1450
- // invoked — hiding only removes it from the listing. See the interactive-TTY
1451
- // guard below, which explains this to anyone who runs it by hand.
1452
- .command('serve', { hidden: true })
1453
- .description('Start HomeGraph as an MCP server for AI assistants')
1454
- .option('-p, --path <path>', 'Project path (optional for MCP mode, uses rootUri from client)')
1455
- .option('--mcp', 'Run as MCP server (stdio transport)')
1456
- .option('--no-watch', 'Disable the file watcher (no auto-sync; useful on slow filesystems like WSL2 /mnt drives)')
1457
- .action(async (options) => {
1416
+ async function runServeMcp(options) {
1458
1417
  const projectPath = options.path ? resolveProjectPath(options.path) : undefined;
1459
1418
  // Commander sets watch=false when --no-watch is passed. Route it through
1460
1419
  // the same env-var chokepoint the watcher and MCP server already honor.
1461
1420
  if (options.watch === false) {
1462
1421
  process.env.HOMEGRAPH_NO_WATCH = '1';
1463
1422
  }
1423
+ // Spec 0005: resolve --sources over HOMEGRAPH_SOURCES, then stamp env so
1424
+ // detached daemons and in-process opens share the same mode.
1425
+ const { resolveGraphSources, applyGraphSourcesToEnv, GRAPH_SOURCES_MODES } = await Promise.resolve().then(() => __importStar(require('../graph-sources')));
1464
1426
  try {
1465
- if (options.mcp) {
1466
- // `serve --mcp` is the stdio MCP server an AI agent launches for itself,
1467
- // not a command to run by hand. A human in a terminal would otherwise
1468
- // see it hang waiting for JSON-RPC on stdin, which reads as broken. If
1469
- // stdin is an interactive TTY, explain instead of hanging. The agent's
1470
- // pipe and the detached daemon both have a non-TTY stdin, so this only
1471
- // ever fires for a person who typed it.
1472
- if (process.stdin.isTTY && !process.env.HOMEGRAPH_DAEMON_INTERNAL) {
1473
- console.error(chalk.bold('\nHomeGraph MCP server\n'));
1474
- console.error("This is the MCP server your AI agent (Claude Code, Cursor, Codex, opencode, …)");
1475
- console.error("starts automatically you don't run it yourself.");
1476
- console.error(`\nIt's already wired up by ${chalk.cyan('homegraph install')}. To check on things:`);
1477
- console.error(` ${chalk.cyan('homegraph status')} ${chalk.dim('— is this project indexed and healthy?')}`);
1478
- console.error(` ${chalk.cyan('homegraph daemon')} ${chalk.dim('— list or stop background MCP servers')}`);
1479
- console.error(chalk.dim('\n(Running it directly only does something when an MCP client drives it over stdin.)'));
1480
- return;
1481
- }
1482
- // Start MCP server - it handles initialization lazily based on rootUri from client
1483
- const { MCPServer } = await Promise.resolve().then(() => __importStar(require('../mcp/index')));
1484
- const server = new MCPServer(projectPath);
1485
- await server.start();
1486
- // Server will run until terminated
1487
- }
1488
- else {
1489
- // Default: show info about MCP mode.
1490
- // Use stderr so stdout stays clean for any piped/stdio usage.
1491
- console.error(chalk.bold('\nHomeGraph MCP Server\n'));
1492
- console.error(chalk.blue((0, glyphs_1.getGlyphs)().info) + ' Use --mcp flag to start the MCP server');
1493
- console.error('\nTo use with Claude Code, add to your MCP configuration:');
1494
- console.error(chalk.dim(`
1427
+ const mode = resolveGraphSources(options.sources);
1428
+ applyGraphSourcesToEnv(mode);
1429
+ }
1430
+ catch (err) {
1431
+ error(err instanceof Error ? err.message : String(err));
1432
+ error(`Valid values: ${GRAPH_SOURCES_MODES.join(', ')}`);
1433
+ process.exit(1);
1434
+ }
1435
+ // The stdio MCP server an AI agent launches for itself — not a command to
1436
+ // run by hand. A human in a terminal would otherwise see it hang waiting
1437
+ // for JSON-RPC on stdin. The agent's pipe and the detached daemon both
1438
+ // have a non-TTY stdin, so this only ever fires for a person who typed it.
1439
+ if (process.stdin.isTTY && !process.env.HOMEGRAPH_DAEMON_INTERNAL) {
1440
+ console.error(chalk.bold('\nHomeGraph MCP server\n'));
1441
+ console.error("This is the MCP server your AI agent (Claude Code, Cursor, Codex, opencode, )");
1442
+ console.error("starts automatically — you don't run it yourself.");
1443
+ console.error(`\nIt's already wired up by ${chalk.cyan('homegraph install')}. To check on things:`);
1444
+ console.error(` ${chalk.cyan('homegraph status')} ${chalk.dim('— is this project indexed and healthy?')}`);
1445
+ console.error(` ${chalk.cyan('homegraph daemon')} ${chalk.dim('— list or stop background MCP servers')}`);
1446
+ console.error(chalk.dim('\n(Running it directly only does something when an MCP client drives it over stdin.)'));
1447
+ return;
1448
+ }
1449
+ const { MCPServer } = await Promise.resolve().then(() => __importStar(require('../mcp/index')));
1450
+ const server = new MCPServer(projectPath);
1451
+ await server.start();
1452
+ }
1453
+ function printServeUsage() {
1454
+ // Use stderr so stdout stays clean for any piped/stdio usage.
1455
+ console.error(chalk.bold('\nHomeGraph MCP Server\n'));
1456
+ console.error(chalk.blue((0, glyphs_1.getGlyphs)().info) + ' Start with: ' + chalk.cyan('homegraph serve mcp'));
1457
+ console.error(chalk.dim(' (legacy alias also works: homegraph serve --mcp)'));
1458
+ console.error('\nTo use with Claude Code, add to your MCP configuration:');
1459
+ console.error(chalk.dim(`
1495
1460
  {
1496
1461
  "mcpServers": {
1497
1462
  "homegraph": {
1498
1463
  "command": "homegraph",
1499
- "args": ["serve", "--mcp"]
1464
+ "args": ["serve", "mcp"]
1500
1465
  }
1501
1466
  }
1502
1467
  }
1503
1468
  `));
1504
- console.error('Available tools:');
1505
- console.error(chalk.cyan(' homegraph_explore') + ' - Primary: source of the relevant symbols for any question');
1506
- console.error(chalk.cyan(' homegraph_search') + ' - Search for code symbols');
1507
- console.error(chalk.cyan(' homegraph_callers') + ' - Find callers of a symbol');
1508
- console.error(chalk.cyan(' homegraph_callees') + ' - Find what a symbol calls');
1509
- console.error(chalk.cyan(' homegraph_impact') + ' - Analyze impact of changes');
1510
- console.error(chalk.cyan(' homegraph_node') + ' - Get symbol details');
1511
- console.error(chalk.cyan(' homegraph_files') + ' - Get project file structure');
1512
- console.error(chalk.cyan(' homegraph_status') + ' - Get index status');
1469
+ console.error('Optional: --sources both|project|sdk|none (or HOMEGRAPH_SOURCES) to limit project vs OHOS SDK graphs.');
1470
+ console.error('Available tools:');
1471
+ console.error(chalk.cyan(' homegraph_explore') + ' - Primary: source of the relevant symbols for any question');
1472
+ console.error(chalk.cyan(' homegraph_search') + ' - Search for code symbols');
1473
+ console.error(chalk.cyan(' homegraph_callers') + ' - Find callers of a symbol');
1474
+ console.error(chalk.cyan(' homegraph_callees') + ' - Find what a symbol calls');
1475
+ console.error(chalk.cyan(' homegraph_impact') + ' - Analyze impact of changes');
1476
+ console.error(chalk.cyan(' homegraph_node') + ' - Get symbol details');
1477
+ console.error(chalk.cyan(' homegraph_files') + ' - Get project file structure');
1478
+ console.error(chalk.cyan(' homegraph_status') + ' - Get index status');
1479
+ }
1480
+ // IMPORTANT: keep `mcp` as an argument (not a nested Commander subcommand).
1481
+ // Nested `serve.command('mcp')` + a parent `-p/--path` made Commander 14 drop
1482
+ // `--path` on `serve mcp --path <repo>` — the daemon then keyed off cwd, so
1483
+ // prewarm/eval looked "timed out" while a useless daemon bound the wrong root.
1484
+ // `spawnDetachedDaemon` also invokes `serve mcp --path <root>`; that path must
1485
+ // stick. Legacy `serve --mcp` stays supported.
1486
+ program
1487
+ .command('serve', { hidden: true })
1488
+ .description('Start HomeGraph protocol servers for AI assistants')
1489
+ .argument('[mode]', 'Run as MCP server (stdio transport)', undefined)
1490
+ .option('-p, --path <path>', 'Project path (optional for MCP mode, uses rootUri from client)')
1491
+ .option('--mcp', 'Legacy alias for `serve mcp` (stdio MCP server)')
1492
+ .option('--no-watch', 'Disable the file watcher (no auto-sync; useful on slow filesystems like WSL2 /mnt drives)')
1493
+ .option('--sources <mode>', 'Graph sources for MCP queries: both|project|sdk|none (default both; env HOMEGRAPH_SOURCES)')
1494
+ .action(async (mode, options) => {
1495
+ try {
1496
+ if (mode === 'mcp' || options.mcp) {
1497
+ await runServeMcp(options);
1498
+ }
1499
+ else {
1500
+ printServeUsage();
1513
1501
  }
1514
1502
  }
1515
1503
  catch (err) {
@@ -2971,45 +2959,6 @@ function main() {
2971
2959
  catch { /* best effort */ }
2972
2960
  }
2973
2961
  });
2974
- /**
2975
- * homegraph telemetry [on|off|status]
2976
- */
2977
- program
2978
- .command('telemetry [action]')
2979
- .description('Show or change anonymous usage telemetry (status, on, off)')
2980
- .action((action) => {
2981
- const t = (0, telemetry_1.getTelemetry)();
2982
- if (action === 'on' || action === 'off') {
2983
- t.setEnabled(action === 'on', 'cli');
2984
- if (action === 'on') {
2985
- success('Telemetry enabled — anonymous usage stats only (no code, paths, or names).');
2986
- }
2987
- else {
2988
- success('Telemetry disabled. Buffered, unsent data was deleted.');
2989
- }
2990
- const effective = t.getStatus();
2991
- if (effective.decidedBy === 'DO_NOT_TRACK' || effective.decidedBy === 'HOMEGRAPH_TELEMETRY') {
2992
- warn(`The ${effective.decidedBy} environment variable overrides this choice — ` +
2993
- `effective state right now: ${effective.enabled ? 'enabled' : 'disabled'}.`);
2994
- }
2995
- return;
2996
- }
2997
- if (action !== undefined && action !== 'status') {
2998
- error(`Unknown action: ${action} (expected status, on, or off)`);
2999
- process.exit(1);
3000
- }
3001
- const s = t.getStatus();
3002
- const decidedBy = {
3003
- DO_NOT_TRACK: 'DO_NOT_TRACK environment variable',
3004
- HOMEGRAPH_TELEMETRY: 'HOMEGRAPH_TELEMETRY environment variable',
3005
- config: 'your saved choice',
3006
- default: 'default',
3007
- };
3008
- console.log(`\nTelemetry: ${s.enabled ? chalk.green('enabled') : chalk.yellow('disabled')} ${chalk.dim(`(${decidedBy[s.decidedBy]})`)}`);
3009
- console.log(`Machine ID: ${s.machineId ?? chalk.dim('(random UUID, created on first use)')}`);
3010
- console.log(`Config: ${s.configPath}`);
3011
- console.log(chalk.dim(`\nExactly what is collected (and never collected): ${telemetry_1.TELEMETRY_DOCS}\n`));
3012
- });
3013
2962
  /**
3014
2963
  * homegraph index-api <input> [version]
3015
2964
  * Build a standalone OHOS API database from command-line-tools SDK.
@@ -3070,9 +3019,9 @@ function main() {
3070
3019
  /**
3071
3020
  * homegraph upgrade [version]
3072
3021
  *
3073
- * Self-update, however HomeGraph was installed (bundle via install.sh/.ps1,
3074
- * npm-global, npx, or a source checkout). See ../upgrade for the detection and
3075
- * per-method upgrade logic.
3022
+ * Self-update for npm-global / npx / source installs. A leftover standalone
3023
+ * bundle install is detected and refused (guide the user to `npm i -g`).
3024
+ * See ../upgrade for detection and per-method upgrade logic.
3076
3025
  */
3077
3026
  program
3078
3027
  .command('upgrade [version]')