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,340 @@
|
|
|
1
|
+
"""Tool proxy cache adapter for hook result caching.
|
|
2
|
+
|
|
3
|
+
Bridges to existing tool_proxy cache infrastructure for consistent caching
|
|
4
|
+
across the crackerjack ecosystem. Implements content-based cache keys using
|
|
5
|
+
hash of hook configuration and file contents.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
import typing as t
|
|
14
|
+
from contextlib import suppress
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from uuid import UUID
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
|
|
20
|
+
from crackerjack.models.task import HookResult
|
|
21
|
+
|
|
22
|
+
if t.TYPE_CHECKING:
|
|
23
|
+
from crackerjack.config.hooks import HookDefinition
|
|
24
|
+
|
|
25
|
+
# ACB Module Registration (REQUIRED)
|
|
26
|
+
MODULE_ID = UUID("01937d86-ace0-7000-8000-000000000004") # Static UUID7
|
|
27
|
+
MODULE_STATUS = "stable"
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ToolProxyCacheSettings(BaseModel):
|
|
33
|
+
"""Settings for tool proxy cache adapter."""
|
|
34
|
+
|
|
35
|
+
default_ttl: int = Field(default=3600, ge=60, le=86400) # 1 minute to 24 hours
|
|
36
|
+
max_cache_size_mb: int = Field(default=100, ge=10, le=1000)
|
|
37
|
+
enable_compression: bool = True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ToolProxyCacheAdapter:
|
|
41
|
+
"""Cache adapter bridging to tool_proxy infrastructure.
|
|
42
|
+
|
|
43
|
+
Features:
|
|
44
|
+
- Content-based cache keys (config + file hashes)
|
|
45
|
+
- Configurable TTL per hook result
|
|
46
|
+
- Integration with existing tool_proxy cache
|
|
47
|
+
- Automatic cache invalidation on content changes
|
|
48
|
+
|
|
49
|
+
Cache Key Format:
|
|
50
|
+
{hook_name}:{config_hash}:{content_hash}
|
|
51
|
+
|
|
52
|
+
Example:
|
|
53
|
+
```python
|
|
54
|
+
cache = ToolProxyCacheAdapter()
|
|
55
|
+
await cache.init()
|
|
56
|
+
|
|
57
|
+
# Check cache
|
|
58
|
+
cached_result = await cache.get(cache_key)
|
|
59
|
+
if cached_result:
|
|
60
|
+
return cached_result
|
|
61
|
+
|
|
62
|
+
# Execute hook and cache result
|
|
63
|
+
result = await execute_hook(hook)
|
|
64
|
+
await cache.set(cache_key, result, ttl=3600)
|
|
65
|
+
```
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
settings: ToolProxyCacheSettings | None = None,
|
|
71
|
+
cache_dir: Path | None = None,
|
|
72
|
+
) -> None:
|
|
73
|
+
"""Initialize tool proxy cache adapter.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
settings: Optional cache settings
|
|
77
|
+
cache_dir: Optional cache directory (defaults to .crackerjack/cache)
|
|
78
|
+
"""
|
|
79
|
+
self.settings = settings or ToolProxyCacheSettings()
|
|
80
|
+
self._cache_dir = cache_dir or Path.cwd() / ".crackerjack" / "cache"
|
|
81
|
+
self._cache: dict[
|
|
82
|
+
str, tuple[HookResult, float]
|
|
83
|
+
] = {} # key -> (result, expiry_timestamp)
|
|
84
|
+
self._initialized = False
|
|
85
|
+
|
|
86
|
+
logger.debug(
|
|
87
|
+
"ToolProxyCacheAdapter initializing",
|
|
88
|
+
extra={
|
|
89
|
+
"cache_dir": str(self._cache_dir),
|
|
90
|
+
"default_ttl": self.settings.default_ttl,
|
|
91
|
+
"enable_compression": self.settings.enable_compression,
|
|
92
|
+
},
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
async def init(self) -> None:
|
|
96
|
+
"""Initialize cache adapter and ensure cache directory exists."""
|
|
97
|
+
if self._initialized:
|
|
98
|
+
logger.debug("Cache adapter already initialized")
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
self._cache_dir.mkdir(parents=True, exist_ok=True)
|
|
103
|
+
self._initialized = True
|
|
104
|
+
|
|
105
|
+
logger.info(
|
|
106
|
+
"ToolProxyCacheAdapter initialized",
|
|
107
|
+
extra={
|
|
108
|
+
"cache_dir": str(self._cache_dir),
|
|
109
|
+
"default_ttl": self.settings.default_ttl,
|
|
110
|
+
},
|
|
111
|
+
)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
logger.error(
|
|
114
|
+
"Failed to initialize cache adapter",
|
|
115
|
+
extra={
|
|
116
|
+
"error": str(e),
|
|
117
|
+
"cache_dir": str(self._cache_dir),
|
|
118
|
+
},
|
|
119
|
+
)
|
|
120
|
+
raise
|
|
121
|
+
|
|
122
|
+
async def get(self, key: str) -> HookResult | None:
|
|
123
|
+
"""Retrieve cached hook result.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
key: Cache key (computed via compute_key())
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Cached HookResult if found and not expired, None otherwise
|
|
130
|
+
"""
|
|
131
|
+
if not self._initialized:
|
|
132
|
+
logger.warning("Cache adapter not initialized, returning None")
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
import time
|
|
137
|
+
|
|
138
|
+
if key in self._cache:
|
|
139
|
+
result, expiry = self._cache[key]
|
|
140
|
+
|
|
141
|
+
# Check expiration
|
|
142
|
+
if time.time() < expiry:
|
|
143
|
+
logger.debug(
|
|
144
|
+
"Cache hit",
|
|
145
|
+
extra={
|
|
146
|
+
"key": key,
|
|
147
|
+
"hook_name": result.name,
|
|
148
|
+
"status": result.status,
|
|
149
|
+
},
|
|
150
|
+
)
|
|
151
|
+
return result
|
|
152
|
+
else:
|
|
153
|
+
# Expired - remove from cache
|
|
154
|
+
del self._cache[key]
|
|
155
|
+
logger.debug("Cache entry expired", extra={"key": key})
|
|
156
|
+
|
|
157
|
+
logger.debug("Cache miss", extra={"key": key})
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
except Exception as e:
|
|
161
|
+
logger.error(
|
|
162
|
+
"Failed to retrieve from cache",
|
|
163
|
+
extra={
|
|
164
|
+
"key": key,
|
|
165
|
+
"error": str(e),
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
async def set(
|
|
171
|
+
self,
|
|
172
|
+
key: str,
|
|
173
|
+
result: HookResult,
|
|
174
|
+
ttl: int | None = None,
|
|
175
|
+
) -> None:
|
|
176
|
+
"""Cache hook result with TTL.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
key: Cache key (computed via compute_key())
|
|
180
|
+
result: HookResult to cache
|
|
181
|
+
ttl: Optional time-to-live in seconds (defaults to settings.default_ttl)
|
|
182
|
+
"""
|
|
183
|
+
if not self._initialized:
|
|
184
|
+
logger.warning("Cache adapter not initialized, skipping cache write")
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
import time
|
|
189
|
+
|
|
190
|
+
ttl_sec = ttl or self.settings.default_ttl
|
|
191
|
+
expiry = time.time() + ttl_sec
|
|
192
|
+
|
|
193
|
+
self._cache[key] = (result, expiry)
|
|
194
|
+
|
|
195
|
+
logger.debug(
|
|
196
|
+
"Cache write",
|
|
197
|
+
extra={
|
|
198
|
+
"key": key,
|
|
199
|
+
"hook_name": result.name,
|
|
200
|
+
"status": result.status,
|
|
201
|
+
"ttl": ttl_sec,
|
|
202
|
+
},
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logger.error(
|
|
207
|
+
"Failed to write to cache",
|
|
208
|
+
extra={
|
|
209
|
+
"key": key,
|
|
210
|
+
"error": str(e),
|
|
211
|
+
},
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
def compute_key(
|
|
215
|
+
self,
|
|
216
|
+
hook: HookDefinition,
|
|
217
|
+
files: list[Path],
|
|
218
|
+
) -> str:
|
|
219
|
+
"""Compute content-based cache key.
|
|
220
|
+
|
|
221
|
+
Cache key format: {hook_name}:{config_hash}:{content_hash}
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
hook: Hook definition
|
|
225
|
+
files: List of files to be checked by hook
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Cache key string
|
|
229
|
+
"""
|
|
230
|
+
try:
|
|
231
|
+
# Hash hook configuration (Phase 8+ direct invocation API)
|
|
232
|
+
config_data = {
|
|
233
|
+
"name": hook.name,
|
|
234
|
+
"command": hook.command, # Direct tool invocation command
|
|
235
|
+
"timeout": hook.timeout,
|
|
236
|
+
"stage": hook.stage.value
|
|
237
|
+
if hasattr(hook.stage, "value")
|
|
238
|
+
else str(hook.stage),
|
|
239
|
+
"security_level": hook.security_level.value
|
|
240
|
+
if hasattr(hook.security_level, "value")
|
|
241
|
+
else str(hook.security_level),
|
|
242
|
+
}
|
|
243
|
+
config_json = json.dumps(config_data, sort_keys=True)
|
|
244
|
+
config_hash = hashlib.sha256(config_json.encode()).hexdigest()[:16]
|
|
245
|
+
|
|
246
|
+
# Hash file contents (for cache invalidation on content changes)
|
|
247
|
+
content_hasher = hashlib.sha256()
|
|
248
|
+
for file_path in sorted(files):
|
|
249
|
+
try:
|
|
250
|
+
if file_path.exists() and file_path.is_file():
|
|
251
|
+
content_hasher.update(file_path.read_bytes())
|
|
252
|
+
except Exception as e:
|
|
253
|
+
logger.warning(
|
|
254
|
+
f"Failed to hash file {file_path}: {e}",
|
|
255
|
+
extra={"file": str(file_path), "error": str(e)},
|
|
256
|
+
)
|
|
257
|
+
continue
|
|
258
|
+
|
|
259
|
+
content_hash = content_hasher.hexdigest()[:16]
|
|
260
|
+
|
|
261
|
+
cache_key = f"{hook.name}:{config_hash}:{content_hash}"
|
|
262
|
+
|
|
263
|
+
logger.debug(
|
|
264
|
+
"Cache key computed",
|
|
265
|
+
extra={
|
|
266
|
+
"hook_name": hook.name,
|
|
267
|
+
"file_count": len(files),
|
|
268
|
+
"config_hash": config_hash,
|
|
269
|
+
"content_hash": content_hash,
|
|
270
|
+
},
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
return cache_key
|
|
274
|
+
|
|
275
|
+
except Exception as e:
|
|
276
|
+
logger.error(
|
|
277
|
+
"Failed to compute cache key",
|
|
278
|
+
extra={
|
|
279
|
+
"hook_name": hook.name,
|
|
280
|
+
"error": str(e),
|
|
281
|
+
},
|
|
282
|
+
)
|
|
283
|
+
# Fallback to simple key
|
|
284
|
+
return f"{hook.name}:error"
|
|
285
|
+
|
|
286
|
+
async def clear(self) -> None:
|
|
287
|
+
"""Clear all cached results."""
|
|
288
|
+
try:
|
|
289
|
+
self._cache.clear()
|
|
290
|
+
logger.info("Cache cleared")
|
|
291
|
+
except Exception as e:
|
|
292
|
+
logger.error("Failed to clear cache", extra={"error": str(e)})
|
|
293
|
+
|
|
294
|
+
async def get_stats(self) -> dict[str, t.Any]:
|
|
295
|
+
"""Get cache statistics.
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
Dictionary with cache statistics
|
|
299
|
+
"""
|
|
300
|
+
import time
|
|
301
|
+
|
|
302
|
+
try:
|
|
303
|
+
total_entries = len(self._cache)
|
|
304
|
+
expired_entries = sum(
|
|
305
|
+
1 for _, expiry in self._cache.values() if time.time() >= expiry
|
|
306
|
+
)
|
|
307
|
+
active_entries = total_entries - expired_entries
|
|
308
|
+
|
|
309
|
+
stats = {
|
|
310
|
+
"total_entries": total_entries,
|
|
311
|
+
"active_entries": active_entries,
|
|
312
|
+
"expired_entries": expired_entries,
|
|
313
|
+
"cache_dir": str(self._cache_dir),
|
|
314
|
+
"default_ttl": self.settings.default_ttl,
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
logger.debug("Cache statistics", extra=stats)
|
|
318
|
+
|
|
319
|
+
return stats
|
|
320
|
+
|
|
321
|
+
except Exception as e:
|
|
322
|
+
logger.error("Failed to get cache statistics", extra={"error": str(e)})
|
|
323
|
+
return {}
|
|
324
|
+
|
|
325
|
+
@property
|
|
326
|
+
def module_id(self) -> UUID:
|
|
327
|
+
"""Reference to module-level MODULE_ID."""
|
|
328
|
+
return MODULE_ID
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def adapter_name(self) -> str:
|
|
332
|
+
"""Human-readable adapter name."""
|
|
333
|
+
return "ToolProxyCacheAdapter"
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
# ACB Registration (REQUIRED at module level)
|
|
337
|
+
with suppress(Exception):
|
|
338
|
+
from acb.depends import depends
|
|
339
|
+
|
|
340
|
+
depends.set(ToolProxyCacheAdapter)
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import os
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from typing import Self
|
|
11
|
+
else:
|
|
12
|
+
Self = "OrchestrationConfig"
|
|
13
|
+
|
|
14
|
+
import yaml
|
|
15
|
+
from acb.depends import depends
|
|
16
|
+
|
|
17
|
+
from crackerjack.config.settings import CrackerjackSettings
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _bool_from_env(value: str) -> bool:
|
|
21
|
+
return value.strip().lower() in {"1", "true", "yes", "on"}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _int_from_env(value: str, default: int) -> int:
|
|
25
|
+
try:
|
|
26
|
+
return int(value)
|
|
27
|
+
except ValueError:
|
|
28
|
+
return default
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class OrchestrationConfig:
|
|
33
|
+
enable_orchestration: bool = True
|
|
34
|
+
orchestration_mode: str = "acb"
|
|
35
|
+
enable_caching: bool = True
|
|
36
|
+
cache_backend: str = "memory"
|
|
37
|
+
cache_ttl: int = 3600
|
|
38
|
+
cache_max_entries: int = 100
|
|
39
|
+
max_parallel_hooks: int = 4
|
|
40
|
+
default_timeout: int = 600
|
|
41
|
+
stop_on_critical_failure: bool = True
|
|
42
|
+
enable_dependency_resolution: bool = True
|
|
43
|
+
log_cache_stats: bool = False
|
|
44
|
+
log_execution_timing: bool = False
|
|
45
|
+
enable_strategy_parallelism: bool = True
|
|
46
|
+
enable_adaptive_execution: bool = True
|
|
47
|
+
max_concurrent_strategies: int = 2
|
|
48
|
+
enable_tool_proxy: bool = True
|
|
49
|
+
use_precommit_legacy: bool = True
|
|
50
|
+
config_file_path: Path | None = field(default=None, repr=False)
|
|
51
|
+
|
|
52
|
+
# --------------------------------------------------------------------- #
|
|
53
|
+
# Construction helpers
|
|
54
|
+
# --------------------------------------------------------------------- #
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def from_settings(
|
|
58
|
+
cls,
|
|
59
|
+
settings: CrackerjackSettings | None = None,
|
|
60
|
+
) -> OrchestrationConfig:
|
|
61
|
+
"""Build configuration from CrackerjackSettings via DI."""
|
|
62
|
+
if settings is None:
|
|
63
|
+
try:
|
|
64
|
+
# Use synchronous DI retrieval to avoid coroutine leakage in tests
|
|
65
|
+
settings = depends.get_sync(CrackerjackSettings) # type: ignore[attr-defined]
|
|
66
|
+
except Exception:
|
|
67
|
+
settings = None
|
|
68
|
+
|
|
69
|
+
if settings is None:
|
|
70
|
+
return cls()
|
|
71
|
+
|
|
72
|
+
return cls(
|
|
73
|
+
enable_orchestration=settings.enable_orchestration,
|
|
74
|
+
orchestration_mode=settings.orchestration_mode,
|
|
75
|
+
enable_caching=settings.enable_caching,
|
|
76
|
+
cache_backend=settings.cache_backend,
|
|
77
|
+
cache_ttl=settings.cache_ttl,
|
|
78
|
+
cache_max_entries=settings.cache_max_entries,
|
|
79
|
+
max_parallel_hooks=settings.max_parallel_hooks,
|
|
80
|
+
default_timeout=settings.default_timeout,
|
|
81
|
+
stop_on_critical_failure=settings.stop_on_critical_failure,
|
|
82
|
+
enable_dependency_resolution=settings.enable_dependency_resolution,
|
|
83
|
+
log_cache_stats=settings.log_cache_stats,
|
|
84
|
+
log_execution_timing=settings.log_execution_timing,
|
|
85
|
+
enable_strategy_parallelism=settings.enable_strategy_parallelism,
|
|
86
|
+
enable_adaptive_execution=settings.enable_adaptive_execution,
|
|
87
|
+
max_concurrent_strategies=settings.max_concurrent_strategies,
|
|
88
|
+
enable_tool_proxy=getattr(settings, "enable_tool_proxy", True),
|
|
89
|
+
use_precommit_legacy=settings.use_precommit_legacy,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def from_file(cls, config_path: Path) -> OrchestrationConfig:
|
|
94
|
+
if not config_path.exists():
|
|
95
|
+
msg = f"Config file not found: {config_path}"
|
|
96
|
+
raise FileNotFoundError(msg)
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
raw = yaml.safe_load(config_path.read_text(encoding="utf-8"))
|
|
100
|
+
data: dict[str, Any] = raw if isinstance(raw, dict) else {}
|
|
101
|
+
except yaml.YAMLError as exc:
|
|
102
|
+
msg = f"Invalid YAML in {config_path}: {exc}"
|
|
103
|
+
raise ValueError(msg) from exc
|
|
104
|
+
|
|
105
|
+
if not isinstance(data, dict):
|
|
106
|
+
msg = f"Invalid config structure in {config_path}"
|
|
107
|
+
raise ValueError(msg)
|
|
108
|
+
|
|
109
|
+
section_any = data.get("orchestration", {})
|
|
110
|
+
section: dict[str, Any] = section_any if isinstance(section_any, dict) else {}
|
|
111
|
+
|
|
112
|
+
config = cls()
|
|
113
|
+
config.config_file_path = config_path
|
|
114
|
+
config._apply_dict(section)
|
|
115
|
+
return config
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def _env_overrides(cls) -> dict[str, Any]:
|
|
119
|
+
"""Return environment variable overrides."""
|
|
120
|
+
env = os.environ
|
|
121
|
+
overrides: dict[str, Any] = {}
|
|
122
|
+
|
|
123
|
+
mapping: dict[str, str] = {
|
|
124
|
+
"CRACKERJACK_ORCHESTRATION_MODE": "orchestration_mode",
|
|
125
|
+
"CRACKERJACK_CACHE_BACKEND": "cache_backend",
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for env_key, attr in mapping.items():
|
|
129
|
+
if env_key in env:
|
|
130
|
+
overrides[attr] = env[env_key]
|
|
131
|
+
|
|
132
|
+
bool_vars = {
|
|
133
|
+
"CRACKERJACK_ENABLE_ORCHESTRATION": "enable_orchestration",
|
|
134
|
+
"CRACKERJACK_ENABLE_CACHING": "enable_caching",
|
|
135
|
+
"CRACKERJACK_STOP_ON_CRITICAL_FAILURE": "stop_on_critical_failure",
|
|
136
|
+
"CRACKERJACK_ENABLE_DEPENDENCY_RESOLUTION": "enable_dependency_resolution",
|
|
137
|
+
"CRACKERJACK_LOG_CACHE_STATS": "log_cache_stats",
|
|
138
|
+
"CRACKERJACK_LOG_EXECUTION_TIMING": "log_execution_timing",
|
|
139
|
+
"CRACKERJACK_ENABLE_STRATEGY_PARALLELISM": "enable_strategy_parallelism",
|
|
140
|
+
"CRACKERJACK_ENABLE_ADAPTIVE_EXECUTION": "enable_adaptive_execution",
|
|
141
|
+
"CRACKERJACK_ENABLE_TOOL_PROXY": "enable_tool_proxy",
|
|
142
|
+
"CRACKERJACK_USE_PRECOMMIT_LEGACY": "use_precommit_legacy",
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for env_key, attr in bool_vars.items():
|
|
146
|
+
if env_key in env:
|
|
147
|
+
overrides[attr] = _bool_from_env(env[env_key])
|
|
148
|
+
|
|
149
|
+
int_vars = {
|
|
150
|
+
"CRACKERJACK_CACHE_TTL": ("cache_ttl", 3600),
|
|
151
|
+
"CRACKERJACK_CACHE_MAX_ENTRIES": ("cache_max_entries", 100),
|
|
152
|
+
"CRACKERJACK_MAX_PARALLEL_HOOKS": ("max_parallel_hooks", 4),
|
|
153
|
+
"CRACKERJACK_DEFAULT_TIMEOUT": ("default_timeout", 600),
|
|
154
|
+
"CRACKERJACK_MAX_CONCURRENT_STRATEGIES": ("max_concurrent_strategies", 2),
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
for env_key, (attr, default) in int_vars.items():
|
|
158
|
+
if env_key in env:
|
|
159
|
+
overrides[attr] = _int_from_env(env[env_key], default)
|
|
160
|
+
|
|
161
|
+
return overrides
|
|
162
|
+
|
|
163
|
+
@classmethod
|
|
164
|
+
def from_env(cls) -> OrchestrationConfig:
|
|
165
|
+
overrides = cls._env_overrides()
|
|
166
|
+
config = cls()
|
|
167
|
+
config._apply_dict(overrides)
|
|
168
|
+
return config
|
|
169
|
+
|
|
170
|
+
@classmethod
|
|
171
|
+
def load(cls, config_path: Path) -> OrchestrationConfig:
|
|
172
|
+
# Start from hard defaults; only override from file/env.
|
|
173
|
+
config = cls()
|
|
174
|
+
|
|
175
|
+
if config_path.exists():
|
|
176
|
+
file_config = cls.from_file(config_path)
|
|
177
|
+
config = config.merge(file_config) # type: ignore[assignment]
|
|
178
|
+
|
|
179
|
+
env_overrides = cls._env_overrides()
|
|
180
|
+
config = config.with_overrides(**env_overrides) # type: ignore[assignment]
|
|
181
|
+
|
|
182
|
+
return config
|
|
183
|
+
|
|
184
|
+
# ------------------------------------------------------------------ #
|
|
185
|
+
# Mutation helpers
|
|
186
|
+
# ------------------------------------------------------------------ #
|
|
187
|
+
|
|
188
|
+
def _apply_dict(self, values: dict[str, Any]) -> None:
|
|
189
|
+
# Accept aliases from YAML where keys are shorter (e.g., enable, mode)
|
|
190
|
+
aliases = {
|
|
191
|
+
"enable": "enable_orchestration",
|
|
192
|
+
"mode": "orchestration_mode",
|
|
193
|
+
}
|
|
194
|
+
for key, value in values.items():
|
|
195
|
+
attr = aliases.get(key, key)
|
|
196
|
+
if not hasattr(self, attr):
|
|
197
|
+
continue
|
|
198
|
+
if isinstance(value, str):
|
|
199
|
+
value = value.strip()
|
|
200
|
+
setattr(self, attr, value)
|
|
201
|
+
|
|
202
|
+
def merge(self, other: OrchestrationConfig) -> OrchestrationConfig:
|
|
203
|
+
data = dataclasses.asdict(self)
|
|
204
|
+
other_data = dataclasses.asdict(other)
|
|
205
|
+
data.update(other_data)
|
|
206
|
+
merged = OrchestrationConfig(**data)
|
|
207
|
+
merged.config_file_path = other.config_file_path or self.config_file_path
|
|
208
|
+
return merged
|
|
209
|
+
|
|
210
|
+
def with_overrides(self, **overrides: Any) -> OrchestrationConfig:
|
|
211
|
+
# Create a shallow copy without using dataclasses.replace to satisfy type checker
|
|
212
|
+
updated = OrchestrationConfig(**dataclasses.asdict(self))
|
|
213
|
+
for key, value in overrides.items():
|
|
214
|
+
if value is not None and hasattr(updated, key):
|
|
215
|
+
setattr(updated, key, value)
|
|
216
|
+
return updated
|
|
217
|
+
|
|
218
|
+
# ------------------------------------------------------------------ #
|
|
219
|
+
# Serialisation
|
|
220
|
+
# ------------------------------------------------------------------ #
|
|
221
|
+
|
|
222
|
+
def to_dict(self) -> dict[str, Any]:
|
|
223
|
+
return {
|
|
224
|
+
"orchestration": {
|
|
225
|
+
"enable": self.enable_orchestration,
|
|
226
|
+
"mode": self.orchestration_mode,
|
|
227
|
+
"enable_caching": self.enable_caching,
|
|
228
|
+
"cache_backend": self.cache_backend,
|
|
229
|
+
"cache_ttl": self.cache_ttl,
|
|
230
|
+
"cache_max_entries": self.cache_max_entries,
|
|
231
|
+
"max_parallel_hooks": self.max_parallel_hooks,
|
|
232
|
+
"default_timeout": self.default_timeout,
|
|
233
|
+
"stop_on_critical_failure": self.stop_on_critical_failure,
|
|
234
|
+
"enable_dependency_resolution": self.enable_dependency_resolution,
|
|
235
|
+
"log_cache_stats": self.log_cache_stats,
|
|
236
|
+
"log_execution_timing": self.log_execution_timing,
|
|
237
|
+
"enable_strategy_parallelism": self.enable_strategy_parallelism,
|
|
238
|
+
"enable_adaptive_execution": self.enable_adaptive_execution,
|
|
239
|
+
"max_concurrent_strategies": self.max_concurrent_strategies,
|
|
240
|
+
"enable_tool_proxy": self.enable_tool_proxy,
|
|
241
|
+
"use_precommit_legacy": self.use_precommit_legacy,
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
def save(self, config_path: Path) -> None:
|
|
246
|
+
config_path.write_text(
|
|
247
|
+
yaml.safe_dump(self.to_dict(), sort_keys=True),
|
|
248
|
+
encoding="utf-8",
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# ------------------------------------------------------------------ #
|
|
252
|
+
# Validation / Conversion
|
|
253
|
+
# ------------------------------------------------------------------ #
|
|
254
|
+
|
|
255
|
+
def validate(self) -> list[str]:
|
|
256
|
+
errors: list[str] = []
|
|
257
|
+
|
|
258
|
+
if self.orchestration_mode not in {"acb", "legacy"}:
|
|
259
|
+
errors.append("Invalid orchestration_mode; expected 'acb' or 'legacy'")
|
|
260
|
+
|
|
261
|
+
if self.cache_backend not in {"memory", "tool_proxy", "redis"}:
|
|
262
|
+
errors.append(
|
|
263
|
+
"Invalid cache_backend; expected 'memory', 'tool_proxy', or 'redis'"
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
if self.cache_ttl <= 0:
|
|
267
|
+
errors.append("cache_ttl must be positive")
|
|
268
|
+
if self.cache_max_entries <= 0:
|
|
269
|
+
errors.append("cache_max_entries must be positive")
|
|
270
|
+
if self.max_parallel_hooks <= 0:
|
|
271
|
+
errors.append("max_parallel_hooks must be positive")
|
|
272
|
+
if self.default_timeout <= 0:
|
|
273
|
+
errors.append("default_timeout must be positive")
|
|
274
|
+
if self.max_concurrent_strategies <= 0:
|
|
275
|
+
errors.append("max_concurrent_strategies must be positive")
|
|
276
|
+
|
|
277
|
+
return errors
|
|
278
|
+
|
|
279
|
+
def to_orchestrator_settings(self) -> Any:
|
|
280
|
+
from crackerjack.orchestration.hook_orchestrator import HookOrchestratorSettings
|
|
281
|
+
|
|
282
|
+
execution_mode = self.orchestration_mode
|
|
283
|
+
if not self.enable_orchestration:
|
|
284
|
+
execution_mode = "legacy"
|
|
285
|
+
|
|
286
|
+
return HookOrchestratorSettings(
|
|
287
|
+
execution_mode=execution_mode,
|
|
288
|
+
enable_caching=self.enable_caching,
|
|
289
|
+
cache_backend=self.cache_backend,
|
|
290
|
+
max_parallel_hooks=self.max_parallel_hooks,
|
|
291
|
+
default_timeout=self.default_timeout,
|
|
292
|
+
enable_adaptive_execution=self.enable_adaptive_execution,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
# Convenience re-export for legacy imports
|
|
297
|
+
__all__ = ["OrchestrationConfig"]
|