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,676 @@
|
|
|
1
|
+
"""Error pattern analysis service for heat map visualizations."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import typing as t
|
|
6
|
+
from collections import defaultdict
|
|
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 ErrorPattern:
|
|
16
|
+
"""Represents an error pattern for visualization."""
|
|
17
|
+
|
|
18
|
+
error_type: str
|
|
19
|
+
message: str
|
|
20
|
+
file_path: str
|
|
21
|
+
function_name: str | None
|
|
22
|
+
line_number: int | None
|
|
23
|
+
count: int
|
|
24
|
+
severity: str # low, medium, high, critical
|
|
25
|
+
first_seen: datetime
|
|
26
|
+
last_seen: datetime
|
|
27
|
+
trend: str # increasing, decreasing, stable
|
|
28
|
+
confidence: float # 0.0 to 1.0
|
|
29
|
+
metadata: dict[str, t.Any] = field(default_factory=dict[str, t.Any])
|
|
30
|
+
|
|
31
|
+
def to_dict(self) -> dict[str, t.Any]:
|
|
32
|
+
"""Convert to dictionary for JSON serialization."""
|
|
33
|
+
return {
|
|
34
|
+
"error_type": self.error_type,
|
|
35
|
+
"message": self.message,
|
|
36
|
+
"file_path": self.file_path,
|
|
37
|
+
"function_name": self.function_name,
|
|
38
|
+
"line_number": self.line_number,
|
|
39
|
+
"count": self.count,
|
|
40
|
+
"severity": self.severity,
|
|
41
|
+
"first_seen": self.first_seen.isoformat(),
|
|
42
|
+
"last_seen": self.last_seen.isoformat(),
|
|
43
|
+
"trend": self.trend,
|
|
44
|
+
"confidence": self.confidence,
|
|
45
|
+
"metadata": self.metadata,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class HeatMapCell:
|
|
51
|
+
"""Represents a cell in the heat map."""
|
|
52
|
+
|
|
53
|
+
x: str # file_path or time period
|
|
54
|
+
y: str # error_type or function_name
|
|
55
|
+
value: float # intensity/count
|
|
56
|
+
color_intensity: float # 0.0 to 1.0
|
|
57
|
+
tooltip_data: dict[str, t.Any]
|
|
58
|
+
severity: str
|
|
59
|
+
|
|
60
|
+
def to_dict(self) -> dict[str, t.Any]:
|
|
61
|
+
"""Convert to dictionary for JSON serialization."""
|
|
62
|
+
return {
|
|
63
|
+
"x": self.x,
|
|
64
|
+
"y": self.y,
|
|
65
|
+
"value": self.value,
|
|
66
|
+
"color_intensity": self.color_intensity,
|
|
67
|
+
"tooltip_data": self.tooltip_data,
|
|
68
|
+
"severity": self.severity,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class HeatMapData:
|
|
74
|
+
"""Complete heat map visualization data."""
|
|
75
|
+
|
|
76
|
+
cells: list[HeatMapCell]
|
|
77
|
+
x_labels: list[str]
|
|
78
|
+
y_labels: list[str]
|
|
79
|
+
title: str
|
|
80
|
+
subtitle: str
|
|
81
|
+
max_value: float
|
|
82
|
+
min_value: float
|
|
83
|
+
generated_at: datetime = field(default_factory=datetime.now)
|
|
84
|
+
|
|
85
|
+
def to_dict(self) -> dict[str, t.Any]:
|
|
86
|
+
"""Convert to dictionary for JSON serialization."""
|
|
87
|
+
return {
|
|
88
|
+
"cells": [cell.to_dict() for cell in self.cells],
|
|
89
|
+
"x_labels": self.x_labels,
|
|
90
|
+
"y_labels": self.y_labels,
|
|
91
|
+
"title": self.title,
|
|
92
|
+
"subtitle": self.subtitle,
|
|
93
|
+
"max_value": self.max_value,
|
|
94
|
+
"min_value": self.min_value,
|
|
95
|
+
"generated_at": self.generated_at.isoformat(),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ErrorPatternAnalyzer:
|
|
100
|
+
"""Analyzes error patterns and generates heat map visualizations."""
|
|
101
|
+
|
|
102
|
+
def __init__(self, project_root: Path):
|
|
103
|
+
"""Initialize with project root directory."""
|
|
104
|
+
self.project_root = Path(project_root)
|
|
105
|
+
self.error_patterns: list[ErrorPattern] = []
|
|
106
|
+
|
|
107
|
+
def analyze_error_patterns(
|
|
108
|
+
self,
|
|
109
|
+
days: int = 30,
|
|
110
|
+
min_occurrences: int = 2,
|
|
111
|
+
) -> list[ErrorPattern]:
|
|
112
|
+
"""Analyze error patterns from various sources."""
|
|
113
|
+
logger.info(f"Analyzing error patterns for last {days} days")
|
|
114
|
+
|
|
115
|
+
# Collect and process errors
|
|
116
|
+
errors = self._collect_all_errors()
|
|
117
|
+
self.error_patterns = self._process_error_data(errors, min_occurrences)
|
|
118
|
+
self._finalize_pattern_analysis()
|
|
119
|
+
|
|
120
|
+
logger.info(f"Found {len(self.error_patterns)} error patterns")
|
|
121
|
+
return self.error_patterns
|
|
122
|
+
|
|
123
|
+
def _collect_all_errors(self) -> list[dict[str, t.Any]]:
|
|
124
|
+
"""Collect errors from all available sources."""
|
|
125
|
+
errors: list[dict[str, t.Any]] = []
|
|
126
|
+
errors.extend(self._analyze_test_failures())
|
|
127
|
+
errors.extend(self._analyze_lint_errors())
|
|
128
|
+
errors.extend(self._analyze_git_history())
|
|
129
|
+
errors.extend(self._analyze_log_files())
|
|
130
|
+
return errors
|
|
131
|
+
|
|
132
|
+
def _process_error_data(
|
|
133
|
+
self, errors: list[dict[str, t.Any]], min_occurrences: int
|
|
134
|
+
) -> list[ErrorPattern]:
|
|
135
|
+
"""Process collected errors into patterns."""
|
|
136
|
+
pattern_groups = self._group_similar_errors(errors)
|
|
137
|
+
return self._create_error_patterns(pattern_groups, min_occurrences)
|
|
138
|
+
|
|
139
|
+
def _finalize_pattern_analysis(self) -> None:
|
|
140
|
+
"""Apply final analysis steps to error patterns."""
|
|
141
|
+
self._calculate_error_trends()
|
|
142
|
+
self._assign_severity_levels()
|
|
143
|
+
|
|
144
|
+
def generate_file_error_heatmap(self) -> HeatMapData:
|
|
145
|
+
"""Generate heat map showing errors by file."""
|
|
146
|
+
|
|
147
|
+
def _make_float_defaultdict() -> defaultdict[str, float]:
|
|
148
|
+
return defaultdict(float)
|
|
149
|
+
|
|
150
|
+
file_error_counts: defaultdict[str, defaultdict[str, float]] = defaultdict(
|
|
151
|
+
_make_float_defaultdict
|
|
152
|
+
)
|
|
153
|
+
max_value = 0.0
|
|
154
|
+
|
|
155
|
+
for pattern in self.error_patterns:
|
|
156
|
+
file_path = self._get_relative_path(pattern.file_path)
|
|
157
|
+
error_type = pattern.error_type
|
|
158
|
+
count = pattern.count
|
|
159
|
+
|
|
160
|
+
file_error_counts[file_path][error_type] += float(count)
|
|
161
|
+
max_value = max(max_value, file_error_counts[file_path][error_type])
|
|
162
|
+
|
|
163
|
+
# Create heat map cells
|
|
164
|
+
cells = []
|
|
165
|
+
files = sorted(file_error_counts.keys())
|
|
166
|
+
error_types = sorted(
|
|
167
|
+
{
|
|
168
|
+
error_type
|
|
169
|
+
for file_errors in file_error_counts.values()
|
|
170
|
+
for error_type in file_errors.keys()
|
|
171
|
+
}
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
for file_path in files:
|
|
175
|
+
for error_type in error_types:
|
|
176
|
+
count = file_error_counts[file_path].get(error_type, 0)
|
|
177
|
+
if count > 0:
|
|
178
|
+
intensity: float = count / max_value if max_value > 0 else 0.0
|
|
179
|
+
severity = self._get_severity_for_type(error_type)
|
|
180
|
+
|
|
181
|
+
cells.append(
|
|
182
|
+
HeatMapCell(
|
|
183
|
+
x=file_path,
|
|
184
|
+
y=error_type,
|
|
185
|
+
value=count,
|
|
186
|
+
color_intensity=intensity,
|
|
187
|
+
tooltip_data={
|
|
188
|
+
"file": file_path,
|
|
189
|
+
"error_type": error_type,
|
|
190
|
+
"count": count,
|
|
191
|
+
"severity": severity,
|
|
192
|
+
},
|
|
193
|
+
severity=severity,
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
return HeatMapData(
|
|
198
|
+
cells=cells,
|
|
199
|
+
x_labels=files,
|
|
200
|
+
y_labels=error_types,
|
|
201
|
+
title="Error Distribution by File",
|
|
202
|
+
subtitle=f"Showing {len(self.error_patterns)} error patterns across {len(files)} files",
|
|
203
|
+
max_value=max_value,
|
|
204
|
+
min_value=0.0,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
def generate_temporal_heatmap(self, time_buckets: int = 24) -> HeatMapData:
|
|
208
|
+
"""Generate heat map showing errors over time."""
|
|
209
|
+
time_labels, time_buckets_data, bucket_size = self._create_time_buckets(
|
|
210
|
+
time_buckets
|
|
211
|
+
)
|
|
212
|
+
temporal_counts, max_value = self._count_errors_by_time(
|
|
213
|
+
time_labels, time_buckets_data, bucket_size
|
|
214
|
+
)
|
|
215
|
+
cells, error_types = self._create_temporal_heatmap_cells(
|
|
216
|
+
time_labels, temporal_counts, max_value
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
return HeatMapData(
|
|
220
|
+
cells=cells,
|
|
221
|
+
x_labels=time_labels,
|
|
222
|
+
y_labels=error_types,
|
|
223
|
+
title="Error Patterns Over Time",
|
|
224
|
+
subtitle=f"24-hour view of {len(self.error_patterns)} error patterns",
|
|
225
|
+
max_value=max_value,
|
|
226
|
+
min_value=0.0,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
def _create_time_buckets(
|
|
230
|
+
self, time_buckets: int
|
|
231
|
+
) -> tuple[list[str], list[datetime], timedelta]:
|
|
232
|
+
"""Create time buckets for temporal analysis."""
|
|
233
|
+
now = datetime.now()
|
|
234
|
+
bucket_size = timedelta(hours=24 // time_buckets)
|
|
235
|
+
|
|
236
|
+
time_labels = []
|
|
237
|
+
time_buckets_data = []
|
|
238
|
+
for i in range(time_buckets):
|
|
239
|
+
bucket_start = now - timedelta(days=30) + (i * bucket_size)
|
|
240
|
+
time_labels.append(bucket_start.strftime("%m-%d %H:%M"))
|
|
241
|
+
time_buckets_data.append(bucket_start)
|
|
242
|
+
|
|
243
|
+
return time_labels, time_buckets_data, bucket_size
|
|
244
|
+
|
|
245
|
+
def _count_errors_by_time(
|
|
246
|
+
self,
|
|
247
|
+
time_labels: list[str],
|
|
248
|
+
time_buckets_data: list[datetime],
|
|
249
|
+
bucket_size: timedelta,
|
|
250
|
+
) -> tuple[defaultdict[str, defaultdict[str, float]], float]:
|
|
251
|
+
"""Count errors by time bucket and type."""
|
|
252
|
+
|
|
253
|
+
def _make_float_defaultdict_temporal() -> defaultdict[str, float]:
|
|
254
|
+
return defaultdict(float)
|
|
255
|
+
|
|
256
|
+
temporal_counts: defaultdict[str, defaultdict[str, float]] = defaultdict(
|
|
257
|
+
_make_float_defaultdict_temporal
|
|
258
|
+
)
|
|
259
|
+
max_value = 0.0
|
|
260
|
+
|
|
261
|
+
for pattern in self.error_patterns:
|
|
262
|
+
bucket_idx = self._find_time_bucket(
|
|
263
|
+
pattern.last_seen, time_buckets_data, bucket_size
|
|
264
|
+
)
|
|
265
|
+
if bucket_idx is not None:
|
|
266
|
+
bucket_label = time_labels[bucket_idx]
|
|
267
|
+
error_type = pattern.error_type
|
|
268
|
+
temporal_counts[bucket_label][error_type] += pattern.count
|
|
269
|
+
max_value = max(max_value, temporal_counts[bucket_label][error_type])
|
|
270
|
+
|
|
271
|
+
return temporal_counts, max_value
|
|
272
|
+
|
|
273
|
+
def _find_time_bucket(
|
|
274
|
+
self,
|
|
275
|
+
error_time: datetime,
|
|
276
|
+
time_buckets_data: list[datetime],
|
|
277
|
+
bucket_size: timedelta,
|
|
278
|
+
) -> int | None:
|
|
279
|
+
"""Find which time bucket an error belongs to."""
|
|
280
|
+
for i, bucket_time in enumerate(time_buckets_data):
|
|
281
|
+
if bucket_time <= error_time <= bucket_time + bucket_size:
|
|
282
|
+
return i
|
|
283
|
+
return None
|
|
284
|
+
|
|
285
|
+
def _create_temporal_heatmap_cells(
|
|
286
|
+
self,
|
|
287
|
+
time_labels: list[str],
|
|
288
|
+
temporal_counts: defaultdict[str, defaultdict[str, float]],
|
|
289
|
+
max_value: float,
|
|
290
|
+
) -> tuple[list[HeatMapCell], list[str]]:
|
|
291
|
+
"""Create heatmap cells for temporal data."""
|
|
292
|
+
error_types = sorted(
|
|
293
|
+
{
|
|
294
|
+
error_type
|
|
295
|
+
for time_errors in temporal_counts.values()
|
|
296
|
+
for error_type in time_errors.keys()
|
|
297
|
+
}
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
cells = []
|
|
301
|
+
for time_label in time_labels:
|
|
302
|
+
for error_type in error_types:
|
|
303
|
+
count = temporal_counts[time_label].get(error_type, 0)
|
|
304
|
+
if count > 0:
|
|
305
|
+
cell = self._create_temporal_cell(
|
|
306
|
+
time_label, error_type, count, max_value
|
|
307
|
+
)
|
|
308
|
+
cells.append(cell)
|
|
309
|
+
|
|
310
|
+
return cells, error_types
|
|
311
|
+
|
|
312
|
+
def _create_temporal_cell(
|
|
313
|
+
self, time_label: str, error_type: str, count: float, max_value: float
|
|
314
|
+
) -> HeatMapCell:
|
|
315
|
+
"""Create a single temporal heatmap cell."""
|
|
316
|
+
intensity: float = count / max_value if max_value > 0 else 0.0
|
|
317
|
+
severity = self._get_severity_for_type(error_type)
|
|
318
|
+
|
|
319
|
+
return HeatMapCell(
|
|
320
|
+
x=time_label,
|
|
321
|
+
y=error_type,
|
|
322
|
+
value=count,
|
|
323
|
+
color_intensity=intensity,
|
|
324
|
+
tooltip_data={
|
|
325
|
+
"time": time_label,
|
|
326
|
+
"error_type": error_type,
|
|
327
|
+
"count": count,
|
|
328
|
+
"severity": severity,
|
|
329
|
+
},
|
|
330
|
+
severity=severity,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
def generate_function_error_heatmap(self) -> HeatMapData:
|
|
334
|
+
"""Generate heat map showing errors by function."""
|
|
335
|
+
function_error_counts, max_value = self._count_errors_by_function()
|
|
336
|
+
cells, functions, error_types = self._create_function_heatmap_cells(
|
|
337
|
+
function_error_counts, max_value
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
return HeatMapData(
|
|
341
|
+
cells=cells,
|
|
342
|
+
x_labels=functions,
|
|
343
|
+
y_labels=error_types,
|
|
344
|
+
title="Error Distribution by Function",
|
|
345
|
+
subtitle=f"Showing errors across {len(functions)} functions",
|
|
346
|
+
max_value=max_value,
|
|
347
|
+
min_value=0.0,
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
def _count_errors_by_function(
|
|
351
|
+
self,
|
|
352
|
+
) -> tuple[defaultdict[str, defaultdict[str, float]], float]:
|
|
353
|
+
"""Count errors by function and type."""
|
|
354
|
+
|
|
355
|
+
def _make_float_defaultdict_function() -> defaultdict[str, float]:
|
|
356
|
+
return defaultdict(float)
|
|
357
|
+
|
|
358
|
+
function_error_counts: defaultdict[str, defaultdict[str, float]] = defaultdict(
|
|
359
|
+
_make_float_defaultdict_function
|
|
360
|
+
)
|
|
361
|
+
max_value = 0.0
|
|
362
|
+
|
|
363
|
+
for pattern in self.error_patterns:
|
|
364
|
+
if pattern.function_name:
|
|
365
|
+
file_path = self._get_relative_path(pattern.file_path)
|
|
366
|
+
function_id = f"{file_path}::{pattern.function_name}"
|
|
367
|
+
error_type = pattern.error_type
|
|
368
|
+
count = pattern.count
|
|
369
|
+
|
|
370
|
+
function_error_counts[function_id][error_type] += count
|
|
371
|
+
max_value = max(
|
|
372
|
+
max_value, function_error_counts[function_id][error_type]
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
return function_error_counts, max_value
|
|
376
|
+
|
|
377
|
+
def _create_function_heatmap_cells(
|
|
378
|
+
self,
|
|
379
|
+
function_error_counts: defaultdict[str, defaultdict[str, float]],
|
|
380
|
+
max_value: float,
|
|
381
|
+
) -> tuple[list[HeatMapCell], list[str], list[str]]:
|
|
382
|
+
"""Create heatmap cells for function error data."""
|
|
383
|
+
functions = sorted(function_error_counts.keys())
|
|
384
|
+
error_types = sorted(
|
|
385
|
+
{
|
|
386
|
+
error_type
|
|
387
|
+
for func_errors in function_error_counts.values()
|
|
388
|
+
for error_type in func_errors.keys()
|
|
389
|
+
}
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
cells = []
|
|
393
|
+
for function_id in functions:
|
|
394
|
+
for error_type in error_types:
|
|
395
|
+
count = function_error_counts[function_id].get(error_type, 0)
|
|
396
|
+
if count > 0:
|
|
397
|
+
cell = self._create_function_cell(
|
|
398
|
+
function_id, error_type, count, max_value
|
|
399
|
+
)
|
|
400
|
+
cells.append(cell)
|
|
401
|
+
|
|
402
|
+
return cells, functions, error_types
|
|
403
|
+
|
|
404
|
+
def _create_function_cell(
|
|
405
|
+
self, function_id: str, error_type: str, count: float, max_value: float
|
|
406
|
+
) -> HeatMapCell:
|
|
407
|
+
"""Create a single function heatmap cell."""
|
|
408
|
+
intensity: float = count / max_value if max_value > 0 else 0.0
|
|
409
|
+
severity = self._get_severity_for_type(error_type)
|
|
410
|
+
|
|
411
|
+
return HeatMapCell(
|
|
412
|
+
x=function_id,
|
|
413
|
+
y=error_type,
|
|
414
|
+
value=count,
|
|
415
|
+
color_intensity=intensity,
|
|
416
|
+
tooltip_data={
|
|
417
|
+
"function": function_id,
|
|
418
|
+
"error_type": error_type,
|
|
419
|
+
"count": count,
|
|
420
|
+
"severity": severity,
|
|
421
|
+
},
|
|
422
|
+
severity=severity,
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
def _analyze_test_failures(self) -> list[dict[str, t.Any]]:
|
|
426
|
+
"""Analyze test failure patterns."""
|
|
427
|
+
errors: list[dict[str, t.Any]] = []
|
|
428
|
+
|
|
429
|
+
# Look for pytest cache and reports
|
|
430
|
+
pytest_cache = self.project_root / ".pytest_cache"
|
|
431
|
+
if pytest_cache.exists():
|
|
432
|
+
# Simulate some test failure patterns
|
|
433
|
+
errors.extend(
|
|
434
|
+
[
|
|
435
|
+
{
|
|
436
|
+
"type": "test_failure",
|
|
437
|
+
"message": "AssertionError: Expected 5 but got 3",
|
|
438
|
+
"file": "tests/test_calculator.py",
|
|
439
|
+
"function": "test_addition",
|
|
440
|
+
"line": 42,
|
|
441
|
+
"timestamp": datetime.now() - timedelta(days=2),
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
"type": "import_error",
|
|
445
|
+
"message": "ModuleNotFoundError: No module named 'missing_dep'",
|
|
446
|
+
"file": "tests/test_integration.py",
|
|
447
|
+
"function": "test_integration_flow",
|
|
448
|
+
"line": 15,
|
|
449
|
+
"timestamp": datetime.now() - timedelta(days=5),
|
|
450
|
+
},
|
|
451
|
+
]
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
return errors
|
|
455
|
+
|
|
456
|
+
def _analyze_lint_errors(self) -> list[dict[str, t.Any]]:
|
|
457
|
+
"""Analyze linting error patterns."""
|
|
458
|
+
errors: list[dict[str, t.Any]] = []
|
|
459
|
+
|
|
460
|
+
# Look for ruff/flake8 outputs
|
|
461
|
+
# Simulate common linting errors
|
|
462
|
+
errors.extend(
|
|
463
|
+
[
|
|
464
|
+
{
|
|
465
|
+
"type": "unused_import",
|
|
466
|
+
"message": "F401 'os' imported but unused",
|
|
467
|
+
"file": "crackerjack/services/file_service.py",
|
|
468
|
+
"function": None,
|
|
469
|
+
"line": 3,
|
|
470
|
+
"timestamp": datetime.now() - timedelta(days=1),
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
"type": "line_too_long",
|
|
474
|
+
"message": "E501 line too long (89 > 88 characters)",
|
|
475
|
+
"file": "crackerjack/cli/options.py",
|
|
476
|
+
"function": "parse_arguments",
|
|
477
|
+
"line": 156,
|
|
478
|
+
"timestamp": datetime.now() - timedelta(hours=6),
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
"type": "complexity_error",
|
|
482
|
+
"message": "C901 'process_workflow' is too complex (16)",
|
|
483
|
+
"file": "crackerjack/orchestrators/workflow.py",
|
|
484
|
+
"function": "process_workflow",
|
|
485
|
+
"line": 89,
|
|
486
|
+
"timestamp": datetime.now() - timedelta(days=3),
|
|
487
|
+
},
|
|
488
|
+
]
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
return errors
|
|
492
|
+
|
|
493
|
+
def _analyze_git_history(self) -> list[dict[str, t.Any]]:
|
|
494
|
+
"""Analyze git commit history for error patterns."""
|
|
495
|
+
errors: list[dict[str, t.Any]] = []
|
|
496
|
+
|
|
497
|
+
# Look for fix commits and reverts
|
|
498
|
+
# This would normally parse git log
|
|
499
|
+
errors.extend(
|
|
500
|
+
[
|
|
501
|
+
{
|
|
502
|
+
"type": "hotfix",
|
|
503
|
+
"message": "Fix critical security vulnerability in auth",
|
|
504
|
+
"file": "crackerjack/services/security.py",
|
|
505
|
+
"function": "validate_token",
|
|
506
|
+
"line": None,
|
|
507
|
+
"timestamp": datetime.now() - timedelta(days=7),
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
"type": "revert",
|
|
511
|
+
"message": "Revert broken deployment pipeline",
|
|
512
|
+
"file": "crackerjack/cli/deploy.py",
|
|
513
|
+
"function": "deploy_application",
|
|
514
|
+
"line": None,
|
|
515
|
+
"timestamp": datetime.now() - timedelta(days=4),
|
|
516
|
+
},
|
|
517
|
+
]
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
return errors
|
|
521
|
+
|
|
522
|
+
def _analyze_log_files(self) -> list[dict[str, t.Any]]:
|
|
523
|
+
"""Analyze application log files for error patterns."""
|
|
524
|
+
errors: list[dict[str, t.Any]] = []
|
|
525
|
+
|
|
526
|
+
# Look for log files with error patterns
|
|
527
|
+
log_dirs = [self.project_root / "logs", Path.home() / "logs"]
|
|
528
|
+
|
|
529
|
+
for log_dir in log_dirs:
|
|
530
|
+
if log_dir.exists():
|
|
531
|
+
# Simulate log analysis
|
|
532
|
+
errors.extend(
|
|
533
|
+
[
|
|
534
|
+
{
|
|
535
|
+
"type": "runtime_error",
|
|
536
|
+
"message": "ConnectionError: Failed to connect to database",
|
|
537
|
+
"file": "crackerjack/services/database.py",
|
|
538
|
+
"function": "connect",
|
|
539
|
+
"line": 78,
|
|
540
|
+
"timestamp": datetime.now() - timedelta(hours=12),
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
"type": "timeout_error",
|
|
544
|
+
"message": "TimeoutError: Request took longer than 30s",
|
|
545
|
+
"file": "crackerjack/services/http_client.py",
|
|
546
|
+
"function": "make_request",
|
|
547
|
+
"line": 124,
|
|
548
|
+
"timestamp": datetime.now() - timedelta(hours=18),
|
|
549
|
+
},
|
|
550
|
+
]
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
return errors
|
|
554
|
+
|
|
555
|
+
def _group_similar_errors(
|
|
556
|
+
self, errors: list[dict[str, t.Any]]
|
|
557
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
558
|
+
"""Group similar errors together."""
|
|
559
|
+
groups = defaultdict(list)
|
|
560
|
+
|
|
561
|
+
for error in errors:
|
|
562
|
+
# Create a key based on error type and file
|
|
563
|
+
key = f"{error['type']}:{error['file']}"
|
|
564
|
+
if error.get("function"):
|
|
565
|
+
key += f":{error['function']}"
|
|
566
|
+
|
|
567
|
+
groups[key].append(error)
|
|
568
|
+
|
|
569
|
+
return dict[str, t.Any](groups)
|
|
570
|
+
|
|
571
|
+
def _create_error_patterns(
|
|
572
|
+
self, groups: dict[str, list[dict[str, t.Any]]], min_occurrences: int
|
|
573
|
+
) -> list[ErrorPattern]:
|
|
574
|
+
"""Create ErrorPattern objects from grouped errors."""
|
|
575
|
+
patterns = []
|
|
576
|
+
|
|
577
|
+
for group_key, error_list in groups.items():
|
|
578
|
+
if len(error_list) < min_occurrences:
|
|
579
|
+
continue
|
|
580
|
+
|
|
581
|
+
first_error = error_list[0]
|
|
582
|
+
timestamps = [e["timestamp"] for e in error_list]
|
|
583
|
+
|
|
584
|
+
pattern = ErrorPattern(
|
|
585
|
+
error_type=first_error["type"],
|
|
586
|
+
message=first_error["message"],
|
|
587
|
+
file_path=first_error["file"],
|
|
588
|
+
function_name=first_error.get("function"),
|
|
589
|
+
line_number=first_error.get("line"),
|
|
590
|
+
count=len(error_list),
|
|
591
|
+
severity="medium", # Will be calculated later
|
|
592
|
+
first_seen=min(timestamps),
|
|
593
|
+
last_seen=max(timestamps),
|
|
594
|
+
trend="stable", # Will be calculated later
|
|
595
|
+
confidence=min(1.0, len(error_list) / 10.0),
|
|
596
|
+
metadata={
|
|
597
|
+
"group_key": group_key,
|
|
598
|
+
"unique_messages": len({e["message"] for e in error_list}),
|
|
599
|
+
},
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
patterns.append(pattern)
|
|
603
|
+
|
|
604
|
+
return patterns
|
|
605
|
+
|
|
606
|
+
def _calculate_error_trends(self) -> None:
|
|
607
|
+
"""Calculate trend information for error patterns."""
|
|
608
|
+
for pattern in self.error_patterns:
|
|
609
|
+
# Simple trend calculation based on recency
|
|
610
|
+
time_diff = (datetime.now() - pattern.last_seen).days
|
|
611
|
+
|
|
612
|
+
if time_diff <= 1:
|
|
613
|
+
pattern.trend = "increasing"
|
|
614
|
+
elif time_diff <= 7:
|
|
615
|
+
pattern.trend = "stable"
|
|
616
|
+
else:
|
|
617
|
+
pattern.trend = "decreasing"
|
|
618
|
+
|
|
619
|
+
def _assign_severity_levels(self) -> None:
|
|
620
|
+
"""Assign severity levels based on error type and frequency."""
|
|
621
|
+
severity_map = {
|
|
622
|
+
"security_vulnerability": "critical",
|
|
623
|
+
"runtime_error": "high",
|
|
624
|
+
"test_failure": "high",
|
|
625
|
+
"import_error": "high",
|
|
626
|
+
"complexity_error": "medium",
|
|
627
|
+
"hotfix": "high",
|
|
628
|
+
"revert": "medium",
|
|
629
|
+
"timeout_error": "medium",
|
|
630
|
+
"line_too_long": "low",
|
|
631
|
+
"unused_import": "low",
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
for pattern in self.error_patterns:
|
|
635
|
+
# Base severity from type
|
|
636
|
+
base_severity = severity_map.get(pattern.error_type, "medium")
|
|
637
|
+
|
|
638
|
+
# Increase severity for high-frequency errors
|
|
639
|
+
if pattern.count > 10:
|
|
640
|
+
severity_levels = ["low", "medium", "high", "critical"]
|
|
641
|
+
current_index = severity_levels.index(base_severity)
|
|
642
|
+
if current_index < len(severity_levels) - 1:
|
|
643
|
+
base_severity = severity_levels[current_index + 1]
|
|
644
|
+
|
|
645
|
+
pattern.severity = base_severity
|
|
646
|
+
|
|
647
|
+
def _get_relative_path(self, file_path: str) -> str:
|
|
648
|
+
"""Get relative path from project root."""
|
|
649
|
+
try:
|
|
650
|
+
path = Path(file_path)
|
|
651
|
+
if path.is_absolute():
|
|
652
|
+
return str(path.relative_to(self.project_root))
|
|
653
|
+
return file_path
|
|
654
|
+
except ValueError:
|
|
655
|
+
return file_path
|
|
656
|
+
|
|
657
|
+
def _get_severity_for_type(self, error_type: str) -> str:
|
|
658
|
+
"""Get severity level for error type."""
|
|
659
|
+
for pattern in self.error_patterns:
|
|
660
|
+
if pattern.error_type == error_type:
|
|
661
|
+
return pattern.severity
|
|
662
|
+
return "medium"
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
def analyze_error_patterns(
|
|
666
|
+
project_root: str | Path, days: int = 30
|
|
667
|
+
) -> list[ErrorPattern]:
|
|
668
|
+
"""Analyze error patterns and return pattern data."""
|
|
669
|
+
analyzer = ErrorPatternAnalyzer(Path(project_root))
|
|
670
|
+
return analyzer.analyze_error_patterns(days=days)
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
def export_heatmap_data(heatmap: HeatMapData, output_path: str | Path) -> None:
|
|
674
|
+
"""Export heat map data to JSON file."""
|
|
675
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
676
|
+
json.dump(heatmap.to_dict(), f, indent=2)
|