@codragraph/cli 2.0.0 → 2.1.1

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 (101) hide show
  1. package/README.md +60 -22
  2. package/dist/_shared/cgdb/schema-constants.d.ts +16 -0
  3. package/dist/_shared/cgdb/schema-constants.d.ts.map +1 -0
  4. package/dist/_shared/cgdb/schema-constants.js +70 -0
  5. package/dist/_shared/cgdb/schema-constants.js.map +1 -0
  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 +3 -2
  13. package/dist/_shared/index.d.ts.map +1 -1
  14. package/dist/_shared/index.js +1 -1
  15. package/dist/_shared/index.js.map +1 -1
  16. package/dist/_shared/pipeline.d.ts +1 -1
  17. package/dist/_shared/pipeline.d.ts.map +1 -1
  18. package/dist/cli/ai-context.js +4 -0
  19. package/dist/cli/analyze.js +30 -27
  20. package/dist/cli/graphstore.js +21 -21
  21. package/dist/cli/index-repo.js +3 -3
  22. package/dist/cli/index.js +37 -0
  23. package/dist/cli/setup.js +9 -5
  24. package/dist/cli/tool.d.ts +25 -0
  25. package/dist/cli/tool.js +74 -0
  26. package/dist/cli/wiki.js +3 -3
  27. package/dist/config/supported-languages.d.ts +3 -3
  28. package/dist/config/supported-languages.js +3 -3
  29. package/dist/core/augmentation/engine.js +7 -7
  30. package/dist/core/cgdb/cgdb-adapter.d.ts +176 -0
  31. package/dist/core/cgdb/cgdb-adapter.js +1336 -0
  32. package/dist/core/cgdb/content-read.d.ts +46 -0
  33. package/dist/core/cgdb/content-read.js +64 -0
  34. package/dist/core/cgdb/csv-generator.d.ts +29 -0
  35. package/dist/core/cgdb/csv-generator.js +523 -0
  36. package/dist/core/cgdb/pool-adapter.d.ts +93 -0
  37. package/dist/core/cgdb/pool-adapter.js +550 -0
  38. package/dist/core/cgdb/schema.d.ts +63 -0
  39. package/dist/core/cgdb/schema.js +557 -0
  40. package/dist/core/embeddings/embedder.js +4 -2
  41. package/dist/core/embeddings/embedding-pipeline.js +4 -4
  42. package/dist/core/graphstore/cgdb-row-source.d.ts +19 -0
  43. package/dist/core/graphstore/cgdb-row-source.js +141 -0
  44. package/dist/core/graphstore/index.d.ts +2 -2
  45. package/dist/core/graphstore/index.js +4 -4
  46. package/dist/core/group/bridge-db.d.ts +2 -2
  47. package/dist/core/group/bridge-db.js +18 -18
  48. package/dist/core/group/bridge-schema.d.ts +4 -4
  49. package/dist/core/group/bridge-schema.js +4 -4
  50. package/dist/core/group/cross-impact.js +3 -3
  51. package/dist/core/group/service.d.ts +16 -0
  52. package/dist/core/group/service.js +360 -0
  53. package/dist/core/group/sync.js +4 -4
  54. package/dist/core/ingestion/emit-references.d.ts +1 -1
  55. package/dist/core/ingestion/emit-references.js +1 -1
  56. package/dist/core/ingestion/feature-cluster-processor.d.ts +62 -0
  57. package/dist/core/ingestion/feature-cluster-processor.js +626 -0
  58. package/dist/core/ingestion/finalize-orchestrator.js +1 -1
  59. package/dist/core/ingestion/model/registration-table.js +1 -0
  60. package/dist/core/ingestion/model/resolve.d.ts +2 -2
  61. package/dist/core/ingestion/model/resolve.js +3 -3
  62. package/dist/core/ingestion/model/semantic-model.d.ts +1 -1
  63. package/dist/core/ingestion/model/semantic-model.js +1 -1
  64. package/dist/core/ingestion/model/symbol-table.d.ts +1 -1
  65. package/dist/core/ingestion/model/symbol-table.js +1 -1
  66. package/dist/core/ingestion/pipeline-phases/feature-clusters.d.ts +17 -0
  67. package/dist/core/ingestion/pipeline-phases/feature-clusters.js +88 -0
  68. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  69. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  70. package/dist/core/ingestion/pipeline.d.ts +4 -0
  71. package/dist/core/ingestion/pipeline.js +9 -5
  72. package/dist/core/run-analyze.d.ts +1 -0
  73. package/dist/core/run-analyze.js +36 -30
  74. package/dist/core/search/bm25-index.d.ts +3 -3
  75. package/dist/core/search/bm25-index.js +9 -9
  76. package/dist/core/search/hybrid-search.js +2 -2
  77. package/dist/core/wiki/generator.d.ts +2 -2
  78. package/dist/core/wiki/generator.js +4 -4
  79. package/dist/core/wiki/graph-queries.d.ts +2 -2
  80. package/dist/core/wiki/graph-queries.js +5 -5
  81. package/dist/mcp/core/cgdb-adapter.d.ts +5 -0
  82. package/dist/mcp/core/cgdb-adapter.js +5 -0
  83. package/dist/mcp/core/embedder.js +6 -3
  84. package/dist/mcp/local/local-backend.d.ts +14 -2
  85. package/dist/mcp/local/local-backend.js +396 -18
  86. package/dist/mcp/resources.js +139 -0
  87. package/dist/mcp/server.js +3 -3
  88. package/dist/mcp/tools.js +175 -3
  89. package/dist/server/analyze-worker.js +2 -2
  90. package/dist/server/api.js +147 -31
  91. package/dist/storage/repo-manager.d.ts +10 -5
  92. package/dist/storage/repo-manager.js +10 -6
  93. package/dist/types/pipeline.d.ts +2 -0
  94. package/hooks/claude/codragraph-hook.cjs +4 -4
  95. package/package.json +15 -6
  96. package/scripts/build.js +21 -21
  97. package/skills/codragraph-cli.md +17 -1
  98. package/skills/codragraph-guide.md +6 -2
  99. package/skills/codragraph-onboarding.md +2 -2
  100. package/vendor/tree-sitter-proto/bindings/node/index.js +3 -3
  101. package/vendor/tree-sitter-proto/src/node-types.json +1 -1
@@ -13,11 +13,11 @@ import path from 'path';
13
13
  import fs from 'fs/promises';
14
14
  import { createRequire } from 'node:module';
15
15
  import { loadMeta, listRegisteredRepos, getStoragePath } from '../storage/repo-manager.js';
16
- import { executeQuery, executePrepared, executeWithReusedStatement, streamQuery, closeLbug, withLbugDb, } from '../core/lbug/lbug-adapter.js';
17
- import { isWriteQuery } from '../core/lbug/pool-adapter.js';
18
- import { decodeContentField } from '../core/lbug/content-read.js';
16
+ import { executeQuery, executePrepared, executeWithReusedStatement, streamQuery, closeCgdb, withCgdbDb, } from '../core/cgdb/cgdb-adapter.js';
17
+ import { isWriteQuery } from '../core/cgdb/pool-adapter.js';
18
+ import { decodeContentField } from '../core/cgdb/content-read.js';
19
19
  import { NODE_TABLES } from '../_shared/index.js';
20
- import { searchFTSFromLbug } from '../core/search/bm25-index.js';
20
+ import { searchFTSFromCgdb } from '../core/search/bm25-index.js';
21
21
  import { hybridSearch } from '../core/search/hybrid-search.js';
22
22
  // Embedding imports are lazy (dynamic import) to avoid loading onnxruntime-node
23
23
  // at server startup — crashes on unsupported Node ABI versions (#89)
@@ -202,6 +202,9 @@ const getNodeQuery = (table, includeContent) => {
202
202
  if (table === 'Process') {
203
203
  return `MATCH (n:${tableLabel}) RETURN n.id AS id, n.label AS label, n.heuristicLabel AS heuristicLabel, n.processType AS processType, n.stepCount AS stepCount, n.communities AS communities, n.entryPointId AS entryPointId, n.terminalId AS terminalId`;
204
204
  }
205
+ if (table === 'FeatureCluster') {
206
+ return `MATCH (n:${tableLabel}) RETURN n.id AS id, n.name AS name, n.slug AS slug, n.featureKind AS featureKind, n.summary AS summary, n.description AS description, n.repo AS repo, n.service AS service, n.signals AS signals, n.memberCount AS memberCount, n.entryPointIds AS entryPointIds, n.routes AS routes, n.tools AS tools, n.testCoverageHints AS testCoverageHints, n.lastIndexedCommit AS lastIndexedCommit, n.confidence AS confidence, n.source AS source`;
207
+ }
205
208
  if (table === 'Route') {
206
209
  return `MATCH (n:${tableLabel}) RETURN n.id AS id, n.name AS name, n.filePath AS filePath, n.responseKeys AS responseKeys, n.errorKeys AS errorKeys, n.middleware AS middleware`;
207
210
  }
@@ -233,6 +236,20 @@ const mapGraphNodeRow = (table, row, includeContent) => ({
233
236
  communities: row.communities,
234
237
  entryPointId: row.entryPointId,
235
238
  terminalId: row.terminalId,
239
+ slug: row.slug,
240
+ featureKind: row.featureKind,
241
+ summary: row.summary,
242
+ repo: row.repo,
243
+ service: row.service,
244
+ signals: row.signals,
245
+ memberCount: row.memberCount,
246
+ entryPointIds: row.entryPointIds,
247
+ routes: row.routes,
248
+ tools: row.tools,
249
+ testCoverageHints: row.testCoverageHints,
250
+ lastIndexedCommit: row.lastIndexedCommit,
251
+ confidence: row.confidence,
252
+ source: row.source,
236
253
  },
237
254
  });
238
255
  const mapGraphRelationshipRow = (row) => ({
@@ -388,7 +405,7 @@ export const createServer = async (port, host = '127.0.0.1') => {
388
405
  const cleanupMcp = mountMCPEndpoints(app, backend);
389
406
  const jobManager = new JobManager();
390
407
  // Shared repo lock — prevents concurrent analyze + embed on the same repo path,
391
- // which would corrupt LadybugDB (analyze calls closeLbug + initLbug while embed has queries in flight).
408
+ // which would corrupt LadybugDB (analyze calls closeCgdb + initCgdb while embed has queries in flight).
392
409
  const activeRepoPaths = new Set();
393
410
  const acquireRepoLock = (repoPath) => {
394
411
  if (activeRepoPaths.has(repoPath)) {
@@ -571,7 +588,7 @@ export const createServer = async (port, host = '127.0.0.1') => {
571
588
  try {
572
589
  // Close any open LadybugDB handle before deleting files
573
590
  try {
574
- await closeLbug();
591
+ await closeCgdb();
575
592
  }
576
593
  catch { }
577
594
  // 1. Delete the .codragraph index/storage directory
@@ -895,21 +912,21 @@ export const createServer = async (port, host = '127.0.0.1') => {
895
912
  res.status(404).json({ error: 'Repository not found' });
896
913
  return;
897
914
  }
898
- const lbugPath = path.join(entry.storagePath, 'lbug');
915
+ const cgdbPath = path.join(entry.storagePath, 'cgdb');
899
916
  const includeContent = req.query.includeContent === 'true';
900
917
  const stream = req.query.stream === 'true';
901
- // Guard: when a repo has no materialized lbug schema (fixture-
902
- // seeded CAS-only repos), or the lbug WAL is corrupt/stale from
918
+ // Guard: when a repo has no materialized cgdb schema (fixture-
919
+ // seeded CAS-only repos), or the cgdb WAL is corrupt/stale from
903
920
  // a prior failed analyze, LadybugDB native will abort with
904
921
  // UNREACHABLE_CODE or an ANY-vector exception. Detect both
905
922
  // shapes — missing file (cheap fs.access) AND empty/4096-byte
906
923
  // schema-only file (fs.stat) — and return an empty graph so
907
924
  // the dashboard doesn't blow up. The Graph tab keeps working
908
- // for repos that actually have a real lbug.
909
- const isLbugMaterialized = await (async () => {
925
+ // for repos that actually have a real cgdb.
926
+ const isCgdbMaterialized = await (async () => {
910
927
  try {
911
- const stat = await fs.stat(lbugPath);
912
- // Schema-only lbug is exactly 4096 bytes (one page, no real
928
+ const stat = await fs.stat(cgdbPath);
929
+ // Schema-only cgdb is exactly 4096 bytes (one page, no real
913
930
  // data). Real graphs are larger.
914
931
  return stat.isFile() && stat.size > 4096;
915
932
  }
@@ -917,14 +934,14 @@ export const createServer = async (port, host = '127.0.0.1') => {
917
934
  return false;
918
935
  }
919
936
  })();
920
- if (!isLbugMaterialized) {
937
+ if (!isCgdbMaterialized) {
921
938
  if (stream) {
922
939
  res.setHeader('Content-Type', 'application/x-ndjson; charset=utf-8');
923
940
  res.flushHeaders();
924
941
  res.write(JSON.stringify({
925
942
  type: 'meta',
926
943
  repoName: entry.name,
927
- note: 'no lbug file — graph not yet materialized',
944
+ note: 'no cgdb file — graph not yet materialized',
928
945
  nodeCount: 0,
929
946
  relationshipCount: 0,
930
947
  }) + '\n');
@@ -937,7 +954,7 @@ export const createServer = async (port, host = '127.0.0.1') => {
937
954
  nodes: [],
938
955
  relationships: [],
939
956
  stats: { nodes: 0, edges: 0 },
940
- note: 'no lbug file — graph not yet materialized',
957
+ note: 'no cgdb file — graph not yet materialized',
941
958
  });
942
959
  return;
943
960
  }
@@ -959,7 +976,7 @@ export const createServer = async (port, host = '127.0.0.1') => {
959
976
  res.once('finish', markFinished);
960
977
  res.once('close', abortStreaming);
961
978
  try {
962
- await withLbugDb(lbugPath, async () => streamGraphNdjson(res, includeContent, abortController.signal));
979
+ await withCgdbDb(cgdbPath, async () => streamGraphNdjson(res, includeContent, abortController.signal));
963
980
  if (!abortController.signal.aborted && !res.writableEnded) {
964
981
  res.end();
965
982
  }
@@ -971,7 +988,7 @@ export const createServer = async (port, host = '127.0.0.1') => {
971
988
  }
972
989
  return;
973
990
  }
974
- const graph = await withLbugDb(lbugPath, async () => buildGraph(includeContent));
991
+ const graph = await withCgdbDb(cgdbPath, async () => buildGraph(includeContent));
975
992
  res.json(graph);
976
993
  }
977
994
  catch (err) {
@@ -1009,14 +1026,53 @@ export const createServer = async (port, host = '127.0.0.1') => {
1009
1026
  res.status(404).json({ error: 'Repository not found' });
1010
1027
  return;
1011
1028
  }
1012
- const lbugPath = path.join(entry.storagePath, 'lbug');
1013
- const result = await withLbugDb(lbugPath, () => executeQuery(cypher));
1029
+ const cgdbPath = path.join(entry.storagePath, 'cgdb');
1030
+ const result = await withCgdbDb(cgdbPath, () => executeQuery(cypher));
1014
1031
  res.json({ result });
1015
1032
  }
1016
1033
  catch (err) {
1017
1034
  res.status(500).json({ error: err.message || 'Query failed' });
1018
1035
  }
1019
1036
  });
1037
+ // Symbol context through the same LocalBackend path as MCP `context`
1038
+ app.post('/api/context', async (req, res) => {
1039
+ try {
1040
+ const name = String(req.body?.name ?? '').trim();
1041
+ if (!name) {
1042
+ res.status(400).json({ error: 'Missing "name" in request body' });
1043
+ return;
1044
+ }
1045
+ const result = await backend.callTool('context', {
1046
+ ...req.body,
1047
+ name,
1048
+ repo: requestedRepo(req),
1049
+ });
1050
+ res.json(result);
1051
+ }
1052
+ catch (err) {
1053
+ res.status(statusFromError(err)).json({ error: err.message || 'Context query failed' });
1054
+ }
1055
+ });
1056
+ // Symbol impact through the same LocalBackend path as MCP `impact`
1057
+ app.post('/api/impact', async (req, res) => {
1058
+ try {
1059
+ const target = String(req.body?.target ?? '').trim();
1060
+ if (!target) {
1061
+ res.status(400).json({ error: 'Missing "target" in request body' });
1062
+ return;
1063
+ }
1064
+ const result = await backend.callTool('impact', {
1065
+ ...req.body,
1066
+ target,
1067
+ direction: req.body?.direction ?? 'upstream',
1068
+ repo: requestedRepo(req),
1069
+ });
1070
+ res.json(result);
1071
+ }
1072
+ catch (err) {
1073
+ res.status(statusFromError(err)).json({ error: err.message || 'Impact query failed' });
1074
+ }
1075
+ });
1020
1076
  // Search (supports mode: 'hybrid' | 'semantic' | 'bm25', and optional enrichment)
1021
1077
  app.post('/api/search', async (req, res) => {
1022
1078
  try {
@@ -1030,14 +1086,14 @@ export const createServer = async (port, host = '127.0.0.1') => {
1030
1086
  res.status(404).json({ error: 'Repository not found' });
1031
1087
  return;
1032
1088
  }
1033
- const lbugPath = path.join(entry.storagePath, 'lbug');
1089
+ const cgdbPath = path.join(entry.storagePath, 'cgdb');
1034
1090
  const parsedLimit = Number(req.body.limit ?? 10);
1035
1091
  const limit = Number.isFinite(parsedLimit)
1036
1092
  ? Math.max(1, Math.min(100, Math.trunc(parsedLimit)))
1037
1093
  : 10;
1038
1094
  const mode = req.body.mode ?? 'hybrid';
1039
1095
  const enrich = req.body.enrich !== false; // default true
1040
- const results = await withLbugDb(lbugPath, async () => {
1096
+ const results = await withCgdbDb(cgdbPath, async () => {
1041
1097
  let searchResults;
1042
1098
  if (mode === 'semantic') {
1043
1099
  const { isEmbedderReady } = await import('../core/embeddings/embedder.js');
@@ -1055,7 +1111,7 @@ export const createServer = async (port, host = '127.0.0.1') => {
1055
1111
  }));
1056
1112
  }
1057
1113
  else if (mode === 'bm25') {
1058
- searchResults = await searchFTSFromLbug(query, limit);
1114
+ searchResults = await searchFTSFromCgdb(query, limit);
1059
1115
  searchResults = searchResults.map((r, i) => ({
1060
1116
  ...r,
1061
1117
  rank: i + 1,
@@ -1070,7 +1126,7 @@ export const createServer = async (port, host = '127.0.0.1') => {
1070
1126
  searchResults = await hybridSearch(query, limit, executeQuery, semSearch);
1071
1127
  }
1072
1128
  else {
1073
- searchResults = await searchFTSFromLbug(query, limit);
1129
+ searchResults = await searchFTSFromCgdb(query, limit);
1074
1130
  }
1075
1131
  }
1076
1132
  if (!enrich)
@@ -1230,8 +1286,8 @@ export const createServer = async (port, host = '127.0.0.1') => {
1230
1286
  const results = [];
1231
1287
  const repoRoot = path.resolve(entry.path);
1232
1288
  // Get file paths from the graph (lightweight — no content loaded)
1233
- const lbugPath = path.join(entry.storagePath, 'lbug');
1234
- const fileRows = await withLbugDb(lbugPath, () => executeQuery(`MATCH (n:File) WHERE n.content IS NOT NULL RETURN n.filePath AS filePath`));
1289
+ const cgdbPath = path.join(entry.storagePath, 'cgdb');
1290
+ const fileRows = await withCgdbDb(cgdbPath, () => executeQuery(`MATCH (n:File) WHERE n.content IS NOT NULL RETURN n.filePath AS filePath`));
1235
1291
  // Search files on disk one at a time (constant memory)
1236
1292
  for (const row of fileRows) {
1237
1293
  if (results.length >= limit)
@@ -1326,6 +1382,66 @@ export const createServer = async (port, host = '127.0.0.1') => {
1326
1382
  .json({ error: err.message || 'Failed to query cluster detail' });
1327
1383
  }
1328
1384
  });
1385
+ // List all feature clusters
1386
+ app.get('/api/feature-clusters', async (req, res) => {
1387
+ try {
1388
+ const limit = req.query.limit ? Number.parseInt(String(req.query.limit), 10) : undefined;
1389
+ const query = String(req.query.query ?? '');
1390
+ const result = await backend.queryFeatureClusters(requestedRepo(req), limit, query);
1391
+ res.json(result);
1392
+ }
1393
+ catch (err) {
1394
+ res
1395
+ .status(statusFromError(err))
1396
+ .json({ error: err.message || 'Failed to query feature clusters' });
1397
+ }
1398
+ });
1399
+ // Feature cluster detail
1400
+ app.get('/api/feature-cluster', async (req, res) => {
1401
+ try {
1402
+ const name = String(req.query.name ?? '').trim();
1403
+ if (!name) {
1404
+ res.status(400).json({ error: 'Missing "name" query parameter' });
1405
+ return;
1406
+ }
1407
+ const limit = req.query.limit ? Number.parseInt(String(req.query.limit), 10) : undefined;
1408
+ const result = await backend.queryFeatureContext(name, requestedRepo(req), limit);
1409
+ if (result?.error) {
1410
+ res.status(404).json({ error: result.error });
1411
+ return;
1412
+ }
1413
+ res.json(result);
1414
+ }
1415
+ catch (err) {
1416
+ res
1417
+ .status(statusFromError(err))
1418
+ .json({ error: err.message || 'Failed to query feature cluster detail' });
1419
+ }
1420
+ });
1421
+ // Feature cluster impact/context pack
1422
+ app.get(['/api/feature-impact', '/api/cluster-impact'], async (req, res) => {
1423
+ try {
1424
+ const name = String(req.query.name ?? '').trim();
1425
+ if (!name) {
1426
+ res.status(400).json({ error: 'Missing "name" query parameter' });
1427
+ return;
1428
+ }
1429
+ const limit = req.query.limit ? Number.parseInt(String(req.query.limit), 10) : undefined;
1430
+ const directionText = String(req.query.direction ?? 'upstream');
1431
+ const direction = directionText === 'downstream' || directionText === 'both' ? directionText : 'upstream';
1432
+ const result = await backend.queryFeatureImpact(name, requestedRepo(req), direction, limit);
1433
+ if (result?.error) {
1434
+ res.status(404).json({ error: result.error });
1435
+ return;
1436
+ }
1437
+ res.json(result);
1438
+ }
1439
+ catch (err) {
1440
+ res
1441
+ .status(statusFromError(err))
1442
+ .json({ error: err.message || 'Failed to query feature cluster impact' });
1443
+ }
1444
+ });
1329
1445
  // ── Analyze API ──────────────────────────────────────────────────────
1330
1446
  // POST /api/analyze — start a new analysis job
1331
1447
  app.post('/api/analyze', async (req, res) => {
@@ -1602,12 +1718,12 @@ export const createServer = async (port, host = '127.0.0.1') => {
1602
1718
  // Run embedding pipeline asynchronously
1603
1719
  (async () => {
1604
1720
  try {
1605
- const lbugPath = path.join(entry.storagePath, 'lbug');
1606
- await withLbugDb(lbugPath, async () => {
1721
+ const cgdbPath = path.join(entry.storagePath, 'cgdb');
1722
+ await withCgdbDb(cgdbPath, async () => {
1607
1723
  const { runEmbeddingPipeline } = await import('../core/embeddings/embedding-pipeline.js');
1608
1724
  // Fetch existing content hashes for incremental embedding.
1609
- // Delegated to lbug-adapter which owns the DB query logic and legacy-fallback handling.
1610
- const { fetchExistingEmbeddingHashes } = await import('../core/lbug/lbug-adapter.js');
1725
+ // Delegated to cgdb-adapter which owns the DB query logic and legacy-fallback handling.
1726
+ const { fetchExistingEmbeddingHashes } = await import('../core/cgdb/cgdb-adapter.js');
1611
1727
  const existingEmbeddings = await fetchExistingEmbeddingHashes(executeQuery);
1612
1728
  if (existingEmbeddings && existingEmbeddings.size > 0) {
1613
1729
  console.log(`[embed] ${existingEmbeddings.size} nodes already embedded — incremental run with content-hash comparison`);
@@ -1718,7 +1834,7 @@ export const createServer = async (port, host = '127.0.0.1') => {
1718
1834
  jobManager.dispose();
1719
1835
  embedJobManager.dispose();
1720
1836
  await cleanupMcp();
1721
- await closeLbug();
1837
+ await closeCgdb();
1722
1838
  await backend.disconnect();
1723
1839
  process.exit(0);
1724
1840
  };
@@ -37,7 +37,7 @@
37
37
  */
38
38
  export declare const canonicalizePath: (p: string) => string;
39
39
  /**
40
- * On-disk schema version for `.codragraph/lbug` and `.codragraph/meta.json`.
40
+ * On-disk schema version for `.codragraph/cgdb` and `.codragraph/meta.json`.
41
41
  *
42
42
  * 1 — pre-RFC-0001-Phase-2 layout. Node tables have `content STRING`
43
43
  * but no `contentEncoding` column. Implicit/missing on existing
@@ -47,6 +47,10 @@ export declare const canonicalizePath: (p: string) => string;
47
47
  * opt into compression via `--compress brotli|zstd` (compression
48
48
  * is OFF by default, so existing readers keep working). Readers
49
49
  * decode based on the per-row encoding tag.
50
+ * 3 - FeatureCluster layer: adds FeatureCluster nodes plus
51
+ * FEATURE_MEMBER_OF / FEATURE_DEPENDS_ON relation rows.
52
+ * 4 - FeatureCluster context-pack metadata: summary, repo/service,
53
+ * routes, tools, test coverage hints, and indexed commit fields.
50
54
  *
51
55
  * Bumping this is the migration trigger: `runFullAnalysis` forces a
52
56
  * full re-analyze when an existing index has a missing or older
@@ -54,7 +58,7 @@ export declare const canonicalizePath: (p: string) => string;
54
58
  * LadybugDB table via ALTER is not validated end-to-end yet — fresh
55
59
  * `CREATE NODE TABLE` is the supported path.
56
60
  */
57
- export declare const INDEX_SCHEMA_VERSION: 2;
61
+ export declare const INDEX_SCHEMA_VERSION: 4;
58
62
  export interface RepoMeta {
59
63
  repoPath: string;
60
64
  lastCommit: string;
@@ -103,6 +107,7 @@ export interface RepoMeta {
103
107
  nodes?: number;
104
108
  edges?: number;
105
109
  communities?: number;
110
+ featureClusters?: number;
106
111
  processes?: number;
107
112
  embeddings?: number;
108
113
  };
@@ -110,7 +115,7 @@ export interface RepoMeta {
110
115
  export interface IndexedRepo {
111
116
  repoPath: string;
112
117
  storagePath: string;
113
- lbugPath: string;
118
+ cgdbPath: string;
114
119
  metaPath: string;
115
120
  meta: RepoMeta;
116
121
  }
@@ -140,7 +145,7 @@ export declare const getStoragePath: (repoPath: string) => string;
140
145
  */
141
146
  export declare const getStoragePaths: (repoPath: string) => {
142
147
  storagePath: string;
143
- lbugPath: string;
148
+ cgdbPath: string;
144
149
  metaPath: string;
145
150
  };
146
151
  /**
@@ -153,7 +158,7 @@ export declare const hasKuzuIndex: (storagePath: string) => Promise<boolean>;
153
158
  *
154
159
  * Returns:
155
160
  * found — true if .codragraph/kuzu existed and was deleted
156
- * needsReindex — true if kuzu existed but lbug does not (re-analyze required)
161
+ * needsReindex — true if kuzu existed but cgdb does not (re-analyze required)
157
162
  *
158
163
  * Callers own the user-facing messaging; this function only deletes files.
159
164
  */
@@ -50,7 +50,7 @@ export const canonicalizePath = (p) => {
50
50
  }
51
51
  };
52
52
  /**
53
- * On-disk schema version for `.codragraph/lbug` and `.codragraph/meta.json`.
53
+ * On-disk schema version for `.codragraph/cgdb` and `.codragraph/meta.json`.
54
54
  *
55
55
  * 1 — pre-RFC-0001-Phase-2 layout. Node tables have `content STRING`
56
56
  * but no `contentEncoding` column. Implicit/missing on existing
@@ -60,6 +60,10 @@ export const canonicalizePath = (p) => {
60
60
  * opt into compression via `--compress brotli|zstd` (compression
61
61
  * is OFF by default, so existing readers keep working). Readers
62
62
  * decode based on the per-row encoding tag.
63
+ * 3 - FeatureCluster layer: adds FeatureCluster nodes plus
64
+ * FEATURE_MEMBER_OF / FEATURE_DEPENDS_ON relation rows.
65
+ * 4 - FeatureCluster context-pack metadata: summary, repo/service,
66
+ * routes, tools, test coverage hints, and indexed commit fields.
63
67
  *
64
68
  * Bumping this is the migration trigger: `runFullAnalysis` forces a
65
69
  * full re-analyze when an existing index has a missing or older
@@ -67,7 +71,7 @@ export const canonicalizePath = (p) => {
67
71
  * LadybugDB table via ALTER is not validated end-to-end yet — fresh
68
72
  * `CREATE NODE TABLE` is the supported path.
69
73
  */
70
- export const INDEX_SCHEMA_VERSION = 2;
74
+ export const INDEX_SCHEMA_VERSION = 4;
71
75
  const CODRAGRAPH_DIR = '.codragraph';
72
76
  // ─── Local Storage Helpers ─────────────────────────────────────────────
73
77
  /**
@@ -83,7 +87,7 @@ export const getStoragePaths = (repoPath) => {
83
87
  const storagePath = getStoragePath(repoPath);
84
88
  return {
85
89
  storagePath,
86
- lbugPath: path.join(storagePath, 'lbug'),
90
+ cgdbPath: path.join(storagePath, 'cgdb'),
87
91
  metaPath: path.join(storagePath, 'meta.json'),
88
92
  };
89
93
  };
@@ -105,16 +109,16 @@ export const hasKuzuIndex = async (storagePath) => {
105
109
  *
106
110
  * Returns:
107
111
  * found — true if .codragraph/kuzu existed and was deleted
108
- * needsReindex — true if kuzu existed but lbug does not (re-analyze required)
112
+ * needsReindex — true if kuzu existed but cgdb does not (re-analyze required)
109
113
  *
110
114
  * Callers own the user-facing messaging; this function only deletes files.
111
115
  */
112
116
  export const cleanupOldKuzuFiles = async (storagePath) => {
113
117
  const oldPath = path.join(storagePath, 'kuzu');
114
- const newPath = path.join(storagePath, 'lbug');
118
+ const newPath = path.join(storagePath, 'cgdb');
115
119
  try {
116
120
  await fs.stat(oldPath);
117
- // Old kuzu file/dir exists — determine if lbug is already present
121
+ // Old kuzu file/dir exists — determine if cgdb is already present
118
122
  let needsReindex = false;
119
123
  try {
120
124
  await fs.stat(newPath);
@@ -1,5 +1,6 @@
1
1
  import type { KnowledgeGraph } from '../core/graph/types.js';
2
2
  import { CommunityDetectionResult } from '../core/ingestion/community-processor.js';
3
+ import { FeatureClusterDetectionResult } from '../core/ingestion/feature-cluster-processor.js';
3
4
  import { ProcessDetectionResult } from '../core/ingestion/process-processor.js';
4
5
  export interface PipelineResult {
5
6
  graph: KnowledgeGraph;
@@ -9,6 +10,7 @@ export interface PipelineResult {
9
10
  totalFileCount: number;
10
11
  communityResult?: CommunityDetectionResult;
11
12
  processResult?: ProcessDetectionResult;
13
+ featureClusterResult?: FeatureClusterDetectionResult;
12
14
  /**
13
15
  * True if the parse phase spawned a worker pool for this run. False means
14
16
  * the sequential fallback handled every chunk. Primarily a test affordance
@@ -236,8 +236,8 @@ function handlePostToolUse(input) {
236
236
 
237
237
  const cwd = input.cwd || process.cwd();
238
238
  if (!path.isAbsolute(cwd)) return;
239
- const gitNexusDir = findCodraGraphDir(cwd);
240
- if (!gitNexusDir) return;
239
+ const codragraphDir = findCodraGraphDir(cwd);
240
+ if (!codragraphDir) return;
241
241
 
242
242
  // Compare HEAD against last indexed commit — skip if unchanged
243
243
  let currentHead = '';
@@ -258,7 +258,7 @@ function handlePostToolUse(input) {
258
258
  let lastCommit = '';
259
259
  let hadEmbeddings = false;
260
260
  try {
261
- const meta = JSON.parse(fs.readFileSync(path.join(gitNexusDir, 'meta.json'), 'utf-8'));
261
+ const meta = JSON.parse(fs.readFileSync(path.join(codragraphDir, 'meta.json'), 'utf-8'));
262
262
  lastCommit = meta.lastCommit || '';
263
263
  hadEmbeddings = meta.stats && meta.stats.embeddings > 0;
264
264
  } catch {
@@ -282,7 +282,7 @@ function handlePostToolUse(input) {
282
282
  // reindex is in flight. The spawned analyze removes it on exit (success or
283
283
  // failure) via CODRAGRAPH_REINDEX_LOCK_PATH; the 10-min mtime fallback
284
284
  // catches the rare crash that bypasses analyze's exit handler.
285
- const coalescePath = path.join(gitNexusDir, '.reindex.coalesce');
285
+ const coalescePath = path.join(codragraphDir, '.reindex.coalesce');
286
286
  const crashSafetyTtlMs = 10 * 60 * 1000;
287
287
  let inFlight = false;
288
288
  try {
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@codragraph/cli",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": {
6
- "name": "Anit Chaudhary",
7
- "email": "getintouch.anit@gmail.com",
8
- "url": "https://anit.upfyn.com"
6
+ "name": "Thinqmesh Technologies",
7
+ "email": "hello@thinqmesh.com",
8
+ "url": "https://thinqmesh.com"
9
9
  },
10
10
  "license": "Apache-2.0",
11
11
  "keywords": [
@@ -56,7 +56,7 @@
56
56
  "prepack": "node scripts/build.js"
57
57
  },
58
58
  "dependencies": {
59
- "@codragraph/graphstore": "^1.0.0",
59
+ "@codragraph/graphstore": "^2.1.0",
60
60
  "@huggingface/transformers": "^4.1.0",
61
61
  "@ladybugdb/core": "^0.16.0",
62
62
  "@modelcontextprotocol/sdk": "^1.0.0",
@@ -99,7 +99,7 @@
99
99
  "tree-sitter-swift": "^0.6.0"
100
100
  },
101
101
  "devDependencies": {
102
- "@codragraph/shared": "file:../codragraph-shared",
102
+ "@codragraph/shared": "file:../shared",
103
103
  "@types/cli-progress": "^3.11.6",
104
104
  "@types/cors": "^2.8.17",
105
105
  "@types/express": "^4.17.21",
@@ -123,5 +123,14 @@
123
123
  "publishConfig": {
124
124
  "access": "public",
125
125
  "registry": "https://registry.npmjs.org/"
126
+ },
127
+ "homepage": "https://github.com/AnitChaudhry/CodraGraph#readme",
128
+ "repository": {
129
+ "type": "git",
130
+ "url": "git+https://github.com/AnitChaudhry/CodraGraph.git",
131
+ "directory": "packages/core"
132
+ },
133
+ "bugs": {
134
+ "url": "https://github.com/AnitChaudhry/CodraGraph/issues"
126
135
  }
127
136
  }
package/scripts/build.js CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Build script that compiles codragraph and inlines codragraph-shared into the dist.
3
+ * Build script that compiles @codragraph/cli and inlines @codragraph/shared
4
+ * into the published dist.
4
5
  *
5
6
  * Steps:
6
- * 1. Build codragraph-shared (tsc)
7
- * 2. Build codragraph-graphstore (tsc) — codragraph imports it; without
7
+ * 1. Build @codragraph/shared (tsc)
8
+ * 2. Build @codragraph/graphstore (tsc) — @codragraph/cli imports it; without
8
9
  * a populated dist/ here, step 3 fails to resolve types.
9
- * 3. Build codragraph (tsc)
10
- * 4. Copy codragraph-shared/dist → dist/_shared
10
+ * 3. Build @codragraph/cli (tsc)
11
+ * 4. Copy packages/shared/dist → dist/_shared
11
12
  * 5. Rewrite bare '@codragraph/shared' specifiers → relative paths
12
13
  */
13
14
  import { execSync } from 'node:child_process';
@@ -17,39 +18,38 @@ import { fileURLToPath } from 'node:url';
17
18
 
18
19
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
20
  const ROOT = path.resolve(__dirname, '..');
20
- // Workspace directory names (NOT package names package names are
21
- // `@codragraph/shared` / `@codragraph/graphstore`, but the on-disk
22
- // dirs stay as `codragraph-shared` / `codragraph-graphstore`).
23
- const SHARED_ROOT = path.resolve(ROOT, '..', 'codragraph-shared');
24
- const GRAPHSTORE_ROOT = path.resolve(ROOT, '..', 'codragraph-graphstore');
21
+ // Workspace directory names packages live under `packages/<short>` on disk
22
+ // and publish as `@codragraph/<short>` on npm.
23
+ const SHARED_ROOT = path.resolve(ROOT, '..', 'shared');
24
+ const GRAPHSTORE_ROOT = path.resolve(ROOT, '..', 'graphstore');
25
25
  const DIST = path.join(ROOT, 'dist');
26
26
  const SHARED_DEST = path.join(DIST, '_shared');
27
27
 
28
- // ── 1. Build codragraph-shared ───────────────────────────────────────
29
- console.log('[build] compiling codragraph-shared…');
28
+ // ── 1. Build @codragraph/shared ──────────────────────────────────────
29
+ console.log('[build] compiling @codragraph/shared…');
30
30
  execSync('npx tsc', { cwd: SHARED_ROOT, stdio: 'inherit' });
31
31
 
32
- // ── 2. Build codragraph-graphstore ───────────────────────────────────
33
- // codragraph depends on this for snapshot/branch/diff types. On a
32
+ // ── 2. Build @codragraph/graphstore ──────────────────────────────────
33
+ // core depends on this for snapshot/branch/diff types. On a
34
34
  // fresh checkout (CI, npm ci) the graphstore dist is empty until we
35
35
  // build it here, so step 3 would otherwise fail to resolve
36
36
  // `@codragraph/graphstore` imports. Skip gracefully if the workspace
37
37
  // is not present (e.g. someone pinned an older monorepo layout).
38
38
  if (fs.existsSync(GRAPHSTORE_ROOT)) {
39
- console.log('[build] compiling codragraph-graphstore…');
39
+ console.log('[build] compiling @codragraph/graphstore…');
40
40
  execSync('npx tsc', { cwd: GRAPHSTORE_ROOT, stdio: 'inherit' });
41
41
  }
42
42
 
43
- // ── 3. Build codragraph ──────────────────────────────────────────────
44
- console.log('[build] compiling codragraph…');
43
+ // ── 3. Build @codragraph/cli ─────────────────────────────────────────
44
+ console.log('[build] compiling @codragraph/cli…');
45
45
  execSync('npx tsc', { cwd: ROOT, stdio: 'inherit' });
46
46
 
47
- // ── 3. Copy shared dist ────────────────────────────────────────────
47
+ // ── 4. Copy shared dist ────────────────────────────────────────────
48
48
  console.log('[build] copying shared module into dist/_shared…');
49
49
  fs.cpSync(path.join(SHARED_ROOT, 'dist'), SHARED_DEST, { recursive: true });
50
50
 
51
- // ── 4. Rewrite imports ─────────────────────────────────────────────
52
- console.log('[build] rewriting codragraph-shared imports…');
51
+ // ── 5. Rewrite imports ─────────────────────────────────────────────
52
+ console.log('[build] rewriting @codragraph/shared imports…');
53
53
  let rewritten = 0;
54
54
 
55
55
  function rewriteFile(filePath) {
@@ -83,7 +83,7 @@ function walk(dir, extensions, cb) {
83
83
 
84
84
  walk(DIST, ['.js', '.d.ts'], rewriteFile);
85
85
 
86
- // ── 5. Make CLI entry executable ────────────────────────────────────
86
+ // ── 6. Make CLI entry executable ────────────────────────────────────
87
87
  const cliEntry = path.join(DIST, 'cli', 'index.js');
88
88
  if (fs.existsSync(cliEntry)) fs.chmodSync(cliEntry, 0o755);
89
89