tree-sitter-analyzer 0.1.3__py3-none-any.whl → 0.3.0__py3-none-any.whl
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.
Potentially problematic release.
This version of tree-sitter-analyzer might be problematic. Click here for more details.
- tree_sitter_analyzer/__init__.py +133 -121
- tree_sitter_analyzer/__main__.py +11 -12
- tree_sitter_analyzer/api.py +531 -539
- tree_sitter_analyzer/cli/__init__.py +39 -39
- tree_sitter_analyzer/cli/__main__.py +12 -13
- tree_sitter_analyzer/cli/commands/__init__.py +26 -27
- tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
- tree_sitter_analyzer/cli/commands/base_command.py +160 -155
- tree_sitter_analyzer/cli/commands/default_command.py +18 -19
- tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -133
- tree_sitter_analyzer/cli/commands/query_command.py +81 -82
- tree_sitter_analyzer/cli/commands/structure_command.py +138 -121
- tree_sitter_analyzer/cli/commands/summary_command.py +101 -93
- tree_sitter_analyzer/cli/commands/table_command.py +232 -233
- tree_sitter_analyzer/cli/info_commands.py +120 -121
- tree_sitter_analyzer/cli_main.py +277 -276
- tree_sitter_analyzer/core/__init__.py +15 -20
- tree_sitter_analyzer/core/analysis_engine.py +591 -574
- tree_sitter_analyzer/core/cache_service.py +320 -330
- tree_sitter_analyzer/core/engine.py +557 -560
- tree_sitter_analyzer/core/parser.py +293 -288
- tree_sitter_analyzer/core/query.py +494 -502
- tree_sitter_analyzer/encoding_utils.py +458 -460
- tree_sitter_analyzer/exceptions.py +337 -340
- tree_sitter_analyzer/file_handler.py +217 -222
- tree_sitter_analyzer/formatters/__init__.py +1 -1
- tree_sitter_analyzer/formatters/base_formatter.py +167 -168
- tree_sitter_analyzer/formatters/formatter_factory.py +78 -74
- tree_sitter_analyzer/formatters/java_formatter.py +287 -270
- tree_sitter_analyzer/formatters/python_formatter.py +255 -235
- tree_sitter_analyzer/interfaces/__init__.py +9 -10
- tree_sitter_analyzer/interfaces/cli.py +528 -557
- tree_sitter_analyzer/interfaces/cli_adapter.py +322 -319
- tree_sitter_analyzer/interfaces/mcp_adapter.py +180 -170
- tree_sitter_analyzer/interfaces/mcp_server.py +405 -416
- tree_sitter_analyzer/java_analyzer.py +218 -219
- tree_sitter_analyzer/language_detector.py +398 -400
- tree_sitter_analyzer/language_loader.py +224 -228
- tree_sitter_analyzer/languages/__init__.py +10 -11
- tree_sitter_analyzer/languages/java_plugin.py +1129 -1113
- tree_sitter_analyzer/languages/python_plugin.py +737 -712
- tree_sitter_analyzer/mcp/__init__.py +31 -32
- tree_sitter_analyzer/mcp/resources/__init__.py +44 -47
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +212 -213
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +560 -550
- tree_sitter_analyzer/mcp/server.py +333 -345
- tree_sitter_analyzer/mcp/tools/__init__.py +30 -31
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +621 -557
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +242 -245
- tree_sitter_analyzer/mcp/tools/base_tool.py +54 -55
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -302
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -359
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -476
- tree_sitter_analyzer/mcp/utils/__init__.py +105 -106
- tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
- tree_sitter_analyzer/models.py +470 -481
- tree_sitter_analyzer/output_manager.py +261 -264
- tree_sitter_analyzer/plugins/__init__.py +333 -334
- tree_sitter_analyzer/plugins/base.py +477 -446
- tree_sitter_analyzer/plugins/java_plugin.py +608 -625
- tree_sitter_analyzer/plugins/javascript_plugin.py +446 -439
- tree_sitter_analyzer/plugins/manager.py +362 -355
- tree_sitter_analyzer/plugins/plugin_loader.py +85 -83
- tree_sitter_analyzer/plugins/python_plugin.py +606 -598
- tree_sitter_analyzer/plugins/registry.py +374 -366
- tree_sitter_analyzer/queries/__init__.py +26 -27
- tree_sitter_analyzer/queries/java.py +391 -394
- tree_sitter_analyzer/queries/javascript.py +148 -149
- tree_sitter_analyzer/queries/python.py +285 -286
- tree_sitter_analyzer/queries/typescript.py +229 -230
- tree_sitter_analyzer/query_loader.py +254 -260
- tree_sitter_analyzer/table_formatter.py +468 -448
- tree_sitter_analyzer/utils.py +277 -277
- tree_sitter_analyzer-0.3.0.dist-info/METADATA +346 -0
- tree_sitter_analyzer-0.3.0.dist-info/RECORD +77 -0
- tree_sitter_analyzer-0.1.3.dist-info/METADATA +0 -444
- tree_sitter_analyzer-0.1.3.dist-info/RECORD +0 -77
- {tree_sitter_analyzer-0.1.3.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.1.3.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,476 +1,543 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import
|
|
11
|
-
from
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
from
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
"description": "
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
"description": "
|
|
64
|
-
"default":
|
|
65
|
-
},
|
|
66
|
-
"
|
|
67
|
-
"type": "boolean",
|
|
68
|
-
"description": "Include
|
|
69
|
-
"default": False,
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
""
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
#
|
|
113
|
-
if not language:
|
|
114
|
-
language
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
result = await self.
|
|
145
|
-
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
result.update(self.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
"
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
"""
|
|
319
|
-
|
|
320
|
-
"
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
"
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
#
|
|
442
|
-
if "
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Universal Code Analysis Tool for MCP
|
|
4
|
+
|
|
5
|
+
This tool provides universal code analysis capabilities for multiple programming
|
|
6
|
+
languages using the existing language detection and analysis infrastructure.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from ...core.analysis_engine import AnalysisRequest, get_analysis_engine
|
|
14
|
+
from ...language_detector import detect_language_from_file, is_language_supported
|
|
15
|
+
from ..utils import get_performance_monitor
|
|
16
|
+
from ..utils.error_handler import handle_mcp_errors
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class UniversalAnalyzeTool:
|
|
22
|
+
"""
|
|
23
|
+
Universal code analysis tool for multiple programming languages
|
|
24
|
+
|
|
25
|
+
This tool automatically detects the programming language and applies
|
|
26
|
+
the appropriate analyzer to provide comprehensive code analysis.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
"""Initialize the universal analysis tool"""
|
|
31
|
+
# Use unified analysis engine instead of deprecated AdvancedAnalyzer
|
|
32
|
+
self.analysis_engine = get_analysis_engine()
|
|
33
|
+
|
|
34
|
+
def get_tool_definition(self) -> dict[str, Any]:
|
|
35
|
+
"""
|
|
36
|
+
Get MCP tool definition for universal code analysis
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Tool definition dictionary
|
|
40
|
+
"""
|
|
41
|
+
return {
|
|
42
|
+
"name": "analyze_code_universal",
|
|
43
|
+
"description": "Universal code analysis for multiple programming languages with automatic language detection",
|
|
44
|
+
"inputSchema": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"properties": {
|
|
47
|
+
"file_path": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": "Path to the code file to analyze",
|
|
50
|
+
},
|
|
51
|
+
"language": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Programming language (optional, auto-detected if not specified)",
|
|
54
|
+
},
|
|
55
|
+
"analysis_type": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"enum": ["basic", "detailed", "structure", "metrics"],
|
|
58
|
+
"description": "Type of analysis to perform",
|
|
59
|
+
"default": "basic",
|
|
60
|
+
},
|
|
61
|
+
"include_ast": {
|
|
62
|
+
"type": "boolean",
|
|
63
|
+
"description": "Include AST information in the analysis",
|
|
64
|
+
"default": False,
|
|
65
|
+
},
|
|
66
|
+
"include_queries": {
|
|
67
|
+
"type": "boolean",
|
|
68
|
+
"description": "Include available query information",
|
|
69
|
+
"default": False,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
"required": ["file_path"],
|
|
73
|
+
"additionalProperties": False,
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@handle_mcp_errors("universal_analyze")
|
|
78
|
+
async def execute(self, arguments: dict[str, Any]) -> dict[str, Any]:
|
|
79
|
+
"""
|
|
80
|
+
Execute universal code analysis
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
arguments: Tool arguments containing file_path and optional parameters
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Dictionary containing analysis results
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
ValueError: If required arguments are missing or invalid
|
|
90
|
+
FileNotFoundError: If the specified file doesn't exist
|
|
91
|
+
"""
|
|
92
|
+
# Validate required arguments
|
|
93
|
+
if "file_path" not in arguments:
|
|
94
|
+
raise ValueError("file_path is required")
|
|
95
|
+
|
|
96
|
+
file_path = arguments["file_path"]
|
|
97
|
+
language = arguments.get("language")
|
|
98
|
+
analysis_type = arguments.get("analysis_type", "basic")
|
|
99
|
+
include_ast = arguments.get("include_ast", False)
|
|
100
|
+
include_queries = arguments.get("include_queries", False)
|
|
101
|
+
|
|
102
|
+
# Validate file exists
|
|
103
|
+
if not Path(file_path).exists():
|
|
104
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
105
|
+
|
|
106
|
+
# Detect language if not specified
|
|
107
|
+
if not language:
|
|
108
|
+
language = detect_language_from_file(file_path)
|
|
109
|
+
if language == "unknown":
|
|
110
|
+
raise ValueError(f"Could not detect language for file: {file_path}")
|
|
111
|
+
|
|
112
|
+
# Check if language is supported
|
|
113
|
+
if not is_language_supported(language):
|
|
114
|
+
raise ValueError(f"Language '{language}' is not supported by tree-sitter")
|
|
115
|
+
|
|
116
|
+
# Validate analysis_type
|
|
117
|
+
valid_analysis_types = ["basic", "detailed", "structure", "metrics"]
|
|
118
|
+
if analysis_type not in valid_analysis_types:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
f"Invalid analysis_type '{analysis_type}'. Valid types: {', '.join(valid_analysis_types)}"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
logger.info(
|
|
124
|
+
f"Analyzing {file_path} (language: {language}, type: {analysis_type})"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
monitor = get_performance_monitor()
|
|
129
|
+
with monitor.measure_operation("universal_analyze"):
|
|
130
|
+
# Get appropriate analyzer
|
|
131
|
+
if language == "java":
|
|
132
|
+
# Use advanced analyzer for Java
|
|
133
|
+
result = await self._analyze_with_advanced_analyzer(
|
|
134
|
+
file_path, language, analysis_type, include_ast
|
|
135
|
+
)
|
|
136
|
+
else:
|
|
137
|
+
# Use universal analyzer for other languages
|
|
138
|
+
result = await self._analyze_with_universal_analyzer(
|
|
139
|
+
file_path, language, analysis_type, include_ast
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Add query information if requested
|
|
143
|
+
if include_queries:
|
|
144
|
+
result["available_queries"] = await self._get_available_queries(
|
|
145
|
+
language
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
logger.info(f"Successfully analyzed {file_path}")
|
|
149
|
+
return result
|
|
150
|
+
|
|
151
|
+
except Exception as e:
|
|
152
|
+
logger.error(f"Error analyzing {file_path}: {e}")
|
|
153
|
+
raise
|
|
154
|
+
|
|
155
|
+
async def _analyze_with_advanced_analyzer(
|
|
156
|
+
self, file_path: str, language: str, analysis_type: str, include_ast: bool
|
|
157
|
+
) -> dict[str, Any]:
|
|
158
|
+
"""
|
|
159
|
+
Analyze using the advanced analyzer (Java-specific)
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
file_path: Path to the file to analyze
|
|
163
|
+
language: Programming language
|
|
164
|
+
analysis_type: Type of analysis to perform
|
|
165
|
+
include_ast: Whether to include AST information
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Analysis results dictionary
|
|
169
|
+
"""
|
|
170
|
+
# Use unified analysis engine instead of deprecated advanced_analyzer
|
|
171
|
+
request = AnalysisRequest(
|
|
172
|
+
file_path=file_path,
|
|
173
|
+
language=language,
|
|
174
|
+
include_complexity=True,
|
|
175
|
+
include_details=True,
|
|
176
|
+
)
|
|
177
|
+
analysis_result = await self.analysis_engine.analyze(request)
|
|
178
|
+
|
|
179
|
+
if analysis_result is None:
|
|
180
|
+
raise RuntimeError(f"Failed to analyze file: {file_path}")
|
|
181
|
+
|
|
182
|
+
# Build base result
|
|
183
|
+
result: dict[str, Any] = {
|
|
184
|
+
"file_path": file_path,
|
|
185
|
+
"language": language,
|
|
186
|
+
"analyzer_type": "advanced",
|
|
187
|
+
"analysis_type": analysis_type,
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if analysis_type == "basic":
|
|
191
|
+
result.update(self._extract_basic_metrics(analysis_result))
|
|
192
|
+
elif analysis_type == "detailed":
|
|
193
|
+
result.update(self._extract_detailed_metrics(analysis_result))
|
|
194
|
+
elif analysis_type == "structure":
|
|
195
|
+
result.update(self._extract_structure_info(analysis_result))
|
|
196
|
+
elif analysis_type == "metrics":
|
|
197
|
+
result.update(self._extract_comprehensive_metrics(analysis_result))
|
|
198
|
+
|
|
199
|
+
if include_ast:
|
|
200
|
+
result["ast_info"] = {
|
|
201
|
+
"node_count": getattr(
|
|
202
|
+
analysis_result, "line_count", 0
|
|
203
|
+
), # Approximation
|
|
204
|
+
"depth": 0, # Advanced analyzer doesn't provide this, use 0 instead of string
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return result
|
|
208
|
+
|
|
209
|
+
async def _analyze_with_universal_analyzer(
|
|
210
|
+
self, file_path: str, language: str, analysis_type: str, include_ast: bool
|
|
211
|
+
) -> dict[str, Any]:
|
|
212
|
+
"""
|
|
213
|
+
Analyze using the universal analyzer
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
file_path: Path to the file to analyze
|
|
217
|
+
language: Programming language
|
|
218
|
+
analysis_type: Type of analysis to perform
|
|
219
|
+
include_ast: Whether to include AST information
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Analysis results dictionary
|
|
223
|
+
"""
|
|
224
|
+
request = AnalysisRequest(
|
|
225
|
+
file_path=file_path,
|
|
226
|
+
language=language,
|
|
227
|
+
include_details=(analysis_type == "detailed"),
|
|
228
|
+
)
|
|
229
|
+
analysis_result = await self.analysis_engine.analyze(request)
|
|
230
|
+
|
|
231
|
+
if not analysis_result or not analysis_result.success:
|
|
232
|
+
error_message = (
|
|
233
|
+
analysis_result.error_message if analysis_result else "Unknown error"
|
|
234
|
+
)
|
|
235
|
+
raise RuntimeError(f"Failed to analyze file: {file_path} - {error_message}")
|
|
236
|
+
|
|
237
|
+
# Convert AnalysisResult to dictionary for consistent processing
|
|
238
|
+
analysis_dict = analysis_result.to_dict()
|
|
239
|
+
|
|
240
|
+
# Build base result
|
|
241
|
+
result: dict[str, Any] = {
|
|
242
|
+
"file_path": file_path,
|
|
243
|
+
"language": language,
|
|
244
|
+
"analyzer_type": "universal",
|
|
245
|
+
"analysis_type": analysis_type,
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if analysis_type == "basic":
|
|
249
|
+
result.update(self._extract_universal_basic_metrics(analysis_dict))
|
|
250
|
+
elif analysis_type == "detailed":
|
|
251
|
+
result.update(self._extract_universal_detailed_metrics(analysis_dict))
|
|
252
|
+
elif analysis_type == "structure":
|
|
253
|
+
result.update(self._extract_universal_structure_info(analysis_dict))
|
|
254
|
+
elif analysis_type == "metrics":
|
|
255
|
+
result.update(self._extract_universal_comprehensive_metrics(analysis_dict))
|
|
256
|
+
|
|
257
|
+
if include_ast:
|
|
258
|
+
result["ast_info"] = analysis_dict.get("ast_info", {})
|
|
259
|
+
|
|
260
|
+
return result
|
|
261
|
+
|
|
262
|
+
def _extract_basic_metrics(self, analysis_result: Any) -> dict[str, Any]:
|
|
263
|
+
"""Extract basic metrics from advanced analyzer result"""
|
|
264
|
+
stats = analysis_result.get_statistics()
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
"metrics": {
|
|
268
|
+
"lines_total": analysis_result.line_count,
|
|
269
|
+
"lines_code": stats.get("lines_of_code", 0),
|
|
270
|
+
"lines_comment": stats.get("comment_lines", 0),
|
|
271
|
+
"lines_blank": stats.get("blank_lines", 0),
|
|
272
|
+
"elements": {
|
|
273
|
+
"classes": len(
|
|
274
|
+
[
|
|
275
|
+
e
|
|
276
|
+
for e in analysis_result.elements
|
|
277
|
+
if e.__class__.__name__ == "Class"
|
|
278
|
+
]
|
|
279
|
+
),
|
|
280
|
+
"methods": len(
|
|
281
|
+
[
|
|
282
|
+
e
|
|
283
|
+
for e in analysis_result.elements
|
|
284
|
+
if e.__class__.__name__ == "Function"
|
|
285
|
+
]
|
|
286
|
+
),
|
|
287
|
+
"fields": len(
|
|
288
|
+
[
|
|
289
|
+
e
|
|
290
|
+
for e in analysis_result.elements
|
|
291
|
+
if e.__class__.__name__ == "Variable"
|
|
292
|
+
]
|
|
293
|
+
),
|
|
294
|
+
"imports": len(
|
|
295
|
+
[
|
|
296
|
+
e
|
|
297
|
+
for e in analysis_result.elements
|
|
298
|
+
if e.__class__.__name__ == "Import"
|
|
299
|
+
]
|
|
300
|
+
),
|
|
301
|
+
"annotations": len(getattr(analysis_result, "annotations", [])),
|
|
302
|
+
},
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
def _extract_detailed_metrics(self, analysis_result: Any) -> dict[str, Any]:
|
|
307
|
+
"""Extract detailed metrics from advanced analyzer result"""
|
|
308
|
+
basic = self._extract_basic_metrics(analysis_result)
|
|
309
|
+
|
|
310
|
+
# Add complexity metrics
|
|
311
|
+
methods = [
|
|
312
|
+
e for e in analysis_result.elements if e.__class__.__name__ == "Function"
|
|
313
|
+
]
|
|
314
|
+
total_complexity = sum(
|
|
315
|
+
getattr(method, "complexity_score", 0) or 0 for method in methods
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
basic["metrics"]["complexity"] = {
|
|
319
|
+
"total": total_complexity,
|
|
320
|
+
"average": (total_complexity / len(methods) if methods else 0),
|
|
321
|
+
"max": max(
|
|
322
|
+
(getattr(method, "complexity_score", 0) or 0 for method in methods),
|
|
323
|
+
default=0,
|
|
324
|
+
),
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return basic
|
|
328
|
+
|
|
329
|
+
def _extract_structure_info(self, analysis_result: Any) -> dict[str, Any]:
|
|
330
|
+
"""Extract structure information from advanced analyzer result"""
|
|
331
|
+
return {
|
|
332
|
+
"structure": {
|
|
333
|
+
"package": (
|
|
334
|
+
analysis_result.package.name if analysis_result.package else None
|
|
335
|
+
),
|
|
336
|
+
"classes": [
|
|
337
|
+
(
|
|
338
|
+
cls.to_summary_item()
|
|
339
|
+
if hasattr(cls, "to_summary_item")
|
|
340
|
+
else {"name": getattr(cls, "name", "unknown")}
|
|
341
|
+
)
|
|
342
|
+
for cls in [
|
|
343
|
+
e
|
|
344
|
+
for e in analysis_result.elements
|
|
345
|
+
if e.__class__.__name__ == "Class"
|
|
346
|
+
]
|
|
347
|
+
],
|
|
348
|
+
"methods": [
|
|
349
|
+
(
|
|
350
|
+
method.to_summary_item()
|
|
351
|
+
if hasattr(method, "to_summary_item")
|
|
352
|
+
else {"name": getattr(method, "name", "unknown")}
|
|
353
|
+
)
|
|
354
|
+
for method in [
|
|
355
|
+
e
|
|
356
|
+
for e in analysis_result.elements
|
|
357
|
+
if e.__class__.__name__ == "Function"
|
|
358
|
+
]
|
|
359
|
+
],
|
|
360
|
+
"fields": [
|
|
361
|
+
(
|
|
362
|
+
field.to_summary_item()
|
|
363
|
+
if hasattr(field, "to_summary_item")
|
|
364
|
+
else {"name": getattr(field, "name", "unknown")}
|
|
365
|
+
)
|
|
366
|
+
for field in [
|
|
367
|
+
e
|
|
368
|
+
for e in analysis_result.elements
|
|
369
|
+
if e.__class__.__name__ == "Variable"
|
|
370
|
+
]
|
|
371
|
+
],
|
|
372
|
+
"imports": [
|
|
373
|
+
(
|
|
374
|
+
imp.to_summary_item()
|
|
375
|
+
if hasattr(imp, "to_summary_item")
|
|
376
|
+
else {"name": getattr(imp, "name", "unknown")}
|
|
377
|
+
)
|
|
378
|
+
for imp in [
|
|
379
|
+
e
|
|
380
|
+
for e in analysis_result.elements
|
|
381
|
+
if e.__class__.__name__ == "Import"
|
|
382
|
+
]
|
|
383
|
+
],
|
|
384
|
+
"annotations": [
|
|
385
|
+
(
|
|
386
|
+
ann.to_summary_item()
|
|
387
|
+
if hasattr(ann, "to_summary_item")
|
|
388
|
+
else {"name": getattr(ann, "name", "unknown")}
|
|
389
|
+
)
|
|
390
|
+
for ann in getattr(analysis_result, "annotations", [])
|
|
391
|
+
],
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
def _extract_comprehensive_metrics(self, analysis_result: Any) -> dict[str, Any]:
|
|
396
|
+
"""Extract comprehensive metrics from advanced analyzer result"""
|
|
397
|
+
detailed = self._extract_detailed_metrics(analysis_result)
|
|
398
|
+
structure = self._extract_structure_info(analysis_result)
|
|
399
|
+
|
|
400
|
+
# Combine both
|
|
401
|
+
result = detailed.copy()
|
|
402
|
+
result.update(structure)
|
|
403
|
+
|
|
404
|
+
return result
|
|
405
|
+
|
|
406
|
+
def _extract_universal_basic_metrics(
|
|
407
|
+
self, analysis_result: dict[str, Any]
|
|
408
|
+
) -> dict[str, Any]:
|
|
409
|
+
"""Extract basic metrics from universal analyzer result"""
|
|
410
|
+
elements = analysis_result.get("elements", [])
|
|
411
|
+
return {
|
|
412
|
+
"metrics": {
|
|
413
|
+
"lines_total": analysis_result.get("line_count", 0),
|
|
414
|
+
"lines_code": analysis_result.get("line_count", 0), # Approximation
|
|
415
|
+
"lines_comment": 0, # Not available in universal analyzer
|
|
416
|
+
"lines_blank": 0, # Not available in universal analyzer
|
|
417
|
+
"elements": {
|
|
418
|
+
"classes": len(
|
|
419
|
+
[e for e in elements if e.get("__class__", "") == "Class"]
|
|
420
|
+
),
|
|
421
|
+
"methods": len(
|
|
422
|
+
[e for e in elements if e.get("__class__", "") == "Function"]
|
|
423
|
+
),
|
|
424
|
+
"fields": len(
|
|
425
|
+
[e for e in elements if e.get("__class__", "") == "Variable"]
|
|
426
|
+
),
|
|
427
|
+
"imports": len(
|
|
428
|
+
[e for e in elements if e.get("__class__", "") == "Import"]
|
|
429
|
+
),
|
|
430
|
+
"annotations": 0, # Not available in universal analyzer
|
|
431
|
+
},
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
def _extract_universal_detailed_metrics(
|
|
436
|
+
self, analysis_result: dict[str, Any]
|
|
437
|
+
) -> dict[str, Any]:
|
|
438
|
+
"""Extract detailed metrics from universal analyzer result"""
|
|
439
|
+
basic = self._extract_universal_basic_metrics(analysis_result)
|
|
440
|
+
|
|
441
|
+
# Add query results if available
|
|
442
|
+
if "query_results" in analysis_result:
|
|
443
|
+
basic["query_results"] = analysis_result["query_results"]
|
|
444
|
+
|
|
445
|
+
return basic
|
|
446
|
+
|
|
447
|
+
def _extract_universal_structure_info(
|
|
448
|
+
self, analysis_result: dict[str, Any]
|
|
449
|
+
) -> dict[str, Any]:
|
|
450
|
+
"""Extract structure information from universal analyzer result"""
|
|
451
|
+
return {
|
|
452
|
+
"structure": analysis_result.get("structure", {}),
|
|
453
|
+
"queries_executed": analysis_result.get("queries_executed", []),
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
def _extract_universal_comprehensive_metrics(
|
|
457
|
+
self, analysis_result: dict[str, Any]
|
|
458
|
+
) -> dict[str, Any]:
|
|
459
|
+
"""Extract comprehensive metrics from universal analyzer result"""
|
|
460
|
+
detailed = self._extract_universal_detailed_metrics(analysis_result)
|
|
461
|
+
structure = self._extract_universal_structure_info(analysis_result)
|
|
462
|
+
|
|
463
|
+
# Combine both
|
|
464
|
+
result = detailed.copy()
|
|
465
|
+
result.update(structure)
|
|
466
|
+
|
|
467
|
+
return result
|
|
468
|
+
|
|
469
|
+
async def _get_available_queries(self, language: str) -> dict[str, Any]:
|
|
470
|
+
"""
|
|
471
|
+
Get available queries for the specified language
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
language: Programming language
|
|
475
|
+
|
|
476
|
+
Returns:
|
|
477
|
+
Dictionary containing available queries information
|
|
478
|
+
"""
|
|
479
|
+
try:
|
|
480
|
+
if language == "java":
|
|
481
|
+
# For Java, we don't have predefined queries in the advanced analyzer
|
|
482
|
+
return {
|
|
483
|
+
"language": language,
|
|
484
|
+
"queries": [],
|
|
485
|
+
"note": "Advanced analyzer uses built-in analysis logic",
|
|
486
|
+
}
|
|
487
|
+
else:
|
|
488
|
+
# For other languages, get from universal analyzer
|
|
489
|
+
queries = self.analysis_engine.get_supported_languages()
|
|
490
|
+
return {"language": language, "queries": queries, "count": len(queries)}
|
|
491
|
+
except Exception as e:
|
|
492
|
+
logger.warning(f"Failed to get queries for {language}: {e}")
|
|
493
|
+
return {"language": language, "queries": [], "error": str(e)}
|
|
494
|
+
|
|
495
|
+
def validate_arguments(self, arguments: dict[str, Any]) -> bool:
|
|
496
|
+
"""
|
|
497
|
+
Validate tool arguments against the schema.
|
|
498
|
+
|
|
499
|
+
Args:
|
|
500
|
+
arguments: Arguments to validate
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
True if arguments are valid
|
|
504
|
+
|
|
505
|
+
Raises:
|
|
506
|
+
ValueError: If arguments are invalid
|
|
507
|
+
"""
|
|
508
|
+
# Check required fields
|
|
509
|
+
if "file_path" not in arguments:
|
|
510
|
+
raise ValueError("Required field 'file_path' is missing")
|
|
511
|
+
|
|
512
|
+
# Validate file_path
|
|
513
|
+
file_path = arguments["file_path"]
|
|
514
|
+
if not isinstance(file_path, str):
|
|
515
|
+
raise ValueError("file_path must be a string")
|
|
516
|
+
if not file_path.strip():
|
|
517
|
+
raise ValueError("file_path cannot be empty")
|
|
518
|
+
|
|
519
|
+
# Validate optional fields
|
|
520
|
+
if "language" in arguments:
|
|
521
|
+
language = arguments["language"]
|
|
522
|
+
if not isinstance(language, str):
|
|
523
|
+
raise ValueError("language must be a string")
|
|
524
|
+
|
|
525
|
+
if "analysis_type" in arguments:
|
|
526
|
+
analysis_type = arguments["analysis_type"]
|
|
527
|
+
if not isinstance(analysis_type, str):
|
|
528
|
+
raise ValueError("analysis_type must be a string")
|
|
529
|
+
valid_types = ["basic", "detailed", "structure", "metrics"]
|
|
530
|
+
if analysis_type not in valid_types:
|
|
531
|
+
raise ValueError(f"analysis_type must be one of {valid_types}")
|
|
532
|
+
|
|
533
|
+
if "include_ast" in arguments:
|
|
534
|
+
include_ast = arguments["include_ast"]
|
|
535
|
+
if not isinstance(include_ast, bool):
|
|
536
|
+
raise ValueError("include_ast must be a boolean")
|
|
537
|
+
|
|
538
|
+
if "include_queries" in arguments:
|
|
539
|
+
include_queries = arguments["include_queries"]
|
|
540
|
+
if not isinstance(include_queries, bool):
|
|
541
|
+
raise ValueError("include_queries must be a boolean")
|
|
542
|
+
|
|
543
|
+
return True
|