tree-sitter-analyzer 0.9.2__py3-none-any.whl → 0.9.3__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/cli/commands/base_command.py +2 -3
- tree_sitter_analyzer/cli_main.py +7 -3
- tree_sitter_analyzer/core/analysis_engine.py +17 -9
- tree_sitter_analyzer/core/cache_service.py +1 -1
- tree_sitter_analyzer/interfaces/cli.py +1 -1
- tree_sitter_analyzer/interfaces/cli_adapter.py +3 -3
- tree_sitter_analyzer/project_detector.py +330 -317
- tree_sitter_analyzer/security/boundary_manager.py +57 -51
- tree_sitter_analyzer/utils.py +3 -3
- {tree_sitter_analyzer-0.9.2.dist-info → tree_sitter_analyzer-0.9.3.dist-info}/METADATA +13 -13
- {tree_sitter_analyzer-0.9.2.dist-info → tree_sitter_analyzer-0.9.3.dist-info}/RECORD +14 -14
- {tree_sitter_analyzer-0.9.2.dist-info → tree_sitter_analyzer-0.9.3.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.9.2.dist-info → tree_sitter_analyzer-0.9.3.dist-info}/entry_points.txt +0 -0
tree_sitter_analyzer/__init__.py
CHANGED
|
@@ -85,9 +85,8 @@ class BaseCommand(ABC):
|
|
|
85
85
|
if (not hasattr(self.args, "table") or not self.args.table) and (
|
|
86
86
|
not hasattr(self.args, "quiet") or not self.args.quiet
|
|
87
87
|
):
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
88
|
+
# Language auto-detected - only show in verbose mode
|
|
89
|
+
pass
|
|
91
90
|
|
|
92
91
|
# Language support validation
|
|
93
92
|
if not is_language_supported(target_language):
|
tree_sitter_analyzer/cli_main.py
CHANGED
|
@@ -215,9 +215,7 @@ def handle_special_commands(args: argparse.Namespace) -> int | None:
|
|
|
215
215
|
return 1
|
|
216
216
|
|
|
217
217
|
if args.end_line and args.end_line < args.start_line:
|
|
218
|
-
output_error(
|
|
219
|
-
"--end-line must be greater than or equal to --start-line"
|
|
220
|
-
)
|
|
218
|
+
output_error("--end-line must be greater than or equal to --start-line")
|
|
221
219
|
return 1
|
|
222
220
|
|
|
223
221
|
if args.start_column is not None and args.start_column < 0:
|
|
@@ -256,10 +254,16 @@ def main() -> None:
|
|
|
256
254
|
|
|
257
255
|
if "--quiet" in sys.argv:
|
|
258
256
|
os.environ["LOG_LEVEL"] = "ERROR"
|
|
257
|
+
else:
|
|
258
|
+
# Set default log level to WARNING to reduce output
|
|
259
|
+
os.environ.setdefault("LOG_LEVEL", "WARNING")
|
|
259
260
|
|
|
260
261
|
parser = create_argument_parser()
|
|
261
262
|
args = parser.parse_args()
|
|
262
263
|
|
|
264
|
+
# Configure default logging levels to reduce output
|
|
265
|
+
logging.getLogger("tree_sitter_analyzer.performance").setLevel(logging.ERROR)
|
|
266
|
+
|
|
263
267
|
# Configure logging for table output
|
|
264
268
|
if hasattr(args, "table") and args.table:
|
|
265
269
|
logging.getLogger().setLevel(logging.ERROR)
|
|
@@ -15,7 +15,7 @@ Roo Code compliance:
|
|
|
15
15
|
import hashlib
|
|
16
16
|
import threading
|
|
17
17
|
from dataclasses import dataclass
|
|
18
|
-
from typing import Any,
|
|
18
|
+
from typing import Any, Optional, Protocol
|
|
19
19
|
|
|
20
20
|
from ..models import AnalysisResult
|
|
21
21
|
from ..plugins.base import LanguagePlugin as BaseLanguagePlugin
|
|
@@ -204,7 +204,7 @@ class UnifiedAnalysisEngine:
|
|
|
204
204
|
_performance_monitor: Performance monitor
|
|
205
205
|
"""
|
|
206
206
|
|
|
207
|
-
_instances:
|
|
207
|
+
_instances: dict[str, "UnifiedAnalysisEngine"] = {}
|
|
208
208
|
_lock: threading.Lock = threading.Lock()
|
|
209
209
|
|
|
210
210
|
def __new__(cls, project_root: str = None) -> "UnifiedAnalysisEngine":
|
|
@@ -238,18 +238,20 @@ class UnifiedAnalysisEngine:
|
|
|
238
238
|
|
|
239
239
|
self._initialized = True
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
log_debug(
|
|
242
|
+
f"UnifiedAnalysisEngine initialized with project root: {project_root}"
|
|
243
|
+
)
|
|
242
244
|
|
|
243
245
|
def _load_plugins(self) -> None:
|
|
244
246
|
"""Auto-load available plugins"""
|
|
245
|
-
|
|
247
|
+
log_debug("Loading plugins using PluginManager...")
|
|
246
248
|
|
|
247
249
|
try:
|
|
248
250
|
# PluginManagerの自動ロード機能を使用
|
|
249
251
|
loaded_plugins = self._plugin_manager.load_plugins()
|
|
250
252
|
|
|
251
253
|
final_languages = [plugin.get_language_name() for plugin in loaded_plugins]
|
|
252
|
-
|
|
254
|
+
log_debug(
|
|
253
255
|
f"Successfully loaded {len(final_languages)} language plugins: {', '.join(final_languages)}"
|
|
254
256
|
)
|
|
255
257
|
except Exception as e:
|
|
@@ -272,12 +274,16 @@ class UnifiedAnalysisEngine:
|
|
|
272
274
|
UnsupportedLanguageError: When language is not supported
|
|
273
275
|
FileNotFoundError: When file is not found
|
|
274
276
|
"""
|
|
275
|
-
|
|
277
|
+
log_debug(f"Starting analysis for {request.file_path}")
|
|
276
278
|
|
|
277
279
|
# Security validation
|
|
278
|
-
is_valid, error_msg = self._security_validator.validate_file_path(
|
|
280
|
+
is_valid, error_msg = self._security_validator.validate_file_path(
|
|
281
|
+
request.file_path
|
|
282
|
+
)
|
|
279
283
|
if not is_valid:
|
|
280
|
-
log_error(
|
|
284
|
+
log_error(
|
|
285
|
+
f"Security validation failed for file path: {request.file_path} - {error_msg}"
|
|
286
|
+
)
|
|
281
287
|
raise ValueError(f"Invalid file path: {error_msg}")
|
|
282
288
|
|
|
283
289
|
# Cache check (shared across CLI/MCP)
|
|
@@ -341,7 +347,9 @@ class UnifiedAnalysisEngine:
|
|
|
341
347
|
# Security validation
|
|
342
348
|
is_valid, error_msg = self._security_validator.validate_file_path(file_path)
|
|
343
349
|
if not is_valid:
|
|
344
|
-
log_error(
|
|
350
|
+
log_error(
|
|
351
|
+
f"Security validation failed for file path: {file_path} - {error_msg}"
|
|
352
|
+
)
|
|
345
353
|
raise ValueError(f"Invalid file path: {error_msg}")
|
|
346
354
|
|
|
347
355
|
request = AnalysisRequest(
|
|
@@ -54,7 +54,7 @@ For more information, visit: https://github.com/aimasteracc/tree-sitter-analyzer
|
|
|
54
54
|
|
|
55
55
|
# Global options
|
|
56
56
|
parser.add_argument(
|
|
57
|
-
"--version", action="version", version="tree-sitter-analyzer 0.
|
|
57
|
+
"--version", action="version", version="tree-sitter-analyzer 0.9.3"
|
|
58
58
|
)
|
|
59
59
|
parser.add_argument(
|
|
60
60
|
"--verbose", "-v", action="store_true", help="Enable verbose output"
|
|
@@ -53,7 +53,7 @@ class CLIAdapter:
|
|
|
53
53
|
"""
|
|
54
54
|
try:
|
|
55
55
|
self._engine = UnifiedAnalysisEngine()
|
|
56
|
-
logger.
|
|
56
|
+
logger.debug("CLIAdapter initialized successfully")
|
|
57
57
|
except Exception as e:
|
|
58
58
|
logger.error(f"Failed to initialize CLIAdapter: {e}")
|
|
59
59
|
raise
|
|
@@ -111,7 +111,7 @@ class CLIAdapter:
|
|
|
111
111
|
|
|
112
112
|
# パフォーマンスログ
|
|
113
113
|
elapsed_time = time.time() - start_time
|
|
114
|
-
logger.
|
|
114
|
+
logger.debug(f"CLI analysis completed: {file_path} in {elapsed_time:.3f}s")
|
|
115
115
|
|
|
116
116
|
return result
|
|
117
117
|
|
|
@@ -251,7 +251,7 @@ class CLIAdapter:
|
|
|
251
251
|
>>> adapter.clear_cache()
|
|
252
252
|
"""
|
|
253
253
|
self._engine.clear_cache()
|
|
254
|
-
logger.
|
|
254
|
+
logger.debug("CLI adapter cache cleared")
|
|
255
255
|
|
|
256
256
|
def get_cache_stats(self) -> dict[str, Any]:
|
|
257
257
|
"""
|
|
@@ -1,317 +1,330 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Project Root Detection
|
|
4
|
-
|
|
5
|
-
Intelligent detection of project root directories based on common project markers.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
logger.debug(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
current_dir
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
""
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
# Priority
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
#
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Project Root Detection
|
|
4
|
+
|
|
5
|
+
Intelligent detection of project root directories based on common project markers.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
# Common project root indicators (in priority order)
|
|
14
|
+
PROJECT_MARKERS = [
|
|
15
|
+
# Version control
|
|
16
|
+
".git",
|
|
17
|
+
".hg",
|
|
18
|
+
".svn",
|
|
19
|
+
# Python projects
|
|
20
|
+
"pyproject.toml",
|
|
21
|
+
"setup.py",
|
|
22
|
+
"setup.cfg",
|
|
23
|
+
"requirements.txt",
|
|
24
|
+
"Pipfile",
|
|
25
|
+
"poetry.lock",
|
|
26
|
+
"conda.yaml",
|
|
27
|
+
"environment.yml",
|
|
28
|
+
# JavaScript/Node.js projects
|
|
29
|
+
"package.json",
|
|
30
|
+
"package-lock.json",
|
|
31
|
+
"yarn.lock",
|
|
32
|
+
"node_modules",
|
|
33
|
+
# Java projects
|
|
34
|
+
"pom.xml",
|
|
35
|
+
"build.gradle",
|
|
36
|
+
"build.gradle.kts",
|
|
37
|
+
"gradlew",
|
|
38
|
+
"mvnw",
|
|
39
|
+
# C/C++ projects
|
|
40
|
+
"CMakeLists.txt",
|
|
41
|
+
"Makefile",
|
|
42
|
+
"configure.ac",
|
|
43
|
+
"configure.in",
|
|
44
|
+
# Rust projects
|
|
45
|
+
"Cargo.toml",
|
|
46
|
+
"Cargo.lock",
|
|
47
|
+
# Go projects
|
|
48
|
+
"go.mod",
|
|
49
|
+
"go.sum",
|
|
50
|
+
# .NET projects
|
|
51
|
+
"*.sln",
|
|
52
|
+
"*.csproj",
|
|
53
|
+
"*.vbproj",
|
|
54
|
+
"*.fsproj",
|
|
55
|
+
# Other common markers
|
|
56
|
+
"README.md",
|
|
57
|
+
"README.rst",
|
|
58
|
+
"README.txt",
|
|
59
|
+
"LICENSE",
|
|
60
|
+
"CHANGELOG.md",
|
|
61
|
+
".gitignore",
|
|
62
|
+
".dockerignore",
|
|
63
|
+
"Dockerfile",
|
|
64
|
+
"docker-compose.yml",
|
|
65
|
+
".editorconfig",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ProjectRootDetector:
|
|
70
|
+
"""Intelligent project root directory detection."""
|
|
71
|
+
|
|
72
|
+
def __init__(self, max_depth: int = 10):
|
|
73
|
+
"""
|
|
74
|
+
Initialize project root detector.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
max_depth: Maximum directory levels to traverse upward
|
|
78
|
+
"""
|
|
79
|
+
self.max_depth = max_depth
|
|
80
|
+
|
|
81
|
+
def detect_from_file(self, file_path: str) -> str | None:
|
|
82
|
+
"""
|
|
83
|
+
Detect project root from a file path.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
file_path: Path to a file within the project
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Project root directory path, or None if not detected
|
|
90
|
+
"""
|
|
91
|
+
if not file_path:
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
# Convert to absolute path and get directory
|
|
96
|
+
abs_path = os.path.abspath(file_path)
|
|
97
|
+
if os.path.isfile(abs_path):
|
|
98
|
+
start_dir = os.path.dirname(abs_path)
|
|
99
|
+
else:
|
|
100
|
+
start_dir = abs_path
|
|
101
|
+
|
|
102
|
+
return self._traverse_upward(start_dir)
|
|
103
|
+
|
|
104
|
+
except Exception as e:
|
|
105
|
+
logger.warning(f"Error detecting project root from {file_path}: {e}")
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
def detect_from_cwd(self) -> str | None:
|
|
109
|
+
"""
|
|
110
|
+
Detect project root from current working directory.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Project root directory path, or None if not detected
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
return self._traverse_upward(os.getcwd())
|
|
117
|
+
except Exception as e:
|
|
118
|
+
logger.warning(f"Error detecting project root from cwd: {e}")
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
def _traverse_upward(self, start_dir: str) -> str | None:
|
|
122
|
+
"""
|
|
123
|
+
Traverse upward from start directory looking for project markers.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
start_dir: Directory to start traversal from
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Project root directory path, or None if not found
|
|
130
|
+
"""
|
|
131
|
+
current_dir = os.path.abspath(start_dir)
|
|
132
|
+
candidates = []
|
|
133
|
+
|
|
134
|
+
for _depth in range(self.max_depth):
|
|
135
|
+
# Check for project markers in current directory
|
|
136
|
+
markers_found = self._find_markers_in_dir(current_dir)
|
|
137
|
+
|
|
138
|
+
if markers_found:
|
|
139
|
+
# Calculate score based on marker priority and count
|
|
140
|
+
score = self._calculate_score(markers_found)
|
|
141
|
+
candidates.append((current_dir, score, markers_found))
|
|
142
|
+
|
|
143
|
+
# If we find high-priority markers, we can stop early
|
|
144
|
+
if any(
|
|
145
|
+
marker
|
|
146
|
+
in [
|
|
147
|
+
".git",
|
|
148
|
+
"pyproject.toml",
|
|
149
|
+
"package.json",
|
|
150
|
+
"pom.xml",
|
|
151
|
+
"Cargo.toml",
|
|
152
|
+
"go.mod",
|
|
153
|
+
]
|
|
154
|
+
for marker in markers_found
|
|
155
|
+
):
|
|
156
|
+
logger.debug(
|
|
157
|
+
f"Found high-priority project root: {current_dir} (markers: {markers_found})"
|
|
158
|
+
)
|
|
159
|
+
return current_dir
|
|
160
|
+
|
|
161
|
+
# Move up one directory
|
|
162
|
+
parent_dir = os.path.dirname(current_dir)
|
|
163
|
+
if parent_dir == current_dir: # Reached filesystem root
|
|
164
|
+
break
|
|
165
|
+
current_dir = parent_dir
|
|
166
|
+
|
|
167
|
+
# Return the best candidate if any found
|
|
168
|
+
if candidates:
|
|
169
|
+
# Sort by score (descending) and return the best
|
|
170
|
+
candidates.sort(key=lambda x: x[1], reverse=True)
|
|
171
|
+
best_candidate = candidates[0]
|
|
172
|
+
logger.debug(
|
|
173
|
+
f"Selected project root: {best_candidate[0]} (score: {best_candidate[1]}, markers: {best_candidate[2]})"
|
|
174
|
+
)
|
|
175
|
+
return best_candidate[0]
|
|
176
|
+
|
|
177
|
+
logger.debug(f"No project root detected from {start_dir}")
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
def _find_markers_in_dir(self, directory: str) -> list[str]:
|
|
181
|
+
"""
|
|
182
|
+
Find project markers in a directory.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
directory: Directory to search in
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
List of found marker names
|
|
189
|
+
"""
|
|
190
|
+
found_markers = []
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
dir_contents = os.listdir(directory)
|
|
194
|
+
|
|
195
|
+
for marker in PROJECT_MARKERS:
|
|
196
|
+
if "*" in marker:
|
|
197
|
+
# Handle glob patterns
|
|
198
|
+
import glob
|
|
199
|
+
|
|
200
|
+
pattern = os.path.join(directory, marker)
|
|
201
|
+
if glob.glob(pattern):
|
|
202
|
+
found_markers.append(marker)
|
|
203
|
+
else:
|
|
204
|
+
# Handle exact matches
|
|
205
|
+
if marker in dir_contents:
|
|
206
|
+
found_markers.append(marker)
|
|
207
|
+
|
|
208
|
+
except (OSError, PermissionError) as e:
|
|
209
|
+
logger.debug(f"Cannot access directory {directory}: {e}")
|
|
210
|
+
|
|
211
|
+
return found_markers
|
|
212
|
+
|
|
213
|
+
def _calculate_score(self, markers: list[str]) -> int:
|
|
214
|
+
"""
|
|
215
|
+
Calculate a score for project root candidates based on markers found.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
markers: List of found markers
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Score (higher is better)
|
|
222
|
+
"""
|
|
223
|
+
score = 0
|
|
224
|
+
|
|
225
|
+
# High-priority markers
|
|
226
|
+
high_priority = [
|
|
227
|
+
".git",
|
|
228
|
+
"pyproject.toml",
|
|
229
|
+
"package.json",
|
|
230
|
+
"pom.xml",
|
|
231
|
+
"Cargo.toml",
|
|
232
|
+
"go.mod",
|
|
233
|
+
]
|
|
234
|
+
medium_priority = ["setup.py", "requirements.txt", "CMakeLists.txt", "Makefile"]
|
|
235
|
+
|
|
236
|
+
for marker in markers:
|
|
237
|
+
if marker in high_priority:
|
|
238
|
+
score += 100
|
|
239
|
+
elif marker in medium_priority:
|
|
240
|
+
score += 50
|
|
241
|
+
else:
|
|
242
|
+
score += 10
|
|
243
|
+
|
|
244
|
+
# Bonus for multiple markers
|
|
245
|
+
if len(markers) > 1:
|
|
246
|
+
score += len(markers) * 5
|
|
247
|
+
|
|
248
|
+
return score
|
|
249
|
+
|
|
250
|
+
def get_fallback_root(self, file_path: str) -> str:
|
|
251
|
+
"""
|
|
252
|
+
Get fallback project root when detection fails.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
file_path: Original file path
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Fallback directory (file's directory or cwd)
|
|
259
|
+
"""
|
|
260
|
+
try:
|
|
261
|
+
if file_path and os.path.exists(file_path):
|
|
262
|
+
if os.path.isfile(file_path):
|
|
263
|
+
return os.path.dirname(os.path.abspath(file_path))
|
|
264
|
+
else:
|
|
265
|
+
return os.path.abspath(file_path)
|
|
266
|
+
else:
|
|
267
|
+
return os.getcwd()
|
|
268
|
+
except Exception:
|
|
269
|
+
return os.getcwd()
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def detect_project_root(
|
|
273
|
+
file_path: str | None = None, explicit_root: str | None = None
|
|
274
|
+
) -> str:
|
|
275
|
+
"""
|
|
276
|
+
Unified project root detection with priority handling.
|
|
277
|
+
|
|
278
|
+
Priority order:
|
|
279
|
+
1. explicit_root parameter (highest priority)
|
|
280
|
+
2. Auto-detection from file_path
|
|
281
|
+
3. Auto-detection from current working directory
|
|
282
|
+
4. Fallback to file directory or cwd
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
file_path: Path to a file within the project
|
|
286
|
+
explicit_root: Explicitly specified project root
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
Project root directory path
|
|
290
|
+
"""
|
|
291
|
+
detector = ProjectRootDetector()
|
|
292
|
+
|
|
293
|
+
# Priority 1: Explicit root
|
|
294
|
+
if explicit_root:
|
|
295
|
+
if os.path.exists(explicit_root) and os.path.isdir(explicit_root):
|
|
296
|
+
logger.debug(f"Using explicit project root: {explicit_root}")
|
|
297
|
+
return os.path.abspath(explicit_root)
|
|
298
|
+
else:
|
|
299
|
+
logger.warning(f"Explicit project root does not exist: {explicit_root}")
|
|
300
|
+
|
|
301
|
+
# Priority 2: Auto-detection from file path
|
|
302
|
+
if file_path:
|
|
303
|
+
detected_root = detector.detect_from_file(file_path)
|
|
304
|
+
if detected_root:
|
|
305
|
+
logger.debug(f"Auto-detected project root from file: {detected_root}")
|
|
306
|
+
return detected_root
|
|
307
|
+
|
|
308
|
+
# Priority 3: Auto-detection from cwd
|
|
309
|
+
detected_root = detector.detect_from_cwd()
|
|
310
|
+
if detected_root:
|
|
311
|
+
logger.debug(f"Auto-detected project root from cwd: {detected_root}")
|
|
312
|
+
return detected_root
|
|
313
|
+
|
|
314
|
+
# Priority 4: Fallback
|
|
315
|
+
fallback_root = detector.get_fallback_root(file_path)
|
|
316
|
+
logger.debug(f"Using fallback project root: {fallback_root}")
|
|
317
|
+
return fallback_root
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
if __name__ == "__main__":
|
|
321
|
+
# Test the detector
|
|
322
|
+
import sys
|
|
323
|
+
|
|
324
|
+
if len(sys.argv) > 1:
|
|
325
|
+
test_path = sys.argv[1]
|
|
326
|
+
result = detect_project_root(test_path)
|
|
327
|
+
print(f"Project root for '{test_path}': {result}")
|
|
328
|
+
else:
|
|
329
|
+
result = detect_project_root()
|
|
330
|
+
print(f"Project root from cwd: {result}")
|
|
@@ -8,7 +8,6 @@ outside the designated project directory.
|
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Optional, Set
|
|
12
11
|
|
|
13
12
|
from ..exceptions import SecurityError
|
|
14
13
|
from ..utils import log_debug, log_info, log_warning
|
|
@@ -17,10 +16,10 @@ from ..utils import log_debug, log_info, log_warning
|
|
|
17
16
|
class ProjectBoundaryManager:
|
|
18
17
|
"""
|
|
19
18
|
Project boundary manager for access control.
|
|
20
|
-
|
|
19
|
+
|
|
21
20
|
This class enforces strict boundaries around project directories
|
|
22
21
|
to prevent unauthorized file access outside the project scope.
|
|
23
|
-
|
|
22
|
+
|
|
24
23
|
Features:
|
|
25
24
|
- Real path resolution for symlink protection
|
|
26
25
|
- Configurable allowed directories
|
|
@@ -31,59 +30,59 @@ class ProjectBoundaryManager:
|
|
|
31
30
|
def __init__(self, project_root: str) -> None:
|
|
32
31
|
"""
|
|
33
32
|
Initialize project boundary manager.
|
|
34
|
-
|
|
33
|
+
|
|
35
34
|
Args:
|
|
36
35
|
project_root: Root directory of the project
|
|
37
|
-
|
|
36
|
+
|
|
38
37
|
Raises:
|
|
39
38
|
SecurityError: If project root is invalid
|
|
40
39
|
"""
|
|
41
40
|
if not project_root:
|
|
42
41
|
raise SecurityError("Project root cannot be empty")
|
|
43
|
-
|
|
42
|
+
|
|
44
43
|
if not os.path.exists(project_root):
|
|
45
44
|
raise SecurityError(f"Project root does not exist: {project_root}")
|
|
46
|
-
|
|
45
|
+
|
|
47
46
|
if not os.path.isdir(project_root):
|
|
48
47
|
raise SecurityError(f"Project root is not a directory: {project_root}")
|
|
49
|
-
|
|
48
|
+
|
|
50
49
|
# Store real path to prevent symlink attacks
|
|
51
50
|
self.project_root = os.path.realpath(project_root)
|
|
52
|
-
self.allowed_directories:
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
self.allowed_directories: set[str] = {self.project_root}
|
|
52
|
+
|
|
53
|
+
log_debug(f"ProjectBoundaryManager initialized with root: {self.project_root}")
|
|
55
54
|
|
|
56
55
|
def add_allowed_directory(self, directory: str) -> None:
|
|
57
56
|
"""
|
|
58
57
|
Add an additional allowed directory.
|
|
59
|
-
|
|
58
|
+
|
|
60
59
|
Args:
|
|
61
60
|
directory: Directory path to allow access to
|
|
62
|
-
|
|
61
|
+
|
|
63
62
|
Raises:
|
|
64
63
|
SecurityError: If directory is invalid
|
|
65
64
|
"""
|
|
66
65
|
if not directory:
|
|
67
66
|
raise SecurityError("Directory cannot be empty")
|
|
68
|
-
|
|
67
|
+
|
|
69
68
|
if not os.path.exists(directory):
|
|
70
69
|
raise SecurityError(f"Directory does not exist: {directory}")
|
|
71
|
-
|
|
70
|
+
|
|
72
71
|
if not os.path.isdir(directory):
|
|
73
72
|
raise SecurityError(f"Path is not a directory: {directory}")
|
|
74
|
-
|
|
73
|
+
|
|
75
74
|
real_dir = os.path.realpath(directory)
|
|
76
75
|
self.allowed_directories.add(real_dir)
|
|
77
|
-
|
|
76
|
+
|
|
78
77
|
log_info(f"Added allowed directory: {real_dir}")
|
|
79
78
|
|
|
80
79
|
def is_within_project(self, file_path: str) -> bool:
|
|
81
80
|
"""
|
|
82
81
|
Check if file path is within project boundaries.
|
|
83
|
-
|
|
82
|
+
|
|
84
83
|
Args:
|
|
85
84
|
file_path: File path to check
|
|
86
|
-
|
|
85
|
+
|
|
87
86
|
Returns:
|
|
88
87
|
True if path is within allowed boundaries
|
|
89
88
|
"""
|
|
@@ -91,58 +90,61 @@ class ProjectBoundaryManager:
|
|
|
91
90
|
if not file_path:
|
|
92
91
|
log_warning("Empty file path provided to boundary check")
|
|
93
92
|
return False
|
|
94
|
-
|
|
93
|
+
|
|
95
94
|
# Resolve real path to handle symlinks
|
|
96
95
|
real_path = os.path.realpath(file_path)
|
|
97
|
-
|
|
96
|
+
|
|
98
97
|
# Check against all allowed directories
|
|
99
98
|
for allowed_dir in self.allowed_directories:
|
|
100
|
-
if
|
|
99
|
+
if (
|
|
100
|
+
real_path.startswith(allowed_dir + os.sep)
|
|
101
|
+
or real_path == allowed_dir
|
|
102
|
+
):
|
|
101
103
|
log_debug(f"File path within boundaries: {file_path}")
|
|
102
104
|
return True
|
|
103
|
-
|
|
105
|
+
|
|
104
106
|
log_warning(f"File path outside boundaries: {file_path} -> {real_path}")
|
|
105
107
|
return False
|
|
106
|
-
|
|
108
|
+
|
|
107
109
|
except Exception as e:
|
|
108
110
|
log_warning(f"Boundary check error for {file_path}: {e}")
|
|
109
111
|
return False
|
|
110
112
|
|
|
111
|
-
def get_relative_path(self, file_path: str) ->
|
|
113
|
+
def get_relative_path(self, file_path: str) -> str | None:
|
|
112
114
|
"""
|
|
113
115
|
Get relative path from project root if within boundaries.
|
|
114
|
-
|
|
116
|
+
|
|
115
117
|
Args:
|
|
116
118
|
file_path: File path to convert
|
|
117
|
-
|
|
119
|
+
|
|
118
120
|
Returns:
|
|
119
121
|
Relative path from project root, or None if outside boundaries
|
|
120
122
|
"""
|
|
121
123
|
if not self.is_within_project(file_path):
|
|
122
124
|
return None
|
|
123
|
-
|
|
125
|
+
|
|
124
126
|
try:
|
|
125
127
|
real_path = os.path.realpath(file_path)
|
|
126
128
|
rel_path = os.path.relpath(real_path, self.project_root)
|
|
127
|
-
|
|
129
|
+
|
|
128
130
|
# Ensure relative path doesn't start with ..
|
|
129
131
|
if rel_path.startswith(".."):
|
|
130
132
|
log_warning(f"Relative path calculation failed: {rel_path}")
|
|
131
133
|
return None
|
|
132
|
-
|
|
134
|
+
|
|
133
135
|
return rel_path
|
|
134
|
-
|
|
136
|
+
|
|
135
137
|
except Exception as e:
|
|
136
138
|
log_warning(f"Relative path calculation error: {e}")
|
|
137
139
|
return None
|
|
138
140
|
|
|
139
|
-
def validate_and_resolve_path(self, file_path: str) ->
|
|
141
|
+
def validate_and_resolve_path(self, file_path: str) -> str | None:
|
|
140
142
|
"""
|
|
141
143
|
Validate path and return resolved absolute path if within boundaries.
|
|
142
|
-
|
|
144
|
+
|
|
143
145
|
Args:
|
|
144
146
|
file_path: File path to validate and resolve
|
|
145
|
-
|
|
147
|
+
|
|
146
148
|
Returns:
|
|
147
149
|
Resolved absolute path if valid, None otherwise
|
|
148
150
|
"""
|
|
@@ -152,22 +154,22 @@ class ProjectBoundaryManager:
|
|
|
152
154
|
full_path = os.path.join(self.project_root, file_path)
|
|
153
155
|
else:
|
|
154
156
|
full_path = file_path
|
|
155
|
-
|
|
157
|
+
|
|
156
158
|
# Check boundaries
|
|
157
159
|
if not self.is_within_project(full_path):
|
|
158
160
|
return None
|
|
159
|
-
|
|
161
|
+
|
|
160
162
|
# Return real path
|
|
161
163
|
return os.path.realpath(full_path)
|
|
162
|
-
|
|
164
|
+
|
|
163
165
|
except Exception as e:
|
|
164
166
|
log_warning(f"Path validation error: {e}")
|
|
165
167
|
return None
|
|
166
168
|
|
|
167
|
-
def list_allowed_directories(self) ->
|
|
169
|
+
def list_allowed_directories(self) -> set[str]:
|
|
168
170
|
"""
|
|
169
171
|
Get list of all allowed directories.
|
|
170
|
-
|
|
172
|
+
|
|
171
173
|
Returns:
|
|
172
174
|
Set of allowed directory paths
|
|
173
175
|
"""
|
|
@@ -176,33 +178,37 @@ class ProjectBoundaryManager:
|
|
|
176
178
|
def is_symlink_safe(self, file_path: str) -> bool:
|
|
177
179
|
"""
|
|
178
180
|
Check if file path is safe from symlink attacks.
|
|
179
|
-
|
|
181
|
+
|
|
180
182
|
Args:
|
|
181
183
|
file_path: File path to check
|
|
182
|
-
|
|
184
|
+
|
|
183
185
|
Returns:
|
|
184
186
|
True if path is safe from symlink attacks
|
|
185
187
|
"""
|
|
186
188
|
try:
|
|
187
189
|
if not os.path.exists(file_path):
|
|
188
190
|
return True # Non-existent files are safe
|
|
189
|
-
|
|
191
|
+
|
|
190
192
|
# Check if any component in the path is a symlink
|
|
191
193
|
path_parts = Path(file_path).parts
|
|
192
194
|
current_path = ""
|
|
193
|
-
|
|
195
|
+
|
|
194
196
|
for part in path_parts:
|
|
195
|
-
current_path =
|
|
196
|
-
|
|
197
|
+
current_path = (
|
|
198
|
+
os.path.join(current_path, part) if current_path else part
|
|
199
|
+
)
|
|
200
|
+
|
|
197
201
|
if os.path.islink(current_path):
|
|
198
202
|
# Check if symlink target is within boundaries
|
|
199
203
|
target = os.path.realpath(current_path)
|
|
200
204
|
if not self.is_within_project(target):
|
|
201
|
-
log_warning(
|
|
205
|
+
log_warning(
|
|
206
|
+
f"Unsafe symlink detected: {current_path} -> {target}"
|
|
207
|
+
)
|
|
202
208
|
return False
|
|
203
|
-
|
|
209
|
+
|
|
204
210
|
return True
|
|
205
|
-
|
|
211
|
+
|
|
206
212
|
except Exception as e:
|
|
207
213
|
log_warning(f"Symlink safety check error: {e}")
|
|
208
214
|
return False
|
|
@@ -210,16 +216,16 @@ class ProjectBoundaryManager:
|
|
|
210
216
|
def audit_access(self, file_path: str, operation: str) -> None:
|
|
211
217
|
"""
|
|
212
218
|
Log file access for security auditing.
|
|
213
|
-
|
|
219
|
+
|
|
214
220
|
Args:
|
|
215
221
|
file_path: File path being accessed
|
|
216
222
|
operation: Type of operation (read, write, analyze, etc.)
|
|
217
223
|
"""
|
|
218
224
|
is_within = self.is_within_project(file_path)
|
|
219
225
|
status = "ALLOWED" if is_within else "DENIED"
|
|
220
|
-
|
|
226
|
+
|
|
221
227
|
log_info(f"AUDIT: {status} {operation} access to {file_path}")
|
|
222
|
-
|
|
228
|
+
|
|
223
229
|
if not is_within:
|
|
224
230
|
log_warning(f"SECURITY: Unauthorized access attempt to {file_path}")
|
|
225
231
|
|
tree_sitter_analyzer/utils.py
CHANGED
|
@@ -14,7 +14,7 @@ from typing import Any
|
|
|
14
14
|
|
|
15
15
|
# Configure global logger
|
|
16
16
|
def setup_logger(
|
|
17
|
-
name: str = "tree_sitter_analyzer", level: int = logging.
|
|
17
|
+
name: str = "tree_sitter_analyzer", level: int = logging.WARNING
|
|
18
18
|
) -> logging.Logger:
|
|
19
19
|
"""Setup unified logger for the project"""
|
|
20
20
|
import os
|
|
@@ -227,7 +227,7 @@ def create_performance_logger(name: str) -> logging.Logger:
|
|
|
227
227
|
formatter = logging.Formatter("%(asctime)s - PERF - %(message)s")
|
|
228
228
|
handler.setFormatter(formatter)
|
|
229
229
|
perf_logger.addHandler(handler)
|
|
230
|
-
perf_logger.setLevel(logging.
|
|
230
|
+
perf_logger.setLevel(logging.DEBUG) # Change to DEBUG level
|
|
231
231
|
|
|
232
232
|
return perf_logger
|
|
233
233
|
|
|
@@ -252,7 +252,7 @@ def log_performance(
|
|
|
252
252
|
else:
|
|
253
253
|
detail_str = str(details)
|
|
254
254
|
message += f" - {detail_str}"
|
|
255
|
-
perf_logger.
|
|
255
|
+
perf_logger.debug(message) # Change to DEBUG level
|
|
256
256
|
except (ValueError, OSError):
|
|
257
257
|
pass # Silently ignore I/O errors
|
|
258
258
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tree-sitter-analyzer
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
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
|
|
@@ -58,30 +58,30 @@ Requires-Dist: isort>=5.13.0; extra == 'dev'
|
|
|
58
58
|
Requires-Dist: memory-profiler>=0.61.0; extra == 'dev'
|
|
59
59
|
Requires-Dist: mypy>=1.17.0; extra == 'dev'
|
|
60
60
|
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
|
|
61
|
-
Requires-Dist: psutil
|
|
61
|
+
Requires-Dist: psutil<6,>=5.9.6; extra == 'dev'
|
|
62
62
|
Requires-Dist: pytest-asyncio>=1.1.0; extra == 'dev'
|
|
63
63
|
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
64
64
|
Requires-Dist: pytest-mock>=3.14.1; extra == 'dev'
|
|
65
65
|
Requires-Dist: pytest>=8.4.1; extra == 'dev'
|
|
66
|
-
Requires-Dist: ruff>=0.
|
|
66
|
+
Requires-Dist: ruff>=0.5.0; extra == 'dev'
|
|
67
67
|
Requires-Dist: types-psutil>=5.9.0; extra == 'dev'
|
|
68
68
|
Provides-Extra: full
|
|
69
|
-
Requires-Dist: anyio>=4.
|
|
69
|
+
Requires-Dist: anyio>=4.0.0; extra == 'full'
|
|
70
70
|
Requires-Dist: black>=24.0.0; extra == 'full'
|
|
71
|
-
Requires-Dist: httpx
|
|
71
|
+
Requires-Dist: httpx<1.0.0,>=0.27.0; extra == 'full'
|
|
72
72
|
Requires-Dist: isort>=5.13.0; extra == 'full'
|
|
73
73
|
Requires-Dist: mcp>=1.12.2; extra == 'full'
|
|
74
74
|
Requires-Dist: memory-profiler>=0.61.0; extra == 'full'
|
|
75
75
|
Requires-Dist: mypy>=1.17.0; extra == 'full'
|
|
76
76
|
Requires-Dist: pre-commit>=3.0.0; extra == 'full'
|
|
77
|
-
Requires-Dist: psutil
|
|
78
|
-
Requires-Dist: pydantic-settings>=2.
|
|
79
|
-
Requires-Dist: pydantic>=2.
|
|
77
|
+
Requires-Dist: psutil<6,>=5.9.6; extra == 'full'
|
|
78
|
+
Requires-Dist: pydantic-settings>=2.2.1; extra == 'full'
|
|
79
|
+
Requires-Dist: pydantic>=2.5.0; extra == 'full'
|
|
80
80
|
Requires-Dist: pytest-asyncio>=1.1.0; extra == 'full'
|
|
81
81
|
Requires-Dist: pytest-cov>=4.0.0; extra == 'full'
|
|
82
82
|
Requires-Dist: pytest-mock>=3.14.1; extra == 'full'
|
|
83
83
|
Requires-Dist: pytest>=8.4.1; extra == 'full'
|
|
84
|
-
Requires-Dist: ruff>=0.
|
|
84
|
+
Requires-Dist: ruff>=0.5.0; extra == 'full'
|
|
85
85
|
Requires-Dist: tree-sitter-c>=0.20.0; extra == 'full'
|
|
86
86
|
Requires-Dist: tree-sitter-cpp>=0.23.4; extra == 'full'
|
|
87
87
|
Requires-Dist: tree-sitter-go>=0.20.0; extra == 'full'
|
|
@@ -98,11 +98,11 @@ Requires-Dist: tree-sitter-java>=0.23.5; extra == 'java'
|
|
|
98
98
|
Provides-Extra: javascript
|
|
99
99
|
Requires-Dist: tree-sitter-javascript>=0.23.1; extra == 'javascript'
|
|
100
100
|
Provides-Extra: mcp
|
|
101
|
-
Requires-Dist: anyio>=4.
|
|
102
|
-
Requires-Dist: httpx
|
|
101
|
+
Requires-Dist: anyio>=4.0.0; extra == 'mcp'
|
|
102
|
+
Requires-Dist: httpx<1.0.0,>=0.27.0; extra == 'mcp'
|
|
103
103
|
Requires-Dist: mcp>=1.12.2; extra == 'mcp'
|
|
104
|
-
Requires-Dist: pydantic-settings>=2.
|
|
105
|
-
Requires-Dist: pydantic>=2.
|
|
104
|
+
Requires-Dist: pydantic-settings>=2.2.1; extra == 'mcp'
|
|
105
|
+
Requires-Dist: pydantic>=2.5.0; extra == 'mcp'
|
|
106
106
|
Provides-Extra: popular
|
|
107
107
|
Requires-Dist: tree-sitter-java>=0.23.5; extra == 'popular'
|
|
108
108
|
Requires-Dist: tree-sitter-javascript>=0.23.1; extra == 'popular'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
tree_sitter_analyzer/__init__.py,sha256=
|
|
1
|
+
tree_sitter_analyzer/__init__.py,sha256=ElX-1qL0OI5EuUf2XVkdqvLuWwRgTVYDGDnyeuLUBww,3067
|
|
2
2
|
tree_sitter_analyzer/__main__.py,sha256=Zl79tpe4UaMu-7yeztc06tgP0CVMRnvGgas4ZQP5SCs,228
|
|
3
3
|
tree_sitter_analyzer/api.py,sha256=naRtGuZ27AIVfn6Rid0zQcHDI71UpO9Nh4NQM9JyD3c,16954
|
|
4
|
-
tree_sitter_analyzer/cli_main.py,sha256=
|
|
4
|
+
tree_sitter_analyzer/cli_main.py,sha256=n491hpKudODmaRIYAMBg_hFmrXSbSFaee5rsPfcEifs,9951
|
|
5
5
|
tree_sitter_analyzer/encoding_utils.py,sha256=ItN5dKfuGY9xqvUVQIRYjOrb3fZ_8_CEMQMQ31sScvc,14479
|
|
6
6
|
tree_sitter_analyzer/exceptions.py,sha256=xO_U6JuJ4QPkmZoXL_3nmV9QUbTa7-hrI05VAuo5r-Y,12093
|
|
7
7
|
tree_sitter_analyzer/file_handler.py,sha256=nUD17QIdOJ2bnXekuo-QzGQFv0f2rxCSJi-zWeQFSAs,6636
|
|
@@ -9,16 +9,16 @@ tree_sitter_analyzer/language_detector.py,sha256=_Hliij5sA2bp_Hp_pFDl-kcxZltyvkh
|
|
|
9
9
|
tree_sitter_analyzer/language_loader.py,sha256=gdLxkSoajm-q7c1vcvFONtBf5XJRgasUVI4L0wMzra0,8124
|
|
10
10
|
tree_sitter_analyzer/models.py,sha256=YXqlz0cGnDzkMhLYfepZJm-5mHpPO2pTNWgOhBqHUX0,16484
|
|
11
11
|
tree_sitter_analyzer/output_manager.py,sha256=saZ8Ss6PhUJgjjwviLgrePFL7CCLMixxdKtdrpuFgHM,8146
|
|
12
|
-
tree_sitter_analyzer/project_detector.py,sha256=
|
|
12
|
+
tree_sitter_analyzer/project_detector.py,sha256=VdZTnY25M2fJTA3MLUzPIjXJtOCPATKlkshEF3eUV7U,9444
|
|
13
13
|
tree_sitter_analyzer/query_loader.py,sha256=jcJc6_kIMeZINfTVGuiEmDii9LViP_pbJfg4A9phJY4,9863
|
|
14
14
|
tree_sitter_analyzer/table_formatter.py,sha256=BfrAouAr3r6MD9xY9yhHw_PwD0aJ4BQo5p1UFhorT5k,27284
|
|
15
|
-
tree_sitter_analyzer/utils.py,sha256=
|
|
15
|
+
tree_sitter_analyzer/utils.py,sha256=4qKFu5O7VZIy9410FdJhLGvNdEVS3hdFDL80kIu01GA,8752
|
|
16
16
|
tree_sitter_analyzer/cli/__init__.py,sha256=O_3URpbdu5Ilb2-r48LjbZuWtOWQu_BhL3pa6C0G3Bk,871
|
|
17
17
|
tree_sitter_analyzer/cli/__main__.py,sha256=Xq8o8-0dPnMDU9WZqmqhzr98rx8rvoffTUHAkAwl-L8,218
|
|
18
18
|
tree_sitter_analyzer/cli/info_commands.py,sha256=0x_6mfMq7jpKBLT9jzhTikXcs0n4TzNEV2Te9dyKNd4,4405
|
|
19
19
|
tree_sitter_analyzer/cli/commands/__init__.py,sha256=jpcpM1ptLuxLMBDUv1y_a87k8RAw1otFzeYpWtXvz3Y,671
|
|
20
20
|
tree_sitter_analyzer/cli/commands/advanced_command.py,sha256=xDZI4zKTMHNdf7fc_QN0eAQ8a5MnRb5DJ29ERLBDUQs,3424
|
|
21
|
-
tree_sitter_analyzer/cli/commands/base_command.py,sha256=
|
|
21
|
+
tree_sitter_analyzer/cli/commands/base_command.py,sha256=o_xjKhn2J5bbGeNC980tALozT1WlsttI0owhLbV2hDM,6597
|
|
22
22
|
tree_sitter_analyzer/cli/commands/default_command.py,sha256=R9_GuI5KVYPK2DfXRuG8L89vwxv0QVW8sur_sigjZKo,542
|
|
23
23
|
tree_sitter_analyzer/cli/commands/partial_read_command.py,sha256=kD3E2f1zCseSKpGQ3bgHnEuCq-DCPRQrT91JJJh8B4Q,4776
|
|
24
24
|
tree_sitter_analyzer/cli/commands/query_command.py,sha256=TNkmuUKaTmTYD80jc8eesYLpw59YVk-6nw478SsYWH8,3640
|
|
@@ -26,8 +26,8 @@ tree_sitter_analyzer/cli/commands/structure_command.py,sha256=0iJwjOgtW838hXleXo
|
|
|
26
26
|
tree_sitter_analyzer/cli/commands/summary_command.py,sha256=02WA3sOzfT83FVT6sW7nK04zVcZ9Qj_1S0WloqlTnFk,3602
|
|
27
27
|
tree_sitter_analyzer/cli/commands/table_command.py,sha256=BAIw26WRi_yXbKvkuV7tXFKzSiWvYKVzRUxAcgsJ7VQ,9676
|
|
28
28
|
tree_sitter_analyzer/core/__init__.py,sha256=VlYOy1epW16vjaVd__knESewnU0sfXF9a4hjrFxiSEE,440
|
|
29
|
-
tree_sitter_analyzer/core/analysis_engine.py,sha256=
|
|
30
|
-
tree_sitter_analyzer/core/cache_service.py,sha256=
|
|
29
|
+
tree_sitter_analyzer/core/analysis_engine.py,sha256=32XO5UXD7PeK86ZBTCi7eVcaNwU2HmkdZGZjc1XZlu0,19481
|
|
30
|
+
tree_sitter_analyzer/core/cache_service.py,sha256=4Pe57kxoBaBD-KsKINJX9jP3IEPyBW3apm4hz1DYw0c,9625
|
|
31
31
|
tree_sitter_analyzer/core/engine.py,sha256=VFXGowDj6EfjFSh2MQDkQIc-4ISXaOg38n4lUhVY5Os,18721
|
|
32
32
|
tree_sitter_analyzer/core/parser.py,sha256=qT3yIlTRdod4tf_2o1hU_B-GYGukyM2BtaFxzSoxois,9293
|
|
33
33
|
tree_sitter_analyzer/core/query.py,sha256=fmuPMLsU4XUnMViaQADPRPoiZ-MzeE2k_e9N35VBNSA,16899
|
|
@@ -37,8 +37,8 @@ tree_sitter_analyzer/formatters/formatter_factory.py,sha256=mCnAbEHycoSttSuF4dU7
|
|
|
37
37
|
tree_sitter_analyzer/formatters/java_formatter.py,sha256=RXW-DkNoHwkze7E9UoywQOKTiA9lNbfJ9euLQjLflcQ,11454
|
|
38
38
|
tree_sitter_analyzer/formatters/python_formatter.py,sha256=UziBqRB6yCsAY-TlLBcmuLlOLgwpGWbVU08mSsBtxTM,10099
|
|
39
39
|
tree_sitter_analyzer/interfaces/__init__.py,sha256=OcT7eNIU0ZXvAeAXbhDqRG3puxn93HeSLqplwj6npTM,271
|
|
40
|
-
tree_sitter_analyzer/interfaces/cli.py,sha256=
|
|
41
|
-
tree_sitter_analyzer/interfaces/cli_adapter.py,sha256=
|
|
40
|
+
tree_sitter_analyzer/interfaces/cli.py,sha256=XYqkCbRyf_ygA1_dlCuvyFSZKWvEBjpu7C-9Ssc__Mw,16840
|
|
41
|
+
tree_sitter_analyzer/interfaces/cli_adapter.py,sha256=LAR5GDwirJSnNB5rP5aVxqcra6diFUwzBzyLU7RUrrQ,10770
|
|
42
42
|
tree_sitter_analyzer/interfaces/mcp_adapter.py,sha256=DJugCRVL-AR6gJRaRrBW5JVXRvfl_iiRjupcnsb0_sE,7111
|
|
43
43
|
tree_sitter_analyzer/interfaces/mcp_server.py,sha256=-vOxiDEJqPaMbRiPr5n0fGtIXOkTM-GxkxKovE2tsB4,16934
|
|
44
44
|
tree_sitter_analyzer/languages/__init__.py,sha256=VTXxJgVjHJAciLhX0zzXOS4EygZMtebeYUbi_0z6fGw,340
|
|
@@ -68,10 +68,10 @@ tree_sitter_analyzer/queries/javascript.py,sha256=pnXrgISwDE5GhPHDbUKEGI3thyLmeb
|
|
|
68
68
|
tree_sitter_analyzer/queries/python.py,sha256=L33KRUyV3sAvA3_HFkPyGgtiq0ygSpNY_n2YojodPlc,7570
|
|
69
69
|
tree_sitter_analyzer/queries/typescript.py,sha256=eersyAF7TladuCWa8WE_-cO9YTF1LUSjLIl-tk2fZDo,6708
|
|
70
70
|
tree_sitter_analyzer/security/__init__.py,sha256=AlBGtSpDqVxlfM4K7JD-dJsDE8cPcuzJvN7OOsNOhm8,646
|
|
71
|
-
tree_sitter_analyzer/security/boundary_manager.py,sha256=
|
|
71
|
+
tree_sitter_analyzer/security/boundary_manager.py,sha256=MiSdg8PliKHGLqJqlMhW6KxnXDSoAko_-YRbSp0tYBU,7994
|
|
72
72
|
tree_sitter_analyzer/security/regex_checker.py,sha256=glKsja9zbrP_kOkaXxPkzkkUjLZpO1EBCi3LS3fobmQ,10317
|
|
73
73
|
tree_sitter_analyzer/security/validator.py,sha256=UPAPcrnmI2mNzbYOm0MabnJMGllK6HlOQ9KX-2bRfgU,8986
|
|
74
|
-
tree_sitter_analyzer-0.9.
|
|
75
|
-
tree_sitter_analyzer-0.9.
|
|
76
|
-
tree_sitter_analyzer-0.9.
|
|
77
|
-
tree_sitter_analyzer-0.9.
|
|
74
|
+
tree_sitter_analyzer-0.9.3.dist-info/METADATA,sha256=T-m2pGpKHwATsIj6RIPWLjdCBBysVTEK8kogkBoHTW8,15909
|
|
75
|
+
tree_sitter_analyzer-0.9.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
76
|
+
tree_sitter_analyzer-0.9.3.dist-info/entry_points.txt,sha256=U4tfLGXgCWubKm2PyEb3zxhQ2pm7zVotMyfyS0CodD8,486
|
|
77
|
+
tree_sitter_analyzer-0.9.3.dist-info/RECORD,,
|
|
File without changes
|
{tree_sitter_analyzer-0.9.2.dist-info → tree_sitter_analyzer-0.9.3.dist-info}/entry_points.txt
RENAMED
|
File without changes
|