@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
@@ -12,12 +12,16 @@ 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 { closeLbug } from '../core/lbug/lbug-adapter.js';
15
+ import { createRequire } from 'module';
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
+ const require = createRequire(import.meta.url);
23
+ const pkg = require('../../package.json');
24
+ const CLI_PACKAGE_SPEC = `@codragraph/cli@${pkg.version}`;
21
25
  const HEAP_MB = 8192;
22
26
  const HEAP_FLAG = `--max-old-space-size=${HEAP_MB}`;
23
27
  /** Increase default stack size (KB) to prevent stack overflow on deep class hierarchies. */
@@ -79,7 +83,7 @@ export const analyzeCommand = async (inputPath, options) => {
79
83
  // gracefully falls back to name-only matches instead of tokenising
80
84
  // base64 garbage. Surface the trade-off so users know what they're
81
85
  // opting into.
82
- console.warn(` Note: --compress ${options.compress} reduces .codragraph/lbug size.\n` +
86
+ console.warn(` Note: --compress ${options.compress} reduces .codragraph/cgdb size.\n` +
83
87
  ` BM25 search will index symbol names only (function bodies are not tokenised\n` +
84
88
  ` when compressed); embeddings, graph queries, and \`context\` / \`impact\` are\n` +
85
89
  ` unaffected. Run with --compress none if you rely on full-text search inside\n` +
@@ -105,25 +109,6 @@ export const analyzeCommand = async (inputPath, options) => {
105
109
  }
106
110
  });
107
111
  }
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
112
  console.log('\n CodraGraph Analyzer\n');
128
113
  let repoPath;
129
114
  if (inputPath) {
@@ -153,6 +138,23 @@ export const analyzeCommand = async (inputPath, options) => {
153
138
  if (!repoHasGit) {
154
139
  console.log(' Warning: no .git directory found \u2014 commit-tracking and incremental updates disabled.\n');
155
140
  }
141
+ // ── First-run auto-setup ───────────────────────────────────────────
142
+ // Makes `npx @codragraph/cli analyze` a true one-command entry. Validate
143
+ // the target repo first so invalid invocations fail fast without mutating
144
+ // editor/global config.
145
+ if (options?.setup !== false) {
146
+ let registryExists = true;
147
+ try {
148
+ await fs.access(getGlobalRegistryPath());
149
+ }
150
+ catch {
151
+ registryExists = false;
152
+ }
153
+ if (!registryExists) {
154
+ const { runSetup } = await import('./setup.js');
155
+ await runSetup({ skipNextSteps: true, compactHeader: true });
156
+ }
157
+ }
156
158
  // KuzuDB migration cleanup is handled by runFullAnalysis internally.
157
159
  // Note: --skills is handled after runFullAnalysis using the returned pipelineResult.
158
160
  if (process.env.CODRAGRAPH_NO_GITIGNORE) {
@@ -182,7 +184,7 @@ export const analyzeCommand = async (inputPath, options) => {
182
184
  aborted = true;
183
185
  bar.stop();
184
186
  console.log('\n Interrupted — cleaning up...');
185
- closeLbug()
187
+ closeCgdb()
186
188
  .catch(() => { })
187
189
  .finally(() => process.exit(130));
188
190
  };
@@ -299,7 +301,8 @@ export const analyzeCommand = async (inputPath, options) => {
299
301
  nodes: s.nodes ?? 0,
300
302
  edges: s.edges ?? 0,
301
303
  communities: s.communities,
302
- clusters: aggregatedClusterCount,
304
+ clusters: result.pipelineResult?.featureClusterResult?.stats.totalClusters ??
305
+ aggregatedClusterCount,
303
306
  processes: s.processes,
304
307
  }, skillResult.skills, { skipAgentsMd: options?.skipAgentsMd, noStats: options?.noStats });
305
308
  }
@@ -392,8 +395,8 @@ export const analyzeCommand = async (inputPath, options) => {
392
395
  console.error(' Suggestions:');
393
396
  console.error(' 1. Clear the npm cache: npm cache clean --force');
394
397
  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');
398
+ console.error(` 3. Reinstall codragraph: npm install -g ${CLI_PACKAGE_SPEC}`);
399
+ console.error(` 4. Or try npx directly: npx ${CLI_PACKAGE_SPEC} analyze`);
397
400
  console.error('');
398
401
  }
399
402
  else if (msg.includes('MODULE_NOT_FOUND') ||
@@ -401,8 +404,8 @@ export const analyzeCommand = async (inputPath, options) => {
401
404
  msg.includes('ERR_MODULE_NOT_FOUND')) {
402
405
  console.error(' A required module could not be loaded. The installation may be corrupt.');
403
406
  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');
407
+ console.error(` 1. Reinstall: npm install -g ${CLI_PACKAGE_SPEC}`);
408
+ console.error(` 2. Clear cache: npm cache clean --force && npx ${CLI_PACKAGE_SPEC} analyze`);
406
409
  console.error('');
407
410
  }
408
411
  process.exitCode = 1;
@@ -12,7 +12,7 @@ import fs from 'node:fs/promises';
12
12
  import { FsCAS, createBranch, createCommit, DEFAULT_BRANCH, deleteBranch, diffSemantic, diffSnapshots, gc as runGc, getJson, listBranches, materializeSnapshot, parseObjectId, readCommit, readHead, resolveHeadCommit, setHead, threeWayMerge, walkCommits, writeHeadBranch, writeHeadDetached, } from '@codragraph/graphstore';
13
13
  import { findRepo, loadMeta, saveMeta } from '../storage/repo-manager.js';
14
14
  import { GRAPHSTORE_SUBDIR } from '../core/graphstore/index.js';
15
- import { initLbug, closeLbug } from '../core/lbug/lbug-adapter.js';
15
+ import { initCgdb, closeCgdb } from '../core/cgdb/cgdb-adapter.js';
16
16
  import { recordAnalysisSnapshot } from '../core/graphstore/index.js';
17
17
  import { getCurrentCommit, hasGitDir } from '../storage/git.js';
18
18
  const resolveGraphstore = async (cwd) => {
@@ -164,7 +164,7 @@ export const commitCommand = async (opts = {}) => {
164
164
  const repo = await findRepo(process.cwd());
165
165
  if (!repo)
166
166
  throw new Error('No CodraGraph index found');
167
- await initLbug(repo.lbugPath);
167
+ await initCgdb(repo.cgdbPath);
168
168
  try {
169
169
  const result = await recordAnalysisSnapshot({
170
170
  storagePath: repo.storagePath,
@@ -191,7 +191,7 @@ export const commitCommand = async (opts = {}) => {
191
191
  }
192
192
  }
193
193
  finally {
194
- await closeLbug();
194
+ await closeCgdb();
195
195
  }
196
196
  // Avoid unused warnings while keeping the import live for future callers.
197
197
  void ctx;
@@ -221,10 +221,10 @@ export const branchCreateCommand = async (name, opts = {}) => {
221
221
  // ──────────────────────────────────────────────────────────────────────
222
222
  //
223
223
  // Two modes:
224
- // - default : just move HEAD (no lbug rewrite). Cheap; good for
224
+ // - default : just move HEAD (no cgdb rewrite). Cheap; good for
225
225
  // log/diff inspection from a different vantage point.
226
226
  // - --materialize : also rebuild the live LadybugDB from the target
227
- // snapshot. Destructive of the current lbug state —
227
+ // snapshot. Destructive of the current cgdb state —
228
228
  // run `commit` first if you have unsaved changes.
229
229
  export const checkoutCommand = async (target, opts = {}) => {
230
230
  const ctx = await resolveGraphstore(process.cwd());
@@ -247,9 +247,9 @@ export const checkoutCommand = async (target, opts = {}) => {
247
247
  throw new Error('No CodraGraph index found');
248
248
  const commit = await readCommit(ctx.cas, commitId);
249
249
  process.stdout.write(`materializing snapshot ${commit.snapshot.slice(7, 7 + 12)}...\n`);
250
- // Wipe and reinit lbug.
251
- await closeLbug();
252
- for (const f of [repo.lbugPath, `${repo.lbugPath}.wal`, `${repo.lbugPath}.lock`]) {
250
+ // Wipe and reinit cgdb.
251
+ await closeCgdb();
252
+ for (const f of [repo.cgdbPath, `${repo.cgdbPath}.wal`, `${repo.cgdbPath}.lock`]) {
253
253
  try {
254
254
  await fs.rm(f, { recursive: true, force: true });
255
255
  }
@@ -257,9 +257,9 @@ export const checkoutCommand = async (target, opts = {}) => {
257
257
  /* swallow */
258
258
  }
259
259
  }
260
- await initLbug(repo.lbugPath);
260
+ await initCgdb(repo.cgdbPath);
261
261
  try {
262
- const sink = await createLbugRowSinkForCheckout(repo.lbugPath);
262
+ const sink = await createCgdbRowSinkForCheckout(repo.cgdbPath);
263
263
  const result = await materializeSnapshot({
264
264
  cas: ctx.cas,
265
265
  snapshotId: commit.snapshot,
@@ -269,14 +269,14 @@ export const checkoutCommand = async (target, opts = {}) => {
269
269
  `${result.stats.edgeRowCount} edges\n`);
270
270
  }
271
271
  finally {
272
- await closeLbug();
272
+ await closeCgdb();
273
273
  }
274
274
  }
275
275
  };
276
276
  // ──────────────────────────────────────────────────────────────────────
277
277
  // codragraph materialize <target> --into <path>
278
278
  // ──────────────────────────────────────────────────────────────────────
279
- // Read-only inspection: rebuild a snapshot into a fresh sibling lbug
279
+ // Read-only inspection: rebuild a snapshot into a fresh sibling cgdb
280
280
  // without touching the live one. Useful for "let me query the graph as
281
281
  // it was at commit X" without disturbing current work.
282
282
  export const materializeCommand = async (target, opts) => {
@@ -295,9 +295,9 @@ export const materializeCommand = async (target, opts) => {
295
295
  catch {
296
296
  /* doesn't exist, good */
297
297
  }
298
- await initLbug(into);
298
+ await initCgdb(into);
299
299
  try {
300
- const sink = await createLbugRowSinkForCheckout(into);
300
+ const sink = await createCgdbRowSinkForCheckout(into);
301
301
  const result = await materializeSnapshot({
302
302
  cas: ctx.cas,
303
303
  snapshotId: commit.snapshot,
@@ -310,7 +310,7 @@ export const materializeCommand = async (target, opts) => {
310
310
  `edges: ${result.stats.edgeRowCount}\n`);
311
311
  }
312
312
  finally {
313
- await closeLbug();
313
+ await closeCgdb();
314
314
  }
315
315
  };
316
316
  // ──────────────────────────────────────────────────────────────────────
@@ -386,7 +386,7 @@ const locateSymbolHash = (manifest, symbolId, tableHint) => {
386
386
  return null;
387
387
  };
388
388
  // ──────────────────────────────────────────────────────────────────────
389
- // LbugRowSink — used by checkout --materialize and `materialize` command.
389
+ // CgdbRowSink — used by checkout --materialize and `materialize` command.
390
390
  // ──────────────────────────────────────────────────────────────────────
391
391
  //
392
392
  // Bulk-loads rows back into a fresh LadybugDB instance. Phase 4 keeps it
@@ -394,9 +394,9 @@ const locateSymbolHash = (manifest, symbolId, tableHint) => {
394
394
  // `executeWithReusedStatement` helper. For typical repos (≤100k rows)
395
395
  // this finishes in seconds; CSV-based bulk loading is a Phase 4.5
396
396
  // optimization once we measure where the bottleneck actually is.
397
- const createLbugRowSinkForCheckout = async (lbugPath) => {
398
- const { executeQuery } = await import('../core/lbug/lbug-adapter.js');
399
- const { SCHEMA_QUERIES } = await import('../core/lbug/schema.js');
397
+ const createCgdbRowSinkForCheckout = async (cgdbPath) => {
398
+ const { executeQuery } = await import('../core/cgdb/cgdb-adapter.js');
399
+ const { SCHEMA_QUERIES } = await import('../core/cgdb/schema.js');
400
400
  // Recreate the schema; the path was just wiped, so this is a clean install.
401
401
  for (const ddl of SCHEMA_QUERIES) {
402
402
  try {
@@ -424,7 +424,7 @@ const createLbugRowSinkForCheckout = async (lbugPath) => {
424
424
  endEdges: async () => { },
425
425
  finalize: async () => { },
426
426
  };
427
- void lbugPath;
427
+ void cgdbPath;
428
428
  return sink;
429
429
  };
430
430
  const insertNode = async (executeQuery, table, row) => {
@@ -468,7 +468,7 @@ const cypherLiteral = (v) => {
468
468
  return JSON.stringify(v);
469
469
  if (typeof v === 'number' || typeof v === 'boolean')
470
470
  return String(v);
471
- // Fall back to JSON for arrays / nested objects; the underlying lbug
471
+ // Fall back to JSON for arrays / nested objects; the underlying cgdb
472
472
  // schema is mostly scalar so this is rare.
473
473
  return JSON.stringify(v);
474
474
  };
@@ -49,7 +49,7 @@ export const indexCommand = async (inputPathParts, options) => {
49
49
  process.exitCode = 1;
50
50
  return;
51
51
  }
52
- const { storagePath, lbugPath } = getStoragePaths(repoPath);
52
+ const { storagePath, cgdbPath } = getStoragePaths(repoPath);
53
53
  // ── Verify .codragraph/ exists ──────────────────────────────────────
54
54
  try {
55
55
  await fs.access(storagePath);
@@ -60,9 +60,9 @@ export const indexCommand = async (inputPathParts, options) => {
60
60
  process.exitCode = 1;
61
61
  return;
62
62
  }
63
- // ── Verify lbug database exists ───────────────────────────────────
63
+ // ── Verify cgdb database exists ───────────────────────────────────
64
64
  try {
65
- await fs.access(lbugPath);
65
+ await fs.access(cgdbPath);
66
66
  }
67
67
  catch {
68
68
  console.log(` .codragraph/ folder exists but contains no LadybugDB index.`);
package/dist/cli/index.js CHANGED
@@ -135,6 +135,43 @@ program
135
135
  .description('Execute raw Cypher query against the knowledge graph')
136
136
  .option('-r, --repo <name>', 'Target repository')
137
137
  .action(createLazyAction(() => import('./tool.js'), 'cypherCommand'));
138
+ program
139
+ .command('feature-clusters')
140
+ .description('List human-facing feature clusters for targeted context building')
141
+ .option('-r, --repo <name>', 'Target repository')
142
+ .option('-l, --limit <n>', 'Max feature clusters to return (default: 100)')
143
+ .action(createLazyAction(() => import('./tool.js'), 'featureClustersCommand'));
144
+ program
145
+ .command('cluster-query [query]')
146
+ .description('Alias for feature-clusters: list product/domain clusters')
147
+ .option('-r, --repo <name>', 'Target repository')
148
+ .option('-l, --limit <n>', 'Max feature clusters to return (default: 100)')
149
+ .action(createLazyAction(() => import('./tool.js'), 'clusterQueryCommand'));
150
+ program
151
+ .command('feature-context <name>')
152
+ .description('Show members, line ranges, and dependencies for a feature cluster')
153
+ .option('-r, --repo <name>', 'Target repository')
154
+ .option('-l, --limit <n>', 'Max members to return (default: 100)')
155
+ .action(createLazyAction(() => import('./tool.js'), 'featureContextCommand'));
156
+ program
157
+ .command('cluster-context <name>')
158
+ .description('Alias for feature-context: show a feature cluster context pack')
159
+ .option('-r, --repo <name>', 'Target repository')
160
+ .option('-l, --limit <n>', 'Max members to return (default: 100)')
161
+ .action(createLazyAction(() => import('./tool.js'), 'clusterContextCommand'));
162
+ program
163
+ .command('context-pack <name>')
164
+ .description('Generate the compact agent context pack for a feature cluster')
165
+ .option('-r, --repo <name>', 'Target repository')
166
+ .option('-l, --limit <n>', 'Max members to return (default: 100)')
167
+ .action(createLazyAction(() => import('./tool.js'), 'contextPackCommand'));
168
+ program
169
+ .command('cluster-impact <name>')
170
+ .description('Feature-level blast radius analysis for a cluster')
171
+ .option('-d, --direction <dir>', 'upstream, downstream, or both', 'upstream')
172
+ .option('-r, --repo <name>', 'Target repository')
173
+ .option('-l, --limit <n>', 'Max context-pack members to include (default: 100)')
174
+ .action(createLazyAction(() => import('./tool.js'), 'clusterImpactCommand'));
138
175
  program
139
176
  .command('detect-changes')
140
177
  .alias('detect_changes')
package/dist/cli/setup.js CHANGED
@@ -11,12 +11,16 @@ 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
26
  * globally (or via npm -g / yarn global). Returns null when not found.
@@ -59,7 +63,7 @@ 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 -y @codragraph/cli@<version>` (cold-cache install of native deps can take
63
67
  * >60 s, exceeding Claude Code's 30 s MCP connection timeout).
64
68
  *
65
69
  * Falls back to npx when the binary isn't on PATH — e.g. first-time
@@ -84,12 +88,12 @@ function getMcpEntry() {
84
88
  if (process.platform === 'win32') {
85
89
  return {
86
90
  command: 'cmd',
87
- args: ['/c', 'npx', '-y', '@codragraph/cli@latest', 'mcp'],
91
+ args: ['/c', 'npx', '-y', CLI_PACKAGE_SPEC, 'mcp'],
88
92
  };
89
93
  }
90
94
  return {
91
95
  command: 'npx',
92
- args: ['-y', '@codragraph/cli@latest', 'mcp'],
96
+ args: ['-y', CLI_PACKAGE_SPEC, 'mcp'],
93
97
  };
94
98
  }
95
99
  /**
@@ -107,10 +111,10 @@ function getOpenCodeMcpEntry() {
107
111
  if (process.platform === 'win32') {
108
112
  return {
109
113
  type: 'local',
110
- command: ['cmd', '/c', 'npx', '-y', '@codragraph/cli@latest', 'mcp'],
114
+ command: ['cmd', '/c', 'npx', '-y', CLI_PACKAGE_SPEC, 'mcp'],
111
115
  };
112
116
  }
113
- return { type: 'local', command: ['npx', '-y', '@codragraph/cli@latest', 'mcp'] };
117
+ return { type: 'local', command: ['npx', '-y', CLI_PACKAGE_SPEC, 'mcp'] };
114
118
  }
115
119
  /**
116
120
  * Merge codragraph entry into an existing MCP config JSON object.
@@ -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}`;
package/dist/cli/wiki.js CHANGED
@@ -91,7 +91,7 @@ export const wikiCommand = async (inputPath, options) => {
91
91
  return;
92
92
  }
93
93
  // ── Check for existing index ────────────────────────────────────────
94
- const { storagePath, lbugPath } = getStoragePaths(repoPath);
94
+ const { storagePath, cgdbPath } = getStoragePaths(repoPath);
95
95
  const meta = await loadMeta(storagePath);
96
96
  if (!meta) {
97
97
  console.log(' Error: No CodraGraph index found.');
@@ -333,7 +333,7 @@ export const wikiCommand = async (inputPath, options) => {
333
333
  concurrency: options?.concurrency ? parseInt(options.concurrency, 10) : undefined,
334
334
  reviewOnly: options?.review,
335
335
  };
336
- const generator = new WikiGenerator(repoPath, storagePath, lbugPath, llmConfig, wikiOptions, (phase, percent, detail) => {
336
+ const generator = new WikiGenerator(repoPath, storagePath, cgdbPath, llmConfig, wikiOptions, (phase, percent, detail) => {
337
337
  const label = detail || phase;
338
338
  if (label !== lastPhase) {
339
339
  lastPhase = label;
@@ -406,7 +406,7 @@ export const wikiCommand = async (inputPath, options) => {
406
406
  ...wikiOptions,
407
407
  reviewOnly: false,
408
408
  };
409
- const continueGenerator = new WikiGenerator(repoPath, storagePath, lbugPath, llmConfig, continueOptions, (phase, percent, detail) => {
409
+ const continueGenerator = new WikiGenerator(repoPath, storagePath, cgdbPath, llmConfig, continueOptions, (phase, percent, detail) => {
410
410
  const label = detail || phase;
411
411
  if (label !== lastPhase) {
412
412
  lastPhase = label;
@@ -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
  */
@@ -56,7 +56,7 @@ async function findRepoForCwd(cwd) {
56
56
  return {
57
57
  name: bestMatch.name,
58
58
  storagePath: bestMatch.storagePath,
59
- lbugPath: path.join(bestMatch.storagePath, 'lbug'),
59
+ cgdbPath: path.join(bestMatch.storagePath, 'cgdb'),
60
60
  };
61
61
  }
62
62
  catch {
@@ -81,16 +81,16 @@ export async function augment(pattern, cwd) {
81
81
  const repo = await findRepoForCwd(workDir);
82
82
  if (!repo)
83
83
  return '';
84
- // Lazy-load lbug adapter (skip unnecessary init)
85
- const { initLbug, executeQuery, isLbugReady } = await import('../lbug/pool-adapter.js');
86
- const { searchFTSFromLbug } = await import('../search/bm25-index.js');
84
+ // Lazy-load cgdb adapter (skip unnecessary init)
85
+ const { initCgdb, executeQuery, isCgdbReady } = await import('../cgdb/pool-adapter.js');
86
+ const { searchFTSFromCgdb } = await import('../search/bm25-index.js');
87
87
  const repoId = repo.name.toLowerCase();
88
88
  // Init LadybugDB if not already
89
- if (!isLbugReady(repoId)) {
90
- await initLbug(repoId, repo.lbugPath);
89
+ if (!isCgdbReady(repoId)) {
90
+ await initCgdb(repoId, repo.cgdbPath);
91
91
  }
92
92
  // Step 1: BM25 search (fast, no embeddings)
93
- const bm25Results = await searchFTSFromLbug(pattern, 10, repoId);
93
+ const bm25Results = await searchFTSFromCgdb(pattern, 10, repoId);
94
94
  if (bm25Results.length === 0)
95
95
  return '';
96
96
  // Step 2: Map BM25 file results to symbols