crackerjack 0.18.2__py3-none-any.whl → 0.45.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +96 -2
- crackerjack/__main__.py +637 -138
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +39 -0
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/lsp/_base.py +194 -0
- crackerjack/adapters/lsp/_client.py +358 -0
- crackerjack/adapters/lsp/_manager.py +193 -0
- crackerjack/adapters/lsp/skylos.py +283 -0
- crackerjack/adapters/lsp/zuban.py +557 -0
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +66 -0
- crackerjack/agents/architect_agent.py +238 -0
- crackerjack/agents/base.py +167 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +600 -0
- crackerjack/agents/documentation_agent.py +520 -0
- crackerjack/agents/dry_agent.py +585 -0
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +230 -0
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/import_optimization_agent.py +1181 -0
- crackerjack/agents/performance_agent.py +325 -0
- crackerjack/agents/performance_helpers.py +205 -0
- crackerjack/agents/proactive_agent.py +55 -0
- crackerjack/agents/refactoring_agent.py +511 -0
- crackerjack/agents/refactoring_helpers.py +247 -0
- crackerjack/agents/security_agent.py +793 -0
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +570 -0
- crackerjack/agents/test_specialist_agent.py +526 -0
- crackerjack/agents/tracker.py +110 -0
- crackerjack/api.py +647 -0
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/cache_handlers.py +209 -0
- crackerjack/cli/cache_handlers_enhanced.py +680 -0
- crackerjack/cli/facade.py +162 -0
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +700 -0
- crackerjack/cli/interactive.py +488 -0
- crackerjack/cli/options.py +1216 -0
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/utils.py +19 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +1307 -0
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +275 -0
- crackerjack/config/global_lock_config.py +207 -0
- crackerjack/config/hooks.py +390 -0
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +738 -0
- crackerjack/core/autofix_coordinator.py +282 -0
- crackerjack/core/container.py +105 -0
- crackerjack/core/enhanced_container.py +583 -0
- crackerjack/core/file_lifecycle.py +472 -0
- crackerjack/core/performance.py +244 -0
- crackerjack/core/performance_monitor.py +357 -0
- crackerjack/core/phase_coordinator.py +1227 -0
- crackerjack/core/proactive_workflow.py +267 -0
- crackerjack/core/resource_manager.py +425 -0
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +601 -0
- crackerjack/core/session_coordinator.py +239 -0
- crackerjack/core/timeout_manager.py +563 -0
- crackerjack/core/websocket_lifecycle.py +410 -0
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +2243 -0
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +767 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +1065 -0
- crackerjack/dynamic_config.py +678 -0
- crackerjack/errors.py +378 -0
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +13 -0
- crackerjack/executors/async_hook_executor.py +938 -0
- crackerjack/executors/cached_hook_executor.py +316 -0
- crackerjack/executors/hook_executor.py +1295 -0
- crackerjack/executors/hook_lock_manager.py +708 -0
- crackerjack/executors/individual_hook_executor.py +739 -0
- crackerjack/executors/lsp_aware_hook_executor.py +349 -0
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +433 -0
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +93 -0
- crackerjack/intelligence/README.md +557 -0
- crackerjack/intelligence/__init__.py +37 -0
- crackerjack/intelligence/adaptive_learning.py +693 -0
- crackerjack/intelligence/agent_orchestrator.py +485 -0
- crackerjack/intelligence/agent_registry.py +377 -0
- crackerjack/intelligence/agent_selector.py +439 -0
- crackerjack/intelligence/integration.py +250 -0
- crackerjack/interactive.py +719 -0
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +585 -0
- crackerjack/managers/publish_manager.py +631 -0
- crackerjack/managers/test_command_builder.py +391 -0
- crackerjack/managers/test_executor.py +474 -0
- crackerjack/managers/test_manager.py +1357 -0
- crackerjack/managers/test_progress.py +187 -0
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +352 -0
- crackerjack/mcp/client_runner.py +121 -0
- crackerjack/mcp/context.py +802 -0
- crackerjack/mcp/dashboard.py +657 -0
- crackerjack/mcp/enhanced_progress_monitor.py +493 -0
- crackerjack/mcp/file_monitor.py +394 -0
- crackerjack/mcp/progress_components.py +607 -0
- crackerjack/mcp/progress_monitor.py +1016 -0
- crackerjack/mcp/rate_limiter.py +336 -0
- crackerjack/mcp/server.py +24 -0
- crackerjack/mcp/server_core.py +526 -0
- crackerjack/mcp/service_watchdog.py +505 -0
- crackerjack/mcp/state.py +407 -0
- crackerjack/mcp/task_manager.py +259 -0
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +19 -0
- crackerjack/mcp/tools/core_tools.py +469 -0
- crackerjack/mcp/tools/error_analyzer.py +283 -0
- crackerjack/mcp/tools/execution_tools.py +384 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
- crackerjack/mcp/tools/intelligence_tools.py +264 -0
- crackerjack/mcp/tools/monitoring_tools.py +628 -0
- crackerjack/mcp/tools/proactive_tools.py +367 -0
- crackerjack/mcp/tools/progress_tools.py +222 -0
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +358 -0
- crackerjack/mcp/tools/workflow_executor.py +699 -0
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +54 -0
- crackerjack/mcp/websocket/endpoints.py +492 -0
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +406 -0
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
- crackerjack/mcp/websocket/server.py +174 -0
- crackerjack/mcp/websocket/websocket_handler.py +276 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +40 -0
- crackerjack/models/config.py +730 -0
- crackerjack/models/config_adapter.py +265 -0
- crackerjack/models/protocols.py +1535 -0
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/resource_protocols.py +299 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +173 -0
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +405 -0
- crackerjack/monitoring/metrics_collector.py +427 -0
- crackerjack/monitoring/regression_prevention.py +580 -0
- crackerjack/monitoring/websocket_server.py +406 -0
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +894 -0
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +180 -0
- crackerjack/orchestration/execution_strategies.py +361 -0
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +647 -0
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +254 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +264 -0
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/security/__init__.py +0 -0
- crackerjack/security/audit.py +197 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +9 -0
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/ai/contextual_ai_assistant.py +542 -0
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +617 -0
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +530 -0
- crackerjack/services/cache.py +369 -0
- crackerjack/services/changelog_automation.py +399 -0
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +132 -0
- crackerjack/services/config_merge.py +546 -0
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +493 -0
- crackerjack/services/coverage_badge_service.py +173 -0
- crackerjack/services/coverage_ratchet.py +381 -0
- crackerjack/services/debug.py +733 -0
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +622 -0
- crackerjack/services/documentation_generator.py +493 -0
- crackerjack/services/documentation_service.py +704 -0
- crackerjack/services/enhanced_filesystem.py +497 -0
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +149 -0
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +381 -0
- crackerjack/services/git.py +422 -0
- crackerjack/services/health_metrics.py +615 -0
- crackerjack/services/heatmap_generator.py +744 -0
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +823 -0
- crackerjack/services/input_validator.py +668 -0
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +289 -0
- crackerjack/services/logging.py +228 -0
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +414 -0
- crackerjack/services/metrics.py +587 -0
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/monitoring/performance_benchmarks.py +410 -0
- crackerjack/services/monitoring/performance_cache.py +388 -0
- crackerjack/services/monitoring/performance_monitor.py +569 -0
- crackerjack/services/parallel_executor.py +527 -0
- crackerjack/services/pattern_cache.py +333 -0
- crackerjack/services/pattern_detector.py +478 -0
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +523 -0
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/quality/quality_baseline.py +395 -0
- crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
- crackerjack/services/quality/quality_intelligence.py +949 -0
- crackerjack/services/regex_patterns.py +58 -0
- crackerjack/services/regex_utils.py +483 -0
- crackerjack/services/secure_path_utils.py +524 -0
- crackerjack/services/secure_status_formatter.py +450 -0
- crackerjack/services/secure_subprocess.py +635 -0
- crackerjack/services/security.py +239 -0
- crackerjack/services/security_logger.py +495 -0
- crackerjack/services/server_manager.py +411 -0
- crackerjack/services/smart_scheduling.py +167 -0
- crackerjack/services/status_authentication.py +460 -0
- crackerjack/services/status_security_manager.py +315 -0
- crackerjack/services/terminal_utils.py +0 -0
- crackerjack/services/thread_safe_status_collector.py +441 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +43 -0
- crackerjack/services/unified_config.py +115 -0
- crackerjack/services/validation_rate_limiter.py +220 -0
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +461 -0
- crackerjack/services/version_checker.py +223 -0
- crackerjack/services/websocket_resource_limiter.py +438 -0
- crackerjack/services/zuban_lsp_service.py +391 -0
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/__init__.py +59 -0
- crackerjack/slash_commands/init.md +112 -0
- crackerjack/slash_commands/run.md +197 -0
- crackerjack/slash_commands/status.md +127 -0
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_input_validator_patterns.py +236 -0
- crackerjack/tools/validate_regex_patterns.py +188 -0
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/__init__.py +1 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- crackerjack-0.45.2.dist-info/METADATA +1678 -0
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -14
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/.pre-commit-config.yaml +0 -91
- crackerjack/.pytest_cache/.gitignore +0 -2
- crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
- crackerjack/.pytest_cache/README.md +0 -8
- crackerjack/.pytest_cache/v/cache/nodeids +0 -1
- crackerjack/.pytest_cache/v/cache/stepwise +0 -1
- crackerjack/.ruff_cache/.gitignore +0 -1
- crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
- crackerjack/crackerjack.py +0 -855
- crackerjack/pyproject.toml +0 -214
- crackerjack-0.18.2.dist-info/METADATA +0 -420
- crackerjack-0.18.2.dist-info/RECORD +0 -59
- crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import hashlib
|
|
3
|
+
import typing as t
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from itertools import starmap
|
|
6
|
+
|
|
7
|
+
from crackerjack.agents.base import (
|
|
8
|
+
AgentContext,
|
|
9
|
+
FixResult,
|
|
10
|
+
Issue,
|
|
11
|
+
IssueType,
|
|
12
|
+
SubAgent,
|
|
13
|
+
agent_registry,
|
|
14
|
+
)
|
|
15
|
+
from crackerjack.agents.error_middleware import agent_error_boundary
|
|
16
|
+
from crackerjack.agents.tracker import get_agent_tracker
|
|
17
|
+
from crackerjack.services.cache import CrackerjackCache
|
|
18
|
+
from crackerjack.services.debug import get_ai_agent_debugger
|
|
19
|
+
from crackerjack.services.logging import get_logger
|
|
20
|
+
|
|
21
|
+
ISSUE_TYPE_TO_AGENTS: dict[IssueType, list[str]] = {
|
|
22
|
+
IssueType.FORMATTING: ["FormattingAgent"],
|
|
23
|
+
IssueType.TYPE_ERROR: ["TestCreationAgent", "RefactoringAgent"],
|
|
24
|
+
IssueType.SECURITY: ["SecurityAgent"],
|
|
25
|
+
IssueType.TEST_FAILURE: ["TestSpecialistAgent", "TestCreationAgent"],
|
|
26
|
+
IssueType.IMPORT_ERROR: ["ImportOptimizationAgent"],
|
|
27
|
+
IssueType.COMPLEXITY: ["RefactoringAgent"],
|
|
28
|
+
IssueType.DEAD_CODE: ["RefactoringAgent", "ImportOptimizationAgent"],
|
|
29
|
+
IssueType.DEPENDENCY: ["ImportOptimizationAgent"],
|
|
30
|
+
IssueType.DRY_VIOLATION: ["DRYAgent"],
|
|
31
|
+
IssueType.PERFORMANCE: ["PerformanceAgent"],
|
|
32
|
+
IssueType.DOCUMENTATION: ["DocumentationAgent"],
|
|
33
|
+
IssueType.TEST_ORGANIZATION: ["TestSpecialistAgent"],
|
|
34
|
+
IssueType.COVERAGE_IMPROVEMENT: ["TestCreationAgent"],
|
|
35
|
+
IssueType.REGEX_VALIDATION: ["SecurityAgent", "RefactoringAgent"],
|
|
36
|
+
IssueType.SEMANTIC_CONTEXT: ["SemanticAgent", "ArchitectAgent"],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AgentCoordinator:
|
|
41
|
+
def __init__(
|
|
42
|
+
self, context: AgentContext, cache: CrackerjackCache | None = None
|
|
43
|
+
) -> None:
|
|
44
|
+
self.context = context
|
|
45
|
+
self.agents: list[SubAgent] = []
|
|
46
|
+
self.logger = get_logger(__name__)
|
|
47
|
+
self._issue_cache: dict[str, FixResult] = {}
|
|
48
|
+
self._collaboration_threshold = 0.7
|
|
49
|
+
self.tracker = get_agent_tracker()
|
|
50
|
+
self.debugger = get_ai_agent_debugger()
|
|
51
|
+
self.proactive_mode = True
|
|
52
|
+
self.cache = cache or CrackerjackCache()
|
|
53
|
+
|
|
54
|
+
def initialize_agents(self) -> None:
|
|
55
|
+
self.agents = agent_registry.create_all(self.context)
|
|
56
|
+
agent_types = [a.name for a in self.agents]
|
|
57
|
+
self.logger.info(f"Initialized {len(self.agents)} agents: {agent_types}")
|
|
58
|
+
|
|
59
|
+
self.tracker.register_agents(agent_types)
|
|
60
|
+
self.tracker.set_coordinator_status("active")
|
|
61
|
+
|
|
62
|
+
self.debugger.log_agent_activity(
|
|
63
|
+
agent_name="coordinator",
|
|
64
|
+
activity="agents_initialized",
|
|
65
|
+
metadata={"agent_count": len(self.agents), "agent_types": agent_types},
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
async def handle_issues(self, issues: list[Issue]) -> FixResult:
|
|
69
|
+
if not self.agents:
|
|
70
|
+
self.initialize_agents()
|
|
71
|
+
|
|
72
|
+
if not issues:
|
|
73
|
+
return FixResult(success=True, confidence=1.0)
|
|
74
|
+
|
|
75
|
+
self.logger.info(f"Handling {len(issues)} issues")
|
|
76
|
+
|
|
77
|
+
issues_by_type = self._group_issues_by_type(issues)
|
|
78
|
+
|
|
79
|
+
# Optimization: Run ALL issue types in parallel instead of sequential
|
|
80
|
+
tasks = list[t.Any](
|
|
81
|
+
starmap(self._handle_issues_by_type, issues_by_type.items())
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
85
|
+
|
|
86
|
+
overall_result = FixResult(success=True, confidence=1.0)
|
|
87
|
+
for result in results:
|
|
88
|
+
if isinstance(result, FixResult):
|
|
89
|
+
overall_result = overall_result.merge_with(result)
|
|
90
|
+
else:
|
|
91
|
+
self.logger.error(f"Issue type handling failed: {result}")
|
|
92
|
+
overall_result.success = False
|
|
93
|
+
overall_result.remaining_issues.append(
|
|
94
|
+
f"Type handling failed: {result}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return overall_result
|
|
98
|
+
|
|
99
|
+
async def _handle_issues_by_type(
|
|
100
|
+
self,
|
|
101
|
+
issue_type: IssueType,
|
|
102
|
+
issues: list[Issue],
|
|
103
|
+
) -> FixResult:
|
|
104
|
+
self.logger.info(f"Handling {len(issues)} {issue_type.value} issues")
|
|
105
|
+
|
|
106
|
+
# Fast agent lookup using static mapping
|
|
107
|
+
preferred_agent_names = ISSUE_TYPE_TO_AGENTS.get(issue_type, [])
|
|
108
|
+
specialist_agents = []
|
|
109
|
+
|
|
110
|
+
# First, try to use agents from static mapping for O(1) lookup
|
|
111
|
+
specialist_agents = [
|
|
112
|
+
agent
|
|
113
|
+
for agent in self.agents
|
|
114
|
+
if agent.__class__.__name__ in preferred_agent_names
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
# Fallback: use traditional dynamic lookup if no static match
|
|
118
|
+
if not specialist_agents:
|
|
119
|
+
specialist_agents = [
|
|
120
|
+
agent
|
|
121
|
+
for agent in self.agents
|
|
122
|
+
if issue_type in agent.get_supported_types()
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
if not specialist_agents:
|
|
126
|
+
self.logger.warning(f"No specialist agents for {issue_type.value}")
|
|
127
|
+
return FixResult(
|
|
128
|
+
success=False,
|
|
129
|
+
confidence=0.0,
|
|
130
|
+
remaining_issues=[f"No agents for {issue_type.value} issues"],
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
tasks: list[t.Coroutine[t.Any, t.Any, FixResult]] = []
|
|
134
|
+
for issue in issues:
|
|
135
|
+
best_specialist = await self._find_best_specialist(specialist_agents, issue)
|
|
136
|
+
if best_specialist:
|
|
137
|
+
task = self._handle_with_single_agent(best_specialist, issue)
|
|
138
|
+
tasks.append(task)
|
|
139
|
+
|
|
140
|
+
if not tasks:
|
|
141
|
+
return FixResult(success=False, confidence=0.0)
|
|
142
|
+
|
|
143
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
144
|
+
|
|
145
|
+
combined_result = FixResult(success=True, confidence=1.0)
|
|
146
|
+
for result in results:
|
|
147
|
+
if isinstance(result, FixResult):
|
|
148
|
+
combined_result = combined_result.merge_with(result)
|
|
149
|
+
else:
|
|
150
|
+
self.logger.error(f"Agent task failed: {result}")
|
|
151
|
+
combined_result.success = False
|
|
152
|
+
combined_result.remaining_issues.append(f"Agent failed: {result}")
|
|
153
|
+
|
|
154
|
+
return combined_result
|
|
155
|
+
|
|
156
|
+
async def _find_best_specialist(
|
|
157
|
+
self,
|
|
158
|
+
specialists: list[SubAgent],
|
|
159
|
+
issue: Issue,
|
|
160
|
+
) -> SubAgent | None:
|
|
161
|
+
candidates = await self._score_all_specialists(specialists, issue)
|
|
162
|
+
if not candidates:
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
best_agent, best_score = self._find_highest_scoring_agent(candidates)
|
|
166
|
+
return self._apply_built_in_preference(candidates, best_agent, best_score)
|
|
167
|
+
|
|
168
|
+
async def _score_all_specialists(
|
|
169
|
+
self, specialists: list[SubAgent], issue: Issue
|
|
170
|
+
) -> list[tuple[SubAgent, float]]:
|
|
171
|
+
"""Score all specialist agents for handling an issue."""
|
|
172
|
+
candidates: list[tuple[SubAgent, float]] = []
|
|
173
|
+
|
|
174
|
+
for agent in specialists:
|
|
175
|
+
try:
|
|
176
|
+
score = await agent.can_handle(issue)
|
|
177
|
+
candidates.append((agent, score))
|
|
178
|
+
except Exception as e:
|
|
179
|
+
self.logger.exception(f"Error evaluating specialist {agent.name}: {e}")
|
|
180
|
+
|
|
181
|
+
return candidates
|
|
182
|
+
|
|
183
|
+
def _find_highest_scoring_agent(
|
|
184
|
+
self, candidates: list[tuple[SubAgent, float]]
|
|
185
|
+
) -> tuple[SubAgent | None, float]:
|
|
186
|
+
"""Find the agent with the highest score."""
|
|
187
|
+
best_agent = None
|
|
188
|
+
best_score = 0.0
|
|
189
|
+
|
|
190
|
+
for agent, score in candidates:
|
|
191
|
+
if score > best_score:
|
|
192
|
+
best_score = score
|
|
193
|
+
best_agent = agent
|
|
194
|
+
|
|
195
|
+
return best_agent, best_score
|
|
196
|
+
|
|
197
|
+
def _apply_built_in_preference(
|
|
198
|
+
self,
|
|
199
|
+
candidates: list[tuple[SubAgent, float]],
|
|
200
|
+
best_agent: SubAgent | None,
|
|
201
|
+
best_score: float,
|
|
202
|
+
) -> SubAgent | None:
|
|
203
|
+
"""Apply preference for built-in agents when scores are close."""
|
|
204
|
+
if not best_agent or best_score <= 0:
|
|
205
|
+
return best_agent
|
|
206
|
+
|
|
207
|
+
# Threshold for considering scores "close" (5% difference)
|
|
208
|
+
CLOSE_SCORE_THRESHOLD = 0.05
|
|
209
|
+
|
|
210
|
+
for agent, score in candidates:
|
|
211
|
+
if self._should_prefer_built_in_agent(
|
|
212
|
+
agent, best_agent, score, best_score, CLOSE_SCORE_THRESHOLD
|
|
213
|
+
):
|
|
214
|
+
self._log_built_in_preference(
|
|
215
|
+
agent, score, best_agent, best_score, best_score - score
|
|
216
|
+
)
|
|
217
|
+
return agent
|
|
218
|
+
|
|
219
|
+
return best_agent
|
|
220
|
+
|
|
221
|
+
def _should_prefer_built_in_agent(
|
|
222
|
+
self,
|
|
223
|
+
agent: SubAgent,
|
|
224
|
+
best_agent: SubAgent,
|
|
225
|
+
score: float,
|
|
226
|
+
best_score: float,
|
|
227
|
+
threshold: float,
|
|
228
|
+
) -> bool:
|
|
229
|
+
"""Check if a built-in agent should be preferred over the current best."""
|
|
230
|
+
return (
|
|
231
|
+
agent != best_agent
|
|
232
|
+
and self._is_built_in_agent(agent)
|
|
233
|
+
and 0 < (best_score - score) <= threshold
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
def _log_built_in_preference(
|
|
237
|
+
self,
|
|
238
|
+
agent: SubAgent,
|
|
239
|
+
score: float,
|
|
240
|
+
best_agent: SubAgent,
|
|
241
|
+
best_score: float,
|
|
242
|
+
score_difference: float,
|
|
243
|
+
) -> None:
|
|
244
|
+
"""Log when preferring a built-in agent."""
|
|
245
|
+
self.logger.info(
|
|
246
|
+
f"Preferring built-in agent {agent.name} (score: {score:.2f}) "
|
|
247
|
+
f"over {best_agent.name} (score: {best_score:.2f}) "
|
|
248
|
+
f"due to {score_difference:.2f} threshold preference"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
def _is_built_in_agent(self, agent: SubAgent) -> bool:
|
|
252
|
+
"""Check if agent is a built-in Crackerjack agent."""
|
|
253
|
+
built_in_agent_names = {
|
|
254
|
+
"ArchitectAgent",
|
|
255
|
+
"DocumentationAgent",
|
|
256
|
+
"DRYAgent",
|
|
257
|
+
"FormattingAgent",
|
|
258
|
+
"ImportOptimizationAgent",
|
|
259
|
+
"PerformanceAgent",
|
|
260
|
+
"RefactoringAgent",
|
|
261
|
+
"SecurityAgent",
|
|
262
|
+
"TestCreationAgent",
|
|
263
|
+
"TestSpecialistAgent",
|
|
264
|
+
}
|
|
265
|
+
return agent.__class__.__name__ in built_in_agent_names
|
|
266
|
+
|
|
267
|
+
async def _handle_with_single_agent(
|
|
268
|
+
self,
|
|
269
|
+
agent: SubAgent,
|
|
270
|
+
issue: Issue,
|
|
271
|
+
) -> FixResult:
|
|
272
|
+
self.logger.info(f"Handling issue with {agent.name}: {issue.message[:100]}")
|
|
273
|
+
|
|
274
|
+
# Create cache key from issue content
|
|
275
|
+
issue_hash = self._create_issue_hash(issue)
|
|
276
|
+
|
|
277
|
+
# Check cache for previous agent decision
|
|
278
|
+
cached_decision = self.cache.get_agent_decision(agent.name, issue_hash)
|
|
279
|
+
if cached_decision:
|
|
280
|
+
self.logger.debug(f"Using cached decision for {agent.name}")
|
|
281
|
+
self.tracker.track_agent_complete(agent.name, cached_decision)
|
|
282
|
+
return cached_decision # type: ignore[no-any-return]
|
|
283
|
+
|
|
284
|
+
confidence = await agent.can_handle(issue)
|
|
285
|
+
self.tracker.track_agent_processing(agent.name, issue, confidence)
|
|
286
|
+
|
|
287
|
+
self.debugger.log_agent_activity(
|
|
288
|
+
agent_name=agent.name,
|
|
289
|
+
activity="processing_started",
|
|
290
|
+
issue_id=issue.id,
|
|
291
|
+
confidence=confidence,
|
|
292
|
+
metadata={"issue_type": issue.type.value, "severity": issue.severity.value},
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
result = await self._execute_agent(agent, issue)
|
|
296
|
+
if result.success:
|
|
297
|
+
self.logger.info(f"{agent.name} successfully fixed issue")
|
|
298
|
+
else:
|
|
299
|
+
self.logger.warning(f"{agent.name} failed to fix issue")
|
|
300
|
+
|
|
301
|
+
self.tracker.track_agent_complete(agent.name, result)
|
|
302
|
+
|
|
303
|
+
self.debugger.log_agent_activity(
|
|
304
|
+
agent_name=agent.name,
|
|
305
|
+
activity="processing_completed",
|
|
306
|
+
issue_id=issue.id,
|
|
307
|
+
confidence=result.confidence,
|
|
308
|
+
result={
|
|
309
|
+
"success": result.success,
|
|
310
|
+
"remaining_issues": len(result.remaining_issues),
|
|
311
|
+
},
|
|
312
|
+
metadata={"fix_applied": result.success},
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
return result
|
|
316
|
+
|
|
317
|
+
def _group_issues_by_type(
|
|
318
|
+
self,
|
|
319
|
+
issues: list[Issue],
|
|
320
|
+
) -> dict[IssueType, list[Issue]]:
|
|
321
|
+
grouped: defaultdict[IssueType, list[Issue]] = defaultdict(list)
|
|
322
|
+
for issue in issues:
|
|
323
|
+
grouped[issue.type].append(issue)
|
|
324
|
+
return dict(grouped)
|
|
325
|
+
|
|
326
|
+
def _create_issue_hash(self, issue: Issue) -> str:
|
|
327
|
+
"""Create a hash from issue content for caching decisions."""
|
|
328
|
+
content = (
|
|
329
|
+
f"{issue.type.value}:{issue.message}:{issue.file_path}:{issue.line_number}"
|
|
330
|
+
)
|
|
331
|
+
return hashlib.md5(content.encode(), usedforsecurity=False).hexdigest()
|
|
332
|
+
|
|
333
|
+
def _get_cache_key(self, agent_name: str, issue: Issue) -> str:
|
|
334
|
+
"""Get cache key for agent-issue combination."""
|
|
335
|
+
issue_hash = self._create_issue_hash(issue)
|
|
336
|
+
return f"{agent_name}:{issue_hash}"
|
|
337
|
+
|
|
338
|
+
@agent_error_boundary
|
|
339
|
+
async def _execute_agent(self, agent: SubAgent, issue: Issue) -> FixResult:
|
|
340
|
+
"""Execute agent analysis with centralized error handling."""
|
|
341
|
+
return await self._cached_analyze_and_fix(agent, issue)
|
|
342
|
+
|
|
343
|
+
async def _cached_analyze_and_fix(self, agent: SubAgent, issue: Issue) -> FixResult:
|
|
344
|
+
"""Analyze and fix issue with intelligent caching."""
|
|
345
|
+
cache_key = self._get_cache_key(agent.name, issue)
|
|
346
|
+
|
|
347
|
+
# Check in-memory cache first (fastest)
|
|
348
|
+
if cache_key in self._issue_cache:
|
|
349
|
+
self.logger.debug(f"Using in-memory cache for {agent.name}")
|
|
350
|
+
return self._issue_cache[cache_key]
|
|
351
|
+
|
|
352
|
+
# Check persistent cache
|
|
353
|
+
cached_result = self.cache.get_agent_decision(
|
|
354
|
+
agent.name, self._create_issue_hash(issue)
|
|
355
|
+
)
|
|
356
|
+
if cached_result:
|
|
357
|
+
self.logger.debug(f"Using persistent cache for {agent.name}")
|
|
358
|
+
# Store in memory cache for even faster future access
|
|
359
|
+
self._issue_cache[cache_key] = cached_result
|
|
360
|
+
return cached_result # type: ignore[no-any-return]
|
|
361
|
+
|
|
362
|
+
# No cache hit - perform actual analysis
|
|
363
|
+
result = await agent.analyze_and_fix(issue)
|
|
364
|
+
|
|
365
|
+
# Cache successful results with high confidence
|
|
366
|
+
if result.success and result.confidence > 0.7:
|
|
367
|
+
self._issue_cache[cache_key] = result
|
|
368
|
+
self.cache.set_agent_decision(
|
|
369
|
+
agent.name, self._create_issue_hash(issue), result
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
return result
|
|
373
|
+
|
|
374
|
+
def get_agent_capabilities(self) -> dict[str, dict[str, t.Any]]:
|
|
375
|
+
if not self.agents:
|
|
376
|
+
self.initialize_agents()
|
|
377
|
+
|
|
378
|
+
capabilities = {}
|
|
379
|
+
for agent in self.agents:
|
|
380
|
+
capabilities[agent.name] = {
|
|
381
|
+
"supported_types": [t.value for t in agent.get_supported_types()],
|
|
382
|
+
"class": agent.__class__.__name__,
|
|
383
|
+
}
|
|
384
|
+
return capabilities
|
|
385
|
+
|
|
386
|
+
async def handle_issues_proactively(self, issues: list[Issue]) -> FixResult:
|
|
387
|
+
if not self.proactive_mode:
|
|
388
|
+
return await self.handle_issues(issues)
|
|
389
|
+
|
|
390
|
+
if not self.agents:
|
|
391
|
+
self.initialize_agents()
|
|
392
|
+
|
|
393
|
+
if not issues:
|
|
394
|
+
return FixResult(success=True, confidence=1.0)
|
|
395
|
+
|
|
396
|
+
self.logger.info(f"Handling {len(issues)} issues with proactive planning")
|
|
397
|
+
|
|
398
|
+
architectural_plan = await self._create_architectural_plan(issues)
|
|
399
|
+
|
|
400
|
+
overall_result = await self._apply_fixes_with_plan(issues, architectural_plan)
|
|
401
|
+
|
|
402
|
+
validation_result = await self._validate_against_plan(
|
|
403
|
+
overall_result, architectural_plan
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
return validation_result
|
|
407
|
+
|
|
408
|
+
async def _create_architectural_plan(self, issues: list[Issue]) -> dict[str, t.Any]:
|
|
409
|
+
architect = self._get_architect_agent()
|
|
410
|
+
|
|
411
|
+
if not architect:
|
|
412
|
+
return self._create_fallback_plan(
|
|
413
|
+
"No ArchitectAgent available for planning"
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
complex_issues = self._filter_complex_issues(issues)
|
|
417
|
+
if not complex_issues:
|
|
418
|
+
return {"strategy": "simple_fixes", "patterns": ["standard_patterns"]}
|
|
419
|
+
|
|
420
|
+
return await self._generate_architectural_plan(
|
|
421
|
+
architect, complex_issues, issues
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
def _create_fallback_plan(self, reason: str) -> dict[str, t.Any]:
|
|
425
|
+
"""Create a fallback plan when architectural planning fails."""
|
|
426
|
+
self.logger.warning(reason)
|
|
427
|
+
return {"strategy": "reactive_fallback", "patterns": []}
|
|
428
|
+
|
|
429
|
+
def _filter_complex_issues(self, issues: list[Issue]) -> list[Issue]:
|
|
430
|
+
"""Filter issues that require architectural planning."""
|
|
431
|
+
complex_types = {
|
|
432
|
+
IssueType.COMPLEXITY,
|
|
433
|
+
IssueType.DRY_VIOLATION,
|
|
434
|
+
IssueType.PERFORMANCE,
|
|
435
|
+
}
|
|
436
|
+
return [issue for issue in issues if issue.type in complex_types]
|
|
437
|
+
|
|
438
|
+
async def _generate_architectural_plan(
|
|
439
|
+
self, architect: t.Any, complex_issues: list[Issue], all_issues: list[Issue]
|
|
440
|
+
) -> dict[str, t.Any]:
|
|
441
|
+
"""Generate architectural plan using the architect agent."""
|
|
442
|
+
primary_issue = complex_issues[0]
|
|
443
|
+
|
|
444
|
+
try:
|
|
445
|
+
plan = await architect.plan_before_action(primary_issue)
|
|
446
|
+
# Ensure plan is properly typed as dict[str, Any]
|
|
447
|
+
if not isinstance(plan, dict):
|
|
448
|
+
plan = {"strategy": "default", "confidence": 0.5}
|
|
449
|
+
enriched_plan = self._enrich_architectural_plan(plan, all_issues)
|
|
450
|
+
|
|
451
|
+
self.logger.info(
|
|
452
|
+
f"Created architectural plan: {enriched_plan.get('strategy', 'unknown')}"
|
|
453
|
+
)
|
|
454
|
+
return enriched_plan
|
|
455
|
+
|
|
456
|
+
except Exception as e:
|
|
457
|
+
self.logger.exception(f"Failed to create architectural plan: {e}")
|
|
458
|
+
return {"strategy": "reactive_fallback", "patterns": [], "error": str(e)}
|
|
459
|
+
|
|
460
|
+
def _enrich_architectural_plan(
|
|
461
|
+
self, plan: dict[str, t.Any], issues: list[Issue]
|
|
462
|
+
) -> dict[str, t.Any]:
|
|
463
|
+
"""Enrich the architectural plan with issue metadata."""
|
|
464
|
+
plan["all_issues"] = [issue.id for issue in issues]
|
|
465
|
+
plan["issue_types"] = list[t.Any]({issue.type.value for issue in issues})
|
|
466
|
+
return plan
|
|
467
|
+
|
|
468
|
+
async def _apply_fixes_with_plan(
|
|
469
|
+
self, issues: list[Issue], plan: dict[str, t.Any]
|
|
470
|
+
) -> FixResult:
|
|
471
|
+
strategy = plan.get("strategy", "reactive_fallback")
|
|
472
|
+
|
|
473
|
+
if strategy == "reactive_fallback":
|
|
474
|
+
return await self.handle_issues(issues)
|
|
475
|
+
|
|
476
|
+
self.logger.info(f"Applying fixes with {strategy} strategy")
|
|
477
|
+
prioritized_issues = self._prioritize_issues_by_plan(issues, plan)
|
|
478
|
+
|
|
479
|
+
return await self._process_prioritized_groups(prioritized_issues, plan)
|
|
480
|
+
|
|
481
|
+
async def _process_prioritized_groups(
|
|
482
|
+
self, prioritized_issues: list[list[Issue]], plan: dict[str, t.Any]
|
|
483
|
+
) -> FixResult:
|
|
484
|
+
"""Process prioritized issue groups according to the plan."""
|
|
485
|
+
overall_result = FixResult(success=True, confidence=1.0)
|
|
486
|
+
|
|
487
|
+
for issue_group in prioritized_issues:
|
|
488
|
+
group_result = await self._handle_issue_group_with_plan(issue_group, plan)
|
|
489
|
+
overall_result = overall_result.merge_with(group_result)
|
|
490
|
+
|
|
491
|
+
if self._should_fail_on_group_failure(group_result, issue_group, plan):
|
|
492
|
+
overall_result = self._mark_critical_group_failure(
|
|
493
|
+
overall_result, issue_group
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
return overall_result
|
|
497
|
+
|
|
498
|
+
def _should_fail_on_group_failure(
|
|
499
|
+
self, group_result: FixResult, issue_group: list[Issue], plan: dict[str, t.Any]
|
|
500
|
+
) -> bool:
|
|
501
|
+
"""Determine if overall process should fail when a group fails."""
|
|
502
|
+
return not group_result.success and self._is_critical_group(issue_group, plan)
|
|
503
|
+
|
|
504
|
+
def _mark_critical_group_failure(
|
|
505
|
+
self, overall_result: FixResult, issue_group: list[Issue]
|
|
506
|
+
) -> FixResult:
|
|
507
|
+
"""Mark overall result as failed due to critical group failure."""
|
|
508
|
+
overall_result.success = False
|
|
509
|
+
overall_result.remaining_issues.append(
|
|
510
|
+
f"Critical issue group failed: {[i.id for i in issue_group]}"
|
|
511
|
+
)
|
|
512
|
+
return overall_result
|
|
513
|
+
|
|
514
|
+
async def _validate_against_plan(
|
|
515
|
+
self, result: FixResult, plan: dict[str, t.Any]
|
|
516
|
+
) -> FixResult:
|
|
517
|
+
validation_steps = plan.get("validation", [])
|
|
518
|
+
|
|
519
|
+
if not validation_steps:
|
|
520
|
+
return result
|
|
521
|
+
|
|
522
|
+
self.logger.info(f"Validating against plan: {validation_steps}")
|
|
523
|
+
|
|
524
|
+
result.recommendations.extend(
|
|
525
|
+
[
|
|
526
|
+
f"Validate with: {', '.join(validation_steps)}",
|
|
527
|
+
f"Applied strategy: {plan.get('strategy', 'unknown')}",
|
|
528
|
+
f"Used patterns: {', '.join(plan.get('patterns', []))}",
|
|
529
|
+
]
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
return result
|
|
533
|
+
|
|
534
|
+
def _get_architect_agent(self) -> SubAgent | None:
|
|
535
|
+
for agent in self.agents:
|
|
536
|
+
if agent.__class__.__name__ == "ArchitectAgent":
|
|
537
|
+
return agent
|
|
538
|
+
return None
|
|
539
|
+
|
|
540
|
+
def _prioritize_issues_by_plan(
|
|
541
|
+
self, issues: list[Issue], plan: dict[str, t.Any]
|
|
542
|
+
) -> list[list[Issue]]:
|
|
543
|
+
strategy = plan.get("strategy", "reactive_fallback")
|
|
544
|
+
|
|
545
|
+
if strategy == "external_specialist_guided":
|
|
546
|
+
complex_issues = [
|
|
547
|
+
issue
|
|
548
|
+
for issue in issues
|
|
549
|
+
if issue.type in {IssueType.COMPLEXITY, IssueType.DRY_VIOLATION}
|
|
550
|
+
]
|
|
551
|
+
other_issues = [issue for issue in issues if issue not in complex_issues]
|
|
552
|
+
return [complex_issues, other_issues] if complex_issues else [other_issues]
|
|
553
|
+
|
|
554
|
+
groups = self._group_issues_by_type(issues)
|
|
555
|
+
return list[t.Any](groups.values())
|
|
556
|
+
|
|
557
|
+
async def _handle_issue_group_with_plan(
|
|
558
|
+
self, issues: list[Issue], plan: dict[str, t.Any]
|
|
559
|
+
) -> FixResult:
|
|
560
|
+
if not issues:
|
|
561
|
+
return FixResult(success=True, confidence=1.0)
|
|
562
|
+
|
|
563
|
+
representative_issue = issues[0]
|
|
564
|
+
|
|
565
|
+
if self._should_use_architect_for_group(issues, plan):
|
|
566
|
+
architect = self._get_architect_agent()
|
|
567
|
+
if architect:
|
|
568
|
+
group_result = FixResult(success=True, confidence=1.0)
|
|
569
|
+
|
|
570
|
+
for issue in issues:
|
|
571
|
+
issue_result = await architect.analyze_and_fix(issue)
|
|
572
|
+
group_result = group_result.merge_with(issue_result)
|
|
573
|
+
|
|
574
|
+
return group_result
|
|
575
|
+
|
|
576
|
+
return await self._handle_issues_by_type(representative_issue.type, issues)
|
|
577
|
+
|
|
578
|
+
def _should_use_architect_for_group(
|
|
579
|
+
self, issues: list[Issue], plan: dict[str, t.Any]
|
|
580
|
+
) -> bool:
|
|
581
|
+
strategy = plan.get("strategy", "")
|
|
582
|
+
|
|
583
|
+
if strategy == "external_specialist_guided":
|
|
584
|
+
return True
|
|
585
|
+
|
|
586
|
+
architectural_types = {
|
|
587
|
+
IssueType.COMPLEXITY,
|
|
588
|
+
IssueType.DRY_VIOLATION,
|
|
589
|
+
IssueType.PERFORMANCE,
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return any(issue.type in architectural_types for issue in issues)
|
|
593
|
+
|
|
594
|
+
def _is_critical_group(self, issues: list[Issue], plan: dict[str, t.Any]) -> bool:
|
|
595
|
+
critical_types = {IssueType.COMPLEXITY, IssueType.DRY_VIOLATION}
|
|
596
|
+
return any(issue.type in critical_types for issue in issues)
|
|
597
|
+
|
|
598
|
+
def set_proactive_mode(self, enabled: bool) -> None:
|
|
599
|
+
self.proactive_mode = enabled
|
|
600
|
+
self.logger.info(f"Proactive mode {'enabled' if enabled else 'disabled'}")
|