tree-sitter-analyzer 1.8.4__py3-none-any.whl → 1.9.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.

Potentially problematic release.


This version of tree-sitter-analyzer might be problematic. Click here for more details.

Files changed (64) hide show
  1. tree_sitter_analyzer/__init__.py +1 -1
  2. tree_sitter_analyzer/api.py +4 -4
  3. tree_sitter_analyzer/cli/argument_validator.py +29 -17
  4. tree_sitter_analyzer/cli/commands/advanced_command.py +7 -5
  5. tree_sitter_analyzer/cli/commands/structure_command.py +7 -5
  6. tree_sitter_analyzer/cli/commands/summary_command.py +10 -6
  7. tree_sitter_analyzer/cli/commands/table_command.py +8 -7
  8. tree_sitter_analyzer/cli/info_commands.py +1 -1
  9. tree_sitter_analyzer/cli_main.py +3 -2
  10. tree_sitter_analyzer/core/analysis_engine.py +5 -5
  11. tree_sitter_analyzer/core/cache_service.py +3 -1
  12. tree_sitter_analyzer/core/query.py +17 -5
  13. tree_sitter_analyzer/core/query_service.py +1 -1
  14. tree_sitter_analyzer/encoding_utils.py +3 -3
  15. tree_sitter_analyzer/exceptions.py +61 -50
  16. tree_sitter_analyzer/file_handler.py +3 -0
  17. tree_sitter_analyzer/formatters/base_formatter.py +10 -5
  18. tree_sitter_analyzer/formatters/formatter_registry.py +83 -68
  19. tree_sitter_analyzer/formatters/html_formatter.py +90 -64
  20. tree_sitter_analyzer/formatters/javascript_formatter.py +21 -16
  21. tree_sitter_analyzer/formatters/language_formatter_factory.py +7 -6
  22. tree_sitter_analyzer/formatters/markdown_formatter.py +247 -124
  23. tree_sitter_analyzer/formatters/python_formatter.py +61 -38
  24. tree_sitter_analyzer/formatters/typescript_formatter.py +113 -45
  25. tree_sitter_analyzer/interfaces/mcp_server.py +2 -2
  26. tree_sitter_analyzer/language_detector.py +6 -6
  27. tree_sitter_analyzer/language_loader.py +3 -1
  28. tree_sitter_analyzer/languages/css_plugin.py +120 -61
  29. tree_sitter_analyzer/languages/html_plugin.py +159 -62
  30. tree_sitter_analyzer/languages/java_plugin.py +42 -34
  31. tree_sitter_analyzer/languages/javascript_plugin.py +59 -30
  32. tree_sitter_analyzer/languages/markdown_plugin.py +402 -368
  33. tree_sitter_analyzer/languages/python_plugin.py +111 -64
  34. tree_sitter_analyzer/languages/typescript_plugin.py +241 -132
  35. tree_sitter_analyzer/mcp/server.py +22 -18
  36. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +13 -8
  37. tree_sitter_analyzer/mcp/tools/base_tool.py +2 -2
  38. tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +232 -26
  39. tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +31 -23
  40. tree_sitter_analyzer/mcp/tools/list_files_tool.py +21 -19
  41. tree_sitter_analyzer/mcp/tools/query_tool.py +17 -18
  42. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +30 -31
  43. tree_sitter_analyzer/mcp/tools/search_content_tool.py +131 -77
  44. tree_sitter_analyzer/mcp/tools/table_format_tool.py +29 -16
  45. tree_sitter_analyzer/mcp/utils/file_output_factory.py +64 -51
  46. tree_sitter_analyzer/mcp/utils/file_output_manager.py +34 -24
  47. tree_sitter_analyzer/mcp/utils/gitignore_detector.py +8 -4
  48. tree_sitter_analyzer/models.py +7 -5
  49. tree_sitter_analyzer/plugins/base.py +9 -7
  50. tree_sitter_analyzer/plugins/manager.py +1 -0
  51. tree_sitter_analyzer/queries/css.py +2 -21
  52. tree_sitter_analyzer/queries/html.py +2 -15
  53. tree_sitter_analyzer/queries/markdown.py +30 -41
  54. tree_sitter_analyzer/queries/python.py +20 -5
  55. tree_sitter_analyzer/query_loader.py +5 -5
  56. tree_sitter_analyzer/security/validator.py +114 -86
  57. tree_sitter_analyzer/utils/__init__.py +58 -28
  58. tree_sitter_analyzer/utils/tree_sitter_compat.py +72 -65
  59. tree_sitter_analyzer/utils.py +26 -15
  60. {tree_sitter_analyzer-1.8.4.dist-info → tree_sitter_analyzer-1.9.1.dist-info}/METADATA +23 -6
  61. tree_sitter_analyzer-1.9.1.dist-info/RECORD +109 -0
  62. tree_sitter_analyzer-1.8.4.dist-info/RECORD +0 -109
  63. {tree_sitter_analyzer-1.8.4.dist-info → tree_sitter_analyzer-1.9.1.dist-info}/WHEEL +0 -0
  64. {tree_sitter_analyzer-1.8.4.dist-info → tree_sitter_analyzer-1.9.1.dist-info}/entry_points.txt +0 -0
@@ -9,7 +9,6 @@ across MCP tools.
9
9
 
10
10
  import threading
11
11
  from pathlib import Path
12
- from typing import Dict, Optional
13
12
 
14
13
  from ...utils import setup_logger
15
14
  from .file_output_manager import FileOutputManager
@@ -24,95 +23,105 @@ class FileOutputManagerFactory:
24
23
  pattern. Each project root gets its own singleton instance, ensuring consistency
25
24
  across MCP tools while preventing duplicate initialization.
26
25
  """
27
-
26
+
28
27
  # Class-level lock for thread safety
29
28
  _lock = threading.RLock()
30
-
29
+
31
30
  # Dictionary to store instances by project root
32
- _instances: Dict[str, FileOutputManager] = {}
33
-
31
+ _instances: dict[str, FileOutputManager] = {}
32
+
34
33
  @classmethod
35
- def get_instance(cls, project_root: Optional[str] = None) -> FileOutputManager:
34
+ def get_instance(cls, project_root: str | None = None) -> FileOutputManager:
36
35
  """
37
36
  Get or create a FileOutputManager instance for the specified project root.
38
-
37
+
39
38
  This method implements the Managed Singleton pattern - one instance per
40
39
  project root, ensuring consistency across all MCP tools.
41
-
40
+
42
41
  Args:
43
42
  project_root: Project root directory. If None, uses current working directory.
44
-
43
+
45
44
  Returns:
46
45
  FileOutputManager instance for the specified project root
47
46
  """
48
47
  # Normalize project root path
49
48
  normalized_root = cls._normalize_project_root(project_root)
50
-
49
+
51
50
  # Double-checked locking pattern for thread safety
52
51
  if normalized_root not in cls._instances:
53
52
  with cls._lock:
54
53
  if normalized_root not in cls._instances:
55
- logger.info(f"Creating new FileOutputManager instance for project root: {normalized_root}")
54
+ logger.info(
55
+ f"Creating new FileOutputManager instance for project root: {normalized_root}"
56
+ )
56
57
  cls._instances[normalized_root] = FileOutputManager(normalized_root)
57
58
  else:
58
- logger.debug(f"Using existing FileOutputManager instance for project root: {normalized_root}")
59
+ logger.debug(
60
+ f"Using existing FileOutputManager instance for project root: {normalized_root}"
61
+ )
59
62
  else:
60
- logger.debug(f"Using existing FileOutputManager instance for project root: {normalized_root}")
61
-
63
+ logger.debug(
64
+ f"Using existing FileOutputManager instance for project root: {normalized_root}"
65
+ )
66
+
62
67
  return cls._instances[normalized_root]
63
-
68
+
64
69
  @classmethod
65
- def _normalize_project_root(cls, project_root: Optional[str]) -> str:
70
+ def _normalize_project_root(cls, project_root: str | None) -> str:
66
71
  """
67
72
  Normalize project root path for consistent key generation.
68
-
73
+
69
74
  Args:
70
75
  project_root: Raw project root path
71
-
76
+
72
77
  Returns:
73
78
  Normalized absolute path string
74
79
  """
75
80
  if project_root is None:
76
81
  return str(Path.cwd().resolve())
77
-
82
+
78
83
  try:
79
84
  return str(Path(project_root).resolve())
80
85
  except Exception as e:
81
86
  logger.warning(f"Failed to resolve project root path '{project_root}': {e}")
82
87
  return str(Path.cwd().resolve())
83
-
88
+
84
89
  @classmethod
85
- def clear_instance(cls, project_root: Optional[str] = None) -> bool:
90
+ def clear_instance(cls, project_root: str | None = None) -> bool:
86
91
  """
87
92
  Clear a specific FileOutputManager instance from the factory.
88
-
93
+
89
94
  This method is primarily for testing purposes or when you need to
90
95
  force recreation of an instance.
91
-
96
+
92
97
  Args:
93
98
  project_root: Project root directory. If None, uses current working directory.
94
-
99
+
95
100
  Returns:
96
101
  True if instance was cleared, False if it didn't exist
97
102
  """
98
103
  normalized_root = cls._normalize_project_root(project_root)
99
-
104
+
100
105
  with cls._lock:
101
106
  if normalized_root in cls._instances:
102
- logger.info(f"Clearing FileOutputManager instance for project root: {normalized_root}")
107
+ logger.info(
108
+ f"Clearing FileOutputManager instance for project root: {normalized_root}"
109
+ )
103
110
  del cls._instances[normalized_root]
104
111
  return True
105
112
  else:
106
- logger.debug(f"No FileOutputManager instance found for project root: {normalized_root}")
113
+ logger.debug(
114
+ f"No FileOutputManager instance found for project root: {normalized_root}"
115
+ )
107
116
  return False
108
-
117
+
109
118
  @classmethod
110
119
  def clear_all_instances(cls) -> int:
111
120
  """
112
121
  Clear all FileOutputManager instances from the factory.
113
-
122
+
114
123
  This method is primarily for testing purposes or cleanup.
115
-
124
+
116
125
  Returns:
117
126
  Number of instances that were cleared
118
127
  """
@@ -124,81 +133,85 @@ class FileOutputManagerFactory:
124
133
  else:
125
134
  logger.debug("No FileOutputManager instances to clear")
126
135
  return count
127
-
136
+
128
137
  @classmethod
129
138
  def get_instance_count(cls) -> int:
130
139
  """
131
140
  Get the current number of managed instances.
132
-
141
+
133
142
  Returns:
134
143
  Number of currently managed FileOutputManager instances
135
144
  """
136
145
  with cls._lock:
137
146
  return len(cls._instances)
138
-
147
+
139
148
  @classmethod
140
149
  def get_managed_project_roots(cls) -> list[str]:
141
150
  """
142
151
  Get list of all currently managed project roots.
143
-
152
+
144
153
  Returns:
145
154
  List of project root paths that have managed instances
146
155
  """
147
156
  with cls._lock:
148
157
  return list(cls._instances.keys())
149
-
158
+
150
159
  @classmethod
151
- def update_project_root(cls, old_root: Optional[str], new_root: str) -> bool:
160
+ def update_project_root(cls, old_root: str | None, new_root: str) -> bool:
152
161
  """
153
162
  Update the project root for an existing instance.
154
-
163
+
155
164
  This method moves an existing instance from one project root key to another,
156
165
  and updates the instance's internal project root.
157
-
166
+
158
167
  Args:
159
168
  old_root: Current project root (None for current working directory)
160
169
  new_root: New project root
161
-
170
+
162
171
  Returns:
163
172
  True if update was successful, False if old instance didn't exist
164
173
  """
165
174
  old_normalized = cls._normalize_project_root(old_root)
166
175
  new_normalized = cls._normalize_project_root(new_root)
167
-
176
+
168
177
  if old_normalized == new_normalized:
169
178
  logger.debug(f"Project root update not needed: {old_normalized}")
170
179
  return True
171
-
180
+
172
181
  with cls._lock:
173
182
  if old_normalized in cls._instances:
174
183
  instance = cls._instances[old_normalized]
175
-
184
+
176
185
  # Update the instance's internal project root
177
186
  instance.set_project_root(new_root)
178
-
187
+
179
188
  # Move to new key
180
189
  cls._instances[new_normalized] = instance
181
190
  del cls._instances[old_normalized]
182
-
183
- logger.info(f"Updated FileOutputManager project root: {old_normalized} -> {new_normalized}")
191
+
192
+ logger.info(
193
+ f"Updated FileOutputManager project root: {old_normalized} -> {new_normalized}"
194
+ )
184
195
  return True
185
196
  else:
186
- logger.warning(f"No FileOutputManager instance found for old project root: {old_normalized}")
197
+ logger.warning(
198
+ f"No FileOutputManager instance found for old project root: {old_normalized}"
199
+ )
187
200
  return False
188
201
 
189
202
 
190
203
  # Convenience function for backward compatibility and ease of use
191
- def get_file_output_manager(project_root: Optional[str] = None) -> FileOutputManager:
204
+ def get_file_output_manager(project_root: str | None = None) -> FileOutputManager:
192
205
  """
193
206
  Convenience function to get a FileOutputManager instance.
194
-
207
+
195
208
  This function provides a simple interface to the factory while maintaining
196
209
  the singleton behavior per project root.
197
-
210
+
198
211
  Args:
199
212
  project_root: Project root directory. If None, uses current working directory.
200
-
213
+
201
214
  Returns:
202
215
  FileOutputManager instance for the specified project root
203
216
  """
204
- return FileOutputManagerFactory.get_instance(project_root)
217
+ return FileOutputManagerFactory.get_instance(project_root)
@@ -12,7 +12,6 @@ instance management across MCP tools.
12
12
  import json
13
13
  import os
14
14
  from pathlib import Path
15
- from typing import Any, Optional
16
15
 
17
16
  from ...utils import setup_logger
18
17
 
@@ -24,12 +23,12 @@ class FileOutputManager:
24
23
  """
25
24
  Manages file output for analysis results with automatic extension detection
26
25
  and security validation.
27
-
26
+
28
27
  Enhanced with factory method support for consistent instance management
29
28
  across MCP tools while maintaining full backward compatibility.
30
29
  """
31
30
 
32
- def __init__(self, project_root: Optional[str] = None):
31
+ def __init__(self, project_root: str | None = None):
33
32
  """
34
33
  Initialize the file output manager.
35
34
 
@@ -39,22 +38,24 @@ class FileOutputManager:
39
38
  self.project_root = project_root
40
39
  self._output_path = None
41
40
  self._initialize_output_path()
42
-
41
+
43
42
  @classmethod
44
- def get_managed_instance(cls, project_root: Optional[str] = None) -> 'FileOutputManager':
43
+ def get_managed_instance(
44
+ cls, project_root: str | None = None
45
+ ) -> "FileOutputManager":
45
46
  """
46
47
  Get a managed FileOutputManager instance using the factory pattern.
47
-
48
+
48
49
  This method provides access to the Managed Singleton Factory Pattern,
49
50
  ensuring one instance per project root for optimal resource usage
50
51
  and consistency across MCP tools.
51
-
52
+
52
53
  Args:
53
54
  project_root: Project root directory. If None, uses current working directory.
54
-
55
+
55
56
  Returns:
56
57
  FileOutputManager instance managed by the factory
57
-
58
+
58
59
  Note:
59
60
  This method requires the factory module to be available. If the factory
60
61
  is not available, it falls back to creating a new instance directly.
@@ -62,23 +63,26 @@ class FileOutputManager:
62
63
  try:
63
64
  # Import here to avoid circular imports
64
65
  from .file_output_factory import FileOutputManagerFactory
66
+
65
67
  return FileOutputManagerFactory.get_instance(project_root)
66
68
  except ImportError as e:
67
- logger.warning(f"Factory not available, creating new instance directly: {e}")
69
+ logger.warning(
70
+ f"Factory not available, creating new instance directly: {e}"
71
+ )
68
72
  return cls(project_root)
69
-
73
+
70
74
  @classmethod
71
- def create_instance(cls, project_root: Optional[str] = None) -> 'FileOutputManager':
75
+ def create_instance(cls, project_root: str | None = None) -> "FileOutputManager":
72
76
  """
73
77
  Create a new FileOutputManager instance directly (bypass factory).
74
-
78
+
75
79
  This method creates a new instance without using the factory pattern.
76
80
  Use this when you specifically need a separate instance that won't
77
81
  be managed by the factory.
78
-
82
+
79
83
  Args:
80
84
  project_root: Project root directory. If None, uses current working directory.
81
-
85
+
82
86
  Returns:
83
87
  New FileOutputManager instance
84
88
  """
@@ -159,14 +163,18 @@ class FileOutputManager:
159
163
  if first_line_commas > 0:
160
164
  # Check if at least 2 more lines have similar comma counts
161
165
  similar_comma_lines = sum(
162
- 1 for line in lines[1:4] if abs(line.count(",") - first_line_commas) <= 1
166
+ 1
167
+ for line in lines[1:4]
168
+ if abs(line.count(",") - first_line_commas) <= 1
163
169
  )
164
170
  if similar_comma_lines >= 1:
165
171
  return "csv"
166
172
 
167
173
  # Check for Markdown (simple heuristic)
168
174
  markdown_indicators = ["#", "##", "###", "|", "```", "*", "-", "+"]
169
- if any(content_stripped.startswith(indicator) for indicator in markdown_indicators):
175
+ if any(
176
+ content_stripped.startswith(indicator) for indicator in markdown_indicators
177
+ ):
170
178
  return "markdown"
171
179
 
172
180
  # Check for table format (pipe-separated)
@@ -193,7 +201,7 @@ class FileOutputManager:
193
201
  "json": ".json",
194
202
  "csv": ".csv",
195
203
  "markdown": ".md",
196
- "text": ".txt"
204
+ "text": ".txt",
197
205
  }
198
206
  return extension_map.get(content_type, ".txt")
199
207
 
@@ -210,13 +218,15 @@ class FileOutputManager:
210
218
  """
211
219
  content_type = self.detect_content_type(content)
212
220
  extension = self.get_file_extension(content_type)
213
-
221
+
214
222
  # Remove existing extension if present
215
223
  base_name_clean = Path(base_name).stem
216
-
224
+
217
225
  return f"{base_name_clean}{extension}"
218
226
 
219
- def save_to_file(self, content: str, filename: str | None = None, base_name: str | None = None) -> str:
227
+ def save_to_file(
228
+ self, content: str, filename: str | None = None, base_name: str | None = None
229
+ ) -> str:
220
230
  """
221
231
  Save content to file with automatic extension detection.
222
232
 
@@ -252,7 +262,7 @@ class FileOutputManager:
252
262
  try:
253
263
  with open(output_file, "w", encoding="utf-8") as f:
254
264
  f.write(content)
255
-
265
+
256
266
  logger.info(f"Content saved to file: {output_file}")
257
267
  return str(output_file)
258
268
 
@@ -272,7 +282,7 @@ class FileOutputManager:
272
282
  """
273
283
  try:
274
284
  path_obj = Path(path).resolve()
275
-
285
+
276
286
  # Check if parent directory exists or can be created
277
287
  parent_dir = path_obj.parent
278
288
  if not parent_dir.exists():
@@ -304,4 +314,4 @@ class FileOutputManager:
304
314
  self.project_root = project_root
305
315
  # Only reinitialize if we don't have an explicit output path from environment
306
316
  if not os.environ.get("TREE_SITTER_OUTPUT_PATH"):
307
- self._initialize_output_path()
317
+ self._initialize_output_path()
@@ -207,9 +207,11 @@ class GitignoreDetector:
207
207
  try:
208
208
  # Check if paths exist before resolving
209
209
  if not search_dir.exists() or not pattern_dir.exists():
210
- logger.debug(f"Path does not exist: {search_dir} or {pattern_dir}, assuming affected")
210
+ logger.debug(
211
+ f"Path does not exist: {search_dir} or {pattern_dir}, assuming affected"
212
+ )
211
213
  return True
212
-
214
+
213
215
  # If search_dir is the same as pattern_dir or is a subdirectory of pattern_dir
214
216
  search_resolved = search_dir.resolve()
215
217
  pattern_resolved = pattern_dir.resolve()
@@ -220,7 +222,9 @@ class GitignoreDetector:
220
222
  ).startswith(str(pattern_resolved) + os.sep)
221
223
  except (OSError, ValueError, RuntimeError):
222
224
  # If path resolution fails, assume it could be affected
223
- logger.debug(f"Path resolution failed for {search_dir} or {pattern_dir}, assuming affected")
225
+ logger.debug(
226
+ f"Path resolution failed for {search_dir} or {pattern_dir}, assuming affected"
227
+ )
224
228
  return True
225
229
 
226
230
  def _directory_has_searchable_files(self, directory: Path) -> bool:
@@ -280,7 +284,7 @@ class GitignoreDetector:
280
284
  # Check if project path exists
281
285
  if not project_path.exists():
282
286
  raise FileNotFoundError(f"Project root does not exist: {project_root}")
283
-
287
+
284
288
  gitignore_files = self._find_gitignore_files(project_path)
285
289
  info["detected_gitignore_files"] = [str(f) for f in gitignore_files]
286
290
 
@@ -161,14 +161,14 @@ class MarkupElement(CodeElement):
161
161
  HTML要素を表現するデータモデル。
162
162
  CodeElementを継承し、マークアップ固有の属性を追加する。
163
163
  """
164
-
164
+
165
165
  tag_name: str = ""
166
166
  attributes: dict[str, str] = field(default_factory=dict)
167
167
  parent: "MarkupElement | None" = None
168
168
  children: list["MarkupElement"] = field(default_factory=list)
169
169
  element_class: str = "" # 分類システムのカテゴリ (例: 'structure', 'media', 'form')
170
170
  element_type: str = "html_element"
171
-
171
+
172
172
  def to_summary_item(self) -> dict[str, Any]:
173
173
  """Return dictionary for summary item"""
174
174
  return {
@@ -186,12 +186,14 @@ class StyleElement(CodeElement):
186
186
  CSSルールを表現するデータモデル。
187
187
  CodeElementを継承する。
188
188
  """
189
-
189
+
190
190
  selector: str = ""
191
191
  properties: dict[str, str] = field(default_factory=dict)
192
- element_class: str = "" # 分類システムのカテゴリ (例: 'layout', 'typography', 'color')
192
+ element_class: str = (
193
+ "" # 分類システムのカテゴリ (例: 'layout', 'typography', 'color')
194
+ )
193
195
  element_type: str = "css_rule"
194
-
196
+
195
197
  def to_summary_item(self) -> dict[str, Any]:
196
198
  """Return dictionary for summary item"""
197
199
  return {
@@ -181,7 +181,7 @@ class LanguagePlugin(ABC):
181
181
  def get_supported_element_types(self) -> list[str]:
182
182
  """
183
183
  Return list of supported CodeElement types.
184
-
184
+
185
185
  Returns:
186
186
  List of element types (e.g., ["function", "class", "variable"])
187
187
  """
@@ -190,20 +190,22 @@ class LanguagePlugin(ABC):
190
190
  def get_queries(self) -> dict[str, str]:
191
191
  """
192
192
  Return language-specific tree-sitter queries.
193
-
193
+
194
194
  Returns:
195
195
  Dictionary mapping query names to query strings
196
196
  """
197
197
  return {}
198
198
 
199
- def execute_query_strategy(self, query_key: str | None, language: str) -> str | None:
199
+ def execute_query_strategy(
200
+ self, query_key: str | None, language: str
201
+ ) -> str | None:
200
202
  """
201
203
  Execute query strategy for this language plugin.
202
-
204
+
203
205
  Args:
204
206
  query_key: Query key to execute
205
207
  language: Programming language
206
-
208
+
207
209
  Returns:
208
210
  Query string or None if not supported
209
211
  """
@@ -213,7 +215,7 @@ class LanguagePlugin(ABC):
213
215
  def get_formatter_map(self) -> dict[str, str]:
214
216
  """
215
217
  Return mapping of format types to formatter class names.
216
-
218
+
217
219
  Returns:
218
220
  Dictionary mapping format names to formatter classes
219
221
  """
@@ -222,7 +224,7 @@ class LanguagePlugin(ABC):
222
224
  def get_element_categories(self) -> dict[str, list[str]]:
223
225
  """
224
226
  Return element categories for HTML/CSS languages.
225
-
227
+
226
228
  Returns:
227
229
  Dictionary mapping category names to element lists
228
230
  """
@@ -66,6 +66,7 @@ class PluginManager:
66
66
  final_plugins = list(unique_plugins.values())
67
67
  # Only log if not in CLI mode (check if we're in quiet mode)
68
68
  import os
69
+
69
70
  log_level = os.environ.get("LOG_LEVEL", "WARNING")
70
71
  if log_level != "ERROR":
71
72
  log_info(f"Successfully loaded {len(final_plugins)} plugins")
@@ -32,7 +32,6 @@ CSS_QUERIES: dict[str, str] = {
32
32
  (declaration
33
33
  value: (_) @property_value)
34
34
  """,
35
-
36
35
  # --- Selectors ---
37
36
  "selector": """
38
37
  (selectors
@@ -74,7 +73,6 @@ CSS_QUERIES: dict[str, str] = {
74
73
  "adjacent_sibling_selector": """
75
74
  (adjacent_sibling_selector) @adjacent_sibling_selector
76
75
  """,
77
-
78
76
  # --- At-Rules ---
79
77
  "at_rule": """
80
78
  (at_rule) @at_rule
@@ -103,7 +101,6 @@ CSS_QUERIES: dict[str, str] = {
103
101
  "font_face_statement": """
104
102
  (font_face_statement) @font_face_statement
105
103
  """,
106
-
107
104
  # --- Media Queries ---
108
105
  "media_query": """
109
106
  (media_query) @media_query
@@ -114,7 +111,6 @@ CSS_QUERIES: dict[str, str] = {
114
111
  "media_type": """
115
112
  (media_type) @media_type
116
113
  """,
117
-
118
114
  # --- Values ---
119
115
  "string_value": """
120
116
  (string_value) @string_value
@@ -181,7 +177,6 @@ CSS_QUERIES: dict[str, str] = {
181
177
  (#match? @func_name "^hsla$")
182
178
  arguments: (arguments) @hsla_args) @hsla
183
179
  """,
184
-
185
180
  # --- Units ---
186
181
  "dimension": """
187
182
  (dimension) @dimension
@@ -193,7 +188,6 @@ CSS_QUERIES: dict[str, str] = {
193
188
  (dimension
194
189
  unit: (unit) @unit)
195
190
  """,
196
-
197
191
  # --- Layout Properties ---
198
192
  "display": """
199
193
  (declaration
@@ -237,7 +231,6 @@ CSS_QUERIES: dict[str, str] = {
237
231
  (#match? @prop_name "^z-index$")
238
232
  value: (_) @z_index_value) @z_index
239
233
  """,
240
-
241
234
  # --- Box Model Properties ---
242
235
  "width": """
243
236
  (declaration
@@ -275,7 +268,6 @@ CSS_QUERIES: dict[str, str] = {
275
268
  (#match? @prop_name "^box-sizing$")
276
269
  value: (_) @box_sizing_value) @box_sizing
277
270
  """,
278
-
279
271
  # --- Typography Properties ---
280
272
  "font": """
281
273
  (declaration
@@ -313,7 +305,6 @@ CSS_QUERIES: dict[str, str] = {
313
305
  (#match? @prop_name "^word-spacing$")
314
306
  value: (_) @word_spacing_value) @word_spacing
315
307
  """,
316
-
317
308
  # --- Background Properties ---
318
309
  "background": """
319
310
  (declaration
@@ -321,7 +312,6 @@ CSS_QUERIES: dict[str, str] = {
321
312
  (#match? @prop_name "^background")
322
313
  value: (_) @background_value) @background
323
314
  """,
324
-
325
315
  # --- Flexbox Properties ---
326
316
  "flex": """
327
317
  (declaration
@@ -347,7 +337,6 @@ CSS_QUERIES: dict[str, str] = {
347
337
  (#match? @prop_name "^align-content$")
348
338
  value: (_) @align_content_value) @align_content
349
339
  """,
350
-
351
340
  # --- Grid Properties ---
352
341
  "grid": """
353
342
  (declaration
@@ -355,7 +344,6 @@ CSS_QUERIES: dict[str, str] = {
355
344
  (#match? @prop_name "^grid")
356
345
  value: (_) @grid_value) @grid
357
346
  """,
358
-
359
347
  # --- Animation Properties ---
360
348
  "animation": """
361
349
  (declaration
@@ -375,12 +363,10 @@ CSS_QUERIES: dict[str, str] = {
375
363
  (#match? @prop_name "^transform")
376
364
  value: (_) @transform_value) @transform
377
365
  """,
378
-
379
366
  # --- Comments ---
380
367
  "comment": """
381
368
  (comment) @comment
382
369
  """,
383
-
384
370
  # --- Custom Properties (CSS Variables) ---
385
371
  "custom_property": """
386
372
  (declaration
@@ -388,7 +374,6 @@ CSS_QUERIES: dict[str, str] = {
388
374
  (#match? @prop_name "^--")
389
375
  value: (_) @custom_value) @custom_property
390
376
  """,
391
-
392
377
  # --- Important Declarations ---
393
378
  "important": """
394
379
  (declaration
@@ -396,7 +381,6 @@ CSS_QUERIES: dict[str, str] = {
396
381
  "!" @important_mark
397
382
  "important" @important_keyword) @important
398
383
  """,
399
-
400
384
  # --- Keyframe Rules ---
401
385
  "keyframe_block": """
402
386
  (keyframe_block) @keyframe_block
@@ -410,7 +394,6 @@ CSS_QUERIES: dict[str, str] = {
410
394
  "to": """
411
395
  (to) @to
412
396
  """,
413
-
414
397
  # --- Name-only Extraction ---
415
398
  "class_name": """
416
399
  (class_selector
@@ -585,9 +568,7 @@ def get_css_query(name: str) -> str:
585
568
  """
586
569
  if name not in CSS_QUERIES:
587
570
  available = list(CSS_QUERIES.keys())
588
- raise ValueError(
589
- f"CSS query '{name}' does not exist. Available: {available}"
590
- )
571
+ raise ValueError(f"CSS query '{name}' does not exist. Available: {available}")
591
572
 
592
573
  return CSS_QUERIES[name]
593
574
 
@@ -631,4 +612,4 @@ def get_available_css_queries() -> list[str]:
631
612
  Returns:
632
613
  List of query names
633
614
  """
634
- return list(CSS_QUERIES.keys())
615
+ return list(CSS_QUERIES.keys())