@codragraph/cli 2.1.0 → 2.1.4

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 (156) hide show
  1. package/README.md +62 -21
  2. package/dist/_shared/cgdb/schema-constants.d.ts +2 -2
  3. package/dist/_shared/cgdb/schema-constants.d.ts.map +1 -1
  4. package/dist/_shared/cgdb/schema-constants.js +3 -0
  5. package/dist/_shared/cgdb/schema-constants.js.map +1 -1
  6. package/dist/_shared/feature-clusters.d.ts +99 -0
  7. package/dist/_shared/feature-clusters.d.ts.map +1 -0
  8. package/dist/_shared/feature-clusters.js +2 -0
  9. package/dist/_shared/feature-clusters.js.map +1 -0
  10. package/dist/_shared/graph/types.d.ts +16 -2
  11. package/dist/_shared/graph/types.d.ts.map +1 -1
  12. package/dist/_shared/index.d.ts +1 -0
  13. package/dist/_shared/index.d.ts.map +1 -1
  14. package/dist/_shared/index.js.map +1 -1
  15. package/dist/_shared/pipeline.d.ts +1 -1
  16. package/dist/_shared/pipeline.d.ts.map +1 -1
  17. package/dist/cli/ai-context.js +4 -0
  18. package/dist/cli/analyze.js +46 -26
  19. package/dist/cli/index.js +39 -1
  20. package/dist/cli/serve.d.ts +1 -0
  21. package/dist/cli/serve.js +3 -1
  22. package/dist/cli/setup.js +42 -21
  23. package/dist/cli/status.d.ts +13 -0
  24. package/dist/cli/status.js +99 -0
  25. package/dist/cli/tool.d.ts +25 -0
  26. package/dist/cli/tool.js +74 -0
  27. package/dist/config/ignore-service.js +2 -0
  28. package/dist/config/supported-languages.d.ts +3 -3
  29. package/dist/config/supported-languages.js +3 -3
  30. package/dist/core/cgdb/cgdb-adapter.js +19 -3
  31. package/dist/core/cgdb/csv-generator.js +33 -2
  32. package/dist/core/cgdb/schema.d.ts +2 -1
  33. package/dist/core/cgdb/schema.js +55 -0
  34. package/dist/core/embeddings/embedder.js +4 -2
  35. package/dist/core/graphstore/cgdb-row-source.js +3 -2
  36. package/dist/core/graphstore/index.d.ts +1 -1
  37. package/dist/core/graphstore/index.js +1 -1
  38. package/dist/core/group/bridge-db.js +42 -10
  39. package/dist/core/group/service.d.ts +16 -0
  40. package/dist/core/group/service.js +360 -0
  41. package/dist/core/ingestion/emit-references.d.ts +1 -1
  42. package/dist/core/ingestion/emit-references.js +1 -1
  43. package/dist/core/ingestion/feature-cluster-processor.d.ts +62 -0
  44. package/dist/core/ingestion/feature-cluster-processor.js +626 -0
  45. package/dist/core/ingestion/finalize-orchestrator.js +1 -1
  46. package/dist/core/ingestion/model/registration-table.js +1 -0
  47. package/dist/core/ingestion/model/resolve.d.ts +2 -2
  48. package/dist/core/ingestion/model/resolve.js +3 -3
  49. package/dist/core/ingestion/model/semantic-model.d.ts +1 -1
  50. package/dist/core/ingestion/model/semantic-model.js +1 -1
  51. package/dist/core/ingestion/model/symbol-table.d.ts +1 -1
  52. package/dist/core/ingestion/model/symbol-table.js +1 -1
  53. package/dist/core/ingestion/pipeline-phases/feature-clusters.d.ts +17 -0
  54. package/dist/core/ingestion/pipeline-phases/feature-clusters.js +88 -0
  55. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  56. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  57. package/dist/core/ingestion/pipeline.d.ts +4 -0
  58. package/dist/core/ingestion/pipeline.js +9 -5
  59. package/dist/core/run-analyze.d.ts +21 -0
  60. package/dist/core/run-analyze.js +213 -6
  61. package/dist/core/search/hybrid-search.js +11 -3
  62. package/dist/mcp/core/embedder.js +5 -2
  63. package/dist/mcp/local/local-backend.d.ts +12 -0
  64. package/dist/mcp/local/local-backend.js +381 -3
  65. package/dist/mcp/resources.js +139 -0
  66. package/dist/mcp/tools.js +174 -2
  67. package/dist/server/api.d.ts +14 -2
  68. package/dist/server/api.js +206 -7
  69. package/dist/server/mcp-http.d.ts +22 -0
  70. package/dist/server/mcp-http.js +21 -2
  71. package/dist/server/web-dashboard.d.ts +28 -0
  72. package/dist/server/web-dashboard.js +61 -0
  73. package/dist/storage/repo-manager.d.ts +6 -1
  74. package/dist/storage/repo-manager.js +5 -1
  75. package/dist/types/pipeline.d.ts +2 -0
  76. package/dist/web/assets/agent-D5lb0zXz.js +1089 -0
  77. package/dist/web/assets/architectureDiagram-EMZXCZ2Q-CZtc99v_.js +36 -0
  78. package/dist/web/assets/blockDiagram-IGV67L2C-BtoUp-6Y.js +132 -0
  79. package/dist/web/assets/c4Diagram-DFAF54RM-C4Hl3J2U.js +10 -0
  80. package/dist/web/assets/chunk-3GS5O3IE-DkUjU0WD.js +231 -0
  81. package/dist/web/assets/chunk-3YCYZ6SJ-CQkVgT_z.js +1 -0
  82. package/dist/web/assets/chunk-7RZVMHOQ-BitYcNVR.js +338 -0
  83. package/dist/web/assets/chunk-AEOMTBSW-BgTIXPsY.js +1 -0
  84. package/dist/web/assets/chunk-H3VCZNTA-Cx5XV_aC.js +13 -0
  85. package/dist/web/assets/chunk-HN6EAY2L-BBnyTNdB.js +1 -0
  86. package/dist/web/assets/chunk-KSICW3F5-BYzvDLNI.js +15 -0
  87. package/dist/web/assets/chunk-O5ABG6QK-dHwHzA6n.js +1 -0
  88. package/dist/web/assets/chunk-PK6DOVAG-CvsEnugt.js +206 -0
  89. package/dist/web/assets/chunk-RWUO3TPN-BgRTY0_k.js +1 -0
  90. package/dist/web/assets/chunk-TBF5ZNIQ-DL5stGM1.js +1 -0
  91. package/dist/web/assets/chunk-TU3PZOEN-RLyvLcv-.js +1 -0
  92. package/dist/web/assets/classDiagram-PPOCWD7C-DTr8QIOf.js +1 -0
  93. package/dist/web/assets/classDiagram-v2-23LJLIIU-DTr8QIOf.js +1 -0
  94. package/dist/web/assets/context-builder-22jU3V56.js +16 -0
  95. package/dist/web/assets/cose-bilkent-PNC4W37J-DVhePRYg.js +1 -0
  96. package/dist/web/assets/dagre-E77IOHMT-Dzx0A6ZU.js +4 -0
  97. package/dist/web/assets/diagram-H7BISOXX-CC9pRew1.js +43 -0
  98. package/dist/web/assets/diagram-JC5VWROH-Bau_i9tf.js +24 -0
  99. package/dist/web/assets/diagram-LXUTUG65-D9_FM2Gt.js +10 -0
  100. package/dist/web/assets/diagram-WEHSV5V5-BMlayouL.js +24 -0
  101. package/dist/web/assets/erDiagram-GCSMX5X6-C3dhDFA8.js +85 -0
  102. package/dist/web/assets/flowDiagram-OTCZ4VVT-CWSFWmhr.js +162 -0
  103. package/dist/web/assets/ganttDiagram-MUNLMDZQ-D3a67Yol.js +292 -0
  104. package/dist/web/assets/gitGraphDiagram-3HKGZ4G3-7jmry-vM.js +106 -0
  105. package/dist/web/assets/index-BgeqpYgd.js +1415 -0
  106. package/dist/web/assets/index-CT0GtFLZ.css +1 -0
  107. package/dist/web/assets/infoDiagram-MN7RKWGX-G7lhP0Ib.js +2 -0
  108. package/dist/web/assets/ishikawaDiagram-YMYX4NHK-DUoJvNP2.js +70 -0
  109. package/dist/web/assets/journeyDiagram-SO5T7YLQ-RMFPNNqz.js +139 -0
  110. package/dist/web/assets/kanban-definition-LJHFXRCJ-BzpDs1K9.js +89 -0
  111. package/dist/web/assets/katex-GD7MH7QM-DBQvrix-.js +261 -0
  112. package/dist/web/assets/mindmap-definition-2EUWGEK5-Bk0O4roa.js +96 -0
  113. package/dist/web/assets/pieDiagram-3IATQBI2-DKU7kpgS.js +30 -0
  114. package/dist/web/assets/quadrantDiagram-E256RVCF-BY0TGWCS.js +7 -0
  115. package/dist/web/assets/requirementDiagram-M5DCFWZL-DLHOVTSv.js +84 -0
  116. package/dist/web/assets/sankeyDiagram-L3NBLAOT-DVMj5rX2.js +10 -0
  117. package/dist/web/assets/sequenceDiagram-ZOUHS735-CJC73bV-.js +157 -0
  118. package/dist/web/assets/stateDiagram-MLPALWAM-BCFyESls.js +1 -0
  119. package/dist/web/assets/stateDiagram-v2-B5LQ5ZB2-DahzzIca.js +1 -0
  120. package/dist/web/assets/timeline-definition-5SPVSISX-TRSDRgPw.js +120 -0
  121. package/dist/web/assets/vennDiagram-IE5QUKF5-DNy7HRBM.js +34 -0
  122. package/dist/web/assets/wardley-RL74JXVD-BCRCBASE-B-eZEzf9.js +161 -0
  123. package/dist/web/assets/wardleyDiagram-XU3VSMPF-BP-r1xzR.js +20 -0
  124. package/dist/web/assets/xychartDiagram-ZHJ5623Y-Dr9r7a35.js +7 -0
  125. package/dist/web/codragraph-logo-512.png +0 -0
  126. package/dist/web/codragraph-logo.png +0 -0
  127. package/dist/web/favicon.png +0 -0
  128. package/dist/web/index.html +36 -0
  129. package/hooks/claude/codragraph-hook.cjs +24 -9
  130. package/hooks/claude/pre-tool-use.sh +6 -1
  131. package/package.json +15 -4
  132. package/scripts/build.js +75 -16
  133. package/scripts/patch-tree-sitter-swift.cjs +0 -1
  134. package/skills/codragraph-cli.md +17 -1
  135. package/skills/codragraph-guide.md +6 -2
  136. package/skills/codragraph-onboarding.md +2 -2
  137. package/vendor/leiden/index.cjs +272 -285
  138. package/vendor/leiden/utils.cjs +264 -274
  139. package/dist/_shared/lbug/schema-constants.d.ts +0 -16
  140. package/dist/_shared/lbug/schema-constants.d.ts.map +0 -1
  141. package/dist/_shared/lbug/schema-constants.js +0 -67
  142. package/dist/_shared/lbug/schema-constants.js.map +0 -1
  143. package/dist/core/graphstore/lbug-row-source.d.ts +0 -19
  144. package/dist/core/graphstore/lbug-row-source.js +0 -141
  145. package/dist/core/lbug/content-read.d.ts +0 -46
  146. package/dist/core/lbug/content-read.js +0 -64
  147. package/dist/core/lbug/csv-generator.d.ts +0 -29
  148. package/dist/core/lbug/csv-generator.js +0 -492
  149. package/dist/core/lbug/lbug-adapter.d.ts +0 -176
  150. package/dist/core/lbug/lbug-adapter.js +0 -1320
  151. package/dist/core/lbug/pool-adapter.d.ts +0 -93
  152. package/dist/core/lbug/pool-adapter.js +0 -550
  153. package/dist/core/lbug/schema.d.ts +0 -62
  154. package/dist/core/lbug/schema.js +0 -502
  155. package/dist/mcp/core/lbug-adapter.d.ts +0 -5
  156. package/dist/mcp/core/lbug-adapter.js +0 -5
@@ -12,12 +12,17 @@ import { execFileSync } from 'child_process';
12
12
  import v8 from 'v8';
13
13
  import cliProgress from 'cli-progress';
14
14
  import * as fsSync from 'node:fs';
15
+ import { createRequire } from 'module';
15
16
  import { closeCgdb } from '../core/cgdb/cgdb-adapter.js';
16
17
  import { getStoragePaths, getGlobalRegistryPath, RegistryNameCollisionError, } from '../storage/repo-manager.js';
17
18
  import { getGitRoot, hasGitDir } from '../storage/git.js';
18
19
  import { runFullAnalysis } from '../core/run-analyze.js';
19
20
  import { getMaxFileSizeBannerMessage } from '../core/ingestion/utils/max-file-size.js';
20
21
  import fs from 'fs/promises';
22
+ import { formatBytes, LARGE_INDEX_WARNING_BYTES, summarizeIndexStorage } from './status.js';
23
+ const require = createRequire(import.meta.url);
24
+ const pkg = require('../../package.json');
25
+ const CLI_PACKAGE_SPEC = `@codragraph/cli@${pkg.version}`;
21
26
  const HEAP_MB = 8192;
22
27
  const HEAP_FLAG = `--max-old-space-size=${HEAP_MB}`;
23
28
  /** Increase default stack size (KB) to prevent stack overflow on deep class hierarchies. */
@@ -105,25 +110,6 @@ export const analyzeCommand = async (inputPath, options) => {
105
110
  }
106
111
  });
107
112
  }
108
- // ── First-run auto-setup ───────────────────────────────────────────
109
- // Makes `npx @codragraph/cli analyze` a true one-command entry. We detect
110
- // first-run by the absence of the global registry — analyze writes to it on
111
- // every successful index, so it's a reliable "this user has never run us
112
- // before" signal. Opt out with `--no-setup` for CI / headless contexts;
113
- // commander maps `--no-setup` to `options.setup === false`.
114
- if (options?.setup !== false) {
115
- let registryExists = true;
116
- try {
117
- await fs.access(getGlobalRegistryPath());
118
- }
119
- catch {
120
- registryExists = false;
121
- }
122
- if (!registryExists) {
123
- const { runSetup } = await import('./setup.js');
124
- await runSetup({ skipNextSteps: true, compactHeader: true });
125
- }
126
- }
127
113
  console.log('\n CodraGraph Analyzer\n');
128
114
  let repoPath;
129
115
  if (inputPath) {
@@ -153,6 +139,23 @@ export const analyzeCommand = async (inputPath, options) => {
153
139
  if (!repoHasGit) {
154
140
  console.log(' Warning: no .git directory found \u2014 commit-tracking and incremental updates disabled.\n');
155
141
  }
142
+ // ── First-run auto-setup ───────────────────────────────────────────
143
+ // Makes `npx @codragraph/cli analyze` a true one-command entry. Validate
144
+ // the target repo first so invalid invocations fail fast without mutating
145
+ // editor/global config.
146
+ if (options?.setup !== false) {
147
+ let registryExists = true;
148
+ try {
149
+ await fs.access(getGlobalRegistryPath());
150
+ }
151
+ catch {
152
+ registryExists = false;
153
+ }
154
+ if (!registryExists) {
155
+ const { runSetup } = await import('./setup.js');
156
+ await runSetup({ skipNextSteps: true, compactHeader: true });
157
+ }
158
+ }
156
159
  // KuzuDB migration cleanup is handled by runFullAnalysis internally.
157
160
  // Note: --skills is handled after runFullAnalysis using the returned pipelineResult.
158
161
  if (process.env.CODRAGRAPH_NO_GITIGNORE) {
@@ -253,7 +256,7 @@ export const analyzeCommand = async (inputPath, options) => {
253
256
  console.warn = origWarn;
254
257
  console.error = origError;
255
258
  bar.stop();
256
- console.log(' Already up to date\n');
259
+ console.log(` ${result.reuseReason ?? 'Already up to date'}\n`);
257
260
  // Safe to return without process.exit(0) — the early-return path in
258
261
  // runFullAnalysis never opens LadybugDB, so no native handles prevent exit.
259
262
  return;
@@ -299,7 +302,8 @@ export const analyzeCommand = async (inputPath, options) => {
299
302
  nodes: s.nodes ?? 0,
300
303
  edges: s.edges ?? 0,
301
304
  communities: s.communities,
302
- clusters: aggregatedClusterCount,
305
+ clusters: result.pipelineResult?.featureClusterResult?.stats.totalClusters ??
306
+ aggregatedClusterCount,
303
307
  processes: s.processes,
304
308
  }, skillResult.skills, { skipAgentsMd: options?.skipAgentsMd, noStats: options?.noStats });
305
309
  }
@@ -321,6 +325,20 @@ export const analyzeCommand = async (inputPath, options) => {
321
325
  console.log(`\n Repository indexed successfully (${totalTime}s)\n`);
322
326
  console.log(` ${(s.nodes ?? 0).toLocaleString()} nodes | ${(s.edges ?? 0).toLocaleString()} edges | ${s.communities ?? 0} clusters | ${s.processes ?? 0} flows`);
323
327
  console.log(` ${repoPath}`);
328
+ try {
329
+ const { storagePath } = getStoragePaths(repoPath);
330
+ const storageSummary = await summarizeIndexStorage(storagePath);
331
+ if (storageSummary) {
332
+ console.log(` .codragraph size: ${formatBytes(storageSummary.bytes)}`);
333
+ if (storageSummary.bytes >= LARGE_INDEX_WARNING_BYTES) {
334
+ console.log(` Storage warning: index is >= ${formatBytes(LARGE_INDEX_WARNING_BYTES)}. ` +
335
+ `Use --compress brotli (or zstd on Node >=22.15) and reserve --embeddings for repos that need vector search.`);
336
+ }
337
+ }
338
+ }
339
+ catch {
340
+ /* size summary is best-effort */
341
+ }
324
342
  // Surface @codragraph/compress's value prop with concrete numbers: how
325
343
  // many tokens of distilled context did we generate. Best-effort — never
326
344
  // fail the analyze for a stat read.
@@ -388,12 +406,13 @@ export const analyzeCommand = async (inputPath, options) => {
388
406
  // 'node.target'" crash happens inside npm *before* codragraph code runs,
389
407
  // so it can't be caught here. This branch handles dependency-resolution
390
408
  // errors that surface at runtime (e.g. dynamic require failures).
391
- console.error(' This looks like an npm dependency resolution issue.');
409
+ console.error(' This looks like a package-manager dependency resolution issue.');
392
410
  console.error(' Suggestions:');
393
411
  console.error(' 1. Clear the npm cache: npm cache clean --force');
394
412
  console.error(' 2. Update npm: npm install -g npm@latest');
395
- console.error(' 3. Reinstall codragraph: npm install -g @codragraph/cli@latest');
396
- console.error(' 4. Or try npx directly: npx @codragraph/cli@latest analyze');
413
+ console.error(` 3. Reinstall codragraph: npm install -g ${CLI_PACKAGE_SPEC}`);
414
+ console.error(` 4. Or try npx directly: npx ${CLI_PACKAGE_SPEC} analyze`);
415
+ console.error(` 5. Bun alternative: bunx ${CLI_PACKAGE_SPEC} analyze`);
397
416
  console.error('');
398
417
  }
399
418
  else if (msg.includes('MODULE_NOT_FOUND') ||
@@ -401,8 +420,9 @@ export const analyzeCommand = async (inputPath, options) => {
401
420
  msg.includes('ERR_MODULE_NOT_FOUND')) {
402
421
  console.error(' A required module could not be loaded. The installation may be corrupt.');
403
422
  console.error(' Suggestions:');
404
- console.error(' 1. Reinstall: npm install -g @codragraph/cli@latest');
405
- console.error(' 2. Clear cache: npm cache clean --force && npx @codragraph/cli@latest analyze');
423
+ console.error(` 1. Reinstall: npm install -g ${CLI_PACKAGE_SPEC}`);
424
+ console.error(` 2. Clear cache: npm cache clean --force && npx ${CLI_PACKAGE_SPEC} analyze`);
425
+ console.error(` 3. Bun cache: bun pm cache rm && bunx ${CLI_PACKAGE_SPEC} analyze`);
406
426
  console.error('');
407
427
  }
408
428
  process.exitCode = 1;
package/dist/cli/index.js CHANGED
@@ -55,9 +55,10 @@ program
55
55
  .action(createLazyAction(() => import('./index-repo.js'), 'indexCommand'));
56
56
  program
57
57
  .command('serve')
58
- .description('Start local HTTP server for web UI connection')
58
+ .description('Start local HTTP API and web dashboard')
59
59
  .option('-p, --port <port>', 'Port number', '4747')
60
60
  .option('--host <host>', 'Bind address (default: 127.0.0.1, use 0.0.0.0 for remote access)')
61
+ .option('--web <mode>', 'Dashboard mode: local serves the bundled app, hosted prints hosted UI connection info, off serves API only', 'local')
61
62
  .action(createLazyAction(() => import('./serve.js'), 'serveCommand'));
62
63
  program
63
64
  .command('mcp')
@@ -135,6 +136,43 @@ program
135
136
  .description('Execute raw Cypher query against the knowledge graph')
136
137
  .option('-r, --repo <name>', 'Target repository')
137
138
  .action(createLazyAction(() => import('./tool.js'), 'cypherCommand'));
139
+ program
140
+ .command('feature-clusters')
141
+ .description('List human-facing feature clusters for targeted context building')
142
+ .option('-r, --repo <name>', 'Target repository')
143
+ .option('-l, --limit <n>', 'Max feature clusters to return (default: 100)')
144
+ .action(createLazyAction(() => import('./tool.js'), 'featureClustersCommand'));
145
+ program
146
+ .command('cluster-query [query]')
147
+ .description('Alias for feature-clusters: list product/domain clusters')
148
+ .option('-r, --repo <name>', 'Target repository')
149
+ .option('-l, --limit <n>', 'Max feature clusters to return (default: 100)')
150
+ .action(createLazyAction(() => import('./tool.js'), 'clusterQueryCommand'));
151
+ program
152
+ .command('feature-context <name>')
153
+ .description('Show members, line ranges, and dependencies for a feature cluster')
154
+ .option('-r, --repo <name>', 'Target repository')
155
+ .option('-l, --limit <n>', 'Max members to return (default: 100)')
156
+ .action(createLazyAction(() => import('./tool.js'), 'featureContextCommand'));
157
+ program
158
+ .command('cluster-context <name>')
159
+ .description('Alias for feature-context: show a feature cluster context pack')
160
+ .option('-r, --repo <name>', 'Target repository')
161
+ .option('-l, --limit <n>', 'Max members to return (default: 100)')
162
+ .action(createLazyAction(() => import('./tool.js'), 'clusterContextCommand'));
163
+ program
164
+ .command('context-pack <name>')
165
+ .description('Generate the compact agent context pack for a feature cluster')
166
+ .option('-r, --repo <name>', 'Target repository')
167
+ .option('-l, --limit <n>', 'Max members to return (default: 100)')
168
+ .action(createLazyAction(() => import('./tool.js'), 'contextPackCommand'));
169
+ program
170
+ .command('cluster-impact <name>')
171
+ .description('Feature-level blast radius analysis for a cluster')
172
+ .option('-d, --direction <dir>', 'upstream, downstream, or both', 'upstream')
173
+ .option('-r, --repo <name>', 'Target repository')
174
+ .option('-l, --limit <n>', 'Max context-pack members to include (default: 100)')
175
+ .action(createLazyAction(() => import('./tool.js'), 'clusterImpactCommand'));
138
176
  program
139
177
  .command('detect-changes')
140
178
  .alias('detect_changes')
@@ -1,4 +1,5 @@
1
1
  export declare const serveCommand: (options?: {
2
2
  port?: string;
3
3
  host?: string;
4
+ web?: string;
4
5
  }) => Promise<void>;
package/dist/cli/serve.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createServer } from '../server/api.js';
2
+ import { normalizeWebDashboardMode } from '../server/web-dashboard.js';
2
3
  // Catch anything that would cause a silent exit
3
4
  process.on('uncaughtException', (err) => {
4
5
  console.error('\n[codragraph serve] Uncaught exception:', err.message);
@@ -19,7 +20,8 @@ export const serveCommand = async (options) => {
19
20
  // hosted frontend at codragraph.vercel.app connects to localhost.
20
21
  const host = options?.host ?? 'localhost';
21
22
  try {
22
- await createServer(port, host);
23
+ const web = normalizeWebDashboardMode(options?.web);
24
+ await createServer(port, host, { web });
23
25
  }
24
26
  catch (err) {
25
27
  console.error(`\nFailed to start CodraGraph server:\n`);
package/dist/cli/setup.js CHANGED
@@ -11,15 +11,19 @@ import os from 'os';
11
11
  import { execFile, execFileSync } from 'child_process';
12
12
  import { promisify } from 'util';
13
13
  import { fileURLToPath } from 'url';
14
+ import { createRequire } from 'module';
14
15
  import { glob } from 'glob';
15
16
  import { parseTree, modify, applyEdits } from 'jsonc-parser';
16
17
  import { getGlobalDir } from '../storage/repo-manager.js';
17
18
  const __filename = fileURLToPath(import.meta.url);
18
19
  const __dirname = path.dirname(__filename);
19
20
  const execFileAsync = promisify(execFile);
21
+ const require = createRequire(import.meta.url);
22
+ const pkg = require('../../package.json');
23
+ const CLI_PACKAGE_SPEC = `@codragraph/cli@${pkg.version}`;
20
24
  /**
21
25
  * Resolve the absolute path to the `codragraph` binary if it's installed
22
- * globally (or via npm -g / yarn global). Returns null when not found.
26
+ * globally (or via npm -g / bun -g / yarn global). Returns null when not found.
23
27
  *
24
28
  * Note: the npm package is `@codragraph/cli`, but the executable it installs
25
29
  * is `codragraph` (see package.json `bin`). PATH lookup must use the bin name.
@@ -45,7 +49,7 @@ function resolveCodragraphBin() {
45
49
  .filter(Boolean);
46
50
  if (process.platform === 'win32') {
47
51
  // Prefer a Windows-executable shim. If none is on PATH, return null so
48
- // the caller falls through to the `cmd /c npx -y @codragraph/cli` path.
52
+ // the caller falls through to the package-runner fallback.
49
53
  const exe = lines.find((l) => /\.(cmd|exe|bat)$/i.test(l));
50
54
  return exe ?? null;
51
55
  }
@@ -59,39 +63,61 @@ function resolveCodragraphBin() {
59
63
  * The MCP server entry for all editors.
60
64
  *
61
65
  * Prefers the globally-installed `codragraph` binary (starts in ~1 s) over
62
- * `npx -y @codragraph/cli@latest` (cold-cache install of native deps can take
66
+ * `npx` / `bunx` package execution (cold-cache install of native deps can take
63
67
  * >60 s, exceeding Claude Code's 30 s MCP connection timeout).
64
68
  *
65
- * Falls back to npx when the binary isn't on PATH — e.g. first-time
66
- * users who ran `npx @codragraph/cli analyze` but haven't done `npm i -g`.
69
+ * Falls back to the package runner that invoked setup when the binary isn't on
70
+ * PATH e.g. first-time `npx @codragraph/cli` or `bunx @codragraph/cli` users.
67
71
  *
68
72
  * Windows note: even when the bin is on PATH, we launch via `cmd /c codragraph
69
73
  * mcp` rather than writing the resolved path. Reason: `where codragraph`
70
74
  * returns the extensionless Unix shim before `codragraph.cmd`, and Node's
71
75
  * spawn/execFile cannot launch the extensionless shim on Windows. Letting
72
76
  * cmd resolve via PATHEXT is the only reliable path that works for npm-,
73
- * pnpm-, and yarn-installed shims alike.
77
+ * bun-, pnpm-, and yarn-installed shims alike.
74
78
  */
75
- function getMcpEntry() {
76
- const bin = resolveCodragraphBin();
77
- if (bin) {
79
+ function isRunningUnderBun() {
80
+ const userAgent = (process.env.npm_config_user_agent ?? '').toLowerCase();
81
+ const rawExecPath = process.env.npm_execpath ?? '';
82
+ const execPath = process.platform === 'win32'
83
+ ? path.win32.basename(rawExecPath).toLowerCase()
84
+ : path.basename(rawExecPath).toLowerCase();
85
+ return userAgent.startsWith('bun/') || execPath === 'bun' || execPath === 'bun.exe';
86
+ }
87
+ function getPackageRunnerMcpEntry() {
88
+ if (isRunningUnderBun()) {
78
89
  if (process.platform === 'win32') {
79
- return { command: 'cmd', args: ['/c', 'codragraph', 'mcp'] };
90
+ return {
91
+ command: 'cmd',
92
+ args: ['/c', 'bunx', CLI_PACKAGE_SPEC, 'mcp'],
93
+ };
80
94
  }
81
- return { command: bin, args: ['mcp'] };
95
+ return {
96
+ command: 'bunx',
97
+ args: [CLI_PACKAGE_SPEC, 'mcp'],
98
+ };
82
99
  }
83
- // Fallback: npx (works without a global install, but slow cold-start)
84
100
  if (process.platform === 'win32') {
85
101
  return {
86
102
  command: 'cmd',
87
- args: ['/c', 'npx', '-y', '@codragraph/cli@latest', 'mcp'],
103
+ args: ['/c', 'npx', '-y', CLI_PACKAGE_SPEC, 'mcp'],
88
104
  };
89
105
  }
90
106
  return {
91
107
  command: 'npx',
92
- args: ['-y', '@codragraph/cli@latest', 'mcp'],
108
+ args: ['-y', CLI_PACKAGE_SPEC, 'mcp'],
93
109
  };
94
110
  }
111
+ function getMcpEntry() {
112
+ const bin = resolveCodragraphBin();
113
+ if (bin) {
114
+ if (process.platform === 'win32') {
115
+ return { command: 'cmd', args: ['/c', 'codragraph', 'mcp'] };
116
+ }
117
+ return { command: bin, args: ['mcp'] };
118
+ }
119
+ return getPackageRunnerMcpEntry();
120
+ }
95
121
  /**
96
122
  * OpenCode uses a different MCP format: { type: "local", command: [...] }
97
123
  * where command is a flat array (command + args combined).
@@ -104,13 +130,8 @@ function getOpenCodeMcpEntry() {
104
130
  }
105
131
  return { type: 'local', command: [bin, 'mcp'] };
106
132
  }
107
- if (process.platform === 'win32') {
108
- return {
109
- type: 'local',
110
- command: ['cmd', '/c', 'npx', '-y', '@codragraph/cli@latest', 'mcp'],
111
- };
112
- }
113
- return { type: 'local', command: ['npx', '-y', '@codragraph/cli@latest', 'mcp'] };
133
+ const entry = getPackageRunnerMcpEntry();
134
+ return { type: 'local', command: [entry.command, ...entry.args] };
114
135
  }
115
136
  /**
116
137
  * Merge codragraph entry into an existing MCP config JSON object.
@@ -3,4 +3,17 @@
3
3
  *
4
4
  * Shows the indexing status of the current repository.
5
5
  */
6
+ import { type RepoMeta } from '../storage/repo-manager.js';
7
+ export declare const LARGE_INDEX_WARNING_BYTES: number;
8
+ export interface IndexStorageSummary {
9
+ path: string;
10
+ bytes: number;
11
+ fileCount: number;
12
+ unreadableEntries: number;
13
+ }
14
+ export declare const formatBytes: (bytes: number) => string;
15
+ export declare const summarizeIndexStorage: (storagePath: string) => Promise<IndexStorageSummary | null>;
16
+ export declare const formatIndexStatusLines: (summary: IndexStorageSummary | null, meta: Pick<RepoMeta, "stats" | "compress">, options?: {
17
+ largeIndexWarningBytes?: number;
18
+ }) => string[];
6
19
  export declare const statusCommand: () => Promise<void>;
@@ -3,8 +3,99 @@
3
3
  *
4
4
  * Shows the indexing status of the current repository.
5
5
  */
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
6
8
  import { findRepo, getStoragePaths, hasKuzuIndex } from '../storage/repo-manager.js';
7
9
  import { getCurrentCommit, isGitRepo, getGitRoot } from '../storage/git.js';
10
+ export const LARGE_INDEX_WARNING_BYTES = 500 * 1024 * 1024;
11
+ const STORAGE_SCAN_BATCH_SIZE = 64;
12
+ export const formatBytes = (bytes) => {
13
+ if (!Number.isFinite(bytes) || bytes <= 0)
14
+ return '0 B';
15
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
16
+ let value = bytes;
17
+ let unitIndex = 0;
18
+ while (value >= 1024 && unitIndex < units.length - 1) {
19
+ value /= 1024;
20
+ unitIndex += 1;
21
+ }
22
+ if (unitIndex === 0)
23
+ return `${Math.round(value)} ${units[unitIndex]}`;
24
+ return `${value.toFixed(1)} ${units[unitIndex]}`;
25
+ };
26
+ export const summarizeIndexStorage = async (storagePath) => {
27
+ let rootStat;
28
+ try {
29
+ rootStat = await fs.lstat(storagePath);
30
+ }
31
+ catch (error) {
32
+ if (error.code === 'ENOENT')
33
+ return null;
34
+ return { path: storagePath, bytes: 0, fileCount: 0, unreadableEntries: 1 };
35
+ }
36
+ if (!rootStat.isDirectory()) {
37
+ return { path: storagePath, bytes: rootStat.size, fileCount: 1, unreadableEntries: 0 };
38
+ }
39
+ const stack = [storagePath];
40
+ let bytes = 0;
41
+ let fileCount = 0;
42
+ let unreadableEntries = 0;
43
+ while (stack.length > 0) {
44
+ const current = stack.pop();
45
+ let entries;
46
+ try {
47
+ entries = await fs.readdir(current, { withFileTypes: true });
48
+ }
49
+ catch {
50
+ unreadableEntries += 1;
51
+ continue;
52
+ }
53
+ for (let i = 0; i < entries.length; i += STORAGE_SCAN_BATCH_SIZE) {
54
+ const batch = entries.slice(i, i + STORAGE_SCAN_BATCH_SIZE);
55
+ const stats = await Promise.all(batch.map(async (entry) => {
56
+ const entryPath = path.join(current, entry.name);
57
+ try {
58
+ return { entryPath, stat: await fs.lstat(entryPath) };
59
+ }
60
+ catch {
61
+ return { entryPath, stat: null };
62
+ }
63
+ }));
64
+ for (const { entryPath, stat } of stats) {
65
+ if (!stat) {
66
+ unreadableEntries += 1;
67
+ continue;
68
+ }
69
+ if (stat.isDirectory()) {
70
+ stack.push(entryPath);
71
+ continue;
72
+ }
73
+ bytes += stat.size;
74
+ fileCount += 1;
75
+ }
76
+ }
77
+ }
78
+ return { path: storagePath, bytes, fileCount, unreadableEntries };
79
+ };
80
+ export const formatIndexStatusLines = (summary, meta, options = {}) => {
81
+ const lines = [];
82
+ const threshold = options.largeIndexWarningBytes ?? LARGE_INDEX_WARNING_BYTES;
83
+ if (summary) {
84
+ const fileLabel = summary.fileCount === 1 ? 'file' : 'files';
85
+ lines.push(`Index size: ${formatBytes(summary.bytes)} (${summary.fileCount.toLocaleString('en-US')} ${fileLabel} under .codragraph)`);
86
+ if (summary.unreadableEntries > 0) {
87
+ const entryLabel = summary.unreadableEntries === 1 ? 'entry' : 'entries';
88
+ lines.push(`Index size note: ${summary.unreadableEntries.toLocaleString('en-US')} ${entryLabel} could not be read.`);
89
+ }
90
+ if (summary.bytes >= threshold) {
91
+ lines.push(`Storage warning: .codragraph is ${formatBytes(summary.bytes)} (>= ${formatBytes(threshold)}). Consider codragraph analyze --compress brotli (or zstd on Node >=22.15) and only use --embeddings when vectors are needed.`);
92
+ }
93
+ }
94
+ const embeddings = meta.stats?.embeddings;
95
+ lines.push(`Embeddings: ${typeof embeddings === 'number' ? embeddings.toLocaleString('en-US') : 'unknown'}`);
96
+ lines.push(`Compression: ${meta.compress ?? 'none'}`);
97
+ return lines;
98
+ };
8
99
  export const statusCommand = async () => {
9
100
  const cwd = process.cwd();
10
101
  if (!isGitRepo(cwd)) {
@@ -16,6 +107,7 @@ export const statusCommand = async () => {
16
107
  // Check if there's a stale KuzuDB index that needs migration
17
108
  const repoRoot = getGitRoot(cwd) ?? cwd;
18
109
  const { storagePath } = getStoragePaths(repoRoot);
110
+ const storageSummary = await summarizeIndexStorage(storagePath);
19
111
  if (await hasKuzuIndex(storagePath)) {
20
112
  console.log('Repository has a stale KuzuDB index from a previous version.');
21
113
  console.log('Run: codragraph analyze (rebuilds the index with LadybugDB)');
@@ -24,13 +116,20 @@ export const statusCommand = async () => {
24
116
  console.log('Repository not indexed.');
25
117
  console.log('Run: codragraph analyze');
26
118
  }
119
+ if (storageSummary) {
120
+ for (const line of formatIndexStatusLines(storageSummary, {}))
121
+ console.log(line);
122
+ }
27
123
  return;
28
124
  }
29
125
  const currentCommit = getCurrentCommit(repo.repoPath);
30
126
  const isUpToDate = currentCommit === repo.meta.lastCommit;
127
+ const storageSummary = await summarizeIndexStorage(repo.storagePath);
31
128
  console.log(`Repository: ${repo.repoPath}`);
32
129
  console.log(`Indexed: ${new Date(repo.meta.indexedAt).toLocaleString()}`);
33
130
  console.log(`Indexed commit: ${repo.meta.lastCommit?.slice(0, 7)}`);
34
131
  console.log(`Current commit: ${currentCommit?.slice(0, 7)}`);
132
+ for (const line of formatIndexStatusLines(storageSummary, repo.meta))
133
+ console.log(line);
35
134
  console.log(`Status: ${isUpToDate ? '✅ up-to-date' : '⚠️ stale (re-run codragraph analyze)'}`);
36
135
  };
@@ -36,6 +36,31 @@ export declare function impactCommand(target: string, options?: {
36
36
  export declare function cypherCommand(query: string, options?: {
37
37
  repo?: string;
38
38
  }): Promise<void>;
39
+ export declare function featureClustersCommand(options?: {
40
+ repo?: string;
41
+ limit?: string;
42
+ }): Promise<void>;
43
+ export declare function clusterQueryCommand(query?: string, options?: {
44
+ repo?: string;
45
+ limit?: string;
46
+ }): Promise<void>;
47
+ export declare function featureContextCommand(name: string, options?: {
48
+ repo?: string;
49
+ limit?: string;
50
+ }): Promise<void>;
51
+ export declare function clusterContextCommand(name: string, options?: {
52
+ repo?: string;
53
+ limit?: string;
54
+ }): Promise<void>;
55
+ export declare function contextPackCommand(name: string, options?: {
56
+ repo?: string;
57
+ limit?: string;
58
+ }): Promise<void>;
59
+ export declare function clusterImpactCommand(name: string, options?: {
60
+ direction?: string;
61
+ repo?: string;
62
+ limit?: string;
63
+ }): Promise<void>;
39
64
  export declare function detectChangesCommand(options?: {
40
65
  scope?: string;
41
66
  baseRef?: string;
package/dist/cli/tool.js CHANGED
@@ -128,6 +128,80 @@ export async function cypherCommand(query, options) {
128
128
  });
129
129
  output(result);
130
130
  }
131
+ export async function featureClustersCommand(options) {
132
+ const backend = await getBackend();
133
+ const result = await backend.callTool('feature_clusters', {
134
+ repo: options?.repo,
135
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
136
+ });
137
+ output(result);
138
+ }
139
+ export async function clusterQueryCommand(query, options) {
140
+ const backend = await getBackend();
141
+ const result = await backend.callTool('cluster_query', {
142
+ query,
143
+ repo: options?.repo,
144
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
145
+ });
146
+ output(result);
147
+ }
148
+ export async function featureContextCommand(name, options) {
149
+ if (!name?.trim()) {
150
+ console.error('Usage: codragraph feature-context <name>');
151
+ process.exit(1);
152
+ }
153
+ const backend = await getBackend();
154
+ const result = await backend.callTool('feature_context', {
155
+ name,
156
+ repo: options?.repo,
157
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
158
+ });
159
+ output(result);
160
+ emitTokenStats(result);
161
+ }
162
+ export async function clusterContextCommand(name, options) {
163
+ if (!name?.trim()) {
164
+ console.error('Usage: codragraph cluster-context <name>');
165
+ process.exit(1);
166
+ }
167
+ const backend = await getBackend();
168
+ const result = await backend.callTool('cluster_context', {
169
+ name,
170
+ repo: options?.repo,
171
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
172
+ });
173
+ output(result);
174
+ emitTokenStats(result);
175
+ }
176
+ export async function contextPackCommand(name, options) {
177
+ if (!name?.trim()) {
178
+ console.error('Usage: codragraph context-pack <name>');
179
+ process.exit(1);
180
+ }
181
+ const backend = await getBackend();
182
+ const result = await backend.callTool('context_pack', {
183
+ name,
184
+ repo: options?.repo,
185
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
186
+ });
187
+ output(result);
188
+ emitTokenStats(result);
189
+ }
190
+ export async function clusterImpactCommand(name, options) {
191
+ if (!name?.trim()) {
192
+ console.error('Usage: codragraph cluster-impact <name>');
193
+ process.exit(1);
194
+ }
195
+ const backend = await getBackend();
196
+ const result = await backend.callTool('cluster_impact', {
197
+ name,
198
+ direction: options?.direction,
199
+ repo: options?.repo,
200
+ limit: options?.limit ? parseInt(options.limit, 10) : undefined,
201
+ });
202
+ output(result);
203
+ emitTokenStats(result);
204
+ }
131
205
  function formatDetectChangesResult(result) {
132
206
  if (result?.error)
133
207
  return `Error: ${result.error}`;
@@ -212,6 +212,8 @@ const IGNORED_EXTENSIONS = new Set([
212
212
  // Files to ignore by exact name
213
213
  const IGNORED_FILES = new Set([
214
214
  'package-lock.json',
215
+ 'bun.lock',
216
+ 'bun.lockb',
215
217
  'yarn.lock',
216
218
  'pnpm-lock.yaml',
217
219
  'composer.lock',
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Re-export SupportedLanguages from codragraph-shared (single source of truth).
2
+ * Re-export SupportedLanguages from @codragraph/shared (single source of truth).
3
3
  *
4
4
  * HOW TO ADD A NEW LANGUAGE:
5
5
  *
6
- * 1. Add the enum member in codragraph-shared/src/languages.ts
6
+ * 1. Add the enum member in packages/shared/src/languages.ts
7
7
  * 2. Run `tsc --noEmit` — compiler errors guide you to every dispatch table
8
8
  * 3. Use the checklist in each ingestion file for what to add
9
- * 4. Add tree-sitter-<lang> to codragraph/package.json dependencies
9
+ * 4. Add tree-sitter-<lang> to packages/core/package.json dependencies
10
10
  * 5. Add file extension mapping in utils.ts getLanguageFromFilename()
11
11
  * 6. Run full test suite
12
12
  */
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Re-export SupportedLanguages from codragraph-shared (single source of truth).
2
+ * Re-export SupportedLanguages from @codragraph/shared (single source of truth).
3
3
  *
4
4
  * HOW TO ADD A NEW LANGUAGE:
5
5
  *
6
- * 1. Add the enum member in codragraph-shared/src/languages.ts
6
+ * 1. Add the enum member in packages/shared/src/languages.ts
7
7
  * 2. Run `tsc --noEmit` — compiler errors guide you to every dispatch table
8
8
  * 3. Use the checklist in each ingestion file for what to add
9
- * 4. Add tree-sitter-<lang> to codragraph/package.json dependencies
9
+ * 4. Add tree-sitter-<lang> to packages/core/package.json dependencies
10
10
  * 5. Add file extension mapping in utils.ts getLanguageFromFilename()
11
11
  * 6. Run full test suite
12
12
  */