@optave/codegraph 3.1.4 → 3.2.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 (210) hide show
  1. package/README.md +29 -72
  2. package/package.json +10 -8
  3. package/src/ast-analysis/engine.js +260 -246
  4. package/src/ast-analysis/shared.js +2 -14
  5. package/src/ast-analysis/visitors/cfg-visitor.js +635 -649
  6. package/src/ast-analysis/visitors/complexity-visitor.js +135 -139
  7. package/src/ast-analysis/visitors/dataflow-visitor.js +230 -224
  8. package/src/cli/commands/ast.js +4 -7
  9. package/src/cli/commands/audit.js +11 -11
  10. package/src/cli/commands/batch.js +6 -5
  11. package/src/cli/commands/branch-compare.js +1 -1
  12. package/src/cli/commands/brief.js +12 -0
  13. package/src/cli/commands/build.js +1 -1
  14. package/src/cli/commands/cfg.js +5 -8
  15. package/src/cli/commands/check.js +28 -36
  16. package/src/cli/commands/children.js +9 -7
  17. package/src/cli/commands/co-change.js +5 -3
  18. package/src/cli/commands/communities.js +2 -6
  19. package/src/cli/commands/complexity.js +5 -3
  20. package/src/cli/commands/context.js +9 -8
  21. package/src/cli/commands/cycles.js +12 -8
  22. package/src/cli/commands/dataflow.js +5 -8
  23. package/src/cli/commands/deps.js +9 -8
  24. package/src/cli/commands/diff-impact.js +2 -6
  25. package/src/cli/commands/embed.js +1 -1
  26. package/src/cli/commands/export.js +34 -31
  27. package/src/cli/commands/exports.js +2 -6
  28. package/src/cli/commands/flow.js +5 -8
  29. package/src/cli/commands/fn-impact.js +9 -8
  30. package/src/cli/commands/impact.js +2 -6
  31. package/src/cli/commands/info.js +2 -2
  32. package/src/cli/commands/map.js +1 -1
  33. package/src/cli/commands/mcp.js +1 -1
  34. package/src/cli/commands/models.js +1 -1
  35. package/src/cli/commands/owners.js +5 -3
  36. package/src/cli/commands/path.js +2 -2
  37. package/src/cli/commands/plot.js +40 -31
  38. package/src/cli/commands/query.js +9 -8
  39. package/src/cli/commands/registry.js +2 -2
  40. package/src/cli/commands/roles.js +5 -8
  41. package/src/cli/commands/search.js +9 -3
  42. package/src/cli/commands/sequence.js +5 -8
  43. package/src/cli/commands/snapshot.js +6 -1
  44. package/src/cli/commands/stats.js +1 -1
  45. package/src/cli/commands/structure.js +5 -4
  46. package/src/cli/commands/triage.js +41 -30
  47. package/src/cli/commands/watch.js +1 -1
  48. package/src/cli/commands/where.js +2 -6
  49. package/src/cli/index.js +11 -5
  50. package/src/cli/shared/open-graph.js +13 -0
  51. package/src/cli/shared/options.js +22 -2
  52. package/src/cli.js +1 -1
  53. package/src/db/connection.js +140 -11
  54. package/src/{db.js → db/index.js} +12 -5
  55. package/src/db/migrations.js +42 -65
  56. package/src/db/query-builder.js +72 -9
  57. package/src/db/repository/base.js +1 -1
  58. package/src/db/repository/graph-read.js +3 -3
  59. package/src/db/repository/in-memory-repository.js +30 -28
  60. package/src/db/repository/nodes.js +10 -17
  61. package/src/domain/analysis/brief.js +155 -0
  62. package/src/domain/analysis/context.js +392 -0
  63. package/src/domain/analysis/dependencies.js +395 -0
  64. package/src/{analysis → domain/analysis}/exports.js +11 -6
  65. package/src/domain/analysis/impact.js +581 -0
  66. package/src/domain/analysis/module-map.js +348 -0
  67. package/src/{analysis → domain/analysis}/roles.js +12 -9
  68. package/src/{analysis → domain/analysis}/symbol-lookup.js +19 -11
  69. package/src/{builder → domain/graph/builder}/helpers.js +4 -4
  70. package/src/{builder → domain/graph/builder}/incremental.js +119 -93
  71. package/src/domain/graph/builder/pipeline.js +156 -0
  72. package/src/domain/graph/builder/stages/build-edges.js +376 -0
  73. package/src/{builder → domain/graph/builder}/stages/build-structure.js +4 -4
  74. package/src/{builder → domain/graph/builder}/stages/collect-files.js +2 -2
  75. package/src/{builder → domain/graph/builder}/stages/detect-changes.js +204 -183
  76. package/src/{builder → domain/graph/builder}/stages/finalize.js +4 -4
  77. package/src/domain/graph/builder/stages/insert-nodes.js +203 -0
  78. package/src/{builder → domain/graph/builder}/stages/parse-files.js +2 -2
  79. package/src/{builder → domain/graph/builder}/stages/resolve-imports.js +1 -1
  80. package/src/{builder → domain/graph/builder}/stages/run-analyses.js +2 -2
  81. package/src/{change-journal.js → domain/graph/change-journal.js} +1 -1
  82. package/src/{cycles.js → domain/graph/cycles.js} +4 -4
  83. package/src/{journal.js → domain/graph/journal.js} +1 -1
  84. package/src/{resolve.js → domain/graph/resolve.js} +2 -2
  85. package/src/{watcher.js → domain/graph/watcher.js} +7 -7
  86. package/src/{parser.js → domain/parser.js} +24 -15
  87. package/src/{queries.js → domain/queries.js} +17 -16
  88. package/src/{embeddings → domain/search}/generator.js +3 -3
  89. package/src/{embeddings → domain/search}/models.js +2 -2
  90. package/src/{embeddings → domain/search}/search/cli-formatter.js +1 -1
  91. package/src/{embeddings → domain/search}/search/filters.js +9 -5
  92. package/src/{embeddings → domain/search}/search/hybrid.js +1 -1
  93. package/src/{embeddings → domain/search}/search/keyword.js +13 -6
  94. package/src/{embeddings → domain/search}/search/prepare.js +15 -7
  95. package/src/{embeddings → domain/search}/search/semantic.js +1 -1
  96. package/src/{embeddings → domain/search}/strategies/structured.js +1 -1
  97. package/src/extractors/csharp.js +224 -207
  98. package/src/extractors/go.js +176 -172
  99. package/src/extractors/hcl.js +94 -78
  100. package/src/extractors/java.js +213 -207
  101. package/src/extractors/javascript.js +275 -305
  102. package/src/extractors/php.js +234 -221
  103. package/src/extractors/python.js +252 -250
  104. package/src/extractors/ruby.js +192 -185
  105. package/src/extractors/rust.js +182 -167
  106. package/src/{ast.js → features/ast.js} +13 -11
  107. package/src/{audit.js → features/audit.js} +20 -46
  108. package/src/{batch.js → features/batch.js} +5 -5
  109. package/src/{boundaries.js → features/boundaries.js} +100 -85
  110. package/src/{branch-compare.js → features/branch-compare.js} +3 -3
  111. package/src/{cfg.js → features/cfg.js} +141 -150
  112. package/src/{check.js → features/check.js} +13 -30
  113. package/src/{cochange.js → features/cochange.js} +5 -5
  114. package/src/{communities.js → features/communities.js} +72 -57
  115. package/src/{complexity.js → features/complexity.js} +154 -143
  116. package/src/{dataflow.js → features/dataflow.js} +155 -158
  117. package/src/{export.js → features/export.js} +6 -6
  118. package/src/{flow.js → features/flow.js} +4 -4
  119. package/src/{viewer.js → features/graph-enrichment.js} +8 -8
  120. package/src/{manifesto.js → features/manifesto.js} +15 -12
  121. package/src/{owners.js → features/owners.js} +6 -5
  122. package/src/features/sequence.js +300 -0
  123. package/src/features/shared/find-nodes.js +31 -0
  124. package/src/{snapshot.js → features/snapshot.js} +3 -3
  125. package/src/{structure.js → features/structure.js} +139 -108
  126. package/src/features/triage.js +141 -0
  127. package/src/graph/builders/dependency.js +33 -14
  128. package/src/graph/classifiers/risk.js +3 -2
  129. package/src/graph/classifiers/roles.js +6 -3
  130. package/src/index.cjs +16 -0
  131. package/src/index.js +40 -39
  132. package/src/{native.js → infrastructure/native.js} +1 -1
  133. package/src/mcp/middleware.js +1 -1
  134. package/src/mcp/server.js +68 -59
  135. package/src/mcp/tool-registry.js +15 -2
  136. package/src/mcp/tools/ast-query.js +1 -1
  137. package/src/mcp/tools/audit.js +1 -1
  138. package/src/mcp/tools/batch-query.js +1 -1
  139. package/src/mcp/tools/branch-compare.js +3 -1
  140. package/src/mcp/tools/brief.js +8 -0
  141. package/src/mcp/tools/cfg.js +1 -1
  142. package/src/mcp/tools/check.js +3 -3
  143. package/src/mcp/tools/co-changes.js +1 -1
  144. package/src/mcp/tools/code-owners.js +1 -1
  145. package/src/mcp/tools/communities.js +1 -1
  146. package/src/mcp/tools/complexity.js +1 -1
  147. package/src/mcp/tools/dataflow.js +2 -2
  148. package/src/mcp/tools/execution-flow.js +2 -2
  149. package/src/mcp/tools/export-graph.js +2 -2
  150. package/src/mcp/tools/find-cycles.js +2 -2
  151. package/src/mcp/tools/index.js +2 -0
  152. package/src/mcp/tools/list-repos.js +1 -1
  153. package/src/mcp/tools/sequence.js +1 -1
  154. package/src/mcp/tools/structure.js +1 -1
  155. package/src/mcp/tools/triage.js +2 -2
  156. package/src/{commands → presentation}/audit.js +2 -2
  157. package/src/{commands → presentation}/batch.js +1 -1
  158. package/src/{commands → presentation}/branch-compare.js +2 -2
  159. package/src/presentation/brief.js +51 -0
  160. package/src/{commands → presentation}/cfg.js +1 -1
  161. package/src/{commands → presentation}/check.js +2 -2
  162. package/src/{commands → presentation}/communities.js +1 -1
  163. package/src/{commands → presentation}/complexity.js +1 -1
  164. package/src/{commands → presentation}/dataflow.js +1 -1
  165. package/src/{commands → presentation}/flow.js +2 -2
  166. package/src/{commands → presentation}/manifesto.js +1 -1
  167. package/src/{commands → presentation}/owners.js +1 -1
  168. package/src/presentation/queries-cli/exports.js +53 -0
  169. package/src/presentation/queries-cli/impact.js +214 -0
  170. package/src/presentation/queries-cli/index.js +5 -0
  171. package/src/presentation/queries-cli/inspect.js +329 -0
  172. package/src/presentation/queries-cli/overview.js +196 -0
  173. package/src/presentation/queries-cli/path.js +65 -0
  174. package/src/presentation/queries-cli.js +27 -0
  175. package/src/{commands → presentation}/query.js +1 -1
  176. package/src/presentation/result-formatter.js +126 -3
  177. package/src/{commands → presentation}/sequence.js +2 -2
  178. package/src/{commands → presentation}/structure.js +1 -1
  179. package/src/presentation/table.js +0 -8
  180. package/src/{commands → presentation}/triage.js +1 -1
  181. package/src/{constants.js → shared/constants.js} +1 -1
  182. package/src/shared/file-utils.js +2 -2
  183. package/src/shared/generators.js +9 -5
  184. package/src/shared/hierarchy.js +1 -1
  185. package/src/{kinds.js → shared/kinds.js} +1 -1
  186. package/src/analysis/context.js +0 -408
  187. package/src/analysis/dependencies.js +0 -341
  188. package/src/analysis/impact.js +0 -463
  189. package/src/analysis/module-map.js +0 -322
  190. package/src/builder/pipeline.js +0 -130
  191. package/src/builder/stages/build-edges.js +0 -297
  192. package/src/builder/stages/insert-nodes.js +0 -195
  193. package/src/mcp.js +0 -2
  194. package/src/queries-cli.js +0 -866
  195. package/src/sequence.js +0 -289
  196. package/src/triage.js +0 -126
  197. /package/src/{builder → domain/graph/builder}/context.js +0 -0
  198. /package/src/{builder.js → domain/graph/builder.js} +0 -0
  199. /package/src/{embeddings → domain/search}/index.js +0 -0
  200. /package/src/{embeddings → domain/search}/stores/fts5.js +0 -0
  201. /package/src/{embeddings → domain/search}/stores/sqlite-blob.js +0 -0
  202. /package/src/{embeddings → domain/search}/strategies/source.js +0 -0
  203. /package/src/{embeddings → domain/search}/strategies/text-utils.js +0 -0
  204. /package/src/{config.js → infrastructure/config.js} +0 -0
  205. /package/src/{logger.js → infrastructure/logger.js} +0 -0
  206. /package/src/{registry.js → infrastructure/registry.js} +0 -0
  207. /package/src/{update-check.js → infrastructure/update-check.js} +0 -0
  208. /package/src/{commands → presentation}/cochange.js +0 -0
  209. /package/src/{errors.js → shared/errors.js} +0 -0
  210. /package/src/{paginate.js → shared/paginate.js} +0 -0
@@ -4,191 +4,206 @@ import { findChild, nodeEndLine, rustVisibility } from './helpers.js';
4
4
  * Extract symbols from Rust files.
5
5
  */
6
6
  export function extractRustSymbols(tree, _filePath) {
7
- const definitions = [];
8
- const calls = [];
9
- const imports = [];
10
- const classes = [];
11
- const exports = [];
7
+ const ctx = {
8
+ definitions: [],
9
+ calls: [],
10
+ imports: [],
11
+ classes: [],
12
+ exports: [],
13
+ };
12
14
 
13
- function findCurrentImpl(node) {
14
- let current = node.parent;
15
- while (current) {
16
- if (current.type === 'impl_item') {
17
- const typeNode = current.childForFieldName('type');
18
- return typeNode ? typeNode.text : null;
19
- }
20
- current = current.parent;
21
- }
22
- return null;
15
+ walkRustNode(tree.rootNode, ctx);
16
+ return ctx;
17
+ }
18
+
19
+ function walkRustNode(node, ctx) {
20
+ switch (node.type) {
21
+ case 'function_item':
22
+ handleRustFuncItem(node, ctx);
23
+ break;
24
+ case 'struct_item':
25
+ handleRustStructItem(node, ctx);
26
+ break;
27
+ case 'enum_item':
28
+ handleRustEnumItem(node, ctx);
29
+ break;
30
+ case 'const_item':
31
+ handleRustConstItem(node, ctx);
32
+ break;
33
+ case 'trait_item':
34
+ handleRustTraitItem(node, ctx);
35
+ break;
36
+ case 'impl_item':
37
+ handleRustImplItem(node, ctx);
38
+ break;
39
+ case 'use_declaration':
40
+ handleRustUseDecl(node, ctx);
41
+ break;
42
+ case 'call_expression':
43
+ handleRustCallExpr(node, ctx);
44
+ break;
45
+ case 'macro_invocation':
46
+ handleRustMacroInvocation(node, ctx);
47
+ break;
23
48
  }
24
49
 
25
- function walkRustNode(node) {
26
- switch (node.type) {
27
- case 'function_item': {
28
- const nameNode = node.childForFieldName('name');
29
- if (nameNode) {
30
- const implType = findCurrentImpl(node);
31
- const fullName = implType ? `${implType}.${nameNode.text}` : nameNode.text;
32
- const kind = implType ? 'method' : 'function';
33
- const params = extractRustParameters(node.childForFieldName('parameters'));
34
- definitions.push({
35
- name: fullName,
36
- kind,
37
- line: node.startPosition.row + 1,
38
- endLine: nodeEndLine(node),
39
- children: params.length > 0 ? params : undefined,
40
- visibility: rustVisibility(node),
41
- });
42
- }
43
- break;
44
- }
50
+ for (let i = 0; i < node.childCount; i++) walkRustNode(node.child(i), ctx);
51
+ }
45
52
 
46
- case 'struct_item': {
47
- const nameNode = node.childForFieldName('name');
48
- if (nameNode) {
49
- const fields = extractStructFields(node);
50
- definitions.push({
51
- name: nameNode.text,
52
- kind: 'struct',
53
- line: node.startPosition.row + 1,
54
- endLine: nodeEndLine(node),
55
- children: fields.length > 0 ? fields : undefined,
56
- visibility: rustVisibility(node),
57
- });
58
- }
59
- break;
60
- }
53
+ // ── Walk-path per-node-type handlers ────────────────────────────────────────
61
54
 
62
- case 'enum_item': {
63
- const nameNode = node.childForFieldName('name');
64
- if (nameNode) {
65
- const variants = extractEnumVariants(node);
66
- definitions.push({
67
- name: nameNode.text,
68
- kind: 'enum',
69
- line: node.startPosition.row + 1,
70
- endLine: nodeEndLine(node),
71
- children: variants.length > 0 ? variants : undefined,
72
- });
73
- }
74
- break;
75
- }
55
+ function handleRustFuncItem(node, ctx) {
56
+ // Skip default-impl functions already emitted by handleRustTraitItem
57
+ if (node.parent?.parent?.type === 'trait_item') return;
58
+ const nameNode = node.childForFieldName('name');
59
+ if (!nameNode) return;
60
+ const implType = findCurrentImpl(node);
61
+ const fullName = implType ? `${implType}.${nameNode.text}` : nameNode.text;
62
+ const kind = implType ? 'method' : 'function';
63
+ const params = extractRustParameters(node.childForFieldName('parameters'));
64
+ ctx.definitions.push({
65
+ name: fullName,
66
+ kind,
67
+ line: node.startPosition.row + 1,
68
+ endLine: nodeEndLine(node),
69
+ children: params.length > 0 ? params : undefined,
70
+ visibility: rustVisibility(node),
71
+ });
72
+ }
76
73
 
77
- case 'const_item': {
78
- const nameNode = node.childForFieldName('name');
79
- if (nameNode) {
80
- definitions.push({
81
- name: nameNode.text,
82
- kind: 'constant',
83
- line: node.startPosition.row + 1,
84
- endLine: nodeEndLine(node),
85
- });
86
- }
87
- break;
88
- }
74
+ function handleRustStructItem(node, ctx) {
75
+ const nameNode = node.childForFieldName('name');
76
+ if (!nameNode) return;
77
+ const fields = extractStructFields(node);
78
+ ctx.definitions.push({
79
+ name: nameNode.text,
80
+ kind: 'struct',
81
+ line: node.startPosition.row + 1,
82
+ endLine: nodeEndLine(node),
83
+ children: fields.length > 0 ? fields : undefined,
84
+ visibility: rustVisibility(node),
85
+ });
86
+ }
89
87
 
90
- case 'trait_item': {
91
- const nameNode = node.childForFieldName('name');
92
- if (nameNode) {
93
- definitions.push({
94
- name: nameNode.text,
95
- kind: 'trait',
96
- line: node.startPosition.row + 1,
97
- endLine: nodeEndLine(node),
98
- });
99
- const body = node.childForFieldName('body');
100
- if (body) {
101
- for (let i = 0; i < body.childCount; i++) {
102
- const child = body.child(i);
103
- if (
104
- child &&
105
- (child.type === 'function_signature_item' || child.type === 'function_item')
106
- ) {
107
- const methName = child.childForFieldName('name');
108
- if (methName) {
109
- definitions.push({
110
- name: `${nameNode.text}.${methName.text}`,
111
- kind: 'method',
112
- line: child.startPosition.row + 1,
113
- endLine: child.endPosition.row + 1,
114
- });
115
- }
116
- }
117
- }
118
- }
119
- }
120
- break;
121
- }
88
+ function handleRustEnumItem(node, ctx) {
89
+ const nameNode = node.childForFieldName('name');
90
+ if (!nameNode) return;
91
+ const variants = extractEnumVariants(node);
92
+ ctx.definitions.push({
93
+ name: nameNode.text,
94
+ kind: 'enum',
95
+ line: node.startPosition.row + 1,
96
+ endLine: nodeEndLine(node),
97
+ children: variants.length > 0 ? variants : undefined,
98
+ });
99
+ }
100
+
101
+ function handleRustConstItem(node, ctx) {
102
+ const nameNode = node.childForFieldName('name');
103
+ if (!nameNode) return;
104
+ ctx.definitions.push({
105
+ name: nameNode.text,
106
+ kind: 'constant',
107
+ line: node.startPosition.row + 1,
108
+ endLine: nodeEndLine(node),
109
+ });
110
+ }
122
111
 
123
- case 'impl_item': {
124
- const typeNode = node.childForFieldName('type');
125
- const traitNode = node.childForFieldName('trait');
126
- if (typeNode && traitNode) {
127
- classes.push({
128
- name: typeNode.text,
129
- implements: traitNode.text,
130
- line: node.startPosition.row + 1,
112
+ function handleRustTraitItem(node, ctx) {
113
+ const nameNode = node.childForFieldName('name');
114
+ if (!nameNode) return;
115
+ ctx.definitions.push({
116
+ name: nameNode.text,
117
+ kind: 'trait',
118
+ line: node.startPosition.row + 1,
119
+ endLine: nodeEndLine(node),
120
+ });
121
+ const body = node.childForFieldName('body');
122
+ if (body) {
123
+ for (let i = 0; i < body.childCount; i++) {
124
+ const child = body.child(i);
125
+ if (child && (child.type === 'function_signature_item' || child.type === 'function_item')) {
126
+ const methName = child.childForFieldName('name');
127
+ if (methName) {
128
+ ctx.definitions.push({
129
+ name: `${nameNode.text}.${methName.text}`,
130
+ kind: 'method',
131
+ line: child.startPosition.row + 1,
132
+ endLine: child.endPosition.row + 1,
131
133
  });
132
134
  }
133
- break;
134
135
  }
136
+ }
137
+ }
138
+ }
135
139
 
136
- case 'use_declaration': {
137
- const argNode = node.child(1);
138
- if (argNode) {
139
- const usePaths = extractRustUsePath(argNode);
140
- for (const imp of usePaths) {
141
- imports.push({
142
- source: imp.source,
143
- names: imp.names,
144
- line: node.startPosition.row + 1,
145
- rustUse: true,
146
- });
147
- }
148
- }
149
- break;
150
- }
140
+ function handleRustImplItem(node, ctx) {
141
+ const typeNode = node.childForFieldName('type');
142
+ const traitNode = node.childForFieldName('trait');
143
+ if (typeNode && traitNode) {
144
+ ctx.classes.push({
145
+ name: typeNode.text,
146
+ implements: traitNode.text,
147
+ line: node.startPosition.row + 1,
148
+ });
149
+ }
150
+ }
151
151
 
152
- case 'call_expression': {
153
- const fn = node.childForFieldName('function');
154
- if (fn) {
155
- if (fn.type === 'identifier') {
156
- calls.push({ name: fn.text, line: node.startPosition.row + 1 });
157
- } else if (fn.type === 'field_expression') {
158
- const field = fn.childForFieldName('field');
159
- if (field) {
160
- const value = fn.childForFieldName('value');
161
- const call = { name: field.text, line: node.startPosition.row + 1 };
162
- if (value) call.receiver = value.text;
163
- calls.push(call);
164
- }
165
- } else if (fn.type === 'scoped_identifier') {
166
- const name = fn.childForFieldName('name');
167
- if (name) {
168
- const path = fn.childForFieldName('path');
169
- const call = { name: name.text, line: node.startPosition.row + 1 };
170
- if (path) call.receiver = path.text;
171
- calls.push(call);
172
- }
173
- }
174
- }
175
- break;
176
- }
152
+ function handleRustUseDecl(node, ctx) {
153
+ const argNode = node.child(1);
154
+ if (!argNode) return;
155
+ const usePaths = extractRustUsePath(argNode);
156
+ for (const imp of usePaths) {
157
+ ctx.imports.push({
158
+ source: imp.source,
159
+ names: imp.names,
160
+ line: node.startPosition.row + 1,
161
+ rustUse: true,
162
+ });
163
+ }
164
+ }
177
165
 
178
- case 'macro_invocation': {
179
- const macroNode = node.child(0);
180
- if (macroNode) {
181
- calls.push({ name: `${macroNode.text}!`, line: node.startPosition.row + 1 });
182
- }
183
- break;
184
- }
166
+ function handleRustCallExpr(node, ctx) {
167
+ const fn = node.childForFieldName('function');
168
+ if (!fn) return;
169
+ if (fn.type === 'identifier') {
170
+ ctx.calls.push({ name: fn.text, line: node.startPosition.row + 1 });
171
+ } else if (fn.type === 'field_expression') {
172
+ const field = fn.childForFieldName('field');
173
+ if (field) {
174
+ const value = fn.childForFieldName('value');
175
+ const call = { name: field.text, line: node.startPosition.row + 1 };
176
+ if (value) call.receiver = value.text;
177
+ ctx.calls.push(call);
178
+ }
179
+ } else if (fn.type === 'scoped_identifier') {
180
+ const name = fn.childForFieldName('name');
181
+ if (name) {
182
+ const path = fn.childForFieldName('path');
183
+ const call = { name: name.text, line: node.startPosition.row + 1 };
184
+ if (path) call.receiver = path.text;
185
+ ctx.calls.push(call);
185
186
  }
187
+ }
188
+ }
186
189
 
187
- for (let i = 0; i < node.childCount; i++) walkRustNode(node.child(i));
190
+ function handleRustMacroInvocation(node, ctx) {
191
+ const macroNode = node.child(0);
192
+ if (macroNode) {
193
+ ctx.calls.push({ name: `${macroNode.text}!`, line: node.startPosition.row + 1 });
188
194
  }
195
+ }
189
196
 
190
- walkRustNode(tree.rootNode);
191
- return { definitions, calls, imports, classes, exports };
197
+ function findCurrentImpl(node) {
198
+ let current = node.parent;
199
+ while (current) {
200
+ if (current.type === 'impl_item') {
201
+ const typeNode = current.childForFieldName('type');
202
+ return typeNode ? typeNode.text : null;
203
+ }
204
+ current = current.parent;
205
+ }
206
+ return null;
192
207
  }
193
208
 
194
209
  // ── Child extraction helpers ────────────────────────────────────────────────
@@ -7,14 +7,15 @@
7
7
  */
8
8
 
9
9
  import path from 'node:path';
10
- import { AST_TYPE_MAPS } from './ast-analysis/rules/index.js';
11
- import { buildExtensionSet } from './ast-analysis/shared.js';
12
- import { walkWithVisitors } from './ast-analysis/visitor.js';
13
- import { createAstStoreVisitor } from './ast-analysis/visitors/ast-store-visitor.js';
14
- import { bulkNodeIdsByFile, openReadonlyOrFail } from './db.js';
15
- import { outputResult } from './infrastructure/result-formatter.js';
16
- import { debug } from './logger.js';
17
- import { paginateResult } from './paginate.js';
10
+ import { AST_TYPE_MAPS } from '../ast-analysis/rules/index.js';
11
+ import { buildExtensionSet } from '../ast-analysis/shared.js';
12
+ import { walkWithVisitors } from '../ast-analysis/visitor.js';
13
+ import { createAstStoreVisitor } from '../ast-analysis/visitors/ast-store-visitor.js';
14
+ import { bulkNodeIdsByFile, openReadonlyOrFail } from '../db/index.js';
15
+ import { buildFileConditionSQL } from '../db/query-builder.js';
16
+ import { debug } from '../infrastructure/logger.js';
17
+ import { outputResult } from '../infrastructure/result-formatter.js';
18
+ import { paginateResult } from '../shared/paginate.js';
18
19
 
19
20
  // ─── Constants ────────────────────────────────────────────────────────
20
21
 
@@ -193,9 +194,10 @@ export function astQueryData(pattern, customDbPath, opts = {}) {
193
194
  params.push(kind);
194
195
  }
195
196
 
196
- if (file) {
197
- where += ' AND a.file LIKE ?';
198
- params.push(`%${file}%`);
197
+ {
198
+ const fc = buildFileConditionSQL(file, 'a.file');
199
+ where += fc.sql;
200
+ params.push(...fc.params);
199
201
  }
200
202
 
201
203
  if (noTests) {
@@ -7,21 +7,27 @@
7
7
  */
8
8
 
9
9
  import path from 'node:path';
10
- import { loadConfig } from './config.js';
11
- import { openReadonlyOrFail } from './db.js';
12
- import { isTestFile } from './infrastructure/test-filter.js';
10
+ import { openReadonlyOrFail } from '../db/index.js';
11
+ import { normalizeFileFilter } from '../db/query-builder.js';
12
+ import { bfsTransitiveCallers } from '../domain/analysis/impact.js';
13
+ import { explainData } from '../domain/queries.js';
14
+ import { loadConfig } from '../infrastructure/config.js';
15
+ import { isTestFile } from '../infrastructure/test-filter.js';
13
16
  import { RULE_DEFS } from './manifesto.js';
14
- import { explainData } from './queries.js';
15
17
 
16
18
  // ─── Threshold resolution ───────────────────────────────────────────
17
19
 
18
20
  const FUNCTION_RULES = RULE_DEFS.filter((d) => d.level === 'function');
19
21
 
20
- function resolveThresholds(customDbPath) {
22
+ function resolveThresholds(customDbPath, config) {
21
23
  try {
22
- const dbDir = path.dirname(customDbPath);
23
- const repoRoot = path.resolve(dbDir, '..');
24
- const cfg = loadConfig(repoRoot);
24
+ const cfg =
25
+ config ||
26
+ (() => {
27
+ const dbDir = path.dirname(customDbPath);
28
+ const repoRoot = path.resolve(dbDir, '..');
29
+ return loadConfig(repoRoot);
30
+ })();
25
31
  const userRules = cfg.manifesto || {};
26
32
  const resolved = {};
27
33
  for (const def of FUNCTION_RULES) {
@@ -70,39 +76,6 @@ function checkBreaches(row, thresholds) {
70
76
  return breaches;
71
77
  }
72
78
 
73
- // ─── BFS impact (inline, same algorithm as fnImpactData) ────────────
74
-
75
- function computeImpact(db, nodeId, noTests, maxDepth) {
76
- const visited = new Set([nodeId]);
77
- const levels = {};
78
- let frontier = [nodeId];
79
-
80
- for (let d = 1; d <= maxDepth; d++) {
81
- const nextFrontier = [];
82
- for (const fid of frontier) {
83
- const callers = db
84
- .prepare(
85
- `SELECT DISTINCT n.id, n.name, n.kind, n.file, n.line
86
- FROM edges e JOIN nodes n ON e.source_id = n.id
87
- WHERE e.target_id = ? AND e.kind = 'calls'`,
88
- )
89
- .all(fid);
90
- for (const c of callers) {
91
- if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
92
- visited.add(c.id);
93
- nextFrontier.push(c.id);
94
- if (!levels[d]) levels[d] = [];
95
- levels[d].push({ name: c.name, kind: c.kind, file: c.file, line: c.line });
96
- }
97
- }
98
- }
99
- frontier = nextFrontier;
100
- if (frontier.length === 0) break;
101
- }
102
-
103
- return { totalDependents: visited.size - 1, levels };
104
- }
105
-
106
79
  // ─── Phase 4.4 fields (graceful null fallback) ─────────────────────
107
80
 
108
81
  function readPhase44(db, nodeId) {
@@ -128,7 +101,7 @@ function readPhase44(db, nodeId) {
128
101
  export function auditData(target, customDbPath, opts = {}) {
129
102
  const noTests = opts.noTests || false;
130
103
  const maxDepth = opts.depth || 3;
131
- const file = opts.file;
104
+ const fileFilters = normalizeFileFilter(opts.file);
132
105
  const kind = opts.kind;
133
106
 
134
107
  // 1. Get structure via explainData
@@ -137,7 +110,8 @@ export function auditData(target, customDbPath, opts = {}) {
137
110
  // Apply --file and --kind filters for function targets
138
111
  let results = explained.results;
139
112
  if (explained.kind === 'function') {
140
- if (file) results = results.filter((r) => r.file.includes(file));
113
+ if (fileFilters.length > 0)
114
+ results = results.filter((r) => fileFilters.some((f) => r.file.includes(f)));
141
115
  if (kind) results = results.filter((r) => r.kind === kind);
142
116
  }
143
117
 
@@ -147,7 +121,7 @@ export function auditData(target, customDbPath, opts = {}) {
147
121
 
148
122
  // 2. Open DB for enrichment
149
123
  const db = openReadonlyOrFail(customDbPath);
150
- const thresholds = resolveThresholds(customDbPath);
124
+ const thresholds = resolveThresholds(customDbPath, opts.config);
151
125
 
152
126
  let functions;
153
127
  try {
@@ -189,7 +163,7 @@ function enrichFunction(db, r, noTests, maxDepth, thresholds) {
189
163
  const nodeId = nodeRow?.id;
190
164
  const health = nodeId ? buildHealth(db, nodeId, thresholds) : defaultHealth();
191
165
  const impact = nodeId
192
- ? computeImpact(db, nodeId, noTests, maxDepth)
166
+ ? bfsTransitiveCallers(db, nodeId, { noTests, maxDepth })
193
167
  : { totalDependents: 0, levels: {} };
194
168
  const phase44 = nodeId
195
169
  ? readPhase44(db, nodeId)
@@ -260,7 +234,7 @@ function enrichSymbol(db, sym, file, noTests, maxDepth, thresholds) {
260
234
 
261
235
  const health = nodeId ? buildHealth(db, nodeId, thresholds) : defaultHealth();
262
236
  const impact = nodeId
263
- ? computeImpact(db, nodeId, noTests, maxDepth)
237
+ ? bfsTransitiveCallers(db, nodeId, { noTests, maxDepth })
264
238
  : { totalDependents: 0, levels: {} };
265
239
  const phase44 = nodeId
266
240
  ? readPhase44(db, nodeId)
@@ -5,10 +5,6 @@
5
5
  * Designed for multi-agent swarms that need to dispatch 20+ queries in one call.
6
6
  */
7
7
 
8
- import { complexityData } from './complexity.js';
9
- import { dataflowData } from './dataflow.js';
10
- import { ConfigError } from './errors.js';
11
- import { flowData } from './flow.js';
12
8
  import {
13
9
  contextData,
14
10
  explainData,
@@ -18,7 +14,11 @@ import {
18
14
  fnImpactData,
19
15
  impactAnalysisData,
20
16
  whereData,
21
- } from './queries.js';
17
+ } from '../domain/queries.js';
18
+ import { ConfigError } from '../shared/errors.js';
19
+ import { complexityData } from './complexity.js';
20
+ import { dataflowData } from './dataflow.js';
21
+ import { flowData } from './flow.js';
22
22
 
23
23
  /**
24
24
  * Map of supported batch commands → their data function + first-arg semantics.