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,853 @@
|
|
|
1
|
+
"""Claude AI adapter for code fixing with comprehensive security validation.
|
|
2
|
+
|
|
3
|
+
This adapter provides AI-powered code fixing using the Anthropic Claude API,
|
|
4
|
+
following ACB adapter patterns and implementing comprehensive security measures.
|
|
5
|
+
|
|
6
|
+
Security Features:
|
|
7
|
+
- AI-generated code validation (regex + AST scanning)
|
|
8
|
+
- Prompt injection prevention
|
|
9
|
+
- Error message sanitization
|
|
10
|
+
- File size limits
|
|
11
|
+
- API key format validation
|
|
12
|
+
|
|
13
|
+
ACB Compliance:
|
|
14
|
+
- Static UUID7 for stable adapter identification
|
|
15
|
+
- Public/private method delegation
|
|
16
|
+
- Lazy client initialization via _ensure_client()
|
|
17
|
+
- Resource cleanup via CleanupMixin
|
|
18
|
+
- Async initialization via init()
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import ast
|
|
22
|
+
import asyncio
|
|
23
|
+
import json
|
|
24
|
+
import random
|
|
25
|
+
import re
|
|
26
|
+
import typing as t
|
|
27
|
+
from uuid import UUID
|
|
28
|
+
|
|
29
|
+
from acb.adapters import AdapterCapability, AdapterMetadata, AdapterStatus
|
|
30
|
+
from acb.cleanup import CleanupMixin
|
|
31
|
+
from acb.config import Config
|
|
32
|
+
from acb.depends import depends
|
|
33
|
+
from loguru import logger
|
|
34
|
+
from pydantic import BaseModel, Field, SecretStr, field_validator
|
|
35
|
+
|
|
36
|
+
# Static UUID7 for stable adapter identification (ACB requirement)
|
|
37
|
+
MODULE_METADATA = AdapterMetadata(
|
|
38
|
+
module_id=UUID("01937d86-5f2a-7b3c-9d1e-a4b3c2d1e0f9"), # Static UUID7
|
|
39
|
+
name="Claude AI Code Fixer",
|
|
40
|
+
category="ai",
|
|
41
|
+
provider="anthropic",
|
|
42
|
+
version="1.0.0",
|
|
43
|
+
acb_min_version="0.19.0",
|
|
44
|
+
author="Crackerjack Team",
|
|
45
|
+
created_date="2025-01-01",
|
|
46
|
+
last_modified="2025-01-09",
|
|
47
|
+
status=AdapterStatus.STABLE,
|
|
48
|
+
capabilities=[
|
|
49
|
+
AdapterCapability.ASYNC_OPERATIONS,
|
|
50
|
+
AdapterCapability.ENCRYPTION, # API key encryption support
|
|
51
|
+
],
|
|
52
|
+
required_packages=["anthropic>=0.25.0"],
|
|
53
|
+
optional_packages={},
|
|
54
|
+
description="Claude AI integration for code fixing with retry logic and confidence scoring",
|
|
55
|
+
settings_class="crackerjack.adapters.ai.claude.ClaudeCodeFixerSettings",
|
|
56
|
+
custom={},
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ClaudeCodeFixerSettings(BaseModel):
|
|
61
|
+
"""Configuration settings for Claude Code Fixer adapter.
|
|
62
|
+
|
|
63
|
+
Follows ACB patterns for adapter configuration with proper validation.
|
|
64
|
+
All settings are validated using Pydantic validators.
|
|
65
|
+
|
|
66
|
+
Attributes:
|
|
67
|
+
anthropic_api_key: Anthropic API key (must start with 'sk-ant-')
|
|
68
|
+
model: Claude model to use (default: claude-sonnet-4-5-20250929)
|
|
69
|
+
max_tokens: Maximum tokens in API response (1-8192)
|
|
70
|
+
temperature: Response temperature for consistency (0.0-1.0)
|
|
71
|
+
confidence_threshold: Minimum confidence to apply fixes (0.0-1.0)
|
|
72
|
+
max_retries: Maximum API retry attempts (1-10)
|
|
73
|
+
max_file_size_bytes: Maximum file size to process (1KB-100MB)
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
anthropic_api_key: SecretStr = Field(
|
|
77
|
+
...,
|
|
78
|
+
description="Anthropic API key from environment variable",
|
|
79
|
+
)
|
|
80
|
+
model: str = Field(
|
|
81
|
+
default="claude-sonnet-4-5-20250929",
|
|
82
|
+
description="Claude model to use for code fixing",
|
|
83
|
+
)
|
|
84
|
+
max_tokens: int = Field(
|
|
85
|
+
default=4096,
|
|
86
|
+
ge=1,
|
|
87
|
+
le=8192,
|
|
88
|
+
description="Maximum tokens in API response",
|
|
89
|
+
)
|
|
90
|
+
temperature: float = Field(
|
|
91
|
+
default=0.1,
|
|
92
|
+
ge=0.0,
|
|
93
|
+
le=1.0,
|
|
94
|
+
description="Temperature for response consistency",
|
|
95
|
+
)
|
|
96
|
+
confidence_threshold: float = Field(
|
|
97
|
+
default=0.7,
|
|
98
|
+
ge=0.0,
|
|
99
|
+
le=1.0,
|
|
100
|
+
description="Minimum confidence score to apply fixes",
|
|
101
|
+
)
|
|
102
|
+
max_retries: int = Field(
|
|
103
|
+
default=3,
|
|
104
|
+
ge=1,
|
|
105
|
+
le=10,
|
|
106
|
+
description="Maximum API retry attempts",
|
|
107
|
+
)
|
|
108
|
+
max_file_size_bytes: int = Field(
|
|
109
|
+
default=10_485_760, # 10MB
|
|
110
|
+
ge=1024,
|
|
111
|
+
le=104_857_600, # 100MB absolute max
|
|
112
|
+
description="Maximum file size to process (security limit)",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
@field_validator("anthropic_api_key")
|
|
116
|
+
@classmethod
|
|
117
|
+
def validate_api_key_format(cls, v: SecretStr) -> SecretStr:
|
|
118
|
+
"""Validate API key format for security.
|
|
119
|
+
|
|
120
|
+
Ensures API key:
|
|
121
|
+
- Starts with 'sk-ant-' prefix
|
|
122
|
+
- Has minimum length of 20 characters
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
v: API key to validate
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Validated API key
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
ValueError: If API key format is invalid
|
|
132
|
+
"""
|
|
133
|
+
key = v.get_secret_value()
|
|
134
|
+
|
|
135
|
+
# Anthropic API keys start with 'sk-ant-'
|
|
136
|
+
if not key.startswith("sk-ant-"):
|
|
137
|
+
raise ValueError(
|
|
138
|
+
"Invalid Anthropic API key format (must start with 'sk-ant-')"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Must be reasonable length (not too short)
|
|
142
|
+
if len(key) < 20:
|
|
143
|
+
raise ValueError("API key too short to be valid")
|
|
144
|
+
|
|
145
|
+
return v
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class ClaudeCodeFixer(CleanupMixin): # type: ignore[misc]
|
|
149
|
+
"""Real AI-powered code fixing using Claude API.
|
|
150
|
+
|
|
151
|
+
Follows ACB adapter patterns:
|
|
152
|
+
- Lazy client initialization via _ensure_client()
|
|
153
|
+
- Public/private method delegation
|
|
154
|
+
- Resource cleanup via CleanupMixin
|
|
155
|
+
- Configuration via depends.get(Config)
|
|
156
|
+
- Async initialization via init() method
|
|
157
|
+
|
|
158
|
+
Security features:
|
|
159
|
+
- AI-generated code validation (regex + AST)
|
|
160
|
+
- Prompt injection prevention
|
|
161
|
+
- Error message sanitization
|
|
162
|
+
- File size limits
|
|
163
|
+
- Symlink protection
|
|
164
|
+
|
|
165
|
+
Example:
|
|
166
|
+
```python
|
|
167
|
+
fixer = ClaudeCodeFixer()
|
|
168
|
+
await fixer.init()
|
|
169
|
+
|
|
170
|
+
result = await fixer.fix_code_issue(
|
|
171
|
+
file_path="myfile.py",
|
|
172
|
+
issue_description="Line too long",
|
|
173
|
+
code_context="x = 1",
|
|
174
|
+
fix_type="ruff",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if result["success"]:
|
|
178
|
+
print(f"Fixed with confidence {result['confidence']}")
|
|
179
|
+
print(result["fixed_code"])
|
|
180
|
+
```
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
def __init__(self) -> None:
|
|
184
|
+
"""Initialize the adapter without async operations.
|
|
185
|
+
|
|
186
|
+
Async initialization happens in init() method.
|
|
187
|
+
"""
|
|
188
|
+
super().__init__()
|
|
189
|
+
self._client = None
|
|
190
|
+
self._settings: ClaudeCodeFixerSettings | None = None
|
|
191
|
+
self._client_lock = None
|
|
192
|
+
self._initialized = False
|
|
193
|
+
|
|
194
|
+
async def init(self) -> None:
|
|
195
|
+
"""Initialize adapter asynchronously (ACB pattern).
|
|
196
|
+
|
|
197
|
+
Required by ACB adapter pattern for async setup.
|
|
198
|
+
Loads configuration and validates API key.
|
|
199
|
+
|
|
200
|
+
This method is idempotent - calling it multiple times is safe.
|
|
201
|
+
|
|
202
|
+
Raises:
|
|
203
|
+
RuntimeError: If configuration is missing or invalid
|
|
204
|
+
"""
|
|
205
|
+
if self._initialized:
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
# Load configuration from depends
|
|
209
|
+
config: Config = await depends.get(Config)
|
|
210
|
+
|
|
211
|
+
# Build settings from config with validation
|
|
212
|
+
self._settings = ClaudeCodeFixerSettings(
|
|
213
|
+
anthropic_api_key=SecretStr(config.anthropic_api_key),
|
|
214
|
+
model=getattr(config, "anthropic_model", "claude-sonnet-4-5-20250929"),
|
|
215
|
+
max_tokens=getattr(config, "ai_max_tokens", 4096),
|
|
216
|
+
temperature=getattr(config, "ai_temperature", 0.1),
|
|
217
|
+
confidence_threshold=getattr(config, "ai_confidence_threshold", 0.7),
|
|
218
|
+
max_retries=getattr(config, "ai_max_retries", 3),
|
|
219
|
+
max_file_size_bytes=getattr(config, "ai_max_file_size_bytes", 10_485_760),
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
self._initialized = True
|
|
223
|
+
logger.debug("Claude AI adapter initialized successfully")
|
|
224
|
+
|
|
225
|
+
# Public API
|
|
226
|
+
async def fix_code_issue(
|
|
227
|
+
self,
|
|
228
|
+
file_path: str,
|
|
229
|
+
issue_description: str,
|
|
230
|
+
code_context: str,
|
|
231
|
+
fix_type: str,
|
|
232
|
+
max_retries: int = 3,
|
|
233
|
+
) -> dict[str, str | float | list[str] | bool]:
|
|
234
|
+
"""Public method - delegates to private implementation.
|
|
235
|
+
|
|
236
|
+
Generate code fix using Claude AI with retry logic and validation.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
file_path: Path to the file being fixed
|
|
240
|
+
issue_description: Description of the issue
|
|
241
|
+
code_context: Code context around the issue
|
|
242
|
+
fix_type: Type of fix needed (e.g., 'ruff', 'complexity')
|
|
243
|
+
max_retries: Maximum retry attempts for API failures
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Dictionary containing:
|
|
247
|
+
- success: bool - Whether fix was successful
|
|
248
|
+
- fixed_code: str - The fixed code (if successful)
|
|
249
|
+
- explanation: str - Explanation of changes
|
|
250
|
+
- confidence: float - Confidence score (0.0-1.0)
|
|
251
|
+
- changes_made: list[str] - List of changes
|
|
252
|
+
- potential_side_effects: list[str] - Potential issues
|
|
253
|
+
- error: str - Error message (if failed)
|
|
254
|
+
"""
|
|
255
|
+
return await self._fix_code_issue(
|
|
256
|
+
file_path, issue_description, code_context, fix_type, max_retries
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Private implementation
|
|
260
|
+
async def _fix_code_issue(
|
|
261
|
+
self,
|
|
262
|
+
file_path: str,
|
|
263
|
+
issue_description: str,
|
|
264
|
+
code_context: str,
|
|
265
|
+
fix_type: str,
|
|
266
|
+
max_retries: int,
|
|
267
|
+
) -> dict[str, str | float | list[str] | bool]:
|
|
268
|
+
"""Generate code fix using Claude AI with retry logic.
|
|
269
|
+
|
|
270
|
+
This is the internal implementation that handles:
|
|
271
|
+
- API calls with retries
|
|
272
|
+
- Response parsing and validation
|
|
273
|
+
- Code security validation
|
|
274
|
+
- Confidence scoring
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
file_path: Path to the file being fixed
|
|
278
|
+
issue_description: Description of the issue
|
|
279
|
+
code_context: Code context around the issue
|
|
280
|
+
fix_type: Type of fix needed
|
|
281
|
+
max_retries: Maximum retry attempts
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Fix result dictionary with success status and details
|
|
285
|
+
"""
|
|
286
|
+
client = await self._ensure_client()
|
|
287
|
+
|
|
288
|
+
# Build prompt with context (sanitizes inputs)
|
|
289
|
+
prompt = self._build_fix_prompt(
|
|
290
|
+
file_path, issue_description, code_context, fix_type
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Retry logic for API failures
|
|
294
|
+
for attempt in range(max_retries):
|
|
295
|
+
try:
|
|
296
|
+
response = await self._call_claude_api(client, prompt)
|
|
297
|
+
parsed = self._parse_fix_response(response)
|
|
298
|
+
|
|
299
|
+
# Validate response quality
|
|
300
|
+
if self._validate_fix_quality(parsed, code_context):
|
|
301
|
+
return parsed
|
|
302
|
+
|
|
303
|
+
# Low confidence - retry with enhanced prompt
|
|
304
|
+
if attempt < max_retries - 1:
|
|
305
|
+
prompt = self._enhance_prompt_for_retry(prompt, parsed)
|
|
306
|
+
continue
|
|
307
|
+
|
|
308
|
+
return parsed # Return best effort on final attempt
|
|
309
|
+
|
|
310
|
+
except Exception as e:
|
|
311
|
+
logger.warning(f"API call failed (attempt {attempt + 1}): {e}")
|
|
312
|
+
|
|
313
|
+
if attempt == max_retries - 1:
|
|
314
|
+
return {
|
|
315
|
+
"success": False,
|
|
316
|
+
"error": self._sanitize_error_message(str(e)),
|
|
317
|
+
"confidence": 0.0,
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
# Exponential backoff
|
|
321
|
+
await self._backoff_delay(attempt)
|
|
322
|
+
|
|
323
|
+
# Should never reach here
|
|
324
|
+
return {"success": False, "error": "Max retries exceeded", "confidence": 0.0}
|
|
325
|
+
|
|
326
|
+
async def _initialize_client(self) -> t.Any:
|
|
327
|
+
"""Initialize and return the Anthropic client."""
|
|
328
|
+
# Ensure initialized
|
|
329
|
+
if not self._initialized:
|
|
330
|
+
await self.init()
|
|
331
|
+
|
|
332
|
+
if not self._settings:
|
|
333
|
+
raise RuntimeError("Settings not initialized - call init() first")
|
|
334
|
+
|
|
335
|
+
# Security: API key from validated settings
|
|
336
|
+
import anthropic
|
|
337
|
+
|
|
338
|
+
# Get validated API key (SecretStr)
|
|
339
|
+
api_key = self._settings.anthropic_api_key.get_secret_value()
|
|
340
|
+
|
|
341
|
+
client = anthropic.AsyncAnthropic(
|
|
342
|
+
api_key=api_key,
|
|
343
|
+
max_retries=0, # We handle retries ourselves
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
# Register for cleanup
|
|
347
|
+
self.register_resource(client)
|
|
348
|
+
|
|
349
|
+
logger.debug("Claude API client initialized")
|
|
350
|
+
return client
|
|
351
|
+
|
|
352
|
+
async def _ensure_client(self) -> t.Any:
|
|
353
|
+
"""Lazy client initialization with thread safety (ACB pattern).
|
|
354
|
+
|
|
355
|
+
Creates and caches the Anthropic client instance.
|
|
356
|
+
Uses asyncio.Lock to ensure thread-safe initialization.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
AsyncAnthropic client instance
|
|
360
|
+
|
|
361
|
+
Raises:
|
|
362
|
+
RuntimeError: If adapter not initialized via init()
|
|
363
|
+
"""
|
|
364
|
+
if self._client is None:
|
|
365
|
+
if self._client_lock is None:
|
|
366
|
+
self._client_lock = asyncio.Lock()
|
|
367
|
+
|
|
368
|
+
async with self._client_lock:
|
|
369
|
+
if self._client is None:
|
|
370
|
+
self._client = await self._initialize_client()
|
|
371
|
+
|
|
372
|
+
return self._client
|
|
373
|
+
|
|
374
|
+
def _validate_ai_generated_code(self, code: str) -> tuple[bool, str]:
|
|
375
|
+
"""Validate AI-generated code for security issues.
|
|
376
|
+
|
|
377
|
+
Security checks:
|
|
378
|
+
1. Regex scanning for dangerous patterns (eval, exec, shell=True)
|
|
379
|
+
2. AST parsing to detect malicious constructs
|
|
380
|
+
3. Size limit enforcement
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
code: AI-generated code to validate
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
Tuple of (is_valid, error_message)
|
|
387
|
+
- is_valid: True if code passes all security checks
|
|
388
|
+
- error_message: Description of security violation (empty if valid)
|
|
389
|
+
"""
|
|
390
|
+
# Check 1: Dangerous pattern detection
|
|
391
|
+
is_valid, error_msg = self._check_dangerous_patterns(code)
|
|
392
|
+
if not is_valid:
|
|
393
|
+
return False, error_msg
|
|
394
|
+
|
|
395
|
+
# Check 2: AST validation
|
|
396
|
+
is_valid, error_msg = self._validate_ast_security(code)
|
|
397
|
+
if not is_valid:
|
|
398
|
+
return False, error_msg
|
|
399
|
+
|
|
400
|
+
# Check 3: Code length sanity check
|
|
401
|
+
is_valid, error_msg = self._check_code_size_limit(code)
|
|
402
|
+
if not is_valid:
|
|
403
|
+
return False, error_msg
|
|
404
|
+
|
|
405
|
+
return True, ""
|
|
406
|
+
|
|
407
|
+
def _check_dangerous_patterns(self, code: str) -> tuple[bool, str]:
|
|
408
|
+
"""Check for dangerous code patterns using regex."""
|
|
409
|
+
dangerous_patterns = [
|
|
410
|
+
(r"\beval\s*\(", "eval() call detected"),
|
|
411
|
+
(r"\bexec\s*\(", "exec() call detected"),
|
|
412
|
+
(r"\b__import__\s*\(", "dynamic import detected"),
|
|
413
|
+
(
|
|
414
|
+
r"subprocess\.\w+\([^)]*shell\s*=\s*True",
|
|
415
|
+
"subprocess with shell=True detected",
|
|
416
|
+
),
|
|
417
|
+
(r"\bos\.system\s*\(", "os.system() call detected"),
|
|
418
|
+
(
|
|
419
|
+
r"\bpickle\.loads?\s*\(",
|
|
420
|
+
"pickle usage detected (unsafe with untrusted data)",
|
|
421
|
+
),
|
|
422
|
+
(
|
|
423
|
+
r"\byaml\.load\s*\([^)]*Loader\s*=\s*yaml\.Loader",
|
|
424
|
+
"unsafe YAML loading detected",
|
|
425
|
+
),
|
|
426
|
+
]
|
|
427
|
+
|
|
428
|
+
for pattern, message in dangerous_patterns:
|
|
429
|
+
if re.search(
|
|
430
|
+
pattern, code
|
|
431
|
+
): # REGEX OK: security validation of AI-generated code
|
|
432
|
+
return False, f"Security violation: {message}"
|
|
433
|
+
|
|
434
|
+
return True, ""
|
|
435
|
+
|
|
436
|
+
def _validate_ast_security(self, code: str) -> tuple[bool, str]:
|
|
437
|
+
"""Validate code AST for security issues."""
|
|
438
|
+
try:
|
|
439
|
+
tree = ast.parse(code)
|
|
440
|
+
self._scan_ast_for_dangerous_imports(tree)
|
|
441
|
+
except SyntaxError as e:
|
|
442
|
+
return (
|
|
443
|
+
False,
|
|
444
|
+
f"Syntax error in generated code: {self._sanitize_error_message(str(e))}",
|
|
445
|
+
)
|
|
446
|
+
except Exception as e:
|
|
447
|
+
return (
|
|
448
|
+
False,
|
|
449
|
+
f"Failed to parse generated code: {self._sanitize_error_message(str(e))}",
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
return True, ""
|
|
453
|
+
|
|
454
|
+
def _is_dangerous_import(self, node: ast.Import) -> bool:
|
|
455
|
+
"""Check if the import node contains dangerous modules."""
|
|
456
|
+
for alias in node.names:
|
|
457
|
+
if alias.name in ("os", "subprocess", "sys"):
|
|
458
|
+
return not self._is_safe_usage(node)
|
|
459
|
+
return False
|
|
460
|
+
|
|
461
|
+
def _scan_ast_for_dangerous_imports(self, tree: ast.AST) -> None:
|
|
462
|
+
"""Scan AST nodes for potentially dangerous imports."""
|
|
463
|
+
for node in ast.walk(tree):
|
|
464
|
+
if isinstance(node, ast.Import) and self._is_dangerous_import(node):
|
|
465
|
+
# For now we just log - in production we might want more action
|
|
466
|
+
pass # Allow for now, but log
|
|
467
|
+
|
|
468
|
+
def _check_code_size_limit(self, code: str) -> tuple[bool, str]:
|
|
469
|
+
"""Check if generated code exceeds size limit."""
|
|
470
|
+
assert self._settings is not None, "Settings not initialized"
|
|
471
|
+
if len(code) > self._settings.max_file_size_bytes:
|
|
472
|
+
return (
|
|
473
|
+
False,
|
|
474
|
+
f"Generated code exceeds size limit ({len(code)} > {self._settings.max_file_size_bytes})",
|
|
475
|
+
)
|
|
476
|
+
return True, ""
|
|
477
|
+
|
|
478
|
+
def _sanitize_error_message(self, error_msg: str) -> str:
|
|
479
|
+
"""Sanitize error messages to prevent information leakage.
|
|
480
|
+
|
|
481
|
+
Removes:
|
|
482
|
+
- File system paths that might reveal structure
|
|
483
|
+
- API keys or secrets that might be in messages
|
|
484
|
+
- Internal implementation details
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
error_msg: Raw error message
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
Sanitized error message safe for logging/display
|
|
491
|
+
"""
|
|
492
|
+
# Remove absolute paths
|
|
493
|
+
error_msg = re.sub(
|
|
494
|
+
r"/[\w\-./ ]+/", "<path>/", error_msg
|
|
495
|
+
) # REGEX OK: sanitizing Unix paths in error messages
|
|
496
|
+
error_msg = re.sub(
|
|
497
|
+
r"[A-Z]:\\[\w\-\\ ]+\\", "<path>\\", error_msg
|
|
498
|
+
) # REGEX OK: sanitizing Windows paths in error messages
|
|
499
|
+
|
|
500
|
+
# Remove potential secrets (basic pattern matching)
|
|
501
|
+
error_msg = re.sub(
|
|
502
|
+
r"sk-[a-zA-Z0-9]{20,}", "<api-key>", error_msg
|
|
503
|
+
) # REGEX OK: masking OpenAI API keys in error messages
|
|
504
|
+
error_msg = re.sub(
|
|
505
|
+
r'["\'][\w\-]{32,}["\']', "<secret>", error_msg
|
|
506
|
+
) # REGEX OK: masking generic secrets in error messages
|
|
507
|
+
|
|
508
|
+
return error_msg
|
|
509
|
+
|
|
510
|
+
def _sanitize_prompt_input(self, user_input: str) -> str:
|
|
511
|
+
"""Sanitize user inputs to prevent prompt injection attacks.
|
|
512
|
+
|
|
513
|
+
Prevents:
|
|
514
|
+
- Injection of system instructions
|
|
515
|
+
- Attempts to override assistant behavior
|
|
516
|
+
- Escaping from code context
|
|
517
|
+
|
|
518
|
+
Args:
|
|
519
|
+
user_input: Raw user input
|
|
520
|
+
|
|
521
|
+
Returns:
|
|
522
|
+
Sanitized input safe for inclusion in prompts
|
|
523
|
+
"""
|
|
524
|
+
# Remove potential system instruction injections
|
|
525
|
+
sanitized = user_input
|
|
526
|
+
|
|
527
|
+
# Remove attempts to inject new system instructions
|
|
528
|
+
injection_patterns = [
|
|
529
|
+
r"(?i)(ignore previous|disregard previous|forget previous)",
|
|
530
|
+
r"(?i)(system:|assistant:|user:)",
|
|
531
|
+
r"(?i)(you are now|act as|pretend to be)",
|
|
532
|
+
]
|
|
533
|
+
|
|
534
|
+
for pattern in injection_patterns:
|
|
535
|
+
sanitized = re.sub(
|
|
536
|
+
pattern, "[FILTERED]", sanitized
|
|
537
|
+
) # REGEX OK: preventing prompt injection attacks
|
|
538
|
+
|
|
539
|
+
# Escape markdown code blocks to prevent context breaking
|
|
540
|
+
sanitized = sanitized.replace("```", "'''")
|
|
541
|
+
|
|
542
|
+
return sanitized
|
|
543
|
+
|
|
544
|
+
def _is_safe_usage(self, import_node: ast.Import) -> bool:
|
|
545
|
+
"""Heuristic check if an import is used safely.
|
|
546
|
+
|
|
547
|
+
This is a simplified check - full analysis would require data flow tracking.
|
|
548
|
+
|
|
549
|
+
Args:
|
|
550
|
+
import_node: AST Import node to check
|
|
551
|
+
|
|
552
|
+
Returns:
|
|
553
|
+
True if import appears safe (conservative: allow but monitor)
|
|
554
|
+
"""
|
|
555
|
+
# For now, we allow imports but log them for review
|
|
556
|
+
# In production, implement more sophisticated checks
|
|
557
|
+
return True # Conservative: allow but monitor
|
|
558
|
+
|
|
559
|
+
def _build_fix_prompt(
|
|
560
|
+
self,
|
|
561
|
+
file_path: str,
|
|
562
|
+
issue: str,
|
|
563
|
+
context: str,
|
|
564
|
+
fix_type: str,
|
|
565
|
+
) -> str:
|
|
566
|
+
"""Build comprehensive prompt for Claude API.
|
|
567
|
+
|
|
568
|
+
Strategy:
|
|
569
|
+
- Provide clear role and task
|
|
570
|
+
- Include file context and specific issue
|
|
571
|
+
- Request structured JSON output
|
|
572
|
+
- Ask for confidence score
|
|
573
|
+
- Request explanation of changes
|
|
574
|
+
|
|
575
|
+
Security:
|
|
576
|
+
- Sanitizes all user inputs to prevent prompt injection
|
|
577
|
+
- Limits context size to prevent DoS
|
|
578
|
+
|
|
579
|
+
Args:
|
|
580
|
+
file_path: Path to the file
|
|
581
|
+
issue: Issue description
|
|
582
|
+
context: Code context
|
|
583
|
+
fix_type: Type of fix needed
|
|
584
|
+
|
|
585
|
+
Returns:
|
|
586
|
+
Complete prompt for Claude API
|
|
587
|
+
"""
|
|
588
|
+
# Sanitize inputs
|
|
589
|
+
issue = self._sanitize_prompt_input(issue)
|
|
590
|
+
context = self._sanitize_prompt_input(context)
|
|
591
|
+
|
|
592
|
+
# Enforce size limits
|
|
593
|
+
assert self._settings is not None, "Settings not initialized"
|
|
594
|
+
if len(context) > self._settings.max_file_size_bytes:
|
|
595
|
+
context = (
|
|
596
|
+
context[: self._settings.max_file_size_bytes] + "\n... (truncated)"
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
return f"""You are an expert Python code fixer specialized in {fix_type} issues.
|
|
600
|
+
|
|
601
|
+
**Task**: Fix the following code issue in a production codebase.
|
|
602
|
+
|
|
603
|
+
**File**: {file_path}
|
|
604
|
+
**Issue Type**: {fix_type}
|
|
605
|
+
**Issue Description**: {issue}
|
|
606
|
+
|
|
607
|
+
**Current Code**:
|
|
608
|
+
```python
|
|
609
|
+
{context}
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
**Requirements**:
|
|
613
|
+
1. Fix the issue while maintaining existing functionality
|
|
614
|
+
2. Follow Python 3.13+ best practices
|
|
615
|
+
3. Preserve existing code style and formatting where possible
|
|
616
|
+
4. Ensure the fix is minimal and focused on the specific issue
|
|
617
|
+
5. Provide a confidence score (0.0-1.0) for your fix
|
|
618
|
+
|
|
619
|
+
**Response Format** (valid JSON only):
|
|
620
|
+
```json
|
|
621
|
+
{{
|
|
622
|
+
"fixed_code": "... complete fixed code ...",
|
|
623
|
+
"explanation": "Brief explanation of what was changed and why",
|
|
624
|
+
"confidence": 0.95,
|
|
625
|
+
"changes_made": ["change 1", "change 2"],
|
|
626
|
+
"potential_side_effects": ["possible side effect 1"]
|
|
627
|
+
}}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
Respond with ONLY the JSON, no additional text."""
|
|
631
|
+
|
|
632
|
+
async def _call_claude_api(self, client, prompt: str): # type: ignore[no-untyped-def]
|
|
633
|
+
"""Call Claude API with the given prompt.
|
|
634
|
+
|
|
635
|
+
Args:
|
|
636
|
+
client: Anthropic AsyncAnthropic client instance
|
|
637
|
+
prompt: Prompt to send to Claude
|
|
638
|
+
|
|
639
|
+
Returns:
|
|
640
|
+
Anthropic Message response object
|
|
641
|
+
|
|
642
|
+
Raises:
|
|
643
|
+
Exception: If API call fails
|
|
644
|
+
"""
|
|
645
|
+
assert self._settings is not None, "Settings not initialized"
|
|
646
|
+
response = await client.messages.create(
|
|
647
|
+
model=self._settings.model,
|
|
648
|
+
max_tokens=self._settings.max_tokens,
|
|
649
|
+
temperature=self._settings.temperature,
|
|
650
|
+
messages=[{"role": "user", "content": prompt}],
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
return response
|
|
654
|
+
|
|
655
|
+
def _parse_fix_response(
|
|
656
|
+
self, response
|
|
657
|
+
) -> dict[str, str | float | list[str] | bool]: # type: ignore[no-untyped-def]
|
|
658
|
+
"""Parse Claude's response with robust error handling and security validation.
|
|
659
|
+
|
|
660
|
+
Args:
|
|
661
|
+
response: Claude API response object
|
|
662
|
+
|
|
663
|
+
Returns:
|
|
664
|
+
Dictionary with parsed fix result including:
|
|
665
|
+
- success: Whether parsing succeeded
|
|
666
|
+
- fixed_code: Fixed code (if successful)
|
|
667
|
+
- explanation: Explanation of changes
|
|
668
|
+
- confidence: Confidence score
|
|
669
|
+
- changes_made: List of changes
|
|
670
|
+
- potential_side_effects: List of potential issues
|
|
671
|
+
- error: Error message (if failed)
|
|
672
|
+
"""
|
|
673
|
+
try:
|
|
674
|
+
content = response.content[0].text
|
|
675
|
+
|
|
676
|
+
# Extract JSON from response (handle markdown code blocks)
|
|
677
|
+
json_str = self._extract_json_from_response(content)
|
|
678
|
+
|
|
679
|
+
# Parse and validate
|
|
680
|
+
data = json.loads(json_str)
|
|
681
|
+
|
|
682
|
+
# Ensure required fields exist
|
|
683
|
+
required_fields = ["fixed_code", "explanation", "confidence"]
|
|
684
|
+
missing = [f for f in required_fields if f not in data]
|
|
685
|
+
|
|
686
|
+
if missing:
|
|
687
|
+
logger.warning(f"Missing fields in response: {missing}")
|
|
688
|
+
# Add defaults for missing fields
|
|
689
|
+
data.setdefault("fixed_code", "")
|
|
690
|
+
data.setdefault("explanation", "No explanation provided")
|
|
691
|
+
data.setdefault("confidence", 0.5)
|
|
692
|
+
|
|
693
|
+
# Normalize confidence to 0.0-1.0 range
|
|
694
|
+
confidence = float(data.get("confidence", 0.5))
|
|
695
|
+
data["confidence"] = max(0.0, min(1.0, confidence))
|
|
696
|
+
|
|
697
|
+
# SECURITY: Validate AI-generated code
|
|
698
|
+
fixed_code = data["fixed_code"]
|
|
699
|
+
is_valid, error_msg = self._validate_ai_generated_code(fixed_code)
|
|
700
|
+
|
|
701
|
+
if not is_valid:
|
|
702
|
+
logger.error(
|
|
703
|
+
f"AI-generated code failed security validation: {error_msg}"
|
|
704
|
+
)
|
|
705
|
+
return {
|
|
706
|
+
"success": False,
|
|
707
|
+
"error": f"Security validation failed: {error_msg}",
|
|
708
|
+
"confidence": 0.0,
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
# Sanitize explanation to prevent information leakage
|
|
712
|
+
explanation = self._sanitize_error_message(data["explanation"])
|
|
713
|
+
|
|
714
|
+
return {
|
|
715
|
+
"success": True,
|
|
716
|
+
"fixed_code": fixed_code,
|
|
717
|
+
"explanation": explanation,
|
|
718
|
+
"confidence": data["confidence"],
|
|
719
|
+
"changes_made": data.get("changes_made", []),
|
|
720
|
+
"potential_side_effects": data.get("potential_side_effects", []),
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
except json.JSONDecodeError as e:
|
|
724
|
+
sanitized_error = self._sanitize_error_message(str(e))
|
|
725
|
+
logger.error(f"Failed to parse JSON response: {sanitized_error}")
|
|
726
|
+
return {
|
|
727
|
+
"success": False,
|
|
728
|
+
"error": f"Invalid JSON: {sanitized_error}",
|
|
729
|
+
"confidence": 0.0,
|
|
730
|
+
}
|
|
731
|
+
except Exception as e:
|
|
732
|
+
sanitized_error = self._sanitize_error_message(str(e))
|
|
733
|
+
logger.error(f"Unexpected error parsing response: {sanitized_error}")
|
|
734
|
+
return {
|
|
735
|
+
"success": False,
|
|
736
|
+
"error": sanitized_error,
|
|
737
|
+
"confidence": 0.0,
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
def _extract_json_from_response(self, content: str) -> str:
|
|
741
|
+
"""Extract JSON from response, handling markdown code blocks.
|
|
742
|
+
|
|
743
|
+
Args:
|
|
744
|
+
content: Raw response content
|
|
745
|
+
|
|
746
|
+
Returns:
|
|
747
|
+
Extracted JSON string
|
|
748
|
+
"""
|
|
749
|
+
# Remove markdown code blocks if present
|
|
750
|
+
if "```json" in content:
|
|
751
|
+
json_start = content.find("```json") + 7
|
|
752
|
+
json_end = content.find("```", json_start)
|
|
753
|
+
return content[json_start:json_end].strip()
|
|
754
|
+
|
|
755
|
+
if "```" in content:
|
|
756
|
+
json_start = content.find("```") + 3
|
|
757
|
+
json_end = content.find("```", json_start)
|
|
758
|
+
return content[json_start:json_end].strip()
|
|
759
|
+
|
|
760
|
+
# Assume entire content is JSON
|
|
761
|
+
return content.strip()
|
|
762
|
+
|
|
763
|
+
def _validate_fix_quality(
|
|
764
|
+
self,
|
|
765
|
+
parsed_response: dict[str, str | float | list[str] | bool],
|
|
766
|
+
original_code: str,
|
|
767
|
+
) -> bool:
|
|
768
|
+
"""Validate that the fix meets quality thresholds.
|
|
769
|
+
|
|
770
|
+
Checks:
|
|
771
|
+
- Response was successful
|
|
772
|
+
- Fixed code is non-empty
|
|
773
|
+
- Fixed code is different from original
|
|
774
|
+
- Confidence score is above minimum threshold
|
|
775
|
+
|
|
776
|
+
Args:
|
|
777
|
+
parsed_response: Parsed API response
|
|
778
|
+
original_code: Original code before fixing
|
|
779
|
+
|
|
780
|
+
Returns:
|
|
781
|
+
True if fix meets quality standards
|
|
782
|
+
"""
|
|
783
|
+
if not parsed_response.get("success"):
|
|
784
|
+
return False
|
|
785
|
+
|
|
786
|
+
# Extract values with proper type narrowing
|
|
787
|
+
fixed_code_raw = parsed_response.get("fixed_code", "")
|
|
788
|
+
confidence_raw = parsed_response.get("confidence", 0.0)
|
|
789
|
+
|
|
790
|
+
# Type narrowing: ensure we have strings and floats
|
|
791
|
+
fixed_code = str(fixed_code_raw) if fixed_code_raw else ""
|
|
792
|
+
confidence = (
|
|
793
|
+
float(confidence_raw) if isinstance(confidence_raw, (int, float)) else 0.0
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
# Must have actual code
|
|
797
|
+
if not fixed_code or not fixed_code.strip():
|
|
798
|
+
logger.warning("Fixed code is empty")
|
|
799
|
+
return False
|
|
800
|
+
|
|
801
|
+
# Must be different from original
|
|
802
|
+
if fixed_code.strip() == original_code.strip():
|
|
803
|
+
logger.warning("Fixed code is identical to original")
|
|
804
|
+
return False
|
|
805
|
+
|
|
806
|
+
# Must meet confidence threshold from settings
|
|
807
|
+
assert self._settings is not None, "Settings not initialized"
|
|
808
|
+
min_confidence = self._settings.confidence_threshold
|
|
809
|
+
if confidence < min_confidence:
|
|
810
|
+
logger.info(f"Confidence {confidence:.2f} below threshold {min_confidence}")
|
|
811
|
+
return False
|
|
812
|
+
|
|
813
|
+
return True
|
|
814
|
+
|
|
815
|
+
async def _backoff_delay(self, attempt: int) -> None:
|
|
816
|
+
"""Exponential backoff with jitter.
|
|
817
|
+
|
|
818
|
+
Args:
|
|
819
|
+
attempt: Current retry attempt number (0-indexed)
|
|
820
|
+
"""
|
|
821
|
+
# Base delay: 1s, 2s, 4s, 8s, ...
|
|
822
|
+
base_delay = 2**attempt
|
|
823
|
+
# Add jitter: ±25%
|
|
824
|
+
jitter = random.uniform(-0.25, 0.25) * base_delay # nosec B311 - not cryptographic
|
|
825
|
+
delay = base_delay + jitter
|
|
826
|
+
|
|
827
|
+
logger.info(f"Backing off for {delay:.2f}s before retry")
|
|
828
|
+
await asyncio.sleep(delay)
|
|
829
|
+
|
|
830
|
+
def _enhance_prompt_for_retry(
|
|
831
|
+
self,
|
|
832
|
+
original_prompt: str,
|
|
833
|
+
previous_response: dict[str, str | float | list[str] | bool],
|
|
834
|
+
) -> str:
|
|
835
|
+
"""Enhance prompt with feedback from previous attempt.
|
|
836
|
+
|
|
837
|
+
Args:
|
|
838
|
+
original_prompt: Original prompt
|
|
839
|
+
previous_response: Previous API response
|
|
840
|
+
|
|
841
|
+
Returns:
|
|
842
|
+
Enhanced prompt for retry
|
|
843
|
+
"""
|
|
844
|
+
confidence = previous_response.get("confidence", 0.0)
|
|
845
|
+
|
|
846
|
+
feedback = f"""
|
|
847
|
+
**Previous Attempt Analysis**:
|
|
848
|
+
The previous fix had confidence {confidence:.2f}.
|
|
849
|
+
Please provide a more robust solution with higher confidence.
|
|
850
|
+
|
|
851
|
+
{original_prompt}
|
|
852
|
+
"""
|
|
853
|
+
return feedback
|