@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,239 +4,245 @@ import { extractModifierVisibility, findChild, nodeEndLine } from './helpers.js'
4
4
  * Extract symbols from Java files.
5
5
  */
6
6
  export function extractJavaSymbols(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 findJavaParentClass(node) {
14
- let current = node.parent;
15
- while (current) {
16
- if (
17
- current.type === 'class_declaration' ||
18
- current.type === 'enum_declaration' ||
19
- current.type === 'interface_declaration'
20
- ) {
21
- const nameNode = current.childForFieldName('name');
22
- return nameNode ? nameNode.text : null;
23
- }
24
- current = current.parent;
25
- }
26
- return null;
15
+ walkJavaNode(tree.rootNode, ctx);
16
+ return ctx;
17
+ }
18
+
19
+ function walkJavaNode(node, ctx) {
20
+ switch (node.type) {
21
+ case 'class_declaration':
22
+ handleJavaClassDecl(node, ctx);
23
+ break;
24
+ case 'interface_declaration':
25
+ handleJavaInterfaceDecl(node, ctx);
26
+ break;
27
+ case 'enum_declaration':
28
+ handleJavaEnumDecl(node, ctx);
29
+ break;
30
+ case 'method_declaration':
31
+ handleJavaMethodDecl(node, ctx);
32
+ break;
33
+ case 'constructor_declaration':
34
+ handleJavaConstructorDecl(node, ctx);
35
+ break;
36
+ case 'import_declaration':
37
+ handleJavaImportDecl(node, ctx);
38
+ break;
39
+ case 'method_invocation':
40
+ handleJavaMethodInvocation(node, ctx);
41
+ break;
42
+ case 'object_creation_expression':
43
+ handleJavaObjectCreation(node, ctx);
44
+ break;
27
45
  }
28
46
 
29
- function walkJavaNode(node) {
30
- switch (node.type) {
31
- case 'class_declaration': {
32
- const nameNode = node.childForFieldName('name');
33
- if (nameNode) {
34
- const classChildren = extractClassFields(node);
35
- definitions.push({
36
- name: nameNode.text,
37
- kind: 'class',
38
- line: node.startPosition.row + 1,
39
- endLine: nodeEndLine(node),
40
- children: classChildren.length > 0 ? classChildren : undefined,
41
- });
47
+ for (let i = 0; i < node.childCount; i++) walkJavaNode(node.child(i), ctx);
48
+ }
42
49
 
43
- const superclass = node.childForFieldName('superclass');
44
- if (superclass) {
45
- for (let i = 0; i < superclass.childCount; i++) {
46
- const child = superclass.child(i);
47
- if (
48
- child &&
49
- (child.type === 'type_identifier' ||
50
- child.type === 'identifier' ||
51
- child.type === 'generic_type')
52
- ) {
53
- const superName = child.type === 'generic_type' ? child.child(0)?.text : child.text;
54
- if (superName)
55
- classes.push({
56
- name: nameNode.text,
57
- extends: superName,
58
- line: node.startPosition.row + 1,
59
- });
60
- break;
61
- }
62
- }
63
- }
50
+ // ── Walk-path per-node-type handlers ────────────────────────────────────────
64
51
 
65
- const interfaces = node.childForFieldName('interfaces');
66
- if (interfaces) {
67
- for (let i = 0; i < interfaces.childCount; i++) {
68
- const child = interfaces.child(i);
69
- if (
70
- child &&
71
- (child.type === 'type_identifier' ||
72
- child.type === 'identifier' ||
73
- child.type === 'type_list' ||
74
- child.type === 'generic_type')
75
- ) {
76
- if (child.type === 'type_list') {
77
- for (let j = 0; j < child.childCount; j++) {
78
- const t = child.child(j);
79
- if (
80
- t &&
81
- (t.type === 'type_identifier' ||
82
- t.type === 'identifier' ||
83
- t.type === 'generic_type')
84
- ) {
85
- const ifaceName = t.type === 'generic_type' ? t.child(0)?.text : t.text;
86
- if (ifaceName)
87
- classes.push({
88
- name: nameNode.text,
89
- implements: ifaceName,
90
- line: node.startPosition.row + 1,
91
- });
92
- }
93
- }
94
- } else {
95
- const ifaceName =
96
- child.type === 'generic_type' ? child.child(0)?.text : child.text;
97
- if (ifaceName)
98
- classes.push({
99
- name: nameNode.text,
100
- implements: ifaceName,
101
- line: node.startPosition.row + 1,
102
- });
103
- }
104
- }
105
- }
106
- }
107
- }
108
- break;
109
- }
52
+ function handleJavaClassDecl(node, ctx) {
53
+ const nameNode = node.childForFieldName('name');
54
+ if (!nameNode) return;
55
+ const classChildren = extractClassFields(node);
56
+ ctx.definitions.push({
57
+ name: nameNode.text,
58
+ kind: 'class',
59
+ line: node.startPosition.row + 1,
60
+ endLine: nodeEndLine(node),
61
+ children: classChildren.length > 0 ? classChildren : undefined,
62
+ });
110
63
 
111
- case 'interface_declaration': {
112
- const nameNode = node.childForFieldName('name');
113
- if (nameNode) {
114
- definitions.push({
64
+ const superclass = node.childForFieldName('superclass');
65
+ if (superclass) {
66
+ for (let i = 0; i < superclass.childCount; i++) {
67
+ const child = superclass.child(i);
68
+ if (
69
+ child &&
70
+ (child.type === 'type_identifier' ||
71
+ child.type === 'identifier' ||
72
+ child.type === 'generic_type')
73
+ ) {
74
+ const superName = child.type === 'generic_type' ? child.child(0)?.text : child.text;
75
+ if (superName)
76
+ ctx.classes.push({
115
77
  name: nameNode.text,
116
- kind: 'interface',
78
+ extends: superName,
117
79
  line: node.startPosition.row + 1,
118
- endLine: nodeEndLine(node),
119
80
  });
120
- const body = node.childForFieldName('body');
121
- if (body) {
122
- for (let i = 0; i < body.childCount; i++) {
123
- const child = body.child(i);
124
- if (child && child.type === 'method_declaration') {
125
- const methName = child.childForFieldName('name');
126
- if (methName) {
127
- definitions.push({
128
- name: `${nameNode.text}.${methName.text}`,
129
- kind: 'method',
130
- line: child.startPosition.row + 1,
131
- endLine: child.endPosition.row + 1,
132
- });
133
- }
134
- }
135
- }
136
- }
137
- }
138
81
  break;
139
82
  }
83
+ }
84
+ }
140
85
 
141
- case 'enum_declaration': {
142
- const nameNode = node.childForFieldName('name');
143
- if (nameNode) {
144
- const enumChildren = extractEnumConstants(node);
145
- definitions.push({
146
- name: nameNode.text,
147
- kind: 'enum',
148
- line: node.startPosition.row + 1,
149
- endLine: nodeEndLine(node),
150
- children: enumChildren.length > 0 ? enumChildren : undefined,
151
- });
152
- }
153
- break;
154
- }
86
+ const interfaces = node.childForFieldName('interfaces');
87
+ if (interfaces) {
88
+ extractJavaInterfaces(interfaces, nameNode.text, node.startPosition.row + 1, ctx);
89
+ }
90
+ }
155
91
 
156
- case 'method_declaration': {
157
- const nameNode = node.childForFieldName('name');
158
- if (nameNode) {
159
- const parentClass = findJavaParentClass(node);
160
- const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
161
- const params = extractJavaParameters(node.childForFieldName('parameters'));
162
- definitions.push({
163
- name: fullName,
164
- kind: 'method',
165
- line: node.startPosition.row + 1,
166
- endLine: nodeEndLine(node),
167
- children: params.length > 0 ? params : undefined,
168
- visibility: extractModifierVisibility(node),
169
- });
92
+ function extractJavaInterfaces(interfaces, className, line, ctx) {
93
+ for (let i = 0; i < interfaces.childCount; i++) {
94
+ const child = interfaces.child(i);
95
+ if (
96
+ child &&
97
+ (child.type === 'type_identifier' ||
98
+ child.type === 'identifier' ||
99
+ child.type === 'type_list' ||
100
+ child.type === 'generic_type')
101
+ ) {
102
+ if (child.type === 'type_list') {
103
+ for (let j = 0; j < child.childCount; j++) {
104
+ const t = child.child(j);
105
+ if (
106
+ t &&
107
+ (t.type === 'type_identifier' || t.type === 'identifier' || t.type === 'generic_type')
108
+ ) {
109
+ const ifaceName = t.type === 'generic_type' ? t.child(0)?.text : t.text;
110
+ if (ifaceName) ctx.classes.push({ name: className, implements: ifaceName, line });
111
+ }
170
112
  }
171
- break;
113
+ } else {
114
+ const ifaceName = child.type === 'generic_type' ? child.child(0)?.text : child.text;
115
+ if (ifaceName) ctx.classes.push({ name: className, implements: ifaceName, line });
172
116
  }
117
+ }
118
+ }
119
+ }
173
120
 
174
- case 'constructor_declaration': {
175
- const nameNode = node.childForFieldName('name');
176
- if (nameNode) {
177
- const parentClass = findJavaParentClass(node);
178
- const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
179
- const params = extractJavaParameters(node.childForFieldName('parameters'));
180
- definitions.push({
181
- name: fullName,
121
+ function handleJavaInterfaceDecl(node, ctx) {
122
+ const nameNode = node.childForFieldName('name');
123
+ if (!nameNode) return;
124
+ ctx.definitions.push({
125
+ name: nameNode.text,
126
+ kind: 'interface',
127
+ line: node.startPosition.row + 1,
128
+ endLine: nodeEndLine(node),
129
+ });
130
+ const body = node.childForFieldName('body');
131
+ if (body) {
132
+ for (let i = 0; i < body.childCount; i++) {
133
+ const child = body.child(i);
134
+ if (child && child.type === 'method_declaration') {
135
+ const methName = child.childForFieldName('name');
136
+ if (methName) {
137
+ ctx.definitions.push({
138
+ name: `${nameNode.text}.${methName.text}`,
182
139
  kind: 'method',
183
- line: node.startPosition.row + 1,
184
- endLine: nodeEndLine(node),
185
- children: params.length > 0 ? params : undefined,
186
- visibility: extractModifierVisibility(node),
140
+ line: child.startPosition.row + 1,
141
+ endLine: child.endPosition.row + 1,
187
142
  });
188
143
  }
189
- break;
190
144
  }
145
+ }
146
+ }
147
+ }
191
148
 
192
- case 'import_declaration': {
193
- for (let i = 0; i < node.childCount; i++) {
194
- const child = node.child(i);
195
- if (child && (child.type === 'scoped_identifier' || child.type === 'identifier')) {
196
- const fullPath = child.text;
197
- const lastName = fullPath.split('.').pop();
198
- imports.push({
199
- source: fullPath,
200
- names: [lastName],
201
- line: node.startPosition.row + 1,
202
- javaImport: true,
203
- });
204
- }
205
- if (child && child.type === 'asterisk') {
206
- const lastImport = imports[imports.length - 1];
207
- if (lastImport) lastImport.names = ['*'];
208
- }
209
- }
210
- break;
211
- }
149
+ function handleJavaEnumDecl(node, ctx) {
150
+ const nameNode = node.childForFieldName('name');
151
+ if (!nameNode) return;
152
+ const enumChildren = extractEnumConstants(node);
153
+ ctx.definitions.push({
154
+ name: nameNode.text,
155
+ kind: 'enum',
156
+ line: node.startPosition.row + 1,
157
+ endLine: nodeEndLine(node),
158
+ children: enumChildren.length > 0 ? enumChildren : undefined,
159
+ });
160
+ }
212
161
 
213
- case 'method_invocation': {
214
- const nameNode = node.childForFieldName('name');
215
- if (nameNode) {
216
- const obj = node.childForFieldName('object');
217
- const call = { name: nameNode.text, line: node.startPosition.row + 1 };
218
- if (obj) call.receiver = obj.text;
219
- calls.push(call);
220
- }
221
- break;
222
- }
162
+ function handleJavaMethodDecl(node, ctx) {
163
+ // Skip interface methods already emitted by handleJavaInterfaceDecl
164
+ if (node.parent?.parent?.type === 'interface_declaration') return;
165
+ const nameNode = node.childForFieldName('name');
166
+ if (!nameNode) return;
167
+ const parentClass = findJavaParentClass(node);
168
+ const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
169
+ const params = extractJavaParameters(node.childForFieldName('parameters'));
170
+ ctx.definitions.push({
171
+ name: fullName,
172
+ kind: 'method',
173
+ line: node.startPosition.row + 1,
174
+ endLine: nodeEndLine(node),
175
+ children: params.length > 0 ? params : undefined,
176
+ visibility: extractModifierVisibility(node),
177
+ });
178
+ }
223
179
 
224
- case 'object_creation_expression': {
225
- const typeNode = node.childForFieldName('type');
226
- if (typeNode) {
227
- const typeName =
228
- typeNode.type === 'generic_type' ? typeNode.child(0)?.text : typeNode.text;
229
- if (typeName) calls.push({ name: typeName, line: node.startPosition.row + 1 });
230
- }
231
- break;
232
- }
233
- }
180
+ function handleJavaConstructorDecl(node, ctx) {
181
+ const nameNode = node.childForFieldName('name');
182
+ if (!nameNode) return;
183
+ const parentClass = findJavaParentClass(node);
184
+ const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
185
+ const params = extractJavaParameters(node.childForFieldName('parameters'));
186
+ ctx.definitions.push({
187
+ name: fullName,
188
+ kind: 'method',
189
+ line: node.startPosition.row + 1,
190
+ endLine: nodeEndLine(node),
191
+ children: params.length > 0 ? params : undefined,
192
+ visibility: extractModifierVisibility(node),
193
+ });
194
+ }
234
195
 
235
- for (let i = 0; i < node.childCount; i++) walkJavaNode(node.child(i));
196
+ function handleJavaImportDecl(node, ctx) {
197
+ for (let i = 0; i < node.childCount; i++) {
198
+ const child = node.child(i);
199
+ if (child && (child.type === 'scoped_identifier' || child.type === 'identifier')) {
200
+ const fullPath = child.text;
201
+ const lastName = fullPath.split('.').pop();
202
+ ctx.imports.push({
203
+ source: fullPath,
204
+ names: [lastName],
205
+ line: node.startPosition.row + 1,
206
+ javaImport: true,
207
+ });
208
+ }
209
+ if (child && child.type === 'asterisk') {
210
+ const lastImport = ctx.imports[ctx.imports.length - 1];
211
+ if (lastImport) lastImport.names = ['*'];
212
+ }
236
213
  }
214
+ }
215
+
216
+ function handleJavaMethodInvocation(node, ctx) {
217
+ const nameNode = node.childForFieldName('name');
218
+ if (!nameNode) return;
219
+ const obj = node.childForFieldName('object');
220
+ const call = { name: nameNode.text, line: node.startPosition.row + 1 };
221
+ if (obj) call.receiver = obj.text;
222
+ ctx.calls.push(call);
223
+ }
237
224
 
238
- walkJavaNode(tree.rootNode);
239
- return { definitions, calls, imports, classes, exports };
225
+ function handleJavaObjectCreation(node, ctx) {
226
+ const typeNode = node.childForFieldName('type');
227
+ if (!typeNode) return;
228
+ const typeName = typeNode.type === 'generic_type' ? typeNode.child(0)?.text : typeNode.text;
229
+ if (typeName) ctx.calls.push({ name: typeName, line: node.startPosition.row + 1 });
230
+ }
231
+
232
+ function findJavaParentClass(node) {
233
+ let current = node.parent;
234
+ while (current) {
235
+ if (
236
+ current.type === 'class_declaration' ||
237
+ current.type === 'enum_declaration' ||
238
+ current.type === 'interface_declaration'
239
+ ) {
240
+ const nameNode = current.childForFieldName('name');
241
+ return nameNode ? nameNode.text : null;
242
+ }
243
+ current = current.parent;
244
+ }
245
+ return null;
240
246
  }
241
247
 
242
248
  // ── Child extraction helpers ────────────────────────────────────────────────