tree-sitter-analyzer 1.9.17.1__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.
- tree_sitter_analyzer/__init__.py +132 -0
- tree_sitter_analyzer/__main__.py +11 -0
- tree_sitter_analyzer/api.py +853 -0
- tree_sitter_analyzer/cli/__init__.py +39 -0
- tree_sitter_analyzer/cli/__main__.py +12 -0
- tree_sitter_analyzer/cli/argument_validator.py +89 -0
- tree_sitter_analyzer/cli/commands/__init__.py +26 -0
- tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
- tree_sitter_analyzer/cli/commands/base_command.py +181 -0
- tree_sitter_analyzer/cli/commands/default_command.py +18 -0
- tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
- tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
- tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
- tree_sitter_analyzer/cli/commands/query_command.py +109 -0
- tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
- tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
- tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
- tree_sitter_analyzer/cli/commands/table_command.py +414 -0
- tree_sitter_analyzer/cli/info_commands.py +124 -0
- tree_sitter_analyzer/cli_main.py +472 -0
- tree_sitter_analyzer/constants.py +85 -0
- tree_sitter_analyzer/core/__init__.py +15 -0
- tree_sitter_analyzer/core/analysis_engine.py +580 -0
- tree_sitter_analyzer/core/cache_service.py +333 -0
- tree_sitter_analyzer/core/engine.py +585 -0
- tree_sitter_analyzer/core/parser.py +293 -0
- tree_sitter_analyzer/core/query.py +605 -0
- tree_sitter_analyzer/core/query_filter.py +200 -0
- tree_sitter_analyzer/core/query_service.py +340 -0
- tree_sitter_analyzer/encoding_utils.py +530 -0
- tree_sitter_analyzer/exceptions.py +747 -0
- tree_sitter_analyzer/file_handler.py +246 -0
- tree_sitter_analyzer/formatters/__init__.py +1 -0
- tree_sitter_analyzer/formatters/base_formatter.py +201 -0
- tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
- tree_sitter_analyzer/formatters/formatter_config.py +197 -0
- tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
- tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
- tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
- tree_sitter_analyzer/formatters/go_formatter.py +368 -0
- tree_sitter_analyzer/formatters/html_formatter.py +498 -0
- tree_sitter_analyzer/formatters/java_formatter.py +423 -0
- tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
- tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
- tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
- tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
- tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
- tree_sitter_analyzer/formatters/php_formatter.py +301 -0
- tree_sitter_analyzer/formatters/python_formatter.py +830 -0
- tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
- tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
- tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
- tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
- tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
- tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
- tree_sitter_analyzer/interfaces/__init__.py +9 -0
- tree_sitter_analyzer/interfaces/cli.py +535 -0
- tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
- tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
- tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
- tree_sitter_analyzer/language_detector.py +553 -0
- tree_sitter_analyzer/language_loader.py +271 -0
- tree_sitter_analyzer/languages/__init__.py +10 -0
- tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
- tree_sitter_analyzer/languages/css_plugin.py +449 -0
- tree_sitter_analyzer/languages/go_plugin.py +836 -0
- tree_sitter_analyzer/languages/html_plugin.py +496 -0
- tree_sitter_analyzer/languages/java_plugin.py +1299 -0
- tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
- tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
- tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
- tree_sitter_analyzer/languages/php_plugin.py +862 -0
- tree_sitter_analyzer/languages/python_plugin.py +1636 -0
- tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
- tree_sitter_analyzer/languages/rust_plugin.py +673 -0
- tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
- tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
- tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
- tree_sitter_analyzer/legacy_table_formatter.py +860 -0
- tree_sitter_analyzer/mcp/__init__.py +34 -0
- tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
- tree_sitter_analyzer/mcp/server.py +869 -0
- tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
- tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
- tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
- tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
- tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
- tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
- tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
- tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
- tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
- tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
- tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
- tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
- tree_sitter_analyzer/models.py +840 -0
- tree_sitter_analyzer/mypy_current_errors.txt +2 -0
- tree_sitter_analyzer/output_manager.py +255 -0
- tree_sitter_analyzer/platform_compat/__init__.py +3 -0
- tree_sitter_analyzer/platform_compat/adapter.py +324 -0
- tree_sitter_analyzer/platform_compat/compare.py +224 -0
- tree_sitter_analyzer/platform_compat/detector.py +67 -0
- tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
- tree_sitter_analyzer/platform_compat/profiles.py +217 -0
- tree_sitter_analyzer/platform_compat/record.py +55 -0
- tree_sitter_analyzer/platform_compat/recorder.py +155 -0
- tree_sitter_analyzer/platform_compat/report.py +92 -0
- tree_sitter_analyzer/plugins/__init__.py +280 -0
- tree_sitter_analyzer/plugins/base.py +647 -0
- tree_sitter_analyzer/plugins/manager.py +384 -0
- tree_sitter_analyzer/project_detector.py +328 -0
- tree_sitter_analyzer/queries/__init__.py +27 -0
- tree_sitter_analyzer/queries/csharp.py +216 -0
- tree_sitter_analyzer/queries/css.py +615 -0
- tree_sitter_analyzer/queries/go.py +275 -0
- tree_sitter_analyzer/queries/html.py +543 -0
- tree_sitter_analyzer/queries/java.py +402 -0
- tree_sitter_analyzer/queries/javascript.py +724 -0
- tree_sitter_analyzer/queries/kotlin.py +192 -0
- tree_sitter_analyzer/queries/markdown.py +258 -0
- tree_sitter_analyzer/queries/php.py +95 -0
- tree_sitter_analyzer/queries/python.py +859 -0
- tree_sitter_analyzer/queries/ruby.py +92 -0
- tree_sitter_analyzer/queries/rust.py +223 -0
- tree_sitter_analyzer/queries/sql.py +555 -0
- tree_sitter_analyzer/queries/typescript.py +871 -0
- tree_sitter_analyzer/queries/yaml.py +236 -0
- tree_sitter_analyzer/query_loader.py +272 -0
- tree_sitter_analyzer/security/__init__.py +22 -0
- tree_sitter_analyzer/security/boundary_manager.py +277 -0
- tree_sitter_analyzer/security/regex_checker.py +297 -0
- tree_sitter_analyzer/security/validator.py +599 -0
- tree_sitter_analyzer/table_formatter.py +782 -0
- tree_sitter_analyzer/utils/__init__.py +53 -0
- tree_sitter_analyzer/utils/logging.py +433 -0
- tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
CLI Adapter for tree-sitter-analyzer
|
|
4
|
+
|
|
5
|
+
Adapter that uses the new unified analysis engine while maintaining compatibility with existing CLI API.
|
|
6
|
+
|
|
7
|
+
Roo Code compliance:
|
|
8
|
+
- Type hints: Required for all functions
|
|
9
|
+
- MCP logging: Log output at each step
|
|
10
|
+
- docstring: Google Style docstring
|
|
11
|
+
- Error handling: Proper exception handling
|
|
12
|
+
- Performance: Optimization through unified engine
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
import logging
|
|
17
|
+
import time
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from ..core.analysis_engine import AnalysisRequest, UnifiedAnalysisEngine
|
|
22
|
+
from ..models import AnalysisResult
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CLIAdapter:
|
|
28
|
+
"""
|
|
29
|
+
CLI Adapter
|
|
30
|
+
|
|
31
|
+
Uses the new unified analysis engine while maintaining compatibility with existing CLI API.
|
|
32
|
+
Provides synchronous API and internally calls asynchronous engine.
|
|
33
|
+
|
|
34
|
+
Features:
|
|
35
|
+
- Maintaining existing API compatibility
|
|
36
|
+
- Utilizing unified analysis engine
|
|
37
|
+
- Sync/async conversion
|
|
38
|
+
- Performance monitoring
|
|
39
|
+
- Error handling
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> adapter = CLIAdapter()
|
|
43
|
+
>>> result = adapter.analyze_file("example.java")
|
|
44
|
+
>>> print(result.classes)
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self) -> None:
|
|
48
|
+
"""
|
|
49
|
+
Initialize CLI adapter
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
Exception: If unified analysis engine initialization fails
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
self._engine = UnifiedAnalysisEngine()
|
|
56
|
+
logger.debug("CLIAdapter initialized successfully")
|
|
57
|
+
except Exception as e:
|
|
58
|
+
logger.error(f"Failed to initialize CLIAdapter: {e}")
|
|
59
|
+
raise
|
|
60
|
+
|
|
61
|
+
def analyze_file(self, file_path: str, **kwargs: Any) -> AnalysisResult:
|
|
62
|
+
"""
|
|
63
|
+
Analyze file (synchronous version)
|
|
64
|
+
|
|
65
|
+
Provides synchronous interface to maintain compatibility with existing CLI API.
|
|
66
|
+
Internally calls asynchronous methods of unified analysis engine.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
file_path: Path to file to analyze
|
|
70
|
+
**kwargs: Analysis options
|
|
71
|
+
- language: Language specification (auto-detection possible)
|
|
72
|
+
- include_complexity: Include complexity calculation
|
|
73
|
+
- include_details: Include detailed information
|
|
74
|
+
- format_type: Output format ("standard", "structure", "summary")
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
AnalysisResult: Analysis result
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
ValueError: For invalid file path
|
|
81
|
+
FileNotFoundError: If file does not exist
|
|
82
|
+
UnsupportedLanguageError: For unsupported language
|
|
83
|
+
|
|
84
|
+
Example:
|
|
85
|
+
>>> adapter = CLIAdapter()
|
|
86
|
+
>>> result = adapter.analyze_file("example.java", include_complexity=True)
|
|
87
|
+
>>> print(f"Classes: {len(result.classes)}")
|
|
88
|
+
"""
|
|
89
|
+
start_time = time.time()
|
|
90
|
+
|
|
91
|
+
# Input validation
|
|
92
|
+
if not file_path or not file_path.strip():
|
|
93
|
+
raise ValueError("File path cannot be empty")
|
|
94
|
+
|
|
95
|
+
# File existence check
|
|
96
|
+
if not Path(file_path).exists():
|
|
97
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
# Create AnalysisRequest
|
|
101
|
+
request = AnalysisRequest(
|
|
102
|
+
file_path=file_path,
|
|
103
|
+
language=kwargs.get("language"),
|
|
104
|
+
include_complexity=kwargs.get("include_complexity", False),
|
|
105
|
+
include_details=kwargs.get("include_details", True),
|
|
106
|
+
format_type=kwargs.get("format_type", "standard"),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Run async engine synchronously
|
|
110
|
+
result = asyncio.run(self._engine.analyze(request))
|
|
111
|
+
|
|
112
|
+
# パフォーマンスログ
|
|
113
|
+
elapsed_time = time.time() - start_time
|
|
114
|
+
logger.debug(f"CLI analysis completed: {file_path} in {elapsed_time:.3f}s")
|
|
115
|
+
|
|
116
|
+
return result
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"CLI analysis failed for {file_path}: {e}")
|
|
120
|
+
raise
|
|
121
|
+
|
|
122
|
+
def analyze_structure(self, file_path: str, **kwargs: Any) -> dict[str, Any]:
|
|
123
|
+
"""
|
|
124
|
+
Structure analysis (legacy API compatible)
|
|
125
|
+
|
|
126
|
+
Returns structure info as a dictionary to keep compatibility with
|
|
127
|
+
existing CLI API.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
file_path: Path to the file to analyze
|
|
131
|
+
**kwargs: Analysis options
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Dict[str, Any]: Structure dictionary
|
|
135
|
+
- file_path
|
|
136
|
+
- classes
|
|
137
|
+
- methods
|
|
138
|
+
- fields
|
|
139
|
+
- imports
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
>>> adapter = CLIAdapter()
|
|
143
|
+
>>> structure = adapter.analyze_structure("example.java")
|
|
144
|
+
>>> print(structure["classes"])
|
|
145
|
+
"""
|
|
146
|
+
# 詳細情報を含めて解析
|
|
147
|
+
kwargs["include_details"] = True
|
|
148
|
+
kwargs["format_type"] = "structure"
|
|
149
|
+
|
|
150
|
+
result = self.analyze_file(file_path, **kwargs)
|
|
151
|
+
|
|
152
|
+
# 構造情報を辞書形式に変換
|
|
153
|
+
# Use unified elements system
|
|
154
|
+
from ..constants import (
|
|
155
|
+
ELEMENT_TYPE_ANNOTATION,
|
|
156
|
+
ELEMENT_TYPE_CLASS,
|
|
157
|
+
ELEMENT_TYPE_FUNCTION,
|
|
158
|
+
ELEMENT_TYPE_IMPORT,
|
|
159
|
+
ELEMENT_TYPE_VARIABLE,
|
|
160
|
+
is_element_of_type,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
elements = result.elements or []
|
|
164
|
+
|
|
165
|
+
# Extract elements by type from unified list
|
|
166
|
+
imports = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_IMPORT)]
|
|
167
|
+
classes = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_CLASS)]
|
|
168
|
+
methods = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_FUNCTION)]
|
|
169
|
+
fields = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_VARIABLE)]
|
|
170
|
+
annotations = [
|
|
171
|
+
e for e in elements if is_element_of_type(e, ELEMENT_TYPE_ANNOTATION)
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
"file_path": result.file_path,
|
|
176
|
+
"language": result.language,
|
|
177
|
+
"package": result.package,
|
|
178
|
+
"imports": [
|
|
179
|
+
{"name": imp.name, "type": str(type(imp).__name__)} for imp in imports
|
|
180
|
+
],
|
|
181
|
+
"classes": [
|
|
182
|
+
{"name": cls.name, "type": str(type(cls).__name__)} for cls in classes
|
|
183
|
+
],
|
|
184
|
+
"methods": [
|
|
185
|
+
{"name": method.name, "type": str(type(method).__name__)}
|
|
186
|
+
for method in methods
|
|
187
|
+
],
|
|
188
|
+
"fields": [
|
|
189
|
+
{"name": field.name, "type": str(type(field).__name__)}
|
|
190
|
+
for field in fields
|
|
191
|
+
],
|
|
192
|
+
"annotations": [
|
|
193
|
+
{
|
|
194
|
+
"name": getattr(ann, "name", str(ann)),
|
|
195
|
+
"type": str(type(ann).__name__),
|
|
196
|
+
}
|
|
197
|
+
for ann in annotations
|
|
198
|
+
],
|
|
199
|
+
"analysis_time": result.analysis_time,
|
|
200
|
+
"elements": [
|
|
201
|
+
{"name": elem.name, "type": str(type(elem).__name__)}
|
|
202
|
+
for elem in result.elements
|
|
203
|
+
],
|
|
204
|
+
"success": result.success,
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
def analyze_batch(
|
|
208
|
+
self, file_paths: list[str], **kwargs: Any
|
|
209
|
+
) -> list[AnalysisResult]:
|
|
210
|
+
"""
|
|
211
|
+
Analyze multiple files in batch
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
file_paths: List of file paths
|
|
215
|
+
**kwargs: Analysis options
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
list[AnalysisResult]: List of results
|
|
219
|
+
|
|
220
|
+
Example:
|
|
221
|
+
>>> adapter = CLIAdapter()
|
|
222
|
+
>>> results = adapter.analyze_batch(["file1.java", "file2.java"])
|
|
223
|
+
>>> print(f"Analyzed {len(results)} files")
|
|
224
|
+
"""
|
|
225
|
+
results = []
|
|
226
|
+
|
|
227
|
+
for file_path in file_paths:
|
|
228
|
+
try:
|
|
229
|
+
result = self.analyze_file(file_path, **kwargs)
|
|
230
|
+
results.append(result)
|
|
231
|
+
except Exception as e:
|
|
232
|
+
logger.warning(f"Failed to analyze {file_path}: {e}")
|
|
233
|
+
# Include failed item with error message
|
|
234
|
+
error_result = AnalysisResult(
|
|
235
|
+
file_path=file_path,
|
|
236
|
+
package=None,
|
|
237
|
+
elements=[],
|
|
238
|
+
analysis_time=0.0,
|
|
239
|
+
success=False,
|
|
240
|
+
error_message=str(e),
|
|
241
|
+
)
|
|
242
|
+
results.append(error_result)
|
|
243
|
+
|
|
244
|
+
return results
|
|
245
|
+
|
|
246
|
+
def get_supported_languages(self) -> list[str]:
|
|
247
|
+
"""
|
|
248
|
+
Get list of supported languages
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
list[str]
|
|
252
|
+
|
|
253
|
+
Example:
|
|
254
|
+
>>> adapter = CLIAdapter()
|
|
255
|
+
>>> languages = adapter.get_supported_languages()
|
|
256
|
+
>>> print(languages)
|
|
257
|
+
"""
|
|
258
|
+
return self._engine.get_supported_languages()
|
|
259
|
+
|
|
260
|
+
def clear_cache(self) -> None:
|
|
261
|
+
"""
|
|
262
|
+
Clear cache
|
|
263
|
+
|
|
264
|
+
Example:
|
|
265
|
+
>>> adapter = CLIAdapter()
|
|
266
|
+
>>> adapter.clear_cache()
|
|
267
|
+
"""
|
|
268
|
+
self._engine.clear_cache()
|
|
269
|
+
logger.debug("CLI adapter cache cleared")
|
|
270
|
+
|
|
271
|
+
def get_cache_stats(self) -> dict[str, Any]:
|
|
272
|
+
"""
|
|
273
|
+
Get cache statistics
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Dict[str, Any]
|
|
277
|
+
|
|
278
|
+
Example:
|
|
279
|
+
>>> adapter = CLIAdapter()
|
|
280
|
+
>>> stats = adapter.get_cache_stats()
|
|
281
|
+
>>> print(f"Cache hit rate: {stats['hit_rate']:.2%}")
|
|
282
|
+
"""
|
|
283
|
+
return self._engine.get_cache_stats()
|
|
284
|
+
|
|
285
|
+
def validate_file(self, file_path: str) -> bool:
|
|
286
|
+
"""
|
|
287
|
+
Validate whether a file is analyzable
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
file_path: File path to validate
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
bool
|
|
294
|
+
|
|
295
|
+
Example:
|
|
296
|
+
>>> adapter = CLIAdapter()
|
|
297
|
+
>>> if adapter.validate_file("example.java"):
|
|
298
|
+
... result = adapter.analyze_file("example.java")
|
|
299
|
+
"""
|
|
300
|
+
try:
|
|
301
|
+
# ファイル存在チェック
|
|
302
|
+
if not Path(file_path).exists():
|
|
303
|
+
return False
|
|
304
|
+
|
|
305
|
+
# 言語サポートチェック
|
|
306
|
+
supported_languages = self.get_supported_languages()
|
|
307
|
+
file_extension = Path(file_path).suffix.lower()
|
|
308
|
+
|
|
309
|
+
# 拡張子から言語を推定
|
|
310
|
+
extension_map = {
|
|
311
|
+
".java": "java",
|
|
312
|
+
".py": "python",
|
|
313
|
+
".js": "javascript",
|
|
314
|
+
".ts": "typescript",
|
|
315
|
+
".cpp": "cpp",
|
|
316
|
+
".c": "c",
|
|
317
|
+
".cs": "csharp",
|
|
318
|
+
".go": "go",
|
|
319
|
+
".rs": "rust",
|
|
320
|
+
".php": "php",
|
|
321
|
+
".rb": "ruby",
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
language = extension_map.get(file_extension)
|
|
325
|
+
return language in supported_languages if language else False
|
|
326
|
+
|
|
327
|
+
except Exception as e:
|
|
328
|
+
logger.warning(f"File validation failed for {file_path}: {e}")
|
|
329
|
+
return False
|
|
330
|
+
|
|
331
|
+
def get_engine_info(self) -> dict[str, Any]:
|
|
332
|
+
"""
|
|
333
|
+
Get engine information
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
Dict[str, Any]
|
|
337
|
+
|
|
338
|
+
Example:
|
|
339
|
+
>>> adapter = CLIAdapter()
|
|
340
|
+
>>> info = adapter.get_engine_info()
|
|
341
|
+
>>> print(f"Engine version: {info['version']}")
|
|
342
|
+
"""
|
|
343
|
+
return {
|
|
344
|
+
"adapter_type": "CLI",
|
|
345
|
+
"engine_type": "UnifiedAnalysisEngine",
|
|
346
|
+
"supported_languages": self.get_supported_languages(),
|
|
347
|
+
"cache_enabled": True,
|
|
348
|
+
"async_support": True,
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
# Legacy AdvancedAnalyzerAdapter removed - use UnifiedAnalysisEngine directly
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def get_analysis_engine() -> "UnifiedAnalysisEngine":
|
|
356
|
+
"""Get analysis engine instance for testing compatibility."""
|
|
357
|
+
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
358
|
+
|
|
359
|
+
return UnifiedAnalysisEngine()
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MCP Adapter for Tree-Sitter Analyzer
|
|
4
|
+
|
|
5
|
+
This module provides an adapter interface for integrating with the MCP protocol.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
|
+
|
|
10
|
+
from ..models import AnalysisResult
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_analysis_engine() -> "UnifiedAnalysisEngine":
|
|
17
|
+
"""Get analysis engine instance for testing compatibility."""
|
|
18
|
+
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
19
|
+
|
|
20
|
+
return UnifiedAnalysisEngine()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def handle_mcp_resource_request(uri: str) -> dict[str, Any]:
|
|
24
|
+
"""Handle MCP resource request for testing compatibility."""
|
|
25
|
+
return {
|
|
26
|
+
"contents": [
|
|
27
|
+
{"mimeType": "application/json", "text": {"mock": "resource"}, "uri": uri}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def read_file_safe(file_path: str) -> str:
|
|
33
|
+
"""Read file safely for MCP resource requests."""
|
|
34
|
+
try:
|
|
35
|
+
from ..encoding_utils import read_file_safe
|
|
36
|
+
|
|
37
|
+
content, _ = read_file_safe(file_path)
|
|
38
|
+
return content
|
|
39
|
+
except Exception as e:
|
|
40
|
+
raise FileNotFoundError(f"Could not read file {file_path}: {e}") from e
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class MCPAdapter:
|
|
44
|
+
"""MCP Adapter for testing compatibility."""
|
|
45
|
+
|
|
46
|
+
def __init__(self) -> None:
|
|
47
|
+
"""Initialize MCP Adapter."""
|
|
48
|
+
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
49
|
+
|
|
50
|
+
self.engine = UnifiedAnalysisEngine()
|
|
51
|
+
|
|
52
|
+
async def analyze_file_async(
|
|
53
|
+
self, file_path: str, **kwargs: Any
|
|
54
|
+
) -> "AnalysisResult":
|
|
55
|
+
"""Analyze file asynchronously."""
|
|
56
|
+
from ..core.analysis_engine import AnalysisRequest
|
|
57
|
+
|
|
58
|
+
request = AnalysisRequest(
|
|
59
|
+
file_path=file_path,
|
|
60
|
+
language=kwargs.get("language"),
|
|
61
|
+
include_complexity=kwargs.get("include_complexity", False),
|
|
62
|
+
include_details=kwargs.get("include_details", True),
|
|
63
|
+
format_type=kwargs.get("format_type", "standard"),
|
|
64
|
+
)
|
|
65
|
+
return await self.engine.analyze(request)
|
|
66
|
+
|
|
67
|
+
async def get_file_structure_async(
|
|
68
|
+
self, file_path: str, **kwargs: Any
|
|
69
|
+
) -> dict[str, Any]:
|
|
70
|
+
"""Get file structure asynchronously."""
|
|
71
|
+
result = await self.analyze_file_async(file_path, **kwargs)
|
|
72
|
+
# Use unified elements system
|
|
73
|
+
from ..constants import (
|
|
74
|
+
ELEMENT_TYPE_ANNOTATION,
|
|
75
|
+
ELEMENT_TYPE_CLASS,
|
|
76
|
+
ELEMENT_TYPE_FUNCTION,
|
|
77
|
+
ELEMENT_TYPE_IMPORT,
|
|
78
|
+
ELEMENT_TYPE_VARIABLE,
|
|
79
|
+
is_element_of_type,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
elements = result.elements or []
|
|
83
|
+
|
|
84
|
+
# Extract elements by type from unified list
|
|
85
|
+
imports = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_IMPORT)]
|
|
86
|
+
classes = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_CLASS)]
|
|
87
|
+
methods = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_FUNCTION)]
|
|
88
|
+
fields = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_VARIABLE)]
|
|
89
|
+
annotations = [
|
|
90
|
+
e for e in elements if is_element_of_type(e, ELEMENT_TYPE_ANNOTATION)
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
"file_path": result.file_path,
|
|
95
|
+
"language": result.language,
|
|
96
|
+
"structure": {
|
|
97
|
+
"classes": [
|
|
98
|
+
{"name": cls.name, "type": str(type(cls).__name__)}
|
|
99
|
+
for cls in classes
|
|
100
|
+
],
|
|
101
|
+
"methods": [
|
|
102
|
+
{"name": method.name, "type": str(type(method).__name__)}
|
|
103
|
+
for method in methods
|
|
104
|
+
],
|
|
105
|
+
"fields": [
|
|
106
|
+
{"name": field.name, "type": str(type(field).__name__)}
|
|
107
|
+
for field in fields
|
|
108
|
+
],
|
|
109
|
+
"imports": [
|
|
110
|
+
{"name": imp.name, "type": str(type(imp).__name__)}
|
|
111
|
+
for imp in imports
|
|
112
|
+
],
|
|
113
|
+
"annotations": [
|
|
114
|
+
{
|
|
115
|
+
"name": getattr(ann, "name", str(ann)),
|
|
116
|
+
"type": str(type(ann).__name__),
|
|
117
|
+
}
|
|
118
|
+
for ann in annotations
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
"metadata": {
|
|
122
|
+
"analysis_time": result.analysis_time,
|
|
123
|
+
"success": result.success,
|
|
124
|
+
"error_message": result.error_message,
|
|
125
|
+
"package": result.package,
|
|
126
|
+
"class_count": len(classes),
|
|
127
|
+
"method_count": len(methods),
|
|
128
|
+
"field_count": len(fields),
|
|
129
|
+
"import_count": len(imports),
|
|
130
|
+
"annotation_count": len(annotations),
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async def analyze_batch_async(
|
|
135
|
+
self, file_paths: list[str], **kwargs: Any
|
|
136
|
+
) -> list["AnalysisResult"]:
|
|
137
|
+
"""Analyze multiple files asynchronously."""
|
|
138
|
+
results = []
|
|
139
|
+
for file_path in file_paths:
|
|
140
|
+
try:
|
|
141
|
+
result = await self.analyze_file_async(file_path, **kwargs)
|
|
142
|
+
results.append(result)
|
|
143
|
+
except Exception as e:
|
|
144
|
+
# Create error result
|
|
145
|
+
from ..models import AnalysisResult
|
|
146
|
+
|
|
147
|
+
error_result = AnalysisResult(
|
|
148
|
+
file_path=file_path,
|
|
149
|
+
language="unknown",
|
|
150
|
+
line_count=0,
|
|
151
|
+
elements=[],
|
|
152
|
+
node_count=0,
|
|
153
|
+
query_results={},
|
|
154
|
+
source_code="",
|
|
155
|
+
package=None,
|
|
156
|
+
analysis_time=0.0,
|
|
157
|
+
success=False,
|
|
158
|
+
error_message=str(e),
|
|
159
|
+
)
|
|
160
|
+
results.append(error_result)
|
|
161
|
+
return results
|
|
162
|
+
|
|
163
|
+
async def handle_mcp_tool_request(
|
|
164
|
+
self, tool_name: str, arguments: dict[str, Any]
|
|
165
|
+
) -> dict[str, Any]:
|
|
166
|
+
"""Handle MCP tool request."""
|
|
167
|
+
if tool_name == "analyze_file":
|
|
168
|
+
file_path = arguments.get("file_path")
|
|
169
|
+
if not file_path:
|
|
170
|
+
return {"error": "file_path is required"}
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
result = await self.analyze_file_async(file_path)
|
|
174
|
+
return {"success": True, "result": result.to_dict()}
|
|
175
|
+
except Exception as e:
|
|
176
|
+
return {"error": str(e)}
|
|
177
|
+
|
|
178
|
+
return {"error": f"Unknown tool: {tool_name}"}
|
|
179
|
+
|
|
180
|
+
async def handle_mcp_resource_request(self, uri: str) -> dict[str, Any]:
|
|
181
|
+
"""Handle MCP resource request."""
|
|
182
|
+
if uri.startswith("code://"):
|
|
183
|
+
# Extract file path from URI
|
|
184
|
+
file_path = uri.replace("code://", "")
|
|
185
|
+
try:
|
|
186
|
+
content = read_file_safe(file_path)
|
|
187
|
+
return {"uri": uri, "content": content, "mimeType": "text/plain"}
|
|
188
|
+
except Exception as e:
|
|
189
|
+
return {"error": str(e)}
|
|
190
|
+
|
|
191
|
+
return {"error": f"Unsupported URI: {uri}"}
|
|
192
|
+
|
|
193
|
+
def cleanup(self) -> None:
|
|
194
|
+
"""Cleanup resources."""
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
async def aclose(self) -> None:
|
|
198
|
+
"""Async cleanup."""
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
async def analyze_with_mcp_request(
|
|
202
|
+
self, arguments: dict[str, Any]
|
|
203
|
+
) -> AnalysisResult:
|
|
204
|
+
"""Analyze with MCP request."""
|
|
205
|
+
if "file_path" not in arguments:
|
|
206
|
+
raise KeyError("file_path is required in MCP request")
|
|
207
|
+
result = await self.analyze_file_async(arguments["file_path"])
|
|
208
|
+
return result
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class MCPServerAdapter:
|
|
212
|
+
"""MCP Server Adapter for testing compatibility."""
|
|
213
|
+
|
|
214
|
+
def __init__(self) -> None:
|
|
215
|
+
"""Initialize MCP Server Adapter."""
|
|
216
|
+
self.mcp_adapter = MCPAdapter()
|
|
217
|
+
|
|
218
|
+
async def handle_request(
|
|
219
|
+
self, method: str, params: dict[str, Any]
|
|
220
|
+
) -> dict[str, Any]:
|
|
221
|
+
"""Handle MCP request."""
|
|
222
|
+
if not params:
|
|
223
|
+
return {"error": "params are required"}
|
|
224
|
+
return await self.mcp_adapter.handle_mcp_tool_request(method, params)
|