crackerjack 0.37.9__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 +30 -1
- crackerjack/__main__.py +342 -1263
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +27 -5
- 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/{rust_tool_manager.py → lsp/_manager.py} +3 -3
- crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
- crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
- 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 +40 -12
- crackerjack/agents/base.py +1 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +49 -53
- crackerjack/agents/dry_agent.py +187 -3
- 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 +6 -8
- 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/performance_agent.py +121 -1152
- crackerjack/agents/refactoring_agent.py +156 -655
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +19 -1605
- crackerjack/api.py +5 -7
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +1 -1
- crackerjack/cli/cache_handlers.py +23 -18
- crackerjack/cli/cache_handlers_enhanced.py +1 -4
- crackerjack/cli/facade.py +70 -8
- 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 +249 -49
- crackerjack/cli/interactive.py +8 -5
- crackerjack/cli/options.py +203 -110
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +60 -24
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +256 -0
- crackerjack/config/global_lock_config.py +191 -54
- crackerjack/config/hooks.py +188 -16
- 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/async_workflow_orchestrator.py +79 -53
- crackerjack/core/autofix_coordinator.py +22 -9
- crackerjack/core/container.py +10 -9
- crackerjack/core/enhanced_container.py +9 -9
- crackerjack/core/performance.py +1 -1
- crackerjack/core/performance_monitor.py +5 -3
- crackerjack/core/phase_coordinator.py +1018 -634
- crackerjack/core/proactive_workflow.py +3 -3
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +167 -23
- crackerjack/core/session_coordinator.py +187 -382
- crackerjack/core/timeout_manager.py +161 -44
- 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 +1247 -953
- 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/README.md +11 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/ai_templates.py +1 -1
- crackerjack/documentation/dual_output_generator.py +11 -9
- crackerjack/documentation/reference_generator.py +104 -59
- crackerjack/dynamic_config.py +52 -61
- crackerjack/errors.py +1 -1
- 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 +2 -0
- crackerjack/executors/async_hook_executor.py +539 -77
- crackerjack/executors/cached_hook_executor.py +3 -3
- crackerjack/executors/hook_executor.py +967 -102
- crackerjack/executors/hook_lock_manager.py +31 -22
- crackerjack/executors/individual_hook_executor.py +66 -32
- crackerjack/executors/lsp_aware_hook_executor.py +136 -57
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +23 -7
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +8 -9
- crackerjack/intelligence/README.md +557 -0
- crackerjack/interactive.py +37 -10
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/async_hook_manager.py +41 -57
- crackerjack/managers/hook_manager.py +449 -79
- crackerjack/managers/publish_manager.py +81 -36
- crackerjack/managers/test_command_builder.py +290 -12
- crackerjack/managers/test_executor.py +93 -8
- crackerjack/managers/test_manager.py +1082 -75
- crackerjack/managers/test_progress.py +118 -26
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/cache.py +25 -2
- crackerjack/mcp/client_runner.py +35 -18
- crackerjack/mcp/context.py +9 -9
- crackerjack/mcp/dashboard.py +24 -8
- crackerjack/mcp/enhanced_progress_monitor.py +34 -23
- crackerjack/mcp/file_monitor.py +27 -6
- crackerjack/mcp/progress_components.py +45 -34
- crackerjack/mcp/progress_monitor.py +6 -9
- crackerjack/mcp/rate_limiter.py +11 -7
- crackerjack/mcp/server.py +2 -0
- crackerjack/mcp/server_core.py +187 -55
- crackerjack/mcp/service_watchdog.py +12 -9
- crackerjack/mcp/task_manager.py +2 -2
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +2 -0
- crackerjack/mcp/tools/core_tools.py +75 -52
- crackerjack/mcp/tools/execution_tools.py +87 -31
- crackerjack/mcp/tools/intelligence_tools.py +2 -2
- crackerjack/mcp/tools/proactive_tools.py +1 -1
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +180 -132
- crackerjack/mcp/tools/workflow_executor.py +87 -46
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/app.py +11 -1
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +27 -4
- 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 +16 -2930
- crackerjack/mcp/websocket/server.py +1 -3
- crackerjack/mcp/websocket/websocket_handler.py +107 -6
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +10 -1
- crackerjack/models/config.py +639 -22
- crackerjack/models/config_adapter.py +6 -6
- crackerjack/models/protocols.py +1167 -23
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +19 -3
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/ai_agent_watchdog.py +5 -4
- crackerjack/monitoring/metrics_collector.py +4 -3
- crackerjack/monitoring/regression_prevention.py +4 -3
- crackerjack/monitoring/websocket_server.py +4 -241
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +20 -67
- 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 +13 -6
- crackerjack/orchestration/execution_strategies.py +6 -6
- 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 +1 -1
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/hooks.py +3 -2
- crackerjack/plugins/loader.py +3 -3
- crackerjack/plugins/managers.py +1 -1
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +8 -21
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
- 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/api_extractor.py +5 -3
- crackerjack/services/bounded_status_operations.py +45 -5
- crackerjack/services/cache.py +249 -318
- crackerjack/services/changelog_automation.py +7 -3
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +83 -39
- crackerjack/services/config_merge.py +9 -6
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +13 -26
- crackerjack/services/coverage_badge_service.py +6 -4
- crackerjack/services/coverage_ratchet.py +53 -27
- crackerjack/services/debug.py +18 -7
- crackerjack/services/dependency_analyzer.py +4 -4
- crackerjack/services/dependency_monitor.py +13 -13
- crackerjack/services/documentation_generator.py +4 -2
- crackerjack/services/documentation_service.py +62 -33
- crackerjack/services/enhanced_filesystem.py +81 -27
- crackerjack/services/enterprise_optimizer.py +1 -1
- crackerjack/services/error_pattern_analyzer.py +10 -10
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +5 -7
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +80 -109
- crackerjack/services/git.py +99 -5
- crackerjack/services/health_metrics.py +4 -6
- crackerjack/services/heatmap_generator.py +12 -3
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +101 -49
- crackerjack/services/log_manager.py +2 -2
- crackerjack/services/logging.py +120 -68
- crackerjack/services/lsp_client.py +12 -12
- crackerjack/services/memory_optimizer.py +27 -22
- 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/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
- crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
- crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
- crackerjack/services/parallel_executor.py +166 -55
- 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 +21 -8
- 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_baseline.py → quality/quality_baseline.py} +163 -2
- crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
- crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
- crackerjack/services/regex_patterns.py +58 -2987
- crackerjack/services/regex_utils.py +55 -29
- crackerjack/services/secure_status_formatter.py +42 -15
- crackerjack/services/secure_subprocess.py +35 -2
- crackerjack/services/security.py +16 -8
- crackerjack/services/server_manager.py +40 -51
- crackerjack/services/smart_scheduling.py +46 -6
- crackerjack/services/status_authentication.py +3 -3
- crackerjack/services/thread_safe_status_collector.py +1 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +9 -5
- crackerjack/services/unified_config.py +43 -351
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +6 -4
- crackerjack/services/version_checker.py +14 -8
- crackerjack/services/zuban_lsp_service.py +5 -4
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/init.md +2 -12
- crackerjack/slash_commands/run.md +84 -50
- 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_regex_patterns.py +7 -3
- crackerjack/ui/README.md +11 -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.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack/managers/test_manager_backup.py +0 -1075
- crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
- crackerjack/mixins/__init__.py +0 -3
- crackerjack/mixins/error_handling.py +0 -145
- crackerjack/services/config.py +0 -358
- crackerjack/ui/server_panels.py +0 -125
- crackerjack-0.37.9.dist-info/RECORD +0 -231
- /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
- /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,811 @@
|
|
|
1
|
+
"""ACB workflow action handlers for crackerjack.
|
|
2
|
+
|
|
3
|
+
This module provides action handler functions that wrap existing crackerjack
|
|
4
|
+
phase execution logic for use with ACB workflows. These handlers are registered
|
|
5
|
+
with the CrackerjackWorkflowEngine and called during workflow execution.
|
|
6
|
+
|
|
7
|
+
Action handlers follow ACB's async callable signature:
|
|
8
|
+
async def action_name(
|
|
9
|
+
context: dict[str, Any],
|
|
10
|
+
step_id: str,
|
|
11
|
+
**params
|
|
12
|
+
) -> Any
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
import time
|
|
19
|
+
import typing as t
|
|
20
|
+
|
|
21
|
+
from acb.depends import Inject, depends
|
|
22
|
+
|
|
23
|
+
from crackerjack.core.workflow_orchestrator import WorkflowPipeline
|
|
24
|
+
from crackerjack.events.workflow_bus import WorkflowEvent, WorkflowEventBus
|
|
25
|
+
from crackerjack.models.protocols import OptionsProtocol
|
|
26
|
+
|
|
27
|
+
if t.TYPE_CHECKING:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def run_configuration(
|
|
32
|
+
context: dict[str, t.Any],
|
|
33
|
+
step_id: str,
|
|
34
|
+
**params: t.Any,
|
|
35
|
+
) -> dict[str, t.Any]:
|
|
36
|
+
"""Execute configuration phase.
|
|
37
|
+
|
|
38
|
+
This action wraps the configuration phase logic, which typically
|
|
39
|
+
updates dependencies and performs pre-flight checks.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
context: Workflow execution context with "options" key
|
|
43
|
+
step_id: Step identifier (unused, for ACB compatibility)
|
|
44
|
+
**params: Additional step parameters
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
dict with configuration results
|
|
48
|
+
"""
|
|
49
|
+
options: OptionsProtocol = context.get("options") # type: ignore[assignment]
|
|
50
|
+
if not options:
|
|
51
|
+
msg = "Missing 'options' in workflow context"
|
|
52
|
+
raise ValueError(msg)
|
|
53
|
+
|
|
54
|
+
# Configuration phase is typically skipped in current implementation
|
|
55
|
+
# but we include it for workflow completeness
|
|
56
|
+
return {
|
|
57
|
+
"phase": "config",
|
|
58
|
+
"success": True,
|
|
59
|
+
"message": "Configuration phase skipped (no automated updates defined)",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@depends.inject # type: ignore[misc]
|
|
64
|
+
async def run_fast_hooks(
|
|
65
|
+
context: dict[str, t.Any],
|
|
66
|
+
step_id: str,
|
|
67
|
+
event_bus: Inject[WorkflowEventBus] = None,
|
|
68
|
+
**params: t.Any,
|
|
69
|
+
) -> dict[str, t.Any]:
|
|
70
|
+
"""Execute fast hooks phase.
|
|
71
|
+
|
|
72
|
+
Fast hooks include:
|
|
73
|
+
- Formatters (ruff, mdformat)
|
|
74
|
+
- Import sorting (ruff)
|
|
75
|
+
- Basic static analysis
|
|
76
|
+
|
|
77
|
+
This action wraps the existing fast hooks execution logic from
|
|
78
|
+
WorkflowPipeline passed explicitly in context.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
context: Workflow execution context with "options" and "pipeline" keys
|
|
82
|
+
step_id: Step identifier (unused, for ACB compatibility)
|
|
83
|
+
event_bus: WorkflowEventBus for event emission (injected)
|
|
84
|
+
**params: Additional step parameters
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
dict with fast hooks execution results
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
RuntimeError: If fast hooks execution fails
|
|
91
|
+
"""
|
|
92
|
+
options: OptionsProtocol = context.get("options") # type: ignore[assignment]
|
|
93
|
+
if not options:
|
|
94
|
+
msg = "Missing 'options' in workflow context"
|
|
95
|
+
raise ValueError(msg)
|
|
96
|
+
|
|
97
|
+
# Phase 4.1: Get pipeline from context instead of DI injection
|
|
98
|
+
pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
|
|
99
|
+
if not pipeline:
|
|
100
|
+
msg = "WorkflowPipeline not available in context"
|
|
101
|
+
raise RuntimeError(msg)
|
|
102
|
+
|
|
103
|
+
# Phase 7.2: Emit start event
|
|
104
|
+
start_time = time.time()
|
|
105
|
+
if event_bus:
|
|
106
|
+
await event_bus.publish(
|
|
107
|
+
WorkflowEvent.HOOK_STRATEGY_STARTED,
|
|
108
|
+
{"step_id": step_id, "strategy": "fast", "timestamp": start_time},
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
|
|
112
|
+
# Use asyncio.to_thread to avoid blocking event loop with synchronous operations
|
|
113
|
+
try:
|
|
114
|
+
success = await asyncio.to_thread(
|
|
115
|
+
pipeline._run_fast_hooks_phase,
|
|
116
|
+
options,
|
|
117
|
+
)
|
|
118
|
+
except Exception as exc:
|
|
119
|
+
# Phase 7.2: Emit failure event
|
|
120
|
+
if event_bus:
|
|
121
|
+
await event_bus.publish(
|
|
122
|
+
WorkflowEvent.HOOK_STRATEGY_FAILED,
|
|
123
|
+
{
|
|
124
|
+
"step_id": step_id,
|
|
125
|
+
"strategy": "fast",
|
|
126
|
+
"error": str(exc),
|
|
127
|
+
"timestamp": time.time(),
|
|
128
|
+
"duration": time.time() - start_time,
|
|
129
|
+
},
|
|
130
|
+
)
|
|
131
|
+
raise
|
|
132
|
+
|
|
133
|
+
if not success:
|
|
134
|
+
# Phase 7.2: Emit failure event
|
|
135
|
+
if event_bus:
|
|
136
|
+
await event_bus.publish(
|
|
137
|
+
WorkflowEvent.HOOK_STRATEGY_FAILED,
|
|
138
|
+
{
|
|
139
|
+
"step_id": step_id,
|
|
140
|
+
"strategy": "fast",
|
|
141
|
+
"timestamp": time.time(),
|
|
142
|
+
"duration": time.time() - start_time,
|
|
143
|
+
},
|
|
144
|
+
)
|
|
145
|
+
msg = "Fast hooks execution failed"
|
|
146
|
+
raise RuntimeError(msg)
|
|
147
|
+
|
|
148
|
+
# Phase 7.2: Emit completion event
|
|
149
|
+
if event_bus:
|
|
150
|
+
await event_bus.publish(
|
|
151
|
+
WorkflowEvent.HOOK_STRATEGY_COMPLETED,
|
|
152
|
+
{
|
|
153
|
+
"step_id": step_id,
|
|
154
|
+
"strategy": "fast",
|
|
155
|
+
"success": True,
|
|
156
|
+
"timestamp": time.time(),
|
|
157
|
+
"duration": time.time() - start_time,
|
|
158
|
+
},
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
"phase": "fast_hooks",
|
|
163
|
+
"success": True,
|
|
164
|
+
"message": "Fast hooks completed successfully",
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@depends.inject # type: ignore[misc]
|
|
169
|
+
async def run_code_cleaning(
|
|
170
|
+
context: dict[str, t.Any],
|
|
171
|
+
step_id: str,
|
|
172
|
+
event_bus: Inject[WorkflowEventBus] = None,
|
|
173
|
+
**params: t.Any,
|
|
174
|
+
) -> dict[str, t.Any]:
|
|
175
|
+
"""Execute code cleaning phase.
|
|
176
|
+
|
|
177
|
+
Code cleaning includes:
|
|
178
|
+
- Unused import removal
|
|
179
|
+
- Dead code elimination
|
|
180
|
+
- Code formatting fixes
|
|
181
|
+
|
|
182
|
+
This action wraps the existing cleaning execution logic from
|
|
183
|
+
WorkflowPipeline passed explicitly in context.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
context: Workflow execution context with "options" and "pipeline" keys
|
|
187
|
+
step_id: Step identifier (unused, for ACB compatibility)
|
|
188
|
+
event_bus: WorkflowEventBus for event emission (injected)
|
|
189
|
+
**params: Additional step parameters
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
dict with cleaning execution results (or skip result if strip_code=False)
|
|
193
|
+
|
|
194
|
+
Raises:
|
|
195
|
+
RuntimeError: If cleaning execution fails
|
|
196
|
+
"""
|
|
197
|
+
options: OptionsProtocol = context.get("options") # type: ignore[assignment]
|
|
198
|
+
if not options:
|
|
199
|
+
msg = "Missing 'options' in workflow context"
|
|
200
|
+
raise ValueError(msg)
|
|
201
|
+
|
|
202
|
+
# Skip code cleaning unless -x/--strip-code flag is set
|
|
203
|
+
if not getattr(options, "strip_code", False):
|
|
204
|
+
return {
|
|
205
|
+
"phase": "cleaning",
|
|
206
|
+
"success": True,
|
|
207
|
+
"skipped": True,
|
|
208
|
+
"reason": "Code cleaning only runs with -x/--strip-code flag",
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# Phase 4.1: Get pipeline from context instead of DI injection
|
|
212
|
+
pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
|
|
213
|
+
if not pipeline:
|
|
214
|
+
msg = "WorkflowPipeline not available in context"
|
|
215
|
+
raise RuntimeError(msg)
|
|
216
|
+
|
|
217
|
+
# Phase 7.2: Emit start event
|
|
218
|
+
start_time = time.time()
|
|
219
|
+
if event_bus:
|
|
220
|
+
await event_bus.publish(
|
|
221
|
+
WorkflowEvent.QUALITY_PHASE_STARTED,
|
|
222
|
+
{"step_id": step_id, "phase": "cleaning", "timestamp": start_time},
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
|
|
226
|
+
# Use asyncio.to_thread to avoid blocking event loop with synchronous operations
|
|
227
|
+
try:
|
|
228
|
+
success = await asyncio.to_thread(
|
|
229
|
+
pipeline._run_code_cleaning_phase,
|
|
230
|
+
options,
|
|
231
|
+
)
|
|
232
|
+
except Exception as exc:
|
|
233
|
+
# Phase 7.2: Emit failure event
|
|
234
|
+
if event_bus:
|
|
235
|
+
await event_bus.publish(
|
|
236
|
+
WorkflowEvent.WORKFLOW_FAILED,
|
|
237
|
+
{
|
|
238
|
+
"step_id": step_id,
|
|
239
|
+
"phase": "cleaning",
|
|
240
|
+
"error": str(exc),
|
|
241
|
+
"timestamp": time.time(),
|
|
242
|
+
"duration": time.time() - start_time,
|
|
243
|
+
},
|
|
244
|
+
)
|
|
245
|
+
raise
|
|
246
|
+
|
|
247
|
+
if not success:
|
|
248
|
+
# Phase 7.2: Emit failure event
|
|
249
|
+
if event_bus:
|
|
250
|
+
await event_bus.publish(
|
|
251
|
+
WorkflowEvent.WORKFLOW_FAILED,
|
|
252
|
+
{
|
|
253
|
+
"step_id": step_id,
|
|
254
|
+
"phase": "cleaning",
|
|
255
|
+
"timestamp": time.time(),
|
|
256
|
+
"duration": time.time() - start_time,
|
|
257
|
+
},
|
|
258
|
+
)
|
|
259
|
+
msg = "Code cleaning execution failed"
|
|
260
|
+
raise RuntimeError(msg)
|
|
261
|
+
|
|
262
|
+
# Phase 7.2: Emit completion event
|
|
263
|
+
if event_bus:
|
|
264
|
+
await event_bus.publish(
|
|
265
|
+
WorkflowEvent.QUALITY_PHASE_COMPLETED,
|
|
266
|
+
{
|
|
267
|
+
"step_id": step_id,
|
|
268
|
+
"phase": "cleaning",
|
|
269
|
+
"success": True,
|
|
270
|
+
"timestamp": time.time(),
|
|
271
|
+
"duration": time.time() - start_time,
|
|
272
|
+
},
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
"phase": "cleaning",
|
|
277
|
+
"success": True,
|
|
278
|
+
"message": "Code cleaning completed successfully",
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
@depends.inject # type: ignore[misc]
|
|
283
|
+
async def run_comprehensive_hooks(
|
|
284
|
+
context: dict[str, t.Any],
|
|
285
|
+
step_id: str,
|
|
286
|
+
event_bus: Inject[WorkflowEventBus] = None,
|
|
287
|
+
**params: t.Any,
|
|
288
|
+
) -> dict[str, t.Any]:
|
|
289
|
+
"""Execute comprehensive hooks phase.
|
|
290
|
+
|
|
291
|
+
Comprehensive hooks include:
|
|
292
|
+
- Type checking (zuban)
|
|
293
|
+
- Security scanning (bandit, gitleaks)
|
|
294
|
+
- Complexity analysis (skylos)
|
|
295
|
+
- Additional static analysis
|
|
296
|
+
|
|
297
|
+
This action wraps the existing comprehensive hooks execution logic from
|
|
298
|
+
WorkflowPipeline passed explicitly in context.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
context: Workflow execution context with "options" and "pipeline" keys
|
|
302
|
+
step_id: Step identifier (unused, for ACB compatibility)
|
|
303
|
+
event_bus: WorkflowEventBus for event emission (injected)
|
|
304
|
+
**params: Additional step parameters
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
dict with comprehensive hooks execution results
|
|
308
|
+
|
|
309
|
+
Raises:
|
|
310
|
+
RuntimeError: If comprehensive hooks execution fails
|
|
311
|
+
"""
|
|
312
|
+
options: OptionsProtocol = context.get("options") # type: ignore[assignment]
|
|
313
|
+
if not options:
|
|
314
|
+
msg = "Missing 'options' in workflow context"
|
|
315
|
+
raise ValueError(msg)
|
|
316
|
+
|
|
317
|
+
# Phase 4.1: Get pipeline from context instead of DI injection
|
|
318
|
+
pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
|
|
319
|
+
if not pipeline:
|
|
320
|
+
msg = "WorkflowPipeline not available in context"
|
|
321
|
+
raise RuntimeError(msg)
|
|
322
|
+
|
|
323
|
+
# Phase 7.2: Emit start event
|
|
324
|
+
start_time = time.time()
|
|
325
|
+
if event_bus:
|
|
326
|
+
await event_bus.publish(
|
|
327
|
+
WorkflowEvent.HOOK_STRATEGY_STARTED,
|
|
328
|
+
{"step_id": step_id, "strategy": "comprehensive", "timestamp": start_time},
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
# Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
|
|
332
|
+
# Use asyncio.to_thread to avoid blocking event loop with synchronous operations
|
|
333
|
+
try:
|
|
334
|
+
success = await asyncio.to_thread(
|
|
335
|
+
pipeline._run_comprehensive_hooks_phase,
|
|
336
|
+
options,
|
|
337
|
+
)
|
|
338
|
+
except Exception as exc:
|
|
339
|
+
# Phase 7.2: Emit failure event
|
|
340
|
+
if event_bus:
|
|
341
|
+
await event_bus.publish(
|
|
342
|
+
WorkflowEvent.HOOK_STRATEGY_FAILED,
|
|
343
|
+
{
|
|
344
|
+
"step_id": step_id,
|
|
345
|
+
"strategy": "comprehensive",
|
|
346
|
+
"error": str(exc),
|
|
347
|
+
"timestamp": time.time(),
|
|
348
|
+
"duration": time.time() - start_time,
|
|
349
|
+
},
|
|
350
|
+
)
|
|
351
|
+
raise
|
|
352
|
+
|
|
353
|
+
if not success:
|
|
354
|
+
# Phase 7.2: Emit failure event
|
|
355
|
+
if event_bus:
|
|
356
|
+
await event_bus.publish(
|
|
357
|
+
WorkflowEvent.HOOK_STRATEGY_FAILED,
|
|
358
|
+
{
|
|
359
|
+
"step_id": step_id,
|
|
360
|
+
"strategy": "comprehensive",
|
|
361
|
+
"timestamp": time.time(),
|
|
362
|
+
"duration": time.time() - start_time,
|
|
363
|
+
},
|
|
364
|
+
)
|
|
365
|
+
msg = "Comprehensive hooks execution failed"
|
|
366
|
+
raise RuntimeError(msg)
|
|
367
|
+
|
|
368
|
+
# Phase 7.2: Emit completion event
|
|
369
|
+
if event_bus:
|
|
370
|
+
await event_bus.publish(
|
|
371
|
+
WorkflowEvent.HOOK_STRATEGY_COMPLETED,
|
|
372
|
+
{
|
|
373
|
+
"step_id": step_id,
|
|
374
|
+
"strategy": "comprehensive",
|
|
375
|
+
"success": True,
|
|
376
|
+
"timestamp": time.time(),
|
|
377
|
+
"duration": time.time() - start_time,
|
|
378
|
+
},
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
"phase": "comprehensive",
|
|
383
|
+
"success": True,
|
|
384
|
+
"message": "Comprehensive hooks completed successfully",
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
async def _handle_test_exception(
|
|
389
|
+
event_bus: WorkflowEventBus | None,
|
|
390
|
+
step_id: str,
|
|
391
|
+
start_time: float,
|
|
392
|
+
exc: Exception,
|
|
393
|
+
) -> None:
|
|
394
|
+
"""Handle exceptions during test execution."""
|
|
395
|
+
# Phase 7.2: Emit failure event
|
|
396
|
+
if event_bus:
|
|
397
|
+
await event_bus.publish(
|
|
398
|
+
WorkflowEvent.WORKFLOW_FAILED,
|
|
399
|
+
{
|
|
400
|
+
"step_id": step_id,
|
|
401
|
+
"phase": "testing",
|
|
402
|
+
"error": str(exc),
|
|
403
|
+
"timestamp": time.time(),
|
|
404
|
+
"duration": time.time() - start_time,
|
|
405
|
+
},
|
|
406
|
+
)
|
|
407
|
+
# Log the exception details before re-raising
|
|
408
|
+
import traceback
|
|
409
|
+
|
|
410
|
+
print(f"❌ Test execution failed with exception: {exc}")
|
|
411
|
+
print(f"Traceback: {traceback.format_exc()}")
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
async def _handle_test_failure(
|
|
415
|
+
pipeline: WorkflowPipeline,
|
|
416
|
+
event_bus: WorkflowEventBus | None,
|
|
417
|
+
step_id: str,
|
|
418
|
+
start_time: float,
|
|
419
|
+
) -> None:
|
|
420
|
+
"""Handle test failure cases."""
|
|
421
|
+
# Phase 7.2: Emit failure event
|
|
422
|
+
if event_bus:
|
|
423
|
+
await event_bus.publish(
|
|
424
|
+
WorkflowEvent.WORKFLOW_FAILED,
|
|
425
|
+
{
|
|
426
|
+
"step_id": step_id,
|
|
427
|
+
"phase": "testing",
|
|
428
|
+
"timestamp": time.time(),
|
|
429
|
+
"duration": time.time() - start_time,
|
|
430
|
+
},
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
# Try to get test failure details from test manager
|
|
434
|
+
test_manager = pipeline.phases.test_manager
|
|
435
|
+
if hasattr(test_manager, "get_test_failures"):
|
|
436
|
+
failures = test_manager.get_test_failures()
|
|
437
|
+
if failures:
|
|
438
|
+
print("\n📋 Test failures detected:")
|
|
439
|
+
for i, failure in enumerate(failures[:10], 1): # Show first 10 failures
|
|
440
|
+
print(f" {i}. {failure.strip()}")
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
async def _handle_test_completion(
|
|
444
|
+
event_bus: WorkflowEventBus | None,
|
|
445
|
+
step_id: str,
|
|
446
|
+
start_time: float,
|
|
447
|
+
) -> None:
|
|
448
|
+
"""Handle test completion event."""
|
|
449
|
+
# Phase 7.2: Emit completion event
|
|
450
|
+
if event_bus:
|
|
451
|
+
await event_bus.publish(
|
|
452
|
+
WorkflowEvent.QUALITY_PHASE_COMPLETED,
|
|
453
|
+
{
|
|
454
|
+
"step_id": step_id,
|
|
455
|
+
"phase": "testing",
|
|
456
|
+
"success": True,
|
|
457
|
+
"timestamp": time.time(),
|
|
458
|
+
"duration": time.time() - start_time,
|
|
459
|
+
},
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
@depends.inject # type: ignore[misc]
|
|
464
|
+
async def run_test_workflow(
|
|
465
|
+
context: dict[str, t.Any],
|
|
466
|
+
step_id: str,
|
|
467
|
+
event_bus: Inject[WorkflowEventBus] = None,
|
|
468
|
+
**params: t.Any,
|
|
469
|
+
) -> dict[str, t.Any]:
|
|
470
|
+
"""Execute test workflow.
|
|
471
|
+
|
|
472
|
+
This action runs the test suite using pytest with configured options.
|
|
473
|
+
|
|
474
|
+
Args:
|
|
475
|
+
context: Workflow execution context with "options" and "pipeline" keys
|
|
476
|
+
step_id: Step identifier (unused, for ACB compatibility)
|
|
477
|
+
event_bus: WorkflowEventBus for event emission (injected)
|
|
478
|
+
**params: Additional step parameters
|
|
479
|
+
|
|
480
|
+
Returns:
|
|
481
|
+
dict with test execution results
|
|
482
|
+
|
|
483
|
+
Raises:
|
|
484
|
+
RuntimeError: If test execution fails
|
|
485
|
+
"""
|
|
486
|
+
options: OptionsProtocol = context.get("options") # type: ignore[assignment]
|
|
487
|
+
if not options:
|
|
488
|
+
msg = "Missing 'options' in workflow context"
|
|
489
|
+
raise ValueError(msg)
|
|
490
|
+
|
|
491
|
+
# Phase 4.1: Get pipeline from context instead of DI injection
|
|
492
|
+
pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
|
|
493
|
+
if not pipeline:
|
|
494
|
+
msg = "WorkflowPipeline not available in context"
|
|
495
|
+
raise RuntimeError(msg)
|
|
496
|
+
|
|
497
|
+
# Phase 7.2: Emit start event
|
|
498
|
+
start_time = time.time()
|
|
499
|
+
if event_bus:
|
|
500
|
+
await event_bus.publish(
|
|
501
|
+
WorkflowEvent.QUALITY_PHASE_STARTED,
|
|
502
|
+
{"step_id": step_id, "phase": "testing", "timestamp": start_time},
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
# Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
|
|
506
|
+
# Use asyncio.to_thread to avoid blocking event loop with synchronous operations
|
|
507
|
+
try:
|
|
508
|
+
success = await asyncio.to_thread(
|
|
509
|
+
pipeline._run_testing_phase,
|
|
510
|
+
options,
|
|
511
|
+
)
|
|
512
|
+
except Exception as exc:
|
|
513
|
+
await _handle_test_exception(event_bus, step_id, start_time, exc)
|
|
514
|
+
raise
|
|
515
|
+
|
|
516
|
+
if not success:
|
|
517
|
+
await _handle_test_failure(pipeline, event_bus, step_id, start_time)
|
|
518
|
+
msg = "Test workflow execution failed"
|
|
519
|
+
raise RuntimeError(msg)
|
|
520
|
+
|
|
521
|
+
await _handle_test_completion(event_bus, step_id, start_time)
|
|
522
|
+
|
|
523
|
+
return {
|
|
524
|
+
"phase": "test_workflow",
|
|
525
|
+
"success": True,
|
|
526
|
+
"message": "Test workflow completed successfully",
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
async def run_hook(
|
|
531
|
+
context: dict[str, t.Any],
|
|
532
|
+
step_id: str,
|
|
533
|
+
hook_name: str,
|
|
534
|
+
**params: t.Any,
|
|
535
|
+
) -> dict[str, t.Any]:
|
|
536
|
+
"""Execute a single hook by name.
|
|
537
|
+
|
|
538
|
+
This action is used for Phase 3 hook-level parallelization where
|
|
539
|
+
individual hooks (zuban, bandit, gitleaks, etc.) run as separate steps.
|
|
540
|
+
|
|
541
|
+
Args:
|
|
542
|
+
context: Workflow execution context with "options" key
|
|
543
|
+
step_id: Step identifier (unused, for ACB compatibility)
|
|
544
|
+
hook_name: Name of the hook to execute
|
|
545
|
+
**params: Additional step parameters
|
|
546
|
+
|
|
547
|
+
Returns:
|
|
548
|
+
dict with hook execution results
|
|
549
|
+
|
|
550
|
+
Raises:
|
|
551
|
+
NotImplementedError: Phase 3 feature not yet implemented
|
|
552
|
+
"""
|
|
553
|
+
msg = "Hook-level parallelization is a Phase 3 feature (not yet implemented)"
|
|
554
|
+
raise NotImplementedError(msg)
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
@depends.inject # type: ignore[misc]
|
|
558
|
+
async def run_commit_phase(
|
|
559
|
+
context: dict[str, t.Any],
|
|
560
|
+
step_id: str,
|
|
561
|
+
event_bus: Inject[WorkflowEventBus] = None,
|
|
562
|
+
**params: t.Any,
|
|
563
|
+
) -> dict[str, t.Any]:
|
|
564
|
+
"""Execute git commit phase.
|
|
565
|
+
|
|
566
|
+
This action commits all changes to git and optionally pushes to remote.
|
|
567
|
+
Only runs if options.commit is True.
|
|
568
|
+
|
|
569
|
+
Args:
|
|
570
|
+
context: Workflow execution context with "options" and "pipeline" keys
|
|
571
|
+
step_id: Step identifier (unused, for ACB compatibility)
|
|
572
|
+
event_bus: WorkflowEventBus for event emission (injected)
|
|
573
|
+
**params: Additional step parameters
|
|
574
|
+
|
|
575
|
+
Returns:
|
|
576
|
+
dict with commit execution results
|
|
577
|
+
|
|
578
|
+
Raises:
|
|
579
|
+
RuntimeError: If commit execution fails
|
|
580
|
+
"""
|
|
581
|
+
options: OptionsProtocol = context.get("options") # type: ignore[assignment]
|
|
582
|
+
if not options:
|
|
583
|
+
msg = "Missing 'options' in workflow context"
|
|
584
|
+
raise ValueError(msg)
|
|
585
|
+
|
|
586
|
+
# Skip commit unless --commit flag is set
|
|
587
|
+
if not getattr(options, "commit", False):
|
|
588
|
+
return {
|
|
589
|
+
"phase": "commit",
|
|
590
|
+
"success": True,
|
|
591
|
+
"skipped": True,
|
|
592
|
+
"reason": "Commit phase only runs with --commit flag",
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
# Phase 4.1: Get pipeline from context instead of DI injection
|
|
596
|
+
pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
|
|
597
|
+
if not pipeline:
|
|
598
|
+
msg = "WorkflowPipeline not available in context"
|
|
599
|
+
raise RuntimeError(msg)
|
|
600
|
+
|
|
601
|
+
# Phase 7.2: Emit start event
|
|
602
|
+
start_time = time.time()
|
|
603
|
+
if event_bus:
|
|
604
|
+
await event_bus.publish(
|
|
605
|
+
WorkflowEvent.QUALITY_PHASE_STARTED,
|
|
606
|
+
{"step_id": step_id, "phase": "commit", "timestamp": start_time},
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
# Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
|
|
610
|
+
# Use asyncio.to_thread to avoid blocking event loop with synchronous operations
|
|
611
|
+
try:
|
|
612
|
+
success = await asyncio.to_thread(
|
|
613
|
+
pipeline.phases.run_commit_phase,
|
|
614
|
+
options,
|
|
615
|
+
)
|
|
616
|
+
except Exception as exc:
|
|
617
|
+
# Phase 7.2: Emit failure event
|
|
618
|
+
if event_bus:
|
|
619
|
+
await event_bus.publish(
|
|
620
|
+
WorkflowEvent.WORKFLOW_FAILED,
|
|
621
|
+
{
|
|
622
|
+
"step_id": step_id,
|
|
623
|
+
"phase": "commit",
|
|
624
|
+
"error": str(exc),
|
|
625
|
+
"timestamp": time.time(),
|
|
626
|
+
"duration": time.time() - start_time,
|
|
627
|
+
},
|
|
628
|
+
)
|
|
629
|
+
raise
|
|
630
|
+
|
|
631
|
+
if not success:
|
|
632
|
+
# Phase 7.2: Emit failure event
|
|
633
|
+
if event_bus:
|
|
634
|
+
await event_bus.publish(
|
|
635
|
+
WorkflowEvent.WORKFLOW_FAILED,
|
|
636
|
+
{
|
|
637
|
+
"step_id": step_id,
|
|
638
|
+
"phase": "commit",
|
|
639
|
+
"timestamp": time.time(),
|
|
640
|
+
"duration": time.time() - start_time,
|
|
641
|
+
},
|
|
642
|
+
)
|
|
643
|
+
msg = "Git commit execution failed"
|
|
644
|
+
raise RuntimeError(msg)
|
|
645
|
+
|
|
646
|
+
# Phase 7.2: Emit completion event
|
|
647
|
+
if event_bus:
|
|
648
|
+
await event_bus.publish(
|
|
649
|
+
WorkflowEvent.QUALITY_PHASE_COMPLETED,
|
|
650
|
+
{
|
|
651
|
+
"step_id": step_id,
|
|
652
|
+
"phase": "commit",
|
|
653
|
+
"success": True,
|
|
654
|
+
"timestamp": time.time(),
|
|
655
|
+
"duration": time.time() - start_time,
|
|
656
|
+
},
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
return {
|
|
660
|
+
"phase": "commit",
|
|
661
|
+
"success": True,
|
|
662
|
+
"message": "Git commit completed successfully",
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
@depends.inject # type: ignore[misc]
|
|
667
|
+
async def run_publish_phase(
|
|
668
|
+
context: dict[str, t.Any],
|
|
669
|
+
step_id: str,
|
|
670
|
+
event_bus: Inject[WorkflowEventBus] = None,
|
|
671
|
+
**params: t.Any,
|
|
672
|
+
) -> dict[str, t.Any]:
|
|
673
|
+
"""Execute publishing phase (version bump and PyPI publish).
|
|
674
|
+
|
|
675
|
+
This action bumps the version and publishes to PyPI.
|
|
676
|
+
Only runs if options.publish, options.all, or options.bump is set.
|
|
677
|
+
|
|
678
|
+
Args:
|
|
679
|
+
context: Workflow execution context with "options" and "pipeline" keys
|
|
680
|
+
step_id: Step identifier (unused, for ACB compatibility)
|
|
681
|
+
event_bus: WorkflowEventBus for event emission (injected)
|
|
682
|
+
**params: Additional step parameters
|
|
683
|
+
|
|
684
|
+
Returns:
|
|
685
|
+
dict with publish execution results
|
|
686
|
+
|
|
687
|
+
Raises:
|
|
688
|
+
RuntimeError: If publish execution fails
|
|
689
|
+
"""
|
|
690
|
+
options: OptionsProtocol = context.get("options") # type: ignore[assignment]
|
|
691
|
+
if not options:
|
|
692
|
+
msg = "Missing 'options' in workflow context"
|
|
693
|
+
raise ValueError(msg)
|
|
694
|
+
|
|
695
|
+
# Skip publish unless --publish, --all, or --bump flags are set
|
|
696
|
+
if not any(
|
|
697
|
+
[
|
|
698
|
+
getattr(options, "publish", False),
|
|
699
|
+
getattr(options, "all", False),
|
|
700
|
+
getattr(options, "bump", False),
|
|
701
|
+
]
|
|
702
|
+
):
|
|
703
|
+
return {
|
|
704
|
+
"phase": "publish",
|
|
705
|
+
"success": True,
|
|
706
|
+
"skipped": True,
|
|
707
|
+
"reason": "Publish phase only runs with --publish, --all, or --bump flags",
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
# Phase 4.1: Get pipeline from context instead of DI injection
|
|
711
|
+
pipeline: WorkflowPipeline | None = context.get("pipeline") # type: ignore[assignment]
|
|
712
|
+
if not pipeline:
|
|
713
|
+
msg = "WorkflowPipeline not available in context"
|
|
714
|
+
raise RuntimeError(msg)
|
|
715
|
+
|
|
716
|
+
# Phase 7.2: Emit start event
|
|
717
|
+
start_time = time.time()
|
|
718
|
+
if event_bus:
|
|
719
|
+
await event_bus.publish(
|
|
720
|
+
WorkflowEvent.QUALITY_PHASE_STARTED,
|
|
721
|
+
{"step_id": step_id, "phase": "publish", "timestamp": start_time},
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
# Phase 4.2: DI context now preserved with Inject[] pattern (not depends())
|
|
725
|
+
# Use asyncio.to_thread to avoid blocking event loop with synchronous operations
|
|
726
|
+
try:
|
|
727
|
+
success = await asyncio.to_thread(
|
|
728
|
+
pipeline.phases.run_publishing_phase,
|
|
729
|
+
options,
|
|
730
|
+
)
|
|
731
|
+
except Exception as exc:
|
|
732
|
+
# Phase 7.2: Emit failure event
|
|
733
|
+
if event_bus:
|
|
734
|
+
await event_bus.publish(
|
|
735
|
+
WorkflowEvent.WORKFLOW_FAILED,
|
|
736
|
+
{
|
|
737
|
+
"step_id": step_id,
|
|
738
|
+
"phase": "publish",
|
|
739
|
+
"error": str(exc),
|
|
740
|
+
"timestamp": time.time(),
|
|
741
|
+
"duration": time.time() - start_time,
|
|
742
|
+
},
|
|
743
|
+
)
|
|
744
|
+
raise
|
|
745
|
+
|
|
746
|
+
if not success:
|
|
747
|
+
# Phase 7.2: Emit failure event
|
|
748
|
+
if event_bus:
|
|
749
|
+
await event_bus.publish(
|
|
750
|
+
WorkflowEvent.WORKFLOW_FAILED,
|
|
751
|
+
{
|
|
752
|
+
"step_id": step_id,
|
|
753
|
+
"phase": "publish",
|
|
754
|
+
"timestamp": time.time(),
|
|
755
|
+
"duration": time.time() - start_time,
|
|
756
|
+
},
|
|
757
|
+
)
|
|
758
|
+
msg = "Publishing execution failed"
|
|
759
|
+
raise RuntimeError(msg)
|
|
760
|
+
|
|
761
|
+
# Phase 7.2: Emit completion event
|
|
762
|
+
if event_bus:
|
|
763
|
+
await event_bus.publish(
|
|
764
|
+
WorkflowEvent.QUALITY_PHASE_COMPLETED,
|
|
765
|
+
{
|
|
766
|
+
"step_id": step_id,
|
|
767
|
+
"phase": "publish",
|
|
768
|
+
"success": True,
|
|
769
|
+
"timestamp": time.time(),
|
|
770
|
+
"duration": time.time() - start_time,
|
|
771
|
+
},
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
return {
|
|
775
|
+
"phase": "publish",
|
|
776
|
+
"success": True,
|
|
777
|
+
"message": "Publishing completed successfully",
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
# Action registry for easy registration with engine
|
|
782
|
+
ACTION_REGISTRY: dict[str, t.Callable[..., t.Awaitable[t.Any]]] = {
|
|
783
|
+
"run_configuration": run_configuration,
|
|
784
|
+
"run_fast_hooks": run_fast_hooks,
|
|
785
|
+
"run_code_cleaning": run_code_cleaning,
|
|
786
|
+
"run_comprehensive_hooks": run_comprehensive_hooks,
|
|
787
|
+
"run_test_workflow": run_test_workflow,
|
|
788
|
+
"run_commit_phase": run_commit_phase,
|
|
789
|
+
"run_publish_phase": run_publish_phase,
|
|
790
|
+
"run_hook": run_hook,
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
def register_actions(engine: CrackerjackWorkflowEngine) -> None: # type: ignore[name-defined]
|
|
795
|
+
"""Register all action handlers with the workflow engine.
|
|
796
|
+
|
|
797
|
+
This convenience function registers all action handlers from the
|
|
798
|
+
ACTION_REGISTRY with the provided engine.
|
|
799
|
+
|
|
800
|
+
Args:
|
|
801
|
+
engine: CrackerjackWorkflowEngine instance to register actions with
|
|
802
|
+
|
|
803
|
+
Example:
|
|
804
|
+
```python
|
|
805
|
+
engine = CrackerjackWorkflowEngine()
|
|
806
|
+
register_actions(engine)
|
|
807
|
+
result = await engine.execute(FAST_HOOKS_WORKFLOW)
|
|
808
|
+
```
|
|
809
|
+
"""
|
|
810
|
+
for action_name, action_func in ACTION_REGISTRY.items():
|
|
811
|
+
engine.register_action(action_name, action_func)
|