tree-sitter-analyzer 0.9.7__py3-none-any.whl → 0.9.8__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/mcp/tools/analyze_scale_tool.py +22 -10
- tree_sitter_analyzer/mcp/tools/query_tool.py +16 -8
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +4 -15
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +11 -24
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +32 -22
- tree_sitter_analyzer/mcp/utils/__init__.py +7 -0
- tree_sitter_analyzer/mcp/utils/path_resolver.py +194 -0
- {tree_sitter_analyzer-0.9.7.dist-info → tree_sitter_analyzer-0.9.8.dist-info}/METADATA +15 -10
- {tree_sitter_analyzer-0.9.7.dist-info → tree_sitter_analyzer-0.9.8.dist-info}/RECORD +11 -10
- {tree_sitter_analyzer-0.9.7.dist-info → tree_sitter_analyzer-0.9.8.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.9.7.dist-info → tree_sitter_analyzer-0.9.8.dist-info}/entry_points.txt +0 -0
|
@@ -15,6 +15,7 @@ from ...core.analysis_engine import AnalysisRequest, get_analysis_engine
|
|
|
15
15
|
from ...language_detector import detect_language_from_file
|
|
16
16
|
from ...security import SecurityValidator
|
|
17
17
|
from ...utils import setup_logger
|
|
18
|
+
from ..utils.path_resolver import PathResolver
|
|
18
19
|
|
|
19
20
|
# Set up logging
|
|
20
21
|
logger = setup_logger(__name__)
|
|
@@ -35,6 +36,7 @@ class AnalyzeScaleTool:
|
|
|
35
36
|
self.project_root = project_root
|
|
36
37
|
self.analysis_engine = get_analysis_engine(project_root)
|
|
37
38
|
self.security_validator = SecurityValidator(project_root)
|
|
39
|
+
self.path_resolver = PathResolver(project_root)
|
|
38
40
|
logger.info("AnalyzeScaleTool initialized with security validation")
|
|
39
41
|
|
|
40
42
|
def _calculate_file_metrics(self, file_path: str) -> dict[str, Any]:
|
|
@@ -354,11 +356,17 @@ class AnalyzeScaleTool:
|
|
|
354
356
|
include_details = arguments.get("include_details", False)
|
|
355
357
|
include_guidance = arguments.get("include_guidance", True)
|
|
356
358
|
|
|
357
|
-
#
|
|
358
|
-
|
|
359
|
+
# Resolve file path to absolute path
|
|
360
|
+
resolved_file_path = self.path_resolver.resolve(file_path)
|
|
361
|
+
logger.info(f"Analyzing file: {file_path} (resolved to: {resolved_file_path})")
|
|
362
|
+
|
|
363
|
+
# Security validation using resolved path
|
|
364
|
+
is_valid, error_msg = self.security_validator.validate_file_path(
|
|
365
|
+
resolved_file_path
|
|
366
|
+
)
|
|
359
367
|
if not is_valid:
|
|
360
368
|
logger.warning(
|
|
361
|
-
f"Security validation failed for file path: {
|
|
369
|
+
f"Security validation failed for file path: {resolved_file_path} - {error_msg}"
|
|
362
370
|
)
|
|
363
371
|
raise ValueError(f"Invalid file path: {error_msg}")
|
|
364
372
|
|
|
@@ -367,16 +375,20 @@ class AnalyzeScaleTool:
|
|
|
367
375
|
language = self.security_validator.sanitize_input(language, max_length=50)
|
|
368
376
|
|
|
369
377
|
# Validate file exists
|
|
370
|
-
if not Path(
|
|
378
|
+
if not Path(resolved_file_path).exists():
|
|
371
379
|
raise ValueError("Invalid file path: file does not exist")
|
|
372
380
|
|
|
373
381
|
# Detect language if not specified
|
|
374
382
|
if not language:
|
|
375
|
-
language = detect_language_from_file(
|
|
383
|
+
language = detect_language_from_file(resolved_file_path)
|
|
376
384
|
if language == "unknown":
|
|
377
|
-
raise ValueError(
|
|
385
|
+
raise ValueError(
|
|
386
|
+
f"Could not detect language for file: {resolved_file_path}"
|
|
387
|
+
)
|
|
378
388
|
|
|
379
|
-
logger.info(
|
|
389
|
+
logger.info(
|
|
390
|
+
f"Analyzing code scale for {resolved_file_path} (language: {language})"
|
|
391
|
+
)
|
|
380
392
|
|
|
381
393
|
try:
|
|
382
394
|
# Use performance monitoring with proper context manager
|
|
@@ -386,14 +398,14 @@ class AnalyzeScaleTool:
|
|
|
386
398
|
"analyze_code_scale_enhanced"
|
|
387
399
|
):
|
|
388
400
|
# Calculate basic file metrics
|
|
389
|
-
file_metrics = self._calculate_file_metrics(
|
|
401
|
+
file_metrics = self._calculate_file_metrics(resolved_file_path)
|
|
390
402
|
|
|
391
403
|
# Use appropriate analyzer based on language
|
|
392
404
|
if language == "java":
|
|
393
405
|
# Use AdvancedAnalyzer for comprehensive analysis
|
|
394
406
|
# Use unified analysis engine instead of deprecated advanced_analyzer
|
|
395
407
|
request = AnalysisRequest(
|
|
396
|
-
file_path=
|
|
408
|
+
file_path=resolved_file_path,
|
|
397
409
|
language=language,
|
|
398
410
|
include_complexity=True,
|
|
399
411
|
include_details=True,
|
|
@@ -408,7 +420,7 @@ class AnalyzeScaleTool:
|
|
|
408
420
|
else:
|
|
409
421
|
# Use universal analysis_engine for other languages
|
|
410
422
|
request = AnalysisRequest(
|
|
411
|
-
file_path=
|
|
423
|
+
file_path=resolved_file_path,
|
|
412
424
|
language=language,
|
|
413
425
|
include_details=include_details,
|
|
414
426
|
)
|
|
@@ -13,6 +13,7 @@ from ...core.query_service import QueryService
|
|
|
13
13
|
from ...language_detector import detect_language_from_file
|
|
14
14
|
from ...security import SecurityValidator
|
|
15
15
|
from ..utils.error_handler import handle_mcp_errors
|
|
16
|
+
from ..utils.path_resolver import PathResolver
|
|
16
17
|
|
|
17
18
|
logger = logging.getLogger(__name__)
|
|
18
19
|
|
|
@@ -25,6 +26,7 @@ class QueryTool:
|
|
|
25
26
|
self.project_root = project_root
|
|
26
27
|
self.query_service = QueryService(project_root)
|
|
27
28
|
self.security_validator = SecurityValidator(project_root)
|
|
29
|
+
self.path_resolver = PathResolver(project_root)
|
|
28
30
|
|
|
29
31
|
def get_tool_definition(self) -> dict[str, Any]:
|
|
30
32
|
"""
|
|
@@ -41,7 +43,7 @@ class QueryTool:
|
|
|
41
43
|
"properties": {
|
|
42
44
|
"file_path": {
|
|
43
45
|
"type": "string",
|
|
44
|
-
"description": "Path to the code file to query (relative to project root)",
|
|
46
|
+
"description": "Path to the code file to query (relative to project root or absolute path)",
|
|
45
47
|
},
|
|
46
48
|
"language": {
|
|
47
49
|
"type": "string",
|
|
@@ -90,12 +92,18 @@ class QueryTool:
|
|
|
90
92
|
if not file_path:
|
|
91
93
|
raise ValueError("file_path is required")
|
|
92
94
|
|
|
93
|
-
#
|
|
94
|
-
|
|
95
|
+
# Resolve file path to absolute path
|
|
96
|
+
resolved_file_path = self.path_resolver.resolve(file_path)
|
|
97
|
+
logger.info(f"Querying file: {file_path} (resolved to: {resolved_file_path})")
|
|
98
|
+
|
|
99
|
+
# Security validation using resolved path
|
|
100
|
+
is_valid, error_msg = self.security_validator.validate_file_path(
|
|
101
|
+
resolved_file_path
|
|
102
|
+
)
|
|
95
103
|
if not is_valid:
|
|
96
|
-
raise ValueError(
|
|
97
|
-
|
|
98
|
-
|
|
104
|
+
raise ValueError(
|
|
105
|
+
f"Invalid or unsafe file path: {error_msg or resolved_file_path}"
|
|
106
|
+
)
|
|
99
107
|
|
|
100
108
|
# Get query parameters
|
|
101
109
|
query_key = arguments.get("query_key")
|
|
@@ -112,14 +120,14 @@ class QueryTool:
|
|
|
112
120
|
# Detect language
|
|
113
121
|
language = arguments.get("language")
|
|
114
122
|
if not language:
|
|
115
|
-
language = detect_language_from_file(
|
|
123
|
+
language = detect_language_from_file(resolved_file_path)
|
|
116
124
|
if not language:
|
|
117
125
|
raise ValueError(f"Could not detect language for file: {file_path}")
|
|
118
126
|
|
|
119
127
|
try:
|
|
120
128
|
# Execute query
|
|
121
129
|
results = await self.query_service.execute_query(
|
|
122
|
-
|
|
130
|
+
resolved_file_path, language, query_key, query_string, filter_expression
|
|
123
131
|
)
|
|
124
132
|
|
|
125
133
|
if not results:
|
|
@@ -7,13 +7,13 @@ allowing selective content extraction with line and column range support.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import json
|
|
10
|
-
import os
|
|
11
10
|
from pathlib import Path
|
|
12
11
|
from typing import Any
|
|
13
12
|
|
|
14
13
|
from ...file_handler import read_file_partial
|
|
15
14
|
from ...security import SecurityValidator
|
|
16
15
|
from ...utils import setup_logger
|
|
16
|
+
from ..utils.path_resolver import PathResolver
|
|
17
17
|
|
|
18
18
|
# Set up logging
|
|
19
19
|
logger = setup_logger(__name__)
|
|
@@ -31,6 +31,7 @@ class ReadPartialTool:
|
|
|
31
31
|
"""Initialize the read partial tool."""
|
|
32
32
|
self.security_validator = SecurityValidator(project_root)
|
|
33
33
|
self.project_root = project_root
|
|
34
|
+
self.path_resolver = PathResolver(project_root)
|
|
34
35
|
logger.info("ReadPartialTool initialized with security validation")
|
|
35
36
|
|
|
36
37
|
def get_tool_schema(self) -> dict[str, Any]:
|
|
@@ -106,20 +107,8 @@ class ReadPartialTool:
|
|
|
106
107
|
end_column = arguments.get("end_column")
|
|
107
108
|
# output_format = arguments.get("format", "text") # Not used currently
|
|
108
109
|
|
|
109
|
-
# Resolve
|
|
110
|
-
|
|
111
|
-
getattr(
|
|
112
|
-
getattr(self.security_validator, "boundary_manager", None),
|
|
113
|
-
"project_root",
|
|
114
|
-
None,
|
|
115
|
-
)
|
|
116
|
-
or self.project_root
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
if not os.path.isabs(file_path) and base_root:
|
|
120
|
-
resolved_path = os.path.realpath(os.path.join(base_root, file_path))
|
|
121
|
-
else:
|
|
122
|
-
resolved_path = file_path
|
|
110
|
+
# Resolve file path using common path resolver
|
|
111
|
+
resolved_path = self.path_resolver.resolve(file_path)
|
|
123
112
|
|
|
124
113
|
# Security validation (validate resolved absolute path when possible)
|
|
125
114
|
is_valid, error_msg = self.security_validator.validate_file_path(resolved_path)
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Table Format MCP
|
|
3
|
+
Table Format Tool for MCP
|
|
4
4
|
|
|
5
|
-
This tool provides
|
|
6
|
-
|
|
5
|
+
This tool provides code structure analysis and table formatting through the MCP protocol,
|
|
6
|
+
converting analysis results into structured table formats for better readability.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
import os
|
|
10
9
|
from pathlib import Path
|
|
11
10
|
from typing import Any
|
|
12
11
|
|
|
@@ -16,6 +15,7 @@ from ...security import SecurityValidator
|
|
|
16
15
|
from ...table_formatter import TableFormatter
|
|
17
16
|
from ...utils import setup_logger
|
|
18
17
|
from ..utils import get_performance_monitor
|
|
18
|
+
from ..utils.path_resolver import PathResolver
|
|
19
19
|
|
|
20
20
|
# Set up logging
|
|
21
21
|
logger = setup_logger(__name__)
|
|
@@ -23,20 +23,19 @@ logger = setup_logger(__name__)
|
|
|
23
23
|
|
|
24
24
|
class TableFormatTool:
|
|
25
25
|
"""
|
|
26
|
-
MCP Tool for
|
|
26
|
+
MCP Tool for code structure analysis and table formatting.
|
|
27
27
|
|
|
28
|
-
This tool integrates with existing
|
|
29
|
-
|
|
30
|
-
the CLI --table=full option.
|
|
28
|
+
This tool integrates with existing analyzer components to provide
|
|
29
|
+
structured table output through the MCP protocol.
|
|
31
30
|
"""
|
|
32
31
|
|
|
33
32
|
def __init__(self, project_root: str = None) -> None:
|
|
34
33
|
"""Initialize the table format tool."""
|
|
35
|
-
self.logger = logger
|
|
36
34
|
self.project_root = project_root
|
|
37
35
|
self.analysis_engine = get_analysis_engine(project_root)
|
|
38
36
|
self.security_validator = SecurityValidator(project_root)
|
|
39
|
-
|
|
37
|
+
self.path_resolver = PathResolver(project_root)
|
|
38
|
+
self.logger = logger
|
|
40
39
|
|
|
41
40
|
def get_tool_schema(self) -> dict[str, Any]:
|
|
42
41
|
"""
|
|
@@ -272,20 +271,8 @@ class TableFormatTool:
|
|
|
272
271
|
format_type = args.get("format_type", "full")
|
|
273
272
|
language = args.get("language")
|
|
274
273
|
|
|
275
|
-
# Resolve
|
|
276
|
-
|
|
277
|
-
getattr(
|
|
278
|
-
getattr(self.security_validator, "boundary_manager", None),
|
|
279
|
-
"project_root",
|
|
280
|
-
None,
|
|
281
|
-
)
|
|
282
|
-
or self.project_root
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
if not os.path.isabs(file_path) and base_root:
|
|
286
|
-
resolved_path = os.path.realpath(os.path.join(base_root, file_path))
|
|
287
|
-
else:
|
|
288
|
-
resolved_path = file_path
|
|
274
|
+
# Resolve file path using common path resolver
|
|
275
|
+
resolved_path = self.path_resolver.resolve(file_path)
|
|
289
276
|
|
|
290
277
|
# Security validation
|
|
291
278
|
is_valid, error_msg = self.security_validator.validate_file_path(
|
|
@@ -1,38 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Universal
|
|
3
|
+
Universal Analyze Tool for MCP
|
|
4
4
|
|
|
5
|
-
This tool provides universal code analysis capabilities
|
|
6
|
-
|
|
5
|
+
This tool provides universal code analysis capabilities through the MCP protocol,
|
|
6
|
+
supporting multiple languages with both basic and detailed analysis options.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
import logging
|
|
10
9
|
from pathlib import Path
|
|
11
10
|
from typing import Any
|
|
12
11
|
|
|
13
12
|
from ...core.analysis_engine import AnalysisRequest, get_analysis_engine
|
|
14
13
|
from ...language_detector import detect_language_from_file, is_language_supported
|
|
15
14
|
from ...security import SecurityValidator
|
|
15
|
+
from ...utils import setup_logger
|
|
16
16
|
from ..utils import get_performance_monitor
|
|
17
17
|
from ..utils.error_handler import handle_mcp_errors
|
|
18
|
+
from ..utils.path_resolver import PathResolver
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
# Set up logging
|
|
21
|
+
logger = setup_logger(__name__)
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
class UniversalAnalyzeTool:
|
|
23
25
|
"""
|
|
24
|
-
Universal code analysis
|
|
26
|
+
Universal MCP Tool for code analysis across multiple languages.
|
|
25
27
|
|
|
26
|
-
This tool
|
|
27
|
-
|
|
28
|
+
This tool provides comprehensive code analysis capabilities through the MCP protocol,
|
|
29
|
+
supporting both basic and detailed analysis with language-specific optimizations.
|
|
28
30
|
"""
|
|
29
31
|
|
|
30
|
-
def __init__(self, project_root: str = None) -> None:
|
|
31
|
-
"""Initialize the universal
|
|
32
|
-
# Use unified analysis engine instead of deprecated AdvancedAnalyzer
|
|
32
|
+
def __init__(self, project_root: str | None = None) -> None:
|
|
33
|
+
"""Initialize the universal analyze tool."""
|
|
33
34
|
self.project_root = project_root
|
|
34
35
|
self.analysis_engine = get_analysis_engine(project_root)
|
|
35
36
|
self.security_validator = SecurityValidator(project_root)
|
|
37
|
+
self.path_resolver = PathResolver(project_root)
|
|
36
38
|
logger.info("UniversalAnalyzeTool initialized with security validation")
|
|
37
39
|
|
|
38
40
|
def get_tool_definition(self) -> dict[str, Any]:
|
|
@@ -101,11 +103,17 @@ class UniversalAnalyzeTool:
|
|
|
101
103
|
language = arguments.get("language")
|
|
102
104
|
analysis_type = arguments.get("analysis_type", "basic")
|
|
103
105
|
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
+
# Resolve file path to absolute path
|
|
107
|
+
resolved_file_path = self.path_resolver.resolve(file_path)
|
|
108
|
+
logger.info(f"Analyzing file: {file_path} (resolved to: {resolved_file_path})")
|
|
109
|
+
|
|
110
|
+
# Security validation using resolved path
|
|
111
|
+
is_valid, error_msg = self.security_validator.validate_file_path(
|
|
112
|
+
resolved_file_path
|
|
113
|
+
)
|
|
106
114
|
if not is_valid:
|
|
107
115
|
logger.warning(
|
|
108
|
-
f"Security validation failed for file path: {
|
|
116
|
+
f"Security validation failed for file path: {resolved_file_path} - {error_msg}"
|
|
109
117
|
)
|
|
110
118
|
raise ValueError(f"Invalid file path: {error_msg}")
|
|
111
119
|
|
|
@@ -120,14 +128,16 @@ class UniversalAnalyzeTool:
|
|
|
120
128
|
include_queries = arguments.get("include_queries", False)
|
|
121
129
|
|
|
122
130
|
# Validate file exists
|
|
123
|
-
if not Path(
|
|
131
|
+
if not Path(resolved_file_path).exists():
|
|
124
132
|
raise ValueError("Invalid file path: file does not exist")
|
|
125
133
|
|
|
126
134
|
# Detect language if not specified
|
|
127
135
|
if not language:
|
|
128
|
-
language = detect_language_from_file(
|
|
136
|
+
language = detect_language_from_file(resolved_file_path)
|
|
129
137
|
if language == "unknown":
|
|
130
|
-
raise ValueError(
|
|
138
|
+
raise ValueError(
|
|
139
|
+
f"Could not detect language for file: {resolved_file_path}"
|
|
140
|
+
)
|
|
131
141
|
|
|
132
142
|
# Check if language is supported
|
|
133
143
|
if not is_language_supported(language):
|
|
@@ -141,7 +151,7 @@ class UniversalAnalyzeTool:
|
|
|
141
151
|
)
|
|
142
152
|
|
|
143
153
|
logger.info(
|
|
144
|
-
f"Analyzing {
|
|
154
|
+
f"Analyzing {resolved_file_path} (language: {language}, type: {analysis_type})"
|
|
145
155
|
)
|
|
146
156
|
|
|
147
157
|
try:
|
|
@@ -151,12 +161,12 @@ class UniversalAnalyzeTool:
|
|
|
151
161
|
if language == "java":
|
|
152
162
|
# Use advanced analyzer for Java
|
|
153
163
|
result = await self._analyze_with_advanced_analyzer(
|
|
154
|
-
|
|
164
|
+
resolved_file_path, language, analysis_type, include_ast
|
|
155
165
|
)
|
|
156
166
|
else:
|
|
157
167
|
# Use universal analyzer for other languages
|
|
158
168
|
result = await self._analyze_with_universal_analyzer(
|
|
159
|
-
|
|
169
|
+
resolved_file_path, language, analysis_type, include_ast
|
|
160
170
|
)
|
|
161
171
|
|
|
162
172
|
# Add query information if requested
|
|
@@ -165,11 +175,11 @@ class UniversalAnalyzeTool:
|
|
|
165
175
|
language
|
|
166
176
|
)
|
|
167
177
|
|
|
168
|
-
logger.info(f"Successfully analyzed {
|
|
178
|
+
logger.info(f"Successfully analyzed {resolved_file_path}")
|
|
169
179
|
return result
|
|
170
180
|
|
|
171
181
|
except Exception as e:
|
|
172
|
-
logger.error(f"Error analyzing {
|
|
182
|
+
logger.error(f"Error analyzing {resolved_file_path}: {e}")
|
|
173
183
|
raise
|
|
174
184
|
|
|
175
185
|
async def _analyze_with_advanced_analyzer(
|
|
@@ -26,6 +26,9 @@ from .error_handler import (
|
|
|
26
26
|
handle_mcp_errors,
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
+
# Export path resolver utilities
|
|
30
|
+
from .path_resolver import PathResolver, resolve_path
|
|
31
|
+
|
|
29
32
|
# Module metadata
|
|
30
33
|
__version__ = "2.0.0"
|
|
31
34
|
__author__ = "Tree-Sitter Analyzer Team"
|
|
@@ -36,6 +39,7 @@ MCP_UTILS_CAPABILITIES = {
|
|
|
36
39
|
"features": [
|
|
37
40
|
"Comprehensive Error Handling",
|
|
38
41
|
"Unified Core Services Integration",
|
|
42
|
+
"Cross-Platform Path Resolution",
|
|
39
43
|
],
|
|
40
44
|
"deprecated_features": [
|
|
41
45
|
"LRU Cache with TTL (moved to core.cache_service)",
|
|
@@ -99,6 +103,9 @@ __all__ = [
|
|
|
99
103
|
"ErrorCategory",
|
|
100
104
|
"handle_mcp_errors",
|
|
101
105
|
"get_error_handler",
|
|
106
|
+
# Path resolution
|
|
107
|
+
"PathResolver",
|
|
108
|
+
"resolve_path",
|
|
102
109
|
# Backward compatibility
|
|
103
110
|
"get_cache_manager",
|
|
104
111
|
"get_performance_monitor",
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Path Resolver Utility for MCP Tools
|
|
4
|
+
|
|
5
|
+
This module provides unified path resolution functionality for all MCP tools,
|
|
6
|
+
ensuring consistent handling of relative and absolute paths across different
|
|
7
|
+
operating systems.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PathResolver:
|
|
17
|
+
"""
|
|
18
|
+
Utility class for resolving file paths in MCP tools.
|
|
19
|
+
|
|
20
|
+
Handles relative path resolution against project root and provides
|
|
21
|
+
cross-platform compatibility for Windows, macOS, and Linux.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, project_root: str | None = None):
|
|
25
|
+
"""
|
|
26
|
+
Initialize the path resolver.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
project_root: Optional project root directory for resolving relative paths
|
|
30
|
+
"""
|
|
31
|
+
self.project_root = project_root
|
|
32
|
+
if project_root:
|
|
33
|
+
# Normalize project root path
|
|
34
|
+
self.project_root = os.path.normpath(project_root)
|
|
35
|
+
logger.debug(
|
|
36
|
+
f"PathResolver initialized with project root: {self.project_root}"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def resolve(self, file_path: str) -> str:
|
|
40
|
+
"""
|
|
41
|
+
Resolve a file path to an absolute path.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
file_path: Input file path (can be relative or absolute)
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Resolved absolute file path
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
ValueError: If file_path is empty or None
|
|
51
|
+
"""
|
|
52
|
+
if not file_path:
|
|
53
|
+
raise ValueError("file_path cannot be empty or None")
|
|
54
|
+
|
|
55
|
+
# If already absolute, return as is
|
|
56
|
+
if os.path.isabs(file_path):
|
|
57
|
+
resolved_path = os.path.normpath(file_path)
|
|
58
|
+
logger.debug(f"Path already absolute: {file_path} -> {resolved_path}")
|
|
59
|
+
return resolved_path
|
|
60
|
+
|
|
61
|
+
# If we have a project root, resolve relative to it
|
|
62
|
+
if self.project_root:
|
|
63
|
+
resolved_path = os.path.join(self.project_root, file_path)
|
|
64
|
+
# Normalize path separators for cross-platform compatibility
|
|
65
|
+
resolved_path = os.path.normpath(resolved_path)
|
|
66
|
+
logger.debug(
|
|
67
|
+
f"Resolved relative path '{file_path}' to '{resolved_path}' using project root"
|
|
68
|
+
)
|
|
69
|
+
return resolved_path
|
|
70
|
+
|
|
71
|
+
# Fallback: try to resolve relative to current working directory
|
|
72
|
+
resolved_path = os.path.abspath(file_path)
|
|
73
|
+
resolved_path = os.path.normpath(resolved_path)
|
|
74
|
+
logger.debug(
|
|
75
|
+
f"Resolved relative path '{file_path}' to '{resolved_path}' using current working directory"
|
|
76
|
+
)
|
|
77
|
+
return resolved_path
|
|
78
|
+
|
|
79
|
+
def is_relative(self, file_path: str) -> bool:
|
|
80
|
+
"""
|
|
81
|
+
Check if a file path is relative.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
file_path: File path to check
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
True if the path is relative, False if absolute
|
|
88
|
+
"""
|
|
89
|
+
return not os.path.isabs(file_path)
|
|
90
|
+
|
|
91
|
+
def get_relative_path(self, absolute_path: str) -> str:
|
|
92
|
+
"""
|
|
93
|
+
Get the relative path from project root to the given absolute path.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
absolute_path: Absolute file path
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Relative path from project root, or the original path if no project root
|
|
100
|
+
|
|
101
|
+
Raises:
|
|
102
|
+
ValueError: If absolute_path is not actually absolute
|
|
103
|
+
"""
|
|
104
|
+
if not os.path.isabs(absolute_path):
|
|
105
|
+
raise ValueError(f"Path is not absolute: {absolute_path}")
|
|
106
|
+
|
|
107
|
+
if not self.project_root:
|
|
108
|
+
return absolute_path
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
# Get relative path from project root
|
|
112
|
+
relative_path = os.path.relpath(absolute_path, self.project_root)
|
|
113
|
+
logger.debug(
|
|
114
|
+
f"Converted absolute path '{absolute_path}' to relative path '{relative_path}'"
|
|
115
|
+
)
|
|
116
|
+
return relative_path
|
|
117
|
+
except ValueError:
|
|
118
|
+
# Paths are on different drives (Windows) or other error
|
|
119
|
+
logger.warning(
|
|
120
|
+
f"Could not convert absolute path '{absolute_path}' to relative path"
|
|
121
|
+
)
|
|
122
|
+
return absolute_path
|
|
123
|
+
|
|
124
|
+
def validate_path(self, file_path: str) -> tuple[bool, str | None]:
|
|
125
|
+
"""
|
|
126
|
+
Validate if a file path is valid and safe.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
file_path: File path to validate
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Tuple of (is_valid, error_message)
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
resolved_path = self.resolve(file_path)
|
|
136
|
+
|
|
137
|
+
# Check if file exists
|
|
138
|
+
if not os.path.exists(resolved_path):
|
|
139
|
+
return False, f"File does not exist: {resolved_path}"
|
|
140
|
+
|
|
141
|
+
# Check if it's a file (not directory)
|
|
142
|
+
if not os.path.isfile(resolved_path):
|
|
143
|
+
return False, f"Path is not a file: {resolved_path}"
|
|
144
|
+
|
|
145
|
+
# Check if it's within project root (if we have one)
|
|
146
|
+
if self.project_root:
|
|
147
|
+
try:
|
|
148
|
+
os.path.commonpath([resolved_path, self.project_root])
|
|
149
|
+
except ValueError:
|
|
150
|
+
return False, f"File path is outside project root: {resolved_path}"
|
|
151
|
+
|
|
152
|
+
return True, None
|
|
153
|
+
|
|
154
|
+
except Exception as e:
|
|
155
|
+
return False, f"Path validation error: {str(e)}"
|
|
156
|
+
|
|
157
|
+
def get_project_root(self) -> str | None:
|
|
158
|
+
"""
|
|
159
|
+
Get the current project root.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Project root path or None if not set
|
|
163
|
+
"""
|
|
164
|
+
return self.project_root
|
|
165
|
+
|
|
166
|
+
def set_project_root(self, project_root: str) -> None:
|
|
167
|
+
"""
|
|
168
|
+
Set or update the project root.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
project_root: New project root directory
|
|
172
|
+
"""
|
|
173
|
+
if project_root:
|
|
174
|
+
self.project_root = os.path.normpath(project_root)
|
|
175
|
+
logger.info(f"Project root updated to: {self.project_root}")
|
|
176
|
+
else:
|
|
177
|
+
self.project_root = None
|
|
178
|
+
logger.info("Project root cleared")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# Convenience function for backward compatibility
|
|
182
|
+
def resolve_path(file_path: str, project_root: str | None = None) -> str:
|
|
183
|
+
"""
|
|
184
|
+
Convenience function to resolve a file path.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
file_path: File path to resolve
|
|
188
|
+
project_root: Optional project root directory
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Resolved absolute file path
|
|
192
|
+
"""
|
|
193
|
+
resolver = PathResolver(project_root)
|
|
194
|
+
return resolver.resolve(file_path)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tree-sitter-analyzer
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.8
|
|
4
4
|
Summary: Extensible multi-language code analyzer framework using Tree-sitter with dynamic plugin architecture
|
|
5
5
|
Project-URL: Homepage, https://github.com/aimasteracc/tree-sitter-analyzer
|
|
6
6
|
Project-URL: Documentation, https://github.com/aimasteracc/tree-sitter-analyzer#readme
|
|
@@ -137,11 +137,11 @@ Description-Content-Type: text/markdown
|
|
|
137
137
|
|
|
138
138
|
[](https://python.org)
|
|
139
139
|
[](LICENSE)
|
|
140
|
-
[](#quality-assurance)
|
|
141
|
+
[](#quality-assurance)
|
|
142
142
|
[](#quality-assurance)
|
|
143
143
|
[](https://pypi.org/project/tree-sitter-analyzer/)
|
|
144
|
-
[](https://github.com/aimasteracc/tree-sitter-analyzer/releases)
|
|
145
145
|
[](https://github.com/aimasteracc/tree-sitter-analyzer)
|
|
146
146
|
|
|
147
147
|
## 🚀 Break Through LLM Token Limits, Let AI Understand Code Files of Any Size
|
|
@@ -347,16 +347,21 @@ Parameters: {"file_path": "examples/BigService.java"}
|
|
|
347
347
|
**Prompt:**
|
|
348
348
|
```
|
|
349
349
|
Use MCP tool extract_code_section to extract specified code section
|
|
350
|
-
Parameters: {"file_path": "examples/BigService.java", "start_line":
|
|
350
|
+
Parameters: {"file_path": "examples/BigService.java", "start_line": 93, "end_line": 105}
|
|
351
351
|
```
|
|
352
352
|
|
|
353
353
|
**Return Format:**
|
|
354
354
|
```json
|
|
355
355
|
{
|
|
356
356
|
"file_path": "examples/BigService.java",
|
|
357
|
-
"range": {
|
|
358
|
-
|
|
359
|
-
|
|
357
|
+
"range": {
|
|
358
|
+
"start_line": 93,
|
|
359
|
+
"end_line": 105,
|
|
360
|
+
"start_column": null,
|
|
361
|
+
"end_column": null
|
|
362
|
+
},
|
|
363
|
+
"content": " private void checkMemoryUsage() {\n Runtime runtime = Runtime.getRuntime();\n long totalMemory = runtime.totalMemory();\n long freeMemory = runtime.freeMemory();\n long usedMemory = totalMemory - freeMemory;\n\n System.out.println(\"Total Memory: \" + totalMemory);\n System.out.println(\"Free Memory: \" + freeMemory);\n System.out.println(\"Used Memory: \" + usedMemory);\n\n if (usedMemory > totalMemory * 0.8) {\n System.out.println(\"WARNING: High memory usage detected!\");\n }\n",
|
|
364
|
+
"content_length": 542
|
|
360
365
|
}
|
|
361
366
|
```
|
|
362
367
|
|
|
@@ -551,8 +556,8 @@ Tree-sitter Analyzer automatically detects and protects project boundaries:
|
|
|
551
556
|
## 🏆 Quality Assurance
|
|
552
557
|
|
|
553
558
|
### 📊 **Quality Metrics**
|
|
554
|
-
- **1,
|
|
555
|
-
- **74.
|
|
559
|
+
- **1,358 Tests** - 100% pass rate ✅
|
|
560
|
+
- **74.54% Code Coverage** - Industry-leading level
|
|
556
561
|
- **Zero Test Failures** - Complete CI/CD ready
|
|
557
562
|
- **Cross-platform Compatible** - Windows, macOS, Linux
|
|
558
563
|
|
|
@@ -53,15 +53,16 @@ tree_sitter_analyzer/mcp/resources/__init__.py,sha256=SWnK8liTQkuQXlVgyRP9mf5Hzp
|
|
|
53
53
|
tree_sitter_analyzer/mcp/resources/code_file_resource.py,sha256=ZX5ZYSJfylBedpL80kTDlco2YZqgRMb5f3OW0VvOVRM,6166
|
|
54
54
|
tree_sitter_analyzer/mcp/resources/project_stats_resource.py,sha256=V5-daZ99SU4rOt7qLk9GmhkzdXJpEINBobNlT14ojYY,19441
|
|
55
55
|
tree_sitter_analyzer/mcp/tools/__init__.py,sha256=u7JrSLwE95y50mfjSus6HRdtdhkNbVrW8jP4AooawEU,762
|
|
56
|
-
tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py,sha256=
|
|
56
|
+
tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py,sha256=ZiwIzVbzSMoUXpuC94-N9_xLrlSCWEFlHv2nB_ibOgU,27856
|
|
57
57
|
tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py,sha256=mssed7bEfGeGxW4mOf7dg8BDS1oqHLolIBNX9DaZ3DM,8997
|
|
58
58
|
tree_sitter_analyzer/mcp/tools/base_tool.py,sha256=FVSMgKIliQ5EBVQEfjYwWeqzWt9OqOFDr3dyACIDxig,1210
|
|
59
|
-
tree_sitter_analyzer/mcp/tools/query_tool.py,sha256=
|
|
60
|
-
tree_sitter_analyzer/mcp/tools/read_partial_tool.py,sha256=
|
|
61
|
-
tree_sitter_analyzer/mcp/tools/table_format_tool.py,sha256=
|
|
62
|
-
tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py,sha256=
|
|
63
|
-
tree_sitter_analyzer/mcp/utils/__init__.py,sha256=
|
|
59
|
+
tree_sitter_analyzer/mcp/tools/query_tool.py,sha256=UzJSfVXr6DsF1E8EdysMHxlYOREZOKrym1Zv9OtK_8Y,8545
|
|
60
|
+
tree_sitter_analyzer/mcp/tools/read_partial_tool.py,sha256=Hc4PnF7wBBRhxVlzuqaz1rBA5rWSNvC6qEiWSLqdogI,11504
|
|
61
|
+
tree_sitter_analyzer/mcp/tools/table_format_tool.py,sha256=YeZs5tL8hjWOCR-v6VekNDMyCQOcNI06wHGbGdnacIQ,15451
|
|
62
|
+
tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py,sha256=cdc1K-NZomQK7PD1viVR1kBaNWwDeo1FrU0nn5sb1YA,22141
|
|
63
|
+
tree_sitter_analyzer/mcp/utils/__init__.py,sha256=6f4xTAfA7kCdk3e8r_ee-j7h84OLBEFSNvA3o-TOMqM,3148
|
|
64
64
|
tree_sitter_analyzer/mcp/utils/error_handler.py,sha256=_XeGjfF331EEeHUnJ5cGvJB7WP0lHkD8RXtBayQElr4,18973
|
|
65
|
+
tree_sitter_analyzer/mcp/utils/path_resolver.py,sha256=L9qTXEDwJw3I9nFIQCOdaec0Nuq-6ZvM_UXaOBV-w-c,6196
|
|
65
66
|
tree_sitter_analyzer/plugins/__init__.py,sha256=ITE9bTz7NO4axnn8g5Z-1_ydhSLT0RnY6Y1J9OhUP3E,10326
|
|
66
67
|
tree_sitter_analyzer/plugins/base.py,sha256=FMRAOtjtDutNV8RnB6cmFgdvcjxKRAbrrzqldBBT1yk,17167
|
|
67
68
|
tree_sitter_analyzer/plugins/manager.py,sha256=PyEY3jeuCBpDVqguWhaAu7nzUZM17_pI6wml2e0Hamo,12535
|
|
@@ -74,7 +75,7 @@ tree_sitter_analyzer/security/__init__.py,sha256=ZTqTt24hsljCpTXAZpJC57L7MU5lJLT
|
|
|
74
75
|
tree_sitter_analyzer/security/boundary_manager.py,sha256=CUQWU5j1zdjEbN9UmArcYkq9HbemhttQzk0pVk-vxZs,8153
|
|
75
76
|
tree_sitter_analyzer/security/regex_checker.py,sha256=jWK6H8PTPgzbwRPfK_RZ8bBTS6rtEbgjY5vr3YWjQ_U,9616
|
|
76
77
|
tree_sitter_analyzer/security/validator.py,sha256=UPAPcrnmI2mNzbYOm0MabnJMGllK6HlOQ9KX-2bRfgU,8986
|
|
77
|
-
tree_sitter_analyzer-0.9.
|
|
78
|
-
tree_sitter_analyzer-0.9.
|
|
79
|
-
tree_sitter_analyzer-0.9.
|
|
80
|
-
tree_sitter_analyzer-0.9.
|
|
78
|
+
tree_sitter_analyzer-0.9.8.dist-info/METADATA,sha256=zFg_OCSiYitBph2K6aHC7HRooxgkDN1kbj9WF76FrEU,23557
|
|
79
|
+
tree_sitter_analyzer-0.9.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
80
|
+
tree_sitter_analyzer-0.9.8.dist-info/entry_points.txt,sha256=U4tfLGXgCWubKm2PyEb3zxhQ2pm7zVotMyfyS0CodD8,486
|
|
81
|
+
tree_sitter_analyzer-0.9.8.dist-info/RECORD,,
|
|
File without changes
|
{tree_sitter_analyzer-0.9.7.dist-info → tree_sitter_analyzer-0.9.8.dist-info}/entry_points.txt
RENAMED
|
File without changes
|