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,557 @@
|
|
|
1
|
+
"""Zuban adapter for type checking with LSP integration."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import typing as t
|
|
5
|
+
from contextlib import suppress
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from ._base import BaseRustToolAdapter, Issue, ToolResult
|
|
10
|
+
|
|
11
|
+
if t.TYPE_CHECKING:
|
|
12
|
+
from crackerjack.orchestration.execution_strategies import ExecutionContext
|
|
13
|
+
from crackerjack.services.lsp_client import LSPClient
|
|
14
|
+
|
|
15
|
+
# Import the LSP client wrapper
|
|
16
|
+
from ._client import ZubanLSPClient
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class TypeIssue(Issue):
|
|
21
|
+
"""Zuban type checking issue."""
|
|
22
|
+
|
|
23
|
+
severity: str = "error" # Override default, type errors are typically errors
|
|
24
|
+
column: int = 1
|
|
25
|
+
error_code: str | None = None
|
|
26
|
+
|
|
27
|
+
def to_dict(self) -> dict[str, t.Any]:
|
|
28
|
+
"""Convert issue to dictionary with Zuban-specific fields."""
|
|
29
|
+
base_dict = super().to_dict()
|
|
30
|
+
base_dict.update(
|
|
31
|
+
{
|
|
32
|
+
"column": self.column,
|
|
33
|
+
"error_code": self.error_code,
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
return base_dict
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ZubanAdapter(BaseRustToolAdapter):
|
|
40
|
+
"""Zuban type checking adapter with LSP integration."""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
context: "ExecutionContext",
|
|
45
|
+
strict_mode: bool = True,
|
|
46
|
+
mypy_compatibility: bool = True,
|
|
47
|
+
use_lsp: bool = True,
|
|
48
|
+
) -> None:
|
|
49
|
+
"""Initialize Zuban adapter.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
context: Execution context
|
|
53
|
+
strict_mode: Enable strict type checking
|
|
54
|
+
mypy_compatibility: Use MyPy-compatible mode
|
|
55
|
+
use_lsp: Enable LSP integration for faster checking
|
|
56
|
+
"""
|
|
57
|
+
super().__init__(context)
|
|
58
|
+
self.strict_mode = strict_mode
|
|
59
|
+
self.mypy_compatibility = mypy_compatibility
|
|
60
|
+
self.use_lsp = use_lsp
|
|
61
|
+
self._lsp_client: LSPClient | None = None
|
|
62
|
+
self._lsp_wrapper: ZubanLSPClient | None = None
|
|
63
|
+
self._lsp_available = False
|
|
64
|
+
|
|
65
|
+
def get_tool_name(self) -> str:
|
|
66
|
+
"""Get the name of the tool."""
|
|
67
|
+
return "zuban"
|
|
68
|
+
|
|
69
|
+
def check_tool_health(self) -> bool:
|
|
70
|
+
"""Check if Zuban is functional before use.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
True if Zuban can be used safely, False otherwise
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
import subprocess
|
|
77
|
+
|
|
78
|
+
# Test basic version command - this should not crash
|
|
79
|
+
result = subprocess.run(
|
|
80
|
+
["uv", "run", "zuban", "--version"],
|
|
81
|
+
capture_output=True,
|
|
82
|
+
text=True,
|
|
83
|
+
timeout=10,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if result.returncode != 0:
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
# Test if we can parse TOML without crashing
|
|
90
|
+
# Create a minimal test to check for TOML parsing bug
|
|
91
|
+
result = subprocess.run(
|
|
92
|
+
["uv", "run", "zuban", "--help"],
|
|
93
|
+
capture_output=True,
|
|
94
|
+
text=True,
|
|
95
|
+
timeout=10,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return result.returncode == 0
|
|
99
|
+
|
|
100
|
+
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, Exception):
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
def supports_json_output(self) -> bool:
|
|
104
|
+
"""Zuban does not support JSON output mode."""
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
def _ensure_lsp_client(self) -> None:
|
|
108
|
+
"""Initialize LSP client if not already available."""
|
|
109
|
+
if not self.use_lsp or self._lsp_client is not None:
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
# Import here to avoid circular imports
|
|
114
|
+
from crackerjack.services.lsp_client import LSPClient
|
|
115
|
+
|
|
116
|
+
self._lsp_client = LSPClient()
|
|
117
|
+
self._lsp_available = self._lsp_client.is_server_running()
|
|
118
|
+
|
|
119
|
+
except ImportError:
|
|
120
|
+
self._lsp_available = False
|
|
121
|
+
|
|
122
|
+
async def get_lsp_diagnostics(self, target_files: list[Path]) -> list[TypeIssue]:
|
|
123
|
+
"""Get real-time diagnostics from LSP server.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
target_files: List of files to check
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
List of type issues found by LSP server
|
|
130
|
+
"""
|
|
131
|
+
self._ensure_lsp_client()
|
|
132
|
+
|
|
133
|
+
if not self._lsp_available or not self._lsp_client:
|
|
134
|
+
return []
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
# Convert paths to strings for LSP client
|
|
138
|
+
[str(f.resolve()) for f in target_files]
|
|
139
|
+
|
|
140
|
+
# Get diagnostics from LSP client
|
|
141
|
+
diagnostics, _ = self._lsp_client.check_project_with_feedback(
|
|
142
|
+
project_path=target_files[0].parent if target_files else Path.cwd(),
|
|
143
|
+
show_progress=False,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Convert LSP diagnostics to TypeIssue objects
|
|
147
|
+
issues: list[TypeIssue] = []
|
|
148
|
+
for file_path, file_diagnostics in diagnostics.items():
|
|
149
|
+
for diag in file_diagnostics:
|
|
150
|
+
issues.append(
|
|
151
|
+
TypeIssue(
|
|
152
|
+
file_path=Path(file_path),
|
|
153
|
+
line_number=diag.get("line", 1),
|
|
154
|
+
column=diag.get("column", 1),
|
|
155
|
+
message=diag.get("message", "Type error"),
|
|
156
|
+
severity=diag.get("severity", "error"),
|
|
157
|
+
error_code=diag.get("code"),
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
return issues
|
|
162
|
+
|
|
163
|
+
except Exception:
|
|
164
|
+
# LSP failed, return empty list[t.Any] to trigger fallback
|
|
165
|
+
self._lsp_available = False
|
|
166
|
+
return []
|
|
167
|
+
|
|
168
|
+
async def get_lsp_diagnostics_optimized(
|
|
169
|
+
self, target_files: list[Path]
|
|
170
|
+
) -> list[TypeIssue]:
|
|
171
|
+
"""Get diagnostics using optimized LSP wrapper.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
target_files: List of files to check
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
List of type issues found by LSP server
|
|
178
|
+
"""
|
|
179
|
+
if not self.use_lsp:
|
|
180
|
+
return []
|
|
181
|
+
|
|
182
|
+
if not self._lsp_wrapper:
|
|
183
|
+
self._lsp_wrapper = ZubanLSPClient()
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
async with self._lsp_wrapper as lsp:
|
|
187
|
+
if not await self._initialize_lsp_workspace(lsp, target_files):
|
|
188
|
+
return []
|
|
189
|
+
|
|
190
|
+
issues = await self._process_files_with_lsp(lsp, target_files)
|
|
191
|
+
return issues
|
|
192
|
+
|
|
193
|
+
except Exception:
|
|
194
|
+
# Fallback to original LSP client method
|
|
195
|
+
return await self.get_lsp_diagnostics(target_files)
|
|
196
|
+
|
|
197
|
+
async def _initialize_lsp_workspace(
|
|
198
|
+
self, lsp: t.Any, target_files: list[Path]
|
|
199
|
+
) -> bool:
|
|
200
|
+
"""Initialize LSP workspace and return success status."""
|
|
201
|
+
root_path = target_files[0].parent if target_files else Path.cwd()
|
|
202
|
+
init_result = await lsp.initialize(root_path)
|
|
203
|
+
return init_result and not init_result.get("error")
|
|
204
|
+
|
|
205
|
+
async def _process_files_with_lsp(
|
|
206
|
+
self, lsp: t.Any, target_files: list[Path]
|
|
207
|
+
) -> list[TypeIssue]:
|
|
208
|
+
"""Process files with LSP and collect diagnostics."""
|
|
209
|
+
issues: list[TypeIssue] = []
|
|
210
|
+
for file_path in target_files:
|
|
211
|
+
if file_path.exists():
|
|
212
|
+
file_issues = await self._get_file_diagnostics_from_lsp(lsp, file_path)
|
|
213
|
+
issues.extend(file_issues)
|
|
214
|
+
return issues
|
|
215
|
+
|
|
216
|
+
async def _get_file_diagnostics_from_lsp(
|
|
217
|
+
self, lsp: t.Any, file_path: Path
|
|
218
|
+
) -> list[TypeIssue]:
|
|
219
|
+
"""Get diagnostics for a single file from LSP."""
|
|
220
|
+
await lsp.text_document_did_open(file_path)
|
|
221
|
+
await asyncio.sleep(0.1) # Wait briefly for diagnostics
|
|
222
|
+
|
|
223
|
+
diagnostics = await lsp.get_diagnostics()
|
|
224
|
+
issues = []
|
|
225
|
+
|
|
226
|
+
for diag in diagnostics:
|
|
227
|
+
issue = self._create_type_issue_from_diagnostic(diag, file_path)
|
|
228
|
+
issues.append(issue)
|
|
229
|
+
|
|
230
|
+
await lsp.text_document_did_close(file_path)
|
|
231
|
+
return issues
|
|
232
|
+
|
|
233
|
+
def _create_type_issue_from_diagnostic(
|
|
234
|
+
self, diag: dict[str, t.Any], file_path: Path
|
|
235
|
+
) -> TypeIssue:
|
|
236
|
+
"""Create a TypeIssue from an LSP diagnostic."""
|
|
237
|
+
return TypeIssue(
|
|
238
|
+
file_path=Path(diag.get("uri", str(file_path)).replace("file://", "")),
|
|
239
|
+
line_number=diag.get("range", {}).get("start", {}).get("line", 0) + 1,
|
|
240
|
+
column=diag.get("range", {}).get("start", {}).get("character", 0) + 1,
|
|
241
|
+
message=diag.get("message", "Type error"),
|
|
242
|
+
severity=self._map_lsp_severity(diag.get("severity", 1)),
|
|
243
|
+
error_code=diag.get("code"),
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
def _map_lsp_severity(self, lsp_severity: int) -> str:
|
|
247
|
+
"""Map LSP severity codes to string values.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
lsp_severity: LSP severity (1=Error, 2=Warning, 3=Information, 4=Hint)
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
String severity level
|
|
254
|
+
"""
|
|
255
|
+
return {1: "error", 2: "warning", 3: "info", 4: "info"}.get(
|
|
256
|
+
lsp_severity, "error"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
def get_command_args(self, target_files: list[Path]) -> list[str]:
|
|
260
|
+
"""Get command arguments for Zuban execution (fallback mode)."""
|
|
261
|
+
args = ["uv", "run", "zuban"]
|
|
262
|
+
|
|
263
|
+
# Mode selection
|
|
264
|
+
if self.mypy_compatibility:
|
|
265
|
+
args.append("mypy") # MyPy-compatible mode
|
|
266
|
+
else:
|
|
267
|
+
args.append("check") # Native Zuban mode
|
|
268
|
+
|
|
269
|
+
# Strictness
|
|
270
|
+
if self.strict_mode:
|
|
271
|
+
args.append("--strict")
|
|
272
|
+
|
|
273
|
+
# Add error codes for better parsing
|
|
274
|
+
args.append("--show-error-codes")
|
|
275
|
+
|
|
276
|
+
# Target files/directories
|
|
277
|
+
if target_files:
|
|
278
|
+
args.extend(str(f) for f in target_files)
|
|
279
|
+
else:
|
|
280
|
+
args.append(".") # Check entire project
|
|
281
|
+
|
|
282
|
+
return args
|
|
283
|
+
|
|
284
|
+
async def check_with_lsp_or_fallback(self, target_files: list[Path]) -> ToolResult:
|
|
285
|
+
"""Check files using LSP if available, otherwise fallback to CLI mode.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
target_files: Files to type-check
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
ToolResult with issues found
|
|
292
|
+
"""
|
|
293
|
+
# First check if Zuban is functional at all
|
|
294
|
+
if not self.check_tool_health():
|
|
295
|
+
return self._create_error_result(
|
|
296
|
+
"Zuban is not functional due to TOML parsing bug. "
|
|
297
|
+
"Consider using pyright as alternative. "
|
|
298
|
+
"See ZUBAN_TOML_PARSING_BUG_ANALYSIS.md for details."
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# Try optimized LSP mode first
|
|
302
|
+
if self.use_lsp:
|
|
303
|
+
# Try optimized LSP wrapper first, then fallback to basic LSP client
|
|
304
|
+
lsp_issues = await self.get_lsp_diagnostics_optimized(target_files)
|
|
305
|
+
if not lsp_issues: # If optimized fails, try basic LSP client
|
|
306
|
+
lsp_issues = await self.get_lsp_diagnostics(target_files)
|
|
307
|
+
|
|
308
|
+
if lsp_issues is not None: # LSP succeeded (empty list is valid)
|
|
309
|
+
# Create successful result from LSP
|
|
310
|
+
error_issues = [i for i in lsp_issues if i.severity == "error"]
|
|
311
|
+
success = len(error_issues) == 0
|
|
312
|
+
|
|
313
|
+
result = ToolResult(
|
|
314
|
+
success=success,
|
|
315
|
+
issues=list[Issue](
|
|
316
|
+
lsp_issues
|
|
317
|
+
), # Convert TypeIssue list to Issue list
|
|
318
|
+
raw_output=f"LSP diagnostics: {len(lsp_issues)} issue(s) found",
|
|
319
|
+
tool_version=self.get_tool_version(),
|
|
320
|
+
)
|
|
321
|
+
# Add execution mode as custom attribute for tracking
|
|
322
|
+
result._execution_mode = "lsp"
|
|
323
|
+
return result
|
|
324
|
+
|
|
325
|
+
# LSP unavailable or failed, fallback to CLI mode
|
|
326
|
+
return await self._run_cli_fallback(target_files)
|
|
327
|
+
|
|
328
|
+
async def _run_cli_fallback(self, target_files: list[Path]) -> ToolResult:
|
|
329
|
+
"""Run Zuban in CLI mode as fallback.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
target_files: Files to check
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
ToolResult from CLI execution
|
|
336
|
+
"""
|
|
337
|
+
# Use the existing CLI execution logic
|
|
338
|
+
import subprocess
|
|
339
|
+
|
|
340
|
+
try:
|
|
341
|
+
cmd_args = self.get_command_args(target_files)
|
|
342
|
+
result = subprocess.run(
|
|
343
|
+
cmd_args,
|
|
344
|
+
capture_output=True,
|
|
345
|
+
text=True,
|
|
346
|
+
timeout=60,
|
|
347
|
+
cwd=self.context.root_path
|
|
348
|
+
if hasattr(self.context, "root_path")
|
|
349
|
+
else None,
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
# Parse the CLI output using existing methods
|
|
353
|
+
tool_result = self.parse_output(result.stdout + result.stderr)
|
|
354
|
+
|
|
355
|
+
# Mark as CLI mode for tracking
|
|
356
|
+
tool_result._execution_mode = "cli"
|
|
357
|
+
|
|
358
|
+
return tool_result
|
|
359
|
+
|
|
360
|
+
except subprocess.TimeoutExpired:
|
|
361
|
+
return self._create_error_result("Zuban execution timed out")
|
|
362
|
+
except Exception as e:
|
|
363
|
+
return self._create_error_result(f"Zuban execution failed: {e}")
|
|
364
|
+
|
|
365
|
+
def parse_output(self, output: str) -> ToolResult:
|
|
366
|
+
"""Parse Zuban output into standardized result."""
|
|
367
|
+
if self._should_use_json_output():
|
|
368
|
+
return self._parse_json_output(output)
|
|
369
|
+
return self._parse_text_output(output)
|
|
370
|
+
|
|
371
|
+
def _parse_json_output(self, output: str) -> ToolResult:
|
|
372
|
+
"""Parse JSON output for AI agents."""
|
|
373
|
+
data = self._parse_json_output_safe(output)
|
|
374
|
+
if data is None:
|
|
375
|
+
return self._create_error_result(
|
|
376
|
+
"Invalid JSON output from Zuban", raw_output=output
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
try:
|
|
380
|
+
issues: list[Issue] = []
|
|
381
|
+
for item in data.get("diagnostics", []):
|
|
382
|
+
# Determine severity
|
|
383
|
+
severity = item.get("severity", "error").lower()
|
|
384
|
+
if severity not in ("error", "warning", "info"):
|
|
385
|
+
severity = "error"
|
|
386
|
+
|
|
387
|
+
issues.append(
|
|
388
|
+
TypeIssue(
|
|
389
|
+
file_path=Path(item["file"]),
|
|
390
|
+
line_number=item.get("line", 1),
|
|
391
|
+
column=item.get("column", 1),
|
|
392
|
+
message=item["message"],
|
|
393
|
+
severity=severity,
|
|
394
|
+
error_code=item.get("code"),
|
|
395
|
+
)
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
# Success if no error-level issues
|
|
399
|
+
error_issues = [i for i in issues if i.severity == "error"]
|
|
400
|
+
success = len(error_issues) == 0
|
|
401
|
+
|
|
402
|
+
return ToolResult(
|
|
403
|
+
success=success,
|
|
404
|
+
issues=issues,
|
|
405
|
+
raw_output=output,
|
|
406
|
+
tool_version=self.get_tool_version(),
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
except (KeyError, TypeError, ValueError) as e:
|
|
410
|
+
return self._create_error_result(
|
|
411
|
+
f"Failed to parse Zuban JSON output: {e}", raw_output=output
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
def _parse_text_output(self, output: str) -> ToolResult:
|
|
415
|
+
"""Parse text output for human-readable display."""
|
|
416
|
+
issues: list[Issue] = []
|
|
417
|
+
|
|
418
|
+
if not output.strip():
|
|
419
|
+
# No output typically means no type errors found
|
|
420
|
+
return ToolResult(
|
|
421
|
+
success=True,
|
|
422
|
+
issues=[],
|
|
423
|
+
raw_output=output,
|
|
424
|
+
tool_version=self.get_tool_version(),
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
# Parse Zuban/MyPy-style text output
|
|
428
|
+
for line in output.strip().split("\\n"):
|
|
429
|
+
line = line.strip()
|
|
430
|
+
if not line:
|
|
431
|
+
continue
|
|
432
|
+
|
|
433
|
+
issue = self._parse_text_line(line)
|
|
434
|
+
if issue:
|
|
435
|
+
issues.append(issue)
|
|
436
|
+
|
|
437
|
+
# Success if no error-level issues
|
|
438
|
+
error_issues = [i for i in issues if i.severity == "error"]
|
|
439
|
+
success = len(error_issues) == 0
|
|
440
|
+
|
|
441
|
+
return ToolResult(
|
|
442
|
+
success=success,
|
|
443
|
+
issues=issues,
|
|
444
|
+
raw_output=output,
|
|
445
|
+
tool_version=self.get_tool_version(),
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
def _parse_text_line(self, line: str) -> TypeIssue | None:
|
|
449
|
+
"""Parse a single line of Zuban text output."""
|
|
450
|
+
try:
|
|
451
|
+
basic_info = self._extract_line_components(line)
|
|
452
|
+
if not basic_info:
|
|
453
|
+
return None
|
|
454
|
+
|
|
455
|
+
file_path, line_number, message_part = basic_info
|
|
456
|
+
column = self._extract_column_number(message_part)
|
|
457
|
+
|
|
458
|
+
message_data = self._parse_message_content(message_part)
|
|
459
|
+
severity = self._normalize_severity(message_data["severity"] or "error")
|
|
460
|
+
|
|
461
|
+
return TypeIssue(
|
|
462
|
+
file_path=file_path,
|
|
463
|
+
line_number=line_number,
|
|
464
|
+
column=column,
|
|
465
|
+
message=message_data["message"] or "Unknown error",
|
|
466
|
+
severity=severity,
|
|
467
|
+
error_code=message_data["error_code"],
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
except (IndexError, ValueError):
|
|
471
|
+
return None
|
|
472
|
+
|
|
473
|
+
def _extract_line_components(self, line: str) -> tuple[Path, int, str] | None:
|
|
474
|
+
"""Extract file path, line number, and remaining message from line."""
|
|
475
|
+
if ":" not in line:
|
|
476
|
+
return None
|
|
477
|
+
|
|
478
|
+
parts = line.split(":", 3)
|
|
479
|
+
if len(parts) < 3:
|
|
480
|
+
return None
|
|
481
|
+
|
|
482
|
+
file_path = Path(parts[0].strip())
|
|
483
|
+
|
|
484
|
+
try:
|
|
485
|
+
line_number = int(parts[1].strip())
|
|
486
|
+
except ValueError:
|
|
487
|
+
return None
|
|
488
|
+
|
|
489
|
+
# Handle both 3-part and 4-part formats
|
|
490
|
+
if len(parts) == 4:
|
|
491
|
+
message_part = f"{parts[2]}:{parts[3]}".strip()
|
|
492
|
+
else:
|
|
493
|
+
message_part = parts[2].strip()
|
|
494
|
+
|
|
495
|
+
return file_path, line_number, message_part
|
|
496
|
+
|
|
497
|
+
def _extract_column_number(self, message_part: str) -> int:
|
|
498
|
+
"""Extract column number if present in message part."""
|
|
499
|
+
# Try to extract column from the beginning of message_part
|
|
500
|
+
parts = message_part.split(":", 2)
|
|
501
|
+
if len(parts) >= 2:
|
|
502
|
+
with suppress(ValueError):
|
|
503
|
+
return int(parts[0].strip())
|
|
504
|
+
return 1
|
|
505
|
+
|
|
506
|
+
def _parse_message_content(self, message_part: str) -> dict[str, str | None]:
|
|
507
|
+
"""Parse message content to extract severity, message, and error code."""
|
|
508
|
+
# Skip column number if present
|
|
509
|
+
parts = message_part.split(":", 2)
|
|
510
|
+
if len(parts) >= 2:
|
|
511
|
+
try:
|
|
512
|
+
int(parts[0].strip()) # Check if first part is column number
|
|
513
|
+
working_message = ":".join(parts[1:]).strip()
|
|
514
|
+
except ValueError:
|
|
515
|
+
working_message = message_part
|
|
516
|
+
else:
|
|
517
|
+
working_message = message_part
|
|
518
|
+
|
|
519
|
+
severity, message = self._extract_severity_and_message(working_message)
|
|
520
|
+
error_code = self._extract_error_code(message)
|
|
521
|
+
|
|
522
|
+
# Remove error code from message if found
|
|
523
|
+
if error_code and "[" in message:
|
|
524
|
+
code_start = message.rfind("[")
|
|
525
|
+
message = message[:code_start].strip()
|
|
526
|
+
|
|
527
|
+
return {"severity": severity, "message": message, "error_code": error_code}
|
|
528
|
+
|
|
529
|
+
def _extract_severity_and_message(self, working_message: str) -> tuple[str, str]:
|
|
530
|
+
"""Extract severity indicator and remaining message."""
|
|
531
|
+
severity_indicators = ["error:", "warning:", "note:", "info:"]
|
|
532
|
+
|
|
533
|
+
for indicator in severity_indicators:
|
|
534
|
+
if working_message.lower().startswith(indicator):
|
|
535
|
+
severity = indicator[:-1] # Remove colon
|
|
536
|
+
message = working_message[len(indicator) :].strip()
|
|
537
|
+
return severity, message
|
|
538
|
+
|
|
539
|
+
# Default to error severity
|
|
540
|
+
return "error", working_message
|
|
541
|
+
|
|
542
|
+
def _extract_error_code(self, message: str) -> str | None:
|
|
543
|
+
"""Extract error code from message if present."""
|
|
544
|
+
if "[" in message and "]" in message:
|
|
545
|
+
code_start = message.rfind("[")
|
|
546
|
+
code_end = message.rfind("]")
|
|
547
|
+
if code_start < code_end:
|
|
548
|
+
return message[code_start + 1 : code_end]
|
|
549
|
+
return None
|
|
550
|
+
|
|
551
|
+
def _normalize_severity(self, severity: str) -> str:
|
|
552
|
+
"""Normalize severity to standard values."""
|
|
553
|
+
if severity in ("note", "info"):
|
|
554
|
+
return "info"
|
|
555
|
+
elif severity not in ("error", "warning"):
|
|
556
|
+
return "error"
|
|
557
|
+
return severity
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
> Crackerjack Docs: [Main](<../../../README.md>) | [Adapters](<../README.md>) | [Refactor](<./README.md>)
|
|
2
|
+
|
|
3
|
+
# Refactor Adapters
|
|
4
|
+
|
|
5
|
+
Modernization and dead-code tooling to simplify code and reduce risk.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
- Suggest idiomatic patterns and cleanups (Refurb)
|
|
10
|
+
- Identify unused dependencies (Creosote)
|
|
11
|
+
- Detect dead code for deletion (Skylos)
|
|
12
|
+
|
|
13
|
+
## Built-in Implementations
|
|
14
|
+
|
|
15
|
+
| Module | Description | Status |
|
|
16
|
+
| ------ | ----------- | ------ |
|
|
17
|
+
| `refurb.py` | Refactoring suggestions for modern Python idioms | Stable |
|
|
18
|
+
| `creosote.py` | Detects unused dependencies in `pyproject.toml`/`requirements*.txt` | Stable |
|
|
19
|
+
| `skylos.py` | Dead code detection (text/JSON parsing) | Stable |
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
Refurb:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from crackerjack.adapters.refactor.refurb import RefurbAdapter, RefurbSettings
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def suggest_refactors() -> None:
|
|
31
|
+
adapter = RefurbAdapter(settings=RefurbSettings(explain=True))
|
|
32
|
+
await adapter.init()
|
|
33
|
+
result = await adapter.check(files=[Path("src/")])
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Creosote:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
from crackerjack.adapters.refactor.creosote import CreosoteAdapter, CreosoteSettings
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def find_unused_deps() -> None:
|
|
44
|
+
adapter = CreosoteAdapter(
|
|
45
|
+
settings=CreosoteSettings(config_file=Path("pyproject.toml"))
|
|
46
|
+
)
|
|
47
|
+
await adapter.init()
|
|
48
|
+
result = await adapter.check()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Notes
|
|
52
|
+
|
|
53
|
+
- Treat Refurb suggestions as warnings; human review recommended
|
|
54
|
+
- Exclude dev/build tools in Creosote to avoid false positives
|
|
55
|
+
|
|
56
|
+
## Related
|
|
57
|
+
|
|
58
|
+
- [Complexity](<../complexity/README.md>) — Use complexity reports to target refactors
|
|
59
|
+
- [Format](<../format/README.md>) — Formatting changes often enable simpler refactors
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Refactor adapters for code improvement suggestions.
|
|
2
|
+
|
|
3
|
+
Adapters:
|
|
4
|
+
- refurb: Modern Python best practices and idiom suggestions
|
|
5
|
+
- creosote: Unused dependency detection
|
|
6
|
+
- skylos: Dead code detection (unused imports, functions, classes, variables)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# ACB will auto-discover these adapters via depends.set() in module files
|
|
10
|
+
# No explicit imports needed here
|
|
11
|
+
|
|
12
|
+
__all__ = []
|