@hongmaple0820/scale-engine 0.43.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.en.md +2 -2
  2. package/README.md +3 -3
  3. package/dist/api/cli.js +24 -2
  4. package/dist/api/cli.js.map +1 -1
  5. package/dist/api/mcp.js +86 -0
  6. package/dist/api/mcp.js.map +1 -1
  7. package/dist/codegraph/CodeIntelligence.d.ts +67 -0
  8. package/dist/codegraph/CodeIntelligence.js +457 -5
  9. package/dist/codegraph/CodeIntelligence.js.map +1 -1
  10. package/dist/cortex/SessionInjector.d.ts +1 -0
  11. package/dist/cortex/SessionInjector.js +33 -0
  12. package/dist/cortex/SessionInjector.js.map +1 -1
  13. package/dist/dashboard/DashboardServer.d.ts +33 -13
  14. package/dist/dashboard/DashboardServer.js +314 -182
  15. package/dist/dashboard/DashboardServer.js.map +1 -1
  16. package/dist/dashboard/index.d.ts +2 -2
  17. package/dist/dashboard/index.js +1 -1
  18. package/dist/dashboard/index.js.map +1 -1
  19. package/dist/dashboard/server.d.ts +8 -22
  20. package/dist/dashboard/server.js +2 -83
  21. package/dist/dashboard/server.js.map +1 -1
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/memory/MemoryBrain.d.ts +22 -0
  26. package/dist/memory/MemoryBrain.js +183 -4
  27. package/dist/memory/MemoryBrain.js.map +1 -1
  28. package/dist/memory/MemoryProviders.d.ts +6 -1
  29. package/dist/memory/MemoryProviders.js +190 -6
  30. package/dist/memory/MemoryProviders.js.map +1 -1
  31. package/dist/setup/SetupWizard.js +21 -7
  32. package/dist/setup/SetupWizard.js.map +1 -1
  33. package/dist/skills/SkillRepository.js +64 -1
  34. package/dist/skills/SkillRepository.js.map +1 -1
  35. package/dist/topology/DomainMapper.d.ts +23 -0
  36. package/dist/topology/DomainMapper.js +179 -0
  37. package/dist/topology/DomainMapper.js.map +1 -0
  38. package/dist/topology/LayerClassifier.d.ts +8 -0
  39. package/dist/topology/LayerClassifier.js +109 -0
  40. package/dist/topology/LayerClassifier.js.map +1 -0
  41. package/dist/topology/TourGenerator.d.ts +18 -0
  42. package/dist/topology/TourGenerator.js +120 -0
  43. package/dist/topology/TourGenerator.js.map +1 -0
  44. package/dist/topology/index.d.ts +3 -0
  45. package/dist/topology/index.js +4 -0
  46. package/dist/topology/index.js.map +1 -0
  47. package/docs/README.md +3 -0
  48. package/docs/architecture/README.md +248 -0
  49. package/docs/migration/v0.38-to-v0.44.md +232 -0
  50. package/docs/reference/cli.md +234 -0
  51. package/package.json +6 -5
  52. package/docs/EXTERNAL_REFERENCES.md +0 -66
  53. package/docs/SKILL-REPOSITORY.md +0 -57
  54. package/docs/SKILL_RADAR.md +0 -135
  55. package/docs/THIRD_PARTY_SKILLS.md +0 -114
@@ -24,6 +24,10 @@ const CODEGRAPH_PROJECT_INIT_HINT = 'codegraph init -i';
24
24
  const CODEGRAPH_SERVE_COMMAND = 'codegraph serve --mcp';
25
25
  const GRAPHIFY_SOURCE = 'https://github.com/safishamsi/graphify';
26
26
  const GRAPHIFY_INSTALL_HINT = 'uv tool install graphify && graphify install --platform codex';
27
+ const CRG_SOURCE = 'https://github.com/tirth8205/code-review-graph';
28
+ const CRG_INSTALL_HINT = 'pip install code-review-graph';
29
+ const CRG_PROJECT_INIT_HINT = 'code-review-graph build';
30
+ const CRG_SERVE_COMMAND = 'code-review-graph serve';
27
31
  const DEFAULT_CODEGRAPH_PROVIDER = {
28
32
  id: 'codegraph',
29
33
  type: 'external-cli',
@@ -44,6 +48,17 @@ const DEFAULT_GRAPHIFY_PROVIDER = {
44
48
  source: GRAPHIFY_SOURCE,
45
49
  installHint: GRAPHIFY_INSTALL_HINT,
46
50
  };
51
+ const DEFAULT_CRG_PROVIDER = {
52
+ id: 'code-review-graph',
53
+ type: 'external-cli',
54
+ enabled: true,
55
+ command: 'code-review-graph',
56
+ capabilities: ['symbols', 'callers', 'callees', 'impact', 'context', 'summary', 'module-map'],
57
+ source: CRG_SOURCE,
58
+ installHint: CRG_INSTALL_HINT,
59
+ projectInitHint: CRG_PROJECT_INIT_HINT,
60
+ serveCommand: CRG_SERVE_COMMAND,
61
+ };
47
62
  let execFileSyncImpl = childProcess.execFileSync;
48
63
  export function setCodeIntelligenceExecFileSyncForTesting(impl) {
49
64
  execFileSyncImpl = impl ?? childProcess.execFileSync;
@@ -54,6 +69,7 @@ export function defaultCodeIntelligenceConfig() {
54
69
  providers: [
55
70
  { ...DEFAULT_CODEGRAPH_PROVIDER },
56
71
  { ...DEFAULT_GRAPHIFY_PROVIDER },
72
+ { ...DEFAULT_CRG_PROVIDER },
57
73
  ],
58
74
  fallback: {
59
75
  enabled: true,
@@ -225,6 +241,314 @@ export function createCodeGraphRoiReport(options) {
225
241
  evidenceLevel: report.fallbackUsed ? 'estimated' : 'measured',
226
242
  };
227
243
  }
244
+ export function dumpCodeGraphData(options) {
245
+ const projectDir = resolve(options.projectDir ?? process.cwd());
246
+ const status = inspectCodeIntelligence({ projectDir, scaleDir: options.scaleDir });
247
+ const warnings = [];
248
+ // Priority 1: artifact manifest (graph.json)
249
+ const artifactProvider = status.providers.find(p => p.available && p.type === 'artifact');
250
+ if (artifactProvider) {
251
+ const config = loadCodeIntelligenceConfig(projectDir, options.scaleDir).config;
252
+ const providerConfig = config.providers.find(p => p.id === artifactProvider.id);
253
+ if (providerConfig?.manifest) {
254
+ const result = dumpArtifactManifest(projectDir, providerConfig);
255
+ if (result.nodes.length > 0)
256
+ return result;
257
+ }
258
+ }
259
+ // Priority 2: codegraph CLI (full index)
260
+ const externalProvider = status.providers.find(p => p.available && p.type === 'external-cli' && p.id === 'codegraph');
261
+ if (externalProvider && status.projectIndexExists) {
262
+ const result = dumpExternalCodeGraph(projectDir);
263
+ if (result.nodes.length > 0)
264
+ return result;
265
+ }
266
+ // Priority 3: fallback file walk
267
+ return dumpFallbackTopology(projectDir);
268
+ }
269
+ function dumpArtifactManifest(projectDir, provider) {
270
+ if (!provider.manifest)
271
+ return emptyTopology(projectDir, provider.id);
272
+ const manifestPath = resolveManifestWithFallback(projectDir, provider.manifest);
273
+ if (!manifestPath)
274
+ return emptyTopology(projectDir, provider.id);
275
+ try {
276
+ const content = readFileSync(manifestPath, 'utf-8');
277
+ const parsed = JSON.parse(content);
278
+ // Graphify format: nodes[] + links[]
279
+ if (Array.isArray(parsed.nodes) && (Array.isArray(parsed.links) || Array.isArray(parsed.edges))) {
280
+ return dumpGraphifyManifest(projectDir, provider.id, parsed);
281
+ }
282
+ // Traditional format: symbols[] + files[]
283
+ const symbols = Array.isArray(parsed.symbols) ? parsed.symbols : [];
284
+ const files = Array.isArray(parsed.files) ? parsed.files : [];
285
+ return dumpTraditionalManifest(projectDir, provider.id, symbols, files);
286
+ }
287
+ catch {
288
+ return emptyTopology(projectDir, provider.id);
289
+ }
290
+ }
291
+ function resolveManifestWithFallback(projectDir, manifest) {
292
+ const primary = resolveProjectPath(projectDir, manifest);
293
+ if (existsSync(primary))
294
+ return primary;
295
+ // Fallback: try graph.json in the same directory
296
+ const dir = dirname(primary);
297
+ const fallback = join(dir, 'graph.json');
298
+ if (existsSync(fallback))
299
+ return fallback;
300
+ return undefined;
301
+ }
302
+ function dumpGraphifyManifest(projectDir, provider, manifest) {
303
+ const nodes = [];
304
+ const edges = [];
305
+ for (const node of manifest.nodes) {
306
+ const filePath = normalizeManifestPath(node.source_file ?? '');
307
+ nodes.push({
308
+ id: node.id,
309
+ kind: mapGraphifyKind(node.metadata?.kind),
310
+ name: node.label ?? node.id,
311
+ filePath,
312
+ line: parseLineNumber(node.source_location),
313
+ });
314
+ }
315
+ const linkSource = manifest.links ?? manifest.edges ?? [];
316
+ for (const link of linkSource) {
317
+ edges.push({
318
+ source: link.source,
319
+ target: link.target,
320
+ kind: mapGraphifyRelation(link.relation),
321
+ });
322
+ }
323
+ return {
324
+ nodes: dedupeTopologyNodes(nodes),
325
+ edges: dedupeTopologyEdges(edges),
326
+ generatedAt: new Date().toISOString(),
327
+ provider,
328
+ projectDir,
329
+ };
330
+ }
331
+ function mapGraphifyKind(kind) {
332
+ switch (kind) {
333
+ case 'file': return 'file';
334
+ case 'bash_function':
335
+ case 'bash_entrypoint': return 'function';
336
+ case 'code': return 'function';
337
+ default: return 'file';
338
+ }
339
+ }
340
+ function mapGraphifyRelation(relation) {
341
+ switch (relation) {
342
+ case 'calls': return 'calls';
343
+ case 'imports':
344
+ case 'imports_from': return 'imports';
345
+ case 'extends': return 'extends';
346
+ case 'implements': return 'implements';
347
+ case 'contains':
348
+ case 'defines':
349
+ case 'method': return 'depends-on';
350
+ default: return 'depends-on';
351
+ }
352
+ }
353
+ function parseLineNumber(loc) {
354
+ if (!loc)
355
+ return undefined;
356
+ const match = loc.match(/L(\d+)/);
357
+ return match ? parseInt(match[1], 10) : undefined;
358
+ }
359
+ function dumpTraditionalManifest(projectDir, provider, symbols, files) {
360
+ const nodes = [];
361
+ const edges = [];
362
+ const nodeIds = new Set();
363
+ // Symbol nodes
364
+ for (const sym of symbols) {
365
+ const name = String(sym.name ?? '');
366
+ const file = normalizeManifestPath(sym.file ?? sym.path);
367
+ if (!name)
368
+ continue;
369
+ const id = file ? `${file}::${name}` : name;
370
+ nodeIds.add(id);
371
+ nodes.push({
372
+ id,
373
+ kind: guessSymbolKind(name),
374
+ name,
375
+ filePath: file || '',
376
+ });
377
+ // Edges from callers/callees/dependencies
378
+ for (const callee of stringifyArray(sym.callees)) {
379
+ const target = resolveEdgeTarget(callee, symbols);
380
+ if (target)
381
+ edges.push({ source: id, target, kind: 'calls' });
382
+ }
383
+ for (const dep of stringifyArray(sym.dependencies)) {
384
+ const target = resolveEdgeTarget(dep, symbols);
385
+ if (target)
386
+ edges.push({ source: id, target, kind: 'depends-on' });
387
+ }
388
+ for (const caller of stringifyArray(sym.callers)) {
389
+ const target = resolveEdgeTarget(caller, symbols);
390
+ if (target)
391
+ edges.push({ source: target, target: id, kind: 'calls' });
392
+ }
393
+ for (const dependent of stringifyArray(sym.dependents)) {
394
+ const target = resolveEdgeTarget(dependent, symbols);
395
+ if (target)
396
+ edges.push({ source: target, target: id, kind: 'depends-on' });
397
+ }
398
+ }
399
+ // File nodes (for files not yet represented by symbols)
400
+ for (const file of files) {
401
+ const path = normalizeManifestPath(file.path ?? file.file);
402
+ if (!path)
403
+ continue;
404
+ const fileId = `file:${path}`;
405
+ if (!nodeIds.has(fileId)) {
406
+ nodeIds.add(fileId);
407
+ nodes.push({ id: fileId, kind: 'file', name: path.split('/').pop() ?? path, filePath: path });
408
+ }
409
+ // Import edges
410
+ for (const imp of stringifyArray(file.imports)) {
411
+ const targetFile = normalizeManifestPath(imp);
412
+ if (targetFile) {
413
+ const targetId = `file:${targetFile}`;
414
+ edges.push({ source: fileId, target: targetId, kind: 'imports' });
415
+ }
416
+ }
417
+ // dependsOn edges
418
+ for (const dep of stringifyArray(file.dependsOn)) {
419
+ const targetFile = normalizeManifestPath(dep);
420
+ if (targetFile) {
421
+ const targetId = `file:${targetFile}`;
422
+ edges.push({ source: fileId, target: targetId, kind: 'depends-on' });
423
+ }
424
+ }
425
+ }
426
+ return {
427
+ nodes: dedupeTopologyNodes(nodes),
428
+ edges: dedupeTopologyEdges(edges),
429
+ generatedAt: new Date().toISOString(),
430
+ provider,
431
+ projectDir,
432
+ };
433
+ }
434
+ function dumpExternalCodeGraph(projectDir) {
435
+ try {
436
+ // Use codegraph CLI to get a broad dump via empty query
437
+ const output = runExternalCommandSync('codegraph', ['query', '', '-p', projectDir, '--json'], {
438
+ encoding: 'utf8',
439
+ stdio: ['ignore', 'pipe', 'pipe'],
440
+ }, {
441
+ execFileSync: execFileSyncImpl,
442
+ spawnSync: childProcess.spawnSync,
443
+ });
444
+ const parsed = JSON.parse(String(output));
445
+ const nodes = [];
446
+ const seen = new Set();
447
+ for (const entry of parsed) {
448
+ const file = normalizeManifestPath(entry.node?.filePath);
449
+ const name = String(entry.node?.qualifiedName ?? entry.node?.name ?? '').trim();
450
+ if (!file || !name)
451
+ continue;
452
+ const id = `${file}::${name}`;
453
+ if (seen.has(id))
454
+ continue;
455
+ seen.add(id);
456
+ nodes.push({
457
+ id,
458
+ kind: mapCodeGraphKind(entry.node?.kind),
459
+ name,
460
+ filePath: file,
461
+ line: entry.node?.startLine,
462
+ });
463
+ }
464
+ return {
465
+ nodes,
466
+ edges: [], // CLI doesn't return edge data in query mode
467
+ generatedAt: new Date().toISOString(),
468
+ provider: 'codegraph',
469
+ projectDir,
470
+ };
471
+ }
472
+ catch {
473
+ return emptyTopology(projectDir, 'codegraph');
474
+ }
475
+ }
476
+ function dumpFallbackTopology(projectDir) {
477
+ const files = sourceFiles(projectDir);
478
+ const nodes = files.map(file => {
479
+ const rel = normalizePath(relative(projectDir, file));
480
+ return {
481
+ id: `file:${rel}`,
482
+ kind: 'file',
483
+ name: rel.split('/').pop() ?? rel,
484
+ filePath: rel,
485
+ };
486
+ });
487
+ return {
488
+ nodes,
489
+ edges: [],
490
+ generatedAt: new Date().toISOString(),
491
+ provider: 'fallback-file-walk',
492
+ projectDir,
493
+ };
494
+ }
495
+ function resolveEdgeTarget(ref, symbols) {
496
+ const asPath = normalizeManifestPath(ref);
497
+ if (asPath && hasFileExtension(asPath)) {
498
+ // Reference is a file path — find a symbol in that file
499
+ const sym = symbols.find(s => normalizeManifestPath(s.file ?? s.path) === asPath);
500
+ return sym ? `${asPath}::${sym.name}` : `file:${asPath}`;
501
+ }
502
+ // Reference is a symbol name
503
+ const sym = symbols.find(s => String(s.name) === ref);
504
+ if (sym) {
505
+ const file = normalizeManifestPath(sym.file ?? sym.path);
506
+ return file ? `${file}::${ref}` : ref;
507
+ }
508
+ return undefined;
509
+ }
510
+ function guessSymbolKind(name) {
511
+ if (/^[A-Z]/.test(name))
512
+ return 'class';
513
+ if (/^[A-Z_][A-Z_0-9]*$/.test(name))
514
+ return 'constant';
515
+ return 'function';
516
+ }
517
+ function mapCodeGraphKind(kind) {
518
+ switch (kind?.toLowerCase()) {
519
+ case 'class':
520
+ case 'struct':
521
+ case 'interface':
522
+ case 'type': return 'class';
523
+ case 'function':
524
+ case 'method':
525
+ case 'constructor': return 'function';
526
+ case 'constant':
527
+ case 'enum': return 'constant';
528
+ case 'module':
529
+ case 'namespace':
530
+ case 'package': return 'module';
531
+ default: return 'function';
532
+ }
533
+ }
534
+ function dedupeTopologyNodes(nodes) {
535
+ const seen = new Set();
536
+ return nodes.filter(n => { if (seen.has(n.id))
537
+ return false; seen.add(n.id); return true; });
538
+ }
539
+ function dedupeTopologyEdges(edges) {
540
+ const seen = new Set();
541
+ return edges.filter(e => {
542
+ const key = `${e.source}->${e.target}:${e.kind}`;
543
+ if (seen.has(key))
544
+ return false;
545
+ seen.add(key);
546
+ return true;
547
+ });
548
+ }
549
+ function emptyTopology(projectDir, provider) {
550
+ return { nodes: [], edges: [], generatedAt: new Date().toISOString(), provider, projectDir };
551
+ }
228
552
  function runCodeGraphQuery(options) {
229
553
  const projectDir = resolve(options.projectDir ?? process.cwd());
230
554
  const query = options.query.trim();
@@ -255,6 +579,16 @@ function runCodeGraphQuery(options) {
255
579
  warnings.push(`Provider ${externalAvailable.id} returned no hits for "${query}".`);
256
580
  }
257
581
  }
582
+ else if (externalAvailable?.id === 'code-review-graph' && options.mode === 'query') {
583
+ const crgQuery = queryExternalCRG({ projectDir, query });
584
+ if (crgQuery) {
585
+ hits = crgQuery.hits;
586
+ provider = externalAvailable.id;
587
+ warnings.push(...crgQuery.warnings);
588
+ if (hits.length === 0)
589
+ warnings.push(`Provider code-review-graph returned no hits for "${query}".`);
590
+ }
591
+ }
258
592
  else if (externalAvailable) {
259
593
  if (externalAvailable.id === 'codegraph' && !status.projectIndexExists) {
260
594
  warnings.push('CodeGraph CLI is installed, but this project has no .codegraph/ index yet; run codegraph init -i to enable upstream graph queries.');
@@ -310,15 +644,20 @@ function providerStatus(projectDir) {
310
644
  };
311
645
  }
312
646
  if (provider.type === 'artifact') {
313
- const manifest = provider.manifest ? resolveProjectPath(projectDir, provider.manifest) : '';
314
- const available = Boolean(manifest && existsSync(manifest));
647
+ const resolved = provider.manifest ? resolveManifestWithFallback(projectDir, provider.manifest) : undefined;
648
+ const available = Boolean(resolved);
649
+ const reason = available
650
+ ? resolved === resolveProjectPath(projectDir, provider.manifest ?? '')
651
+ ? `manifest found at ${provider.manifest}`
652
+ : `manifest found via fallback at graph.json (configured: ${provider.manifest})`
653
+ : `manifest not found: ${provider.manifest ?? '(missing)'}`;
315
654
  return {
316
655
  id: provider.id,
317
656
  type: provider.type,
318
657
  enabled,
319
658
  available,
320
659
  capabilities,
321
- reason: available ? `manifest found at ${provider.manifest}` : `manifest not found: ${provider.manifest ?? '(missing)'}`,
660
+ reason,
322
661
  source: provider.source,
323
662
  installHint: provider.installHint,
324
663
  projectInitHint: provider.projectInitHint,
@@ -331,7 +670,9 @@ function providerStatus(projectDir) {
331
670
  ? projectIndexExists
332
671
  ? '; project index found at .codegraph/'
333
672
  : '; project index missing (.codegraph/)'
334
- : '';
673
+ : provider.id === 'code-review-graph'
674
+ ? '; run code-review-graph build to index the project'
675
+ : '';
335
676
  return {
336
677
  id: provider.id,
337
678
  type: provider.type,
@@ -529,6 +870,7 @@ function confidence(hitCount, fallbackUsed) {
529
870
  function recommendations(configExists, providers, fallbackAvailable, projectIndexExists) {
530
871
  const output = [];
531
872
  const codegraph = providers.find(provider => provider.id === 'codegraph');
873
+ const crg = providers.find(provider => provider.id === 'code-review-graph');
532
874
  if (!configExists)
533
875
  output.push('Run scale codegraph init to create .scale/code-intelligence.json.');
534
876
  if (codegraph && !codegraph.available && codegraph.installHint) {
@@ -537,6 +879,9 @@ function recommendations(configExists, providers, fallbackAvailable, projectInde
537
879
  if (codegraph?.available && !projectIndexExists) {
538
880
  output.push('Run codegraph init -i in the project root to build the local .codegraph/ index.');
539
881
  }
882
+ if (crg && !crg.available && crg.installHint) {
883
+ output.push(`Install code-review-graph from ${crg.source ?? CRG_SOURCE}: ${crg.installHint}.`);
884
+ }
540
885
  if (providers.every(provider => !provider.available))
541
886
  output.push('No graph provider is available; exploration will use explicit fallback.');
542
887
  if (!fallbackAvailable)
@@ -584,7 +929,9 @@ function hydrateProviderConfig(provider) {
584
929
  ? DEFAULT_CODEGRAPH_PROVIDER
585
930
  : provider.id === 'graphify'
586
931
  ? DEFAULT_GRAPHIFY_PROVIDER
587
- : undefined;
932
+ : provider.id === 'code-review-graph'
933
+ ? DEFAULT_CRG_PROVIDER
934
+ : undefined;
588
935
  return {
589
936
  ...(defaults ?? {}),
590
937
  ...provider,
@@ -678,4 +1025,109 @@ function queryExternalCodeGraphContext(options) {
678
1025
  return null;
679
1026
  }
680
1027
  }
1028
+ function crgCommandExists() {
1029
+ return commandExists('code-review-graph');
1030
+ }
1031
+ function runCRGCommand(args, projectDir) {
1032
+ try {
1033
+ const result = runExternalCommandSync('code-review-graph', args, {
1034
+ encoding: 'utf8',
1035
+ cwd: projectDir,
1036
+ stdio: ['ignore', 'pipe', 'pipe'],
1037
+ }, {
1038
+ execFileSync: execFileSyncImpl,
1039
+ spawnSync: childProcess.spawnSync,
1040
+ });
1041
+ return typeof result === 'string' ? result : String(result);
1042
+ }
1043
+ catch {
1044
+ return null;
1045
+ }
1046
+ }
1047
+ export function queryExternalCRG(options) {
1048
+ // code-review-graph exposes search via MCP tools, not CLI query.
1049
+ // Use detect-changes as a proxy for graph availability, and fall through
1050
+ // to the artifact provider (SQLite graph) for actual search.
1051
+ if (!crgCommandExists())
1052
+ return null;
1053
+ // Verify graph exists
1054
+ const output = runCRGCommand(['status'], options.projectDir);
1055
+ if (!output || !output.includes('Nodes:'))
1056
+ return { hits: [], warnings: ['code-review-graph graph not built; run code-review-graph build'] };
1057
+ // CRG search is MCP-only; return empty hits so the caller falls through to artifact or fallback
1058
+ return { hits: [], warnings: ['code-review-graph search is available via MCP tools only; use code-review-graph serve or code-review-graph mcp'] };
1059
+ }
1060
+ export function detectChangesCRG(options) {
1061
+ if (!crgCommandExists())
1062
+ return null;
1063
+ const args = ['detect-changes'];
1064
+ if (options.baseRef)
1065
+ args.push('--base', options.baseRef);
1066
+ const output = runCRGCommand(args, options.projectDir);
1067
+ if (!output)
1068
+ return null;
1069
+ try {
1070
+ const parsed = JSON.parse(output);
1071
+ const changedFiles = [];
1072
+ const affectedSymbols = [];
1073
+ const affectedTests = [];
1074
+ for (const fn of parsed.changed_functions ?? []) {
1075
+ const file = normalizeManifestPath(fn.file);
1076
+ if (file && !changedFiles.includes(file))
1077
+ changedFiles.push(file);
1078
+ if (fn.name && file)
1079
+ affectedSymbols.push({ symbol: fn.name, file, reason: 'changed function' });
1080
+ }
1081
+ for (const gap of parsed.test_gaps ?? []) {
1082
+ const file = normalizeManifestPath(gap.file);
1083
+ if (file)
1084
+ affectedTests.push(file);
1085
+ }
1086
+ for (const priority of parsed.review_priorities ?? []) {
1087
+ const file = normalizeManifestPath(priority.file);
1088
+ if (file && !changedFiles.includes(file))
1089
+ changedFiles.push(file);
1090
+ }
1091
+ return {
1092
+ changedFiles,
1093
+ affectedSymbols,
1094
+ affectedTests,
1095
+ blastRadiusFiles: changedFiles,
1096
+ provider: 'code-review-graph',
1097
+ };
1098
+ }
1099
+ catch {
1100
+ return null;
1101
+ }
1102
+ }
1103
+ export function reviewContextCRG(options) {
1104
+ if (!crgCommandExists())
1105
+ return null;
1106
+ // Use detect-changes for token savings overview
1107
+ const output = runCRGCommand(['detect-changes'], options.projectDir);
1108
+ if (!output)
1109
+ return null;
1110
+ try {
1111
+ const parsed = JSON.parse(output);
1112
+ const files = (parsed.changed_functions ?? [])
1113
+ .map(fn => normalizeManifestPath(fn.file))
1114
+ .filter(Boolean);
1115
+ const savedTokens = parsed.context_savings?.saved_tokens ?? 0;
1116
+ const savedPercent = parsed.context_savings?.saved_percent ?? 0;
1117
+ const naiveCorpus = savedTokens > 0 && savedPercent > 0 && savedPercent < 100
1118
+ ? Math.round(savedTokens / (1 - savedPercent / 100))
1119
+ : savedTokens > 0 ? savedTokens + 70 : 0;
1120
+ const graphQuery = naiveCorpus > savedTokens ? naiveCorpus - savedTokens : 70;
1121
+ const reduction = naiveCorpus > 0 ? Math.round(naiveCorpus / graphQuery) : 1;
1122
+ return {
1123
+ files,
1124
+ blastRadius: [],
1125
+ tokenSavings: { naiveCorpus, graphQuery, reduction },
1126
+ provider: 'code-review-graph',
1127
+ };
1128
+ }
1129
+ catch {
1130
+ return null;
1131
+ }
1132
+ }
681
1133
  //# sourceMappingURL=CodeIntelligence.js.map