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.

@@ -11,7 +11,7 @@ Architecture:
11
11
  - Data Models: Generic and language-specific code element representations
12
12
  """
13
13
 
14
- __version__ = "0.9.2"
14
+ __version__ = "0.9.3"
15
15
  __author__ = "aisheng.yu"
16
16
  __email__ = "aimasteracc@gmail.com"
17
17
 
@@ -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
- output_info(
89
- f"INFO: Language auto-detected from extension: {target_language}"
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):
@@ -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, Dict, Optional, Protocol
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: Dict[str, "UnifiedAnalysisEngine"] = {}
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
- log_info(f"UnifiedAnalysisEngine initialized with project root: {project_root}")
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
- log_info("Loading plugins using PluginManager...")
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
- log_info(
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
- log_info(f"Starting analysis for {request.file_path}")
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(request.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(f"Security validation failed for file path: {request.file_path} - {error_msg}")
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(f"Security validation failed for file path: {file_path} - {error_msg}")
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(
@@ -109,7 +109,7 @@ class CacheService:
109
109
  # デフォルト設定
110
110
  self._default_ttl = ttl_seconds
111
111
 
112
- log_info(
112
+ log_debug(
113
113
  f"CacheService initialized: L1={l1_maxsize}, L2={l2_maxsize}, "
114
114
  f"L3={l3_maxsize}, TTL={ttl_seconds}s"
115
115
  )
@@ -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.0.1"
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.info("CLIAdapter initialized successfully")
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.info(f"CLI analysis completed: {file_path} in {elapsed_time:.3f}s")
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.info("CLI adapter cache cleared")
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 os
9
- from pathlib import Path
10
- from typing import Optional, List, Tuple
11
- import logging
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
- # Common project root indicators (in priority order)
16
- PROJECT_MARKERS = [
17
- # Version control
18
- '.git',
19
- '.hg',
20
- '.svn',
21
-
22
- # Python projects
23
- 'pyproject.toml',
24
- 'setup.py',
25
- 'setup.cfg',
26
- 'requirements.txt',
27
- 'Pipfile',
28
- 'poetry.lock',
29
- 'conda.yaml',
30
- 'environment.yml',
31
-
32
- # JavaScript/Node.js projects
33
- 'package.json',
34
- 'package-lock.json',
35
- 'yarn.lock',
36
- 'node_modules',
37
-
38
- # Java projects
39
- 'pom.xml',
40
- 'build.gradle',
41
- 'build.gradle.kts',
42
- 'gradlew',
43
- 'mvnw',
44
-
45
- # C/C++ projects
46
- 'CMakeLists.txt',
47
- 'Makefile',
48
- 'configure.ac',
49
- 'configure.in',
50
-
51
- # Rust projects
52
- 'Cargo.toml',
53
- 'Cargo.lock',
54
-
55
- # Go projects
56
- 'go.mod',
57
- 'go.sum',
58
-
59
- # .NET projects
60
- '*.sln',
61
- '*.csproj',
62
- '*.vbproj',
63
- '*.fsproj',
64
-
65
- # Other common markers
66
- 'README.md',
67
- 'README.rst',
68
- 'README.txt',
69
- 'LICENSE',
70
- 'CHANGELOG.md',
71
- '.gitignore',
72
- '.dockerignore',
73
- 'Dockerfile',
74
- 'docker-compose.yml',
75
- '.editorconfig',
76
- ]
77
-
78
-
79
- class ProjectRootDetector:
80
- """Intelligent project root directory detection."""
81
-
82
- def __init__(self, max_depth: int = 10):
83
- """
84
- Initialize project root detector.
85
-
86
- Args:
87
- max_depth: Maximum directory levels to traverse upward
88
- """
89
- self.max_depth = max_depth
90
-
91
- def detect_from_file(self, file_path: str) -> Optional[str]:
92
- """
93
- Detect project root from a file path.
94
-
95
- Args:
96
- file_path: Path to a file within the project
97
-
98
- Returns:
99
- Project root directory path, or None if not detected
100
- """
101
- if not file_path:
102
- return None
103
-
104
- try:
105
- # Convert to absolute path and get directory
106
- abs_path = os.path.abspath(file_path)
107
- if os.path.isfile(abs_path):
108
- start_dir = os.path.dirname(abs_path)
109
- else:
110
- start_dir = abs_path
111
-
112
- return self._traverse_upward(start_dir)
113
-
114
- except Exception as e:
115
- logger.warning(f"Error detecting project root from {file_path}: {e}")
116
- return None
117
-
118
- def detect_from_cwd(self) -> Optional[str]:
119
- """
120
- Detect project root from current working directory.
121
-
122
- Returns:
123
- Project root directory path, or None if not detected
124
- """
125
- try:
126
- return self._traverse_upward(os.getcwd())
127
- except Exception as e:
128
- logger.warning(f"Error detecting project root from cwd: {e}")
129
- return None
130
-
131
- def _traverse_upward(self, start_dir: str) -> Optional[str]:
132
- """
133
- Traverse upward from start directory looking for project markers.
134
-
135
- Args:
136
- start_dir: Directory to start traversal from
137
-
138
- Returns:
139
- Project root directory path, or None if not found
140
- """
141
- current_dir = os.path.abspath(start_dir)
142
- candidates = []
143
-
144
- for depth in range(self.max_depth):
145
- # Check for project markers in current directory
146
- markers_found = self._find_markers_in_dir(current_dir)
147
-
148
- if markers_found:
149
- # Calculate score based on marker priority and count
150
- score = self._calculate_score(markers_found)
151
- candidates.append((current_dir, score, markers_found))
152
-
153
- # If we find high-priority markers, we can stop early
154
- if any(marker in ['.git', 'pyproject.toml', 'package.json', 'pom.xml', 'Cargo.toml', 'go.mod']
155
- for marker in markers_found):
156
- logger.debug(f"Found high-priority project root: {current_dir} (markers: {markers_found})")
157
- return current_dir
158
-
159
- # Move up one directory
160
- parent_dir = os.path.dirname(current_dir)
161
- if parent_dir == current_dir: # Reached filesystem root
162
- break
163
- current_dir = parent_dir
164
-
165
- # Return the best candidate if any found
166
- if candidates:
167
- # Sort by score (descending) and return the best
168
- candidates.sort(key=lambda x: x[1], reverse=True)
169
- best_candidate = candidates[0]
170
- logger.debug(f"Selected project root: {best_candidate[0]} (score: {best_candidate[1]}, markers: {best_candidate[2]})")
171
- return best_candidate[0]
172
-
173
- logger.debug(f"No project root detected from {start_dir}")
174
- return None
175
-
176
- def _find_markers_in_dir(self, directory: str) -> List[str]:
177
- """
178
- Find project markers in a directory.
179
-
180
- Args:
181
- directory: Directory to search in
182
-
183
- Returns:
184
- List of found marker names
185
- """
186
- found_markers = []
187
-
188
- try:
189
- dir_contents = os.listdir(directory)
190
-
191
- for marker in PROJECT_MARKERS:
192
- if '*' in marker:
193
- # Handle glob patterns
194
- import glob
195
- pattern = os.path.join(directory, marker)
196
- if glob.glob(pattern):
197
- found_markers.append(marker)
198
- else:
199
- # Handle exact matches
200
- if marker in dir_contents:
201
- found_markers.append(marker)
202
-
203
- except (OSError, PermissionError) as e:
204
- logger.debug(f"Cannot access directory {directory}: {e}")
205
-
206
- return found_markers
207
-
208
- def _calculate_score(self, markers: List[str]) -> int:
209
- """
210
- Calculate a score for project root candidates based on markers found.
211
-
212
- Args:
213
- markers: List of found markers
214
-
215
- Returns:
216
- Score (higher is better)
217
- """
218
- score = 0
219
-
220
- # High-priority markers
221
- high_priority = ['.git', 'pyproject.toml', 'package.json', 'pom.xml', 'Cargo.toml', 'go.mod']
222
- medium_priority = ['setup.py', 'requirements.txt', 'CMakeLists.txt', 'Makefile']
223
-
224
- for marker in markers:
225
- if marker in high_priority:
226
- score += 100
227
- elif marker in medium_priority:
228
- score += 50
229
- else:
230
- score += 10
231
-
232
- # Bonus for multiple markers
233
- if len(markers) > 1:
234
- score += len(markers) * 5
235
-
236
- return score
237
-
238
- def get_fallback_root(self, file_path: str) -> str:
239
- """
240
- Get fallback project root when detection fails.
241
-
242
- Args:
243
- file_path: Original file path
244
-
245
- Returns:
246
- Fallback directory (file's directory or cwd)
247
- """
248
- try:
249
- if file_path and os.path.exists(file_path):
250
- if os.path.isfile(file_path):
251
- return os.path.dirname(os.path.abspath(file_path))
252
- else:
253
- return os.path.abspath(file_path)
254
- else:
255
- return os.getcwd()
256
- except Exception:
257
- return os.getcwd()
258
-
259
-
260
- def detect_project_root(file_path: Optional[str] = None,
261
- explicit_root: Optional[str] = None) -> str:
262
- """
263
- Unified project root detection with priority handling.
264
-
265
- Priority order:
266
- 1. explicit_root parameter (highest priority)
267
- 2. Auto-detection from file_path
268
- 3. Auto-detection from current working directory
269
- 4. Fallback to file directory or cwd
270
-
271
- Args:
272
- file_path: Path to a file within the project
273
- explicit_root: Explicitly specified project root
274
-
275
- Returns:
276
- Project root directory path
277
- """
278
- detector = ProjectRootDetector()
279
-
280
- # Priority 1: Explicit root
281
- if explicit_root:
282
- if os.path.exists(explicit_root) and os.path.isdir(explicit_root):
283
- logger.info(f"Using explicit project root: {explicit_root}")
284
- return os.path.abspath(explicit_root)
285
- else:
286
- logger.warning(f"Explicit project root does not exist: {explicit_root}")
287
-
288
- # Priority 2: Auto-detection from file path
289
- if file_path:
290
- detected_root = detector.detect_from_file(file_path)
291
- if detected_root:
292
- logger.info(f"Auto-detected project root from file: {detected_root}")
293
- return detected_root
294
-
295
- # Priority 3: Auto-detection from cwd
296
- detected_root = detector.detect_from_cwd()
297
- if detected_root:
298
- logger.info(f"Auto-detected project root from cwd: {detected_root}")
299
- return detected_root
300
-
301
- # Priority 4: Fallback
302
- fallback_root = detector.get_fallback_root(file_path)
303
- logger.info(f"Using fallback project root: {fallback_root}")
304
- return fallback_root
305
-
306
-
307
- if __name__ == "__main__":
308
- # Test the detector
309
- import sys
310
-
311
- if len(sys.argv) > 1:
312
- test_path = sys.argv[1]
313
- result = detect_project_root(test_path)
314
- print(f"Project root for '{test_path}': {result}")
315
- else:
316
- result = detect_project_root()
317
- print(f"Project root from cwd: {result}")
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: Set[str] = {self.project_root}
53
-
54
- log_info(f"ProjectBoundaryManager initialized with root: {self.project_root}")
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 real_path.startswith(allowed_dir + os.sep) or real_path == allowed_dir:
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) -> Optional[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) -> Optional[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) -> Set[str]:
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 = os.path.join(current_path, part) if current_path else part
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(f"Unsafe symlink detected: {current_path} -> {target}")
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
 
@@ -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.INFO
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.INFO)
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.info(message)
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.2
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>=7.0.0; extra == 'dev'
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.1.0; extra == 'dev'
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.9.0; extra == 'full'
69
+ Requires-Dist: anyio>=4.0.0; extra == 'full'
70
70
  Requires-Dist: black>=24.0.0; extra == 'full'
71
- Requires-Dist: httpx>=0.28.1; extra == 'full'
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>=7.0.0; extra == 'full'
78
- Requires-Dist: pydantic-settings>=2.10.1; extra == 'full'
79
- Requires-Dist: pydantic>=2.11.7; extra == 'full'
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.1.0; extra == 'full'
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.9.0; extra == 'mcp'
102
- Requires-Dist: httpx>=0.28.1; extra == 'mcp'
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.10.1; extra == 'mcp'
105
- Requires-Dist: pydantic>=2.11.7; extra == 'mcp'
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=LZoQ9dIbFkG5E3PMUvSpYmzNSwNbjzTf4nCyBbv2aGE,3067
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=ses68m5tLoYMP6Co3Fk2vqBACuFd38MqF85uEoa0mbw,9714
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=FWk9bgnonxhCBRBGST-iSSxI6zHC3Ea-VTnP8iM_S10,9882
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=o9Yw0x9s5UJ-9g2tgR9u8kUL_b8DNF6yil7McgD-BBw,8697
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=7dNOZ8kPvw4ihH7mgbfZDrtknRytE5AVqr6ZozRk848,6644
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=rXd79d5XXA4vJCDApC3cLkhytoiOKmCw_4TpYreNvuM,19371
30
- tree_sitter_analyzer/core/cache_service.py,sha256=TQrOI9xwI-0KRF8c6-cXwXM1Ut3AhLOXa4GpyQsIHW4,9624
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=EsRcy0Wrt5BzR8RxBaXToH29XZoahR-fSLwGaMyf2LE,16840
41
- tree_sitter_analyzer/interfaces/cli_adapter.py,sha256=NXiMPa0409jiE0keVuYnmsYRe5isZE9PrkrAYmh0BlU,10767
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=jj7iHiZiMxC590aiw7oUlTjztGxY8WqauG8Rwu8n6Ig,8288
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.2.dist-info/METADATA,sha256=g8yx19WVWTJHVG-O-LzEwfqCDLjNH5UrPFLF2cgDTis,15893
75
- tree_sitter_analyzer-0.9.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
76
- tree_sitter_analyzer-0.9.2.dist-info/entry_points.txt,sha256=U4tfLGXgCWubKm2PyEb3zxhQ2pm7zVotMyfyS0CodD8,486
77
- tree_sitter_analyzer-0.9.2.dist-info/RECORD,,
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,,