crackerjack 0.18.2__py3-none-any.whl → 0.45.2__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.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +96 -2
- crackerjack/__main__.py +637 -138
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +39 -0
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/lsp/_base.py +194 -0
- crackerjack/adapters/lsp/_client.py +358 -0
- crackerjack/adapters/lsp/_manager.py +193 -0
- crackerjack/adapters/lsp/skylos.py +283 -0
- crackerjack/adapters/lsp/zuban.py +557 -0
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +66 -0
- crackerjack/agents/architect_agent.py +238 -0
- crackerjack/agents/base.py +167 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +600 -0
- crackerjack/agents/documentation_agent.py +520 -0
- crackerjack/agents/dry_agent.py +585 -0
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +230 -0
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/import_optimization_agent.py +1181 -0
- crackerjack/agents/performance_agent.py +325 -0
- crackerjack/agents/performance_helpers.py +205 -0
- crackerjack/agents/proactive_agent.py +55 -0
- crackerjack/agents/refactoring_agent.py +511 -0
- crackerjack/agents/refactoring_helpers.py +247 -0
- crackerjack/agents/security_agent.py +793 -0
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +570 -0
- crackerjack/agents/test_specialist_agent.py +526 -0
- crackerjack/agents/tracker.py +110 -0
- crackerjack/api.py +647 -0
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/cache_handlers.py +209 -0
- crackerjack/cli/cache_handlers_enhanced.py +680 -0
- crackerjack/cli/facade.py +162 -0
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +700 -0
- crackerjack/cli/interactive.py +488 -0
- crackerjack/cli/options.py +1216 -0
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/utils.py +19 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +1307 -0
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +275 -0
- crackerjack/config/global_lock_config.py +207 -0
- crackerjack/config/hooks.py +390 -0
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +738 -0
- crackerjack/core/autofix_coordinator.py +282 -0
- crackerjack/core/container.py +105 -0
- crackerjack/core/enhanced_container.py +583 -0
- crackerjack/core/file_lifecycle.py +472 -0
- crackerjack/core/performance.py +244 -0
- crackerjack/core/performance_monitor.py +357 -0
- crackerjack/core/phase_coordinator.py +1227 -0
- crackerjack/core/proactive_workflow.py +267 -0
- crackerjack/core/resource_manager.py +425 -0
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +601 -0
- crackerjack/core/session_coordinator.py +239 -0
- crackerjack/core/timeout_manager.py +563 -0
- crackerjack/core/websocket_lifecycle.py +410 -0
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +2243 -0
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +767 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +1065 -0
- crackerjack/dynamic_config.py +678 -0
- crackerjack/errors.py +378 -0
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +13 -0
- crackerjack/executors/async_hook_executor.py +938 -0
- crackerjack/executors/cached_hook_executor.py +316 -0
- crackerjack/executors/hook_executor.py +1295 -0
- crackerjack/executors/hook_lock_manager.py +708 -0
- crackerjack/executors/individual_hook_executor.py +739 -0
- crackerjack/executors/lsp_aware_hook_executor.py +349 -0
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +433 -0
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +93 -0
- crackerjack/intelligence/README.md +557 -0
- crackerjack/intelligence/__init__.py +37 -0
- crackerjack/intelligence/adaptive_learning.py +693 -0
- crackerjack/intelligence/agent_orchestrator.py +485 -0
- crackerjack/intelligence/agent_registry.py +377 -0
- crackerjack/intelligence/agent_selector.py +439 -0
- crackerjack/intelligence/integration.py +250 -0
- crackerjack/interactive.py +719 -0
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +585 -0
- crackerjack/managers/publish_manager.py +631 -0
- crackerjack/managers/test_command_builder.py +391 -0
- crackerjack/managers/test_executor.py +474 -0
- crackerjack/managers/test_manager.py +1357 -0
- crackerjack/managers/test_progress.py +187 -0
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +352 -0
- crackerjack/mcp/client_runner.py +121 -0
- crackerjack/mcp/context.py +802 -0
- crackerjack/mcp/dashboard.py +657 -0
- crackerjack/mcp/enhanced_progress_monitor.py +493 -0
- crackerjack/mcp/file_monitor.py +394 -0
- crackerjack/mcp/progress_components.py +607 -0
- crackerjack/mcp/progress_monitor.py +1016 -0
- crackerjack/mcp/rate_limiter.py +336 -0
- crackerjack/mcp/server.py +24 -0
- crackerjack/mcp/server_core.py +526 -0
- crackerjack/mcp/service_watchdog.py +505 -0
- crackerjack/mcp/state.py +407 -0
- crackerjack/mcp/task_manager.py +259 -0
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +19 -0
- crackerjack/mcp/tools/core_tools.py +469 -0
- crackerjack/mcp/tools/error_analyzer.py +283 -0
- crackerjack/mcp/tools/execution_tools.py +384 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
- crackerjack/mcp/tools/intelligence_tools.py +264 -0
- crackerjack/mcp/tools/monitoring_tools.py +628 -0
- crackerjack/mcp/tools/proactive_tools.py +367 -0
- crackerjack/mcp/tools/progress_tools.py +222 -0
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +358 -0
- crackerjack/mcp/tools/workflow_executor.py +699 -0
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +54 -0
- crackerjack/mcp/websocket/endpoints.py +492 -0
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +406 -0
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
- crackerjack/mcp/websocket/server.py +174 -0
- crackerjack/mcp/websocket/websocket_handler.py +276 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +40 -0
- crackerjack/models/config.py +730 -0
- crackerjack/models/config_adapter.py +265 -0
- crackerjack/models/protocols.py +1535 -0
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/resource_protocols.py +299 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +173 -0
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +405 -0
- crackerjack/monitoring/metrics_collector.py +427 -0
- crackerjack/monitoring/regression_prevention.py +580 -0
- crackerjack/monitoring/websocket_server.py +406 -0
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +894 -0
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +180 -0
- crackerjack/orchestration/execution_strategies.py +361 -0
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +647 -0
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +254 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +264 -0
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/security/__init__.py +0 -0
- crackerjack/security/audit.py +197 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +9 -0
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/ai/contextual_ai_assistant.py +542 -0
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +617 -0
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +530 -0
- crackerjack/services/cache.py +369 -0
- crackerjack/services/changelog_automation.py +399 -0
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +132 -0
- crackerjack/services/config_merge.py +546 -0
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +493 -0
- crackerjack/services/coverage_badge_service.py +173 -0
- crackerjack/services/coverage_ratchet.py +381 -0
- crackerjack/services/debug.py +733 -0
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +622 -0
- crackerjack/services/documentation_generator.py +493 -0
- crackerjack/services/documentation_service.py +704 -0
- crackerjack/services/enhanced_filesystem.py +497 -0
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +149 -0
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +381 -0
- crackerjack/services/git.py +422 -0
- crackerjack/services/health_metrics.py +615 -0
- crackerjack/services/heatmap_generator.py +744 -0
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +823 -0
- crackerjack/services/input_validator.py +668 -0
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +289 -0
- crackerjack/services/logging.py +228 -0
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +414 -0
- crackerjack/services/metrics.py +587 -0
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/monitoring/performance_benchmarks.py +410 -0
- crackerjack/services/monitoring/performance_cache.py +388 -0
- crackerjack/services/monitoring/performance_monitor.py +569 -0
- crackerjack/services/parallel_executor.py +527 -0
- crackerjack/services/pattern_cache.py +333 -0
- crackerjack/services/pattern_detector.py +478 -0
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +523 -0
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/quality/quality_baseline.py +395 -0
- crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
- crackerjack/services/quality/quality_intelligence.py +949 -0
- crackerjack/services/regex_patterns.py +58 -0
- crackerjack/services/regex_utils.py +483 -0
- crackerjack/services/secure_path_utils.py +524 -0
- crackerjack/services/secure_status_formatter.py +450 -0
- crackerjack/services/secure_subprocess.py +635 -0
- crackerjack/services/security.py +239 -0
- crackerjack/services/security_logger.py +495 -0
- crackerjack/services/server_manager.py +411 -0
- crackerjack/services/smart_scheduling.py +167 -0
- crackerjack/services/status_authentication.py +460 -0
- crackerjack/services/status_security_manager.py +315 -0
- crackerjack/services/terminal_utils.py +0 -0
- crackerjack/services/thread_safe_status_collector.py +441 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +43 -0
- crackerjack/services/unified_config.py +115 -0
- crackerjack/services/validation_rate_limiter.py +220 -0
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +461 -0
- crackerjack/services/version_checker.py +223 -0
- crackerjack/services/websocket_resource_limiter.py +438 -0
- crackerjack/services/zuban_lsp_service.py +391 -0
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/__init__.py +59 -0
- crackerjack/slash_commands/init.md +112 -0
- crackerjack/slash_commands/run.md +197 -0
- crackerjack/slash_commands/status.md +127 -0
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_input_validator_patterns.py +236 -0
- crackerjack/tools/validate_regex_patterns.py +188 -0
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/__init__.py +1 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- crackerjack-0.45.2.dist-info/METADATA +1678 -0
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -14
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/.pre-commit-config.yaml +0 -91
- crackerjack/.pytest_cache/.gitignore +0 -2
- crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
- crackerjack/.pytest_cache/README.md +0 -8
- crackerjack/.pytest_cache/v/cache/nodeids +0 -1
- crackerjack/.pytest_cache/v/cache/stepwise +0 -1
- crackerjack/.ruff_cache/.gitignore +0 -1
- crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
- crackerjack/crackerjack.py +0 -855
- crackerjack/pyproject.toml +0 -214
- crackerjack-0.18.2.dist-info/METADATA +0 -420
- crackerjack-0.18.2.dist-info/RECORD +0 -59
- crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"""LSP client wrapper for Zuban communication."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import socket
|
|
7
|
+
import typing as t
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("crackerjack.lsp_client")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ZubanLSPClient:
|
|
14
|
+
"""Minimal LSP client for zuban communication."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, host: str = "127.0.0.1", port: int = 8677) -> None:
|
|
17
|
+
"""Initialize LSP client.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
host: LSP server host
|
|
21
|
+
port: LSP server port
|
|
22
|
+
"""
|
|
23
|
+
self.host = host
|
|
24
|
+
self.port = port
|
|
25
|
+
self._socket: socket.socket | None = None
|
|
26
|
+
self._reader: asyncio.StreamReader | None = None
|
|
27
|
+
self._writer: asyncio.StreamWriter | None = None
|
|
28
|
+
self._request_id = 0
|
|
29
|
+
self._initialized = False
|
|
30
|
+
|
|
31
|
+
def _next_request_id(self) -> int:
|
|
32
|
+
"""Generate next request ID."""
|
|
33
|
+
self._request_id += 1
|
|
34
|
+
return self._request_id
|
|
35
|
+
|
|
36
|
+
async def connect(self, timeout: float = 5.0) -> bool:
|
|
37
|
+
"""Connect to zuban LSP server.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
timeout: Connection timeout in seconds
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
True if connected successfully
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
# Attempt TCP connection
|
|
47
|
+
self._reader, self._writer = await asyncio.wait_for(
|
|
48
|
+
asyncio.open_connection(self.host, self.port), timeout=timeout
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
logger.info(f"Connected to Zuban LSP server at {self.host}:{self.port}")
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
except (TimeoutError, OSError) as e:
|
|
55
|
+
logger.warning(f"Failed to connect to LSP server: {e}")
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
async def disconnect(self) -> None:
|
|
59
|
+
"""Disconnect from LSP server."""
|
|
60
|
+
if self._writer:
|
|
61
|
+
try:
|
|
62
|
+
# Send shutdown request
|
|
63
|
+
if self._initialized:
|
|
64
|
+
await self._send_request("shutdown")
|
|
65
|
+
await self._send_notification("exit")
|
|
66
|
+
|
|
67
|
+
self._writer.close()
|
|
68
|
+
await self._writer.wait_closed()
|
|
69
|
+
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.warning(f"Error during disconnect: {e}")
|
|
72
|
+
finally:
|
|
73
|
+
self._writer = None
|
|
74
|
+
self._reader = None
|
|
75
|
+
self._initialized = False
|
|
76
|
+
|
|
77
|
+
async def initialize(self, root_path: Path) -> dict[str, t.Any] | None:
|
|
78
|
+
"""Send initialize request.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
root_path: Workspace root path
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Initialize response from server
|
|
85
|
+
"""
|
|
86
|
+
if self._initialized:
|
|
87
|
+
return {"status": "already_initialized"}
|
|
88
|
+
|
|
89
|
+
params = {
|
|
90
|
+
"processId": None,
|
|
91
|
+
"rootPath": str(root_path),
|
|
92
|
+
"rootUri": f"file://{root_path}",
|
|
93
|
+
"capabilities": {
|
|
94
|
+
"textDocument": {
|
|
95
|
+
"publishDiagnostics": {
|
|
96
|
+
"versionSupport": True,
|
|
97
|
+
"tagSupport": {"valueSet": [1, 2]},
|
|
98
|
+
"relatedInformation": True,
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"workspace": {
|
|
102
|
+
"workspaceFolders": True,
|
|
103
|
+
"configuration": True,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
"workspaceFolders": [
|
|
107
|
+
{"uri": f"file://{root_path}", "name": root_path.name}
|
|
108
|
+
],
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
response = await self._send_request("initialize", params)
|
|
112
|
+
if response and not response.get("error"):
|
|
113
|
+
# Send initialized notification
|
|
114
|
+
await self._send_notification("initialized")
|
|
115
|
+
self._initialized = True
|
|
116
|
+
logger.info("LSP client initialized successfully")
|
|
117
|
+
|
|
118
|
+
return response
|
|
119
|
+
|
|
120
|
+
async def text_document_did_open(self, file_path: Path) -> None:
|
|
121
|
+
"""Notify server of opened document.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
file_path: Path to opened file
|
|
125
|
+
"""
|
|
126
|
+
if not file_path.exists():
|
|
127
|
+
logger.warning(f"File does not exist: {file_path}")
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
content = file_path.read_text(encoding="utf-8")
|
|
132
|
+
except UnicodeDecodeError:
|
|
133
|
+
logger.warning(f"Could not read file as UTF-8: {file_path}")
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
params = {
|
|
137
|
+
"textDocument": {
|
|
138
|
+
"uri": f"file://{file_path}",
|
|
139
|
+
"languageId": "python",
|
|
140
|
+
"version": 1,
|
|
141
|
+
"text": content,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await self._send_notification("textDocument/didOpen", params)
|
|
146
|
+
|
|
147
|
+
async def text_document_did_change(
|
|
148
|
+
self, file_path: Path, content: str, version: int = 2
|
|
149
|
+
) -> None:
|
|
150
|
+
"""Notify server of document changes.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
file_path: Path to changed file
|
|
154
|
+
content: New file content
|
|
155
|
+
version: Document version number
|
|
156
|
+
"""
|
|
157
|
+
params = {
|
|
158
|
+
"textDocument": {
|
|
159
|
+
"uri": f"file://{file_path}",
|
|
160
|
+
"version": version,
|
|
161
|
+
},
|
|
162
|
+
"contentChanges": [{"text": content}],
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
await self._send_notification("textDocument/didChange", params)
|
|
166
|
+
|
|
167
|
+
async def text_document_did_close(self, file_path: Path) -> None:
|
|
168
|
+
"""Notify server of closed document.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
file_path: Path to closed file
|
|
172
|
+
"""
|
|
173
|
+
params = {
|
|
174
|
+
"textDocument": {
|
|
175
|
+
"uri": f"file://{file_path}",
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await self._send_notification("textDocument/didClose", params)
|
|
180
|
+
|
|
181
|
+
async def get_diagnostics(self, timeout: float = 2.0) -> list[dict[str, t.Any]]:
|
|
182
|
+
"""Retrieve current diagnostics from server.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
timeout: Timeout for waiting for diagnostics
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
List of diagnostic messages
|
|
189
|
+
"""
|
|
190
|
+
# For now, return empty list[t.Any] as diagnostics are typically pushed via notifications
|
|
191
|
+
# In a full implementation, we'd maintain a diagnostics store updated by notifications
|
|
192
|
+
return []
|
|
193
|
+
|
|
194
|
+
async def _send_request(
|
|
195
|
+
self, method: str, params: dict[str, t.Any] | None = None, timeout: float = 10.0
|
|
196
|
+
) -> dict[str, t.Any] | None:
|
|
197
|
+
"""Send LSP request and wait for response.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
method: LSP method name
|
|
201
|
+
params: Request parameters
|
|
202
|
+
timeout: Response timeout
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Response from server
|
|
206
|
+
"""
|
|
207
|
+
if not self._writer or not self._reader:
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
request_id = self._next_request_id()
|
|
211
|
+
request = {
|
|
212
|
+
"jsonrpc": "2.0",
|
|
213
|
+
"id": request_id,
|
|
214
|
+
"method": method,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if params is not None:
|
|
218
|
+
request["params"] = params
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
# Send request
|
|
222
|
+
await self._send_message(request)
|
|
223
|
+
|
|
224
|
+
# Wait for response
|
|
225
|
+
response = await asyncio.wait_for(
|
|
226
|
+
self._read_response(request_id), timeout=timeout
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
return response
|
|
230
|
+
|
|
231
|
+
except TimeoutError:
|
|
232
|
+
logger.warning(f"LSP request {method} timed out")
|
|
233
|
+
return {"error": "timeout", "id": request_id}
|
|
234
|
+
except Exception as e:
|
|
235
|
+
logger.error(f"LSP request {method} failed: {e}")
|
|
236
|
+
return {"error": str(e), "id": request_id}
|
|
237
|
+
|
|
238
|
+
async def _send_notification(
|
|
239
|
+
self, method: str, params: dict[str, t.Any] | None = None
|
|
240
|
+
) -> None:
|
|
241
|
+
"""Send LSP notification (no response expected).
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
method: LSP method name
|
|
245
|
+
params: Notification parameters
|
|
246
|
+
"""
|
|
247
|
+
if not self._writer:
|
|
248
|
+
return
|
|
249
|
+
|
|
250
|
+
notification: dict[str, t.Any] = {
|
|
251
|
+
"jsonrpc": "2.0",
|
|
252
|
+
"method": method,
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if params is not None:
|
|
256
|
+
notification["params"] = params
|
|
257
|
+
|
|
258
|
+
try:
|
|
259
|
+
await self._send_message(notification)
|
|
260
|
+
except Exception as e:
|
|
261
|
+
logger.error(f"LSP notification {method} failed: {e}")
|
|
262
|
+
|
|
263
|
+
async def _send_message(self, message: dict[str, t.Any]) -> None:
|
|
264
|
+
"""Send LSP message with proper formatting.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
message: Message to send
|
|
268
|
+
"""
|
|
269
|
+
if not self._writer:
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
content_bytes = json.dumps(message).encode("utf-8")
|
|
273
|
+
content_length = len(content_bytes)
|
|
274
|
+
|
|
275
|
+
# LSP protocol: Content-Length header + \r\n\r\n + JSON
|
|
276
|
+
header = f"Content-Length: {content_length}\r\n\r\n"
|
|
277
|
+
full_message = header.encode("ascii") + content_bytes
|
|
278
|
+
|
|
279
|
+
self._writer.write(full_message)
|
|
280
|
+
await self._writer.drain()
|
|
281
|
+
|
|
282
|
+
async def _read_response(self, expected_id: int) -> dict[str, t.Any] | None:
|
|
283
|
+
"""Read LSP response for specific request ID.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
expected_id: Expected request ID
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
Response message
|
|
290
|
+
"""
|
|
291
|
+
while True:
|
|
292
|
+
message = await self._read_message()
|
|
293
|
+
if not message:
|
|
294
|
+
return None
|
|
295
|
+
|
|
296
|
+
# Check if this is the response we're waiting for
|
|
297
|
+
if message.get("id") == expected_id:
|
|
298
|
+
return message
|
|
299
|
+
|
|
300
|
+
# If it's a different response or notification, log and continue
|
|
301
|
+
if "id" in message:
|
|
302
|
+
logger.debug(
|
|
303
|
+
f"Received response for ID {message['id']}, expected {expected_id}"
|
|
304
|
+
)
|
|
305
|
+
else:
|
|
306
|
+
logger.debug(
|
|
307
|
+
f"Received notification: {message.get('method', 'unknown')}"
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
async def _read_message(self) -> dict[str, t.Any] | None:
|
|
311
|
+
"""Read complete LSP message.
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
Parsed message dictionary
|
|
315
|
+
"""
|
|
316
|
+
if not self._reader:
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
try:
|
|
320
|
+
# Read Content-Length header
|
|
321
|
+
header_line = await self._reader.readline()
|
|
322
|
+
header_str = header_line.decode("ascii").strip()
|
|
323
|
+
|
|
324
|
+
if not header_str.startswith("Content-Length:"):
|
|
325
|
+
logger.warning(f"Invalid LSP header: {header_str}")
|
|
326
|
+
return None
|
|
327
|
+
|
|
328
|
+
content_length = int(header_str.split(":", 1)[1].strip())
|
|
329
|
+
|
|
330
|
+
# Read separator line
|
|
331
|
+
separator = await self._reader.readline()
|
|
332
|
+
if separator.strip():
|
|
333
|
+
logger.warning("Expected empty separator line")
|
|
334
|
+
|
|
335
|
+
# Read JSON content
|
|
336
|
+
content_bytes = await self._reader.readexactly(content_length)
|
|
337
|
+
content = content_bytes.decode()
|
|
338
|
+
|
|
339
|
+
json_result = json.loads(content)
|
|
340
|
+
return t.cast(dict[str, t.Any] | None, json_result)
|
|
341
|
+
|
|
342
|
+
except Exception as e:
|
|
343
|
+
logger.error(f"Failed to read LSP message: {e}")
|
|
344
|
+
return None
|
|
345
|
+
|
|
346
|
+
async def __aenter__(self) -> "ZubanLSPClient":
|
|
347
|
+
"""Async context manager entry."""
|
|
348
|
+
await self.connect()
|
|
349
|
+
return self
|
|
350
|
+
|
|
351
|
+
async def __aexit__(
|
|
352
|
+
self,
|
|
353
|
+
exc_type: type[BaseException] | None,
|
|
354
|
+
exc_val: BaseException | None,
|
|
355
|
+
exc_tb: t.Any,
|
|
356
|
+
) -> None:
|
|
357
|
+
"""Async context manager exit."""
|
|
358
|
+
await self.disconnect()
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Rust tool manager for unified execution and coordination."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import time
|
|
5
|
+
import typing as t
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from ._base import BaseRustToolAdapter, ToolResult
|
|
9
|
+
from .skylos import SkylosAdapter
|
|
10
|
+
from .zuban import ZubanAdapter
|
|
11
|
+
|
|
12
|
+
if t.TYPE_CHECKING:
|
|
13
|
+
from crackerjack.orchestration.execution_strategies import ExecutionContext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RustToolHookManager:
|
|
17
|
+
"""Manager for coordinating Rust-based analysis tools."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, context: "ExecutionContext") -> None:
|
|
20
|
+
"""Initialize manager with execution context."""
|
|
21
|
+
self.context = context
|
|
22
|
+
self.adapters: dict[str, BaseRustToolAdapter] = {}
|
|
23
|
+
self._initialize_adapters()
|
|
24
|
+
|
|
25
|
+
def _initialize_adapters(self) -> None:
|
|
26
|
+
"""Initialize available Rust tool adapters."""
|
|
27
|
+
# Dead code detection with Skylos
|
|
28
|
+
self.adapters["skylos"] = SkylosAdapter(context=self.context)
|
|
29
|
+
|
|
30
|
+
# Type checking with Zuban
|
|
31
|
+
self.adapters["zuban"] = ZubanAdapter(context=self.context)
|
|
32
|
+
|
|
33
|
+
async def run_all_tools(
|
|
34
|
+
self, target_files: list[Path] | None = None
|
|
35
|
+
) -> dict[str, ToolResult]:
|
|
36
|
+
"""Run all available Rust tools in parallel."""
|
|
37
|
+
target_files = target_files or []
|
|
38
|
+
|
|
39
|
+
# Filter to available tools only
|
|
40
|
+
available_adapters = {
|
|
41
|
+
name: adapter
|
|
42
|
+
for name, adapter in self.adapters.items()
|
|
43
|
+
if adapter.validate_tool_available()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if not available_adapters:
|
|
47
|
+
return {
|
|
48
|
+
"error": ToolResult(
|
|
49
|
+
success=False,
|
|
50
|
+
error=(
|
|
51
|
+
"No Rust tools are available. "
|
|
52
|
+
"Install skylos and zuban with: uv add skylos zuban"
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Run tools in parallel
|
|
58
|
+
tasks = [
|
|
59
|
+
self._run_single_tool(name, adapter, target_files)
|
|
60
|
+
for name, adapter in available_adapters.items()
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
64
|
+
|
|
65
|
+
# Process results
|
|
66
|
+
tool_results = {}
|
|
67
|
+
for i, (name, _) in enumerate(available_adapters.items()):
|
|
68
|
+
result = results[i]
|
|
69
|
+
if isinstance(result, Exception):
|
|
70
|
+
tool_results[name] = ToolResult(
|
|
71
|
+
success=False,
|
|
72
|
+
error=f"Tool execution failed: {result}",
|
|
73
|
+
)
|
|
74
|
+
elif isinstance(result, ToolResult): # Explicit check for mypy
|
|
75
|
+
tool_results[name] = result
|
|
76
|
+
|
|
77
|
+
return tool_results
|
|
78
|
+
|
|
79
|
+
async def run_single_tool(
|
|
80
|
+
self, tool_name: str, target_files: list[Path] | None = None
|
|
81
|
+
) -> ToolResult:
|
|
82
|
+
"""Run a single Rust tool by name."""
|
|
83
|
+
if tool_name not in self.adapters:
|
|
84
|
+
return ToolResult(
|
|
85
|
+
success=False,
|
|
86
|
+
error=(
|
|
87
|
+
f"Unknown tool: {tool_name}. "
|
|
88
|
+
f"Available: {list[t.Any](self.adapters.keys())}"
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
adapter = self.adapters[tool_name]
|
|
93
|
+
if not adapter.validate_tool_available():
|
|
94
|
+
return ToolResult(
|
|
95
|
+
success=False,
|
|
96
|
+
error=(
|
|
97
|
+
f"Tool {tool_name} is not available. "
|
|
98
|
+
f"Install with: uv add {tool_name}"
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return await self._run_single_tool(tool_name, adapter, target_files or [])
|
|
103
|
+
|
|
104
|
+
async def _run_single_tool(
|
|
105
|
+
self, name: str, adapter: BaseRustToolAdapter, target_files: list[Path]
|
|
106
|
+
) -> ToolResult:
|
|
107
|
+
"""Execute a single tool adapter."""
|
|
108
|
+
start_time = time.time()
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
# Get command arguments
|
|
112
|
+
cmd_args = adapter.get_command_args(target_files)
|
|
113
|
+
|
|
114
|
+
# Execute command
|
|
115
|
+
process = await asyncio.create_subprocess_exec(
|
|
116
|
+
*cmd_args,
|
|
117
|
+
stdout=asyncio.subprocess.PIPE,
|
|
118
|
+
stderr=asyncio.subprocess.STDOUT,
|
|
119
|
+
cwd=self.context.working_directory,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
stdout, _ = await process.communicate()
|
|
123
|
+
output = stdout.decode() if stdout else ""
|
|
124
|
+
|
|
125
|
+
# Parse output
|
|
126
|
+
result = adapter.parse_output(output)
|
|
127
|
+
result.execution_time = time.time() - start_time
|
|
128
|
+
|
|
129
|
+
return result
|
|
130
|
+
|
|
131
|
+
except Exception as e:
|
|
132
|
+
return ToolResult(
|
|
133
|
+
success=False,
|
|
134
|
+
error=f"Failed to execute {name}: {e}",
|
|
135
|
+
execution_time=time.time() - start_time,
|
|
136
|
+
tool_version=adapter.get_tool_version(),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def get_available_tools(self) -> list[str]:
|
|
140
|
+
"""Get list[t.Any] of available Rust tools."""
|
|
141
|
+
return [
|
|
142
|
+
name
|
|
143
|
+
for name, adapter in self.adapters.items()
|
|
144
|
+
if adapter.validate_tool_available()
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
def get_tool_info(self) -> dict[str, dict[str, t.Any]]:
|
|
148
|
+
"""Get information about all configured tools."""
|
|
149
|
+
info: dict[str, dict[str, t.Any]] = {}
|
|
150
|
+
for name, adapter in self.adapters.items():
|
|
151
|
+
info[name] = {
|
|
152
|
+
"available": adapter.validate_tool_available(),
|
|
153
|
+
"supports_json": adapter.supports_json_output(),
|
|
154
|
+
"version": adapter.get_tool_version(),
|
|
155
|
+
"tool_name": adapter.get_tool_name(),
|
|
156
|
+
}
|
|
157
|
+
return info
|
|
158
|
+
|
|
159
|
+
def create_consolidated_report(
|
|
160
|
+
self, results: dict[str, ToolResult]
|
|
161
|
+
) -> dict[str, t.Any]:
|
|
162
|
+
"""Create a consolidated report from multiple tool results."""
|
|
163
|
+
total_issues = 0
|
|
164
|
+
total_errors = 0
|
|
165
|
+
total_warnings = 0
|
|
166
|
+
all_success = True
|
|
167
|
+
execution_times = {}
|
|
168
|
+
|
|
169
|
+
for tool_name, result in results.items():
|
|
170
|
+
if tool_name == "error":
|
|
171
|
+
all_success = False
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
total_issues += len(result.issues)
|
|
175
|
+
total_errors += result.error_count
|
|
176
|
+
total_warnings += result.warning_count
|
|
177
|
+
execution_times[tool_name] = result.execution_time
|
|
178
|
+
|
|
179
|
+
if not result.success:
|
|
180
|
+
all_success = False
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
"overall_success": all_success,
|
|
184
|
+
"total_issues": total_issues,
|
|
185
|
+
"total_errors": total_errors,
|
|
186
|
+
"total_warnings": total_warnings,
|
|
187
|
+
"tools_run": list[t.Any](results.keys()),
|
|
188
|
+
"execution_times": execution_times,
|
|
189
|
+
"total_time": sum(execution_times.values()),
|
|
190
|
+
"results_by_tool": {
|
|
191
|
+
name: result.to_dict() for name, result in results.items()
|
|
192
|
+
},
|
|
193
|
+
}
|