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,1158 @@
|
|
|
1
|
+
"""Phase execution orchestration for workflow pipeline.
|
|
2
|
+
|
|
3
|
+
Executes individual workflow phases (config, quality, tests, hooks, publishing, commit).
|
|
4
|
+
Handles LSP configuration and integration with Zuban type checking server.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
import typing as t
|
|
13
|
+
from contextlib import suppress
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from acb.console import Console
|
|
17
|
+
from acb.depends import Inject, depends
|
|
18
|
+
|
|
19
|
+
from crackerjack.agents.base import AgentContext
|
|
20
|
+
from crackerjack.events import WorkflowEvent, WorkflowEventBus
|
|
21
|
+
from crackerjack.models.protocols import (
|
|
22
|
+
DebugServiceProtocol,
|
|
23
|
+
LoggerProtocol,
|
|
24
|
+
OptionsProtocol,
|
|
25
|
+
QualityIntelligenceProtocol,
|
|
26
|
+
)
|
|
27
|
+
from crackerjack.services.monitoring.performance_monitor import phase_monitor
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class WorkflowPhaseExecutor:
|
|
31
|
+
"""Executes individual workflow phases and manages LSP configuration.
|
|
32
|
+
|
|
33
|
+
This class handles:
|
|
34
|
+
- Phase execution (config, quality, testing, hooks, publishing, commit)
|
|
35
|
+
- LSP server lifecycle (initialization, configuration, cleanup)
|
|
36
|
+
- AI-assisted fixing workflows
|
|
37
|
+
- Phase monitoring and status tracking
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
@depends.inject
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
console: Inject[Console],
|
|
44
|
+
logger: Inject[LoggerProtocol],
|
|
45
|
+
pkg_path: Path,
|
|
46
|
+
debugger: Inject[DebugServiceProtocol],
|
|
47
|
+
quality_intelligence: Inject[QualityIntelligenceProtocol] | None = None,
|
|
48
|
+
) -> None:
|
|
49
|
+
"""Initialize phase executor.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
console: Console for user output
|
|
53
|
+
logger: Structured logger
|
|
54
|
+
pkg_path: Project root path
|
|
55
|
+
debugger: Debug service for workflow tracking
|
|
56
|
+
quality_intelligence: Optional quality intelligence service
|
|
57
|
+
"""
|
|
58
|
+
self.console = console
|
|
59
|
+
self.logger = logger
|
|
60
|
+
self.pkg_path = pkg_path
|
|
61
|
+
self.debugger = debugger
|
|
62
|
+
self._quality_intelligence = quality_intelligence
|
|
63
|
+
self._mcp_state_manager: t.Any = None
|
|
64
|
+
self._last_security_audit: t.Any = None
|
|
65
|
+
self._code_cleaning_complete = False
|
|
66
|
+
|
|
67
|
+
# These will be injected by orchestrator
|
|
68
|
+
self.session: t.Any = None
|
|
69
|
+
self.phases: t.Any = None
|
|
70
|
+
self._event_bus: WorkflowEventBus | None = None
|
|
71
|
+
|
|
72
|
+
def configure(
|
|
73
|
+
self, session: t.Any, phases: t.Any, event_bus: WorkflowEventBus | None = None
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Configure executor with required dependencies.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
session: Session coordinator
|
|
79
|
+
phases: Phase coordinator
|
|
80
|
+
event_bus: Optional event bus for workflow events
|
|
81
|
+
"""
|
|
82
|
+
self.session = session
|
|
83
|
+
self.phases = phases
|
|
84
|
+
self._event_bus = event_bus
|
|
85
|
+
|
|
86
|
+
# =============================================================================
|
|
87
|
+
# LSP CONFIGURATION METHODS (8 methods)
|
|
88
|
+
# =============================================================================
|
|
89
|
+
|
|
90
|
+
def _configure_session_cleanup(self, options: OptionsProtocol) -> None:
|
|
91
|
+
"""Configure session cleanup behavior."""
|
|
92
|
+
if hasattr(options, "cleanup"):
|
|
93
|
+
self.session.set_cleanup_config(options.cleanup)
|
|
94
|
+
|
|
95
|
+
def _initialize_zuban_lsp(self, options: OptionsProtocol) -> None:
|
|
96
|
+
"""Initialize Zuban LSP server if not disabled."""
|
|
97
|
+
if self._should_skip_zuban_lsp(options):
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
if self._is_zuban_lsp_already_running():
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
self._start_zuban_lsp_server(options)
|
|
104
|
+
|
|
105
|
+
def _should_skip_zuban_lsp(self, options: OptionsProtocol) -> bool:
|
|
106
|
+
"""Check if Zuban LSP server should be skipped."""
|
|
107
|
+
if getattr(options, "no_zuban_lsp", False):
|
|
108
|
+
self.logger.debug("Zuban LSP server disabled by --no-zuban-lsp flag")
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
config = getattr(options, "zuban_lsp", None)
|
|
112
|
+
if config and not config.enabled:
|
|
113
|
+
self.logger.debug("Zuban LSP server disabled in configuration")
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
if config and not config.auto_start:
|
|
117
|
+
self.logger.debug("Zuban LSP server auto-start disabled in configuration")
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
def _is_zuban_lsp_already_running(self) -> bool:
|
|
123
|
+
"""Check if LSP server is already running to avoid duplicates."""
|
|
124
|
+
from crackerjack.services.server_manager import find_zuban_lsp_processes
|
|
125
|
+
|
|
126
|
+
existing_processes = find_zuban_lsp_processes()
|
|
127
|
+
if existing_processes:
|
|
128
|
+
self.logger.debug(
|
|
129
|
+
f"Zuban LSP server already running (PID: {existing_processes[0]['pid']})"
|
|
130
|
+
)
|
|
131
|
+
return True
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
def _start_zuban_lsp_server(self, options: OptionsProtocol) -> None:
|
|
135
|
+
"""Start the Zuban LSP server in background."""
|
|
136
|
+
try:
|
|
137
|
+
config = getattr(options, "zuban_lsp", None)
|
|
138
|
+
zuban_lsp_port, zuban_lsp_mode = self._get_zuban_lsp_config(options, config)
|
|
139
|
+
|
|
140
|
+
cmd = [
|
|
141
|
+
sys.executable,
|
|
142
|
+
"-m",
|
|
143
|
+
"crackerjack",
|
|
144
|
+
"--start-zuban-lsp",
|
|
145
|
+
"--zuban-lsp-port",
|
|
146
|
+
str(zuban_lsp_port),
|
|
147
|
+
"--zuban-lsp-mode",
|
|
148
|
+
zuban_lsp_mode,
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
subprocess.Popen(
|
|
152
|
+
cmd,
|
|
153
|
+
stdout=subprocess.DEVNULL,
|
|
154
|
+
stderr=subprocess.DEVNULL,
|
|
155
|
+
start_new_session=True,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
self.logger.info(
|
|
159
|
+
f"Auto-started Zuban LSP server on port {zuban_lsp_port} ({zuban_lsp_mode} mode)"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
except Exception as e:
|
|
163
|
+
self.logger.warning(f"Failed to auto-start Zuban LSP server: {e}")
|
|
164
|
+
|
|
165
|
+
def _get_zuban_lsp_config(
|
|
166
|
+
self, options: OptionsProtocol, config: t.Any
|
|
167
|
+
) -> tuple[int, str]:
|
|
168
|
+
"""Get Zuban LSP configuration values."""
|
|
169
|
+
if config:
|
|
170
|
+
return config.port, config.mode
|
|
171
|
+
return (
|
|
172
|
+
getattr(options, "zuban_lsp_port", 8677),
|
|
173
|
+
getattr(options, "zuban_lsp_mode", "stdio"),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def _configure_hook_manager_lsp(self, options: OptionsProtocol) -> None:
|
|
177
|
+
"""Configure hook manager with LSP optimization settings."""
|
|
178
|
+
# Check if LSP hooks are enabled
|
|
179
|
+
enable_lsp_hooks = getattr(options, "enable_lsp_hooks", False)
|
|
180
|
+
|
|
181
|
+
# Configure the hook manager
|
|
182
|
+
hook_manager = self.phases.hook_manager
|
|
183
|
+
if hasattr(hook_manager, "configure_lsp_optimization"):
|
|
184
|
+
hook_manager.configure_lsp_optimization(enable_lsp_hooks)
|
|
185
|
+
|
|
186
|
+
if enable_lsp_hooks and not getattr(options, "no_zuban_lsp", False):
|
|
187
|
+
self.console.print(
|
|
188
|
+
"🔍 LSP-optimized hook execution enabled for faster type checking",
|
|
189
|
+
style="blue",
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def _register_lsp_cleanup_handler(self, options: OptionsProtocol) -> None:
|
|
193
|
+
"""Register cleanup handler to stop LSP server when workflow completes."""
|
|
194
|
+
# Get configuration to check if we should handle LSP cleanup
|
|
195
|
+
config = getattr(options, "zuban_lsp", None)
|
|
196
|
+
if config and not config.enabled:
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
if getattr(options, "no_zuban_lsp", False):
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
def cleanup_lsp_server() -> None:
|
|
203
|
+
"""Cleanup function to gracefully stop LSP server if it was auto-started."""
|
|
204
|
+
try:
|
|
205
|
+
from crackerjack.services.server_manager import (
|
|
206
|
+
find_zuban_lsp_processes,
|
|
207
|
+
stop_process,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
lsp_processes = find_zuban_lsp_processes()
|
|
211
|
+
if lsp_processes:
|
|
212
|
+
for proc in lsp_processes:
|
|
213
|
+
self.logger.debug(
|
|
214
|
+
f"Stopping auto-started Zuban LSP server (PID: {proc['pid']})"
|
|
215
|
+
)
|
|
216
|
+
stop_process(proc["pid"])
|
|
217
|
+
|
|
218
|
+
except Exception as e:
|
|
219
|
+
self.logger.debug(f"Error during LSP cleanup: {e}")
|
|
220
|
+
|
|
221
|
+
# Register the cleanup handler with the session
|
|
222
|
+
self.session.register_cleanup(cleanup_lsp_server)
|
|
223
|
+
|
|
224
|
+
# =============================================================================
|
|
225
|
+
# PHASE EXECUTION METHODS (~51 methods)
|
|
226
|
+
# =============================================================================
|
|
227
|
+
|
|
228
|
+
async def _execute_workflow_phases(
|
|
229
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
230
|
+
) -> bool:
|
|
231
|
+
"""Execute all workflow phases in order.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
options: Workflow options
|
|
235
|
+
workflow_id: Unique workflow identifier
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
True if all phases succeeded, False otherwise
|
|
239
|
+
"""
|
|
240
|
+
# Execute configuration phase
|
|
241
|
+
config_success, success = await self._execute_config_phase(options, workflow_id)
|
|
242
|
+
|
|
243
|
+
# Execute quality phase
|
|
244
|
+
(
|
|
245
|
+
quality_success,
|
|
246
|
+
quality_phase_status,
|
|
247
|
+
) = await self._execute_quality_phase_with_events(options, workflow_id)
|
|
248
|
+
success = success and quality_phase_status
|
|
249
|
+
|
|
250
|
+
# If quality phase failed and we're in publishing mode, stop here
|
|
251
|
+
if not quality_success and self._is_publishing_workflow(options):
|
|
252
|
+
return False
|
|
253
|
+
|
|
254
|
+
# Execute publishing workflow if requested
|
|
255
|
+
(
|
|
256
|
+
publish_requested,
|
|
257
|
+
publishing_success,
|
|
258
|
+
) = await self._execute_publishing_if_requested(options, workflow_id)
|
|
259
|
+
if not publishing_success:
|
|
260
|
+
success = False
|
|
261
|
+
|
|
262
|
+
# Execute commit workflow independently if requested
|
|
263
|
+
commit_requested, commit_success = await self._execute_commit_if_requested(
|
|
264
|
+
options, workflow_id
|
|
265
|
+
)
|
|
266
|
+
if not commit_success:
|
|
267
|
+
success = False
|
|
268
|
+
|
|
269
|
+
# Only fail the overall workflow if publishing was explicitly requested and failed
|
|
270
|
+
if self._should_fail_on_publish_failure(publishing_success, options):
|
|
271
|
+
self.console.print(
|
|
272
|
+
"[red]❌ Publishing failed - overall workflow marked as failed[/red]"
|
|
273
|
+
)
|
|
274
|
+
return False
|
|
275
|
+
|
|
276
|
+
return success
|
|
277
|
+
|
|
278
|
+
async def _execute_config_phase(
|
|
279
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
280
|
+
) -> tuple[bool, bool]:
|
|
281
|
+
"""Execute the configuration phase and return success status and overall status."""
|
|
282
|
+
await self._publish_event(
|
|
283
|
+
WorkflowEvent.CONFIG_PHASE_STARTED,
|
|
284
|
+
{"workflow_id": workflow_id},
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
with phase_monitor(workflow_id, "configuration"):
|
|
288
|
+
config_success = self.phases.run_configuration_phase(options)
|
|
289
|
+
success = config_success
|
|
290
|
+
|
|
291
|
+
await self._publish_event(
|
|
292
|
+
WorkflowEvent.CONFIG_PHASE_COMPLETED,
|
|
293
|
+
{"workflow_id": workflow_id, "success": config_success},
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
return config_success, success
|
|
297
|
+
|
|
298
|
+
async def _execute_quality_phase_with_events(
|
|
299
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
300
|
+
) -> tuple[bool, bool]:
|
|
301
|
+
"""Execute the quality phase with events and return success and combined status."""
|
|
302
|
+
await self._publish_event(
|
|
303
|
+
WorkflowEvent.QUALITY_PHASE_STARTED,
|
|
304
|
+
{"workflow_id": workflow_id},
|
|
305
|
+
)
|
|
306
|
+
quality_success = await self._execute_quality_phase(options, workflow_id)
|
|
307
|
+
success = quality_success
|
|
308
|
+
|
|
309
|
+
await self._publish_event(
|
|
310
|
+
WorkflowEvent.QUALITY_PHASE_COMPLETED,
|
|
311
|
+
{"workflow_id": workflow_id, "success": quality_success},
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
return quality_success, success
|
|
315
|
+
|
|
316
|
+
async def _execute_quality_phase(
|
|
317
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
318
|
+
) -> bool:
|
|
319
|
+
"""Execute the quality phase based on options."""
|
|
320
|
+
# Use quality intelligence to make informed decisions about quality phase
|
|
321
|
+
if self._quality_intelligence:
|
|
322
|
+
quality_decision = await self._make_quality_intelligence_decision(options)
|
|
323
|
+
self.console.print(
|
|
324
|
+
f"[dim]🧠 Quality Intelligence: {quality_decision}[/dim]"
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
if hasattr(options, "fast") and options.fast:
|
|
328
|
+
return await self._run_fast_hooks_phase_monitored(options, workflow_id)
|
|
329
|
+
if hasattr(options, "fast_iteration") and options.fast_iteration:
|
|
330
|
+
return await self._run_fast_hooks_phase_monitored(options, workflow_id)
|
|
331
|
+
if hasattr(options, "comp") and options.comp:
|
|
332
|
+
return await self._run_comprehensive_hooks_phase_monitored(
|
|
333
|
+
options, workflow_id
|
|
334
|
+
)
|
|
335
|
+
if getattr(options, "test", False):
|
|
336
|
+
return await self._execute_test_workflow(options, workflow_id)
|
|
337
|
+
return await self._execute_standard_hooks_workflow_monitored(
|
|
338
|
+
options, workflow_id
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
async def _execute_test_workflow(
|
|
342
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
343
|
+
) -> bool:
|
|
344
|
+
"""Execute test workflow with optional AI fixing."""
|
|
345
|
+
iteration = self._start_iteration_tracking(options)
|
|
346
|
+
|
|
347
|
+
if not await self._execute_initial_phases(options, workflow_id, iteration):
|
|
348
|
+
return False
|
|
349
|
+
|
|
350
|
+
(
|
|
351
|
+
testing_passed,
|
|
352
|
+
comprehensive_passed,
|
|
353
|
+
) = await self._run_main_quality_phases_async(options, workflow_id)
|
|
354
|
+
|
|
355
|
+
return await self._handle_ai_workflow_completion(
|
|
356
|
+
options, iteration, testing_passed, comprehensive_passed, workflow_id
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
async def _execute_initial_phases(
|
|
360
|
+
self, options: OptionsProtocol, workflow_id: str, iteration: int
|
|
361
|
+
) -> bool:
|
|
362
|
+
"""Execute initial fast hooks phase."""
|
|
363
|
+
with phase_monitor(workflow_id, "fast_hooks") as monitor:
|
|
364
|
+
if not await self._run_initial_fast_hooks_async(
|
|
365
|
+
options, iteration, monitor
|
|
366
|
+
):
|
|
367
|
+
return False
|
|
368
|
+
|
|
369
|
+
return self._execute_optional_cleaning_phase(options)
|
|
370
|
+
|
|
371
|
+
def _execute_optional_cleaning_phase(self, options: OptionsProtocol) -> bool:
|
|
372
|
+
"""Execute code cleaning phase if requested."""
|
|
373
|
+
if not getattr(options, "clean", False):
|
|
374
|
+
return True
|
|
375
|
+
|
|
376
|
+
if not self._run_code_cleaning_phase(options):
|
|
377
|
+
return False
|
|
378
|
+
|
|
379
|
+
if not self._run_post_cleaning_fast_hooks(options):
|
|
380
|
+
return False
|
|
381
|
+
|
|
382
|
+
self._mark_code_cleaning_complete()
|
|
383
|
+
return True
|
|
384
|
+
|
|
385
|
+
async def _execute_publishing_if_requested(
|
|
386
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
387
|
+
) -> tuple[bool, bool]:
|
|
388
|
+
"""Execute publishing phase if requested."""
|
|
389
|
+
publish_requested = bool(
|
|
390
|
+
getattr(options, "publish", False) or getattr(options, "all", False)
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
if publish_requested:
|
|
394
|
+
await self._publish_event(
|
|
395
|
+
WorkflowEvent.PUBLISH_PHASE_STARTED,
|
|
396
|
+
{"workflow_id": workflow_id},
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
publishing_success = await self._execute_publishing_workflow(
|
|
400
|
+
options, workflow_id
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
if publish_requested:
|
|
404
|
+
await self._publish_event(
|
|
405
|
+
WorkflowEvent.PUBLISH_PHASE_COMPLETED,
|
|
406
|
+
{
|
|
407
|
+
"workflow_id": workflow_id,
|
|
408
|
+
"success": publishing_success,
|
|
409
|
+
},
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
return publish_requested, publishing_success
|
|
413
|
+
|
|
414
|
+
async def _execute_commit_if_requested(
|
|
415
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
416
|
+
) -> tuple[bool, bool]:
|
|
417
|
+
"""Execute commit phase if requested."""
|
|
418
|
+
commit_requested = bool(getattr(options, "commit", False))
|
|
419
|
+
|
|
420
|
+
if commit_requested:
|
|
421
|
+
await self._publish_event(
|
|
422
|
+
WorkflowEvent.COMMIT_PHASE_STARTED,
|
|
423
|
+
{"workflow_id": workflow_id},
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
commit_success = await self._execute_commit_workflow(options, workflow_id)
|
|
427
|
+
|
|
428
|
+
if commit_requested:
|
|
429
|
+
await self._publish_event(
|
|
430
|
+
WorkflowEvent.COMMIT_PHASE_COMPLETED,
|
|
431
|
+
{
|
|
432
|
+
"workflow_id": workflow_id,
|
|
433
|
+
"success": commit_success,
|
|
434
|
+
},
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
return commit_requested, commit_success
|
|
438
|
+
|
|
439
|
+
async def _execute_publishing_workflow(
|
|
440
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
441
|
+
) -> bool:
|
|
442
|
+
"""Execute publishing workflow."""
|
|
443
|
+
if not options.publish and not options.all:
|
|
444
|
+
return True
|
|
445
|
+
|
|
446
|
+
with phase_monitor(workflow_id, "publishing"):
|
|
447
|
+
if not self.phases.run_publishing_phase(options):
|
|
448
|
+
self.session.fail_task("workflow", "Publishing failed")
|
|
449
|
+
return False
|
|
450
|
+
return True
|
|
451
|
+
|
|
452
|
+
async def _execute_commit_workflow(
|
|
453
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
454
|
+
) -> bool:
|
|
455
|
+
"""Execute commit workflow."""
|
|
456
|
+
if not options.commit:
|
|
457
|
+
return True
|
|
458
|
+
|
|
459
|
+
with phase_monitor(workflow_id, "commit"):
|
|
460
|
+
if not self.phases.run_commit_phase(options):
|
|
461
|
+
return False
|
|
462
|
+
return True
|
|
463
|
+
|
|
464
|
+
def _is_publishing_workflow(self, options: OptionsProtocol) -> bool:
|
|
465
|
+
"""Check if this is a publishing workflow."""
|
|
466
|
+
return bool(
|
|
467
|
+
getattr(options, "publish", False) or getattr(options, "all", False)
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
def _should_fail_on_publish_failure(
|
|
471
|
+
self, publishing_success: bool, options: OptionsProtocol
|
|
472
|
+
) -> bool:
|
|
473
|
+
"""Check if the overall workflow should fail due to publishing failure."""
|
|
474
|
+
return not publishing_success and (
|
|
475
|
+
getattr(options, "publish", False) or getattr(options, "all", False)
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
# Phase execution helpers
|
|
479
|
+
|
|
480
|
+
def _start_iteration_tracking(self, options: OptionsProtocol) -> int:
|
|
481
|
+
"""Start tracking iteration for AI workflows."""
|
|
482
|
+
iteration = 1
|
|
483
|
+
if options.ai_agent and self._should_debug():
|
|
484
|
+
self.debugger.log_iteration_start(iteration)
|
|
485
|
+
return iteration
|
|
486
|
+
|
|
487
|
+
async def _run_main_quality_phases_async(
|
|
488
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
489
|
+
) -> tuple[bool, bool]:
|
|
490
|
+
"""Run testing and comprehensive hooks phases in parallel."""
|
|
491
|
+
testing_task = asyncio.create_task(
|
|
492
|
+
self._run_testing_phase_async(options, workflow_id)
|
|
493
|
+
)
|
|
494
|
+
comprehensive_task = asyncio.create_task(
|
|
495
|
+
self._run_comprehensive_hooks_phase_monitored(options, workflow_id)
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
results = await asyncio.gather(
|
|
499
|
+
testing_task, comprehensive_task, return_exceptions=True
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
testing_result, comprehensive_result = results
|
|
503
|
+
|
|
504
|
+
if isinstance(testing_result, Exception):
|
|
505
|
+
self.logger.error(f"Testing phase failed with exception: {testing_result}")
|
|
506
|
+
testing_passed = False
|
|
507
|
+
else:
|
|
508
|
+
testing_passed = bool(testing_result)
|
|
509
|
+
|
|
510
|
+
if isinstance(comprehensive_result, Exception):
|
|
511
|
+
self.logger.error(
|
|
512
|
+
f"Comprehensive hooks failed with exception: {comprehensive_result}"
|
|
513
|
+
)
|
|
514
|
+
comprehensive_passed = False
|
|
515
|
+
else:
|
|
516
|
+
comprehensive_passed = bool(comprehensive_result)
|
|
517
|
+
|
|
518
|
+
return testing_passed, comprehensive_passed
|
|
519
|
+
|
|
520
|
+
async def _handle_ai_workflow_completion(
|
|
521
|
+
self,
|
|
522
|
+
options: OptionsProtocol,
|
|
523
|
+
iteration: int,
|
|
524
|
+
testing_passed: bool,
|
|
525
|
+
comprehensive_passed: bool,
|
|
526
|
+
workflow_id: str = "unknown",
|
|
527
|
+
) -> bool:
|
|
528
|
+
"""Handle workflow completion with optional AI fixing."""
|
|
529
|
+
if options.ai_agent:
|
|
530
|
+
return await self._handle_ai_agent_workflow(
|
|
531
|
+
options, iteration, testing_passed, comprehensive_passed, workflow_id
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
return await self._handle_standard_workflow(
|
|
535
|
+
options, iteration, testing_passed, comprehensive_passed
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
async def _handle_ai_agent_workflow(
|
|
539
|
+
self,
|
|
540
|
+
options: OptionsProtocol,
|
|
541
|
+
iteration: int,
|
|
542
|
+
testing_passed: bool,
|
|
543
|
+
comprehensive_passed: bool,
|
|
544
|
+
workflow_id: str = "unknown",
|
|
545
|
+
) -> bool:
|
|
546
|
+
"""Handle workflow with AI agent enabled."""
|
|
547
|
+
# Import security gates methods from workflow_security_gates
|
|
548
|
+
from .workflow_security_gates import WorkflowSecurityGates
|
|
549
|
+
|
|
550
|
+
security_gates = WorkflowSecurityGates(
|
|
551
|
+
console=self.console,
|
|
552
|
+
logger=self.logger,
|
|
553
|
+
session=self.session,
|
|
554
|
+
debugger=self.debugger,
|
|
555
|
+
)
|
|
556
|
+
security_gates.configure(self.phases, self._execute_ai_fixing_workflow)
|
|
557
|
+
|
|
558
|
+
if not await security_gates.process_security_gates(options):
|
|
559
|
+
return False
|
|
560
|
+
|
|
561
|
+
needs_ai_fixing = self._determine_ai_fixing_needed(
|
|
562
|
+
testing_passed, comprehensive_passed, bool(options.publish or options.all)
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
if needs_ai_fixing:
|
|
566
|
+
return await self._execute_ai_fixing_workflow(options, iteration)
|
|
567
|
+
|
|
568
|
+
return self._finalize_ai_workflow_success(
|
|
569
|
+
options, iteration, testing_passed, comprehensive_passed
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
async def _execute_ai_fixing_workflow(
|
|
573
|
+
self, options: OptionsProtocol, iteration: int
|
|
574
|
+
) -> bool:
|
|
575
|
+
"""Execute AI fixing workflow."""
|
|
576
|
+
# Import AI coordinator methods from workflow_ai_coordinator
|
|
577
|
+
from .workflow_ai_coordinator import WorkflowAICoordinator
|
|
578
|
+
|
|
579
|
+
ai_coordinator = WorkflowAICoordinator(
|
|
580
|
+
console=self.console,
|
|
581
|
+
logger=self.logger,
|
|
582
|
+
pkg_path=self.pkg_path,
|
|
583
|
+
debugger=self.debugger,
|
|
584
|
+
)
|
|
585
|
+
ai_coordinator.configure(self.session, self.phases, self._mcp_state_manager)
|
|
586
|
+
|
|
587
|
+
success = await ai_coordinator.run_ai_agent_fixing_phase(options)
|
|
588
|
+
if self._should_debug():
|
|
589
|
+
self.debugger.log_iteration_end(iteration, success)
|
|
590
|
+
return success
|
|
591
|
+
|
|
592
|
+
def _finalize_ai_workflow_success(
|
|
593
|
+
self,
|
|
594
|
+
options: OptionsProtocol,
|
|
595
|
+
iteration: int,
|
|
596
|
+
testing_passed: bool,
|
|
597
|
+
comprehensive_passed: bool,
|
|
598
|
+
) -> bool:
|
|
599
|
+
"""Finalize AI workflow when no fixing is needed."""
|
|
600
|
+
publishing_requested = bool(options.publish or options.all)
|
|
601
|
+
|
|
602
|
+
final_success = self._determine_workflow_success(
|
|
603
|
+
testing_passed, comprehensive_passed, publishing_requested
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
self._show_partial_success_warning_if_needed(
|
|
607
|
+
publishing_requested, final_success, testing_passed, comprehensive_passed
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
if self._should_debug():
|
|
611
|
+
self.debugger.log_iteration_end(iteration, final_success)
|
|
612
|
+
|
|
613
|
+
return final_success
|
|
614
|
+
|
|
615
|
+
def _show_partial_success_warning_if_needed(
|
|
616
|
+
self,
|
|
617
|
+
publishing_requested: bool,
|
|
618
|
+
final_success: bool,
|
|
619
|
+
testing_passed: bool,
|
|
620
|
+
comprehensive_passed: bool,
|
|
621
|
+
) -> None:
|
|
622
|
+
"""Show warning if workflow succeeded with partial quality."""
|
|
623
|
+
should_show_warning = (
|
|
624
|
+
publishing_requested
|
|
625
|
+
and final_success
|
|
626
|
+
and not (testing_passed and comprehensive_passed)
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
if should_show_warning:
|
|
630
|
+
self._show_security_audit_warning()
|
|
631
|
+
|
|
632
|
+
async def _handle_standard_workflow(
|
|
633
|
+
self,
|
|
634
|
+
options: OptionsProtocol,
|
|
635
|
+
iteration: int,
|
|
636
|
+
testing_passed: bool,
|
|
637
|
+
comprehensive_passed: bool,
|
|
638
|
+
) -> bool:
|
|
639
|
+
"""Handle standard workflow without AI agent."""
|
|
640
|
+
# Import security gates methods
|
|
641
|
+
from .workflow_security_gates import WorkflowSecurityGates
|
|
642
|
+
|
|
643
|
+
security_gates = WorkflowSecurityGates(
|
|
644
|
+
console=self.console,
|
|
645
|
+
logger=self.logger,
|
|
646
|
+
session=self.session,
|
|
647
|
+
debugger=self.debugger,
|
|
648
|
+
)
|
|
649
|
+
security_gates.configure(self.phases, None)
|
|
650
|
+
|
|
651
|
+
(
|
|
652
|
+
publishing_requested,
|
|
653
|
+
security_blocks,
|
|
654
|
+
) = await security_gates.check_security_gates_for_publishing(options)
|
|
655
|
+
|
|
656
|
+
if publishing_requested and security_blocks:
|
|
657
|
+
return await security_gates.handle_security_gate_failure(options)
|
|
658
|
+
|
|
659
|
+
success = self._determine_workflow_success(
|
|
660
|
+
testing_passed,
|
|
661
|
+
comprehensive_passed,
|
|
662
|
+
publishing_requested,
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
if (
|
|
666
|
+
publishing_requested
|
|
667
|
+
and success
|
|
668
|
+
and not (testing_passed and comprehensive_passed)
|
|
669
|
+
):
|
|
670
|
+
self._show_security_audit_warning()
|
|
671
|
+
elif publishing_requested and not success:
|
|
672
|
+
self.console.print(
|
|
673
|
+
"[red]❌ Quality checks failed - cannot proceed to publishing[/red]"
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
if not success and getattr(options, "verbose", False):
|
|
677
|
+
self._show_verbose_failure_details(testing_passed, comprehensive_passed)
|
|
678
|
+
|
|
679
|
+
if options.ai_agent and self._should_debug():
|
|
680
|
+
self.debugger.log_iteration_end(iteration, success)
|
|
681
|
+
return success
|
|
682
|
+
|
|
683
|
+
def _determine_workflow_success(
|
|
684
|
+
self,
|
|
685
|
+
testing_passed: bool,
|
|
686
|
+
comprehensive_passed: bool,
|
|
687
|
+
publishing_requested: bool,
|
|
688
|
+
) -> bool:
|
|
689
|
+
"""Determine overall workflow success."""
|
|
690
|
+
if publishing_requested:
|
|
691
|
+
return testing_passed and comprehensive_passed
|
|
692
|
+
|
|
693
|
+
return testing_passed and comprehensive_passed
|
|
694
|
+
|
|
695
|
+
def _show_verbose_failure_details(
|
|
696
|
+
self, testing_passed: bool, comprehensive_passed: bool
|
|
697
|
+
) -> None:
|
|
698
|
+
"""Show detailed failure information in verbose mode."""
|
|
699
|
+
self.console.print(
|
|
700
|
+
f"[yellow]⚠️ Quality phase results - testing_passed: {testing_passed}, comprehensive_passed: {comprehensive_passed}[/yellow]"
|
|
701
|
+
)
|
|
702
|
+
if not testing_passed:
|
|
703
|
+
self.console.print("[yellow] → Tests reported failure[/yellow]")
|
|
704
|
+
if not comprehensive_passed:
|
|
705
|
+
self.console.print(
|
|
706
|
+
"[yellow] → Comprehensive hooks reported failure[/yellow]"
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
def _determine_ai_fixing_needed(
|
|
710
|
+
self,
|
|
711
|
+
testing_passed: bool,
|
|
712
|
+
comprehensive_passed: bool,
|
|
713
|
+
publishing_requested: bool,
|
|
714
|
+
) -> bool:
|
|
715
|
+
"""Determine if AI fixing is needed."""
|
|
716
|
+
if publishing_requested:
|
|
717
|
+
return not testing_passed or not comprehensive_passed
|
|
718
|
+
|
|
719
|
+
return not testing_passed or not comprehensive_passed
|
|
720
|
+
|
|
721
|
+
def _show_security_audit_warning(self) -> None:
|
|
722
|
+
"""Show security audit warning."""
|
|
723
|
+
audit_report = getattr(self, "_last_security_audit", None)
|
|
724
|
+
|
|
725
|
+
if audit_report:
|
|
726
|
+
self.console.print(
|
|
727
|
+
"[yellow]⚠️ SECURITY AUDIT: Proceeding with partial quality success[/yellow]"
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
for warning in audit_report.security_warnings:
|
|
731
|
+
if "CRITICAL" in warning:
|
|
732
|
+
self.console.print(f"[red]{warning}[/red]")
|
|
733
|
+
elif "HIGH" in warning:
|
|
734
|
+
self.console.print(f"[yellow]{warning}[/yellow]")
|
|
735
|
+
else:
|
|
736
|
+
self.console.print(f"[blue]{warning}[/blue]")
|
|
737
|
+
|
|
738
|
+
if audit_report.recommendations:
|
|
739
|
+
self.console.print("[bold]Security Recommendations: [/bold]")
|
|
740
|
+
for rec in audit_report.recommendations[:3]:
|
|
741
|
+
self.console.print(f"[dim]{rec}[/dim]")
|
|
742
|
+
else:
|
|
743
|
+
self.console.print(
|
|
744
|
+
"[yellow]⚠️ SECURITY AUDIT: Proceeding with partial quality success[/yellow]"
|
|
745
|
+
)
|
|
746
|
+
self.console.print(
|
|
747
|
+
"[yellow]✅ Security-critical checks (bandit, pyright, gitleaks) have passed[/yellow]"
|
|
748
|
+
)
|
|
749
|
+
self.console.print(
|
|
750
|
+
"[yellow]⚠️ Some non-critical quality checks failed - consider reviewing before production deployment[/yellow]"
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
# Individual phase execution methods
|
|
754
|
+
|
|
755
|
+
def _run_fast_hooks_phase(self, options: OptionsProtocol) -> bool:
|
|
756
|
+
"""Execute fast hooks phase."""
|
|
757
|
+
self._update_mcp_status("fast", "running")
|
|
758
|
+
|
|
759
|
+
if not self.phases.run_fast_hooks_only(options):
|
|
760
|
+
self.session.fail_task("workflow", "Fast hooks failed")
|
|
761
|
+
self._update_mcp_status("fast", "failed")
|
|
762
|
+
return False
|
|
763
|
+
|
|
764
|
+
self._update_mcp_status("fast", "completed")
|
|
765
|
+
return True
|
|
766
|
+
|
|
767
|
+
def _run_testing_phase(self, options: OptionsProtocol) -> bool:
|
|
768
|
+
"""Execute testing phase."""
|
|
769
|
+
self._update_mcp_status("tests", "running")
|
|
770
|
+
|
|
771
|
+
success = self.phases.run_testing_phase(options)
|
|
772
|
+
if not success:
|
|
773
|
+
self.session.fail_task("workflow", "Testing failed")
|
|
774
|
+
self._handle_test_failures()
|
|
775
|
+
self._update_mcp_status("tests", "failed")
|
|
776
|
+
|
|
777
|
+
else:
|
|
778
|
+
self._update_mcp_status("tests", "completed")
|
|
779
|
+
|
|
780
|
+
return success
|
|
781
|
+
|
|
782
|
+
def _run_comprehensive_hooks_phase(self, options: OptionsProtocol) -> bool:
|
|
783
|
+
"""Execute comprehensive hooks phase."""
|
|
784
|
+
self._update_mcp_status("comprehensive", "running")
|
|
785
|
+
|
|
786
|
+
success = self.phases.run_comprehensive_hooks_only(options)
|
|
787
|
+
if not success:
|
|
788
|
+
self.session.fail_task("comprehensive_hooks", "Comprehensive hooks failed")
|
|
789
|
+
self._update_mcp_status("comprehensive", "failed")
|
|
790
|
+
|
|
791
|
+
else:
|
|
792
|
+
self._update_mcp_status("comprehensive", "completed")
|
|
793
|
+
|
|
794
|
+
return success
|
|
795
|
+
|
|
796
|
+
def _run_code_cleaning_phase(self, options: OptionsProtocol) -> bool:
|
|
797
|
+
"""Execute code cleaning phase."""
|
|
798
|
+
self.console.print("\n[bold blue]🧹 Running Code Cleaning Phase...[/bold blue]")
|
|
799
|
+
|
|
800
|
+
success = self.phases.run_cleaning_phase(options)
|
|
801
|
+
if success:
|
|
802
|
+
self.console.print("[green]✅ Code cleaning completed successfully[/green]")
|
|
803
|
+
else:
|
|
804
|
+
self.console.print("[red]❌ Code cleaning failed[/red]")
|
|
805
|
+
self.session.fail_task("workflow", "Code cleaning phase failed")
|
|
806
|
+
|
|
807
|
+
return success
|
|
808
|
+
|
|
809
|
+
def _run_post_cleaning_fast_hooks(self, options: OptionsProtocol) -> bool:
|
|
810
|
+
"""Run fast hooks after code cleaning as sanity check."""
|
|
811
|
+
self.console.print(
|
|
812
|
+
"\n[bold cyan]🔍 Running Post-Cleaning Fast Hooks Sanity Check...[/bold cyan]"
|
|
813
|
+
)
|
|
814
|
+
# Allow a single re-run after cleaning by resetting the session guard
|
|
815
|
+
with suppress(Exception):
|
|
816
|
+
# Access PhaseCoordinator instance to reset its duplicate guard
|
|
817
|
+
setattr(self.phases, "_fast_hooks_started", False)
|
|
818
|
+
success = self._run_fast_hooks_phase(options)
|
|
819
|
+
if success:
|
|
820
|
+
self.console.print("[green]✅ Post-cleaning sanity check passed[/green]")
|
|
821
|
+
else:
|
|
822
|
+
self.console.print("[red]❌ Post-cleaning sanity check failed[/red]")
|
|
823
|
+
self.session.fail_task("workflow", "Post-cleaning fast hooks failed")
|
|
824
|
+
|
|
825
|
+
return success
|
|
826
|
+
|
|
827
|
+
# Async phase execution with monitoring
|
|
828
|
+
|
|
829
|
+
async def _run_initial_fast_hooks_async(
|
|
830
|
+
self, options: OptionsProtocol, iteration: int, monitor: t.Any
|
|
831
|
+
) -> bool:
|
|
832
|
+
"""Run initial fast hooks asynchronously."""
|
|
833
|
+
monitor.record_sequential_op()
|
|
834
|
+
fast_hooks_passed = self._run_fast_hooks_phase(options)
|
|
835
|
+
if not fast_hooks_passed:
|
|
836
|
+
if options.ai_agent and self._should_debug():
|
|
837
|
+
self.debugger.log_iteration_end(iteration, False)
|
|
838
|
+
return False
|
|
839
|
+
return True
|
|
840
|
+
|
|
841
|
+
async def _run_fast_hooks_phase_monitored(
|
|
842
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
843
|
+
) -> bool:
|
|
844
|
+
"""Run fast hooks phase with monitoring."""
|
|
845
|
+
iteration = self._start_iteration_tracking(options)
|
|
846
|
+
|
|
847
|
+
with phase_monitor(workflow_id, "fast_hooks") as monitor:
|
|
848
|
+
monitor.record_sequential_op()
|
|
849
|
+
# Run blocking sync method in thread to avoid blocking event loop
|
|
850
|
+
fast_hooks_success = await asyncio.to_thread(
|
|
851
|
+
self._run_fast_hooks_phase, options
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
# Delegate to AI workflow completion handler if AI agent enabled
|
|
855
|
+
if options.ai_agent:
|
|
856
|
+
return await self._handle_ai_workflow_completion(
|
|
857
|
+
options, iteration, fast_hooks_success, True, workflow_id
|
|
858
|
+
)
|
|
859
|
+
|
|
860
|
+
return fast_hooks_success
|
|
861
|
+
|
|
862
|
+
async def _run_comprehensive_hooks_phase_monitored(
|
|
863
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
864
|
+
) -> bool:
|
|
865
|
+
"""Run comprehensive hooks phase with monitoring."""
|
|
866
|
+
iteration = self._start_iteration_tracking(options)
|
|
867
|
+
|
|
868
|
+
with phase_monitor(workflow_id, "comprehensive_hooks") as monitor:
|
|
869
|
+
monitor.record_sequential_op()
|
|
870
|
+
# Run blocking sync method in thread to avoid blocking event loop
|
|
871
|
+
comprehensive_success = await asyncio.to_thread(
|
|
872
|
+
self._run_comprehensive_hooks_phase, options
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
# Delegate to AI workflow completion handler if AI agent enabled
|
|
876
|
+
if options.ai_agent:
|
|
877
|
+
return await self._handle_ai_workflow_completion(
|
|
878
|
+
options, iteration, True, comprehensive_success, workflow_id
|
|
879
|
+
)
|
|
880
|
+
|
|
881
|
+
return comprehensive_success
|
|
882
|
+
|
|
883
|
+
async def _run_testing_phase_async(
|
|
884
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
885
|
+
) -> bool:
|
|
886
|
+
"""Run testing phase asynchronously with optional coverage improvement."""
|
|
887
|
+
with phase_monitor(workflow_id, "testing") as monitor:
|
|
888
|
+
monitor.record_sequential_op()
|
|
889
|
+
test_result = self._run_testing_phase(options)
|
|
890
|
+
|
|
891
|
+
# Execute coverage improvement if boost_coverage is enabled and tests passed
|
|
892
|
+
if test_result and getattr(options, "boost_coverage", False):
|
|
893
|
+
await self._execute_coverage_improvement(options)
|
|
894
|
+
|
|
895
|
+
return test_result
|
|
896
|
+
|
|
897
|
+
async def _execute_coverage_improvement(self, options: OptionsProtocol) -> None:
|
|
898
|
+
"""Execute coverage improvement when boost_coverage is enabled."""
|
|
899
|
+
try:
|
|
900
|
+
from crackerjack.orchestration.coverage_improvement import (
|
|
901
|
+
create_coverage_improvement_orchestrator,
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
coverage_orchestrator = await create_coverage_improvement_orchestrator(
|
|
905
|
+
self.pkg_path
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
should_improve = await coverage_orchestrator.should_improve_coverage()
|
|
909
|
+
if not should_improve:
|
|
910
|
+
self.console.print(
|
|
911
|
+
"[dim]📈 Coverage at 100% - no improvement needed[/dim]"
|
|
912
|
+
)
|
|
913
|
+
return
|
|
914
|
+
|
|
915
|
+
# Create agent context for coverage improvement
|
|
916
|
+
agent_context = AgentContext(
|
|
917
|
+
project_path=self.pkg_path,
|
|
918
|
+
)
|
|
919
|
+
|
|
920
|
+
result = await coverage_orchestrator.execute_coverage_improvement(
|
|
921
|
+
agent_context
|
|
922
|
+
)
|
|
923
|
+
|
|
924
|
+
if result["status"] == "completed":
|
|
925
|
+
# Coverage orchestrator already printed success message
|
|
926
|
+
pass
|
|
927
|
+
elif result["status"] == "skipped":
|
|
928
|
+
self.console.print(
|
|
929
|
+
f"[dim]📈 Coverage improvement skipped: {result.get('reason', 'Unknown')}[/dim]"
|
|
930
|
+
)
|
|
931
|
+
else:
|
|
932
|
+
# Coverage orchestrator already printed failure message
|
|
933
|
+
pass
|
|
934
|
+
|
|
935
|
+
except Exception as e:
|
|
936
|
+
# Coverage orchestrator handles error display, only log for internal tracking
|
|
937
|
+
self.logger.warning(f"Coverage improvement error: {e}")
|
|
938
|
+
|
|
939
|
+
async def _execute_standard_hooks_workflow_monitored(
|
|
940
|
+
self, options: OptionsProtocol, workflow_id: str
|
|
941
|
+
) -> bool:
|
|
942
|
+
"""Execute standard hooks workflow with monitoring."""
|
|
943
|
+
iteration = self._start_iteration_tracking(options)
|
|
944
|
+
|
|
945
|
+
with phase_monitor(workflow_id, "hooks") as monitor:
|
|
946
|
+
self._update_hooks_status_running()
|
|
947
|
+
|
|
948
|
+
# Run blocking sync method in thread to avoid blocking event loop
|
|
949
|
+
fast_hooks_success = await asyncio.to_thread(
|
|
950
|
+
self._execute_monitored_fast_hooks_phase, options, monitor
|
|
951
|
+
)
|
|
952
|
+
if not fast_hooks_success:
|
|
953
|
+
self._handle_hooks_completion(False)
|
|
954
|
+
# If AI agent is enabled and hooks failed, delegate to AI workflow completion
|
|
955
|
+
if options.ai_agent:
|
|
956
|
+
return await self._handle_ai_workflow_completion(
|
|
957
|
+
options, iteration, fast_hooks_success, False, workflow_id
|
|
958
|
+
)
|
|
959
|
+
return False
|
|
960
|
+
|
|
961
|
+
# Run blocking sync method in thread to avoid blocking event loop
|
|
962
|
+
cleaning_success = await asyncio.to_thread(
|
|
963
|
+
self._execute_monitored_cleaning_phase, options
|
|
964
|
+
)
|
|
965
|
+
if not cleaning_success:
|
|
966
|
+
self._handle_hooks_completion(False)
|
|
967
|
+
return False
|
|
968
|
+
|
|
969
|
+
# Run blocking sync method in thread to avoid blocking event loop
|
|
970
|
+
comprehensive_success = await asyncio.to_thread(
|
|
971
|
+
self._execute_monitored_comprehensive_phase, options, monitor
|
|
972
|
+
)
|
|
973
|
+
|
|
974
|
+
hooks_success = fast_hooks_success and comprehensive_success
|
|
975
|
+
self._handle_hooks_completion(hooks_success)
|
|
976
|
+
|
|
977
|
+
# Delegate to AI workflow completion handler to check if AI fixing is needed
|
|
978
|
+
return await self._handle_ai_workflow_completion(
|
|
979
|
+
options,
|
|
980
|
+
iteration,
|
|
981
|
+
fast_hooks_success,
|
|
982
|
+
comprehensive_success,
|
|
983
|
+
workflow_id,
|
|
984
|
+
)
|
|
985
|
+
|
|
986
|
+
def _execute_monitored_fast_hooks_phase(
|
|
987
|
+
self, options: OptionsProtocol, monitor: t.Any
|
|
988
|
+
) -> bool:
|
|
989
|
+
"""Execute fast hooks with monitoring."""
|
|
990
|
+
fast_hooks_success = self._run_fast_hooks_phase(options)
|
|
991
|
+
if fast_hooks_success:
|
|
992
|
+
monitor.record_sequential_op()
|
|
993
|
+
return fast_hooks_success
|
|
994
|
+
|
|
995
|
+
def _execute_monitored_cleaning_phase(self, options: OptionsProtocol) -> bool:
|
|
996
|
+
"""Execute cleaning phase with monitoring."""
|
|
997
|
+
if not getattr(options, "clean", False):
|
|
998
|
+
return True
|
|
999
|
+
|
|
1000
|
+
if not self._run_code_cleaning_phase(options):
|
|
1001
|
+
return False
|
|
1002
|
+
|
|
1003
|
+
if not self._run_post_cleaning_fast_hooks(options):
|
|
1004
|
+
return False
|
|
1005
|
+
|
|
1006
|
+
self._mark_code_cleaning_complete()
|
|
1007
|
+
return True
|
|
1008
|
+
|
|
1009
|
+
def _execute_monitored_comprehensive_phase(
|
|
1010
|
+
self, options: OptionsProtocol, monitor: t.Any
|
|
1011
|
+
) -> bool:
|
|
1012
|
+
"""Execute comprehensive hooks with monitoring."""
|
|
1013
|
+
comprehensive_success = self._run_comprehensive_hooks_phase(options)
|
|
1014
|
+
if comprehensive_success:
|
|
1015
|
+
monitor.record_sequential_op()
|
|
1016
|
+
return comprehensive_success
|
|
1017
|
+
|
|
1018
|
+
# Quality intelligence methods
|
|
1019
|
+
|
|
1020
|
+
async def _make_quality_intelligence_decision(
|
|
1021
|
+
self, options: OptionsProtocol
|
|
1022
|
+
) -> str:
|
|
1023
|
+
"""Use quality intelligence to make informed decisions about workflow execution."""
|
|
1024
|
+
try:
|
|
1025
|
+
if not self._quality_intelligence:
|
|
1026
|
+
return "Quality intelligence not available"
|
|
1027
|
+
|
|
1028
|
+
anomalies = await self._quality_intelligence.detect_anomalies_async()
|
|
1029
|
+
patterns = await self._quality_intelligence.identify_patterns_async()
|
|
1030
|
+
|
|
1031
|
+
recommendations = self._build_quality_recommendations(anomalies, patterns)
|
|
1032
|
+
return "; ".join(recommendations)
|
|
1033
|
+
|
|
1034
|
+
except Exception as e:
|
|
1035
|
+
return f"Quality intelligence analysis failed: {str(e)[:50]}..."
|
|
1036
|
+
|
|
1037
|
+
def _build_quality_recommendations(
|
|
1038
|
+
self, anomalies: t.Any, patterns: t.Any
|
|
1039
|
+
) -> list[str]:
|
|
1040
|
+
"""Build quality recommendations based on anomalies and patterns."""
|
|
1041
|
+
recommendations = []
|
|
1042
|
+
|
|
1043
|
+
if anomalies:
|
|
1044
|
+
recommendations.extend(self._analyze_anomalies(anomalies))
|
|
1045
|
+
|
|
1046
|
+
if patterns:
|
|
1047
|
+
recommendations.extend(self._analyze_patterns(patterns))
|
|
1048
|
+
|
|
1049
|
+
if not recommendations:
|
|
1050
|
+
recommendations.append("baseline quality analysis active")
|
|
1051
|
+
|
|
1052
|
+
return recommendations
|
|
1053
|
+
|
|
1054
|
+
def _analyze_anomalies(self, anomalies: t.Any) -> list[str]:
|
|
1055
|
+
"""Analyze anomalies and return recommendations."""
|
|
1056
|
+
high_severity_anomalies = [
|
|
1057
|
+
a for a in anomalies if a.severity.name in ("CRITICAL", "HIGH")
|
|
1058
|
+
]
|
|
1059
|
+
|
|
1060
|
+
if high_severity_anomalies:
|
|
1061
|
+
return ["comprehensive analysis recommended due to quality anomalies"]
|
|
1062
|
+
return ["standard quality checks sufficient"]
|
|
1063
|
+
|
|
1064
|
+
def _analyze_patterns(self, patterns: t.Any) -> list[str]:
|
|
1065
|
+
"""Analyze patterns and return recommendations."""
|
|
1066
|
+
improving_patterns = [
|
|
1067
|
+
p for p in patterns if p.trend_direction.name == "IMPROVING"
|
|
1068
|
+
]
|
|
1069
|
+
|
|
1070
|
+
if improving_patterns:
|
|
1071
|
+
return ["quality trending upward"]
|
|
1072
|
+
return ["quality monitoring active"]
|
|
1073
|
+
|
|
1074
|
+
# Helper methods
|
|
1075
|
+
|
|
1076
|
+
def _update_mcp_status(self, stage: str, status: str) -> None:
|
|
1077
|
+
"""Update MCP state manager status."""
|
|
1078
|
+
if hasattr(self, "_mcp_state_manager") and self._mcp_state_manager:
|
|
1079
|
+
self._mcp_state_manager.update_stage_status(stage, status)
|
|
1080
|
+
|
|
1081
|
+
def _update_hooks_status_running(self) -> None:
|
|
1082
|
+
"""Update hooks status to running."""
|
|
1083
|
+
if self._has_mcp_state_manager():
|
|
1084
|
+
self._mcp_state_manager.update_stage_status("fast", "running")
|
|
1085
|
+
self._mcp_state_manager.update_stage_status("comprehensive", "running")
|
|
1086
|
+
|
|
1087
|
+
def _handle_hooks_completion(self, hooks_success: bool) -> None:
|
|
1088
|
+
"""Handle hooks completion status."""
|
|
1089
|
+
if not hooks_success:
|
|
1090
|
+
self.session.fail_task("workflow", "Hooks failed")
|
|
1091
|
+
self._update_hooks_status_failed()
|
|
1092
|
+
else:
|
|
1093
|
+
self._update_hooks_status_completed()
|
|
1094
|
+
|
|
1095
|
+
def _has_mcp_state_manager(self) -> bool:
|
|
1096
|
+
"""Check if MCP state manager is available."""
|
|
1097
|
+
return hasattr(self, "_mcp_state_manager") and self._mcp_state_manager
|
|
1098
|
+
|
|
1099
|
+
def _update_hooks_status_failed(self) -> None:
|
|
1100
|
+
"""Update hooks status to failed."""
|
|
1101
|
+
if self._has_mcp_state_manager():
|
|
1102
|
+
self._mcp_state_manager.update_stage_status("fast", "failed")
|
|
1103
|
+
self._mcp_state_manager.update_stage_status("comprehensive", "failed")
|
|
1104
|
+
|
|
1105
|
+
def _update_hooks_status_completed(self) -> None:
|
|
1106
|
+
"""Update hooks status to completed."""
|
|
1107
|
+
if self._has_mcp_state_manager():
|
|
1108
|
+
self._mcp_state_manager.update_stage_status("fast", "completed")
|
|
1109
|
+
self._mcp_state_manager.update_stage_status("comprehensive", "completed")
|
|
1110
|
+
|
|
1111
|
+
def _has_code_cleaning_run(self) -> bool:
|
|
1112
|
+
"""Check if code cleaning has been run."""
|
|
1113
|
+
return getattr(self, "_code_cleaning_complete", False)
|
|
1114
|
+
|
|
1115
|
+
def _mark_code_cleaning_complete(self) -> None:
|
|
1116
|
+
"""Mark code cleaning as complete."""
|
|
1117
|
+
self._code_cleaning_complete = True
|
|
1118
|
+
|
|
1119
|
+
def _handle_test_failures(self) -> None:
|
|
1120
|
+
"""Handle test failures by logging to MCP state."""
|
|
1121
|
+
if not (hasattr(self, "_mcp_state_manager") and self._mcp_state_manager):
|
|
1122
|
+
return
|
|
1123
|
+
|
|
1124
|
+
test_manager = self.phases.test_manager
|
|
1125
|
+
if not hasattr(test_manager, "get_test_failures"):
|
|
1126
|
+
return
|
|
1127
|
+
|
|
1128
|
+
failures = test_manager.get_test_failures()
|
|
1129
|
+
|
|
1130
|
+
if self._should_debug():
|
|
1131
|
+
self.debugger.log_test_failures(len(failures))
|
|
1132
|
+
|
|
1133
|
+
from crackerjack.mcp.state import Issue, Priority
|
|
1134
|
+
|
|
1135
|
+
for i, failure in enumerate(failures[:10]):
|
|
1136
|
+
issue = Issue(
|
|
1137
|
+
id=f"test_failure_{i}",
|
|
1138
|
+
type="test_failure",
|
|
1139
|
+
message=failure.strip(),
|
|
1140
|
+
file_path="tests/",
|
|
1141
|
+
priority=Priority.HIGH,
|
|
1142
|
+
stage="tests",
|
|
1143
|
+
auto_fixable=False,
|
|
1144
|
+
)
|
|
1145
|
+
self._mcp_state_manager.add_issue(issue)
|
|
1146
|
+
|
|
1147
|
+
def _should_debug(self) -> bool:
|
|
1148
|
+
"""Check if debug mode is enabled."""
|
|
1149
|
+
import os
|
|
1150
|
+
|
|
1151
|
+
return os.environ.get("AI_AGENT_DEBUG", "0") == "1"
|
|
1152
|
+
|
|
1153
|
+
async def _publish_event(
|
|
1154
|
+
self, event: WorkflowEvent, data: dict[str, t.Any]
|
|
1155
|
+
) -> None:
|
|
1156
|
+
"""Publish workflow event if event bus is available."""
|
|
1157
|
+
if self._event_bus:
|
|
1158
|
+
await self._event_bus.publish_async(event, data)
|