crackerjack 0.37.9__py3-none-any.whl → 0.45.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +30 -1
- crackerjack/__main__.py +342 -1263
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +27 -5
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/{rust_tool_manager.py → lsp/_manager.py} +3 -3
- crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
- crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +40 -12
- crackerjack/agents/base.py +1 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +49 -53
- crackerjack/agents/dry_agent.py +187 -3
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +6 -8
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/performance_agent.py +121 -1152
- crackerjack/agents/refactoring_agent.py +156 -655
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +19 -1605
- crackerjack/api.py +5 -7
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +1 -1
- crackerjack/cli/cache_handlers.py +23 -18
- crackerjack/cli/cache_handlers_enhanced.py +1 -4
- crackerjack/cli/facade.py +70 -8
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +249 -49
- crackerjack/cli/interactive.py +8 -5
- crackerjack/cli/options.py +203 -110
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +60 -24
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +256 -0
- crackerjack/config/global_lock_config.py +191 -54
- crackerjack/config/hooks.py +188 -16
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/async_workflow_orchestrator.py +79 -53
- crackerjack/core/autofix_coordinator.py +22 -9
- crackerjack/core/container.py +10 -9
- crackerjack/core/enhanced_container.py +9 -9
- crackerjack/core/performance.py +1 -1
- crackerjack/core/performance_monitor.py +5 -3
- crackerjack/core/phase_coordinator.py +1018 -634
- crackerjack/core/proactive_workflow.py +3 -3
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +167 -23
- crackerjack/core/session_coordinator.py +187 -382
- crackerjack/core/timeout_manager.py +161 -44
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +1247 -953
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/ai_templates.py +1 -1
- crackerjack/documentation/dual_output_generator.py +11 -9
- crackerjack/documentation/reference_generator.py +104 -59
- crackerjack/dynamic_config.py +52 -61
- crackerjack/errors.py +1 -1
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +2 -0
- crackerjack/executors/async_hook_executor.py +539 -77
- crackerjack/executors/cached_hook_executor.py +3 -3
- crackerjack/executors/hook_executor.py +967 -102
- crackerjack/executors/hook_lock_manager.py +31 -22
- crackerjack/executors/individual_hook_executor.py +66 -32
- crackerjack/executors/lsp_aware_hook_executor.py +136 -57
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +23 -7
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +8 -9
- crackerjack/intelligence/README.md +557 -0
- crackerjack/interactive.py +37 -10
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/async_hook_manager.py +41 -57
- crackerjack/managers/hook_manager.py +449 -79
- crackerjack/managers/publish_manager.py +81 -36
- crackerjack/managers/test_command_builder.py +290 -12
- crackerjack/managers/test_executor.py +93 -8
- crackerjack/managers/test_manager.py +1082 -75
- crackerjack/managers/test_progress.py +118 -26
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/cache.py +25 -2
- crackerjack/mcp/client_runner.py +35 -18
- crackerjack/mcp/context.py +9 -9
- crackerjack/mcp/dashboard.py +24 -8
- crackerjack/mcp/enhanced_progress_monitor.py +34 -23
- crackerjack/mcp/file_monitor.py +27 -6
- crackerjack/mcp/progress_components.py +45 -34
- crackerjack/mcp/progress_monitor.py +6 -9
- crackerjack/mcp/rate_limiter.py +11 -7
- crackerjack/mcp/server.py +2 -0
- crackerjack/mcp/server_core.py +187 -55
- crackerjack/mcp/service_watchdog.py +12 -9
- crackerjack/mcp/task_manager.py +2 -2
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +2 -0
- crackerjack/mcp/tools/core_tools.py +75 -52
- crackerjack/mcp/tools/execution_tools.py +87 -31
- crackerjack/mcp/tools/intelligence_tools.py +2 -2
- crackerjack/mcp/tools/proactive_tools.py +1 -1
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +180 -132
- crackerjack/mcp/tools/workflow_executor.py +87 -46
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/app.py +11 -1
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +27 -4
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +16 -2930
- crackerjack/mcp/websocket/server.py +1 -3
- crackerjack/mcp/websocket/websocket_handler.py +107 -6
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +10 -1
- crackerjack/models/config.py +639 -22
- crackerjack/models/config_adapter.py +6 -6
- crackerjack/models/protocols.py +1167 -23
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +19 -3
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/ai_agent_watchdog.py +5 -4
- crackerjack/monitoring/metrics_collector.py +4 -3
- crackerjack/monitoring/regression_prevention.py +4 -3
- crackerjack/monitoring/websocket_server.py +4 -241
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +20 -67
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +13 -6
- crackerjack/orchestration/execution_strategies.py +6 -6
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +1 -1
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/hooks.py +3 -2
- crackerjack/plugins/loader.py +3 -3
- crackerjack/plugins/managers.py +1 -1
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +8 -21
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/api_extractor.py +5 -3
- crackerjack/services/bounded_status_operations.py +45 -5
- crackerjack/services/cache.py +249 -318
- crackerjack/services/changelog_automation.py +7 -3
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +83 -39
- crackerjack/services/config_merge.py +9 -6
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +13 -26
- crackerjack/services/coverage_badge_service.py +6 -4
- crackerjack/services/coverage_ratchet.py +53 -27
- crackerjack/services/debug.py +18 -7
- crackerjack/services/dependency_analyzer.py +4 -4
- crackerjack/services/dependency_monitor.py +13 -13
- crackerjack/services/documentation_generator.py +4 -2
- crackerjack/services/documentation_service.py +62 -33
- crackerjack/services/enhanced_filesystem.py +81 -27
- crackerjack/services/enterprise_optimizer.py +1 -1
- crackerjack/services/error_pattern_analyzer.py +10 -10
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +5 -7
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +80 -109
- crackerjack/services/git.py +99 -5
- crackerjack/services/health_metrics.py +4 -6
- crackerjack/services/heatmap_generator.py +12 -3
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +101 -49
- crackerjack/services/log_manager.py +2 -2
- crackerjack/services/logging.py +120 -68
- crackerjack/services/lsp_client.py +12 -12
- crackerjack/services/memory_optimizer.py +27 -22
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
- crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
- crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
- crackerjack/services/parallel_executor.py +166 -55
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +21 -8
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/{quality_baseline.py → quality/quality_baseline.py} +163 -2
- crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
- crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
- crackerjack/services/regex_patterns.py +58 -2987
- crackerjack/services/regex_utils.py +55 -29
- crackerjack/services/secure_status_formatter.py +42 -15
- crackerjack/services/secure_subprocess.py +35 -2
- crackerjack/services/security.py +16 -8
- crackerjack/services/server_manager.py +40 -51
- crackerjack/services/smart_scheduling.py +46 -6
- crackerjack/services/status_authentication.py +3 -3
- crackerjack/services/thread_safe_status_collector.py +1 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +9 -5
- crackerjack/services/unified_config.py +43 -351
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +6 -4
- crackerjack/services/version_checker.py +14 -8
- crackerjack/services/zuban_lsp_service.py +5 -4
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/init.md +2 -12
- crackerjack/slash_commands/run.md +84 -50
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_regex_patterns.py +7 -3
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack/managers/test_manager_backup.py +0 -1075
- crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
- crackerjack/mixins/__init__.py +0 -3
- crackerjack/mixins/error_handling.py +0 -145
- crackerjack/services/config.py +0 -358
- crackerjack/ui/server_panels.py +0 -125
- crackerjack-0.37.9.dist-info/RECORD +0 -231
- /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
- /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
"""Advanced predictive analytics engine for quality metrics and trends."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import statistics
|
|
5
|
+
import typing as t
|
|
6
|
+
from collections import defaultdict, deque
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from datetime import datetime, timedelta
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class TrendAnalysis:
|
|
16
|
+
"""Trend analysis result for a metric."""
|
|
17
|
+
|
|
18
|
+
metric_type: str
|
|
19
|
+
trend_direction: str # increasing, decreasing, stable, volatile
|
|
20
|
+
trend_strength: float # 0.0 to 1.0
|
|
21
|
+
predicted_values: list[float]
|
|
22
|
+
confidence_intervals: list[tuple[float, float]]
|
|
23
|
+
analysis_period: timedelta
|
|
24
|
+
last_updated: datetime
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class Prediction:
|
|
29
|
+
"""Prediction for a specific metric at a future time."""
|
|
30
|
+
|
|
31
|
+
metric_type: str
|
|
32
|
+
predicted_at: datetime
|
|
33
|
+
predicted_for: datetime
|
|
34
|
+
predicted_value: float
|
|
35
|
+
confidence_interval: tuple[float, float]
|
|
36
|
+
confidence_level: float
|
|
37
|
+
model_accuracy: float
|
|
38
|
+
metadata: dict[str, t.Any] = field(default_factory=dict[str, t.Any])
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class CapacityForecast:
|
|
43
|
+
"""Capacity planning forecast."""
|
|
44
|
+
|
|
45
|
+
resource_type: str
|
|
46
|
+
current_usage: float
|
|
47
|
+
predicted_usage: list[tuple[datetime, float]]
|
|
48
|
+
capacity_threshold: float
|
|
49
|
+
estimated_exhaustion: datetime | None
|
|
50
|
+
recommended_actions: list[str]
|
|
51
|
+
confidence: float
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class MovingAveragePredictor:
|
|
55
|
+
"""Simple moving average predictor."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, window_size: int = 10):
|
|
58
|
+
self.window_size = window_size
|
|
59
|
+
|
|
60
|
+
def predict(self, values: list[float], periods: int = 1) -> list[float]:
|
|
61
|
+
"""Predict future values using moving average."""
|
|
62
|
+
if len(values) < self.window_size:
|
|
63
|
+
return [values[-1]] * periods if values else [0.0] * periods
|
|
64
|
+
|
|
65
|
+
recent_values = values[-self.window_size :]
|
|
66
|
+
ma = statistics.mean(recent_values)
|
|
67
|
+
|
|
68
|
+
return [ma] * periods
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class LinearTrendPredictor:
|
|
72
|
+
"""Linear trend-based predictor."""
|
|
73
|
+
|
|
74
|
+
def predict(self, values: list[float], periods: int = 1) -> list[float]:
|
|
75
|
+
"""Predict future values using linear regression."""
|
|
76
|
+
if len(values) < 2:
|
|
77
|
+
return [values[-1]] * periods if values else [0.0] * periods
|
|
78
|
+
|
|
79
|
+
# Simple linear regression
|
|
80
|
+
n = len(values)
|
|
81
|
+
x = list[t.Any](range(n))
|
|
82
|
+
y = values
|
|
83
|
+
|
|
84
|
+
# Calculate slope and intercept
|
|
85
|
+
x_mean = statistics.mean(x)
|
|
86
|
+
y_mean = statistics.mean(y)
|
|
87
|
+
|
|
88
|
+
numerator = sum((x[i] - x_mean) * (y[i] - y_mean) for i in range(n))
|
|
89
|
+
denominator = sum((x[i] - x_mean) ** 2 for i in range(n))
|
|
90
|
+
|
|
91
|
+
if denominator == 0:
|
|
92
|
+
return [y_mean] * periods
|
|
93
|
+
|
|
94
|
+
slope = numerator / denominator
|
|
95
|
+
intercept = y_mean - slope * x_mean
|
|
96
|
+
|
|
97
|
+
# Predict future values
|
|
98
|
+
predictions = []
|
|
99
|
+
for i in range(1, periods + 1):
|
|
100
|
+
future_x = n + i - 1
|
|
101
|
+
prediction = slope * future_x + intercept
|
|
102
|
+
predictions.append(prediction)
|
|
103
|
+
|
|
104
|
+
return predictions
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class SeasonalPredictor:
|
|
108
|
+
"""Seasonal pattern-based predictor."""
|
|
109
|
+
|
|
110
|
+
def __init__(self, season_length: int = 24):
|
|
111
|
+
self.season_length = season_length
|
|
112
|
+
|
|
113
|
+
def predict(self, values: list[float], periods: int = 1) -> list[float]:
|
|
114
|
+
"""Predict using seasonal patterns."""
|
|
115
|
+
if len(values) < self.season_length:
|
|
116
|
+
return [values[-1]] * periods if values else [0.0] * periods
|
|
117
|
+
|
|
118
|
+
predictions = []
|
|
119
|
+
for i in range(periods):
|
|
120
|
+
# Use seasonal pattern
|
|
121
|
+
season_index = (len(values) + i) % self.season_length
|
|
122
|
+
if season_index < len(values):
|
|
123
|
+
seasonal_value = values[-(self.season_length - season_index)]
|
|
124
|
+
predictions.append(seasonal_value)
|
|
125
|
+
else:
|
|
126
|
+
predictions.append(values[-1])
|
|
127
|
+
|
|
128
|
+
return predictions
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class PredictiveAnalyticsEngine:
|
|
132
|
+
"""Advanced predictive analytics system for quality metrics."""
|
|
133
|
+
|
|
134
|
+
def __init__(self, history_limit: int = 1000):
|
|
135
|
+
"""Initialize predictive analytics engine."""
|
|
136
|
+
self.history_limit = history_limit
|
|
137
|
+
|
|
138
|
+
# Data storage
|
|
139
|
+
self.metric_history: dict[str, deque[tuple[datetime, float]]] = defaultdict(
|
|
140
|
+
lambda: deque[tuple[datetime, float]](maxlen=history_limit)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Predictors
|
|
144
|
+
self.predictors = {
|
|
145
|
+
"moving_average": MovingAveragePredictor(window_size=10),
|
|
146
|
+
"linear_trend": LinearTrendPredictor(),
|
|
147
|
+
"seasonal": SeasonalPredictor(season_length=24),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# Cached analyses
|
|
151
|
+
self.trend_analyses: dict[str, TrendAnalysis] = {}
|
|
152
|
+
self.predictions_cache: dict[str, list[Prediction]] = defaultdict(list)
|
|
153
|
+
|
|
154
|
+
# Configuration
|
|
155
|
+
self.metric_configs = {
|
|
156
|
+
"test_pass_rate": {
|
|
157
|
+
"critical_threshold": 0.8,
|
|
158
|
+
"optimal_range": (0.95, 1.0),
|
|
159
|
+
"predictor": "moving_average",
|
|
160
|
+
},
|
|
161
|
+
"coverage_percentage": {
|
|
162
|
+
"critical_threshold": 0.7,
|
|
163
|
+
"optimal_range": (0.9, 1.0),
|
|
164
|
+
"predictor": "linear_trend",
|
|
165
|
+
},
|
|
166
|
+
"execution_time": {
|
|
167
|
+
"critical_threshold": 300.0,
|
|
168
|
+
"predictor": "seasonal",
|
|
169
|
+
},
|
|
170
|
+
"memory_usage": {
|
|
171
|
+
"critical_threshold": 1024.0,
|
|
172
|
+
"predictor": "linear_trend",
|
|
173
|
+
},
|
|
174
|
+
"complexity_score": {
|
|
175
|
+
"critical_threshold": 15.0,
|
|
176
|
+
"predictor": "moving_average",
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
def add_metric(
|
|
181
|
+
self,
|
|
182
|
+
metric_type: str,
|
|
183
|
+
value: float,
|
|
184
|
+
timestamp: datetime | None = None,
|
|
185
|
+
) -> None:
|
|
186
|
+
"""Add new metric data point."""
|
|
187
|
+
if timestamp is None:
|
|
188
|
+
timestamp = datetime.now()
|
|
189
|
+
|
|
190
|
+
self.metric_history[metric_type].append((timestamp, value))
|
|
191
|
+
|
|
192
|
+
# Update trend analysis if we have enough data
|
|
193
|
+
if len(self.metric_history[metric_type]) >= 10:
|
|
194
|
+
self._update_trend_analysis(metric_type)
|
|
195
|
+
|
|
196
|
+
def _update_trend_analysis(self, metric_type: str) -> None:
|
|
197
|
+
"""Update trend analysis for a metric."""
|
|
198
|
+
history: list[tuple[datetime, float]] = list(self.metric_history[metric_type])
|
|
199
|
+
values = [point[1] for point in history]
|
|
200
|
+
timestamps = [point[0] for point in history]
|
|
201
|
+
|
|
202
|
+
# Calculate trend direction and strength
|
|
203
|
+
trend_direction, trend_strength = self._calculate_trend(values)
|
|
204
|
+
|
|
205
|
+
# Generate predictions
|
|
206
|
+
config = self.metric_configs.get(metric_type, {})
|
|
207
|
+
predictor_name = config.get("predictor", "moving_average")
|
|
208
|
+
predictor = self.predictors[predictor_name]
|
|
209
|
+
|
|
210
|
+
predicted_values = predictor.predict(values, periods=24) # 24 periods ahead
|
|
211
|
+
confidence_intervals = self._calculate_confidence_intervals(
|
|
212
|
+
values, predicted_values
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
self.trend_analyses[metric_type] = TrendAnalysis(
|
|
216
|
+
metric_type=metric_type,
|
|
217
|
+
trend_direction=trend_direction,
|
|
218
|
+
trend_strength=trend_strength,
|
|
219
|
+
predicted_values=predicted_values,
|
|
220
|
+
confidence_intervals=confidence_intervals,
|
|
221
|
+
analysis_period=timestamps[-1] - timestamps[0],
|
|
222
|
+
last_updated=datetime.now(),
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
def _calculate_trend(self, values: list[float]) -> tuple[str, float]:
|
|
226
|
+
"""Calculate trend direction and strength."""
|
|
227
|
+
if len(values) < 3:
|
|
228
|
+
return "stable", 0.0
|
|
229
|
+
|
|
230
|
+
# Use linear regression to determine trend
|
|
231
|
+
n = len(values)
|
|
232
|
+
x: list[int] = list(range(n))
|
|
233
|
+
y = values
|
|
234
|
+
|
|
235
|
+
x_mean = statistics.mean(x)
|
|
236
|
+
y_mean = statistics.mean(y)
|
|
237
|
+
|
|
238
|
+
numerator = sum((x[i] - x_mean) * (y[i] - y_mean) for i in range(n))
|
|
239
|
+
denominator = sum((x[i] - x_mean) ** 2 for i in range(n))
|
|
240
|
+
|
|
241
|
+
if denominator == 0:
|
|
242
|
+
return "stable", 0.0
|
|
243
|
+
|
|
244
|
+
slope = numerator / denominator
|
|
245
|
+
|
|
246
|
+
# Calculate R-squared for trend strength
|
|
247
|
+
y_pred = [slope * xi + (y_mean - slope * x_mean) for xi in x]
|
|
248
|
+
ss_res = sum((y[i] - y_pred[i]) ** 2 for i in range(n))
|
|
249
|
+
ss_tot = sum((y[i] - y_mean) ** 2 for i in range(n))
|
|
250
|
+
|
|
251
|
+
r_squared = 1 - (ss_res / ss_tot) if ss_tot != 0 else 0
|
|
252
|
+
trend_strength = max(0.0, min(1.0, r_squared))
|
|
253
|
+
|
|
254
|
+
# Determine direction
|
|
255
|
+
if abs(slope) < 0.01:
|
|
256
|
+
direction = "stable"
|
|
257
|
+
elif slope > 0:
|
|
258
|
+
direction = "increasing"
|
|
259
|
+
else:
|
|
260
|
+
direction = "decreasing"
|
|
261
|
+
|
|
262
|
+
# Check for volatility
|
|
263
|
+
if trend_strength < 0.3:
|
|
264
|
+
recent_std = statistics.stdev(values[-10:]) if len(values) >= 10 else 0
|
|
265
|
+
overall_std = statistics.stdev(values)
|
|
266
|
+
if recent_std > overall_std * 1.5:
|
|
267
|
+
direction = "volatile"
|
|
268
|
+
|
|
269
|
+
return direction, trend_strength
|
|
270
|
+
|
|
271
|
+
def _calculate_confidence_intervals(
|
|
272
|
+
self, historical: list[float], predictions: list[float]
|
|
273
|
+
) -> list[tuple[float, float]]:
|
|
274
|
+
"""Calculate confidence intervals for predictions."""
|
|
275
|
+
if len(historical) < 2:
|
|
276
|
+
return [(pred, pred) for pred in predictions]
|
|
277
|
+
|
|
278
|
+
# Use historical standard deviation for confidence intervals
|
|
279
|
+
std_dev = statistics.stdev(historical)
|
|
280
|
+
confidence_multiplier = 1.96 # 95% confidence
|
|
281
|
+
|
|
282
|
+
intervals = []
|
|
283
|
+
for pred in predictions:
|
|
284
|
+
lower = pred - confidence_multiplier * std_dev
|
|
285
|
+
upper = pred + confidence_multiplier * std_dev
|
|
286
|
+
intervals.append((lower, upper))
|
|
287
|
+
|
|
288
|
+
return intervals
|
|
289
|
+
|
|
290
|
+
def predict_metric(
|
|
291
|
+
self,
|
|
292
|
+
metric_type: str,
|
|
293
|
+
periods_ahead: int = 1,
|
|
294
|
+
predictor_name: str | None = None,
|
|
295
|
+
) -> list[Prediction]:
|
|
296
|
+
"""Generate predictions for a metric."""
|
|
297
|
+
if metric_type not in self.metric_history:
|
|
298
|
+
return []
|
|
299
|
+
|
|
300
|
+
history: list[tuple[datetime, float]] = list(self.metric_history[metric_type])
|
|
301
|
+
values = [point[1] for point in history]
|
|
302
|
+
last_timestamp = history[-1][0] if history else datetime.now()
|
|
303
|
+
|
|
304
|
+
# Select predictor
|
|
305
|
+
if predictor_name is None:
|
|
306
|
+
config = self.metric_configs.get(metric_type, {})
|
|
307
|
+
predictor_name = t.cast(str, config.get("predictor", "moving_average"))
|
|
308
|
+
|
|
309
|
+
predictor = self.predictors[predictor_name]
|
|
310
|
+
predicted_values = predictor.predict(values, periods_ahead)
|
|
311
|
+
|
|
312
|
+
# Calculate confidence intervals
|
|
313
|
+
confidence_intervals = self._calculate_confidence_intervals(
|
|
314
|
+
values, predicted_values
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# Calculate model accuracy
|
|
318
|
+
accuracy = self._calculate_model_accuracy(metric_type, predictor_name)
|
|
319
|
+
|
|
320
|
+
# Generate predictions
|
|
321
|
+
predictions = []
|
|
322
|
+
for i, (pred_value, conf_interval) in enumerate(
|
|
323
|
+
zip(predicted_values, confidence_intervals)
|
|
324
|
+
):
|
|
325
|
+
prediction_time = last_timestamp + timedelta(hours=i + 1)
|
|
326
|
+
|
|
327
|
+
prediction = Prediction(
|
|
328
|
+
metric_type=metric_type,
|
|
329
|
+
predicted_at=datetime.now(),
|
|
330
|
+
predicted_for=prediction_time,
|
|
331
|
+
predicted_value=pred_value,
|
|
332
|
+
confidence_interval=conf_interval,
|
|
333
|
+
confidence_level=0.95,
|
|
334
|
+
model_accuracy=accuracy,
|
|
335
|
+
metadata={"predictor": predictor_name},
|
|
336
|
+
)
|
|
337
|
+
predictions.append(prediction)
|
|
338
|
+
|
|
339
|
+
# Cache predictions
|
|
340
|
+
self.predictions_cache[metric_type] = predictions
|
|
341
|
+
|
|
342
|
+
return predictions
|
|
343
|
+
|
|
344
|
+
def _calculate_model_accuracy(self, metric_type: str, predictor_name: str) -> float:
|
|
345
|
+
"""Calculate historical accuracy of the prediction model."""
|
|
346
|
+
if len(self.metric_history[metric_type]) < 20:
|
|
347
|
+
return 0.5 # Default accuracy for insufficient data
|
|
348
|
+
|
|
349
|
+
history: list[tuple[datetime, float]] = list(self.metric_history[metric_type])
|
|
350
|
+
values = [point[1] for point in history]
|
|
351
|
+
|
|
352
|
+
# Use last 10 points for validation
|
|
353
|
+
train_data = values[:-10]
|
|
354
|
+
validation_data = values[-10:]
|
|
355
|
+
|
|
356
|
+
if len(train_data) < 5:
|
|
357
|
+
return 0.5
|
|
358
|
+
|
|
359
|
+
# Generate predictions for validation period
|
|
360
|
+
predictor = self.predictors[predictor_name]
|
|
361
|
+
predictions = predictor.predict(train_data, periods=len(validation_data))
|
|
362
|
+
|
|
363
|
+
# Calculate accuracy (inverse of mean absolute error)
|
|
364
|
+
mae = statistics.mean(
|
|
365
|
+
abs(pred - actual) for pred, actual in zip(predictions, validation_data)
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
# Convert to accuracy score (0-1)
|
|
369
|
+
if mae == 0:
|
|
370
|
+
return 1.0
|
|
371
|
+
|
|
372
|
+
avg_value = statistics.mean(validation_data)
|
|
373
|
+
relative_error = mae / abs(avg_value) if avg_value != 0 else mae
|
|
374
|
+
|
|
375
|
+
return max(0.1, min(1.0, 1.0 - relative_error))
|
|
376
|
+
|
|
377
|
+
def analyze_capacity_requirements(
|
|
378
|
+
self, resource_type: str, current_usage: float, threshold: float = 0.8
|
|
379
|
+
) -> CapacityForecast:
|
|
380
|
+
"""Analyze capacity requirements and forecast exhaustion."""
|
|
381
|
+
if resource_type not in self.metric_history:
|
|
382
|
+
return CapacityForecast(
|
|
383
|
+
resource_type=resource_type,
|
|
384
|
+
current_usage=current_usage,
|
|
385
|
+
predicted_usage=[],
|
|
386
|
+
capacity_threshold=threshold,
|
|
387
|
+
estimated_exhaustion=None,
|
|
388
|
+
recommended_actions=["Insufficient data for analysis"],
|
|
389
|
+
confidence=0.0,
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
# Get predictions for the next 30 days
|
|
393
|
+
predictions = self.predict_metric(resource_type, periods_ahead=24 * 30)
|
|
394
|
+
|
|
395
|
+
predicted_usage = [
|
|
396
|
+
(pred.predicted_for, pred.predicted_value) for pred in predictions
|
|
397
|
+
]
|
|
398
|
+
|
|
399
|
+
# Find when threshold will be exceeded
|
|
400
|
+
estimated_exhaustion = None
|
|
401
|
+
for timestamp, usage in predicted_usage:
|
|
402
|
+
if usage >= threshold:
|
|
403
|
+
estimated_exhaustion = timestamp
|
|
404
|
+
break
|
|
405
|
+
|
|
406
|
+
# Generate recommendations
|
|
407
|
+
recommendations = self._generate_capacity_recommendations(
|
|
408
|
+
resource_type, current_usage, threshold, estimated_exhaustion
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
# Calculate confidence based on prediction accuracy
|
|
412
|
+
avg_accuracy = statistics.mean(pred.model_accuracy for pred in predictions)
|
|
413
|
+
|
|
414
|
+
return CapacityForecast(
|
|
415
|
+
resource_type=resource_type,
|
|
416
|
+
current_usage=current_usage,
|
|
417
|
+
predicted_usage=predicted_usage,
|
|
418
|
+
capacity_threshold=threshold,
|
|
419
|
+
estimated_exhaustion=estimated_exhaustion,
|
|
420
|
+
recommended_actions=recommendations,
|
|
421
|
+
confidence=avg_accuracy,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
def _generate_capacity_recommendations(
|
|
425
|
+
self,
|
|
426
|
+
resource_type: str,
|
|
427
|
+
current_usage: float,
|
|
428
|
+
threshold: float,
|
|
429
|
+
estimated_exhaustion: datetime | None,
|
|
430
|
+
) -> list[str]:
|
|
431
|
+
"""Generate capacity planning recommendations."""
|
|
432
|
+
recommendations: list[str] = []
|
|
433
|
+
|
|
434
|
+
utilization = current_usage / threshold if threshold > 0 else 0
|
|
435
|
+
|
|
436
|
+
if estimated_exhaustion:
|
|
437
|
+
days_until = (estimated_exhaustion - datetime.now()).days
|
|
438
|
+
if days_until < 7:
|
|
439
|
+
recommendations.extend(
|
|
440
|
+
(
|
|
441
|
+
f"URGENT: {resource_type} capacity will be exceeded in {days_until} days",
|
|
442
|
+
"Consider immediate scaling or optimization",
|
|
443
|
+
)
|
|
444
|
+
)
|
|
445
|
+
elif days_until < 30:
|
|
446
|
+
recommendations.append(
|
|
447
|
+
f"Plan capacity increase for {resource_type} within {days_until} days"
|
|
448
|
+
)
|
|
449
|
+
else:
|
|
450
|
+
recommendations.append(
|
|
451
|
+
f"Monitor {resource_type} usage, capacity limit expected in {days_until} days"
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
if utilization > 0.7:
|
|
455
|
+
recommendations.extend(
|
|
456
|
+
(
|
|
457
|
+
f"High {resource_type} utilization ({utilization:.1%})",
|
|
458
|
+
"Consider proactive scaling",
|
|
459
|
+
)
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
if not recommendations:
|
|
463
|
+
recommendations.append(f"{resource_type} capacity is within normal limits")
|
|
464
|
+
|
|
465
|
+
return recommendations
|
|
466
|
+
|
|
467
|
+
def get_trend_summary(self) -> dict[str, dict[str, t.Any]]:
|
|
468
|
+
"""Get summary of all trend analyses."""
|
|
469
|
+
summary = {}
|
|
470
|
+
|
|
471
|
+
for metric_type, analysis in self.trend_analyses.items():
|
|
472
|
+
summary[metric_type] = {
|
|
473
|
+
"trend_direction": analysis.trend_direction,
|
|
474
|
+
"trend_strength": analysis.trend_strength,
|
|
475
|
+
"next_predicted_value": analysis.predicted_values[0]
|
|
476
|
+
if analysis.predicted_values
|
|
477
|
+
else None,
|
|
478
|
+
"confidence_range": analysis.confidence_intervals[0]
|
|
479
|
+
if analysis.confidence_intervals
|
|
480
|
+
else None,
|
|
481
|
+
"last_updated": analysis.last_updated.isoformat(),
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return summary
|
|
485
|
+
|
|
486
|
+
def export_analytics_data(self, output_path: str | Path) -> None:
|
|
487
|
+
"""Export analytics data for external analysis."""
|
|
488
|
+
import json
|
|
489
|
+
|
|
490
|
+
data = {
|
|
491
|
+
"trend_analyses": {
|
|
492
|
+
metric_type: {
|
|
493
|
+
"metric_type": analysis.metric_type,
|
|
494
|
+
"trend_direction": analysis.trend_direction,
|
|
495
|
+
"trend_strength": analysis.trend_strength,
|
|
496
|
+
"predicted_values": analysis.predicted_values[:10], # Limit size
|
|
497
|
+
"analysis_period": analysis.analysis_period.total_seconds(),
|
|
498
|
+
"last_updated": analysis.last_updated.isoformat(),
|
|
499
|
+
}
|
|
500
|
+
for metric_type, analysis in self.trend_analyses.items()
|
|
501
|
+
},
|
|
502
|
+
"predictions_summary": {
|
|
503
|
+
metric_type: len(predictions)
|
|
504
|
+
for metric_type, predictions in self.predictions_cache.items()
|
|
505
|
+
},
|
|
506
|
+
"exported_at": datetime.now().isoformat(),
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
510
|
+
json.dump(data, f, indent=2)
|
|
@@ -6,7 +6,8 @@ import re
|
|
|
6
6
|
import typing as t
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from acb.console import Console
|
|
10
|
+
from acb.depends import Inject, depends
|
|
10
11
|
|
|
11
12
|
from ..models.protocols import APIExtractorProtocol
|
|
12
13
|
from .regex_patterns import SAFE_PATTERNS
|
|
@@ -120,7 +121,8 @@ class PythonDocstringParser:
|
|
|
120
121
|
class APIExtractorImpl(APIExtractorProtocol):
|
|
121
122
|
"""Implementation of API documentation extraction from source code."""
|
|
122
123
|
|
|
123
|
-
|
|
124
|
+
@depends.inject
|
|
125
|
+
def __init__(self, console: Inject[Console]) -> None:
|
|
124
126
|
self.console = console
|
|
125
127
|
self.docstring_parser = PythonDocstringParser()
|
|
126
128
|
|
|
@@ -443,7 +445,7 @@ class APIExtractorImpl(APIExtractorProtocol):
|
|
|
443
445
|
for base in node.bases:
|
|
444
446
|
base_name = self._get_node_name(base)
|
|
445
447
|
if "Protocol" in base_name and isinstance(
|
|
446
|
-
service_info["protocols_implemented"], list
|
|
448
|
+
service_info["protocols_implemented"], list
|
|
447
449
|
):
|
|
448
450
|
service_info["protocols_implemented"].append(base_name)
|
|
449
451
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import os
|
|
2
3
|
import time
|
|
3
4
|
import typing as t
|
|
4
5
|
from collections import defaultdict, deque
|
|
@@ -6,6 +7,13 @@ from dataclasses import dataclass, field
|
|
|
6
7
|
from enum import Enum
|
|
7
8
|
from threading import RLock
|
|
8
9
|
|
|
10
|
+
import psutil
|
|
11
|
+
|
|
12
|
+
from crackerjack.models.protocols import (
|
|
13
|
+
BoundedStatusOperationsProtocol,
|
|
14
|
+
ServiceProtocol,
|
|
15
|
+
)
|
|
16
|
+
|
|
9
17
|
from .security_logger import SecurityEventLevel, SecurityEventType, get_security_logger
|
|
10
18
|
|
|
11
19
|
|
|
@@ -59,7 +67,7 @@ class CircuitBreakerOpenError(Exception):
|
|
|
59
67
|
pass
|
|
60
68
|
|
|
61
69
|
|
|
62
|
-
class BoundedStatusOperations:
|
|
70
|
+
class BoundedStatusOperations(BoundedStatusOperationsProtocol, ServiceProtocol):
|
|
63
71
|
def __init__(self, limits: OperationLimits | None = None):
|
|
64
72
|
self.limits = limits or OperationLimits()
|
|
65
73
|
self.security_logger = get_security_logger()
|
|
@@ -91,6 +99,42 @@ class BoundedStatusOperations:
|
|
|
91
99
|
"health_check": "Health check operation",
|
|
92
100
|
}
|
|
93
101
|
|
|
102
|
+
def initialize(self) -> None:
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
def cleanup(self) -> None:
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
def health_check(self) -> bool:
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
def shutdown(self) -> None:
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
def metrics(self) -> dict[str, t.Any]:
|
|
115
|
+
return {}
|
|
116
|
+
|
|
117
|
+
def is_healthy(self) -> bool:
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
def register_resource(self, resource: t.Any) -> None:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
def cleanup_resource(self, resource: t.Any) -> None:
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
def record_error(self, error: Exception) -> None:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
def increment_requests(self) -> None:
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
def get_custom_metric(self, name: str) -> t.Any:
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
def set_custom_metric(self, name: str, value: t.Any) -> None:
|
|
136
|
+
pass
|
|
137
|
+
|
|
94
138
|
async def execute_bounded_operation(
|
|
95
139
|
self,
|
|
96
140
|
operation_type: str,
|
|
@@ -293,10 +337,6 @@ class BoundedStatusOperations:
|
|
|
293
337
|
pass
|
|
294
338
|
|
|
295
339
|
async def _monitor_operation(self, metrics: OperationMetrics) -> None:
|
|
296
|
-
import os
|
|
297
|
-
|
|
298
|
-
import psutil
|
|
299
|
-
|
|
300
340
|
try:
|
|
301
341
|
process = psutil.Process(os.getpid())
|
|
302
342
|
initial_cpu_time = process.cpu_times().user + process.cpu_times().system
|