crackerjack 0.37.9__py3-none-any.whl → 0.45.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +30 -1
- crackerjack/__main__.py +342 -1263
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +27 -5
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/{rust_tool_manager.py → lsp/_manager.py} +3 -3
- crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
- crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +40 -12
- crackerjack/agents/base.py +1 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +49 -53
- crackerjack/agents/dry_agent.py +187 -3
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +6 -8
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/performance_agent.py +121 -1152
- crackerjack/agents/refactoring_agent.py +156 -655
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +19 -1605
- crackerjack/api.py +5 -7
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +1 -1
- crackerjack/cli/cache_handlers.py +23 -18
- crackerjack/cli/cache_handlers_enhanced.py +1 -4
- crackerjack/cli/facade.py +70 -8
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +249 -49
- crackerjack/cli/interactive.py +8 -5
- crackerjack/cli/options.py +203 -110
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +60 -24
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +256 -0
- crackerjack/config/global_lock_config.py +191 -54
- crackerjack/config/hooks.py +188 -16
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/async_workflow_orchestrator.py +79 -53
- crackerjack/core/autofix_coordinator.py +22 -9
- crackerjack/core/container.py +10 -9
- crackerjack/core/enhanced_container.py +9 -9
- crackerjack/core/performance.py +1 -1
- crackerjack/core/performance_monitor.py +5 -3
- crackerjack/core/phase_coordinator.py +1018 -634
- crackerjack/core/proactive_workflow.py +3 -3
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +167 -23
- crackerjack/core/session_coordinator.py +187 -382
- crackerjack/core/timeout_manager.py +161 -44
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +1247 -953
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/ai_templates.py +1 -1
- crackerjack/documentation/dual_output_generator.py +11 -9
- crackerjack/documentation/reference_generator.py +104 -59
- crackerjack/dynamic_config.py +52 -61
- crackerjack/errors.py +1 -1
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +2 -0
- crackerjack/executors/async_hook_executor.py +539 -77
- crackerjack/executors/cached_hook_executor.py +3 -3
- crackerjack/executors/hook_executor.py +967 -102
- crackerjack/executors/hook_lock_manager.py +31 -22
- crackerjack/executors/individual_hook_executor.py +66 -32
- crackerjack/executors/lsp_aware_hook_executor.py +136 -57
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +23 -7
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +8 -9
- crackerjack/intelligence/README.md +557 -0
- crackerjack/interactive.py +37 -10
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/async_hook_manager.py +41 -57
- crackerjack/managers/hook_manager.py +449 -79
- crackerjack/managers/publish_manager.py +81 -36
- crackerjack/managers/test_command_builder.py +290 -12
- crackerjack/managers/test_executor.py +93 -8
- crackerjack/managers/test_manager.py +1082 -75
- crackerjack/managers/test_progress.py +118 -26
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/cache.py +25 -2
- crackerjack/mcp/client_runner.py +35 -18
- crackerjack/mcp/context.py +9 -9
- crackerjack/mcp/dashboard.py +24 -8
- crackerjack/mcp/enhanced_progress_monitor.py +34 -23
- crackerjack/mcp/file_monitor.py +27 -6
- crackerjack/mcp/progress_components.py +45 -34
- crackerjack/mcp/progress_monitor.py +6 -9
- crackerjack/mcp/rate_limiter.py +11 -7
- crackerjack/mcp/server.py +2 -0
- crackerjack/mcp/server_core.py +187 -55
- crackerjack/mcp/service_watchdog.py +12 -9
- crackerjack/mcp/task_manager.py +2 -2
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +2 -0
- crackerjack/mcp/tools/core_tools.py +75 -52
- crackerjack/mcp/tools/execution_tools.py +87 -31
- crackerjack/mcp/tools/intelligence_tools.py +2 -2
- crackerjack/mcp/tools/proactive_tools.py +1 -1
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +180 -132
- crackerjack/mcp/tools/workflow_executor.py +87 -46
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/app.py +11 -1
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +27 -4
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +16 -2930
- crackerjack/mcp/websocket/server.py +1 -3
- crackerjack/mcp/websocket/websocket_handler.py +107 -6
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +10 -1
- crackerjack/models/config.py +639 -22
- crackerjack/models/config_adapter.py +6 -6
- crackerjack/models/protocols.py +1167 -23
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +19 -3
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/ai_agent_watchdog.py +5 -4
- crackerjack/monitoring/metrics_collector.py +4 -3
- crackerjack/monitoring/regression_prevention.py +4 -3
- crackerjack/monitoring/websocket_server.py +4 -241
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +20 -67
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +13 -6
- crackerjack/orchestration/execution_strategies.py +6 -6
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +1 -1
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/hooks.py +3 -2
- crackerjack/plugins/loader.py +3 -3
- crackerjack/plugins/managers.py +1 -1
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +8 -21
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/api_extractor.py +5 -3
- crackerjack/services/bounded_status_operations.py +45 -5
- crackerjack/services/cache.py +249 -318
- crackerjack/services/changelog_automation.py +7 -3
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +83 -39
- crackerjack/services/config_merge.py +9 -6
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +13 -26
- crackerjack/services/coverage_badge_service.py +6 -4
- crackerjack/services/coverage_ratchet.py +53 -27
- crackerjack/services/debug.py +18 -7
- crackerjack/services/dependency_analyzer.py +4 -4
- crackerjack/services/dependency_monitor.py +13 -13
- crackerjack/services/documentation_generator.py +4 -2
- crackerjack/services/documentation_service.py +62 -33
- crackerjack/services/enhanced_filesystem.py +81 -27
- crackerjack/services/enterprise_optimizer.py +1 -1
- crackerjack/services/error_pattern_analyzer.py +10 -10
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +5 -7
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +80 -109
- crackerjack/services/git.py +99 -5
- crackerjack/services/health_metrics.py +4 -6
- crackerjack/services/heatmap_generator.py +12 -3
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +101 -49
- crackerjack/services/log_manager.py +2 -2
- crackerjack/services/logging.py +120 -68
- crackerjack/services/lsp_client.py +12 -12
- crackerjack/services/memory_optimizer.py +27 -22
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
- crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
- crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
- crackerjack/services/parallel_executor.py +166 -55
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +21 -8
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/{quality_baseline.py → quality/quality_baseline.py} +163 -2
- crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
- crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
- crackerjack/services/regex_patterns.py +58 -2987
- crackerjack/services/regex_utils.py +55 -29
- crackerjack/services/secure_status_formatter.py +42 -15
- crackerjack/services/secure_subprocess.py +35 -2
- crackerjack/services/security.py +16 -8
- crackerjack/services/server_manager.py +40 -51
- crackerjack/services/smart_scheduling.py +46 -6
- crackerjack/services/status_authentication.py +3 -3
- crackerjack/services/thread_safe_status_collector.py +1 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +9 -5
- crackerjack/services/unified_config.py +43 -351
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +6 -4
- crackerjack/services/version_checker.py +14 -8
- crackerjack/services/zuban_lsp_service.py +5 -4
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/init.md +2 -12
- crackerjack/slash_commands/run.md +84 -50
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_regex_patterns.py +7 -3
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack/managers/test_manager_backup.py +0 -1075
- crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
- crackerjack/mixins/__init__.py +0 -3
- crackerjack/mixins/error_handling.py +0 -145
- crackerjack/services/config.py +0 -358
- crackerjack/ui/server_panels.py +0 -125
- crackerjack-0.37.9.dist-info/RECORD +0 -231
- /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
- /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
crackerjack/config/hooks.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
+
from crackerjack.config.tool_commands import get_tool_command
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
class HookStage(Enum):
|
|
7
9
|
FAST = "fast"
|
|
@@ -27,15 +29,42 @@ class HookDefinition:
|
|
|
27
29
|
command: list[str]
|
|
28
30
|
timeout: int = 60
|
|
29
31
|
stage: HookStage = HookStage.FAST
|
|
32
|
+
description: str | None = None
|
|
30
33
|
retry_on_failure: bool = False
|
|
31
34
|
is_formatting: bool = False
|
|
32
35
|
manual_stage: bool = False
|
|
33
36
|
config_path: Path | None = None
|
|
34
37
|
security_level: SecurityLevel = SecurityLevel.MEDIUM
|
|
38
|
+
use_precommit_legacy: bool = True # Phase 8.2: Backward compatibility flag
|
|
39
|
+
accepts_file_paths: bool = False # Phase 10.4.4: Can tool process individual files?
|
|
40
|
+
_direct_cmd_cache: list[str] | None = field(default=None, init=False, repr=False)
|
|
35
41
|
|
|
36
42
|
def get_command(self) -> list[str]:
|
|
43
|
+
"""Get the command to execute this hook.
|
|
44
|
+
|
|
45
|
+
Returns the appropriate command based on use_precommit_legacy flag:
|
|
46
|
+
- If use_precommit_legacy=True: Returns pre-commit wrapper command (legacy mode)
|
|
47
|
+
- If use_precommit_legacy=False: Returns direct tool command (Phase 8+ mode)
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
List of command arguments for subprocess execution
|
|
51
|
+
"""
|
|
52
|
+
# Phase 8.2: Direct invocation mode (new behavior)
|
|
53
|
+
if not self.use_precommit_legacy:
|
|
54
|
+
if self._direct_cmd_cache is None:
|
|
55
|
+
try:
|
|
56
|
+
self._direct_cmd_cache = get_tool_command(
|
|
57
|
+
self.name, pkg_path=Path.cwd()
|
|
58
|
+
)
|
|
59
|
+
except KeyError:
|
|
60
|
+
# Fallback to pre-commit if tool not in registry
|
|
61
|
+
# This ensures graceful degradation during migration
|
|
62
|
+
self._direct_cmd_cache = None
|
|
63
|
+
if self._direct_cmd_cache is not None:
|
|
64
|
+
return self._direct_cmd_cache
|
|
65
|
+
|
|
66
|
+
# Legacy mode: Use pre-commit wrapper (Phase 1-7 behavior)
|
|
37
67
|
import shutil
|
|
38
|
-
from pathlib import Path
|
|
39
68
|
|
|
40
69
|
pre_commit_path = None
|
|
41
70
|
current_dir = Path.cwd()
|
|
@@ -53,6 +82,34 @@ class HookDefinition:
|
|
|
53
82
|
cmd.extend([self.name, "--all-files"])
|
|
54
83
|
return cmd
|
|
55
84
|
|
|
85
|
+
def build_command(self, files: list[Path] | None = None) -> list[str]:
|
|
86
|
+
"""Build command with optional file paths for targeted execution.
|
|
87
|
+
|
|
88
|
+
Phase 10.4.4: Enables incremental execution on specific files when supported.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
files: Optional list of file paths to process. If None, processes all files.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Command list with file paths appended if tool accepts them.
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
>>> hook = HookDefinition(
|
|
98
|
+
... name="ruff-check",
|
|
99
|
+
... command=["ruff", "check"],
|
|
100
|
+
... accepts_file_paths=True,
|
|
101
|
+
... )
|
|
102
|
+
>>> hook.build_command([Path("foo.py"), Path("bar.py")])
|
|
103
|
+
["ruff", "check", "foo.py", "bar.py"]
|
|
104
|
+
"""
|
|
105
|
+
base_cmd = self.get_command().copy()
|
|
106
|
+
|
|
107
|
+
# Append file paths if tool accepts them and files are provided
|
|
108
|
+
if files and self.accepts_file_paths:
|
|
109
|
+
base_cmd.extend([str(f) for f in files])
|
|
110
|
+
|
|
111
|
+
return base_cmd
|
|
112
|
+
|
|
56
113
|
|
|
57
114
|
@dataclass
|
|
58
115
|
class HookStrategy:
|
|
@@ -69,74 +126,124 @@ FAST_HOOKS = [
|
|
|
69
126
|
name="validate-regex-patterns",
|
|
70
127
|
command=[],
|
|
71
128
|
is_formatting=True,
|
|
72
|
-
timeout=
|
|
129
|
+
timeout=120, # Increased from 60 to handle larger codebases and prevent timeout issues
|
|
73
130
|
retry_on_failure=True,
|
|
74
131
|
security_level=SecurityLevel.HIGH,
|
|
132
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
75
133
|
),
|
|
76
134
|
HookDefinition(
|
|
77
135
|
name="trailing-whitespace",
|
|
78
136
|
command=[],
|
|
79
137
|
is_formatting=True,
|
|
138
|
+
timeout=120, # Increased from 60 to handle larger codebases and prevent timeout issues
|
|
80
139
|
retry_on_failure=True,
|
|
81
140
|
security_level=SecurityLevel.LOW,
|
|
141
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
142
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level fixer
|
|
82
143
|
),
|
|
83
144
|
HookDefinition(
|
|
84
145
|
name="end-of-file-fixer",
|
|
85
146
|
command=[],
|
|
86
147
|
is_formatting=True,
|
|
148
|
+
timeout=120, # Increased from 60 to handle larger codebases and prevent timeout issues
|
|
87
149
|
retry_on_failure=True,
|
|
88
150
|
security_level=SecurityLevel.LOW,
|
|
151
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
152
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level fixer
|
|
89
153
|
),
|
|
90
154
|
HookDefinition(
|
|
91
155
|
name="check-yaml",
|
|
92
156
|
command=[],
|
|
157
|
+
timeout=60, # Increased from 20 to reduce timeout issues with larger YAML files
|
|
93
158
|
security_level=SecurityLevel.MEDIUM,
|
|
159
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
160
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level validator
|
|
94
161
|
),
|
|
95
162
|
HookDefinition(
|
|
96
163
|
name="check-toml",
|
|
97
164
|
command=[],
|
|
165
|
+
timeout=150, # Increased from 79 to reduce timeout issues with larger TOML files
|
|
98
166
|
security_level=SecurityLevel.MEDIUM,
|
|
167
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
168
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level validator
|
|
99
169
|
),
|
|
100
170
|
HookDefinition(
|
|
101
|
-
name="check-
|
|
171
|
+
name="check-json",
|
|
102
172
|
command=[],
|
|
173
|
+
timeout=90, # Increased from 30 to reduce timeout issues with larger JSON files
|
|
174
|
+
security_level=SecurityLevel.MEDIUM,
|
|
175
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
176
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level validator
|
|
177
|
+
),
|
|
178
|
+
HookDefinition(
|
|
179
|
+
name="check-ast",
|
|
180
|
+
command=[],
|
|
181
|
+
timeout=90, # Increased from 30 to reduce timeout issues with larger Python files
|
|
103
182
|
security_level=SecurityLevel.HIGH,
|
|
183
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
184
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level validator
|
|
104
185
|
),
|
|
105
186
|
HookDefinition(
|
|
106
|
-
name="
|
|
187
|
+
name="format-json",
|
|
188
|
+
command=[],
|
|
189
|
+
is_formatting=True,
|
|
190
|
+
timeout=120, # Increased from 45 to reduce timeout issues with larger JSON files
|
|
191
|
+
retry_on_failure=True,
|
|
192
|
+
security_level=SecurityLevel.LOW,
|
|
193
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
194
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level formatter
|
|
195
|
+
),
|
|
196
|
+
HookDefinition(
|
|
197
|
+
name="check-added-large-files",
|
|
107
198
|
command=[],
|
|
199
|
+
timeout=90, # Increased from 30 to reduce timeout issues with repositories with many files
|
|
108
200
|
security_level=SecurityLevel.HIGH,
|
|
201
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
109
202
|
),
|
|
110
203
|
HookDefinition(
|
|
111
|
-
name="
|
|
204
|
+
name="uv-lock",
|
|
112
205
|
command=[],
|
|
113
|
-
|
|
206
|
+
timeout=60, # Increased from 20 to reduce timeout issues with complex dependency trees
|
|
207
|
+
security_level=SecurityLevel.HIGH,
|
|
208
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
114
209
|
),
|
|
115
210
|
HookDefinition(
|
|
116
211
|
name="codespell",
|
|
117
212
|
command=[],
|
|
213
|
+
timeout=150, # Increased from 45 to reduce timeout issues with large codebases
|
|
118
214
|
security_level=SecurityLevel.LOW,
|
|
215
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
216
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level spell checker
|
|
119
217
|
),
|
|
120
218
|
HookDefinition(
|
|
121
219
|
name="ruff-check",
|
|
122
220
|
command=[],
|
|
123
221
|
is_formatting=True,
|
|
222
|
+
timeout=240, # Increased from 120 to reduce timeout issues with large codebases
|
|
124
223
|
retry_on_failure=True,
|
|
125
224
|
security_level=SecurityLevel.MEDIUM,
|
|
225
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
226
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level Python linter
|
|
126
227
|
),
|
|
127
228
|
HookDefinition(
|
|
128
229
|
name="ruff-format",
|
|
129
230
|
command=[],
|
|
130
231
|
is_formatting=True,
|
|
232
|
+
timeout=240, # Increased from 120 to reduce timeout issues with large codebases
|
|
131
233
|
retry_on_failure=True,
|
|
132
234
|
security_level=SecurityLevel.LOW,
|
|
235
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
236
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level Python formatter
|
|
133
237
|
),
|
|
134
238
|
HookDefinition(
|
|
135
239
|
name="mdformat",
|
|
136
240
|
command=[],
|
|
137
241
|
is_formatting=True,
|
|
242
|
+
timeout=300, # Increased from 120 to reduce timeout issues with larger markdown files
|
|
138
243
|
retry_on_failure=True,
|
|
139
244
|
security_level=SecurityLevel.LOW,
|
|
245
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
246
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level markdown formatter
|
|
140
247
|
),
|
|
141
248
|
]
|
|
142
249
|
|
|
@@ -144,50 +251,111 @@ COMPREHENSIVE_HOOKS = [
|
|
|
144
251
|
HookDefinition(
|
|
145
252
|
name="zuban",
|
|
146
253
|
command=[],
|
|
147
|
-
timeout=
|
|
254
|
+
timeout=240, # Increased from 80 to reduce timeout issues with larger codebases during type checking
|
|
255
|
+
stage=HookStage.COMPREHENSIVE,
|
|
256
|
+
manual_stage=True,
|
|
257
|
+
security_level=SecurityLevel.HIGH, # Changed from CRITICAL to HIGH to allow other hooks to run
|
|
258
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
259
|
+
accepts_file_paths=True, # Phase 10.5: Allow incremental execution on changed files to avoid virtual environments
|
|
260
|
+
),
|
|
261
|
+
HookDefinition(
|
|
262
|
+
name="semgrep",
|
|
263
|
+
command=[],
|
|
264
|
+
timeout=480, # Increased from 240 to reduce timeout issues with comprehensive security scans
|
|
148
265
|
stage=HookStage.COMPREHENSIVE,
|
|
149
266
|
manual_stage=True,
|
|
150
267
|
security_level=SecurityLevel.CRITICAL,
|
|
268
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
269
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level SAST scanner
|
|
151
270
|
),
|
|
152
271
|
HookDefinition(
|
|
153
|
-
name="
|
|
272
|
+
name="pyscn",
|
|
154
273
|
command=[],
|
|
155
|
-
timeout=300,
|
|
274
|
+
timeout=300, # CFG analysis, clone detection, complexity metrics
|
|
275
|
+
stage=HookStage.COMPREHENSIVE,
|
|
276
|
+
manual_stage=True,
|
|
277
|
+
security_level=SecurityLevel.HIGH, # Security + quality analysis
|
|
278
|
+
use_precommit_legacy=False, # Direct invocation
|
|
279
|
+
accepts_file_paths=True, # Can scan specific Python files
|
|
280
|
+
),
|
|
281
|
+
# NOTE: Bandit replaced with Semgrep (using uvx for Python 3.13 isolation)
|
|
282
|
+
# HookDefinition(
|
|
283
|
+
# name="bandit",
|
|
284
|
+
# command=[],
|
|
285
|
+
# timeout=180, # 3 minutes for SAST scanning
|
|
286
|
+
# stage=HookStage.COMPREHENSIVE,
|
|
287
|
+
# manual_stage=True,
|
|
288
|
+
# security_level=SecurityLevel.CRITICAL,
|
|
289
|
+
# use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
290
|
+
# accepts_file_paths=True, # Phase 10.4.4: File-level SAST scanner
|
|
291
|
+
# ),
|
|
292
|
+
HookDefinition(
|
|
293
|
+
name="gitleaks",
|
|
294
|
+
command=[],
|
|
295
|
+
timeout=180, # Increased from 45 to reduce timeout issues with comprehensive security scans
|
|
156
296
|
stage=HookStage.COMPREHENSIVE,
|
|
157
297
|
manual_stage=True,
|
|
158
298
|
security_level=SecurityLevel.CRITICAL,
|
|
299
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
300
|
+
),
|
|
301
|
+
HookDefinition(
|
|
302
|
+
name="pip-audit",
|
|
303
|
+
command=[],
|
|
304
|
+
timeout=180, # Network calls to vulnerability databases
|
|
305
|
+
stage=HookStage.COMPREHENSIVE,
|
|
306
|
+
manual_stage=True,
|
|
307
|
+
security_level=SecurityLevel.CRITICAL, # CVE vulnerabilities are critical
|
|
308
|
+
use_precommit_legacy=False, # Direct invocation
|
|
309
|
+
accepts_file_paths=False, # Scans entire environment/requirements
|
|
159
310
|
),
|
|
160
311
|
HookDefinition(
|
|
161
312
|
name="skylos",
|
|
162
313
|
command=[],
|
|
163
|
-
timeout=
|
|
314
|
+
timeout=180, # Increased from 60 to reduce timeout issues with comprehensive dead code scanning
|
|
164
315
|
stage=HookStage.COMPREHENSIVE,
|
|
165
316
|
manual_stage=True,
|
|
166
317
|
security_level=SecurityLevel.MEDIUM,
|
|
318
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
319
|
+
accepts_file_paths=True, # Phase 10.5: Incremental execution on changed files
|
|
167
320
|
),
|
|
168
321
|
HookDefinition(
|
|
169
322
|
name="refurb",
|
|
170
323
|
command=[],
|
|
171
|
-
timeout=
|
|
324
|
+
timeout=480, # Increased from 240 to reduce timeout issues with comprehensive refactoring analysis
|
|
172
325
|
stage=HookStage.COMPREHENSIVE,
|
|
173
326
|
manual_stage=True,
|
|
174
327
|
security_level=SecurityLevel.MEDIUM,
|
|
328
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
329
|
+
accepts_file_paths=True, # Phase 10.5: Incremental execution on changed files (240s -> ~10s)
|
|
175
330
|
),
|
|
176
331
|
HookDefinition(
|
|
177
332
|
name="creosote",
|
|
178
333
|
command=[],
|
|
179
|
-
timeout=
|
|
334
|
+
timeout=360, # Increased from 180 to reduce timeout issues with comprehensive dependency analysis
|
|
180
335
|
stage=HookStage.COMPREHENSIVE,
|
|
181
336
|
manual_stage=True,
|
|
182
337
|
security_level=SecurityLevel.HIGH,
|
|
338
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
183
339
|
),
|
|
184
340
|
HookDefinition(
|
|
185
341
|
name="complexipy",
|
|
186
342
|
command=[],
|
|
187
|
-
timeout=
|
|
343
|
+
timeout=300, # Increased from 120 to reduce timeout issues with comprehensive complexity analysis
|
|
188
344
|
stage=HookStage.COMPREHENSIVE,
|
|
189
345
|
manual_stage=True,
|
|
190
346
|
security_level=SecurityLevel.MEDIUM,
|
|
347
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
348
|
+
accepts_file_paths=True, # Phase 10.5: Incremental execution on changed files
|
|
349
|
+
),
|
|
350
|
+
HookDefinition(
|
|
351
|
+
name="check-jsonschema",
|
|
352
|
+
command=[],
|
|
353
|
+
timeout=180, # Increased from 60 to reduce timeout issues with complex schema validation
|
|
354
|
+
stage=HookStage.COMPREHENSIVE,
|
|
355
|
+
manual_stage=True,
|
|
356
|
+
security_level=SecurityLevel.HIGH,
|
|
357
|
+
use_precommit_legacy=False, # Phase 8.4: Direct invocation
|
|
358
|
+
accepts_file_paths=True, # Phase 10.4.4: File-level schema validator
|
|
191
359
|
),
|
|
192
360
|
]
|
|
193
361
|
|
|
@@ -195,15 +363,19 @@ COMPREHENSIVE_HOOKS = [
|
|
|
195
363
|
FAST_STRATEGY = HookStrategy(
|
|
196
364
|
name="fast",
|
|
197
365
|
hooks=FAST_HOOKS,
|
|
198
|
-
timeout=60
|
|
366
|
+
timeout=300, # Increased from 60 to accommodate all increased hook timeouts
|
|
199
367
|
retry_policy=RetryPolicy.FORMATTING_ONLY,
|
|
368
|
+
parallel=True, # Phase 6: Enable parallel execution for 2-3x speedup
|
|
369
|
+
max_workers=4, # Optimal concurrency for fast hooks
|
|
200
370
|
)
|
|
201
371
|
|
|
202
372
|
COMPREHENSIVE_STRATEGY = HookStrategy(
|
|
203
373
|
name="comprehensive",
|
|
204
374
|
hooks=COMPREHENSIVE_HOOKS,
|
|
205
|
-
timeout=300
|
|
375
|
+
timeout=1800, # Increased from 300 to accommodate all increased hook timeouts
|
|
206
376
|
retry_policy=RetryPolicy.NONE,
|
|
377
|
+
parallel=True, # Phase 6: Enable parallel execution for 2x speedup
|
|
378
|
+
max_workers=4, # Optimal concurrency for comprehensive hooks
|
|
207
379
|
)
|
|
208
380
|
|
|
209
381
|
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""Settings loader for ACB configuration with YAML support.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for loading ACB Settings from YAML files
|
|
4
|
+
with support for configuration layering and environment-specific overrides.
|
|
5
|
+
|
|
6
|
+
Configuration Priority (highest to lowest):
|
|
7
|
+
1. settings/local.yaml (local overrides, gitignored)
|
|
8
|
+
2. settings/crackerjack.yaml (main configuration)
|
|
9
|
+
3. Default values from Settings class
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
>>> from crackerjack.config import CrackerjackSettings
|
|
13
|
+
>>> settings = CrackerjackSettings.load()
|
|
14
|
+
>>> settings.verbose
|
|
15
|
+
False
|
|
16
|
+
|
|
17
|
+
>>> # Async loading with full ACB initialization
|
|
18
|
+
>>> settings = await CrackerjackSettings.load_async()
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import typing as t
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import TypeVar
|
|
27
|
+
|
|
28
|
+
import yaml
|
|
29
|
+
from acb.config import Settings
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
T = TypeVar("T", bound=Settings)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _load_single_config_file(config_file: Path) -> dict[str, t.Any]:
|
|
37
|
+
"""Load a single config file and return its data."""
|
|
38
|
+
if not config_file.exists():
|
|
39
|
+
logger.debug(f"Configuration file not found: {config_file}")
|
|
40
|
+
return {}
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
with config_file.open() as f:
|
|
44
|
+
loaded_data: t.Any = yaml.safe_load(f)
|
|
45
|
+
if isinstance(loaded_data, dict):
|
|
46
|
+
logger.debug(f"Loaded configuration from {config_file}")
|
|
47
|
+
return loaded_data
|
|
48
|
+
else:
|
|
49
|
+
logger.warning(
|
|
50
|
+
f"Invalid YAML format in {config_file}: expected dict, got {type(loaded_data).__name__}"
|
|
51
|
+
)
|
|
52
|
+
return {}
|
|
53
|
+
except yaml.YAMLError as e:
|
|
54
|
+
logger.error(f"Failed to parse YAML from {config_file}: {e}")
|
|
55
|
+
return {}
|
|
56
|
+
except OSError as e:
|
|
57
|
+
logger.error(f"Failed to read {config_file}: {e}")
|
|
58
|
+
return {}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _merge_config_data(config_files: list[Path]) -> dict[str, t.Any]:
|
|
62
|
+
"""Merge data from all config files."""
|
|
63
|
+
merged_data = {}
|
|
64
|
+
for config_file in config_files:
|
|
65
|
+
file_data = _load_single_config_file(config_file)
|
|
66
|
+
merged_data.update(file_data)
|
|
67
|
+
return merged_data
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def load_settings[T: Settings](
|
|
71
|
+
settings_class: type[T],
|
|
72
|
+
settings_dir: Path | None = None,
|
|
73
|
+
) -> T:
|
|
74
|
+
"""Load settings from YAML files with layered configuration.
|
|
75
|
+
|
|
76
|
+
This function loads configuration from multiple YAML files and merges them
|
|
77
|
+
with priority-based overriding. Unknown YAML fields are silently ignored.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
settings_class: The Settings class to instantiate
|
|
81
|
+
settings_dir: Directory containing YAML files (default: ./settings)
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Initialized Settings instance with merged configuration
|
|
85
|
+
|
|
86
|
+
Configuration Priority (highest to lowest):
|
|
87
|
+
1. settings/local.yaml (local overrides, gitignored)
|
|
88
|
+
2. settings/crackerjack.yaml (main configuration)
|
|
89
|
+
3. Default values from Settings class
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
>>> class MySettings(Settings):
|
|
93
|
+
... debug: bool = False
|
|
94
|
+
... max_workers: int = 4
|
|
95
|
+
>>> settings = load_settings(MySettings)
|
|
96
|
+
>>> settings.debug
|
|
97
|
+
False
|
|
98
|
+
"""
|
|
99
|
+
if settings_dir is None:
|
|
100
|
+
settings_dir = Path.cwd() / "settings"
|
|
101
|
+
|
|
102
|
+
# Configuration files in priority order (lowest to highest)
|
|
103
|
+
config_files = [
|
|
104
|
+
settings_dir / "crackerjack.yaml",
|
|
105
|
+
settings_dir / "local.yaml",
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
# Load and merge data from all config files
|
|
109
|
+
merged_data = _merge_config_data(config_files)
|
|
110
|
+
|
|
111
|
+
# Filter to only fields defined in the Settings class
|
|
112
|
+
# This prevents validation errors from unknown YAML keys
|
|
113
|
+
relevant_data = {
|
|
114
|
+
k: v for k, v in merged_data.items() if k in settings_class.model_fields
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Log filtered fields if any were excluded
|
|
118
|
+
excluded_fields = set(merged_data.keys()) - set(relevant_data.keys())
|
|
119
|
+
if excluded_fields:
|
|
120
|
+
logger.debug(
|
|
121
|
+
f"Ignored unknown configuration fields: {', '.join(sorted(excluded_fields))}"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
logger.info(
|
|
125
|
+
f"Loaded {len(relevant_data)} configuration values for {settings_class.__name__}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Synchronous initialization (safe for module-level code)
|
|
129
|
+
return settings_class(**relevant_data)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
async def load_settings_async[T: Settings](
|
|
133
|
+
settings_class: type[T],
|
|
134
|
+
settings_dir: Path | None = None,
|
|
135
|
+
) -> T:
|
|
136
|
+
"""Load settings asynchronously with full ACB initialization.
|
|
137
|
+
|
|
138
|
+
This function provides the same configuration loading as load_settings()
|
|
139
|
+
but uses ACB's async initialization which includes secret loading and
|
|
140
|
+
other async setup operations.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
settings_class: The Settings class to instantiate
|
|
144
|
+
settings_dir: Directory containing YAML files (default: ./settings)
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Initialized Settings instance with async initialization complete
|
|
148
|
+
|
|
149
|
+
Note:
|
|
150
|
+
Use this for application runtime when async context is available.
|
|
151
|
+
For module-level initialization, use synchronous load_settings().
|
|
152
|
+
|
|
153
|
+
Example:
|
|
154
|
+
>>> settings = await load_settings_async(CrackerjackSettings)
|
|
155
|
+
>>> settings.verbose
|
|
156
|
+
False
|
|
157
|
+
"""
|
|
158
|
+
if settings_dir is None:
|
|
159
|
+
settings_dir = Path.cwd() / "settings"
|
|
160
|
+
|
|
161
|
+
# Configuration files in priority order (lowest to highest)
|
|
162
|
+
config_files = [
|
|
163
|
+
settings_dir / "crackerjack.yaml",
|
|
164
|
+
settings_dir / "local.yaml",
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
# Load and merge YAML data from all files
|
|
168
|
+
merged_data = await _load_yaml_data(config_files)
|
|
169
|
+
|
|
170
|
+
# Process the loaded data
|
|
171
|
+
relevant_data = _filter_relevant_data(merged_data, settings_class)
|
|
172
|
+
_log_filtered_fields(merged_data, relevant_data)
|
|
173
|
+
_log_load_info(settings_class, relevant_data)
|
|
174
|
+
|
|
175
|
+
# Async initialization (loads secrets, performs async setup)
|
|
176
|
+
return await settings_class.create_async(**relevant_data)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
async def _load_yaml_data(config_files: list[Path]) -> dict[str, t.Any]:
|
|
180
|
+
"""Load and merge YAML data from configuration files."""
|
|
181
|
+
merged_data: dict[str, t.Any] = {}
|
|
182
|
+
for config_file in config_files:
|
|
183
|
+
file_data = await _load_single_yaml_file(config_file)
|
|
184
|
+
if file_data is not None:
|
|
185
|
+
merged_data.update(file_data)
|
|
186
|
+
elif not config_file.exists():
|
|
187
|
+
logger.debug(f"Configuration file not found: {config_file}")
|
|
188
|
+
return merged_data
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
async def _load_single_yaml_file(config_file: Path) -> dict[str, t.Any] | None:
|
|
192
|
+
"""Load a single YAML file and return its content."""
|
|
193
|
+
if not config_file.exists():
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
with config_file.open() as f:
|
|
198
|
+
loaded_data: t.Any = yaml.safe_load(f)
|
|
199
|
+
if isinstance(loaded_data, dict):
|
|
200
|
+
logger.debug(f"Loaded configuration from {config_file}")
|
|
201
|
+
return loaded_data
|
|
202
|
+
else:
|
|
203
|
+
logger.warning(
|
|
204
|
+
f"Invalid YAML format in {config_file}: expected dict, got {type(loaded_data).__name__}"
|
|
205
|
+
)
|
|
206
|
+
return {}
|
|
207
|
+
except yaml.YAMLError as e:
|
|
208
|
+
logger.error(f"Failed to parse YAML from {config_file}: {e}")
|
|
209
|
+
return None
|
|
210
|
+
except OSError as e:
|
|
211
|
+
logger.error(f"Failed to read {config_file}: {e}")
|
|
212
|
+
return None
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _filter_relevant_data[T: Settings](
|
|
216
|
+
merged_data: dict[str, t.Any], settings_class: type[T]
|
|
217
|
+
) -> dict[str, t.Any]:
|
|
218
|
+
"""Filter the loaded data to only fields defined in the Settings class."""
|
|
219
|
+
return {k: v for k, v in merged_data.items() if k in settings_class.model_fields}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _log_filtered_fields(
|
|
223
|
+
merged_data: dict[str, t.Any], relevant_data: dict[str, t.Any]
|
|
224
|
+
) -> None:
|
|
225
|
+
"""Log any fields that were excluded due to not being in the settings class."""
|
|
226
|
+
excluded_fields = set(merged_data.keys()) - set(relevant_data.keys())
|
|
227
|
+
if excluded_fields:
|
|
228
|
+
logger.debug(
|
|
229
|
+
f"Ignored unknown configuration fields: {', '.join(sorted(excluded_fields))}"
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _log_load_info[T: Settings](
|
|
234
|
+
settings_class: type[T], relevant_data: dict[str, t.Any]
|
|
235
|
+
) -> None:
|
|
236
|
+
"""Log information about the loaded configuration."""
|
|
237
|
+
logger.info(
|
|
238
|
+
f"Loaded {len(relevant_data)} configuration values for {settings_class.__name__} (async)"
|
|
239
|
+
)
|