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,346 @@
|
|
|
1
|
+
"""Workflow event bus built on top of ACB event primitives.
|
|
2
|
+
|
|
3
|
+
Provides a lightweight in-process event bus that uses ``acb.events`` data
|
|
4
|
+
structures so the rest of the codebase can adopt the forthcoming ACB EventBus
|
|
5
|
+
without waiting on external messaging infrastructure. Subscribers register
|
|
6
|
+
handlers for workflow event types and publishers emit events that are dispatched
|
|
7
|
+
asynchronously.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import logging
|
|
14
|
+
import threading
|
|
15
|
+
import typing as t
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from enum import Enum
|
|
18
|
+
|
|
19
|
+
from acb.events import (
|
|
20
|
+
Event,
|
|
21
|
+
EventHandlerResult,
|
|
22
|
+
FunctionalEventHandler,
|
|
23
|
+
create_event,
|
|
24
|
+
)
|
|
25
|
+
from acb.events._base import EventSubscription
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
HandlerCallable = t.Callable[
|
|
30
|
+
[Event], t.Awaitable[EventHandlerResult] | EventHandlerResult
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class WorkflowEvent(str, Enum):
|
|
35
|
+
"""Standard workflow events emitted inside Crackerjack."""
|
|
36
|
+
|
|
37
|
+
WORKFLOW_STARTED = "workflow.started"
|
|
38
|
+
WORKFLOW_SESSION_INITIALIZING = "workflow.session.initializing"
|
|
39
|
+
WORKFLOW_SESSION_READY = "workflow.session.ready"
|
|
40
|
+
CONFIG_PHASE_STARTED = "workflow.config.started"
|
|
41
|
+
CONFIG_PHASE_COMPLETED = "workflow.config.completed"
|
|
42
|
+
WORKFLOW_COMPLETED = "workflow.completed"
|
|
43
|
+
WORKFLOW_FAILED = "workflow.failed"
|
|
44
|
+
WORKFLOW_INTERRUPTED = "workflow.interrupted"
|
|
45
|
+
|
|
46
|
+
QUALITY_PHASE_STARTED = "workflow.quality.started"
|
|
47
|
+
QUALITY_PHASE_COMPLETED = "workflow.quality.completed"
|
|
48
|
+
|
|
49
|
+
PUBLISH_PHASE_STARTED = "workflow.publish.started"
|
|
50
|
+
PUBLISH_PHASE_COMPLETED = "workflow.publish.completed"
|
|
51
|
+
|
|
52
|
+
COMMIT_PHASE_STARTED = "workflow.commit.started"
|
|
53
|
+
COMMIT_PHASE_COMPLETED = "workflow.commit.completed"
|
|
54
|
+
|
|
55
|
+
HOOK_STRATEGY_STARTED = "hooks.strategy.started"
|
|
56
|
+
HOOK_STRATEGY_COMPLETED = "hooks.strategy.completed"
|
|
57
|
+
HOOK_STRATEGY_FAILED = "hooks.strategy.failed"
|
|
58
|
+
|
|
59
|
+
HOOK_EXECUTION_STARTED = "hooks.execution.started"
|
|
60
|
+
HOOK_EXECUTION_COMPLETED = "hooks.execution.completed"
|
|
61
|
+
HOOK_EXECUTION_FAILED = "hooks.execution.failed"
|
|
62
|
+
|
|
63
|
+
def __str__(self) -> str: # pragma: no cover - trivial
|
|
64
|
+
return str(self.value)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class WorkflowEventDispatchResult:
|
|
69
|
+
"""Result returned from publishing an event."""
|
|
70
|
+
|
|
71
|
+
event: Event
|
|
72
|
+
results: list[EventHandlerResult] = field(default_factory=list)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class _SubscriptionEntry:
|
|
77
|
+
"""Internal representation of a subscription with concurrency control."""
|
|
78
|
+
|
|
79
|
+
subscription: EventSubscription
|
|
80
|
+
description: str | None = None
|
|
81
|
+
semaphore: asyncio.Semaphore = field(default_factory=lambda: asyncio.Semaphore(1))
|
|
82
|
+
max_retries: int = 0
|
|
83
|
+
retry_backoff: float = 0.5
|
|
84
|
+
|
|
85
|
+
def __post_init__(self) -> None:
|
|
86
|
+
max_concurrent = self.subscription.max_concurrent or 1
|
|
87
|
+
self.semaphore = asyncio.Semaphore(max_concurrent)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class WorkflowEventBus:
|
|
91
|
+
"""In-process event bus compatible with ACB event handlers."""
|
|
92
|
+
|
|
93
|
+
def __init__(self) -> None:
|
|
94
|
+
self._subscriptions: dict[str | None, dict[str, _SubscriptionEntry]] = {}
|
|
95
|
+
self._lock = threading.RLock()
|
|
96
|
+
self._default_handlers_registered = False
|
|
97
|
+
|
|
98
|
+
def subscribe(
|
|
99
|
+
self,
|
|
100
|
+
event_type: WorkflowEvent | str | None,
|
|
101
|
+
handler: HandlerCallable,
|
|
102
|
+
*,
|
|
103
|
+
predicate: t.Callable[[Event], bool] | None = None,
|
|
104
|
+
max_concurrent: int | None = None,
|
|
105
|
+
description: str | None = None,
|
|
106
|
+
max_retries: int = 0,
|
|
107
|
+
retry_backoff: float = 0.5,
|
|
108
|
+
) -> str:
|
|
109
|
+
"""Register a handler for an event type.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
event_type: WorkflowEvent enum value, string, or ``None`` for wildcard.
|
|
113
|
+
handler: Callable invoked when the event is published.
|
|
114
|
+
predicate: Optional additional filter predicate.
|
|
115
|
+
max_concurrent: Maximum concurrent invocations for this handler.
|
|
116
|
+
description: Optional human readable identifier.
|
|
117
|
+
max_retries: Number of retry attempts when a handler raises.
|
|
118
|
+
retry_backoff: Initial backoff delay (seconds) between retries; doubles each attempt.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Subscription ID string.
|
|
122
|
+
"""
|
|
123
|
+
event_type_value = (
|
|
124
|
+
event_type.value if isinstance(event_type, WorkflowEvent) else event_type
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
event_handler = FunctionalEventHandler(
|
|
128
|
+
handler,
|
|
129
|
+
event_type=event_type_value,
|
|
130
|
+
predicate=predicate,
|
|
131
|
+
)
|
|
132
|
+
subscription = EventSubscription(
|
|
133
|
+
handler=event_handler,
|
|
134
|
+
event_type=event_type_value,
|
|
135
|
+
predicate=predicate,
|
|
136
|
+
max_concurrent=max_concurrent or 1,
|
|
137
|
+
)
|
|
138
|
+
entry = _SubscriptionEntry(
|
|
139
|
+
subscription=subscription,
|
|
140
|
+
description=description,
|
|
141
|
+
max_retries=max_retries,
|
|
142
|
+
retry_backoff=retry_backoff,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
key = event_type_value
|
|
146
|
+
|
|
147
|
+
with self._lock:
|
|
148
|
+
bucket = self._subscriptions.setdefault(key, {})
|
|
149
|
+
bucket[str(subscription.subscription_id)] = entry
|
|
150
|
+
|
|
151
|
+
logger.debug(
|
|
152
|
+
"Registered workflow event subscription",
|
|
153
|
+
extra={
|
|
154
|
+
"event_type": event_type_value or "*",
|
|
155
|
+
"subscription_id": str(subscription.subscription_id),
|
|
156
|
+
"description": description,
|
|
157
|
+
"max_concurrent": max_concurrent or 1,
|
|
158
|
+
"max_retries": max_retries,
|
|
159
|
+
"retry_backoff": retry_backoff,
|
|
160
|
+
},
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return str(subscription.subscription_id)
|
|
164
|
+
|
|
165
|
+
def unsubscribe(self, subscription_id: str) -> bool:
|
|
166
|
+
"""Remove a subscription by ID."""
|
|
167
|
+
with self._lock:
|
|
168
|
+
for bucket in self._subscriptions.values():
|
|
169
|
+
if subscription_id in bucket:
|
|
170
|
+
del bucket[subscription_id]
|
|
171
|
+
logger.debug(
|
|
172
|
+
"Removed workflow event subscription",
|
|
173
|
+
extra={"subscription_id": subscription_id},
|
|
174
|
+
)
|
|
175
|
+
return True
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
def list_subscriptions(self) -> list[dict[str, t.Any]]:
|
|
179
|
+
"""Return summary information for registered subscriptions."""
|
|
180
|
+
data: list[dict[str, t.Any]] = []
|
|
181
|
+
with self._lock:
|
|
182
|
+
for event_type, bucket in self._subscriptions.items():
|
|
183
|
+
for subscription_id, entry in bucket.items():
|
|
184
|
+
data.append(
|
|
185
|
+
{
|
|
186
|
+
"subscription_id": subscription_id,
|
|
187
|
+
"event_type": event_type or "*",
|
|
188
|
+
"description": entry.description,
|
|
189
|
+
"max_concurrent": entry.subscription.max_concurrent,
|
|
190
|
+
}
|
|
191
|
+
)
|
|
192
|
+
return data
|
|
193
|
+
|
|
194
|
+
async def publish(
|
|
195
|
+
self,
|
|
196
|
+
event_type: WorkflowEvent | str,
|
|
197
|
+
payload: dict[str, t.Any] | None = None,
|
|
198
|
+
*,
|
|
199
|
+
source: str = "crackerjack.workflow",
|
|
200
|
+
**metadata: t.Any,
|
|
201
|
+
) -> WorkflowEventDispatchResult:
|
|
202
|
+
"""Publish an event to subscribed handlers."""
|
|
203
|
+
event_type_value = (
|
|
204
|
+
event_type.value if isinstance(event_type, WorkflowEvent) else event_type
|
|
205
|
+
)
|
|
206
|
+
payload = payload or {}
|
|
207
|
+
event = create_event(event_type_value, source, payload, **metadata)
|
|
208
|
+
|
|
209
|
+
entries = self._collect_subscriptions(event)
|
|
210
|
+
if not entries:
|
|
211
|
+
logger.debug(
|
|
212
|
+
"Workflow event published with no subscribers",
|
|
213
|
+
extra={"event_type": event_type_value},
|
|
214
|
+
)
|
|
215
|
+
return WorkflowEventDispatchResult(event=event, results=[])
|
|
216
|
+
|
|
217
|
+
tasks = [self._invoke_subscription(entry, event) for entry in entries]
|
|
218
|
+
raw_results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
219
|
+
|
|
220
|
+
results: list[EventHandlerResult] = []
|
|
221
|
+
for entry, result in zip(entries, raw_results, strict=False):
|
|
222
|
+
if isinstance(result, EventHandlerResult):
|
|
223
|
+
results.append(result)
|
|
224
|
+
continue
|
|
225
|
+
|
|
226
|
+
if isinstance(result, Exception):
|
|
227
|
+
logger.exception(
|
|
228
|
+
"Workflow event handler raised an exception",
|
|
229
|
+
extra={
|
|
230
|
+
"event_type": event_type_value,
|
|
231
|
+
"subscription_id": entry.subscription.subscription_id,
|
|
232
|
+
"description": entry.description,
|
|
233
|
+
},
|
|
234
|
+
exc_info=result,
|
|
235
|
+
)
|
|
236
|
+
results.append(
|
|
237
|
+
EventHandlerResult(
|
|
238
|
+
success=False,
|
|
239
|
+
error_message=str(result),
|
|
240
|
+
metadata={
|
|
241
|
+
"subscription_id": str(entry.subscription.subscription_id)
|
|
242
|
+
},
|
|
243
|
+
)
|
|
244
|
+
)
|
|
245
|
+
continue
|
|
246
|
+
|
|
247
|
+
# Allow handlers to return truthy values instead of EventHandlerResult
|
|
248
|
+
success = True if result is None else bool(result)
|
|
249
|
+
results.append(
|
|
250
|
+
EventHandlerResult(
|
|
251
|
+
success=success,
|
|
252
|
+
metadata={
|
|
253
|
+
"subscription_id": str(entry.subscription.subscription_id)
|
|
254
|
+
},
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
return WorkflowEventDispatchResult(event=event, results=results)
|
|
259
|
+
|
|
260
|
+
def register_logging_handler(self) -> None:
|
|
261
|
+
"""Install a default debug logging handler (idempotent)."""
|
|
262
|
+
if self._default_handlers_registered:
|
|
263
|
+
return
|
|
264
|
+
|
|
265
|
+
async def _log_event(event: Event) -> EventHandlerResult:
|
|
266
|
+
logger.debug(
|
|
267
|
+
"Workflow event dispatched",
|
|
268
|
+
extra={
|
|
269
|
+
"event_type": event.metadata.event_type,
|
|
270
|
+
"source": event.metadata.source,
|
|
271
|
+
"payload": event.payload,
|
|
272
|
+
},
|
|
273
|
+
)
|
|
274
|
+
return EventHandlerResult(success=True)
|
|
275
|
+
|
|
276
|
+
self.subscribe(
|
|
277
|
+
event_type=None,
|
|
278
|
+
handler=_log_event,
|
|
279
|
+
description="workflow.logging",
|
|
280
|
+
)
|
|
281
|
+
self._default_handlers_registered = True
|
|
282
|
+
|
|
283
|
+
def _collect_subscriptions(self, event: Event) -> list[_SubscriptionEntry]:
|
|
284
|
+
with self._lock:
|
|
285
|
+
specific = list(
|
|
286
|
+
self._subscriptions.get(event.metadata.event_type, {}).values()
|
|
287
|
+
)
|
|
288
|
+
wildcard = list(self._subscriptions.get(None, {}).values())
|
|
289
|
+
return specific + wildcard
|
|
290
|
+
|
|
291
|
+
async def _invoke_subscription(
|
|
292
|
+
self,
|
|
293
|
+
entry: _SubscriptionEntry,
|
|
294
|
+
event: Event,
|
|
295
|
+
) -> EventHandlerResult:
|
|
296
|
+
handler = entry.subscription.handler
|
|
297
|
+
if not handler.can_handle(event):
|
|
298
|
+
return EventHandlerResult(
|
|
299
|
+
success=True,
|
|
300
|
+
metadata={
|
|
301
|
+
"subscription_id": str(entry.subscription.subscription_id),
|
|
302
|
+
"skipped": True,
|
|
303
|
+
},
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
async with entry.semaphore:
|
|
307
|
+
attempt = 0
|
|
308
|
+
delay = max(entry.retry_backoff, 0.0)
|
|
309
|
+
while True:
|
|
310
|
+
try:
|
|
311
|
+
return await handler.handle(event)
|
|
312
|
+
except Exception as exc:
|
|
313
|
+
attempt += 1
|
|
314
|
+
if attempt > entry.max_retries:
|
|
315
|
+
logger.exception(
|
|
316
|
+
"Workflow event handler failed after retries",
|
|
317
|
+
extra={
|
|
318
|
+
"subscription_id": str(
|
|
319
|
+
entry.subscription.subscription_id
|
|
320
|
+
),
|
|
321
|
+
"description": entry.description,
|
|
322
|
+
"event_type": event.metadata.event_type,
|
|
323
|
+
"attempts": attempt,
|
|
324
|
+
},
|
|
325
|
+
exc_info=exc,
|
|
326
|
+
)
|
|
327
|
+
raise
|
|
328
|
+
|
|
329
|
+
logger.warning(
|
|
330
|
+
"Workflow event handler raised; retrying",
|
|
331
|
+
extra={
|
|
332
|
+
"subscription_id": str(entry.subscription.subscription_id),
|
|
333
|
+
"description": entry.description,
|
|
334
|
+
"event_type": event.metadata.event_type,
|
|
335
|
+
"attempt": attempt,
|
|
336
|
+
"max_retries": entry.max_retries,
|
|
337
|
+
"retry_delay": delay,
|
|
338
|
+
},
|
|
339
|
+
exc_info=exc,
|
|
340
|
+
)
|
|
341
|
+
if delay > 0:
|
|
342
|
+
await asyncio.sleep(delay)
|
|
343
|
+
delay *= 2
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
__all__ = ["WorkflowEventBus", "WorkflowEvent", "WorkflowEventDispatchResult"]
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
> Crackerjack Docs: [Main](<../../README.md>) | [Crackerjack Package](<../README.md>) | [Exceptions](<./README.md>)
|
|
2
|
+
|
|
3
|
+
# Exceptions
|
|
4
|
+
|
|
5
|
+
Custom exception types with rich formatting and context-aware error reporting. Provides enhanced UX for tool execution failures and configuration issues.
|
|
6
|
+
|
|
7
|
+
## Exception Hierarchy
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
BaseException
|
|
11
|
+
└── Exception
|
|
12
|
+
├── ToolExecutionError # Rich tool execution failures
|
|
13
|
+
└── ConfigIntegrityError # Configuration validation issues
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Custom Exception Types
|
|
17
|
+
|
|
18
|
+
### ToolExecutionError
|
|
19
|
+
|
|
20
|
+
Enhanced error with rich formatting for failed tool executions. Provides detailed context including exit codes, stdout/stderr, duration, and actionable suggestions.
|
|
21
|
+
|
|
22
|
+
**Features:**
|
|
23
|
+
|
|
24
|
+
- Rich console formatting with syntax highlighting
|
|
25
|
+
- Automatic output truncation for readability
|
|
26
|
+
- Actionable error messages with suggestions
|
|
27
|
+
- Context tracking (command, cwd, duration)
|
|
28
|
+
- Integration with Crackerjack's console system
|
|
29
|
+
|
|
30
|
+
**Constructor:**
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
ToolExecutionError(
|
|
34
|
+
tool: str, # Tool name that failed
|
|
35
|
+
exit_code: int, # Process exit code
|
|
36
|
+
stdout: str = "", # Standard output
|
|
37
|
+
stderr: str = "", # Standard error
|
|
38
|
+
command: list[str] | None = None, # Full command executed
|
|
39
|
+
cwd: Path | None = None, # Working directory
|
|
40
|
+
duration: float | None = None, # Execution duration (seconds)
|
|
41
|
+
)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Usage Example:**
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from crackerjack.exceptions import ToolExecutionError
|
|
48
|
+
from pathlib import Path
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
result = subprocess.run(
|
|
52
|
+
["ruff", "check", "."], capture_output=True, text=True, cwd=project_dir
|
|
53
|
+
)
|
|
54
|
+
if result.returncode != 0:
|
|
55
|
+
raise ToolExecutionError(
|
|
56
|
+
tool="ruff",
|
|
57
|
+
exit_code=result.returncode,
|
|
58
|
+
stdout=result.stdout,
|
|
59
|
+
stderr=result.stderr,
|
|
60
|
+
command=["ruff", "check", "."],
|
|
61
|
+
cwd=project_dir,
|
|
62
|
+
duration=2.5,
|
|
63
|
+
)
|
|
64
|
+
except ToolExecutionError as e:
|
|
65
|
+
# Rich formatted output
|
|
66
|
+
console.print(e.format_rich())
|
|
67
|
+
|
|
68
|
+
# Or get actionable message
|
|
69
|
+
print(e.get_actionable_message())
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Rich Formatting:**
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from acb.console import Console
|
|
76
|
+
|
|
77
|
+
console = Console()
|
|
78
|
+
|
|
79
|
+
# Create error
|
|
80
|
+
error = ToolExecutionError(
|
|
81
|
+
tool="pytest",
|
|
82
|
+
exit_code=1,
|
|
83
|
+
stderr="test_example.py::test_feature FAILED",
|
|
84
|
+
duration=3.2,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Display with rich formatting
|
|
88
|
+
panel = error.format_rich(console)
|
|
89
|
+
console.print(panel)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Output:**
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
╭─ ❌ Tool Execution Failed: pytest ─────────────╮
|
|
96
|
+
│ Tool: pytest │
|
|
97
|
+
│ Exit Code: 1 │
|
|
98
|
+
│ Duration: 3.20s │
|
|
99
|
+
│ │
|
|
100
|
+
│ Error Output: │
|
|
101
|
+
│ test_example.py::test_feature FAILED │
|
|
102
|
+
╰────────────────────────────────────────────────╯
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Actionable Messages:**
|
|
106
|
+
|
|
107
|
+
The `get_actionable_message()` method provides smart error pattern detection:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
error = ToolExecutionError(
|
|
111
|
+
tool="ruff", exit_code=1, stderr="ModuleNotFoundError: No module named 'requests'"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
print(error.get_actionable_message())
|
|
115
|
+
# Output:
|
|
116
|
+
# Tool 'ruff' failed with exit code 1
|
|
117
|
+
# → Check Python dependencies are installed (try: uv sync)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Common Patterns Detected:**
|
|
121
|
+
|
|
122
|
+
- Permission denied → Check file permissions
|
|
123
|
+
- Command not found → Ensure tool is installed
|
|
124
|
+
- Timeout errors → Increase timeout setting
|
|
125
|
+
- Syntax errors → Check code syntax
|
|
126
|
+
- Import errors → Run dependency sync
|
|
127
|
+
- Type errors → Fix type annotations
|
|
128
|
+
- Out of memory → Reduce batch size
|
|
129
|
+
|
|
130
|
+
### ConfigIntegrityError
|
|
131
|
+
|
|
132
|
+
Simple exception for configuration validation and integrity issues.
|
|
133
|
+
|
|
134
|
+
**Usage Example:**
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from crackerjack.exceptions import ConfigIntegrityError
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def validate_config(config: dict) -> None:
|
|
141
|
+
if "required_field" not in config:
|
|
142
|
+
raise ConfigIntegrityError(
|
|
143
|
+
"Missing required field 'required_field' in configuration"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if config.get("version") != "1.0":
|
|
147
|
+
raise ConfigIntegrityError(
|
|
148
|
+
f"Unsupported config version: {config.get('version')}"
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Error Handling Patterns
|
|
153
|
+
|
|
154
|
+
### Pattern 1: Tool Execution with Context
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from crackerjack.exceptions import ToolExecutionError
|
|
158
|
+
import subprocess
|
|
159
|
+
import time
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def run_tool(tool: str, args: list[str], cwd: Path) -> bool:
|
|
163
|
+
"""Run tool with comprehensive error reporting."""
|
|
164
|
+
start = time.time()
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
result = subprocess.run(
|
|
168
|
+
[tool] + args, capture_output=True, text=True, cwd=cwd, timeout=300
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
if result.returncode != 0:
|
|
172
|
+
raise ToolExecutionError(
|
|
173
|
+
tool=tool,
|
|
174
|
+
exit_code=result.returncode,
|
|
175
|
+
stdout=result.stdout,
|
|
176
|
+
stderr=result.stderr,
|
|
177
|
+
command=[tool] + args,
|
|
178
|
+
cwd=cwd,
|
|
179
|
+
duration=time.time() - start,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
except subprocess.TimeoutExpired as e:
|
|
185
|
+
raise ToolExecutionError(
|
|
186
|
+
tool=tool,
|
|
187
|
+
exit_code=-1,
|
|
188
|
+
stderr=f"Tool execution timed out after {e.timeout}s",
|
|
189
|
+
command=[tool] + args,
|
|
190
|
+
cwd=cwd,
|
|
191
|
+
duration=e.timeout,
|
|
192
|
+
) from e
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Pattern 2: Graceful Error Display
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from crackerjack.exceptions import ToolExecutionError
|
|
199
|
+
from acb.console import Console
|
|
200
|
+
from acb.depends import depends
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@depends.inject
|
|
204
|
+
def execute_with_nice_errors(func: callable, console: Console = depends()) -> bool:
|
|
205
|
+
"""Execute function with rich error formatting."""
|
|
206
|
+
try:
|
|
207
|
+
return func()
|
|
208
|
+
except ToolExecutionError as e:
|
|
209
|
+
console.print(e.format_rich())
|
|
210
|
+
console.print(f"\n[yellow]{e.get_actionable_message()}[/yellow]")
|
|
211
|
+
return False
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Pattern 3: Config Validation
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
from crackerjack.exceptions import ConfigIntegrityError
|
|
218
|
+
from pathlib import Path
|
|
219
|
+
import yaml
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def load_and_validate_config(path: Path) -> dict:
|
|
223
|
+
"""Load config with integrity validation."""
|
|
224
|
+
if not path.exists():
|
|
225
|
+
raise ConfigIntegrityError(f"Configuration file not found: {path}")
|
|
226
|
+
|
|
227
|
+
with open(path) as f:
|
|
228
|
+
config = yaml.safe_load(f)
|
|
229
|
+
|
|
230
|
+
required = ["version", "project_name", "settings"]
|
|
231
|
+
missing = [k for k in required if k not in config]
|
|
232
|
+
|
|
233
|
+
if missing:
|
|
234
|
+
raise ConfigIntegrityError(
|
|
235
|
+
f"Missing required config fields: {', '.join(missing)}"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
return config
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Best Practices
|
|
242
|
+
|
|
243
|
+
### Use ToolExecutionError for All Tool Failures
|
|
244
|
+
|
|
245
|
+
Provides consistent, rich error reporting across the codebase:
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
# ✅ Good - Rich error context
|
|
249
|
+
if exit_code != 0:
|
|
250
|
+
raise ToolExecutionError(
|
|
251
|
+
tool="mypy", exit_code=exit_code, stderr=stderr, duration=duration
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# ❌ Bad - Generic exception
|
|
255
|
+
if exit_code != 0:
|
|
256
|
+
raise RuntimeError(f"mypy failed: {stderr}")
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Include All Available Context
|
|
260
|
+
|
|
261
|
+
More context enables better error messages:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
# ✅ Good - Full context
|
|
265
|
+
raise ToolExecutionError(
|
|
266
|
+
tool="pytest",
|
|
267
|
+
exit_code=1,
|
|
268
|
+
stdout=result.stdout,
|
|
269
|
+
stderr=result.stderr,
|
|
270
|
+
command=full_command,
|
|
271
|
+
cwd=project_dir,
|
|
272
|
+
duration=elapsed,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# ⚠️ Minimal - Missing helpful context
|
|
276
|
+
raise ToolExecutionError(tool="pytest", exit_code=1)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Use format_rich() for User-Facing Errors
|
|
280
|
+
|
|
281
|
+
Provides best UX with Rich console integration:
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
try:
|
|
285
|
+
run_checks()
|
|
286
|
+
except ToolExecutionError as e:
|
|
287
|
+
console.print(e.format_rich()) # Beautiful formatting
|
|
288
|
+
console.print(f"\n{e.get_actionable_message()}") # Smart suggestions
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Implementation Files
|
|
292
|
+
|
|
293
|
+
- **`tool_execution_error.py`** - ToolExecutionError with rich formatting
|
|
294
|
+
- **`config.py`** - ConfigIntegrityError for configuration validation
|
|
295
|
+
|
|
296
|
+
## Related
|
|
297
|
+
|
|
298
|
+
- [Decorators](<../decorators/README.md>) - Error handling decorators
|
|
299
|
+
- [Errors](../errors/README.md) - Core error types and CrackerjackError base
|
|
300
|
+
- [MCP](<../mcp/README.md>) - Error caching and pattern analysis
|
|
301
|
+
- [Console Integration](https://github.com/Textualize/rich) - Rich console formatting
|