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,349 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import typing as t
|
|
3
|
+
from contextlib import suppress
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from acb.console import Console
|
|
7
|
+
from acb.depends import Inject, depends
|
|
8
|
+
|
|
9
|
+
from crackerjack.config.hooks import HookDefinition, HookStrategy
|
|
10
|
+
from crackerjack.executors.hook_executor import HookExecutionResult, HookExecutor
|
|
11
|
+
from crackerjack.models.task import HookResult
|
|
12
|
+
from crackerjack.services.lsp_client import LSPClient
|
|
13
|
+
|
|
14
|
+
# Conditional import for ToolProxy
|
|
15
|
+
try:
|
|
16
|
+
from crackerjack.executors.tool_proxy import ToolProxy
|
|
17
|
+
except ImportError:
|
|
18
|
+
ToolProxy = None # type: ignore
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class LSPAwareHookExecutor(HookExecutor):
|
|
22
|
+
"""Hook executor that can leverage LSP server for enhanced performance."""
|
|
23
|
+
|
|
24
|
+
@depends.inject
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
console: Inject[Console],
|
|
28
|
+
pkg_path: Path,
|
|
29
|
+
verbose: bool = False,
|
|
30
|
+
quiet: bool = False,
|
|
31
|
+
debug: bool = False,
|
|
32
|
+
use_tool_proxy: bool = True,
|
|
33
|
+
use_incremental: bool = False,
|
|
34
|
+
git_service: t.Any | None = None,
|
|
35
|
+
) -> None:
|
|
36
|
+
super().__init__(
|
|
37
|
+
console, pkg_path, verbose, quiet, debug, use_incremental, git_service
|
|
38
|
+
)
|
|
39
|
+
self.lsp_client = LSPClient()
|
|
40
|
+
self.use_tool_proxy = use_tool_proxy and ToolProxy is not None
|
|
41
|
+
self.tool_proxy = ToolProxy() if self.use_tool_proxy else None
|
|
42
|
+
self.debug = debug
|
|
43
|
+
|
|
44
|
+
def execute_strategy(self, strategy: HookStrategy) -> HookExecutionResult:
|
|
45
|
+
"""Execute hook strategy with LSP optimization where possible."""
|
|
46
|
+
start_time = time.time()
|
|
47
|
+
results = []
|
|
48
|
+
|
|
49
|
+
# Check if LSP server is available
|
|
50
|
+
lsp_available = self._check_lsp_availability()
|
|
51
|
+
|
|
52
|
+
# Execute hooks with LSP optimization and tool proxy resilience
|
|
53
|
+
for hook in strategy.hooks:
|
|
54
|
+
self._handle_progress_start(len(strategy.hooks))
|
|
55
|
+
result = self._execute_single_hook_with_strategies(hook, lsp_available)
|
|
56
|
+
results.append(result)
|
|
57
|
+
self._handle_progress_completion(len(strategy.hooks))
|
|
58
|
+
|
|
59
|
+
duration = time.time() - start_time
|
|
60
|
+
success = all(result.status in ("passed", "skipped") for result in results)
|
|
61
|
+
|
|
62
|
+
return HookExecutionResult(
|
|
63
|
+
strategy_name=strategy.name,
|
|
64
|
+
results=results,
|
|
65
|
+
total_duration=duration,
|
|
66
|
+
success=success,
|
|
67
|
+
concurrent_execution=False,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def _check_lsp_availability(self) -> bool:
|
|
71
|
+
"""Check if LSP server is available and print info message."""
|
|
72
|
+
lsp_available = self.lsp_client.is_server_running()
|
|
73
|
+
|
|
74
|
+
if lsp_available and not self.quiet:
|
|
75
|
+
server_info = self.lsp_client.get_server_info()
|
|
76
|
+
if server_info:
|
|
77
|
+
self.console.print(
|
|
78
|
+
f"🔍 LSP server available (PID: {server_info['pid']}), using optimized execution"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return lsp_available
|
|
82
|
+
|
|
83
|
+
def _execute_single_hook_with_strategies(
|
|
84
|
+
self, hook: HookDefinition, lsp_available: bool
|
|
85
|
+
) -> HookResult:
|
|
86
|
+
"""Execute a single hook using appropriate strategy."""
|
|
87
|
+
if self._should_use_lsp_for_hook(hook, lsp_available):
|
|
88
|
+
return self._execute_lsp_hook(hook)
|
|
89
|
+
elif self._should_use_tool_proxy(hook):
|
|
90
|
+
return self._execute_hook_with_proxy(hook)
|
|
91
|
+
|
|
92
|
+
return self.execute_single_hook(hook)
|
|
93
|
+
|
|
94
|
+
def _handle_progress_start(self, total_hooks: int | None = None) -> None:
|
|
95
|
+
"""Handle progress start callback."""
|
|
96
|
+
with suppress(Exception):
|
|
97
|
+
callback = getattr(self, "_progress_start_callback", None)
|
|
98
|
+
if callback:
|
|
99
|
+
# _total_hooks/_started_hooks are initialized by set_progress_callbacks on base class
|
|
100
|
+
self._started_hooks += 1 # type: ignore[attr-defined]
|
|
101
|
+
total = self._total_hooks or total_hooks # type: ignore[attr-defined]
|
|
102
|
+
if total:
|
|
103
|
+
callback(self._started_hooks, total) # type: ignore[attr-defined]
|
|
104
|
+
|
|
105
|
+
def _handle_progress_completion(self, total_hooks: int | None = None) -> None:
|
|
106
|
+
"""Handle progress completion callback."""
|
|
107
|
+
with suppress(Exception):
|
|
108
|
+
callback = getattr(self, "_progress_callback", None)
|
|
109
|
+
if callback:
|
|
110
|
+
self._completed_hooks += 1 # type: ignore[attr-defined]
|
|
111
|
+
total = self._total_hooks or total_hooks # type: ignore[attr-defined]
|
|
112
|
+
if total:
|
|
113
|
+
callback(self._completed_hooks, total) # type: ignore[attr-defined]
|
|
114
|
+
|
|
115
|
+
def _should_use_lsp_for_hook(
|
|
116
|
+
self, hook: HookDefinition, lsp_available: bool
|
|
117
|
+
) -> bool:
|
|
118
|
+
"""Determine if a hook should use LSP-based execution."""
|
|
119
|
+
# Only use LSP for type-checking hooks when server is available
|
|
120
|
+
return (
|
|
121
|
+
lsp_available
|
|
122
|
+
and hook.name == "zuban"
|
|
123
|
+
and hook.stage.value == "comprehensive"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def _execute_lsp_hook(self, hook: HookDefinition) -> HookResult:
|
|
127
|
+
"""Execute a hook using LSP server with real-time feedback."""
|
|
128
|
+
start_time = time.time()
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
return self._perform_lsp_execution(hook, start_time)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
return self._handle_lsp_execution_error(hook, start_time, e)
|
|
134
|
+
|
|
135
|
+
def _perform_lsp_execution(
|
|
136
|
+
self, hook: HookDefinition, start_time: float
|
|
137
|
+
) -> HookResult:
|
|
138
|
+
"""Perform the actual LSP execution."""
|
|
139
|
+
if not self.quiet:
|
|
140
|
+
self.console.print(
|
|
141
|
+
f"🚀 Using LSP-optimized execution for {hook.name}", style="cyan"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Use the new real-time feedback method
|
|
145
|
+
diagnostics, summary = self.lsp_client.check_project_with_feedback(
|
|
146
|
+
self.pkg_path, show_progress=not self.quiet
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
duration = time.time() - start_time
|
|
150
|
+
has_errors = any(diags for diags in diagnostics.values())
|
|
151
|
+
output = self._format_lsp_output(diagnostics, duration)
|
|
152
|
+
|
|
153
|
+
self._display_lsp_results(hook, has_errors, output, summary)
|
|
154
|
+
|
|
155
|
+
# Create hook result
|
|
156
|
+
return self._create_lsp_hook_result(
|
|
157
|
+
hook, duration, has_errors, output, diagnostics
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def _create_lsp_hook_result(
|
|
161
|
+
self,
|
|
162
|
+
hook: HookDefinition,
|
|
163
|
+
duration: float,
|
|
164
|
+
has_errors: bool,
|
|
165
|
+
output: str,
|
|
166
|
+
diagnostics: dict,
|
|
167
|
+
) -> HookResult:
|
|
168
|
+
"""Create the HookResult for LSP execution."""
|
|
169
|
+
# Ensure failed hooks always have at least 1 issue count
|
|
170
|
+
issues_found = [output] if has_errors else []
|
|
171
|
+
issues_count = max(len(issues_found), 1 if has_errors else 0)
|
|
172
|
+
|
|
173
|
+
return HookResult(
|
|
174
|
+
id=f"{hook.name}-lsp-{int(time.time())}",
|
|
175
|
+
name=f"{hook.name}-lsp",
|
|
176
|
+
status="failed" if has_errors else "passed",
|
|
177
|
+
duration=duration,
|
|
178
|
+
files_processed=len(diagnostics),
|
|
179
|
+
issues_found=issues_found,
|
|
180
|
+
issues_count=issues_count,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def _format_lsp_output(self, diagnostics: dict[str, t.Any], duration: float) -> str:
|
|
184
|
+
"""Format LSP diagnostic output with performance info."""
|
|
185
|
+
output = self.lsp_client.format_diagnostics(diagnostics)
|
|
186
|
+
file_count = len(self.lsp_client.get_project_files(self.pkg_path))
|
|
187
|
+
perf_info = f"\n⚡ LSP-optimized check completed in {duration:.2f}s ({file_count} files)"
|
|
188
|
+
return output + perf_info
|
|
189
|
+
|
|
190
|
+
def _display_lsp_results(
|
|
191
|
+
self, hook: HookDefinition, has_errors: bool, output: str, summary: str
|
|
192
|
+
) -> None:
|
|
193
|
+
"""Display LSP execution results."""
|
|
194
|
+
if self.verbose or has_errors:
|
|
195
|
+
if not self.quiet:
|
|
196
|
+
self.console.print(f"🔍 {hook.name} (LSP):", style="bold blue")
|
|
197
|
+
if has_errors:
|
|
198
|
+
self.console.print(output)
|
|
199
|
+
else:
|
|
200
|
+
self.console.print(summary, style="green")
|
|
201
|
+
|
|
202
|
+
def _handle_lsp_execution_error(
|
|
203
|
+
self, hook: HookDefinition, start_time: float, error: Exception
|
|
204
|
+
) -> HookResult:
|
|
205
|
+
"""Handle LSP execution errors with fallback."""
|
|
206
|
+
time.time() - start_time
|
|
207
|
+
error_msg = f"LSP execution failed: {error}"
|
|
208
|
+
|
|
209
|
+
if not self.quiet:
|
|
210
|
+
self.console.print(f"❌ {hook.name} (LSP): {error_msg}", style="red")
|
|
211
|
+
self.console.print(f"🔄 Falling back to regular {hook.name} execution")
|
|
212
|
+
|
|
213
|
+
return self.execute_single_hook(hook)
|
|
214
|
+
|
|
215
|
+
def _should_use_tool_proxy(self, hook: HookDefinition) -> bool:
|
|
216
|
+
"""Determine if a hook should use tool proxy for resilient execution."""
|
|
217
|
+
if not self.use_tool_proxy or not self.tool_proxy:
|
|
218
|
+
return False
|
|
219
|
+
|
|
220
|
+
# Use tool proxy for known fragile tools
|
|
221
|
+
fragile_tools = {"zuban", "skylos", "bandit"}
|
|
222
|
+
return hook.name in fragile_tools
|
|
223
|
+
|
|
224
|
+
def _execute_hook_with_proxy(self, hook: HookDefinition) -> HookResult:
|
|
225
|
+
"""Execute a hook using tool proxy for resilient execution."""
|
|
226
|
+
start_time = time.time()
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
return self._perform_proxy_execution(hook, start_time)
|
|
230
|
+
except Exception as e:
|
|
231
|
+
return self._handle_proxy_execution_error(hook, start_time, e)
|
|
232
|
+
|
|
233
|
+
def _perform_proxy_execution(
|
|
234
|
+
self, hook: HookDefinition, start_time: float
|
|
235
|
+
) -> HookResult:
|
|
236
|
+
"""Perform the actual proxy execution."""
|
|
237
|
+
if not self.quiet:
|
|
238
|
+
self.console.print(
|
|
239
|
+
f"🛡️ Using resilient execution for {hook.name}", style="blue"
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Parse hook entry to extract tool name and args
|
|
243
|
+
tool_name, args = self._parse_hook_entry(hook)
|
|
244
|
+
|
|
245
|
+
# Execute through tool proxy
|
|
246
|
+
if self.tool_proxy is not None:
|
|
247
|
+
exit_code = self.tool_proxy.execute_tool(tool_name, args)
|
|
248
|
+
else:
|
|
249
|
+
exit_code = -1 # Error code when tool proxy is not available
|
|
250
|
+
|
|
251
|
+
duration = time.time() - start_time
|
|
252
|
+
status = "passed" if exit_code == 0 else "failed"
|
|
253
|
+
|
|
254
|
+
# Get tool status for output
|
|
255
|
+
tool_status = (
|
|
256
|
+
self.tool_proxy.get_tool_status().get(tool_name, {})
|
|
257
|
+
if self.tool_proxy is not None
|
|
258
|
+
else {}
|
|
259
|
+
)
|
|
260
|
+
output = self._format_proxy_output(tool_name, tool_status, duration)
|
|
261
|
+
|
|
262
|
+
# Ensure failed hooks always have at least 1 issue count
|
|
263
|
+
issues_found = [output] if status == "failed" else []
|
|
264
|
+
issues_count = max(len(issues_found), 1 if status == "failed" else 0)
|
|
265
|
+
|
|
266
|
+
return HookResult(
|
|
267
|
+
id=f"{hook.name}-proxy-{int(time.time())}",
|
|
268
|
+
name=f"{hook.name}-proxy",
|
|
269
|
+
status=status,
|
|
270
|
+
duration=duration,
|
|
271
|
+
files_processed=1, # Placeholder value
|
|
272
|
+
issues_found=issues_found,
|
|
273
|
+
issues_count=issues_count,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
def _handle_proxy_execution_error(
|
|
277
|
+
self, hook: HookDefinition, start_time: float, error: Exception
|
|
278
|
+
) -> HookResult:
|
|
279
|
+
"""Handle proxy execution errors with fallback."""
|
|
280
|
+
time.time() - start_time
|
|
281
|
+
error_msg = f"Tool proxy execution failed: {error}"
|
|
282
|
+
|
|
283
|
+
if not self.quiet:
|
|
284
|
+
self.console.print(f"❌ {hook.name} (proxy): {error_msg}", style="red")
|
|
285
|
+
self.console.print(f"🔄 Falling back to regular {hook.name} execution")
|
|
286
|
+
|
|
287
|
+
# Fallback to regular execution
|
|
288
|
+
return self.execute_single_hook(hook)
|
|
289
|
+
|
|
290
|
+
def _parse_hook_entry(self, hook: HookDefinition) -> tuple[str, list[str]]:
|
|
291
|
+
"""Parse hook entry to extract tool name and arguments."""
|
|
292
|
+
entry_str = " ".join(hook.command)
|
|
293
|
+
entry_parts = entry_str.split()
|
|
294
|
+
|
|
295
|
+
if len(entry_parts) < 3: # e.g., "uv run zuban"
|
|
296
|
+
raise ValueError(f"Invalid hook entry format: {entry_str}")
|
|
297
|
+
|
|
298
|
+
# Extract tool name (assuming "uv run <tool>" format)
|
|
299
|
+
if entry_parts[0] == "uv" and entry_parts[1] == "run":
|
|
300
|
+
tool_name = entry_parts[2]
|
|
301
|
+
args = entry_parts[3:] if len(entry_parts) > 3 else []
|
|
302
|
+
else:
|
|
303
|
+
# Direct tool execution
|
|
304
|
+
tool_name = entry_parts[0]
|
|
305
|
+
args = entry_parts[1:]
|
|
306
|
+
|
|
307
|
+
return tool_name, args
|
|
308
|
+
|
|
309
|
+
def _format_proxy_output(
|
|
310
|
+
self, tool_name: str, tool_status: dict[str, t.Any], duration: float
|
|
311
|
+
) -> str:
|
|
312
|
+
"""Format tool proxy execution output."""
|
|
313
|
+
status_info = []
|
|
314
|
+
|
|
315
|
+
if tool_status.get("circuit_breaker_open"):
|
|
316
|
+
status_info.append("Circuit breaker: OPEN")
|
|
317
|
+
|
|
318
|
+
if tool_status.get("is_healthy") is False:
|
|
319
|
+
status_info.append("Health check: FAILED")
|
|
320
|
+
|
|
321
|
+
fallback_tools = tool_status.get("fallback_tools", [])
|
|
322
|
+
if fallback_tools:
|
|
323
|
+
status_info.append(f"Fallbacks: {', '.join(fallback_tools)}")
|
|
324
|
+
|
|
325
|
+
status_str = f" ({', '.join(status_info)})" if status_info else ""
|
|
326
|
+
|
|
327
|
+
return f"🛡️ Resilient execution completed in {duration:.2f}s{status_str}"
|
|
328
|
+
|
|
329
|
+
def get_execution_mode_summary(self) -> dict[str, t.Any]:
|
|
330
|
+
"""Get summary of execution mode capabilities."""
|
|
331
|
+
lsp_available = self.lsp_client.is_server_running()
|
|
332
|
+
server_info = self.lsp_client.get_server_info() if lsp_available else None
|
|
333
|
+
|
|
334
|
+
summary = {
|
|
335
|
+
"lsp_server_available": lsp_available,
|
|
336
|
+
"lsp_server_info": server_info,
|
|
337
|
+
"optimization_enabled": lsp_available,
|
|
338
|
+
"supported_hooks": ["zuban"] if lsp_available else [],
|
|
339
|
+
"tool_proxy_enabled": self.use_tool_proxy,
|
|
340
|
+
"resilient_tools": ["zuban", "skylos", "bandit"]
|
|
341
|
+
if self.use_tool_proxy
|
|
342
|
+
else [],
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
# Add tool proxy status if available
|
|
346
|
+
if self.tool_proxy:
|
|
347
|
+
summary["tool_status"] = self.tool_proxy.get_tool_status()
|
|
348
|
+
|
|
349
|
+
return summary
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"""Enhanced hook executor with rich progress indicators.
|
|
2
|
+
|
|
3
|
+
Extends base HookExecutor with real-time progress feedback using rich library.
|
|
4
|
+
Part of Phase 10.2.2: Development Velocity Improvements.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import time
|
|
8
|
+
import typing as t
|
|
9
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from acb.console import Console
|
|
13
|
+
from rich.progress import (
|
|
14
|
+
BarColumn,
|
|
15
|
+
MofNCompleteColumn,
|
|
16
|
+
Progress,
|
|
17
|
+
SpinnerColumn,
|
|
18
|
+
TaskProgressColumn,
|
|
19
|
+
TextColumn,
|
|
20
|
+
TimeElapsedColumn,
|
|
21
|
+
TimeRemainingColumn,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from crackerjack.config.hooks import HookStrategy, RetryPolicy
|
|
25
|
+
from crackerjack.executors.hook_executor import HookExecutionResult, HookExecutor
|
|
26
|
+
from crackerjack.models.task import HookResult
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ProgressHookExecutor(HookExecutor):
|
|
30
|
+
"""Hook executor with enhanced progress indicators.
|
|
31
|
+
|
|
32
|
+
Provides real-time feedback during hook execution using rich progress bars,
|
|
33
|
+
improving developer experience during long-running quality checks.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
console: Console,
|
|
39
|
+
pkg_path: Path,
|
|
40
|
+
verbose: bool = False,
|
|
41
|
+
quiet: bool = False,
|
|
42
|
+
show_progress: bool = True,
|
|
43
|
+
debug: bool = False,
|
|
44
|
+
use_incremental: bool = False,
|
|
45
|
+
git_service: t.Any | None = None,
|
|
46
|
+
) -> None:
|
|
47
|
+
"""Initialize progress-enhanced hook executor.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
console: Rich console for output
|
|
51
|
+
pkg_path: Project root path
|
|
52
|
+
verbose: Show detailed output
|
|
53
|
+
quiet: Suppress output
|
|
54
|
+
show_progress: Enable progress bars (disable for CI/testing)
|
|
55
|
+
debug: Enable debug output
|
|
56
|
+
use_incremental: Run hooks only on changed files
|
|
57
|
+
git_service: GitService instance for incremental execution
|
|
58
|
+
"""
|
|
59
|
+
super().__init__(
|
|
60
|
+
console, pkg_path, verbose, quiet, debug, use_incremental, git_service
|
|
61
|
+
)
|
|
62
|
+
self.show_progress = show_progress and not quiet
|
|
63
|
+
self.debug = debug
|
|
64
|
+
|
|
65
|
+
def execute_strategy(self, strategy: HookStrategy) -> HookExecutionResult:
|
|
66
|
+
"""Execute hook strategy with progress indicators.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
strategy: Hook strategy to execute
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Execution result with timing and status information
|
|
73
|
+
"""
|
|
74
|
+
start_time = time.time()
|
|
75
|
+
|
|
76
|
+
self._print_strategy_header(strategy)
|
|
77
|
+
|
|
78
|
+
if not self.show_progress:
|
|
79
|
+
# Fall back to base implementation without progress bars
|
|
80
|
+
return super().execute_strategy(strategy)
|
|
81
|
+
|
|
82
|
+
# Create progress bar context
|
|
83
|
+
with self._create_progress_bar() as progress:
|
|
84
|
+
# Add main task for overall progress
|
|
85
|
+
main_task = progress.add_task(
|
|
86
|
+
f"[cyan]Running {len(strategy.hooks)} hooks...",
|
|
87
|
+
total=len(strategy.hooks),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if strategy.parallel and len(strategy.hooks) > 1:
|
|
91
|
+
results = self._execute_parallel_with_progress(
|
|
92
|
+
strategy, progress, main_task
|
|
93
|
+
)
|
|
94
|
+
else:
|
|
95
|
+
results = self._execute_sequential_with_progress(
|
|
96
|
+
strategy, progress, main_task
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Handle retries (without progress bar to avoid confusion)
|
|
100
|
+
if strategy.retry_policy != RetryPolicy.NONE:
|
|
101
|
+
if not self.quiet:
|
|
102
|
+
self.console.print("\n[yellow]Retrying failed hooks...[/yellow]")
|
|
103
|
+
results = self._handle_retries(strategy, results)
|
|
104
|
+
|
|
105
|
+
total_duration = time.time() - start_time
|
|
106
|
+
success = all(r.status == "passed" for r in results)
|
|
107
|
+
|
|
108
|
+
# Calculate performance gain for the summary
|
|
109
|
+
performance_gain = 0.0 # Default value for progress executor
|
|
110
|
+
if not self.quiet:
|
|
111
|
+
self._print_summary(strategy, results, success, performance_gain)
|
|
112
|
+
|
|
113
|
+
return HookExecutionResult(
|
|
114
|
+
strategy_name=strategy.name,
|
|
115
|
+
results=results,
|
|
116
|
+
total_duration=total_duration,
|
|
117
|
+
success=success,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def _create_progress_bar(self) -> Progress:
|
|
121
|
+
"""Create configured progress bar with appropriate columns.
|
|
122
|
+
|
|
123
|
+
Progress bar width is constrained to respect console width by limiting
|
|
124
|
+
description text and using compact time format.
|
|
125
|
+
|
|
126
|
+
Logger output is suppressed at the source (logging level) to prevent
|
|
127
|
+
interference with progress bar updates.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Configured Progress instance
|
|
131
|
+
"""
|
|
132
|
+
return Progress(
|
|
133
|
+
SpinnerColumn(spinner_name="dots"),
|
|
134
|
+
TextColumn("[progress.description]{task.description}", justify="left"),
|
|
135
|
+
BarColumn(bar_width=20), # Fixed narrow bar to prevent overflow
|
|
136
|
+
TaskProgressColumn(),
|
|
137
|
+
MofNCompleteColumn(),
|
|
138
|
+
TimeElapsedColumn(),
|
|
139
|
+
TimeRemainingColumn(),
|
|
140
|
+
console=self.console,
|
|
141
|
+
transient=True, # Clear progress bar after completion (consistent with test progress)
|
|
142
|
+
refresh_per_second=10, # Smooth single-line updates
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def _execute_sequential_with_progress(
|
|
146
|
+
self,
|
|
147
|
+
strategy: HookStrategy,
|
|
148
|
+
progress: Progress,
|
|
149
|
+
main_task: t.Any,
|
|
150
|
+
) -> list[HookResult]:
|
|
151
|
+
"""Execute hooks sequentially with progress updates.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
strategy: Hook strategy to execute
|
|
155
|
+
progress: Progress bar context
|
|
156
|
+
main_task: Main progress task ID
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
List of hook execution results
|
|
160
|
+
"""
|
|
161
|
+
results: list[HookResult] = []
|
|
162
|
+
|
|
163
|
+
for hook in strategy.hooks:
|
|
164
|
+
# Update description to show current hook
|
|
165
|
+
progress.update(
|
|
166
|
+
main_task,
|
|
167
|
+
description=f"[cyan]Running {hook.name}...",
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
result = self.execute_single_hook(hook)
|
|
171
|
+
results.append(result)
|
|
172
|
+
|
|
173
|
+
# Update progress with completion
|
|
174
|
+
status_icon = "✅" if result.status == "passed" else "❌"
|
|
175
|
+
progress.update(
|
|
176
|
+
main_task,
|
|
177
|
+
advance=1,
|
|
178
|
+
description=f"[cyan]Completed {hook.name} {status_icon}",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
return results
|
|
182
|
+
|
|
183
|
+
def _execute_parallel_with_progress(
|
|
184
|
+
self,
|
|
185
|
+
strategy: HookStrategy,
|
|
186
|
+
progress: Progress,
|
|
187
|
+
main_task: t.Any,
|
|
188
|
+
) -> list[HookResult]:
|
|
189
|
+
"""Execute hooks in parallel with progress updates.
|
|
190
|
+
|
|
191
|
+
Formatting hooks run sequentially first, then analysis hooks run in parallel.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
strategy: Hook strategy to execute
|
|
195
|
+
progress: Progress bar context
|
|
196
|
+
main_task: Main progress task ID
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
List of hook execution results
|
|
200
|
+
"""
|
|
201
|
+
results: list[HookResult] = []
|
|
202
|
+
|
|
203
|
+
# Separate formatting hooks (must run sequentially)
|
|
204
|
+
formatting_hooks = [h for h in strategy.hooks if h.is_formatting]
|
|
205
|
+
other_hooks = [h for h in strategy.hooks if not h.is_formatting]
|
|
206
|
+
|
|
207
|
+
# Execute formatting hooks sequentially
|
|
208
|
+
for hook in formatting_hooks:
|
|
209
|
+
progress.update(
|
|
210
|
+
main_task,
|
|
211
|
+
description=f"[cyan]Running {hook.name}...",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
result = self.execute_single_hook(hook)
|
|
215
|
+
results.append(result)
|
|
216
|
+
|
|
217
|
+
status_icon = "✅" if result.status == "passed" else "❌"
|
|
218
|
+
progress.update(
|
|
219
|
+
main_task,
|
|
220
|
+
advance=1,
|
|
221
|
+
description=f"[cyan]Completed {hook.name} {status_icon}",
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Execute analysis hooks in parallel
|
|
225
|
+
if other_hooks:
|
|
226
|
+
progress.update(
|
|
227
|
+
main_task,
|
|
228
|
+
description=f"[cyan]Running {len(other_hooks)} hooks in parallel...",
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
with ThreadPoolExecutor(max_workers=strategy.max_workers) as executor:
|
|
232
|
+
future_to_hook = {
|
|
233
|
+
executor.submit(self.execute_single_hook, hook): hook
|
|
234
|
+
for hook in other_hooks
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
for future in as_completed(future_to_hook):
|
|
238
|
+
try:
|
|
239
|
+
result = future.result()
|
|
240
|
+
results.append(result)
|
|
241
|
+
|
|
242
|
+
status_icon = "✅" if result.status == "passed" else "❌"
|
|
243
|
+
progress.update(
|
|
244
|
+
main_task,
|
|
245
|
+
advance=1,
|
|
246
|
+
description=f"[cyan]Completed {result.name} {status_icon}",
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
except Exception as e:
|
|
250
|
+
hook = future_to_hook[future]
|
|
251
|
+
error_result = HookResult(
|
|
252
|
+
id=hook.name,
|
|
253
|
+
name=hook.name,
|
|
254
|
+
status="error",
|
|
255
|
+
duration=0.0,
|
|
256
|
+
issues_found=[str(e)],
|
|
257
|
+
issues_count=1, # Error counts as 1 issue
|
|
258
|
+
stage=hook.stage.value,
|
|
259
|
+
)
|
|
260
|
+
results.append(error_result)
|
|
261
|
+
|
|
262
|
+
progress.update(
|
|
263
|
+
main_task,
|
|
264
|
+
advance=1,
|
|
265
|
+
description=f"[cyan]Failed {hook.name} ❌",
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
return results
|
|
269
|
+
|
|
270
|
+
def _display_hook_result(self, result: HookResult) -> None:
|
|
271
|
+
"""Display hook result with emoji status.
|
|
272
|
+
|
|
273
|
+
When progress bars are disabled, always show inline hook status.
|
|
274
|
+
When progress bars are enabled, suppress inline output (progress bar shows it).
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
result: Hook execution result
|
|
278
|
+
"""
|
|
279
|
+
# Always show inline status when progress bars are disabled
|
|
280
|
+
# Skip inline output when progress bars are enabled (they show the status)
|
|
281
|
+
if not self.show_progress:
|
|
282
|
+
super()._display_hook_result(result)
|