tree-sitter-analyzer 1.8.3__py3-none-any.whl → 1.9.0__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 -54
  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 +83 -25
  60. {tree_sitter_analyzer-1.8.3.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/METADATA +19 -5
  61. tree_sitter_analyzer-1.9.0.dist-info/RECORD +109 -0
  62. tree_sitter_analyzer-1.8.3.dist-info/RECORD +0 -109
  63. {tree_sitter_analyzer-1.8.3.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/WHEEL +0 -0
  64. {tree_sitter_analyzer-1.8.3.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/entry_points.txt +0 -0
@@ -403,7 +403,7 @@ class RegexSecurityError(SecurityError):
403
403
  # MCP-specific exceptions for enhanced error handling
404
404
  class MCPToolError(MCPError):
405
405
  """Raised when MCP tool execution fails."""
406
-
406
+
407
407
  def __init__(
408
408
  self,
409
409
  message: str,
@@ -419,17 +419,17 @@ class MCPToolError(MCPError):
419
419
  context["input_params"] = sanitized_params
420
420
  if execution_stage:
421
421
  context["execution_stage"] = execution_stage
422
-
422
+
423
423
  super().__init__(message, tool_name=tool_name, context=context, **kwargs)
424
424
  self.input_params = input_params
425
425
  self.execution_stage = execution_stage
426
-
426
+
427
427
  @staticmethod
428
428
  def _sanitize_params(params: dict[str, Any]) -> dict[str, Any]:
429
429
  """Sanitize sensitive information from parameters."""
430
430
  sanitized = {}
431
431
  sensitive_keys = {"password", "token", "key", "secret", "auth", "credential"}
432
-
432
+
433
433
  for key, value in params.items():
434
434
  if any(sensitive in key.lower() for sensitive in sensitive_keys):
435
435
  sanitized[key] = "***REDACTED***"
@@ -437,13 +437,13 @@ class MCPToolError(MCPError):
437
437
  sanitized[key] = value[:100] + "...[TRUNCATED]"
438
438
  else:
439
439
  sanitized[key] = value
440
-
440
+
441
441
  return sanitized
442
442
 
443
443
 
444
444
  class MCPResourceError(MCPError):
445
445
  """Raised when MCP resource access fails."""
446
-
446
+
447
447
  def __init__(
448
448
  self,
449
449
  message: str,
@@ -457,7 +457,7 @@ class MCPResourceError(MCPError):
457
457
  context["resource_type"] = resource_type
458
458
  if access_mode:
459
459
  context["access_mode"] = access_mode
460
-
460
+
461
461
  super().__init__(message, resource_uri=resource_uri, context=context, **kwargs)
462
462
  self.resource_type = resource_type
463
463
  self.access_mode = access_mode
@@ -465,7 +465,7 @@ class MCPResourceError(MCPError):
465
465
 
466
466
  class MCPTimeoutError(MCPError):
467
467
  """Raised when MCP operation times out."""
468
-
468
+
469
469
  def __init__(
470
470
  self,
471
471
  message: str,
@@ -478,7 +478,7 @@ class MCPTimeoutError(MCPError):
478
478
  context["timeout_seconds"] = timeout_seconds
479
479
  if operation_type:
480
480
  context["operation_type"] = operation_type
481
-
481
+
482
482
  super().__init__(message, context=context, **kwargs)
483
483
  self.timeout_seconds = timeout_seconds
484
484
  self.operation_type = operation_type
@@ -486,7 +486,7 @@ class MCPTimeoutError(MCPError):
486
486
 
487
487
  class MCPValidationError(ValidationError):
488
488
  """Raised when MCP input validation fails."""
489
-
489
+
490
490
  def __init__(
491
491
  self,
492
492
  message: str,
@@ -503,15 +503,17 @@ class MCPValidationError(ValidationError):
503
503
  context["parameter_name"] = parameter_name
504
504
  if validation_rule:
505
505
  context["validation_rule"] = validation_rule
506
-
506
+
507
507
  # Sanitize parameter value for logging
508
508
  if parameter_value is not None:
509
509
  if isinstance(parameter_value, str) and len(parameter_value) > 200:
510
510
  context["parameter_value"] = parameter_value[:200] + "...[TRUNCATED]"
511
511
  else:
512
512
  context["parameter_value"] = parameter_value
513
-
514
- super().__init__(message, validation_type="mcp_parameter", context=context, **kwargs)
513
+
514
+ super().__init__(
515
+ message, validation_type="mcp_parameter", context=context, **kwargs
516
+ )
515
517
  self.tool_name = tool_name
516
518
  self.parameter_name = parameter_name
517
519
  self.validation_rule = validation_rule
@@ -519,7 +521,7 @@ class MCPValidationError(ValidationError):
519
521
 
520
522
  class FileRestrictionError(SecurityError):
521
523
  """Raised when file access is restricted by mode or security policy."""
522
-
524
+
523
525
  def __init__(
524
526
  self,
525
527
  message: str,
@@ -533,13 +535,13 @@ class FileRestrictionError(SecurityError):
533
535
  context["current_mode"] = current_mode
534
536
  if allowed_patterns:
535
537
  context["allowed_patterns"] = allowed_patterns
536
-
538
+
537
539
  super().__init__(
538
540
  message,
539
541
  security_type="file_restriction",
540
542
  file_path=file_path,
541
543
  context=context,
542
- **kwargs
544
+ **kwargs,
543
545
  )
544
546
  self.current_mode = current_mode
545
547
  self.allowed_patterns = allowed_patterns
@@ -554,52 +556,52 @@ def create_mcp_error_response(
554
556
  ) -> dict[str, Any]:
555
557
  """
556
558
  Create standardized MCP error response dictionary.
557
-
559
+
558
560
  Args:
559
561
  exception: The exception to convert
560
562
  tool_name: Name of the MCP tool that failed
561
563
  include_debug_info: Whether to include debug information
562
564
  sanitize_sensitive: Whether to sanitize sensitive information
563
-
565
+
564
566
  Returns:
565
567
  MCP-compliant error response dictionary
566
568
  """
567
569
  import traceback
568
-
570
+
569
571
  response: dict[str, Any] = {
570
572
  "success": False,
571
573
  "error": {
572
574
  "type": exception.__class__.__name__,
573
575
  "message": str(exception),
574
- "timestamp": __import__("datetime").datetime.utcnow().isoformat() + "Z"
575
- }
576
+ "timestamp": __import__("datetime").datetime.utcnow().isoformat() + "Z",
577
+ },
576
578
  }
577
-
579
+
578
580
  # Add tool name if provided
579
581
  if tool_name:
580
582
  response["error"]["tool"] = tool_name
581
-
583
+
582
584
  # Add context if available
583
585
  if hasattr(exception, "context") and exception.context:
584
586
  context = exception.context.copy()
585
-
587
+
586
588
  # Sanitize sensitive information if requested
587
589
  if sanitize_sensitive:
588
590
  context = _sanitize_error_context(context)
589
-
591
+
590
592
  response["error"]["context"] = context
591
-
593
+
592
594
  # Add error code if available
593
595
  if hasattr(exception, "error_code"):
594
596
  response["error"]["code"] = exception.error_code
595
-
597
+
596
598
  # Add debug information if requested
597
599
  if include_debug_info:
598
600
  response["error"]["debug"] = {
599
601
  "traceback": traceback.format_exc(),
600
- "exception_args": list(exception.args) if exception.args else []
602
+ "exception_args": list(exception.args) if exception.args else [],
601
603
  }
602
-
604
+
603
605
  # Add specific error details for known exception types
604
606
  if isinstance(exception, MCPToolError):
605
607
  response["error"]["execution_stage"] = exception.execution_stage
@@ -608,7 +610,7 @@ def create_mcp_error_response(
608
610
  elif isinstance(exception, FileRestrictionError):
609
611
  response["error"]["current_mode"] = exception.current_mode
610
612
  response["error"]["allowed_patterns"] = exception.allowed_patterns
611
-
613
+
612
614
  return response
613
615
 
614
616
 
@@ -616,10 +618,18 @@ def _sanitize_error_context(context: dict[str, Any]) -> dict[str, Any]:
616
618
  """Sanitize sensitive information from error context."""
617
619
  sanitized = {}
618
620
  sensitive_keys = {
619
- "password", "token", "key", "secret", "auth", "credential",
620
- "api_key", "access_token", "private_key", "session_id"
621
+ "password",
622
+ "token",
623
+ "key",
624
+ "secret",
625
+ "auth",
626
+ "credential",
627
+ "api_key",
628
+ "access_token",
629
+ "private_key",
630
+ "session_id",
621
631
  }
622
-
632
+
623
633
  for key, value in context.items():
624
634
  if any(sensitive in key.lower() for sensitive in sensitive_keys):
625
635
  sanitized[key] = "***REDACTED***"
@@ -634,7 +644,7 @@ def _sanitize_error_context(context: dict[str, Any]) -> dict[str, Any]:
634
644
  sanitized[key]["__truncated__"] = True
635
645
  else:
636
646
  sanitized[key] = value
637
-
647
+
638
648
  return sanitized
639
649
 
640
650
 
@@ -648,14 +658,14 @@ async def safe_execute_async(
648
658
  ) -> Any:
649
659
  """
650
660
  Safely execute an async function with exception handling.
651
-
661
+
652
662
  Args:
653
663
  coro: Coroutine to execute
654
664
  default_return: Value to return on exception
655
665
  exception_types: Exception types to catch
656
666
  log_errors: Whether to log errors
657
667
  tool_name: Name of the tool for error context
658
-
668
+
659
669
  Returns:
660
670
  Coroutine result or default_return on exception
661
671
  """
@@ -664,10 +674,10 @@ async def safe_execute_async(
664
674
  except exception_types as e:
665
675
  if log_errors:
666
676
  from .utils import log_error
667
-
677
+
668
678
  error_context = {"tool_name": tool_name} if tool_name else {}
669
679
  log_error(f"Async execution failed: {e}", extra=error_context)
670
-
680
+
671
681
  return default_return
672
682
 
673
683
 
@@ -678,57 +688,58 @@ def mcp_exception_handler(
678
688
  ) -> Any:
679
689
  """
680
690
  Decorator for MCP tool exception handling.
681
-
691
+
682
692
  Args:
683
693
  tool_name: Name of the MCP tool
684
694
  include_debug: Whether to include debug information
685
695
  sanitize_sensitive: Whether to sanitize sensitive information
686
696
  """
697
+
687
698
  def decorator(func: Any) -> Any:
688
699
  async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
689
700
  try:
690
701
  return await func(*args, **kwargs)
691
702
  except Exception as e:
692
703
  from .utils import log_error
693
-
704
+
694
705
  # Log the error with tool context
695
706
  log_error(
696
707
  f"MCP tool '{tool_name}' failed: {e}",
697
- extra={"tool_name": tool_name, "exception_type": type(e).__name__}
708
+ extra={"tool_name": tool_name, "exception_type": type(e).__name__},
698
709
  )
699
-
710
+
700
711
  # Return standardized error response
701
712
  return create_mcp_error_response(
702
713
  e,
703
714
  tool_name=tool_name,
704
715
  include_debug_info=include_debug,
705
- sanitize_sensitive=sanitize_sensitive
716
+ sanitize_sensitive=sanitize_sensitive,
706
717
  )
707
-
718
+
708
719
  def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
709
720
  try:
710
721
  return func(*args, **kwargs)
711
722
  except Exception as e:
712
723
  from .utils import log_error
713
-
724
+
714
725
  # Log the error with tool context
715
726
  log_error(
716
727
  f"MCP tool '{tool_name}' failed: {e}",
717
- extra={"tool_name": tool_name, "exception_type": type(e).__name__}
728
+ extra={"tool_name": tool_name, "exception_type": type(e).__name__},
718
729
  )
719
-
730
+
720
731
  # Return standardized error response
721
732
  return create_mcp_error_response(
722
733
  e,
723
734
  tool_name=tool_name,
724
735
  include_debug_info=include_debug,
725
- sanitize_sensitive=sanitize_sensitive
736
+ sanitize_sensitive=sanitize_sensitive,
726
737
  )
727
-
738
+
728
739
  # Return appropriate wrapper based on function type
729
740
  if __import__("asyncio").iscoroutinefunction(func):
730
741
  return async_wrapper
731
742
  else:
732
743
  return sync_wrapper
733
-
744
+
734
745
  return decorator
@@ -13,14 +13,17 @@ from .utils import setup_logger
13
13
  # Set up logger for this module
14
14
  logger = setup_logger(__name__)
15
15
 
16
+
16
17
  def log_error(message: str, *args, **kwargs) -> None:
17
18
  """Log error message"""
18
19
  logger.error(message, *args, **kwargs)
19
20
 
21
+
20
22
  def log_info(message: str, *args, **kwargs) -> None:
21
23
  """Log info message"""
22
24
  logger.info(message, *args, **kwargs)
23
25
 
26
+
24
27
  def log_warning(message: str, *args, **kwargs) -> None:
25
28
  """Log warning message"""
26
29
  logger.warning(message, *args, **kwargs)
@@ -6,32 +6,37 @@ Base formatter for language-specific formatting.
6
6
  import csv
7
7
  import io
8
8
  from abc import ABC, abstractmethod
9
- from typing import Any, Dict
9
+ from typing import Any
10
10
 
11
11
 
12
12
  class BaseFormatter(ABC):
13
13
  """Base class for language-specific formatters"""
14
14
 
15
+ @abstractmethod
15
16
  def __init__(self):
16
17
  pass
17
18
 
18
19
  @abstractmethod
19
- def format_summary(self, analysis_result: Dict[str, Any]) -> str:
20
+ def format_summary(self, analysis_result: dict[str, Any]) -> str:
20
21
  """Format summary output"""
21
22
  pass
22
23
 
23
24
  @abstractmethod
24
- def format_structure(self, analysis_result: Dict[str, Any]) -> str:
25
+ def format_structure(self, analysis_result: dict[str, Any]) -> str:
25
26
  """Format structure analysis output"""
26
27
  pass
27
28
 
28
29
  @abstractmethod
29
- def format_advanced(self, analysis_result: Dict[str, Any], output_format: str = "json") -> str:
30
+ def format_advanced(
31
+ self, analysis_result: dict[str, Any], output_format: str = "json"
32
+ ) -> str:
30
33
  """Format advanced analysis output"""
31
34
  pass
32
35
 
33
36
  @abstractmethod
34
- def format_table(self, analysis_result: Dict[str, Any], table_type: str = "full") -> str:
37
+ def format_table(
38
+ self, analysis_result: dict[str, Any], table_type: str = "full"
39
+ ) -> str:
35
40
  """Format table output"""
36
41
  pass
37
42