@colbymchenry/codegraph-darwin-x64 1.0.0 → 1.1.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 (175) hide show
  1. package/lib/dist/bin/codegraph.js +258 -17
  2. package/lib/dist/bin/codegraph.js.map +1 -1
  3. package/lib/dist/bin/fatal-handler.d.ts +20 -0
  4. package/lib/dist/bin/fatal-handler.d.ts.map +1 -0
  5. package/lib/dist/bin/fatal-handler.js +118 -0
  6. package/lib/dist/bin/fatal-handler.js.map +1 -0
  7. package/lib/dist/db/index.d.ts +22 -1
  8. package/lib/dist/db/index.d.ts.map +1 -1
  9. package/lib/dist/db/index.js +46 -1
  10. package/lib/dist/db/index.js.map +1 -1
  11. package/lib/dist/db/queries.d.ts +14 -0
  12. package/lib/dist/db/queries.d.ts.map +1 -1
  13. package/lib/dist/db/queries.js +25 -0
  14. package/lib/dist/db/queries.js.map +1 -1
  15. package/lib/dist/directory.d.ts +58 -0
  16. package/lib/dist/directory.d.ts.map +1 -1
  17. package/lib/dist/directory.js +165 -0
  18. package/lib/dist/directory.js.map +1 -1
  19. package/lib/dist/extraction/grammars.d.ts +11 -3
  20. package/lib/dist/extraction/grammars.d.ts.map +1 -1
  21. package/lib/dist/extraction/grammars.js +14 -5
  22. package/lib/dist/extraction/grammars.js.map +1 -1
  23. package/lib/dist/extraction/index.d.ts.map +1 -1
  24. package/lib/dist/extraction/index.js +202 -32
  25. package/lib/dist/extraction/index.js.map +1 -1
  26. package/lib/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  27. package/lib/dist/extraction/languages/c-cpp.js +47 -2
  28. package/lib/dist/extraction/languages/c-cpp.js.map +1 -1
  29. package/lib/dist/extraction/languages/csharp.d.ts.map +1 -1
  30. package/lib/dist/extraction/languages/csharp.js +20 -0
  31. package/lib/dist/extraction/languages/csharp.js.map +1 -1
  32. package/lib/dist/extraction/languages/dart.d.ts.map +1 -1
  33. package/lib/dist/extraction/languages/dart.js +22 -0
  34. package/lib/dist/extraction/languages/dart.js.map +1 -1
  35. package/lib/dist/extraction/languages/java.d.ts.map +1 -1
  36. package/lib/dist/extraction/languages/java.js +213 -9
  37. package/lib/dist/extraction/languages/java.js.map +1 -1
  38. package/lib/dist/extraction/languages/kotlin.d.ts.map +1 -1
  39. package/lib/dist/extraction/languages/kotlin.js +51 -0
  40. package/lib/dist/extraction/languages/kotlin.js.map +1 -1
  41. package/lib/dist/extraction/languages/scala.d.ts.map +1 -1
  42. package/lib/dist/extraction/languages/scala.js +19 -9
  43. package/lib/dist/extraction/languages/scala.js.map +1 -1
  44. package/lib/dist/extraction/parse-worker.js +4 -1
  45. package/lib/dist/extraction/parse-worker.js.map +1 -1
  46. package/lib/dist/extraction/tree-sitter-types.d.ts +13 -0
  47. package/lib/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  48. package/lib/dist/extraction/tree-sitter.d.ts +119 -0
  49. package/lib/dist/extraction/tree-sitter.d.ts.map +1 -1
  50. package/lib/dist/extraction/tree-sitter.js +890 -11
  51. package/lib/dist/extraction/tree-sitter.js.map +1 -1
  52. package/lib/dist/index.d.ts +33 -0
  53. package/lib/dist/index.d.ts.map +1 -1
  54. package/lib/dist/index.js +68 -7
  55. package/lib/dist/index.js.map +1 -1
  56. package/lib/dist/installer/index.d.ts.map +1 -1
  57. package/lib/dist/installer/index.js +33 -56
  58. package/lib/dist/installer/index.js.map +1 -1
  59. package/lib/dist/installer/instructions-template.d.ts +3 -3
  60. package/lib/dist/installer/instructions-template.d.ts.map +1 -1
  61. package/lib/dist/installer/instructions-template.js +4 -4
  62. package/lib/dist/installer/targets/claude.d.ts +18 -12
  63. package/lib/dist/installer/targets/claude.d.ts.map +1 -1
  64. package/lib/dist/installer/targets/claude.js +78 -6
  65. package/lib/dist/installer/targets/claude.js.map +1 -1
  66. package/lib/dist/installer/targets/shared.d.ts +12 -2
  67. package/lib/dist/installer/targets/shared.d.ts.map +1 -1
  68. package/lib/dist/installer/targets/shared.js +13 -12
  69. package/lib/dist/installer/targets/shared.js.map +1 -1
  70. package/lib/dist/installer/targets/types.d.ts +7 -0
  71. package/lib/dist/installer/targets/types.d.ts.map +1 -1
  72. package/lib/dist/mcp/daemon-manager.d.ts +42 -0
  73. package/lib/dist/mcp/daemon-manager.d.ts.map +1 -0
  74. package/lib/dist/mcp/daemon-manager.js +129 -0
  75. package/lib/dist/mcp/daemon-manager.js.map +1 -0
  76. package/lib/dist/mcp/daemon-registry.d.ts +47 -0
  77. package/lib/dist/mcp/daemon-registry.d.ts.map +1 -0
  78. package/lib/dist/mcp/daemon-registry.js +229 -0
  79. package/lib/dist/mcp/daemon-registry.js.map +1 -0
  80. package/lib/dist/mcp/daemon.d.ts.map +1 -1
  81. package/lib/dist/mcp/daemon.js +5 -0
  82. package/lib/dist/mcp/daemon.js.map +1 -1
  83. package/lib/dist/mcp/engine.d.ts.map +1 -1
  84. package/lib/dist/mcp/engine.js +8 -0
  85. package/lib/dist/mcp/engine.js.map +1 -1
  86. package/lib/dist/mcp/index.d.ts +1 -0
  87. package/lib/dist/mcp/index.d.ts.map +1 -1
  88. package/lib/dist/mcp/index.js +13 -0
  89. package/lib/dist/mcp/index.js.map +1 -1
  90. package/lib/dist/mcp/liveness-watchdog.d.ts +18 -0
  91. package/lib/dist/mcp/liveness-watchdog.d.ts.map +1 -0
  92. package/lib/dist/mcp/liveness-watchdog.js +207 -0
  93. package/lib/dist/mcp/liveness-watchdog.js.map +1 -0
  94. package/lib/dist/mcp/server-instructions.d.ts +18 -14
  95. package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
  96. package/lib/dist/mcp/server-instructions.js +57 -52
  97. package/lib/dist/mcp/server-instructions.js.map +1 -1
  98. package/lib/dist/mcp/session.d.ts.map +1 -1
  99. package/lib/dist/mcp/session.js +23 -18
  100. package/lib/dist/mcp/session.js.map +1 -1
  101. package/lib/dist/mcp/tools.d.ts +51 -1
  102. package/lib/dist/mcp/tools.d.ts.map +1 -1
  103. package/lib/dist/mcp/tools.js +585 -151
  104. package/lib/dist/mcp/tools.js.map +1 -1
  105. package/lib/dist/project-config.d.ts +19 -0
  106. package/lib/dist/project-config.d.ts.map +1 -0
  107. package/lib/dist/project-config.js +180 -0
  108. package/lib/dist/project-config.js.map +1 -0
  109. package/lib/dist/reasoning/config.d.ts +45 -0
  110. package/lib/dist/reasoning/config.d.ts.map +1 -0
  111. package/lib/dist/reasoning/config.js +171 -0
  112. package/lib/dist/reasoning/config.js.map +1 -0
  113. package/lib/dist/reasoning/credentials.d.ts +5 -0
  114. package/lib/dist/reasoning/credentials.d.ts.map +1 -0
  115. package/lib/dist/reasoning/credentials.js +83 -0
  116. package/lib/dist/reasoning/credentials.js.map +1 -0
  117. package/lib/dist/reasoning/login.d.ts +21 -0
  118. package/lib/dist/reasoning/login.d.ts.map +1 -0
  119. package/lib/dist/reasoning/login.js +85 -0
  120. package/lib/dist/reasoning/login.js.map +1 -0
  121. package/lib/dist/reasoning/reasoner.d.ts +43 -0
  122. package/lib/dist/reasoning/reasoner.d.ts.map +1 -0
  123. package/lib/dist/reasoning/reasoner.js +308 -0
  124. package/lib/dist/reasoning/reasoner.js.map +1 -0
  125. package/lib/dist/resolution/c-fnptr-synthesizer.d.ts +33 -0
  126. package/lib/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -0
  127. package/lib/dist/resolution/c-fnptr-synthesizer.js +352 -0
  128. package/lib/dist/resolution/c-fnptr-synthesizer.js.map +1 -0
  129. package/lib/dist/resolution/callback-synthesizer.d.ts +6 -1
  130. package/lib/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  131. package/lib/dist/resolution/callback-synthesizer.js +1109 -1
  132. package/lib/dist/resolution/callback-synthesizer.js.map +1 -1
  133. package/lib/dist/resolution/frameworks/goframe.d.ts +41 -0
  134. package/lib/dist/resolution/frameworks/goframe.d.ts.map +1 -0
  135. package/lib/dist/resolution/frameworks/goframe.js +112 -0
  136. package/lib/dist/resolution/frameworks/goframe.js.map +1 -0
  137. package/lib/dist/resolution/frameworks/index.d.ts +1 -0
  138. package/lib/dist/resolution/frameworks/index.d.ts.map +1 -1
  139. package/lib/dist/resolution/frameworks/index.js +5 -1
  140. package/lib/dist/resolution/frameworks/index.js.map +1 -1
  141. package/lib/dist/resolution/frameworks/react.d.ts.map +1 -1
  142. package/lib/dist/resolution/frameworks/react.js +17 -60
  143. package/lib/dist/resolution/frameworks/react.js.map +1 -1
  144. package/lib/dist/resolution/goframe-synthesizer.d.ts +28 -0
  145. package/lib/dist/resolution/goframe-synthesizer.d.ts.map +1 -0
  146. package/lib/dist/resolution/goframe-synthesizer.js +158 -0
  147. package/lib/dist/resolution/goframe-synthesizer.js.map +1 -0
  148. package/lib/dist/resolution/import-resolver.d.ts.map +1 -1
  149. package/lib/dist/resolution/import-resolver.js +56 -0
  150. package/lib/dist/resolution/import-resolver.js.map +1 -1
  151. package/lib/dist/resolution/name-matcher.d.ts.map +1 -1
  152. package/lib/dist/resolution/name-matcher.js +48 -8
  153. package/lib/dist/resolution/name-matcher.js.map +1 -1
  154. package/lib/dist/resolution/strip-comments.d.ts +1 -1
  155. package/lib/dist/resolution/strip-comments.d.ts.map +1 -1
  156. package/lib/dist/resolution/strip-comments.js +2 -0
  157. package/lib/dist/resolution/strip-comments.js.map +1 -1
  158. package/lib/dist/sync/watcher.d.ts +68 -1
  159. package/lib/dist/sync/watcher.d.ts.map +1 -1
  160. package/lib/dist/sync/watcher.js +212 -14
  161. package/lib/dist/sync/watcher.js.map +1 -1
  162. package/lib/dist/telemetry/index.d.ts +0 -3
  163. package/lib/dist/telemetry/index.d.ts.map +1 -1
  164. package/lib/dist/telemetry/index.js +4 -7
  165. package/lib/dist/telemetry/index.js.map +1 -1
  166. package/lib/dist/upgrade/index.d.ts.map +1 -1
  167. package/lib/dist/upgrade/index.js +40 -4
  168. package/lib/dist/upgrade/index.js.map +1 -1
  169. package/lib/dist/utils.d.ts +14 -1
  170. package/lib/dist/utils.d.ts.map +1 -1
  171. package/lib/dist/utils.js +20 -2
  172. package/lib/dist/utils.js.map +1 -1
  173. package/lib/node_modules/.package-lock.json +1 -1
  174. package/lib/package.json +2 -2
  175. package/package.json +1 -1
@@ -65,6 +65,7 @@ const worktree_1 = require("../sync/worktree");
65
65
  const shimmer_progress_1 = require("../ui/shimmer-progress");
66
66
  const glyphs_1 = require("../ui/glyphs");
67
67
  const node_version_check_1 = require("./node-version-check");
68
+ const fatal_handler_1 = require("./fatal-handler");
68
69
  const wasm_runtime_flags_1 = require("../extraction/wasm-runtime-flags");
69
70
  const extraction_version_1 = require("../extraction/extraction-version");
70
71
  const telemetry_1 = require("../telemetry");
@@ -118,6 +119,12 @@ if (nodeMajor < node_version_check_1.MIN_NODE_MAJOR) {
118
119
  // passes the flag. Must run before any grammar (in the parse worker, which
119
120
  // inherits this process's flags) is compiled. See ../extraction/wasm-runtime-flags.
120
121
  (0, wasm_runtime_flags_1.relaunchWithWasmRuntimeFlagsIfNeeded)(__filename);
122
+ // Last-resort fatal handlers: log a bounded line and exit non-zero. A fault
123
+ // that reaches here escaped every boundary, so the process is in an undefined
124
+ // state — keeping it alive is what let the detached MCP daemon orphan and pin a
125
+ // CPU core with no recovery (#799, #850). Installed before the command branch
126
+ // so it also covers a synchronous throw during startup. See ./fatal-handler.
127
+ (0, fatal_handler_1.installFatalHandlers)();
121
128
  // Check if running with no arguments - run installer
122
129
  if (process.argv.length === 2) {
123
130
  Promise.resolve().then(() => __importStar(require('../installer'))).then(({ runInstaller }) => runInstaller()).catch((err) => {
@@ -129,16 +136,21 @@ else {
129
136
  // Normal CLI flow
130
137
  main();
131
138
  }
132
- process.on('uncaughtException', (error) => {
133
- console.error('[CodeGraph] Uncaught exception:', error);
134
- });
135
- process.on('unhandledRejection', (reason) => {
136
- console.error('[CodeGraph] Unhandled rejection:', reason);
137
- });
138
139
  function main() {
139
140
  const program = new commander_1.Command();
140
141
  // Version from package.json
141
142
  const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf-8'));
143
+ // Make the version trivial to reach. commander's `.version()` (below) wires up
144
+ // `--version` and `-V`; intercept the spellings it can't — lowercase `-v` and
145
+ // single-dash `-version` — before any parsing. (commander's version short flag
146
+ // is the capital `-V`, and its parser rejects a multi-character single-dash
147
+ // flag.) The bare `codegraph version` subcommand is registered further down so
148
+ // the affordance also shows up in `codegraph --help`.
149
+ const firstArg = process.argv[2];
150
+ if (firstArg === '-v' || firstArg === '-version') {
151
+ console.log(packageJson.version);
152
+ return;
153
+ }
142
154
  // =============================================================================
143
155
  // ANSI Color Helpers (avoid chalk ESM issues)
144
156
  // =============================================================================
@@ -432,12 +444,24 @@ function main() {
432
444
  .command('init [path]')
433
445
  .description('Initialize CodeGraph in a project directory and build the initial index')
434
446
  .option('-i, --index', 'Deprecated: indexing now runs by default; flag accepted for backward compatibility')
447
+ .option('-f, --force', 'Initialize even if the path looks like your home directory or a filesystem root')
435
448
  .option('-v, --verbose', 'Show detailed worker lifecycle and memory info')
436
449
  .action(async (pathArg, options) => {
437
450
  const projectPath = path.resolve(pathArg || process.cwd());
438
451
  const clack = await importESM('@clack/prompts');
439
452
  clack.intro('Initializing CodeGraph');
440
453
  try {
454
+ // Refuse to index your home directory / a filesystem root — it pulls in
455
+ // caches, other projects, and your whole tree (a multi-GB index + watcher
456
+ // churn, and on pre-1.0 macOS a machine-crashing fd blowup, #845).
457
+ const unsafe = (0, directory_1.unsafeIndexRootReason)(projectPath);
458
+ if (unsafe && !options.force) {
459
+ clack.log.error(`Refusing to initialize in ${projectPath} — it looks like ${unsafe}.`);
460
+ clack.log.info('Run this inside a specific project directory, or pass --force if you really mean to index everything under it.');
461
+ clack.outro('');
462
+ process.exitCode = 1;
463
+ return;
464
+ }
441
465
  if ((0, directory_1.isInitialized)(projectPath)) {
442
466
  clack.log.warn(`Already initialized in ${projectPath}`);
443
467
  clack.log.info('Use "codegraph index" to re-index or "codegraph sync" to update');
@@ -543,13 +567,20 @@ function main() {
543
567
  */
544
568
  program
545
569
  .command('index [path]')
546
- .description('Index all files in the project')
547
- .option('-f, --force', 'Force full re-index even if already indexed')
570
+ .description('Rebuild the full index from scratch (same result as a fresh init)')
571
+ .option('-f, --force', 'Index even if the path looks like your home directory or a filesystem root')
548
572
  .option('-q, --quiet', 'Suppress progress output')
549
573
  .option('-v, --verbose', 'Show detailed worker lifecycle and memory info')
550
574
  .action(async (pathArg, options) => {
551
575
  const projectPath = resolveProjectPath(pathArg);
552
576
  try {
577
+ // Don't (re)index your home directory / a filesystem root (#845). --force
578
+ // doubles as the override.
579
+ const unsafe = (0, directory_1.unsafeIndexRootReason)(projectPath);
580
+ if (unsafe && !options.force) {
581
+ error(`Refusing to index ${projectPath} — it looks like ${unsafe}. Pass --force to override.`);
582
+ process.exit(1);
583
+ }
553
584
  if (!(0, directory_1.isInitialized)(projectPath)) {
554
585
  error(`CodeGraph not initialized in ${projectPath}`);
555
586
  info('Run "codegraph init" first');
@@ -558,9 +589,9 @@ function main() {
558
589
  const { default: CodeGraph } = await loadCodeGraph();
559
590
  const cg = await CodeGraph.open(projectPath);
560
591
  if (options.quiet) {
561
- // Quiet mode: no UI, just run
562
- if (options.force)
563
- cg.clear();
592
+ // Quiet mode: no UI, just run. `index` is a full re-index, so clear the
593
+ // existing graph and rebuild from scratch (see the note below — #874).
594
+ cg.clear();
564
595
  const result = await cg.indexAll();
565
596
  if (!result.success)
566
597
  process.exit(1);
@@ -569,10 +600,12 @@ function main() {
569
600
  }
570
601
  const clack = await importESM('@clack/prompts');
571
602
  clack.intro('Indexing project');
572
- if (options.force) {
573
- cg.clear();
574
- clack.log.info('Cleared existing index');
575
- }
603
+ // `index` is a FULL re-index: clear the existing graph and rebuild it from
604
+ // scratch so the result is identical to a fresh `init`. Without the clear,
605
+ // indexAll() skips every unchanged file by its content hash and reports
606
+ // "0 nodes, 0 edges" against the already-populated graph — which reads as
607
+ // "index wiped my index" (#874). For fast incremental updates use `sync`.
608
+ cg.clear();
576
609
  let result;
577
610
  if (options.verbose) {
578
611
  result = await cg.indexAll({
@@ -800,7 +833,7 @@ function main() {
800
833
  if (reindexRecommended) {
801
834
  const builtWith = buildInfo.version ? `v${buildInfo.version.replace(/^v/, '')}` : 'an earlier version';
802
835
  warn(`Index was built by ${builtWith}; re-index to pick up this engine's improvements.`);
803
- info('Run "codegraph index -f" (full rebuild) or "codegraph sync"');
836
+ info('Run "codegraph index" (full rebuild) or "codegraph sync"');
804
837
  console.log();
805
838
  }
806
839
  cg.destroy();
@@ -913,6 +946,104 @@ function main() {
913
946
  process.exit(1);
914
947
  }
915
948
  });
949
+ /**
950
+ * codegraph prompt-hook (hidden)
951
+ *
952
+ * A Claude Code `UserPromptSubmit` hook entry point. Reads `{prompt, cwd}` JSON
953
+ * on stdin; for a structural/flow/impact prompt it runs `codegraph_explore` on
954
+ * the indexed project and prints the result to stdout, which Claude injects into
955
+ * the agent's context — so the agent's reflex grep/read has nothing left to find
956
+ * and reliably uses CodeGraph (the adoption problem). Installed by the installer
957
+ * into Claude's settings.json (opt-in, default-yes).
958
+ *
959
+ * LOAD-BEARING: this must NEVER break the user's prompt. Every failure path —
960
+ * kill-switch, non-structural prompt, no index, engine error — exits 0 with no
961
+ * output. The only effect is additive context when it can confidently provide it.
962
+ */
963
+ program
964
+ .command('prompt-hook', { hidden: true })
965
+ .description('Claude UserPromptSubmit hook: inject CodeGraph context for structural prompts (reads {prompt,cwd} JSON on stdin)')
966
+ .action(async () => {
967
+ try {
968
+ // Kill-switch: lets a user disable the nudge without uninstalling /
969
+ // editing settings.json (CI, low-power machines, personal preference).
970
+ if (process.env.CODEGRAPH_NO_PROMPT_HOOK === '1' || process.env.CODEGRAPH_PROMPT_HOOK === '0')
971
+ return;
972
+ if (process.stdin.isTTY)
973
+ return; // invoked by hand, no piped payload
974
+ const raw = await new Promise((resolve) => {
975
+ let data = '';
976
+ process.stdin.setEncoding('utf8');
977
+ process.stdin.on('data', (c) => { data += c; });
978
+ process.stdin.on('end', () => resolve(data));
979
+ process.stdin.on('error', () => resolve(data));
980
+ });
981
+ let input = {};
982
+ try {
983
+ input = JSON.parse(raw);
984
+ }
985
+ catch {
986
+ return;
987
+ }
988
+ const prompt = String(input.prompt || '');
989
+ // Gate: only structural / flow / impact / where-how prompts get context.
990
+ // A cheap regex keeps every other prompt ("fix this typo") a zero-cost
991
+ // no-op so we never add latency where there's no structural answer to give.
992
+ const STRUCTURAL = /\b(how|where|trace|flow|path|reach(?:es|ed)?|call(?:s|ed|er|ers|ee)?|depend|impact|affect|wired?|connect|implement|architect|structure|breaks?|what calls|why does)\b/i;
993
+ if (!prompt || !STRUCTURAL.test(prompt))
994
+ return;
995
+ // Decide what to inject, shaped by WHERE the index(es) are: the nearest
996
+ // indexed ancestor of cwd, or — when cwd is an un-indexed workspace root
997
+ // whose indexed project(s) live in sub-dirs (the monorepo case, #964) —
998
+ // the sub-project the prompt points at, plus a `projectPath` nudge for any
999
+ // others. Without the down-scan the hook injected nothing at a monorepo
1000
+ // root (it only walked up), so the validated adoption lever never fired
1001
+ // exactly where the agent most needs it.
1002
+ const plan = (0, directory_1.planFrontload)(String(input.cwd || process.cwd()), prompt);
1003
+ if (!plan.exploreRoot && plan.nudgeProjects.length === 0)
1004
+ return; // nothing reachable — the agent's normal tools apply
1005
+ // A "pass projectPath" line for indexed sub-projects we did NOT front-load.
1006
+ // Follow-up codegraph_explore calls against a sub-project (cwd isn't its
1007
+ // index root) need an explicit projectPath, so spell it out.
1008
+ const nudge = (projects, lead) => `${lead}\n${projects.map((p) => ` - projectPath: "${p}"`).join('\n')}\n`;
1009
+ if (plan.exploreRoot) {
1010
+ const { default: CodeGraph } = await loadCodeGraph();
1011
+ const cg = await CodeGraph.open(plan.exploreRoot);
1012
+ try {
1013
+ const { ToolHandler } = await Promise.resolve().then(() => __importStar(require('../mcp/tools')));
1014
+ const handler = new ToolHandler(cg);
1015
+ const result = await handler.execute('codegraph_explore', { query: prompt });
1016
+ const text = result.content[0]?.text ?? '';
1017
+ if (!result.isError && text.trim()) {
1018
+ // Cap the injection so a large-repo explore can't flood the prompt.
1019
+ const MAX = 16000;
1020
+ const body = text.length > MAX ? `${text.slice(0, MAX)}\n…(truncated; call codegraph_explore for the rest)` : text;
1021
+ // For a front-loaded SUB-project, a follow-up explore needs its path.
1022
+ const more = plan.viaSubScan
1023
+ ? `call codegraph_explore with projectPath: "${plan.exploreRoot}" for more`
1024
+ : 'call codegraph_explore for more';
1025
+ const others = plan.nudgeProjects.length
1026
+ ? `\n${nudge(plan.nudgeProjects, 'Other indexed projects in this workspace — pass projectPath to query them:')}`
1027
+ : '';
1028
+ process.stdout.write(`<codegraph_context note="Structural context from CodeGraph for this prompt — treat returned source as already read; ${more}.">\n${body}${others}\n</codegraph_context>\n`);
1029
+ }
1030
+ }
1031
+ finally {
1032
+ cg.destroy();
1033
+ }
1034
+ }
1035
+ else {
1036
+ // Several indexed sub-projects, none a clear match — don't guess; tell
1037
+ // the agent they exist and how to query one.
1038
+ process.stdout.write(`<codegraph_context note="CodeGraph is available for this workspace's indexed sub-projects — query one by passing projectPath to codegraph_explore.">\n` +
1039
+ nudge(plan.nudgeProjects, "This workspace's CodeGraph indexes live in sub-projects. To use CodeGraph, call codegraph_explore with the projectPath of the relevant one:") +
1040
+ `</codegraph_context>\n`);
1041
+ }
1042
+ }
1043
+ catch {
1044
+ // Degradable by contract: never surface an error to the prompt pipeline.
1045
+ }
1046
+ });
916
1047
  /**
917
1048
  * codegraph node <name>
918
1049
  *
@@ -1081,6 +1212,24 @@ function main() {
1081
1212
  process.exit(1);
1082
1213
  }
1083
1214
  });
1215
+ /**
1216
+ * Normalize a user-supplied file path to the project-relative, forward-slash
1217
+ * form CodeGraph stores in the index. Accepts an absolute path, a `./`-prefixed
1218
+ * path, or Windows back-slashes; an empty string when the input is blank. Used
1219
+ * by `codegraph affected` so `./src/x.ts`, `/abs/repo/src/x.ts`, and
1220
+ * `src/x.ts` all match the same indexed file. (#825)
1221
+ */
1222
+ function normalizeIndexPath(filePath, projectPath) {
1223
+ let f = filePath.trim();
1224
+ if (!f)
1225
+ return '';
1226
+ if (path.isAbsolute(f))
1227
+ f = path.relative(projectPath, f);
1228
+ // Collapse `.`/`..` segments, then force forward slashes and drop a leading
1229
+ // `./` (path.normalize already strips it on POSIX; explicit for Windows).
1230
+ f = path.normalize(f).replace(/\\/g, '/').replace(/^\.\//, '');
1231
+ return f;
1232
+ }
1084
1233
  /**
1085
1234
  * Convert glob pattern to regex
1086
1235
  */
@@ -1143,11 +1292,66 @@ function main() {
1143
1292
  };
1144
1293
  renderNode(root, '', true, 0);
1145
1294
  }
1295
+ /**
1296
+ * codegraph daemon — interactive manager for the background daemons. Arrow keys
1297
+ * to pick one (the current project's daemon floats to the top, auto-selected),
1298
+ * enter to stop it. Falls back to a plain list when output isn't a TTY.
1299
+ */
1300
+ program
1301
+ .command('daemon')
1302
+ .aliases(['daemons'])
1303
+ .description('Manage running CodeGraph background daemons — pick one and press enter to stop it')
1304
+ .action(async () => {
1305
+ const { listDaemons, stopDaemonAt, stopAllDaemons } = await Promise.resolve().then(() => __importStar(require('../mcp/daemon-registry')));
1306
+ const { runDaemonPicker } = await Promise.resolve().then(() => __importStar(require('../mcp/daemon-manager')));
1307
+ const daemons = listDaemons();
1308
+ if (daemons.length === 0) {
1309
+ info('No CodeGraph daemons running.');
1310
+ return;
1311
+ }
1312
+ // No TTY (piped / CI / non-interactive) — can't do arrow-key selection, so
1313
+ // just print what's running instead of crashing on a prompt with no input.
1314
+ if (!process.stdout.isTTY || !process.stdin.isTTY) {
1315
+ for (const d of daemons) {
1316
+ console.log(`pid ${d.pid} v${d.version} up ${formatDuration(Date.now() - d.startedAt)} ${d.root}`);
1317
+ }
1318
+ return;
1319
+ }
1320
+ // The current project's daemon floats to the top and is pre-selected.
1321
+ let cwdRoot = null;
1322
+ const found = (0, directory_1.findNearestCodeGraphRoot)(process.cwd());
1323
+ if (found) {
1324
+ try {
1325
+ cwdRoot = fs.realpathSync(found);
1326
+ }
1327
+ catch {
1328
+ cwdRoot = found;
1329
+ }
1330
+ }
1331
+ const clack = await importESM('@clack/prompts');
1332
+ clack.intro('CodeGraph daemons');
1333
+ await runDaemonPicker({
1334
+ list: listDaemons,
1335
+ stop: stopDaemonAt,
1336
+ stopAll: stopAllDaemons,
1337
+ cwdRoot,
1338
+ now: () => Date.now(),
1339
+ select: (opts) => clack.select(opts),
1340
+ isCancel: (v) => clack.isCancel(v),
1341
+ note: (m) => clack.log.success(m),
1342
+ done: (m) => clack.outro(m),
1343
+ });
1344
+ });
1146
1345
  /**
1147
1346
  * codegraph serve
1148
1347
  */
1149
1348
  program
1150
- .command('serve')
1349
+ // Hidden from `--help`: this is the stdio entry point an AI agent launches
1350
+ // for itself (the installer wires `args: ['serve','--mcp']` into every
1351
+ // agent's MCP config), not a command a human runs. It still works when
1352
+ // invoked — hiding only removes it from the listing. See the interactive-TTY
1353
+ // guard below, which explains this to anyone who runs it by hand.
1354
+ .command('serve', { hidden: true })
1151
1355
  .description('Start CodeGraph as an MCP server for AI assistants')
1152
1356
  .option('-p, --path <path>', 'Project path (optional for MCP mode, uses rootUri from client)')
1153
1357
  .option('--mcp', 'Run as MCP server (stdio transport)')
@@ -1161,6 +1365,22 @@ function main() {
1161
1365
  }
1162
1366
  try {
1163
1367
  if (options.mcp) {
1368
+ // `serve --mcp` is the stdio MCP server an AI agent launches for itself,
1369
+ // not a command to run by hand. A human in a terminal would otherwise
1370
+ // see it hang waiting for JSON-RPC on stdin, which reads as broken. If
1371
+ // stdin is an interactive TTY, explain instead of hanging. The agent's
1372
+ // pipe and the detached daemon both have a non-TTY stdin, so this only
1373
+ // ever fires for a person who typed it.
1374
+ if (process.stdin.isTTY && !process.env.CODEGRAPH_DAEMON_INTERNAL) {
1375
+ console.error(chalk.bold('\nCodeGraph MCP server\n'));
1376
+ console.error("This is the MCP server your AI agent (Claude Code, Cursor, Codex, opencode, …)");
1377
+ console.error("starts automatically — you don't run it yourself.");
1378
+ console.error(`\nIt's already wired up by ${chalk.cyan('codegraph install')}. To check on things:`);
1379
+ console.error(` ${chalk.cyan('codegraph status')} ${chalk.dim('— is this project indexed and healthy?')}`);
1380
+ console.error(` ${chalk.cyan('codegraph daemon')} ${chalk.dim('— list or stop background MCP servers')}`);
1381
+ console.error(chalk.dim('\n(Running it directly only does something when an MCP client drives it over stdin.)'));
1382
+ return;
1383
+ }
1164
1384
  // Start MCP server - it handles initialization lazily based on rootUri from client
1165
1385
  const { MCPServer } = await Promise.resolve().then(() => __importStar(require('../mcp/index')));
1166
1386
  const server = new MCPServer(projectPath);
@@ -1493,6 +1713,13 @@ function main() {
1493
1713
  const stdinFiles = stdinData.split('\n').map(f => f.trim()).filter(Boolean);
1494
1714
  changedFiles.push(...stdinFiles);
1495
1715
  }
1716
+ // Normalize inputs to the project-relative, forward-slash form the index
1717
+ // stores. Without this, `affected ./src/x.ts`, an absolute path (what a
1718
+ // wrapping script often passes), or a Windows back-slash path silently
1719
+ // matches nothing and reports 0 affected tests. (#825)
1720
+ changedFiles = changedFiles
1721
+ .map((f) => normalizeIndexPath(f, projectPath))
1722
+ .filter(Boolean);
1496
1723
  if (changedFiles.length === 0) {
1497
1724
  if (!options.quiet)
1498
1725
  info('No files provided. Use file arguments or --stdin.');
@@ -1748,6 +1975,20 @@ function main() {
1748
1975
  });
1749
1976
  process.exit(code);
1750
1977
  });
1978
+ /**
1979
+ * codegraph version
1980
+ *
1981
+ * The bare-noun form of `--version`. commander already provides `--version`
1982
+ * and `-V`, and the `-v` / `-version` spellings are intercepted before parse
1983
+ * (see top of main). This subcommand makes `codegraph version` work and lists
1984
+ * the version affordance in `codegraph --help`.
1985
+ */
1986
+ program
1987
+ .command('version')
1988
+ .description('Print the installed CodeGraph version (also: -v, --version)')
1989
+ .action(() => {
1990
+ console.log(packageJson.version);
1991
+ });
1751
1992
  // Parse and run
1752
1993
  program.parse();
1753
1994
  } // end main()