@optave/codegraph 3.1.5 → 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 (91) hide show
  1. package/README.md +3 -2
  2. package/package.json +7 -7
  3. package/src/ast-analysis/engine.js +252 -258
  4. package/src/ast-analysis/shared.js +0 -12
  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 +2 -1
  9. package/src/cli/commands/audit.js +2 -1
  10. package/src/cli/commands/batch.js +2 -1
  11. package/src/cli/commands/brief.js +12 -0
  12. package/src/cli/commands/cfg.js +2 -1
  13. package/src/cli/commands/check.js +20 -23
  14. package/src/cli/commands/children.js +6 -1
  15. package/src/cli/commands/complexity.js +2 -1
  16. package/src/cli/commands/context.js +6 -1
  17. package/src/cli/commands/dataflow.js +2 -1
  18. package/src/cli/commands/deps.js +8 -3
  19. package/src/cli/commands/flow.js +2 -1
  20. package/src/cli/commands/fn-impact.js +6 -1
  21. package/src/cli/commands/owners.js +4 -2
  22. package/src/cli/commands/query.js +6 -1
  23. package/src/cli/commands/roles.js +2 -1
  24. package/src/cli/commands/search.js +8 -2
  25. package/src/cli/commands/sequence.js +2 -1
  26. package/src/cli/commands/triage.js +38 -27
  27. package/src/db/connection.js +18 -12
  28. package/src/db/migrations.js +41 -64
  29. package/src/db/query-builder.js +60 -4
  30. package/src/db/repository/in-memory-repository.js +27 -16
  31. package/src/db/repository/nodes.js +8 -10
  32. package/src/domain/analysis/brief.js +155 -0
  33. package/src/domain/analysis/context.js +174 -190
  34. package/src/domain/analysis/dependencies.js +200 -146
  35. package/src/domain/analysis/exports.js +3 -2
  36. package/src/domain/analysis/impact.js +267 -152
  37. package/src/domain/analysis/module-map.js +247 -221
  38. package/src/domain/analysis/roles.js +8 -5
  39. package/src/domain/analysis/symbol-lookup.js +7 -5
  40. package/src/domain/graph/builder/helpers.js +1 -1
  41. package/src/domain/graph/builder/incremental.js +116 -90
  42. package/src/domain/graph/builder/pipeline.js +106 -80
  43. package/src/domain/graph/builder/stages/build-edges.js +318 -239
  44. package/src/domain/graph/builder/stages/detect-changes.js +198 -177
  45. package/src/domain/graph/builder/stages/insert-nodes.js +147 -139
  46. package/src/domain/graph/watcher.js +2 -2
  47. package/src/domain/parser.js +20 -11
  48. package/src/domain/queries.js +1 -0
  49. package/src/domain/search/search/filters.js +9 -5
  50. package/src/domain/search/search/keyword.js +12 -5
  51. package/src/domain/search/search/prepare.js +13 -5
  52. package/src/extractors/csharp.js +224 -207
  53. package/src/extractors/go.js +176 -172
  54. package/src/extractors/hcl.js +94 -78
  55. package/src/extractors/java.js +213 -207
  56. package/src/extractors/javascript.js +274 -304
  57. package/src/extractors/php.js +234 -221
  58. package/src/extractors/python.js +252 -250
  59. package/src/extractors/ruby.js +192 -185
  60. package/src/extractors/rust.js +182 -167
  61. package/src/features/ast.js +5 -3
  62. package/src/features/audit.js +4 -2
  63. package/src/features/boundaries.js +98 -83
  64. package/src/features/cfg.js +134 -143
  65. package/src/features/communities.js +68 -53
  66. package/src/features/complexity.js +143 -132
  67. package/src/features/dataflow.js +146 -149
  68. package/src/features/export.js +3 -3
  69. package/src/features/graph-enrichment.js +2 -2
  70. package/src/features/manifesto.js +9 -6
  71. package/src/features/owners.js +4 -3
  72. package/src/features/sequence.js +152 -141
  73. package/src/features/shared/find-nodes.js +31 -0
  74. package/src/features/structure.js +130 -99
  75. package/src/features/triage.js +83 -68
  76. package/src/graph/classifiers/risk.js +3 -2
  77. package/src/graph/classifiers/roles.js +6 -3
  78. package/src/index.js +1 -0
  79. package/src/mcp/server.js +65 -56
  80. package/src/mcp/tool-registry.js +13 -0
  81. package/src/mcp/tools/brief.js +8 -0
  82. package/src/mcp/tools/index.js +2 -0
  83. package/src/presentation/brief.js +51 -0
  84. package/src/presentation/queries-cli/exports.js +21 -14
  85. package/src/presentation/queries-cli/impact.js +55 -39
  86. package/src/presentation/queries-cli/inspect.js +184 -189
  87. package/src/presentation/queries-cli/overview.js +57 -58
  88. package/src/presentation/queries-cli/path.js +36 -29
  89. package/src/presentation/table.js +0 -8
  90. package/src/shared/generators.js +7 -3
  91. package/src/shared/kinds.js +1 -1
@@ -4,233 +4,250 @@ import { extractModifierVisibility, findChild, nodeEndLine } from './helpers.js'
4
4
  * Extract symbols from C# files.
5
5
  */
6
6
  export function extractCSharpSymbols(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 findCSharpParentType(node) {
14
- let current = node.parent;
15
- while (current) {
16
- if (
17
- current.type === 'class_declaration' ||
18
- current.type === 'struct_declaration' ||
19
- current.type === 'interface_declaration' ||
20
- current.type === 'enum_declaration' ||
21
- current.type === 'record_declaration'
22
- ) {
23
- const nameNode = current.childForFieldName('name');
24
- return nameNode ? nameNode.text : null;
25
- }
26
- current = current.parent;
27
- }
28
- return null;
15
+ walkCSharpNode(tree.rootNode, ctx);
16
+ return ctx;
17
+ }
18
+
19
+ function walkCSharpNode(node, ctx) {
20
+ switch (node.type) {
21
+ case 'class_declaration':
22
+ handleCsClassDecl(node, ctx);
23
+ break;
24
+ case 'struct_declaration':
25
+ handleCsStructDecl(node, ctx);
26
+ break;
27
+ case 'record_declaration':
28
+ handleCsRecordDecl(node, ctx);
29
+ break;
30
+ case 'interface_declaration':
31
+ handleCsInterfaceDecl(node, ctx);
32
+ break;
33
+ case 'enum_declaration':
34
+ handleCsEnumDecl(node, ctx);
35
+ break;
36
+ case 'method_declaration':
37
+ handleCsMethodDecl(node, ctx);
38
+ break;
39
+ case 'constructor_declaration':
40
+ handleCsConstructorDecl(node, ctx);
41
+ break;
42
+ case 'property_declaration':
43
+ handleCsPropertyDecl(node, ctx);
44
+ break;
45
+ case 'using_directive':
46
+ handleCsUsingDirective(node, ctx);
47
+ break;
48
+ case 'invocation_expression':
49
+ handleCsInvocationExpr(node, ctx);
50
+ break;
51
+ case 'object_creation_expression':
52
+ handleCsObjectCreation(node, ctx);
53
+ break;
29
54
  }
30
55
 
31
- function walkCSharpNode(node) {
32
- switch (node.type) {
33
- case 'class_declaration': {
34
- const nameNode = node.childForFieldName('name');
35
- if (nameNode) {
36
- const classChildren = extractCSharpClassFields(node);
37
- definitions.push({
38
- name: nameNode.text,
39
- kind: 'class',
40
- line: node.startPosition.row + 1,
41
- endLine: nodeEndLine(node),
42
- children: classChildren.length > 0 ? classChildren : undefined,
43
- });
44
- extractCSharpBaseTypes(node, nameNode.text, classes);
45
- }
46
- break;
47
- }
56
+ for (let i = 0; i < node.childCount; i++) walkCSharpNode(node.child(i), ctx);
57
+ }
48
58
 
49
- case 'struct_declaration': {
50
- const nameNode = node.childForFieldName('name');
51
- if (nameNode) {
52
- const structChildren = extractCSharpClassFields(node);
53
- definitions.push({
54
- name: nameNode.text,
55
- kind: 'struct',
56
- line: node.startPosition.row + 1,
57
- endLine: nodeEndLine(node),
58
- children: structChildren.length > 0 ? structChildren : undefined,
59
- });
60
- extractCSharpBaseTypes(node, nameNode.text, classes);
61
- }
62
- break;
63
- }
59
+ // ── Walk-path per-node-type handlers ────────────────────────────────────────
64
60
 
65
- case 'record_declaration': {
66
- const nameNode = node.childForFieldName('name');
67
- if (nameNode) {
68
- definitions.push({
69
- name: nameNode.text,
70
- kind: 'record',
71
- line: node.startPosition.row + 1,
72
- endLine: nodeEndLine(node),
73
- });
74
- extractCSharpBaseTypes(node, nameNode.text, classes);
75
- }
76
- break;
77
- }
61
+ function handleCsClassDecl(node, ctx) {
62
+ const nameNode = node.childForFieldName('name');
63
+ if (!nameNode) return;
64
+ const classChildren = extractCSharpClassFields(node);
65
+ ctx.definitions.push({
66
+ name: nameNode.text,
67
+ kind: 'class',
68
+ line: node.startPosition.row + 1,
69
+ endLine: nodeEndLine(node),
70
+ children: classChildren.length > 0 ? classChildren : undefined,
71
+ });
72
+ extractCSharpBaseTypes(node, nameNode.text, ctx.classes);
73
+ }
78
74
 
79
- case 'interface_declaration': {
80
- const nameNode = node.childForFieldName('name');
81
- if (nameNode) {
82
- definitions.push({
83
- name: nameNode.text,
84
- kind: 'interface',
85
- line: node.startPosition.row + 1,
86
- endLine: nodeEndLine(node),
87
- });
88
- const body = node.childForFieldName('body');
89
- if (body) {
90
- for (let i = 0; i < body.childCount; i++) {
91
- const child = body.child(i);
92
- if (child && child.type === 'method_declaration') {
93
- const methName = child.childForFieldName('name');
94
- if (methName) {
95
- definitions.push({
96
- name: `${nameNode.text}.${methName.text}`,
97
- kind: 'method',
98
- line: child.startPosition.row + 1,
99
- endLine: child.endPosition.row + 1,
100
- });
101
- }
102
- }
103
- }
104
- }
105
- }
106
- break;
107
- }
75
+ function handleCsStructDecl(node, ctx) {
76
+ const nameNode = node.childForFieldName('name');
77
+ if (!nameNode) return;
78
+ const structChildren = extractCSharpClassFields(node);
79
+ ctx.definitions.push({
80
+ name: nameNode.text,
81
+ kind: 'struct',
82
+ line: node.startPosition.row + 1,
83
+ endLine: nodeEndLine(node),
84
+ children: structChildren.length > 0 ? structChildren : undefined,
85
+ });
86
+ extractCSharpBaseTypes(node, nameNode.text, ctx.classes);
87
+ }
108
88
 
109
- case 'enum_declaration': {
110
- const nameNode = node.childForFieldName('name');
111
- if (nameNode) {
112
- const enumChildren = extractCSharpEnumMembers(node);
113
- definitions.push({
114
- name: nameNode.text,
115
- kind: 'enum',
116
- line: node.startPosition.row + 1,
117
- endLine: nodeEndLine(node),
118
- children: enumChildren.length > 0 ? enumChildren : undefined,
119
- });
120
- }
121
- break;
122
- }
89
+ function handleCsRecordDecl(node, ctx) {
90
+ const nameNode = node.childForFieldName('name');
91
+ if (!nameNode) return;
92
+ ctx.definitions.push({
93
+ name: nameNode.text,
94
+ kind: 'record',
95
+ line: node.startPosition.row + 1,
96
+ endLine: nodeEndLine(node),
97
+ });
98
+ extractCSharpBaseTypes(node, nameNode.text, ctx.classes);
99
+ }
123
100
 
124
- case 'method_declaration': {
125
- const nameNode = node.childForFieldName('name');
126
- if (nameNode) {
127
- const parentType = findCSharpParentType(node);
128
- const fullName = parentType ? `${parentType}.${nameNode.text}` : nameNode.text;
129
- const params = extractCSharpParameters(node.childForFieldName('parameters'));
130
- definitions.push({
131
- name: fullName,
101
+ function handleCsInterfaceDecl(node, ctx) {
102
+ const nameNode = node.childForFieldName('name');
103
+ if (!nameNode) return;
104
+ ctx.definitions.push({
105
+ name: nameNode.text,
106
+ kind: 'interface',
107
+ line: node.startPosition.row + 1,
108
+ endLine: nodeEndLine(node),
109
+ });
110
+ const body = node.childForFieldName('body');
111
+ if (body) {
112
+ for (let i = 0; i < body.childCount; i++) {
113
+ const child = body.child(i);
114
+ if (child && child.type === 'method_declaration') {
115
+ const methName = child.childForFieldName('name');
116
+ if (methName) {
117
+ ctx.definitions.push({
118
+ name: `${nameNode.text}.${methName.text}`,
132
119
  kind: 'method',
133
- line: node.startPosition.row + 1,
134
- endLine: nodeEndLine(node),
135
- children: params.length > 0 ? params : undefined,
136
- visibility: extractModifierVisibility(node),
120
+ line: child.startPosition.row + 1,
121
+ endLine: child.endPosition.row + 1,
137
122
  });
138
123
  }
139
- break;
140
124
  }
125
+ }
126
+ }
127
+ }
141
128
 
142
- case 'constructor_declaration': {
143
- const nameNode = node.childForFieldName('name');
144
- if (nameNode) {
145
- const parentType = findCSharpParentType(node);
146
- const fullName = parentType ? `${parentType}.${nameNode.text}` : nameNode.text;
147
- const params = extractCSharpParameters(node.childForFieldName('parameters'));
148
- definitions.push({
149
- name: fullName,
150
- kind: 'method',
151
- line: node.startPosition.row + 1,
152
- endLine: nodeEndLine(node),
153
- children: params.length > 0 ? params : undefined,
154
- visibility: extractModifierVisibility(node),
155
- });
156
- }
157
- break;
158
- }
129
+ function handleCsEnumDecl(node, ctx) {
130
+ const nameNode = node.childForFieldName('name');
131
+ if (!nameNode) return;
132
+ const enumChildren = extractCSharpEnumMembers(node);
133
+ ctx.definitions.push({
134
+ name: nameNode.text,
135
+ kind: 'enum',
136
+ line: node.startPosition.row + 1,
137
+ endLine: nodeEndLine(node),
138
+ children: enumChildren.length > 0 ? enumChildren : undefined,
139
+ });
140
+ }
159
141
 
160
- case 'property_declaration': {
161
- const nameNode = node.childForFieldName('name');
162
- if (nameNode) {
163
- const parentType = findCSharpParentType(node);
164
- const fullName = parentType ? `${parentType}.${nameNode.text}` : nameNode.text;
165
- definitions.push({
166
- name: fullName,
167
- kind: 'property',
168
- line: node.startPosition.row + 1,
169
- endLine: nodeEndLine(node),
170
- visibility: extractModifierVisibility(node),
171
- });
172
- }
173
- break;
174
- }
142
+ function handleCsMethodDecl(node, ctx) {
143
+ // Skip interface methods already emitted by handleCsInterfaceDecl
144
+ if (node.parent?.parent?.type === 'interface_declaration') return;
145
+ const nameNode = node.childForFieldName('name');
146
+ if (!nameNode) return;
147
+ const parentType = findCSharpParentType(node);
148
+ const fullName = parentType ? `${parentType}.${nameNode.text}` : nameNode.text;
149
+ const params = extractCSharpParameters(node.childForFieldName('parameters'));
150
+ ctx.definitions.push({
151
+ name: fullName,
152
+ kind: 'method',
153
+ line: node.startPosition.row + 1,
154
+ endLine: nodeEndLine(node),
155
+ children: params.length > 0 ? params : undefined,
156
+ visibility: extractModifierVisibility(node),
157
+ });
158
+ }
175
159
 
176
- case 'using_directive': {
177
- // using System.Collections.Generic;
178
- const nameNode =
179
- node.childForFieldName('name') ||
180
- findChild(node, 'qualified_name') ||
181
- findChild(node, 'identifier');
182
- if (nameNode) {
183
- const fullPath = nameNode.text;
184
- const lastName = fullPath.split('.').pop();
185
- imports.push({
186
- source: fullPath,
187
- names: [lastName],
188
- line: node.startPosition.row + 1,
189
- csharpUsing: true,
190
- });
191
- }
192
- break;
193
- }
160
+ function handleCsConstructorDecl(node, ctx) {
161
+ const nameNode = node.childForFieldName('name');
162
+ if (!nameNode) return;
163
+ const parentType = findCSharpParentType(node);
164
+ const fullName = parentType ? `${parentType}.${nameNode.text}` : nameNode.text;
165
+ const params = extractCSharpParameters(node.childForFieldName('parameters'));
166
+ ctx.definitions.push({
167
+ name: fullName,
168
+ kind: 'method',
169
+ line: node.startPosition.row + 1,
170
+ endLine: nodeEndLine(node),
171
+ children: params.length > 0 ? params : undefined,
172
+ visibility: extractModifierVisibility(node),
173
+ });
174
+ }
194
175
 
195
- case 'invocation_expression': {
196
- const fn = node.childForFieldName('function') || node.child(0);
197
- if (fn) {
198
- if (fn.type === 'identifier') {
199
- calls.push({ name: fn.text, line: node.startPosition.row + 1 });
200
- } else if (fn.type === 'member_access_expression') {
201
- const name = fn.childForFieldName('name');
202
- if (name) {
203
- const expr = fn.childForFieldName('expression');
204
- const call = { name: name.text, line: node.startPosition.row + 1 };
205
- if (expr) call.receiver = expr.text;
206
- calls.push(call);
207
- }
208
- } else if (fn.type === 'generic_name' || fn.type === 'member_binding_expression') {
209
- const name = fn.childForFieldName('name') || fn.child(0);
210
- if (name) calls.push({ name: name.text, line: node.startPosition.row + 1 });
211
- }
212
- }
213
- break;
214
- }
176
+ function handleCsPropertyDecl(node, ctx) {
177
+ const nameNode = node.childForFieldName('name');
178
+ if (!nameNode) return;
179
+ const parentType = findCSharpParentType(node);
180
+ const fullName = parentType ? `${parentType}.${nameNode.text}` : nameNode.text;
181
+ ctx.definitions.push({
182
+ name: fullName,
183
+ kind: 'property',
184
+ line: node.startPosition.row + 1,
185
+ endLine: nodeEndLine(node),
186
+ visibility: extractModifierVisibility(node),
187
+ });
188
+ }
215
189
 
216
- case 'object_creation_expression': {
217
- const typeNode = node.childForFieldName('type');
218
- if (typeNode) {
219
- const typeName =
220
- typeNode.type === 'generic_name'
221
- ? typeNode.childForFieldName('name')?.text || typeNode.child(0)?.text
222
- : typeNode.text;
223
- if (typeName) calls.push({ name: typeName, line: node.startPosition.row + 1 });
224
- }
225
- break;
226
- }
227
- }
190
+ function handleCsUsingDirective(node, ctx) {
191
+ const nameNode =
192
+ node.childForFieldName('name') ||
193
+ findChild(node, 'qualified_name') ||
194
+ findChild(node, 'identifier');
195
+ if (!nameNode) return;
196
+ const fullPath = nameNode.text;
197
+ const lastName = fullPath.split('.').pop();
198
+ ctx.imports.push({
199
+ source: fullPath,
200
+ names: [lastName],
201
+ line: node.startPosition.row + 1,
202
+ csharpUsing: true,
203
+ });
204
+ }
228
205
 
229
- for (let i = 0; i < node.childCount; i++) walkCSharpNode(node.child(i));
206
+ function handleCsInvocationExpr(node, ctx) {
207
+ const fn = node.childForFieldName('function') || node.child(0);
208
+ if (!fn) return;
209
+ if (fn.type === 'identifier') {
210
+ ctx.calls.push({ name: fn.text, line: node.startPosition.row + 1 });
211
+ } else if (fn.type === 'member_access_expression') {
212
+ const name = fn.childForFieldName('name');
213
+ if (name) {
214
+ const expr = fn.childForFieldName('expression');
215
+ const call = { name: name.text, line: node.startPosition.row + 1 };
216
+ if (expr) call.receiver = expr.text;
217
+ ctx.calls.push(call);
218
+ }
219
+ } else if (fn.type === 'generic_name' || fn.type === 'member_binding_expression') {
220
+ const name = fn.childForFieldName('name') || fn.child(0);
221
+ if (name) ctx.calls.push({ name: name.text, line: node.startPosition.row + 1 });
230
222
  }
223
+ }
224
+
225
+ function handleCsObjectCreation(node, ctx) {
226
+ const typeNode = node.childForFieldName('type');
227
+ if (!typeNode) return;
228
+ const typeName =
229
+ typeNode.type === 'generic_name'
230
+ ? typeNode.childForFieldName('name')?.text || typeNode.child(0)?.text
231
+ : typeNode.text;
232
+ if (typeName) ctx.calls.push({ name: typeName, line: node.startPosition.row + 1 });
233
+ }
231
234
 
232
- walkCSharpNode(tree.rootNode);
233
- return { definitions, calls, imports, classes, exports };
235
+ function findCSharpParentType(node) {
236
+ let current = node.parent;
237
+ while (current) {
238
+ if (
239
+ current.type === 'class_declaration' ||
240
+ current.type === 'struct_declaration' ||
241
+ current.type === 'interface_declaration' ||
242
+ current.type === 'enum_declaration' ||
243
+ current.type === 'record_declaration'
244
+ ) {
245
+ const nameNode = current.childForFieldName('name');
246
+ return nameNode ? nameNode.text : null;
247
+ }
248
+ current = current.parent;
249
+ }
250
+ return null;
234
251
  }
235
252
 
236
253
  // ── Child extraction helpers ────────────────────────────────────────────────