@optave/codegraph 3.1.4 → 3.1.5

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 (181) hide show
  1. package/README.md +26 -70
  2. package/package.json +10 -8
  3. package/src/ast-analysis/engine.js +32 -12
  4. package/src/ast-analysis/shared.js +2 -2
  5. package/src/cli/commands/ast.js +2 -6
  6. package/src/cli/commands/audit.js +9 -10
  7. package/src/cli/commands/batch.js +4 -4
  8. package/src/cli/commands/branch-compare.js +1 -1
  9. package/src/cli/commands/build.js +1 -1
  10. package/src/cli/commands/cfg.js +3 -7
  11. package/src/cli/commands/check.js +12 -17
  12. package/src/cli/commands/children.js +3 -6
  13. package/src/cli/commands/co-change.js +5 -3
  14. package/src/cli/commands/communities.js +2 -6
  15. package/src/cli/commands/complexity.js +3 -2
  16. package/src/cli/commands/context.js +3 -7
  17. package/src/cli/commands/cycles.js +12 -8
  18. package/src/cli/commands/dataflow.js +3 -7
  19. package/src/cli/commands/deps.js +2 -6
  20. package/src/cli/commands/diff-impact.js +2 -6
  21. package/src/cli/commands/embed.js +1 -1
  22. package/src/cli/commands/export.js +34 -31
  23. package/src/cli/commands/exports.js +2 -6
  24. package/src/cli/commands/flow.js +3 -7
  25. package/src/cli/commands/fn-impact.js +3 -7
  26. package/src/cli/commands/impact.js +2 -6
  27. package/src/cli/commands/info.js +2 -2
  28. package/src/cli/commands/map.js +1 -1
  29. package/src/cli/commands/mcp.js +1 -1
  30. package/src/cli/commands/models.js +1 -1
  31. package/src/cli/commands/owners.js +1 -1
  32. package/src/cli/commands/path.js +2 -2
  33. package/src/cli/commands/plot.js +40 -31
  34. package/src/cli/commands/query.js +3 -7
  35. package/src/cli/commands/registry.js +2 -2
  36. package/src/cli/commands/roles.js +3 -7
  37. package/src/cli/commands/search.js +1 -1
  38. package/src/cli/commands/sequence.js +3 -7
  39. package/src/cli/commands/snapshot.js +6 -1
  40. package/src/cli/commands/stats.js +1 -1
  41. package/src/cli/commands/structure.js +5 -4
  42. package/src/cli/commands/triage.js +4 -4
  43. package/src/cli/commands/watch.js +1 -1
  44. package/src/cli/commands/where.js +2 -6
  45. package/src/cli/index.js +11 -5
  46. package/src/cli/shared/open-graph.js +13 -0
  47. package/src/cli/shared/options.js +22 -2
  48. package/src/cli.js +1 -1
  49. package/src/db/connection.js +127 -4
  50. package/src/{db.js → db/index.js} +12 -5
  51. package/src/db/migrations.js +1 -1
  52. package/src/db/query-builder.js +15 -8
  53. package/src/db/repository/base.js +1 -1
  54. package/src/db/repository/graph-read.js +3 -3
  55. package/src/db/repository/in-memory-repository.js +4 -13
  56. package/src/db/repository/nodes.js +3 -8
  57. package/src/{analysis → domain/analysis}/context.js +6 -6
  58. package/src/{analysis → domain/analysis}/dependencies.js +5 -5
  59. package/src/{analysis → domain/analysis}/exports.js +8 -4
  60. package/src/{analysis → domain/analysis}/impact.js +61 -58
  61. package/src/{analysis → domain/analysis}/module-map.js +3 -3
  62. package/src/{analysis → domain/analysis}/roles.js +4 -4
  63. package/src/{analysis → domain/analysis}/symbol-lookup.js +13 -7
  64. package/src/{builder → domain/graph/builder}/helpers.js +3 -3
  65. package/src/{builder → domain/graph/builder}/incremental.js +3 -3
  66. package/src/{builder → domain/graph/builder}/pipeline.js +4 -4
  67. package/src/{builder → domain/graph/builder}/stages/build-edges.js +2 -2
  68. package/src/{builder → domain/graph/builder}/stages/build-structure.js +4 -4
  69. package/src/{builder → domain/graph/builder}/stages/collect-files.js +2 -2
  70. package/src/{builder → domain/graph/builder}/stages/detect-changes.js +6 -6
  71. package/src/{builder → domain/graph/builder}/stages/finalize.js +4 -4
  72. package/src/{builder → domain/graph/builder}/stages/insert-nodes.js +1 -1
  73. package/src/{builder → domain/graph/builder}/stages/parse-files.js +2 -2
  74. package/src/{builder → domain/graph/builder}/stages/resolve-imports.js +1 -1
  75. package/src/{builder → domain/graph/builder}/stages/run-analyses.js +2 -2
  76. package/src/{change-journal.js → domain/graph/change-journal.js} +1 -1
  77. package/src/{cycles.js → domain/graph/cycles.js} +4 -4
  78. package/src/{journal.js → domain/graph/journal.js} +1 -1
  79. package/src/{resolve.js → domain/graph/resolve.js} +2 -2
  80. package/src/{watcher.js → domain/graph/watcher.js} +5 -5
  81. package/src/{parser.js → domain/parser.js} +5 -5
  82. package/src/{queries.js → domain/queries.js} +16 -16
  83. package/src/{embeddings → domain/search}/generator.js +3 -3
  84. package/src/{embeddings → domain/search}/models.js +2 -2
  85. package/src/{embeddings → domain/search}/search/cli-formatter.js +1 -1
  86. package/src/{embeddings → domain/search}/search/hybrid.js +1 -1
  87. package/src/{embeddings → domain/search}/search/keyword.js +1 -1
  88. package/src/{embeddings → domain/search}/search/prepare.js +2 -2
  89. package/src/{embeddings → domain/search}/search/semantic.js +1 -1
  90. package/src/{embeddings → domain/search}/strategies/structured.js +1 -1
  91. package/src/extractors/javascript.js +1 -1
  92. package/src/{ast.js → features/ast.js} +8 -8
  93. package/src/{audit.js → features/audit.js} +16 -44
  94. package/src/{batch.js → features/batch.js} +5 -5
  95. package/src/{boundaries.js → features/boundaries.js} +2 -2
  96. package/src/{branch-compare.js → features/branch-compare.js} +3 -3
  97. package/src/{cfg.js → features/cfg.js} +10 -10
  98. package/src/{check.js → features/check.js} +13 -30
  99. package/src/{cochange.js → features/cochange.js} +5 -5
  100. package/src/{communities.js → features/communities.js} +7 -7
  101. package/src/{complexity.js → features/complexity.js} +13 -13
  102. package/src/{dataflow.js → features/dataflow.js} +11 -11
  103. package/src/{export.js → features/export.js} +3 -3
  104. package/src/{flow.js → features/flow.js} +4 -4
  105. package/src/{viewer.js → features/graph-enrichment.js} +6 -6
  106. package/src/{manifesto.js → features/manifesto.js} +6 -6
  107. package/src/{owners.js → features/owners.js} +2 -2
  108. package/src/{sequence.js → features/sequence.js} +15 -15
  109. package/src/{snapshot.js → features/snapshot.js} +3 -3
  110. package/src/{structure.js → features/structure.js} +7 -7
  111. package/src/{triage.js → features/triage.js} +8 -8
  112. package/src/graph/builders/dependency.js +33 -14
  113. package/src/index.cjs +16 -0
  114. package/src/index.js +39 -39
  115. package/src/{native.js → infrastructure/native.js} +1 -1
  116. package/src/mcp/middleware.js +1 -1
  117. package/src/mcp/server.js +5 -5
  118. package/src/mcp/tool-registry.js +2 -2
  119. package/src/mcp/tools/ast-query.js +1 -1
  120. package/src/mcp/tools/audit.js +1 -1
  121. package/src/mcp/tools/batch-query.js +1 -1
  122. package/src/mcp/tools/branch-compare.js +3 -1
  123. package/src/mcp/tools/cfg.js +1 -1
  124. package/src/mcp/tools/check.js +3 -3
  125. package/src/mcp/tools/co-changes.js +1 -1
  126. package/src/mcp/tools/code-owners.js +1 -1
  127. package/src/mcp/tools/communities.js +1 -1
  128. package/src/mcp/tools/complexity.js +1 -1
  129. package/src/mcp/tools/dataflow.js +2 -2
  130. package/src/mcp/tools/execution-flow.js +2 -2
  131. package/src/mcp/tools/export-graph.js +2 -2
  132. package/src/mcp/tools/find-cycles.js +2 -2
  133. package/src/mcp/tools/list-repos.js +1 -1
  134. package/src/mcp/tools/sequence.js +1 -1
  135. package/src/mcp/tools/structure.js +1 -1
  136. package/src/mcp/tools/triage.js +2 -2
  137. package/src/{commands → presentation}/audit.js +2 -2
  138. package/src/{commands → presentation}/batch.js +1 -1
  139. package/src/{commands → presentation}/branch-compare.js +2 -2
  140. package/src/{commands → presentation}/cfg.js +1 -1
  141. package/src/{commands → presentation}/check.js +2 -2
  142. package/src/{commands → presentation}/communities.js +1 -1
  143. package/src/{commands → presentation}/complexity.js +1 -1
  144. package/src/{commands → presentation}/dataflow.js +1 -1
  145. package/src/{commands → presentation}/flow.js +2 -2
  146. package/src/{commands → presentation}/manifesto.js +1 -1
  147. package/src/{commands → presentation}/owners.js +1 -1
  148. package/src/presentation/queries-cli/exports.js +46 -0
  149. package/src/presentation/queries-cli/impact.js +198 -0
  150. package/src/presentation/queries-cli/index.js +5 -0
  151. package/src/presentation/queries-cli/inspect.js +334 -0
  152. package/src/presentation/queries-cli/overview.js +197 -0
  153. package/src/presentation/queries-cli/path.js +58 -0
  154. package/src/presentation/queries-cli.js +27 -0
  155. package/src/{commands → presentation}/query.js +1 -1
  156. package/src/presentation/result-formatter.js +126 -3
  157. package/src/{commands → presentation}/sequence.js +2 -2
  158. package/src/{commands → presentation}/structure.js +1 -1
  159. package/src/{commands → presentation}/triage.js +1 -1
  160. package/src/{constants.js → shared/constants.js} +1 -1
  161. package/src/shared/file-utils.js +2 -2
  162. package/src/shared/generators.js +2 -2
  163. package/src/shared/hierarchy.js +1 -1
  164. package/src/mcp.js +0 -2
  165. package/src/queries-cli.js +0 -866
  166. /package/src/{builder → domain/graph/builder}/context.js +0 -0
  167. /package/src/{builder.js → domain/graph/builder.js} +0 -0
  168. /package/src/{embeddings → domain/search}/index.js +0 -0
  169. /package/src/{embeddings → domain/search}/search/filters.js +0 -0
  170. /package/src/{embeddings → domain/search}/stores/fts5.js +0 -0
  171. /package/src/{embeddings → domain/search}/stores/sqlite-blob.js +0 -0
  172. /package/src/{embeddings → domain/search}/strategies/source.js +0 -0
  173. /package/src/{embeddings → domain/search}/strategies/text-utils.js +0 -0
  174. /package/src/{config.js → infrastructure/config.js} +0 -0
  175. /package/src/{logger.js → infrastructure/logger.js} +0 -0
  176. /package/src/{registry.js → infrastructure/registry.js} +0 -0
  177. /package/src/{update-check.js → infrastructure/update-check.js} +0 -0
  178. /package/src/{commands → presentation}/cochange.js +0 -0
  179. /package/src/{errors.js → shared/errors.js} +0 -0
  180. /package/src/{kinds.js → shared/kinds.js} +0 -0
  181. /package/src/{paginate.js → shared/paginate.js} +0 -0
@@ -11,9 +11,9 @@ import fs from 'node:fs';
11
11
  import os from 'node:os';
12
12
  import path from 'node:path';
13
13
  import Database from 'better-sqlite3';
14
- import { buildGraph } from './builder.js';
15
- import { isTestFile } from './infrastructure/test-filter.js';
16
- import { kindIcon } from './queries.js';
14
+ import { buildGraph } from '../domain/graph/builder.js';
15
+ import { kindIcon } from '../domain/queries.js';
16
+ import { isTestFile } from '../infrastructure/test-filter.js';
17
17
 
18
18
  // ─── Git Helpers ────────────────────────────────────────────────────────
19
19
 
@@ -7,14 +7,14 @@
7
7
 
8
8
  import fs from 'node:fs';
9
9
  import path from 'node:path';
10
- import { CFG_RULES } from './ast-analysis/rules/index.js';
10
+ import { CFG_RULES } from '../ast-analysis/rules/index.js';
11
11
  import {
12
12
  makeCfgRules as _makeCfgRules,
13
13
  buildExtensionSet,
14
14
  buildExtToLangMap,
15
- } from './ast-analysis/shared.js';
16
- import { walkWithVisitors } from './ast-analysis/visitor.js';
17
- import { createCfgVisitor } from './ast-analysis/visitors/cfg-visitor.js';
15
+ } from '../ast-analysis/shared.js';
16
+ import { walkWithVisitors } from '../ast-analysis/visitor.js';
17
+ import { createCfgVisitor } from '../ast-analysis/visitors/cfg-visitor.js';
18
18
  import {
19
19
  deleteCfgForNode,
20
20
  getCfgBlocks,
@@ -22,10 +22,10 @@ import {
22
22
  getFunctionNodeId,
23
23
  hasCfgTables,
24
24
  openReadonlyOrFail,
25
- } from './db.js';
26
- import { isTestFile } from './infrastructure/test-filter.js';
27
- import { info } from './logger.js';
28
- import { paginateResult } from './paginate.js';
25
+ } from '../db/index.js';
26
+ import { info } from '../infrastructure/logger.js';
27
+ import { isTestFile } from '../infrastructure/test-filter.js';
28
+ import { paginateResult } from '../shared/paginate.js';
29
29
 
30
30
  // Re-export for backward compatibility
31
31
  export { _makeCfgRules as makeCfgRules, CFG_RULES };
@@ -104,13 +104,13 @@ export async function buildCFGData(db, fileSymbols, rootDir, _engineOpts) {
104
104
  }
105
105
 
106
106
  if (needsFallback) {
107
- const { createParsers } = await import('./parser.js');
107
+ const { createParsers } = await import('../domain/parser.js');
108
108
  parsers = await createParsers();
109
109
  }
110
110
 
111
111
  let getParserFn = null;
112
112
  if (parsers) {
113
- const mod = await import('./parser.js');
113
+ const mod = await import('../domain/parser.js');
114
114
  getParserFn = mod.getParser;
115
115
  }
116
116
 
@@ -1,10 +1,11 @@
1
1
  import { execFileSync } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import { loadConfig } from './config.js';
5
- import { findCycles } from './cycles.js';
6
- import { findDbPath, openReadonlyOrFail } from './db.js';
7
- import { isTestFile } from './infrastructure/test-filter.js';
4
+ import { findDbPath, openReadonlyOrFail } from '../db/index.js';
5
+ import { bfsTransitiveCallers } from '../domain/analysis/impact.js';
6
+ import { findCycles } from '../domain/graph/cycles.js';
7
+ import { loadConfig } from '../infrastructure/config.js';
8
+ import { isTestFile } from '../infrastructure/test-filter.js';
8
9
  import { matchOwners, parseCodeowners } from './owners.js';
9
10
 
10
11
  // ─── Diff Parser ──────────────────────────────────────────────────────
@@ -96,31 +97,10 @@ export function checkMaxBlastRadius(db, changedRanges, threshold, noTests, maxDe
96
97
  }
97
98
  if (!overlaps) continue;
98
99
 
99
- // BFS transitive callers
100
- const visited = new Set([def.id]);
101
- let frontier = [def.id];
102
- let totalCallers = 0;
103
- for (let d = 1; d <= maxDepth; d++) {
104
- const nextFrontier = [];
105
- for (const fid of frontier) {
106
- const callers = db
107
- .prepare(
108
- `SELECT DISTINCT n.id, n.name, n.kind, n.file, n.line
109
- FROM edges e JOIN nodes n ON e.source_id = n.id
110
- WHERE e.target_id = ? AND e.kind = 'calls'`,
111
- )
112
- .all(fid);
113
- for (const c of callers) {
114
- if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
115
- visited.add(c.id);
116
- nextFrontier.push(c.id);
117
- totalCallers++;
118
- }
119
- }
120
- }
121
- frontier = nextFrontier;
122
- if (frontier.length === 0) break;
123
- }
100
+ const { totalDependents: totalCallers } = bfsTransitiveCallers(db, def.id, {
101
+ noTests,
102
+ maxDepth,
103
+ });
124
104
 
125
105
  if (totalCallers > maxFound) maxFound = totalCallers;
126
106
  if (totalCallers > threshold) {
@@ -240,7 +220,10 @@ export function checkData(customDbPath, opts = {}) {
240
220
  const maxDepth = opts.depth || 3;
241
221
 
242
222
  // Load config defaults for check predicates
243
- const config = loadConfig(repoRoot);
223
+ // NOTE: opts.config is loaded from process.cwd() at startup (via CLI context),
224
+ // which may differ from the DB's parent repo root when --db points to an external
225
+ // project. This is an acceptable trade-off to avoid duplicate I/O on the hot path.
226
+ const config = opts.config || loadConfig(repoRoot);
244
227
  const checkConfig = config.check || {};
245
228
 
246
229
  // Resolve which predicates are enabled: CLI flags ?? config ?? built-in defaults
@@ -8,11 +8,11 @@
8
8
  import { execFileSync } from 'node:child_process';
9
9
  import fs from 'node:fs';
10
10
  import path from 'node:path';
11
- import { normalizePath } from './constants.js';
12
- import { closeDb, findDbPath, initSchema, openDb, openReadonlyOrFail } from './db.js';
13
- import { isTestFile } from './infrastructure/test-filter.js';
14
- import { warn } from './logger.js';
15
- import { paginateResult } from './paginate.js';
11
+ import { closeDb, findDbPath, initSchema, openDb, openReadonlyOrFail } from '../db/index.js';
12
+ import { warn } from '../infrastructure/logger.js';
13
+ import { isTestFile } from '../infrastructure/test-filter.js';
14
+ import { normalizePath } from '../shared/constants.js';
15
+ import { paginateResult } from '../shared/paginate.js';
16
16
 
17
17
  /**
18
18
  * Scan git history and return parsed commit data.
@@ -1,8 +1,8 @@
1
1
  import path from 'node:path';
2
- import { openReadonlyOrFail } from './db.js';
3
- import { louvainCommunities } from './graph/algorithms/louvain.js';
4
- import { buildDependencyGraph } from './graph/builders/dependency.js';
5
- import { paginateResult } from './paginate.js';
2
+ import { openRepo } from '../db/index.js';
3
+ import { louvainCommunities } from '../graph/algorithms/louvain.js';
4
+ import { buildDependencyGraph } from '../graph/builders/dependency.js';
5
+ import { paginateResult } from '../shared/paginate.js';
6
6
 
7
7
  // ─── Directory Helpers ────────────────────────────────────────────────
8
8
 
@@ -26,15 +26,15 @@ function getDirectory(filePath) {
26
26
  * @returns {{ communities: object[], modularity: number, drift: object, summary: object }}
27
27
  */
28
28
  export function communitiesData(customDbPath, opts = {}) {
29
- const db = openReadonlyOrFail(customDbPath);
29
+ const { repo, close } = openRepo(customDbPath, opts);
30
30
  let graph;
31
31
  try {
32
- graph = buildDependencyGraph(db, {
32
+ graph = buildDependencyGraph(repo, {
33
33
  fileLevel: !opts.functions,
34
34
  noTests: opts.noTests,
35
35
  });
36
36
  } finally {
37
- db.close();
37
+ close();
38
38
  }
39
39
 
40
40
  // Handle empty or trivial graphs
@@ -3,20 +3,20 @@ import path from 'node:path';
3
3
  import {
4
4
  computeLOCMetrics as _computeLOCMetrics,
5
5
  computeMaintainabilityIndex as _computeMaintainabilityIndex,
6
- } from './ast-analysis/metrics.js';
7
- import { COMPLEXITY_RULES, HALSTEAD_RULES } from './ast-analysis/rules/index.js';
6
+ } from '../ast-analysis/metrics.js';
7
+ import { COMPLEXITY_RULES, HALSTEAD_RULES } from '../ast-analysis/rules/index.js';
8
8
  import {
9
9
  findFunctionNode as _findFunctionNode,
10
10
  buildExtensionSet,
11
11
  buildExtToLangMap,
12
- } from './ast-analysis/shared.js';
13
- import { walkWithVisitors } from './ast-analysis/visitor.js';
14
- import { createComplexityVisitor } from './ast-analysis/visitors/complexity-visitor.js';
15
- import { loadConfig } from './config.js';
16
- import { getFunctionNodeId, openReadonlyOrFail } from './db.js';
17
- import { isTestFile } from './infrastructure/test-filter.js';
18
- import { info } from './logger.js';
19
- import { paginateResult } from './paginate.js';
12
+ } from '../ast-analysis/shared.js';
13
+ import { walkWithVisitors } from '../ast-analysis/visitor.js';
14
+ import { createComplexityVisitor } from '../ast-analysis/visitors/complexity-visitor.js';
15
+ import { getFunctionNodeId, openReadonlyOrFail } from '../db/index.js';
16
+ import { loadConfig } from '../infrastructure/config.js';
17
+ import { info } from '../infrastructure/logger.js';
18
+ import { isTestFile } from '../infrastructure/test-filter.js';
19
+ import { paginateResult } from '../shared/paginate.js';
20
20
 
21
21
  // Re-export rules for backward compatibility
22
22
  export { COMPLEXITY_RULES, HALSTEAD_RULES };
@@ -360,12 +360,12 @@ export async function buildComplexityMetrics(db, fileSymbols, rootDir, _engineOp
360
360
  }
361
361
  }
362
362
  if (needsFallback) {
363
- const { createParsers } = await import('./parser.js');
363
+ const { createParsers } = await import('../domain/parser.js');
364
364
  parsers = await createParsers();
365
365
  extToLang = buildExtToLangMap();
366
366
  }
367
367
 
368
- const { getParser } = await import('./parser.js');
368
+ const { getParser } = await import('../domain/parser.js');
369
369
 
370
370
  const upsert = db.prepare(
371
371
  `INSERT OR REPLACE INTO function_complexity
@@ -524,7 +524,7 @@ export function complexityData(customDbPath, opts = {}) {
524
524
  const kindFilter = opts.kind || null;
525
525
 
526
526
  // Load thresholds from config
527
- const config = loadConfig(process.cwd());
527
+ const config = opts.config || loadConfig(process.cwd());
528
528
  const thresholds = config.manifesto?.rules || {
529
529
  cognitive: { warn: 15, fail: null },
530
530
  cyclomatic: { warn: 10, fail: null },
@@ -11,19 +11,19 @@
11
11
 
12
12
  import fs from 'node:fs';
13
13
  import path from 'node:path';
14
- import { DATAFLOW_RULES } from './ast-analysis/rules/index.js';
14
+ import { DATAFLOW_RULES } from '../ast-analysis/rules/index.js';
15
15
  import {
16
16
  makeDataflowRules as _makeDataflowRules,
17
17
  buildExtensionSet,
18
18
  buildExtToLangMap,
19
- } from './ast-analysis/shared.js';
20
- import { walkWithVisitors } from './ast-analysis/visitor.js';
21
- import { createDataflowVisitor } from './ast-analysis/visitors/dataflow-visitor.js';
22
- import { hasDataflowTable, openReadonlyOrFail } from './db.js';
23
- import { isTestFile } from './infrastructure/test-filter.js';
24
- import { info } from './logger.js';
25
- import { paginateResult } from './paginate.js';
26
- import { ALL_SYMBOL_KINDS, normalizeSymbol } from './queries.js';
19
+ } from '../ast-analysis/shared.js';
20
+ import { walkWithVisitors } from '../ast-analysis/visitor.js';
21
+ import { createDataflowVisitor } from '../ast-analysis/visitors/dataflow-visitor.js';
22
+ import { hasDataflowTable, openReadonlyOrFail } from '../db/index.js';
23
+ import { ALL_SYMBOL_KINDS, normalizeSymbol } from '../domain/queries.js';
24
+ import { info } from '../infrastructure/logger.js';
25
+ import { isTestFile } from '../infrastructure/test-filter.js';
26
+ import { paginateResult } from '../shared/paginate.js';
27
27
 
28
28
  // Re-export for backward compatibility
29
29
  export { _makeDataflowRules as makeDataflowRules, DATAFLOW_RULES };
@@ -88,13 +88,13 @@ export async function buildDataflowEdges(db, fileSymbols, rootDir, _engineOpts)
88
88
  }
89
89
 
90
90
  if (needsFallback) {
91
- const { createParsers } = await import('./parser.js');
91
+ const { createParsers } = await import('../domain/parser.js');
92
92
  parsers = await createParsers();
93
93
  }
94
94
 
95
95
  let getParserFn = null;
96
96
  if (parsers) {
97
- const mod = await import('./parser.js');
97
+ const mod = await import('../domain/parser.js');
98
98
  getParserFn = mod.getParser;
99
99
  }
100
100
 
@@ -1,6 +1,5 @@
1
1
  import path from 'node:path';
2
- import { isTestFile } from './infrastructure/test-filter.js';
3
- import { paginateResult } from './paginate.js';
2
+ import { isTestFile } from '../infrastructure/test-filter.js';
4
3
  import {
5
4
  renderFileLevelDOT,
6
5
  renderFileLevelGraphML,
@@ -10,7 +9,8 @@ import {
10
9
  renderFunctionLevelGraphML,
11
10
  renderFunctionLevelMermaid,
12
11
  renderFunctionLevelNeo4jCSV,
13
- } from './presentation/export.js';
12
+ } from '../presentation/export.js';
13
+ import { paginateResult } from '../shared/paginate.js';
14
14
 
15
15
  const DEFAULT_MIN_CONFIDENCE = 0.5;
16
16
 
@@ -5,10 +5,10 @@
5
5
  * framework entry points (routes, commands, events) through their call chains.
6
6
  */
7
7
 
8
- import { openReadonlyOrFail } from './db.js';
9
- import { isTestFile } from './infrastructure/test-filter.js';
10
- import { paginateResult } from './paginate.js';
11
- import { CORE_SYMBOL_KINDS, findMatchingNodes } from './queries.js';
8
+ import { openReadonlyOrFail } from '../db/index.js';
9
+ import { CORE_SYMBOL_KINDS, findMatchingNodes } from '../domain/queries.js';
10
+ import { isTestFile } from '../infrastructure/test-filter.js';
11
+ import { paginateResult } from '../shared/paginate.js';
12
12
  import { FRAMEWORK_ENTRY_PREFIXES } from './structure.js';
13
13
 
14
14
  /**
@@ -1,16 +1,16 @@
1
1
  import path from 'node:path';
2
- import { louvainCommunities } from './graph/algorithms/louvain.js';
3
- import { CodeGraph } from './graph/model.js';
4
- import { isTestFile } from './infrastructure/test-filter.js';
2
+ import { louvainCommunities } from '../graph/algorithms/louvain.js';
3
+ import { CodeGraph } from '../graph/model.js';
4
+ import { isTestFile } from '../infrastructure/test-filter.js';
5
5
  import {
6
6
  COMMUNITY_COLORS,
7
7
  DEFAULT_NODE_COLORS,
8
8
  DEFAULT_ROLE_COLORS,
9
- } from './presentation/colors.js';
10
- import { DEFAULT_CONFIG, renderPlotHTML } from './presentation/viewer.js';
9
+ } from '../presentation/colors.js';
10
+ import { DEFAULT_CONFIG, renderPlotHTML } from '../presentation/viewer.js';
11
11
 
12
12
  // Re-export presentation utilities for backward compatibility
13
- export { loadPlotConfig } from './presentation/viewer.js';
13
+ export { loadPlotConfig } from '../presentation/viewer.js';
14
14
 
15
15
  const DEFAULT_MIN_CONFIDENCE = 0.5;
16
16
 
@@ -1,9 +1,9 @@
1
+ import { openReadonlyOrFail } from '../db/index.js';
2
+ import { findCycles } from '../domain/graph/cycles.js';
3
+ import { loadConfig } from '../infrastructure/config.js';
4
+ import { debug } from '../infrastructure/logger.js';
5
+ import { paginateResult } from '../shared/paginate.js';
1
6
  import { evaluateBoundaries } from './boundaries.js';
2
- import { loadConfig } from './config.js';
3
- import { findCycles } from './cycles.js';
4
- import { openReadonlyOrFail } from './db.js';
5
- import { debug } from './logger.js';
6
- import { paginateResult } from './paginate.js';
7
7
 
8
8
  // ─── Rule Definitions ─────────────────────────────────────────────────
9
9
 
@@ -395,7 +395,7 @@ export function manifestoData(customDbPath, opts = {}) {
395
395
  const db = openReadonlyOrFail(customDbPath);
396
396
 
397
397
  try {
398
- const config = loadConfig(process.cwd());
398
+ const config = opts.config || loadConfig(process.cwd());
399
399
  const rules = resolveRules(config.manifesto?.rules);
400
400
 
401
401
  const violations = [];
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { findDbPath, openReadonlyOrFail } from './db.js';
4
- import { isTestFile } from './infrastructure/test-filter.js';
3
+ import { findDbPath, openReadonlyOrFail } from '../db/index.js';
4
+ import { isTestFile } from '../infrastructure/test-filter.js';
5
5
 
6
6
  // ─── CODEOWNERS Parsing ──────────────────────────────────────────────
7
7
 
@@ -6,10 +6,11 @@
6
6
  * sequence-diagram conventions.
7
7
  */
8
8
 
9
- import { findCallees, openReadonlyOrFail } from './db.js';
10
- import { isTestFile } from './infrastructure/test-filter.js';
11
- import { paginateResult } from './paginate.js';
12
- import { findMatchingNodes } from './queries.js';
9
+ import { openRepo } from '../db/index.js';
10
+ import { SqliteRepository } from '../db/repository/sqlite-repository.js';
11
+ import { findMatchingNodes } from '../domain/queries.js';
12
+ import { isTestFile } from '../infrastructure/test-filter.js';
13
+ import { paginateResult } from '../shared/paginate.js';
13
14
  import { FRAMEWORK_ENTRY_PREFIXES } from './structure.js';
14
15
 
15
16
  // ─── Alias generation ────────────────────────────────────────────────
@@ -85,19 +86,19 @@ function buildAliases(files) {
85
86
  * @returns {{ entry, participants, messages, depth, totalMessages, truncated }}
86
87
  */
87
88
  export function sequenceData(name, dbPath, opts = {}) {
88
- const db = openReadonlyOrFail(dbPath);
89
+ const { repo, close } = openRepo(dbPath, opts);
89
90
  try {
90
91
  const maxDepth = opts.depth || 10;
91
92
  const noTests = opts.noTests || false;
92
93
  const withDataflow = opts.dataflow || false;
93
94
 
94
95
  // Phase 1: Direct LIKE match
95
- let matchNode = findMatchingNodes(db, name, opts)[0] ?? null;
96
+ let matchNode = findMatchingNodes(repo, name, opts)[0] ?? null;
96
97
 
97
98
  // Phase 2: Prefix-stripped matching
98
99
  if (!matchNode) {
99
100
  for (const prefix of FRAMEWORK_ENTRY_PREFIXES) {
100
- matchNode = findMatchingNodes(db, `${prefix}${name}`, opts)[0] ?? null;
101
+ matchNode = findMatchingNodes(repo, `${prefix}${name}`, opts)[0] ?? null;
101
102
  if (matchNode) break;
102
103
  }
103
104
  }
@@ -133,7 +134,7 @@ export function sequenceData(name, dbPath, opts = {}) {
133
134
  const nextFrontier = [];
134
135
 
135
136
  for (const fid of frontier) {
136
- const callees = findCallees(db, fid);
137
+ const callees = repo.findCallees(fid);
137
138
 
138
139
  const caller = idToNode.get(fid);
139
140
 
@@ -163,18 +164,17 @@ export function sequenceData(name, dbPath, opts = {}) {
163
164
 
164
165
  if (d === maxDepth && frontier.length > 0) {
165
166
  // Only mark truncated if at least one frontier node has further callees
166
- const hasMoreCalls = frontier.some((fid) => findCallees(db, fid).length > 0);
167
+ const hasMoreCalls = frontier.some((fid) => repo.findCallees(fid).length > 0);
167
168
  if (hasMoreCalls) truncated = true;
168
169
  }
169
170
  }
170
171
 
171
172
  // Dataflow annotations: add return arrows
172
173
  if (withDataflow && messages.length > 0) {
173
- const hasTable = db
174
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='dataflow'")
175
- .get();
174
+ const hasTable = repo.hasDataflowTable();
176
175
 
177
- if (hasTable) {
176
+ if (hasTable && repo instanceof SqliteRepository) {
177
+ const db = repo.db;
178
178
  // Build name|file lookup for O(1) target node access
179
179
  const nodeByNameFile = new Map();
180
180
  for (const n of idToNode.values()) {
@@ -281,9 +281,9 @@ export function sequenceData(name, dbPath, opts = {}) {
281
281
  }
282
282
  return result;
283
283
  } finally {
284
- db.close();
284
+ close();
285
285
  }
286
286
  }
287
287
 
288
288
  // Re-export Mermaid renderer from presentation layer
289
- export { sequenceToMermaid } from './presentation/sequence-renderer.js';
289
+ export { sequenceToMermaid } from '../presentation/sequence-renderer.js';
@@ -1,9 +1,9 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import Database from 'better-sqlite3';
4
- import { findDbPath } from './db.js';
5
- import { ConfigError, DbError } from './errors.js';
6
- import { debug } from './logger.js';
4
+ import { findDbPath } from '../db/index.js';
5
+ import { debug } from '../infrastructure/logger.js';
6
+ import { ConfigError, DbError } from '../shared/errors.js';
7
7
 
8
8
  const NAME_RE = /^[a-zA-Z0-9_-]+$/;
9
9
 
@@ -1,9 +1,9 @@
1
1
  import path from 'node:path';
2
- import { normalizePath } from './constants.js';
3
- import { getNodeId, openReadonlyOrFail, testFilterSQL } from './db.js';
4
- import { isTestFile } from './infrastructure/test-filter.js';
5
- import { debug } from './logger.js';
6
- import { paginateResult } from './paginate.js';
2
+ import { getNodeId, openReadonlyOrFail, testFilterSQL } from '../db/index.js';
3
+ import { debug } from '../infrastructure/logger.js';
4
+ import { isTestFile } from '../infrastructure/test-filter.js';
5
+ import { normalizePath } from '../shared/constants.js';
6
+ import { paginateResult } from '../shared/paginate.js';
7
7
 
8
8
  // ─── Build-time: insert directory nodes, contains edges, and metrics ────
9
9
 
@@ -313,9 +313,9 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
313
313
  // ─── Node role classification ─────────────────────────────────────────
314
314
 
315
315
  // Re-export from classifier for backward compatibility
316
- export { FRAMEWORK_ENTRY_PREFIXES } from './graph/classifiers/roles.js';
316
+ export { FRAMEWORK_ENTRY_PREFIXES } from '../graph/classifiers/roles.js';
317
317
 
318
- import { classifyRoles } from './graph/classifiers/roles.js';
318
+ import { classifyRoles } from '../graph/classifiers/roles.js';
319
319
 
320
320
  export function classifyNodeRoles(db) {
321
321
  const rows = db
@@ -1,8 +1,8 @@
1
- import { findNodesForTriage, openReadonlyOrFail } from './db.js';
2
- import { DEFAULT_WEIGHTS, scoreRisk } from './graph/classifiers/risk.js';
3
- import { isTestFile } from './infrastructure/test-filter.js';
4
- import { warn } from './logger.js';
5
- import { paginateResult } from './paginate.js';
1
+ import { openRepo } from '../db/index.js';
2
+ import { DEFAULT_WEIGHTS, scoreRisk } from '../graph/classifiers/risk.js';
3
+ import { warn } from '../infrastructure/logger.js';
4
+ import { isTestFile } from '../infrastructure/test-filter.js';
5
+ import { paginateResult } from '../shared/paginate.js';
6
6
 
7
7
  // ─── Data Function ────────────────────────────────────────────────────
8
8
 
@@ -14,7 +14,7 @@ import { paginateResult } from './paginate.js';
14
14
  * @returns {{ items: object[], summary: object, _pagination?: object }}
15
15
  */
16
16
  export function triageData(customDbPath, opts = {}) {
17
- const db = openReadonlyOrFail(customDbPath);
17
+ const { repo, close } = openRepo(customDbPath, opts);
18
18
  try {
19
19
  const noTests = opts.noTests || false;
20
20
  const fileFilter = opts.file || null;
@@ -26,7 +26,7 @@ export function triageData(customDbPath, opts = {}) {
26
26
 
27
27
  let rows;
28
28
  try {
29
- rows = findNodesForTriage(db, {
29
+ rows = repo.findNodesForTriage({
30
30
  noTests,
31
31
  file: fileFilter,
32
32
  kind: kindFilter,
@@ -115,7 +115,7 @@ export function triageData(customDbPath, opts = {}) {
115
115
  offset: opts.offset,
116
116
  });
117
117
  } finally {
118
- db.close();
118
+ close();
119
119
  }
120
120
  }
121
121
 
@@ -3,32 +3,39 @@
3
3
  * Replaces inline graph construction in cycles.js, communities.js, viewer.js, export.js.
4
4
  */
5
5
 
6
- import { getCallableNodes, getCallEdges, getFileNodesAll, getImportEdges } from '../../db.js';
6
+ import {
7
+ getCallableNodes,
8
+ getCallEdges,
9
+ getFileNodesAll,
10
+ getImportEdges,
11
+ Repository,
12
+ } from '../../db/index.js';
7
13
  import { isTestFile } from '../../infrastructure/test-filter.js';
8
14
  import { CodeGraph } from '../model.js';
9
15
 
10
16
  /**
11
- * @param {object} db - Open better-sqlite3 database (readonly)
17
+ * @param {object} dbOrRepo - Open better-sqlite3 database (readonly) or a Repository instance
12
18
  * @param {object} [opts]
13
19
  * @param {boolean} [opts.fileLevel=true] - File-level (imports) or function-level (calls)
14
20
  * @param {boolean} [opts.noTests=false] - Exclude test files
15
21
  * @param {number} [opts.minConfidence] - Minimum edge confidence (function-level only)
16
22
  * @returns {CodeGraph}
17
23
  */
18
- export function buildDependencyGraph(db, opts = {}) {
24
+ export function buildDependencyGraph(dbOrRepo, opts = {}) {
19
25
  const fileLevel = opts.fileLevel !== false;
20
26
  const noTests = opts.noTests || false;
21
27
 
22
28
  if (fileLevel) {
23
- return buildFileLevelGraph(db, noTests);
29
+ return buildFileLevelGraph(dbOrRepo, noTests);
24
30
  }
25
- return buildFunctionLevelGraph(db, noTests, opts.minConfidence);
31
+ return buildFunctionLevelGraph(dbOrRepo, noTests, opts.minConfidence);
26
32
  }
27
33
 
28
- function buildFileLevelGraph(db, noTests) {
34
+ function buildFileLevelGraph(dbOrRepo, noTests) {
29
35
  const graph = new CodeGraph();
36
+ const isRepo = dbOrRepo instanceof Repository;
30
37
 
31
- let nodes = getFileNodesAll(db);
38
+ let nodes = isRepo ? dbOrRepo.getFileNodesAll() : getFileNodesAll(dbOrRepo);
32
39
  if (noTests) nodes = nodes.filter((n) => !isTestFile(n.file));
33
40
 
34
41
  const nodeIds = new Set();
@@ -37,7 +44,7 @@ function buildFileLevelGraph(db, noTests) {
37
44
  nodeIds.add(n.id);
38
45
  }
39
46
 
40
- const edges = getImportEdges(db);
47
+ const edges = isRepo ? dbOrRepo.getImportEdges() : getImportEdges(dbOrRepo);
41
48
  for (const e of edges) {
42
49
  if (!nodeIds.has(e.source_id) || !nodeIds.has(e.target_id)) continue;
43
50
  const src = String(e.source_id);
@@ -51,10 +58,11 @@ function buildFileLevelGraph(db, noTests) {
51
58
  return graph;
52
59
  }
53
60
 
54
- function buildFunctionLevelGraph(db, noTests, minConfidence) {
61
+ function buildFunctionLevelGraph(dbOrRepo, noTests, minConfidence) {
55
62
  const graph = new CodeGraph();
63
+ const isRepo = dbOrRepo instanceof Repository;
56
64
 
57
- let nodes = getCallableNodes(db);
65
+ let nodes = isRepo ? dbOrRepo.getCallableNodes() : getCallableNodes(dbOrRepo);
58
66
  if (noTests) nodes = nodes.filter((n) => !isTestFile(n.file));
59
67
 
60
68
  const nodeIds = new Set();
@@ -70,11 +78,22 @@ function buildFunctionLevelGraph(db, noTests, minConfidence) {
70
78
 
71
79
  let edges;
72
80
  if (minConfidence != null) {
73
- edges = db
74
- .prepare("SELECT source_id, target_id FROM edges WHERE kind = 'calls' AND confidence >= ?")
75
- .all(minConfidence);
81
+ if (isRepo) {
82
+ // Trade-off: Repository.getCallEdges() returns all call edges, so we
83
+ // filter in JS. This is O(all call edges) rather than the SQL path's
84
+ // indexed WHERE clause. Acceptable for current data sizes; a dedicated
85
+ // getCallEdgesByMinConfidence(threshold) method on the Repository
86
+ // interface would be the proper fix if this becomes a bottleneck.
87
+ edges = dbOrRepo
88
+ .getCallEdges()
89
+ .filter((e) => e.confidence != null && e.confidence >= minConfidence);
90
+ } else {
91
+ edges = dbOrRepo
92
+ .prepare("SELECT source_id, target_id FROM edges WHERE kind = 'calls' AND confidence >= ?")
93
+ .all(minConfidence);
94
+ }
76
95
  } else {
77
- edges = getCallEdges(db);
96
+ edges = isRepo ? dbOrRepo.getCallEdges() : getCallEdges(dbOrRepo);
78
97
  }
79
98
 
80
99
  for (const e of edges) {