crackerjack 0.37.9__py3-none-any.whl → 0.45.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +30 -1
- crackerjack/__main__.py +342 -1263
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +27 -5
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/{rust_tool_manager.py → lsp/_manager.py} +3 -3
- crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
- crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +40 -12
- crackerjack/agents/base.py +1 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +49 -53
- crackerjack/agents/dry_agent.py +187 -3
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +6 -8
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/performance_agent.py +121 -1152
- crackerjack/agents/refactoring_agent.py +156 -655
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +19 -1605
- crackerjack/api.py +5 -7
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +1 -1
- crackerjack/cli/cache_handlers.py +23 -18
- crackerjack/cli/cache_handlers_enhanced.py +1 -4
- crackerjack/cli/facade.py +70 -8
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +249 -49
- crackerjack/cli/interactive.py +8 -5
- crackerjack/cli/options.py +203 -110
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +60 -24
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +256 -0
- crackerjack/config/global_lock_config.py +191 -54
- crackerjack/config/hooks.py +188 -16
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/async_workflow_orchestrator.py +79 -53
- crackerjack/core/autofix_coordinator.py +22 -9
- crackerjack/core/container.py +10 -9
- crackerjack/core/enhanced_container.py +9 -9
- crackerjack/core/performance.py +1 -1
- crackerjack/core/performance_monitor.py +5 -3
- crackerjack/core/phase_coordinator.py +1018 -634
- crackerjack/core/proactive_workflow.py +3 -3
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +167 -23
- crackerjack/core/session_coordinator.py +187 -382
- crackerjack/core/timeout_manager.py +161 -44
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +1247 -953
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/ai_templates.py +1 -1
- crackerjack/documentation/dual_output_generator.py +11 -9
- crackerjack/documentation/reference_generator.py +104 -59
- crackerjack/dynamic_config.py +52 -61
- crackerjack/errors.py +1 -1
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +2 -0
- crackerjack/executors/async_hook_executor.py +539 -77
- crackerjack/executors/cached_hook_executor.py +3 -3
- crackerjack/executors/hook_executor.py +967 -102
- crackerjack/executors/hook_lock_manager.py +31 -22
- crackerjack/executors/individual_hook_executor.py +66 -32
- crackerjack/executors/lsp_aware_hook_executor.py +136 -57
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +23 -7
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +8 -9
- crackerjack/intelligence/README.md +557 -0
- crackerjack/interactive.py +37 -10
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/async_hook_manager.py +41 -57
- crackerjack/managers/hook_manager.py +449 -79
- crackerjack/managers/publish_manager.py +81 -36
- crackerjack/managers/test_command_builder.py +290 -12
- crackerjack/managers/test_executor.py +93 -8
- crackerjack/managers/test_manager.py +1082 -75
- crackerjack/managers/test_progress.py +118 -26
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/cache.py +25 -2
- crackerjack/mcp/client_runner.py +35 -18
- crackerjack/mcp/context.py +9 -9
- crackerjack/mcp/dashboard.py +24 -8
- crackerjack/mcp/enhanced_progress_monitor.py +34 -23
- crackerjack/mcp/file_monitor.py +27 -6
- crackerjack/mcp/progress_components.py +45 -34
- crackerjack/mcp/progress_monitor.py +6 -9
- crackerjack/mcp/rate_limiter.py +11 -7
- crackerjack/mcp/server.py +2 -0
- crackerjack/mcp/server_core.py +187 -55
- crackerjack/mcp/service_watchdog.py +12 -9
- crackerjack/mcp/task_manager.py +2 -2
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +2 -0
- crackerjack/mcp/tools/core_tools.py +75 -52
- crackerjack/mcp/tools/execution_tools.py +87 -31
- crackerjack/mcp/tools/intelligence_tools.py +2 -2
- crackerjack/mcp/tools/proactive_tools.py +1 -1
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +180 -132
- crackerjack/mcp/tools/workflow_executor.py +87 -46
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/app.py +11 -1
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +27 -4
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +16 -2930
- crackerjack/mcp/websocket/server.py +1 -3
- crackerjack/mcp/websocket/websocket_handler.py +107 -6
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +10 -1
- crackerjack/models/config.py +639 -22
- crackerjack/models/config_adapter.py +6 -6
- crackerjack/models/protocols.py +1167 -23
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +19 -3
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/ai_agent_watchdog.py +5 -4
- crackerjack/monitoring/metrics_collector.py +4 -3
- crackerjack/monitoring/regression_prevention.py +4 -3
- crackerjack/monitoring/websocket_server.py +4 -241
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +20 -67
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +13 -6
- crackerjack/orchestration/execution_strategies.py +6 -6
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +1 -1
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/hooks.py +3 -2
- crackerjack/plugins/loader.py +3 -3
- crackerjack/plugins/managers.py +1 -1
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +8 -21
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/api_extractor.py +5 -3
- crackerjack/services/bounded_status_operations.py +45 -5
- crackerjack/services/cache.py +249 -318
- crackerjack/services/changelog_automation.py +7 -3
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +83 -39
- crackerjack/services/config_merge.py +9 -6
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +13 -26
- crackerjack/services/coverage_badge_service.py +6 -4
- crackerjack/services/coverage_ratchet.py +53 -27
- crackerjack/services/debug.py +18 -7
- crackerjack/services/dependency_analyzer.py +4 -4
- crackerjack/services/dependency_monitor.py +13 -13
- crackerjack/services/documentation_generator.py +4 -2
- crackerjack/services/documentation_service.py +62 -33
- crackerjack/services/enhanced_filesystem.py +81 -27
- crackerjack/services/enterprise_optimizer.py +1 -1
- crackerjack/services/error_pattern_analyzer.py +10 -10
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +5 -7
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +80 -109
- crackerjack/services/git.py +99 -5
- crackerjack/services/health_metrics.py +4 -6
- crackerjack/services/heatmap_generator.py +12 -3
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +101 -49
- crackerjack/services/log_manager.py +2 -2
- crackerjack/services/logging.py +120 -68
- crackerjack/services/lsp_client.py +12 -12
- crackerjack/services/memory_optimizer.py +27 -22
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
- crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
- crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
- crackerjack/services/parallel_executor.py +166 -55
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +21 -8
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/{quality_baseline.py → quality/quality_baseline.py} +163 -2
- crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
- crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
- crackerjack/services/regex_patterns.py +58 -2987
- crackerjack/services/regex_utils.py +55 -29
- crackerjack/services/secure_status_formatter.py +42 -15
- crackerjack/services/secure_subprocess.py +35 -2
- crackerjack/services/security.py +16 -8
- crackerjack/services/server_manager.py +40 -51
- crackerjack/services/smart_scheduling.py +46 -6
- crackerjack/services/status_authentication.py +3 -3
- crackerjack/services/thread_safe_status_collector.py +1 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +9 -5
- crackerjack/services/unified_config.py +43 -351
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +6 -4
- crackerjack/services/version_checker.py +14 -8
- crackerjack/services/zuban_lsp_service.py +5 -4
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/init.md +2 -12
- crackerjack/slash_commands/run.md +84 -50
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_regex_patterns.py +7 -3
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack/managers/test_manager_backup.py +0 -1075
- crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
- crackerjack/mixins/__init__.py +0 -3
- crackerjack/mixins/error_handling.py +0 -145
- crackerjack/services/config.py +0 -358
- crackerjack/ui/server_panels.py +0 -125
- crackerjack-0.37.9.dist-info/RECORD +0 -231
- /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
- /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,24 +2,35 @@ import ast
|
|
|
2
2
|
import typing as t
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from ..services.regex_patterns import SAFE_PATTERNS
|
|
6
5
|
from .base import (
|
|
6
|
+
AgentContext,
|
|
7
7
|
FixResult,
|
|
8
8
|
Issue,
|
|
9
9
|
IssueType,
|
|
10
10
|
SubAgent,
|
|
11
11
|
agent_registry,
|
|
12
12
|
)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
from .helpers.refactoring.code_transformer import CodeTransformer
|
|
14
|
+
from .helpers.refactoring.complexity_analyzer import ComplexityAnalyzer
|
|
15
|
+
from .helpers.refactoring.dead_code_detector import DeadCodeDetector
|
|
16
|
+
from .semantic_helpers import (
|
|
17
|
+
SemanticInsight,
|
|
18
|
+
create_semantic_enhancer,
|
|
19
|
+
get_session_enhanced_recommendations,
|
|
20
|
+
)
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class RefactoringAgent(SubAgent):
|
|
24
|
+
def __init__(self, context: AgentContext) -> None:
|
|
25
|
+
super().__init__(context)
|
|
26
|
+
self.semantic_enhancer = create_semantic_enhancer(context.project_path)
|
|
27
|
+
self.semantic_insights: dict[str, SemanticInsight] = {}
|
|
28
|
+
|
|
29
|
+
# Initialize helper modules
|
|
30
|
+
self._complexity_analyzer = ComplexityAnalyzer(context)
|
|
31
|
+
self._code_transformer = CodeTransformer(context)
|
|
32
|
+
self._dead_code_detector = DeadCodeDetector(context)
|
|
33
|
+
|
|
23
34
|
def get_supported_types(self) -> set[IssueType]:
|
|
24
35
|
return {IssueType.COMPLEXITY, IssueType.DEAD_CODE}
|
|
25
36
|
|
|
@@ -114,7 +125,10 @@ class RefactoringAgent(SubAgent):
|
|
|
114
125
|
remaining_issues=[f"Could not read file: {file_path}"],
|
|
115
126
|
)
|
|
116
127
|
|
|
117
|
-
|
|
128
|
+
# Delegate to code transformer helper
|
|
129
|
+
refactored_content = self._code_transformer.refactor_detect_agent_needs_pattern(
|
|
130
|
+
content
|
|
131
|
+
)
|
|
118
132
|
|
|
119
133
|
if refactored_content != content:
|
|
120
134
|
success = self.context.write_file_content(file_path, refactored_content)
|
|
@@ -126,7 +140,9 @@ class RefactoringAgent(SubAgent):
|
|
|
126
140
|
"Applied proven complexity reduction pattern for detect_agent_needs"
|
|
127
141
|
],
|
|
128
142
|
files_modified=[str(file_path)],
|
|
129
|
-
recommendations=
|
|
143
|
+
recommendations=await self._enhance_recommendations_with_semantic(
|
|
144
|
+
["Verify functionality after complexity reduction"]
|
|
145
|
+
),
|
|
130
146
|
)
|
|
131
147
|
else:
|
|
132
148
|
return FixResult(
|
|
@@ -176,7 +192,17 @@ class RefactoringAgent(SubAgent):
|
|
|
176
192
|
)
|
|
177
193
|
|
|
178
194
|
tree = ast.parse(content)
|
|
179
|
-
|
|
195
|
+
# Delegate to complexity analyzer helper
|
|
196
|
+
complex_functions = self._complexity_analyzer.find_complex_functions(
|
|
197
|
+
tree, content
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Enhance complex function detection with semantic analysis
|
|
201
|
+
semantic_complex_functions = await self._find_semantic_complex_patterns(
|
|
202
|
+
content, file_path
|
|
203
|
+
)
|
|
204
|
+
if semantic_complex_functions:
|
|
205
|
+
complex_functions.extend(semantic_complex_functions)
|
|
180
206
|
|
|
181
207
|
if not complex_functions:
|
|
182
208
|
return FixResult(
|
|
@@ -185,19 +211,27 @@ class RefactoringAgent(SubAgent):
|
|
|
185
211
|
recommendations=["No overly complex functions found"],
|
|
186
212
|
)
|
|
187
213
|
|
|
188
|
-
return self._apply_and_save_refactoring(
|
|
214
|
+
return await self._apply_and_save_refactoring(
|
|
215
|
+
file_path, content, complex_functions
|
|
216
|
+
)
|
|
189
217
|
|
|
190
|
-
def _apply_and_save_refactoring(
|
|
218
|
+
async def _apply_and_save_refactoring(
|
|
191
219
|
self,
|
|
192
220
|
file_path: Path,
|
|
193
221
|
content: str,
|
|
194
222
|
complex_functions: list[dict[str, t.Any]],
|
|
195
223
|
) -> FixResult:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
complex_functions
|
|
224
|
+
# Delegate refactoring to code transformer helper
|
|
225
|
+
refactored_content = self._code_transformer.refactor_complex_functions(
|
|
226
|
+
content, complex_functions
|
|
199
227
|
)
|
|
200
228
|
|
|
229
|
+
if refactored_content == content:
|
|
230
|
+
# Try enhanced strategies if basic refactoring didn't work
|
|
231
|
+
refactored_content = self._code_transformer.apply_enhanced_strategies(
|
|
232
|
+
content
|
|
233
|
+
)
|
|
234
|
+
|
|
201
235
|
if refactored_content == content:
|
|
202
236
|
return self._create_no_changes_result()
|
|
203
237
|
|
|
@@ -214,7 +248,9 @@ class RefactoringAgent(SubAgent):
|
|
|
214
248
|
confidence=0.8,
|
|
215
249
|
fixes_applied=[f"Reduced complexity in {len(complex_functions)} functions"],
|
|
216
250
|
files_modified=[str(file_path)],
|
|
217
|
-
recommendations=
|
|
251
|
+
recommendations=await self._enhance_recommendations_with_semantic(
|
|
252
|
+
["Verify functionality after complexity reduction"]
|
|
253
|
+
),
|
|
218
254
|
)
|
|
219
255
|
|
|
220
256
|
def _create_no_changes_result(self) -> FixResult:
|
|
@@ -292,7 +328,8 @@ class RefactoringAgent(SubAgent):
|
|
|
292
328
|
)
|
|
293
329
|
|
|
294
330
|
tree = ast.parse(content)
|
|
295
|
-
|
|
331
|
+
# Delegate to dead code detector helper
|
|
332
|
+
dead_code_analysis = self._dead_code_detector.analyze_dead_code(tree, content)
|
|
296
333
|
|
|
297
334
|
if not dead_code_analysis["removable_items"]:
|
|
298
335
|
return FixResult(
|
|
@@ -309,7 +346,14 @@ class RefactoringAgent(SubAgent):
|
|
|
309
346
|
content: str,
|
|
310
347
|
analysis: dict[str, t.Any],
|
|
311
348
|
) -> FixResult:
|
|
312
|
-
|
|
349
|
+
# Remove dead code items
|
|
350
|
+
lines = content.split("\n")
|
|
351
|
+
lines_to_remove = self._collect_all_removable_lines(lines, analysis)
|
|
352
|
+
|
|
353
|
+
filtered_lines = [
|
|
354
|
+
line for i, line in enumerate(lines) if i not in lines_to_remove
|
|
355
|
+
]
|
|
356
|
+
cleaned_content = "\n".join(filtered_lines)
|
|
313
357
|
|
|
314
358
|
if cleaned_content == content:
|
|
315
359
|
return self._create_no_cleanup_result()
|
|
@@ -331,6 +375,25 @@ class RefactoringAgent(SubAgent):
|
|
|
331
375
|
recommendations=["Verify imports and functionality after cleanup"],
|
|
332
376
|
)
|
|
333
377
|
|
|
378
|
+
def _collect_all_removable_lines(
|
|
379
|
+
self, lines: list[str], analysis: dict[str, t.Any]
|
|
380
|
+
) -> set[int]:
|
|
381
|
+
"""Collect all lines to remove from analysis."""
|
|
382
|
+
lines_to_remove: set[int] = set()
|
|
383
|
+
|
|
384
|
+
# Delegate to dead code detector helper for different removal types
|
|
385
|
+
lines_to_remove.update(
|
|
386
|
+
self._dead_code_detector.find_lines_to_remove(lines, analysis)
|
|
387
|
+
)
|
|
388
|
+
lines_to_remove.update(
|
|
389
|
+
self._dead_code_detector._find_unreachable_lines(lines, analysis)
|
|
390
|
+
)
|
|
391
|
+
lines_to_remove.update(
|
|
392
|
+
self._dead_code_detector._find_redundant_lines(lines, analysis)
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
return lines_to_remove
|
|
396
|
+
|
|
334
397
|
def _create_no_cleanup_result(self) -> FixResult:
|
|
335
398
|
return FixResult(
|
|
336
399
|
success=False,
|
|
@@ -349,662 +412,100 @@ class RefactoringAgent(SubAgent):
|
|
|
349
412
|
remaining_issues=[f"Error processing file: {error}"],
|
|
350
413
|
)
|
|
351
414
|
|
|
352
|
-
def
|
|
353
|
-
self,
|
|
354
|
-
tree: ast.AST,
|
|
355
|
-
content: str,
|
|
415
|
+
async def _find_semantic_complex_patterns(
|
|
416
|
+
self, content: str, file_path: Path
|
|
356
417
|
) -> list[dict[str, t.Any]]:
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
class ComplexityAnalyzer(ast.NodeVisitor):
|
|
360
|
-
def __init__(
|
|
361
|
-
self,
|
|
362
|
-
calc_complexity: t.Callable[
|
|
363
|
-
[ast.FunctionDef | ast.AsyncFunctionDef],
|
|
364
|
-
int,
|
|
365
|
-
],
|
|
366
|
-
) -> None:
|
|
367
|
-
self.calc_complexity = calc_complexity
|
|
368
|
-
|
|
369
|
-
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
|
370
|
-
complexity = self.calc_complexity(node)
|
|
371
|
-
if complexity > 15:
|
|
372
|
-
complex_functions.append(
|
|
373
|
-
{
|
|
374
|
-
"name": node.name,
|
|
375
|
-
"line_start": node.lineno,
|
|
376
|
-
"line_end": node.end_lineno or node.lineno,
|
|
377
|
-
"complexity": complexity,
|
|
378
|
-
"node": node,
|
|
379
|
-
},
|
|
380
|
-
)
|
|
381
|
-
self.generic_visit(node)
|
|
382
|
-
|
|
383
|
-
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
|
384
|
-
complexity = self.calc_complexity(node)
|
|
385
|
-
if complexity > 15:
|
|
386
|
-
complex_functions.append(
|
|
387
|
-
{
|
|
388
|
-
"name": node.name,
|
|
389
|
-
"line_start": node.lineno,
|
|
390
|
-
"line_end": node.end_lineno or node.lineno,
|
|
391
|
-
"complexity": complexity,
|
|
392
|
-
"node": node,
|
|
393
|
-
},
|
|
394
|
-
)
|
|
395
|
-
self.generic_visit(node)
|
|
396
|
-
|
|
397
|
-
analyzer = ComplexityAnalyzer(self._calculate_cognitive_complexity)
|
|
398
|
-
analyzer.visit(tree)
|
|
399
|
-
|
|
400
|
-
return complex_functions
|
|
401
|
-
|
|
402
|
-
def _calculate_cognitive_complexity(
|
|
403
|
-
self,
|
|
404
|
-
node: ast.FunctionDef | ast.AsyncFunctionDef,
|
|
405
|
-
) -> int:
|
|
406
|
-
calculator = self._create_complexity_calculator()
|
|
407
|
-
calculator.visit(node)
|
|
408
|
-
return calculator.complexity
|
|
418
|
+
"""Find semantically complex patterns using vector similarity."""
|
|
419
|
+
semantic_functions = []
|
|
409
420
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
def _apply_complexity_reduction(
|
|
416
|
-
self,
|
|
417
|
-
content: str,
|
|
418
|
-
complex_functions: list[dict[str, t.Any]],
|
|
419
|
-
) -> str:
|
|
420
|
-
refactored_content = self._refactor_complex_functions(
|
|
421
|
-
content, complex_functions
|
|
422
|
-
)
|
|
423
|
-
if refactored_content != content:
|
|
424
|
-
return refactored_content
|
|
425
|
-
|
|
426
|
-
return self._apply_enhanced_strategies(content)
|
|
427
|
-
|
|
428
|
-
def _refactor_complex_functions(
|
|
429
|
-
self, content: str, complex_functions: list[dict[str, t.Any]]
|
|
430
|
-
) -> str:
|
|
431
|
-
lines = content.split("\n")
|
|
432
|
-
|
|
433
|
-
for func_info in complex_functions:
|
|
434
|
-
func_name = func_info.get("name", "unknown")
|
|
435
|
-
|
|
436
|
-
if func_name == "detect_agent_needs":
|
|
437
|
-
refactored = self._refactor_detect_agent_needs_pattern(content)
|
|
438
|
-
if refactored != content:
|
|
439
|
-
return refactored
|
|
440
|
-
|
|
441
|
-
func_content = self._extract_function_content(lines, func_info)
|
|
442
|
-
if func_content:
|
|
443
|
-
extracted_helpers = self._extract_logical_sections(
|
|
444
|
-
func_content, func_info
|
|
445
|
-
)
|
|
446
|
-
if extracted_helpers:
|
|
447
|
-
modified_content = self._apply_function_extraction(
|
|
448
|
-
content, func_info, extracted_helpers
|
|
449
|
-
)
|
|
450
|
-
if modified_content != content:
|
|
451
|
-
return modified_content
|
|
452
|
-
|
|
453
|
-
return content
|
|
454
|
-
|
|
455
|
-
def _apply_enhanced_strategies(self, content: str) -> str:
|
|
456
|
-
enhanced_content = self._apply_enhanced_complexity_patterns(content)
|
|
457
|
-
return enhanced_content
|
|
458
|
-
|
|
459
|
-
def _apply_enhanced_complexity_patterns(self, content: str) -> str:
|
|
460
|
-
operations = [
|
|
461
|
-
self._extract_nested_conditions,
|
|
462
|
-
self._simplify_boolean_expressions,
|
|
463
|
-
self._extract_validation_patterns,
|
|
464
|
-
self._simplify_data_structures,
|
|
465
|
-
]
|
|
466
|
-
|
|
467
|
-
modified_content = content
|
|
468
|
-
for operation in operations:
|
|
469
|
-
modified_content = operation(modified_content)
|
|
470
|
-
|
|
471
|
-
return modified_content
|
|
472
|
-
|
|
473
|
-
def _extract_nested_conditions(self, content: str) -> str:
|
|
474
|
-
lines = content.split("\n")
|
|
475
|
-
modified_lines = []
|
|
476
|
-
|
|
477
|
-
for i, line in enumerate(lines):
|
|
478
|
-
stripped = line.strip()
|
|
479
|
-
|
|
480
|
-
if (
|
|
481
|
-
stripped.startswith("if ")
|
|
482
|
-
and (" and " in stripped or " or " in stripped)
|
|
483
|
-
and len(stripped) > 80
|
|
484
|
-
):
|
|
485
|
-
indent = " " * (len(line) - len(line.lstrip()))
|
|
486
|
-
helper_name = f"_is_complex_condition_{i}"
|
|
487
|
-
modified_lines.append(f"{indent}if self.{helper_name}(): ")
|
|
488
|
-
continue
|
|
489
|
-
|
|
490
|
-
modified_lines.append(line)
|
|
491
|
-
|
|
492
|
-
return "\n".join(modified_lines)
|
|
493
|
-
|
|
494
|
-
def _simplify_boolean_expressions(self, content: str) -> str:
|
|
495
|
-
lines = content.split("\n")
|
|
496
|
-
modified_lines = []
|
|
497
|
-
|
|
498
|
-
for line in lines:
|
|
499
|
-
if " and " in line and " or " in line and len(line.strip()) > 100:
|
|
500
|
-
if line.strip().startswith("if "):
|
|
501
|
-
indent = " " * (len(line) - len(line.lstrip()))
|
|
502
|
-
method_name = "_validate_complex_condition"
|
|
503
|
-
modified_lines.append(f"{indent}if self.{method_name}(): ")
|
|
504
|
-
continue
|
|
505
|
-
|
|
506
|
-
modified_lines.append(line)
|
|
507
|
-
|
|
508
|
-
return "\n".join(modified_lines)
|
|
509
|
-
|
|
510
|
-
def _extract_validation_patterns(self, content: str) -> str:
|
|
511
|
-
if "validation_extract" in SAFE_PATTERNS:
|
|
512
|
-
content = SAFE_PATTERNS["validation_extract"].apply(content)
|
|
513
|
-
else:
|
|
514
|
-
pattern_obj = SAFE_PATTERNS["match_validation_patterns"]
|
|
515
|
-
if pattern_obj.test(content):
|
|
516
|
-
matches = len(
|
|
517
|
-
[line for line in content.split("\n") if pattern_obj.test(line)]
|
|
421
|
+
try:
|
|
422
|
+
# Delegate to complexity analyzer helper
|
|
423
|
+
code_elements = (
|
|
424
|
+
self._complexity_analyzer.extract_code_functions_for_semantic_analysis(
|
|
425
|
+
content
|
|
518
426
|
)
|
|
519
|
-
|
|
520
|
-
pass
|
|
521
|
-
|
|
522
|
-
return content
|
|
427
|
+
)
|
|
523
428
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
):
|
|
537
|
-
pass
|
|
538
|
-
|
|
539
|
-
elif stripped.count(": ") > 5 and stripped.count(", ") > 5:
|
|
540
|
-
pass
|
|
541
|
-
|
|
542
|
-
modified_lines.append(line)
|
|
543
|
-
|
|
544
|
-
return "\n".join(modified_lines)
|
|
545
|
-
|
|
546
|
-
def _refactor_detect_agent_needs_pattern(self, content: str) -> str:
|
|
547
|
-
detect_func_start = "async def detect_agent_needs("
|
|
548
|
-
if detect_func_start not in content:
|
|
549
|
-
return content
|
|
550
|
-
|
|
551
|
-
original_pattern = """ recommendations = {
|
|
552
|
-
"urgent_agents": [],
|
|
553
|
-
"suggested_agents": [],
|
|
554
|
-
"workflow_recommendations": [],
|
|
555
|
-
"detection_reasoning": "",
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
if error_context: """
|
|
559
|
-
|
|
560
|
-
replacement_pattern = """ recommendations = {
|
|
561
|
-
"urgent_agents": [],
|
|
562
|
-
"suggested_agents": [],
|
|
563
|
-
"workflow_recommendations": [],
|
|
564
|
-
"detection_reasoning": "",
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
_add_urgent_agents_for_errors(recommendations, error_context)
|
|
568
|
-
_add_python_project_suggestions(recommendations, file_patterns)
|
|
569
|
-
_set_workflow_recommendations(recommendations)
|
|
570
|
-
_generate_detection_reasoning(recommendations)
|
|
571
|
-
|
|
572
|
-
return json.dumps(recommendations, indent=2)"""
|
|
573
|
-
|
|
574
|
-
if original_pattern in content:
|
|
575
|
-
modified_content = content.replace(original_pattern, replacement_pattern)
|
|
576
|
-
if modified_content != content:
|
|
577
|
-
return modified_content
|
|
578
|
-
|
|
579
|
-
return content
|
|
580
|
-
|
|
581
|
-
def _extract_logical_sections(
|
|
582
|
-
self, func_content: str, func_info: dict[str, t.Any]
|
|
583
|
-
) -> list[dict[str, str]]:
|
|
584
|
-
sections: list[dict[str, str]] = []
|
|
585
|
-
lines = func_content.split("\n")
|
|
586
|
-
current_section: list[str] = []
|
|
587
|
-
section_type: str | None = None
|
|
588
|
-
|
|
589
|
-
for line in lines:
|
|
590
|
-
stripped = line.strip()
|
|
591
|
-
|
|
592
|
-
if self._should_start_new_section(stripped, section_type):
|
|
593
|
-
if current_section:
|
|
594
|
-
sections.append(
|
|
595
|
-
self._create_section(
|
|
596
|
-
current_section, section_type, len(sections)
|
|
429
|
+
for element in code_elements:
|
|
430
|
+
if (
|
|
431
|
+
element["type"] == "function"
|
|
432
|
+
and element["estimated_complexity"] > 10
|
|
433
|
+
):
|
|
434
|
+
# Search for similar complex patterns
|
|
435
|
+
insight = (
|
|
436
|
+
await self.semantic_enhancer.find_refactoring_opportunities(
|
|
437
|
+
element["signature"]
|
|
438
|
+
+ "\n"
|
|
439
|
+
+ element["body"][:150], # Include body sample
|
|
440
|
+
current_file=file_path,
|
|
597
441
|
)
|
|
598
442
|
)
|
|
599
443
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
self, stripped: str, current_section_type: str | None
|
|
615
|
-
) -> bool:
|
|
616
|
-
if stripped.startswith("if ") and len(stripped) > 50:
|
|
617
|
-
return True
|
|
618
|
-
return (
|
|
619
|
-
stripped.startswith(("for ", "while ")) and current_section_type != "loop"
|
|
620
|
-
)
|
|
621
|
-
|
|
622
|
-
def _initialize_new_section(
|
|
623
|
-
self, line: str, stripped: str
|
|
624
|
-
) -> tuple[list[str], str]:
|
|
625
|
-
if stripped.startswith("if ") and len(stripped) > 50:
|
|
626
|
-
return [line], "conditional"
|
|
627
|
-
elif stripped.startswith(("for ", "while ")):
|
|
628
|
-
return [line], "loop"
|
|
629
|
-
return [line], "general"
|
|
630
|
-
|
|
631
|
-
def _create_section(
|
|
632
|
-
self, current_section: list[str], section_type: str | None, section_count: int
|
|
633
|
-
) -> dict[str, str]:
|
|
634
|
-
effective_type = section_type or "general"
|
|
635
|
-
name_prefix = "handle" if effective_type == "conditional" else "process"
|
|
636
|
-
|
|
637
|
-
return {
|
|
638
|
-
"type": effective_type,
|
|
639
|
-
"content": "\n".join(current_section),
|
|
640
|
-
"name": f"_{name_prefix}_{effective_type}_{section_count + 1}",
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
def _analyze_dead_code(self, tree: ast.AST, content: str) -> dict[str, t.Any]:
|
|
644
|
-
analysis: dict[str, list[t.Any]] = {
|
|
645
|
-
"unused_imports": [],
|
|
646
|
-
"unused_variables": [],
|
|
647
|
-
"unused_functions": [],
|
|
648
|
-
"unused_classes": [],
|
|
649
|
-
"unreachable_code": [],
|
|
650
|
-
"removable_items": [],
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
analyzer_result = self._collect_usage_data(tree)
|
|
654
|
-
self._process_unused_imports(analysis, analyzer_result)
|
|
655
|
-
self._process_unused_functions(analysis, analyzer_result)
|
|
656
|
-
self._process_unused_classes(analysis, analyzer_result)
|
|
657
|
-
self._detect_unreachable_code(analysis, tree, content)
|
|
658
|
-
self._detect_redundant_code(analysis, tree, content)
|
|
659
|
-
|
|
660
|
-
return analysis
|
|
661
|
-
|
|
662
|
-
def _collect_usage_data(self, tree: ast.AST) -> dict[str, t.Any]:
|
|
663
|
-
collector = self._create_usage_data_collector()
|
|
664
|
-
analyzer = self._create_enhanced_usage_analyzer(collector)
|
|
665
|
-
analyzer.visit(tree)
|
|
666
|
-
return collector.get_results(analyzer)
|
|
667
|
-
|
|
668
|
-
def _create_usage_data_collector(self) -> "UsageDataCollector":
|
|
669
|
-
from . import refactoring_helpers
|
|
670
|
-
|
|
671
|
-
return refactoring_helpers.UsageDataCollector()
|
|
672
|
-
|
|
673
|
-
def _create_enhanced_usage_analyzer(
|
|
674
|
-
self, collector: "UsageDataCollector"
|
|
675
|
-
) -> "EnhancedUsageAnalyzer":
|
|
676
|
-
from . import refactoring_helpers
|
|
677
|
-
|
|
678
|
-
return refactoring_helpers.EnhancedUsageAnalyzer(collector)
|
|
679
|
-
|
|
680
|
-
def _process_unused_imports(
|
|
681
|
-
self,
|
|
682
|
-
analysis: dict[str, t.Any],
|
|
683
|
-
analyzer_result: dict[str, t.Any],
|
|
684
|
-
) -> None:
|
|
685
|
-
import_lines: list[tuple[int, str, str]] = analyzer_result["import_lines"]
|
|
686
|
-
for line_no, name, import_type in import_lines:
|
|
687
|
-
if name not in analyzer_result["used_names"]:
|
|
688
|
-
analysis["unused_imports"].append(
|
|
689
|
-
{
|
|
690
|
-
"name": name,
|
|
691
|
-
"line": line_no,
|
|
692
|
-
"type": import_type,
|
|
693
|
-
},
|
|
694
|
-
)
|
|
695
|
-
analysis["removable_items"].append(f"unused import: {name}")
|
|
696
|
-
|
|
697
|
-
def _process_unused_functions(
|
|
698
|
-
self,
|
|
699
|
-
analysis: dict[str, t.Any],
|
|
700
|
-
analyzer_result: dict[str, t.Any],
|
|
701
|
-
) -> None:
|
|
702
|
-
all_unused_functions: list[dict[str, t.Any]] = analyzer_result[
|
|
703
|
-
"unused_functions"
|
|
704
|
-
]
|
|
705
|
-
unused_functions = [
|
|
706
|
-
func
|
|
707
|
-
for func in all_unused_functions
|
|
708
|
-
if func["name"] not in analyzer_result["used_names"]
|
|
709
|
-
]
|
|
710
|
-
analysis["unused_functions"] = unused_functions
|
|
711
|
-
for func in unused_functions:
|
|
712
|
-
analysis["removable_items"].append(f"unused function: {func['name']}")
|
|
713
|
-
|
|
714
|
-
def _process_unused_classes(
|
|
715
|
-
self, analysis: dict[str, t.Any], analyzer_result: dict[str, t.Any]
|
|
716
|
-
) -> None:
|
|
717
|
-
if "unused_classes" not in analyzer_result:
|
|
718
|
-
return
|
|
719
|
-
|
|
720
|
-
unused_classes = [
|
|
721
|
-
cls
|
|
722
|
-
for cls in analyzer_result["unused_classes"]
|
|
723
|
-
if cls["name"] not in analyzer_result["used_names"]
|
|
724
|
-
]
|
|
725
|
-
|
|
726
|
-
analysis["unused_classes"] = unused_classes
|
|
727
|
-
for cls in unused_classes:
|
|
728
|
-
analysis["removable_items"].append(f"unused class: {cls['name']}")
|
|
729
|
-
|
|
730
|
-
def _detect_unreachable_code(
|
|
731
|
-
self, analysis: dict[str, t.Any], tree: ast.AST, content: str
|
|
732
|
-
) -> None:
|
|
733
|
-
class UnreachableCodeDetector(ast.NodeVisitor):
|
|
734
|
-
def __init__(self) -> None:
|
|
735
|
-
self.unreachable_blocks: list[dict[str, t.Any]] = []
|
|
736
|
-
|
|
737
|
-
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
|
738
|
-
self._check_unreachable_in_function(node)
|
|
739
|
-
self.generic_visit(node)
|
|
740
|
-
|
|
741
|
-
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
|
742
|
-
self._check_unreachable_in_function(node)
|
|
743
|
-
self.generic_visit(node)
|
|
744
|
-
|
|
745
|
-
def _check_unreachable_in_function(
|
|
746
|
-
self, node: ast.FunctionDef | ast.AsyncFunctionDef
|
|
747
|
-
) -> None:
|
|
748
|
-
for i, stmt in enumerate(node.body):
|
|
749
|
-
if isinstance(stmt, ast.Return | ast.Raise):
|
|
750
|
-
if i + 1 < len(node.body):
|
|
751
|
-
next_stmt = node.body[i + 1]
|
|
752
|
-
self.unreachable_blocks.append(
|
|
753
|
-
{
|
|
754
|
-
"type": "unreachable_after_return",
|
|
755
|
-
"line": next_stmt.lineno,
|
|
756
|
-
"function": node.name,
|
|
757
|
-
}
|
|
758
|
-
)
|
|
759
|
-
|
|
760
|
-
detector = UnreachableCodeDetector()
|
|
761
|
-
detector.visit(tree)
|
|
762
|
-
|
|
763
|
-
analysis["unreachable_code"] = detector.unreachable_blocks
|
|
764
|
-
for block in detector.unreachable_blocks:
|
|
765
|
-
analysis["removable_items"].append(
|
|
766
|
-
f"unreachable code after line {block['line']} in {block['function']}"
|
|
767
|
-
)
|
|
768
|
-
|
|
769
|
-
def _detect_redundant_code(
|
|
770
|
-
self, analysis: dict[str, t.Any], tree: ast.AST, content: str
|
|
771
|
-
) -> None:
|
|
772
|
-
lines = content.split("\n")
|
|
444
|
+
if insight.total_matches > 2:
|
|
445
|
+
semantic_functions.append(
|
|
446
|
+
{
|
|
447
|
+
"name": element["name"],
|
|
448
|
+
"line_start": element["start_line"],
|
|
449
|
+
"line_end": element["end_line"],
|
|
450
|
+
"complexity": element["estimated_complexity"],
|
|
451
|
+
"semantic_matches": insight.total_matches,
|
|
452
|
+
"refactor_opportunities": insight.related_patterns[
|
|
453
|
+
:3
|
|
454
|
+
], # Top 3 matches
|
|
455
|
+
"node": element.get("node"),
|
|
456
|
+
}
|
|
457
|
+
)
|
|
773
458
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
if line.strip() and not line.strip().startswith("#"):
|
|
777
|
-
line_hash = hash(line.strip())
|
|
778
|
-
if line_hash in line_hashes:
|
|
779
|
-
analysis["removable_items"].append(
|
|
780
|
-
f"potential duplicate code at line {i + 1}"
|
|
781
|
-
)
|
|
782
|
-
line_hashes[line_hash] = i
|
|
459
|
+
# Store insight for recommendation enhancement
|
|
460
|
+
self.semantic_insights[element["name"]] = insight
|
|
783
461
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
self.redundant_items: list[dict[str, t.Any]] = []
|
|
462
|
+
except Exception as e:
|
|
463
|
+
self.log(f"Warning: Semantic complexity detection failed: {e}")
|
|
787
464
|
|
|
788
|
-
|
|
789
|
-
if len(node.body) == 1 and isinstance(node.body[0], ast.Pass):
|
|
790
|
-
self.redundant_items.append(
|
|
791
|
-
{"type": "empty_except", "line": node.lineno}
|
|
792
|
-
)
|
|
793
|
-
self.generic_visit(node)
|
|
465
|
+
return semantic_functions
|
|
794
466
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
)
|
|
801
|
-
elif node.test.value is False:
|
|
802
|
-
self.redundant_items.append(
|
|
803
|
-
{"type": "if_false", "line": node.lineno}
|
|
804
|
-
)
|
|
805
|
-
self.generic_visit(node)
|
|
806
|
-
|
|
807
|
-
detector = RedundantPatternDetector()
|
|
808
|
-
detector.visit(tree)
|
|
467
|
+
async def _enhance_recommendations_with_semantic(
|
|
468
|
+
self, base_recommendations: list[str]
|
|
469
|
+
) -> list[str]:
|
|
470
|
+
"""Enhance recommendations with semantic insights."""
|
|
471
|
+
enhanced = base_recommendations.copy()
|
|
809
472
|
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
473
|
+
# Add semantic insights if available
|
|
474
|
+
if self.semantic_insights:
|
|
475
|
+
total_semantic_matches = sum(
|
|
476
|
+
insight.total_matches for insight in self.semantic_insights.values()
|
|
813
477
|
)
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
) -> bool:
|
|
818
|
-
if unused_import["type"] == "import":
|
|
819
|
-
return f"import {unused_import['name']}" in line
|
|
820
|
-
elif unused_import["type"] == "from_import":
|
|
821
|
-
return (
|
|
822
|
-
"from " in line
|
|
823
|
-
and unused_import["name"] in line
|
|
824
|
-
and line.strip().endswith(unused_import["name"])
|
|
478
|
+
high_conf_matches = sum(
|
|
479
|
+
insight.high_confidence_matches
|
|
480
|
+
for insight in self.semantic_insights.values()
|
|
825
481
|
)
|
|
826
|
-
return False
|
|
827
|
-
|
|
828
|
-
def _find_lines_to_remove(
|
|
829
|
-
self, lines: list[str], analysis: dict[str, t.Any]
|
|
830
|
-
) -> set[int]:
|
|
831
|
-
lines_to_remove: set[int] = set()
|
|
832
|
-
|
|
833
|
-
for unused_import in analysis["unused_imports"]:
|
|
834
|
-
line_idx = unused_import["line"] - 1
|
|
835
|
-
if 0 <= line_idx < len(lines):
|
|
836
|
-
line = lines[line_idx]
|
|
837
|
-
if self._should_remove_import_line(line, unused_import):
|
|
838
|
-
lines_to_remove.add(line_idx)
|
|
839
|
-
|
|
840
|
-
return lines_to_remove
|
|
841
|
-
|
|
842
|
-
def _remove_dead_code_items(self, content: str, analysis: dict[str, t.Any]) -> str:
|
|
843
|
-
lines = content.split("\n")
|
|
844
|
-
lines_to_remove = self._collect_all_removable_lines(lines, analysis)
|
|
845
|
-
|
|
846
|
-
filtered_lines = [
|
|
847
|
-
line for i, line in enumerate(lines) if i not in lines_to_remove
|
|
848
|
-
]
|
|
849
|
-
|
|
850
|
-
return "\n".join(filtered_lines)
|
|
851
|
-
|
|
852
|
-
def _collect_all_removable_lines(
|
|
853
|
-
self, lines: list[str], analysis: dict[str, t.Any]
|
|
854
|
-
) -> set[int]:
|
|
855
|
-
removal_functions = [
|
|
856
|
-
lambda: self._find_lines_to_remove(lines, analysis),
|
|
857
|
-
lambda: self._find_unreachable_lines(lines, analysis),
|
|
858
|
-
lambda: self._find_redundant_lines(lines, analysis),
|
|
859
|
-
]
|
|
860
|
-
|
|
861
|
-
lines_to_remove: set[int] = set()
|
|
862
|
-
for removal_func in removal_functions:
|
|
863
|
-
lines_to_remove.update(removal_func())
|
|
864
482
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
lines_to_remove: set[int] = set()
|
|
871
|
-
|
|
872
|
-
for item in analysis.get("unreachable_code", []):
|
|
873
|
-
if "line" in item:
|
|
874
|
-
line_idx = item["line"] - 1
|
|
875
|
-
if 0 <= line_idx < len(lines):
|
|
876
|
-
lines_to_remove.add(line_idx)
|
|
877
|
-
|
|
878
|
-
return lines_to_remove
|
|
879
|
-
|
|
880
|
-
def _find_redundant_lines(
|
|
881
|
-
self, lines: list[str], analysis: dict[str, t.Any]
|
|
882
|
-
) -> set[int]:
|
|
883
|
-
lines_to_remove: set[int] = set()
|
|
884
|
-
|
|
885
|
-
for i in range(len(lines)):
|
|
886
|
-
if self._is_empty_except_block(lines, i):
|
|
887
|
-
empty_pass_idx = self._find_empty_pass_line(lines, i)
|
|
888
|
-
if empty_pass_idx is not None:
|
|
889
|
-
lines_to_remove.add(empty_pass_idx)
|
|
890
|
-
|
|
891
|
-
return lines_to_remove
|
|
892
|
-
|
|
893
|
-
def _is_empty_except_block(self, lines: list[str], line_idx: int) -> bool:
|
|
894
|
-
stripped = lines[line_idx].strip()
|
|
895
|
-
return stripped == "except: " or stripped.startswith("except ")
|
|
896
|
-
|
|
897
|
-
def _find_empty_pass_line(self, lines: list[str], except_idx: int) -> int | None:
|
|
898
|
-
for j in range(except_idx + 1, min(except_idx + 5, len(lines))):
|
|
899
|
-
next_line = lines[j].strip()
|
|
900
|
-
if not next_line:
|
|
901
|
-
continue
|
|
902
|
-
if next_line == "pass":
|
|
903
|
-
return j
|
|
904
|
-
break
|
|
905
|
-
return None
|
|
906
|
-
|
|
907
|
-
def _extract_function_content(
|
|
908
|
-
self, lines: list[str], func_info: dict[str, t.Any]
|
|
909
|
-
) -> str:
|
|
910
|
-
start_line = func_info["line_start"] - 1
|
|
911
|
-
end_line = func_info.get("line_end", len(lines)) - 1
|
|
912
|
-
|
|
913
|
-
if start_line < 0 or end_line >= len(lines):
|
|
914
|
-
return ""
|
|
915
|
-
|
|
916
|
-
return "\n".join(lines[start_line : end_line + 1])
|
|
917
|
-
|
|
918
|
-
def _apply_function_extraction(
|
|
919
|
-
self,
|
|
920
|
-
content: str,
|
|
921
|
-
func_info: dict[str, t.Any],
|
|
922
|
-
extracted_helpers: list[dict[str, str]],
|
|
923
|
-
) -> str:
|
|
924
|
-
lines = content.split("\n")
|
|
925
|
-
|
|
926
|
-
if not self._is_extraction_valid(lines, func_info, extracted_helpers):
|
|
927
|
-
return "\n".join(lines)
|
|
928
|
-
|
|
929
|
-
return self._perform_extraction(lines, func_info, extracted_helpers)
|
|
483
|
+
if high_conf_matches > 0:
|
|
484
|
+
enhanced.append(
|
|
485
|
+
f"Semantic analysis found {high_conf_matches} similar complex patterns - "
|
|
486
|
+
f"consider extracting common refactoring utilities"
|
|
487
|
+
)
|
|
930
488
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
) -> bool:
|
|
937
|
-
start_line = func_info["line_start"] - 1
|
|
938
|
-
end_line = func_info.get("line_end", len(lines)) - 1
|
|
489
|
+
if total_semantic_matches >= 3:
|
|
490
|
+
enhanced.append(
|
|
491
|
+
f"Found {total_semantic_matches} related complexity patterns across codebase - "
|
|
492
|
+
f"review for consistent refactoring approach"
|
|
493
|
+
)
|
|
939
494
|
|
|
940
|
-
|
|
495
|
+
# Store insights for session continuity
|
|
496
|
+
for func_name, insight in self.semantic_insights.items():
|
|
497
|
+
summary = self.semantic_enhancer.get_semantic_context_summary(insight)
|
|
498
|
+
self.log(f"Semantic context for {func_name}: {summary}")
|
|
499
|
+
await self.semantic_enhancer.store_insight_to_session(
|
|
500
|
+
insight, "RefactoringAgent"
|
|
501
|
+
)
|
|
941
502
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
func_info: dict[str, t.Any],
|
|
946
|
-
extracted_helpers: list[dict[str, str]],
|
|
947
|
-
) -> str:
|
|
948
|
-
new_lines = self._replace_function_with_calls(
|
|
949
|
-
lines, func_info, extracted_helpers
|
|
503
|
+
# Enhance with session-stored insights
|
|
504
|
+
enhanced = await get_session_enhanced_recommendations(
|
|
505
|
+
enhanced, "RefactoringAgent", self.context.project_path
|
|
950
506
|
)
|
|
951
|
-
return self._add_helper_definitions(new_lines, func_info, extracted_helpers)
|
|
952
|
-
|
|
953
|
-
def _replace_function_with_calls(
|
|
954
|
-
self,
|
|
955
|
-
lines: list[str],
|
|
956
|
-
func_info: dict[str, t.Any],
|
|
957
|
-
extracted_helpers: list[dict[str, str]],
|
|
958
|
-
) -> list[str]:
|
|
959
|
-
start_line = func_info["line_start"] - 1
|
|
960
|
-
end_line = func_info.get("line_end", len(lines)) - 1
|
|
961
|
-
func_indent = len(lines[start_line]) - len(lines[start_line].lstrip())
|
|
962
|
-
indent = " " * (func_indent + 4)
|
|
963
|
-
|
|
964
|
-
new_func_lines = [lines[start_line]]
|
|
965
|
-
for helper in extracted_helpers:
|
|
966
|
-
new_func_lines.append(f"{indent}self.{helper['name']}()")
|
|
967
|
-
|
|
968
|
-
return lines[:start_line] + new_func_lines + lines[end_line + 1 :]
|
|
969
|
-
|
|
970
|
-
def _add_helper_definitions(
|
|
971
|
-
self,
|
|
972
|
-
new_lines: list[str],
|
|
973
|
-
func_info: dict[str, t.Any],
|
|
974
|
-
extracted_helpers: list[dict[str, str]],
|
|
975
|
-
) -> str:
|
|
976
|
-
start_line = func_info["line_start"] - 1
|
|
977
|
-
class_end = self._find_class_end(new_lines, start_line)
|
|
978
|
-
|
|
979
|
-
for helper in extracted_helpers:
|
|
980
|
-
helper_lines = helper["content"].split("\n")
|
|
981
|
-
new_lines = (
|
|
982
|
-
new_lines[:class_end] + [""] + helper_lines + new_lines[class_end:]
|
|
983
|
-
)
|
|
984
|
-
class_end += len(helper_lines) + 1
|
|
985
|
-
|
|
986
|
-
return "\n".join(new_lines)
|
|
987
|
-
|
|
988
|
-
def _find_class_end(self, lines: list[str], func_start: int) -> int:
|
|
989
|
-
class_indent = self._find_class_indent(lines, func_start)
|
|
990
|
-
if class_indent is None:
|
|
991
|
-
return len(lines)
|
|
992
|
-
return self._find_class_end_line(lines, func_start, class_indent)
|
|
993
|
-
|
|
994
|
-
def _find_class_indent(self, lines: list[str], func_start: int) -> int | None:
|
|
995
|
-
for i in range(func_start, -1, -1):
|
|
996
|
-
if lines[i].strip().startswith("class "):
|
|
997
|
-
return len(lines[i]) - len(lines[i].lstrip())
|
|
998
|
-
return None
|
|
999
507
|
|
|
1000
|
-
|
|
1001
|
-
self, lines: list[str], func_start: int, class_indent: int
|
|
1002
|
-
) -> int:
|
|
1003
|
-
for i in range(func_start + 1, len(lines)):
|
|
1004
|
-
line = lines[i]
|
|
1005
|
-
if line.strip() and len(line) - len(line.lstrip()) <= class_indent:
|
|
1006
|
-
return i
|
|
1007
|
-
return len(lines)
|
|
508
|
+
return enhanced
|
|
1008
509
|
|
|
1009
510
|
|
|
1010
511
|
agent_registry.register(RefactoringAgent)
|