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.
Files changed (149) hide show
  1. tree_sitter_analyzer/__init__.py +132 -0
  2. tree_sitter_analyzer/__main__.py +11 -0
  3. tree_sitter_analyzer/api.py +853 -0
  4. tree_sitter_analyzer/cli/__init__.py +39 -0
  5. tree_sitter_analyzer/cli/__main__.py +12 -0
  6. tree_sitter_analyzer/cli/argument_validator.py +89 -0
  7. tree_sitter_analyzer/cli/commands/__init__.py +26 -0
  8. tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
  9. tree_sitter_analyzer/cli/commands/base_command.py +181 -0
  10. tree_sitter_analyzer/cli/commands/default_command.py +18 -0
  11. tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
  12. tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
  13. tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
  14. tree_sitter_analyzer/cli/commands/query_command.py +109 -0
  15. tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
  16. tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
  17. tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
  18. tree_sitter_analyzer/cli/commands/table_command.py +414 -0
  19. tree_sitter_analyzer/cli/info_commands.py +124 -0
  20. tree_sitter_analyzer/cli_main.py +472 -0
  21. tree_sitter_analyzer/constants.py +85 -0
  22. tree_sitter_analyzer/core/__init__.py +15 -0
  23. tree_sitter_analyzer/core/analysis_engine.py +580 -0
  24. tree_sitter_analyzer/core/cache_service.py +333 -0
  25. tree_sitter_analyzer/core/engine.py +585 -0
  26. tree_sitter_analyzer/core/parser.py +293 -0
  27. tree_sitter_analyzer/core/query.py +605 -0
  28. tree_sitter_analyzer/core/query_filter.py +200 -0
  29. tree_sitter_analyzer/core/query_service.py +340 -0
  30. tree_sitter_analyzer/encoding_utils.py +530 -0
  31. tree_sitter_analyzer/exceptions.py +747 -0
  32. tree_sitter_analyzer/file_handler.py +246 -0
  33. tree_sitter_analyzer/formatters/__init__.py +1 -0
  34. tree_sitter_analyzer/formatters/base_formatter.py +201 -0
  35. tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
  36. tree_sitter_analyzer/formatters/formatter_config.py +197 -0
  37. tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
  38. tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
  39. tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
  40. tree_sitter_analyzer/formatters/go_formatter.py +368 -0
  41. tree_sitter_analyzer/formatters/html_formatter.py +498 -0
  42. tree_sitter_analyzer/formatters/java_formatter.py +423 -0
  43. tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
  44. tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
  45. tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
  46. tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
  47. tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
  48. tree_sitter_analyzer/formatters/php_formatter.py +301 -0
  49. tree_sitter_analyzer/formatters/python_formatter.py +830 -0
  50. tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
  51. tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
  52. tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
  53. tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
  54. tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
  55. tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
  56. tree_sitter_analyzer/interfaces/__init__.py +9 -0
  57. tree_sitter_analyzer/interfaces/cli.py +535 -0
  58. tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
  59. tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
  60. tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
  61. tree_sitter_analyzer/language_detector.py +553 -0
  62. tree_sitter_analyzer/language_loader.py +271 -0
  63. tree_sitter_analyzer/languages/__init__.py +10 -0
  64. tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
  65. tree_sitter_analyzer/languages/css_plugin.py +449 -0
  66. tree_sitter_analyzer/languages/go_plugin.py +836 -0
  67. tree_sitter_analyzer/languages/html_plugin.py +496 -0
  68. tree_sitter_analyzer/languages/java_plugin.py +1299 -0
  69. tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
  70. tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
  71. tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
  72. tree_sitter_analyzer/languages/php_plugin.py +862 -0
  73. tree_sitter_analyzer/languages/python_plugin.py +1636 -0
  74. tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
  75. tree_sitter_analyzer/languages/rust_plugin.py +673 -0
  76. tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
  77. tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
  78. tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
  79. tree_sitter_analyzer/legacy_table_formatter.py +860 -0
  80. tree_sitter_analyzer/mcp/__init__.py +34 -0
  81. tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
  82. tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
  83. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
  84. tree_sitter_analyzer/mcp/server.py +869 -0
  85. tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
  86. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
  87. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
  88. tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
  89. tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
  90. tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
  91. tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
  92. tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
  93. tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
  94. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
  95. tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
  96. tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
  97. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
  98. tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
  99. tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
  100. tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
  101. tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
  102. tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
  103. tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
  104. tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
  105. tree_sitter_analyzer/models.py +840 -0
  106. tree_sitter_analyzer/mypy_current_errors.txt +2 -0
  107. tree_sitter_analyzer/output_manager.py +255 -0
  108. tree_sitter_analyzer/platform_compat/__init__.py +3 -0
  109. tree_sitter_analyzer/platform_compat/adapter.py +324 -0
  110. tree_sitter_analyzer/platform_compat/compare.py +224 -0
  111. tree_sitter_analyzer/platform_compat/detector.py +67 -0
  112. tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
  113. tree_sitter_analyzer/platform_compat/profiles.py +217 -0
  114. tree_sitter_analyzer/platform_compat/record.py +55 -0
  115. tree_sitter_analyzer/platform_compat/recorder.py +155 -0
  116. tree_sitter_analyzer/platform_compat/report.py +92 -0
  117. tree_sitter_analyzer/plugins/__init__.py +280 -0
  118. tree_sitter_analyzer/plugins/base.py +647 -0
  119. tree_sitter_analyzer/plugins/manager.py +384 -0
  120. tree_sitter_analyzer/project_detector.py +328 -0
  121. tree_sitter_analyzer/queries/__init__.py +27 -0
  122. tree_sitter_analyzer/queries/csharp.py +216 -0
  123. tree_sitter_analyzer/queries/css.py +615 -0
  124. tree_sitter_analyzer/queries/go.py +275 -0
  125. tree_sitter_analyzer/queries/html.py +543 -0
  126. tree_sitter_analyzer/queries/java.py +402 -0
  127. tree_sitter_analyzer/queries/javascript.py +724 -0
  128. tree_sitter_analyzer/queries/kotlin.py +192 -0
  129. tree_sitter_analyzer/queries/markdown.py +258 -0
  130. tree_sitter_analyzer/queries/php.py +95 -0
  131. tree_sitter_analyzer/queries/python.py +859 -0
  132. tree_sitter_analyzer/queries/ruby.py +92 -0
  133. tree_sitter_analyzer/queries/rust.py +223 -0
  134. tree_sitter_analyzer/queries/sql.py +555 -0
  135. tree_sitter_analyzer/queries/typescript.py +871 -0
  136. tree_sitter_analyzer/queries/yaml.py +236 -0
  137. tree_sitter_analyzer/query_loader.py +272 -0
  138. tree_sitter_analyzer/security/__init__.py +22 -0
  139. tree_sitter_analyzer/security/boundary_manager.py +277 -0
  140. tree_sitter_analyzer/security/regex_checker.py +297 -0
  141. tree_sitter_analyzer/security/validator.py +599 -0
  142. tree_sitter_analyzer/table_formatter.py +782 -0
  143. tree_sitter_analyzer/utils/__init__.py +53 -0
  144. tree_sitter_analyzer/utils/logging.py +433 -0
  145. tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
  146. tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
  147. tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
  148. tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
  149. 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)