tree-sitter-analyzer 0.1.1__py3-none-any.whl → 0.1.2__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 +1 -1
- tree_sitter_analyzer/mcp/server.py +0 -11
- tree_sitter_analyzer/mcp/tools/__init__.py +0 -5
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +1 -2
- tree_sitter_analyzer/plugins/python_plugin.py +598 -598
- {tree_sitter_analyzer-0.1.1.dist-info → tree_sitter_analyzer-0.1.2.dist-info}/METADATA +69 -206
- {tree_sitter_analyzer-0.1.1.dist-info → tree_sitter_analyzer-0.1.2.dist-info}/RECORD +9 -10
- tree_sitter_analyzer/mcp/tools/get_positions_tool.py +0 -448
- {tree_sitter_analyzer-0.1.1.dist-info → tree_sitter_analyzer-0.1.2.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.1.1.dist-info → tree_sitter_analyzer-0.1.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,448 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
Get Code Positions MCP Tool
|
|
5
|
-
|
|
6
|
-
This tool provides precise position information for code elements
|
|
7
|
-
like classes, methods, fields, and imports through the MCP protocol.
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
import json
|
|
11
|
-
import logging
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import Any, Dict, List, Optional
|
|
14
|
-
|
|
15
|
-
from ...core.analysis_engine import get_analysis_engine, AnalysisRequest
|
|
16
|
-
from ...language_detector import detect_language_from_file
|
|
17
|
-
from ...utils import log_performance, setup_logger
|
|
18
|
-
|
|
19
|
-
# Set up logging
|
|
20
|
-
logger = setup_logger(__name__)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class GetPositionsTool:
|
|
24
|
-
"""
|
|
25
|
-
MCP Tool for getting precise position information of code elements.
|
|
26
|
-
|
|
27
|
-
This tool integrates with existing analyzer components to provide
|
|
28
|
-
detailed location data for various code elements.
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
def __init__(self) -> None:
|
|
32
|
-
"""Initialize the get positions tool."""
|
|
33
|
-
self.analysis_engine = get_analysis_engine()
|
|
34
|
-
logger.info("GetPositionsTool initialized")
|
|
35
|
-
|
|
36
|
-
def get_tool_schema(self) -> Dict[str, Any]:
|
|
37
|
-
"""
|
|
38
|
-
Get the MCP tool schema for get_code_positions.
|
|
39
|
-
|
|
40
|
-
Returns:
|
|
41
|
-
Dictionary containing the tool schema
|
|
42
|
-
"""
|
|
43
|
-
return {
|
|
44
|
-
"type": "object",
|
|
45
|
-
"properties": {
|
|
46
|
-
"file_path": {
|
|
47
|
-
"type": "string",
|
|
48
|
-
"description": "Path to the code file to analyze",
|
|
49
|
-
},
|
|
50
|
-
"language": {
|
|
51
|
-
"type": "string",
|
|
52
|
-
"description": "Programming language (optional, auto-detected if not specified)",
|
|
53
|
-
},
|
|
54
|
-
"element_types": {
|
|
55
|
-
"type": "array",
|
|
56
|
-
"items": {
|
|
57
|
-
"type": "string",
|
|
58
|
-
"enum": [
|
|
59
|
-
"classes",
|
|
60
|
-
"methods",
|
|
61
|
-
"fields",
|
|
62
|
-
"imports",
|
|
63
|
-
"annotations",
|
|
64
|
-
],
|
|
65
|
-
},
|
|
66
|
-
"description": "Types of code elements to get positions for",
|
|
67
|
-
"default": ["classes", "methods"],
|
|
68
|
-
},
|
|
69
|
-
"include_details": {
|
|
70
|
-
"type": "boolean",
|
|
71
|
-
"description": "Include detailed information like signatures and visibility",
|
|
72
|
-
"default": False,
|
|
73
|
-
},
|
|
74
|
-
"format": {
|
|
75
|
-
"type": "string",
|
|
76
|
-
"description": "Output format for the position data",
|
|
77
|
-
"enum": ["json", "text"],
|
|
78
|
-
"default": "json",
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
"required": ["file_path"],
|
|
82
|
-
"additionalProperties": False,
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async def execute(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
|
86
|
-
"""
|
|
87
|
-
Execute the get_code_positions tool.
|
|
88
|
-
|
|
89
|
-
Args:
|
|
90
|
-
arguments: Tool arguments containing file_path and analysis options
|
|
91
|
-
|
|
92
|
-
Returns:
|
|
93
|
-
Dictionary containing position information for requested elements
|
|
94
|
-
|
|
95
|
-
Raises:
|
|
96
|
-
ValueError: If required arguments are missing or invalid
|
|
97
|
-
FileNotFoundError: If the specified file doesn't exist
|
|
98
|
-
"""
|
|
99
|
-
# Validate required arguments
|
|
100
|
-
if "file_path" not in arguments:
|
|
101
|
-
raise ValueError("file_path is required")
|
|
102
|
-
|
|
103
|
-
file_path = arguments["file_path"]
|
|
104
|
-
language = arguments.get("language")
|
|
105
|
-
element_types = arguments.get("element_types", ["classes", "methods"])
|
|
106
|
-
include_details = arguments.get("include_details", False)
|
|
107
|
-
output_format = arguments.get("format", "json")
|
|
108
|
-
|
|
109
|
-
# Validate file exists
|
|
110
|
-
if not Path(file_path).exists():
|
|
111
|
-
raise FileNotFoundError(f"File not found: {file_path}")
|
|
112
|
-
|
|
113
|
-
# Detect language if not specified
|
|
114
|
-
if not language:
|
|
115
|
-
language = detect_language_from_file(file_path)
|
|
116
|
-
if language == "unknown":
|
|
117
|
-
raise ValueError(f"Could not detect language for file: {file_path}")
|
|
118
|
-
|
|
119
|
-
logger.info(f"Getting positions for {file_path} (language: {language})")
|
|
120
|
-
|
|
121
|
-
try:
|
|
122
|
-
# Use AdvancedAnalyzer for comprehensive analysis
|
|
123
|
-
# Use performance monitoring with proper context manager
|
|
124
|
-
from ...mcp.utils import get_performance_monitor
|
|
125
|
-
|
|
126
|
-
with get_performance_monitor().measure_operation("get_code_positions"):
|
|
127
|
-
# Use unified analysis engine instead of deprecated advanced_analyzer
|
|
128
|
-
request = AnalysisRequest(
|
|
129
|
-
file_path=file_path,
|
|
130
|
-
language=language,
|
|
131
|
-
include_complexity=True,
|
|
132
|
-
include_details=True
|
|
133
|
-
)
|
|
134
|
-
analysis_result = await self.analysis_engine.analyze(request)
|
|
135
|
-
|
|
136
|
-
if analysis_result is None:
|
|
137
|
-
raise RuntimeError(f"Failed to analyze file: {file_path}")
|
|
138
|
-
|
|
139
|
-
# Extract position information for requested element types
|
|
140
|
-
positions = {}
|
|
141
|
-
|
|
142
|
-
for element_type in element_types:
|
|
143
|
-
positions[element_type] = self._get_element_positions(
|
|
144
|
-
analysis_result, element_type, include_details
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
# Build result structure
|
|
148
|
-
result = {
|
|
149
|
-
"file_path": file_path,
|
|
150
|
-
"language": language,
|
|
151
|
-
"element_types": element_types,
|
|
152
|
-
"include_details": include_details,
|
|
153
|
-
"format": output_format,
|
|
154
|
-
"total_elements": sum(
|
|
155
|
-
len(pos_list) for pos_list in positions.values()
|
|
156
|
-
),
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
# Format output based on requested format
|
|
160
|
-
if output_format == "text":
|
|
161
|
-
# Text format: human-readable representation
|
|
162
|
-
result["content"] = self._format_positions_as_text(
|
|
163
|
-
positions, include_details
|
|
164
|
-
)
|
|
165
|
-
else:
|
|
166
|
-
# JSON format: structured position data
|
|
167
|
-
result["positions"] = positions
|
|
168
|
-
|
|
169
|
-
logger.info(
|
|
170
|
-
f"Successfully extracted positions for {result['total_elements']} elements from {file_path}"
|
|
171
|
-
)
|
|
172
|
-
return result
|
|
173
|
-
|
|
174
|
-
except Exception as e:
|
|
175
|
-
logger.error(f"Error getting positions from {file_path}: {e}")
|
|
176
|
-
raise
|
|
177
|
-
|
|
178
|
-
def _get_element_positions(
|
|
179
|
-
self, analysis_result: Any, element_type: str, include_details: bool
|
|
180
|
-
) -> List[Dict[str, Any]]:
|
|
181
|
-
"""
|
|
182
|
-
Extract position information for a specific element type.
|
|
183
|
-
|
|
184
|
-
Args:
|
|
185
|
-
analysis_result: Result from AdvancedAnalyzer
|
|
186
|
-
element_type: Type of elements to extract
|
|
187
|
-
include_details: Whether to include detailed information
|
|
188
|
-
|
|
189
|
-
Returns:
|
|
190
|
-
List of position dictionaries for the element type
|
|
191
|
-
"""
|
|
192
|
-
positions = []
|
|
193
|
-
|
|
194
|
-
if element_type == "classes":
|
|
195
|
-
classes = [e for e in analysis_result.elements if e.__class__.__name__ == 'Class']
|
|
196
|
-
for cls in classes:
|
|
197
|
-
position = {
|
|
198
|
-
"name": cls.name,
|
|
199
|
-
"start_line": cls.start_line,
|
|
200
|
-
"end_line": cls.end_line,
|
|
201
|
-
"start_column": 0, # Tree-sitter provides line-based positions
|
|
202
|
-
"end_column": 0,
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if include_details:
|
|
206
|
-
position.update(
|
|
207
|
-
{
|
|
208
|
-
"type": cls.class_type,
|
|
209
|
-
"visibility": cls.visibility,
|
|
210
|
-
"extends": cls.extends_class,
|
|
211
|
-
"implements": cls.implements_interfaces,
|
|
212
|
-
"annotations": [ann.name for ann in cls.annotations],
|
|
213
|
-
}
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
positions.append(position)
|
|
217
|
-
|
|
218
|
-
elif element_type == "methods":
|
|
219
|
-
methods = [e for e in analysis_result.elements if e.__class__.__name__ == 'Function']
|
|
220
|
-
for method in methods:
|
|
221
|
-
position = {
|
|
222
|
-
"name": method.name,
|
|
223
|
-
"start_line": method.start_line,
|
|
224
|
-
"end_line": method.end_line,
|
|
225
|
-
"start_column": 0,
|
|
226
|
-
"end_column": 0,
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if include_details:
|
|
230
|
-
position.update(
|
|
231
|
-
{
|
|
232
|
-
"signature": f"{method.name}({', '.join(method.parameters)})",
|
|
233
|
-
"return_type": method.return_type,
|
|
234
|
-
"visibility": method.visibility,
|
|
235
|
-
"is_static": method.is_static,
|
|
236
|
-
"is_constructor": method.is_constructor,
|
|
237
|
-
"complexity": method.complexity_score,
|
|
238
|
-
"annotations": [ann.name for ann in method.annotations],
|
|
239
|
-
}
|
|
240
|
-
)
|
|
241
|
-
|
|
242
|
-
positions.append(position)
|
|
243
|
-
|
|
244
|
-
elif element_type == "fields":
|
|
245
|
-
fields = [e for e in analysis_result.elements if e.__class__.__name__ == 'Variable']
|
|
246
|
-
for field in fields:
|
|
247
|
-
position = {
|
|
248
|
-
"name": field.name,
|
|
249
|
-
"start_line": field.start_line,
|
|
250
|
-
"end_line": field.end_line,
|
|
251
|
-
"start_column": 0,
|
|
252
|
-
"end_column": 0,
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if include_details:
|
|
256
|
-
position.update(
|
|
257
|
-
{
|
|
258
|
-
"type": field.field_type,
|
|
259
|
-
"visibility": field.visibility,
|
|
260
|
-
"is_static": field.is_static,
|
|
261
|
-
"is_final": field.is_final,
|
|
262
|
-
"annotations": [ann.name for ann in field.annotations],
|
|
263
|
-
}
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
positions.append(position)
|
|
267
|
-
|
|
268
|
-
elif element_type == "imports":
|
|
269
|
-
imports = [e for e in analysis_result.elements if e.__class__.__name__ == 'Import']
|
|
270
|
-
for imp in imports:
|
|
271
|
-
position = {
|
|
272
|
-
"name": imp.imported_name,
|
|
273
|
-
"start_line": imp.line_number,
|
|
274
|
-
"end_line": imp.line_number,
|
|
275
|
-
"start_column": 0,
|
|
276
|
-
"end_column": 0,
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if include_details:
|
|
280
|
-
position.update(
|
|
281
|
-
{
|
|
282
|
-
"statement": imp.import_statement,
|
|
283
|
-
"is_static": imp.is_static,
|
|
284
|
-
"is_wildcard": imp.is_wildcard,
|
|
285
|
-
}
|
|
286
|
-
)
|
|
287
|
-
|
|
288
|
-
positions.append(position)
|
|
289
|
-
|
|
290
|
-
elif element_type == "annotations":
|
|
291
|
-
for ann in analysis_result.annotations:
|
|
292
|
-
position = {
|
|
293
|
-
"name": ann.name,
|
|
294
|
-
"start_line": ann.line_number,
|
|
295
|
-
"end_line": ann.line_number,
|
|
296
|
-
"start_column": 0,
|
|
297
|
-
"end_column": 0,
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if include_details:
|
|
301
|
-
position.update(
|
|
302
|
-
{
|
|
303
|
-
"parameters": ann.parameters,
|
|
304
|
-
}
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
positions.append(position)
|
|
308
|
-
|
|
309
|
-
return positions
|
|
310
|
-
|
|
311
|
-
def _format_positions_as_text(
|
|
312
|
-
self, positions: Dict[str, List[Dict[str, Any]]], include_details: bool
|
|
313
|
-
) -> str:
|
|
314
|
-
"""
|
|
315
|
-
Format position data as human-readable text.
|
|
316
|
-
|
|
317
|
-
Args:
|
|
318
|
-
positions: Position data for all element types
|
|
319
|
-
include_details: Whether to include detailed information
|
|
320
|
-
|
|
321
|
-
Returns:
|
|
322
|
-
Formatted text representation
|
|
323
|
-
"""
|
|
324
|
-
lines = []
|
|
325
|
-
|
|
326
|
-
for element_type, elements in positions.items():
|
|
327
|
-
if not elements:
|
|
328
|
-
continue
|
|
329
|
-
|
|
330
|
-
lines.append(f"\n{element_type.upper()}:")
|
|
331
|
-
lines.append("=" * (len(element_type) + 1))
|
|
332
|
-
|
|
333
|
-
for element in elements:
|
|
334
|
-
name = element["name"]
|
|
335
|
-
start_line = element["start_line"]
|
|
336
|
-
end_line = element["end_line"]
|
|
337
|
-
|
|
338
|
-
if start_line == end_line:
|
|
339
|
-
location = f"line {start_line}"
|
|
340
|
-
else:
|
|
341
|
-
location = f"lines {start_line}-{end_line}"
|
|
342
|
-
|
|
343
|
-
lines.append(f" {name} ({location})")
|
|
344
|
-
|
|
345
|
-
if include_details:
|
|
346
|
-
for key, value in element.items():
|
|
347
|
-
if key not in [
|
|
348
|
-
"name",
|
|
349
|
-
"start_line",
|
|
350
|
-
"end_line",
|
|
351
|
-
"start_column",
|
|
352
|
-
"end_column",
|
|
353
|
-
]:
|
|
354
|
-
if value: # Only show non-empty values
|
|
355
|
-
lines.append(f" {key}: {value}")
|
|
356
|
-
|
|
357
|
-
return "\n".join(lines)
|
|
358
|
-
|
|
359
|
-
def validate_arguments(self, arguments: Dict[str, Any]) -> bool:
|
|
360
|
-
"""
|
|
361
|
-
Validate tool arguments against the schema.
|
|
362
|
-
|
|
363
|
-
Args:
|
|
364
|
-
arguments: Arguments to validate
|
|
365
|
-
|
|
366
|
-
Returns:
|
|
367
|
-
True if arguments are valid
|
|
368
|
-
|
|
369
|
-
Raises:
|
|
370
|
-
ValueError: If arguments are invalid
|
|
371
|
-
"""
|
|
372
|
-
schema = self.get_tool_schema()
|
|
373
|
-
required_fields = schema.get("required", [])
|
|
374
|
-
|
|
375
|
-
# Check required fields
|
|
376
|
-
for field in required_fields:
|
|
377
|
-
if field not in arguments:
|
|
378
|
-
raise ValueError(f"Required field '{field}' is missing")
|
|
379
|
-
|
|
380
|
-
# Validate file_path
|
|
381
|
-
if "file_path" in arguments:
|
|
382
|
-
file_path = arguments["file_path"]
|
|
383
|
-
if not isinstance(file_path, str):
|
|
384
|
-
raise ValueError("file_path must be a string")
|
|
385
|
-
if not file_path.strip():
|
|
386
|
-
raise ValueError("file_path cannot be empty")
|
|
387
|
-
|
|
388
|
-
# Validate element_types
|
|
389
|
-
if "element_types" in arguments:
|
|
390
|
-
element_types = arguments["element_types"]
|
|
391
|
-
if not isinstance(element_types, list):
|
|
392
|
-
raise ValueError("element_types must be a list")
|
|
393
|
-
if not element_types:
|
|
394
|
-
raise ValueError("element_types cannot be empty")
|
|
395
|
-
|
|
396
|
-
valid_types = ["classes", "methods", "fields", "imports", "annotations"]
|
|
397
|
-
for element_type in element_types:
|
|
398
|
-
if element_type not in valid_types:
|
|
399
|
-
raise ValueError(
|
|
400
|
-
f"Invalid element_type: {element_type}. Must be one of {valid_types}"
|
|
401
|
-
)
|
|
402
|
-
|
|
403
|
-
# Validate optional fields
|
|
404
|
-
if "language" in arguments:
|
|
405
|
-
language = arguments["language"]
|
|
406
|
-
if not isinstance(language, str):
|
|
407
|
-
raise ValueError("language must be a string")
|
|
408
|
-
|
|
409
|
-
if "include_details" in arguments:
|
|
410
|
-
include_details = arguments["include_details"]
|
|
411
|
-
if not isinstance(include_details, bool):
|
|
412
|
-
raise ValueError("include_details must be a boolean")
|
|
413
|
-
|
|
414
|
-
if "format" in arguments:
|
|
415
|
-
format_value = arguments["format"]
|
|
416
|
-
if not isinstance(format_value, str):
|
|
417
|
-
raise ValueError("format must be a string")
|
|
418
|
-
if format_value not in ["json", "text"]:
|
|
419
|
-
raise ValueError("format must be 'json' or 'text'")
|
|
420
|
-
|
|
421
|
-
return True
|
|
422
|
-
|
|
423
|
-
def get_tool_definition(self) -> Any:
|
|
424
|
-
"""
|
|
425
|
-
Get the MCP tool definition for get_code_positions.
|
|
426
|
-
|
|
427
|
-
Returns:
|
|
428
|
-
Tool definition object compatible with MCP server
|
|
429
|
-
"""
|
|
430
|
-
try:
|
|
431
|
-
from mcp.types import Tool
|
|
432
|
-
|
|
433
|
-
return Tool(
|
|
434
|
-
name="get_code_positions",
|
|
435
|
-
description="Get precise position information for code elements like classes, methods, fields, and imports",
|
|
436
|
-
inputSchema=self.get_tool_schema(),
|
|
437
|
-
)
|
|
438
|
-
except ImportError:
|
|
439
|
-
# Fallback for when MCP is not available
|
|
440
|
-
return {
|
|
441
|
-
"name": "get_code_positions",
|
|
442
|
-
"description": "Get precise position information for code elements like classes, methods, fields, and imports",
|
|
443
|
-
"inputSchema": self.get_tool_schema(),
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
# Tool instance for easy access
|
|
448
|
-
get_positions_tool = GetPositionsTool()
|
|
File without changes
|
{tree_sitter_analyzer-0.1.1.dist-info → tree_sitter_analyzer-0.1.2.dist-info}/entry_points.txt
RENAMED
|
File without changes
|