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,628 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import concurrent.futures
|
|
3
|
+
import subprocess
|
|
4
|
+
import typing as t
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Protocol
|
|
7
|
+
|
|
8
|
+
from acb.console import Console
|
|
9
|
+
from acb.depends import Inject, depends
|
|
10
|
+
from rich.progress import (
|
|
11
|
+
BarColumn,
|
|
12
|
+
Progress,
|
|
13
|
+
SpinnerColumn,
|
|
14
|
+
TextColumn,
|
|
15
|
+
TimeElapsedColumn,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from .server_manager import find_zuban_lsp_processes
|
|
19
|
+
from .zuban_lsp_service import ZubanLSPService
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ProgressCallback(Protocol):
|
|
23
|
+
"""Protocol for progress reporting during type checking."""
|
|
24
|
+
|
|
25
|
+
def on_file_start(self, file_path: str) -> None:
|
|
26
|
+
"""Called when starting to check a file."""
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
def on_file_complete(self, file_path: str, error_count: int) -> None:
|
|
30
|
+
"""Called when finished checking a file."""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
def on_progress(self, current: int, total: int) -> None:
|
|
34
|
+
"""Called to report overall progress."""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class RealTimeTypingFeedback:
|
|
39
|
+
"""Provides real-time feedback during type checking operations."""
|
|
40
|
+
|
|
41
|
+
@depends.inject
|
|
42
|
+
def __init__(self, console: Inject[Console]) -> None:
|
|
43
|
+
self.console = console
|
|
44
|
+
self._total_errors = 0
|
|
45
|
+
self._files_checked = 0
|
|
46
|
+
|
|
47
|
+
def create_progress_display(self) -> Progress:
|
|
48
|
+
"""Create a progress display for type checking."""
|
|
49
|
+
return Progress(
|
|
50
|
+
SpinnerColumn(),
|
|
51
|
+
TextColumn("[progress.description]{task.description}"),
|
|
52
|
+
BarColumn(),
|
|
53
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
54
|
+
TextColumn("({task.completed}/{task.total})"),
|
|
55
|
+
TimeElapsedColumn(),
|
|
56
|
+
console=self.console,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def on_file_start(self, file_path: str) -> None:
|
|
60
|
+
"""Report that we're starting to check a file."""
|
|
61
|
+
rel_path = Path(file_path).name
|
|
62
|
+
self.console.print(f"🔍 Checking {rel_path}...", style="dim")
|
|
63
|
+
|
|
64
|
+
def on_file_complete(self, file_path: str, error_count: int) -> None:
|
|
65
|
+
"""Report completion of file checking."""
|
|
66
|
+
rel_path = Path(file_path).name
|
|
67
|
+
self._files_checked += 1
|
|
68
|
+
self._total_errors += error_count
|
|
69
|
+
|
|
70
|
+
if error_count == 0:
|
|
71
|
+
self.console.print(f"✅ {rel_path} - No issues", style="green dim")
|
|
72
|
+
else:
|
|
73
|
+
self.console.print(
|
|
74
|
+
f"❌ {rel_path} - {error_count} error(s)", style="red dim"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def on_progress(self, current: int, total: int) -> None:
|
|
78
|
+
"""Report overall progress."""
|
|
79
|
+
pass # Progress bar handles this
|
|
80
|
+
|
|
81
|
+
def get_summary(self) -> str:
|
|
82
|
+
"""Get a summary of the checking results."""
|
|
83
|
+
if self._total_errors == 0:
|
|
84
|
+
return f"✅ All {self._files_checked} files passed type checking"
|
|
85
|
+
return (
|
|
86
|
+
f"❌ Found {self._total_errors} type errors in {self._files_checked} files"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class JSONRPCClient:
|
|
91
|
+
"""JSON-RPC client for LSP communication."""
|
|
92
|
+
|
|
93
|
+
def __init__(self, lsp_service: ZubanLSPService) -> None:
|
|
94
|
+
self.lsp_service = lsp_service
|
|
95
|
+
self._request_id = 0
|
|
96
|
+
|
|
97
|
+
def _next_request_id(self) -> int:
|
|
98
|
+
"""Generate next request ID."""
|
|
99
|
+
self._request_id += 1
|
|
100
|
+
return self._request_id
|
|
101
|
+
|
|
102
|
+
async def initialize(self, root_path: str) -> dict[str, t.Any] | None:
|
|
103
|
+
"""Initialize the LSP server for a workspace."""
|
|
104
|
+
params = {
|
|
105
|
+
"processId": None,
|
|
106
|
+
"rootPath": root_path,
|
|
107
|
+
"rootUri": f"file://{root_path}",
|
|
108
|
+
"capabilities": {
|
|
109
|
+
"textDocument": {
|
|
110
|
+
"publishDiagnostics": {
|
|
111
|
+
"versionSupport": True,
|
|
112
|
+
"tagSupport": {"valueSet": [1, 2]},
|
|
113
|
+
"relatedInformation": True,
|
|
114
|
+
"codeDescriptionSupport": True,
|
|
115
|
+
"dataSupport": True,
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
return await self.lsp_service.send_lsp_request("initialize", params)
|
|
121
|
+
|
|
122
|
+
async def did_open(self, file_path: str) -> dict[str, t.Any] | None:
|
|
123
|
+
"""Notify server that a document was opened."""
|
|
124
|
+
content = Path(file_path).read_text(encoding="utf-8")
|
|
125
|
+
|
|
126
|
+
params = {
|
|
127
|
+
"textDocument": {
|
|
128
|
+
"uri": f"file://{file_path}",
|
|
129
|
+
"languageId": "python",
|
|
130
|
+
"version": 1,
|
|
131
|
+
"text": content,
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return await self.lsp_service.send_lsp_request("textDocument/didOpen", params)
|
|
135
|
+
|
|
136
|
+
async def did_close(self, file_path: str) -> dict[str, t.Any] | None:
|
|
137
|
+
"""Notify server that a document was closed."""
|
|
138
|
+
params = {
|
|
139
|
+
"textDocument": {
|
|
140
|
+
"uri": f"file://{file_path}",
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return await self.lsp_service.send_lsp_request("textDocument/didClose", params)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class LSPClient:
|
|
147
|
+
"""Client for communicating with Zuban LSP server."""
|
|
148
|
+
|
|
149
|
+
@depends.inject
|
|
150
|
+
def __init__(self, console: Inject[Console]) -> None:
|
|
151
|
+
self.console = console
|
|
152
|
+
self._server_port: int | None = None
|
|
153
|
+
self._server_host: str = "127.0.0.1"
|
|
154
|
+
self._lsp_service: ZubanLSPService | None = None
|
|
155
|
+
self._jsonrpc_client: JSONRPCClient | None = None
|
|
156
|
+
|
|
157
|
+
def is_server_running(self) -> bool:
|
|
158
|
+
"""Check if Zuban LSP server is currently running."""
|
|
159
|
+
if self._lsp_service and self._lsp_service.is_running:
|
|
160
|
+
return True
|
|
161
|
+
processes = find_zuban_lsp_processes()
|
|
162
|
+
return len(processes) > 0
|
|
163
|
+
|
|
164
|
+
async def _ensure_lsp_service(self) -> bool:
|
|
165
|
+
"""Ensure LSP service is available and initialized."""
|
|
166
|
+
if self._lsp_service and self._lsp_service.is_running:
|
|
167
|
+
return True
|
|
168
|
+
|
|
169
|
+
# Create new LSP service
|
|
170
|
+
self._lsp_service = ZubanLSPService(
|
|
171
|
+
port=self._server_port or 8677,
|
|
172
|
+
mode="stdio", # Currently zuban only supports stdio
|
|
173
|
+
console=self.console,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Start the service
|
|
177
|
+
if await self._lsp_service.start():
|
|
178
|
+
self._jsonrpc_client = JSONRPCClient(self._lsp_service)
|
|
179
|
+
return True
|
|
180
|
+
|
|
181
|
+
return False
|
|
182
|
+
|
|
183
|
+
def get_server_info(self) -> dict[str, t.Any] | None:
|
|
184
|
+
"""Get information about the running LSP server."""
|
|
185
|
+
processes = find_zuban_lsp_processes()
|
|
186
|
+
if not processes:
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
"pid": processes[0]["pid"],
|
|
191
|
+
"cpu": processes[0]["cpu"],
|
|
192
|
+
"mem": processes[0]["mem"],
|
|
193
|
+
"command": processes[0]["command"],
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
def check_files(
|
|
197
|
+
self,
|
|
198
|
+
file_paths: list[str],
|
|
199
|
+
progress_callback: ProgressCallback | None = None,
|
|
200
|
+
show_progress: bool = True,
|
|
201
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
202
|
+
"""
|
|
203
|
+
Check files for type errors using LSP server with real-time feedback.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
file_paths: List of file paths to check
|
|
207
|
+
progress_callback: Optional callback for progress reporting
|
|
208
|
+
show_progress: Whether to show progress display
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Dictionary mapping file paths to lists of diagnostic messages.
|
|
212
|
+
Each diagnostic contains: line, column, severity, message, code.
|
|
213
|
+
"""
|
|
214
|
+
if not self.is_server_running():
|
|
215
|
+
if progress_callback:
|
|
216
|
+
self.console.print(
|
|
217
|
+
"⚠️ Zuban LSP server not running, falling back to direct zuban calls",
|
|
218
|
+
style="yellow",
|
|
219
|
+
)
|
|
220
|
+
return self._check_files_with_feedback(
|
|
221
|
+
file_paths, progress_callback, show_progress
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# When LSP server is running, use it for better performance
|
|
225
|
+
return self._check_files_via_lsp(file_paths, progress_callback, show_progress)
|
|
226
|
+
|
|
227
|
+
def _check_files_with_feedback(
|
|
228
|
+
self,
|
|
229
|
+
file_paths: list[str],
|
|
230
|
+
progress_callback: ProgressCallback | None = None,
|
|
231
|
+
show_progress: bool = True,
|
|
232
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
233
|
+
"""Check files with real-time feedback using direct zuban calls."""
|
|
234
|
+
total_files = len(file_paths)
|
|
235
|
+
|
|
236
|
+
if show_progress and total_files > 1:
|
|
237
|
+
return self._check_files_with_progress_display(
|
|
238
|
+
file_paths, progress_callback, total_files
|
|
239
|
+
)
|
|
240
|
+
return self._check_files_simple_feedback(file_paths, progress_callback)
|
|
241
|
+
|
|
242
|
+
def _check_files_with_progress_display(
|
|
243
|
+
self,
|
|
244
|
+
file_paths: list[str],
|
|
245
|
+
progress_callback: ProgressCallback | None,
|
|
246
|
+
total_files: int,
|
|
247
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
248
|
+
"""Check files with progress display."""
|
|
249
|
+
diagnostics = {}
|
|
250
|
+
feedback = RealTimeTypingFeedback()
|
|
251
|
+
|
|
252
|
+
with feedback.create_progress_display() as progress:
|
|
253
|
+
task = progress.add_task("Type checking files...", total=total_files)
|
|
254
|
+
|
|
255
|
+
for file_path in file_paths:
|
|
256
|
+
diagnostics.update(
|
|
257
|
+
self._process_single_file_with_zuban(file_path, progress_callback)
|
|
258
|
+
)
|
|
259
|
+
progress.update(task, advance=1)
|
|
260
|
+
|
|
261
|
+
return diagnostics
|
|
262
|
+
|
|
263
|
+
def _check_files_simple_feedback(
|
|
264
|
+
self,
|
|
265
|
+
file_paths: list[str],
|
|
266
|
+
progress_callback: ProgressCallback | None,
|
|
267
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
268
|
+
"""Check files without progress display."""
|
|
269
|
+
diagnostics = {}
|
|
270
|
+
|
|
271
|
+
for file_path in file_paths:
|
|
272
|
+
diagnostics.update(
|
|
273
|
+
self._process_single_file_with_zuban(file_path, progress_callback)
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
return diagnostics
|
|
277
|
+
|
|
278
|
+
def _process_single_file_with_zuban(
|
|
279
|
+
self,
|
|
280
|
+
file_path: str,
|
|
281
|
+
progress_callback: ProgressCallback | None,
|
|
282
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
283
|
+
"""Process a single file with zuban and handle callbacks."""
|
|
284
|
+
if progress_callback:
|
|
285
|
+
progress_callback.on_file_start(file_path)
|
|
286
|
+
|
|
287
|
+
file_diagnostics = self._check_file_with_zuban(file_path)
|
|
288
|
+
|
|
289
|
+
if progress_callback:
|
|
290
|
+
progress_callback.on_file_complete(file_path, len(file_diagnostics))
|
|
291
|
+
|
|
292
|
+
return {file_path: file_diagnostics}
|
|
293
|
+
|
|
294
|
+
def _check_files_via_lsp(
|
|
295
|
+
self,
|
|
296
|
+
file_paths: list[str],
|
|
297
|
+
progress_callback: ProgressCallback | None = None,
|
|
298
|
+
show_progress: bool = True,
|
|
299
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
300
|
+
"""Check files using LSP server communication."""
|
|
301
|
+
# Use asyncio to run the async LSP implementation
|
|
302
|
+
try:
|
|
303
|
+
# Try to get or create an event loop
|
|
304
|
+
try:
|
|
305
|
+
asyncio.get_running_loop()
|
|
306
|
+
# We're already in an async context, use a thread pool
|
|
307
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
308
|
+
future = executor.submit(
|
|
309
|
+
self._run_async_lsp_check,
|
|
310
|
+
file_paths,
|
|
311
|
+
progress_callback,
|
|
312
|
+
show_progress,
|
|
313
|
+
)
|
|
314
|
+
return future.result(timeout=120) # 2 minute timeout
|
|
315
|
+
except RuntimeError:
|
|
316
|
+
# No running loop, create new one
|
|
317
|
+
return asyncio.run(
|
|
318
|
+
self._async_check_files_via_lsp(
|
|
319
|
+
file_paths, progress_callback, show_progress
|
|
320
|
+
)
|
|
321
|
+
)
|
|
322
|
+
except Exception as e:
|
|
323
|
+
self.console.print(
|
|
324
|
+
f"[yellow]⚠️ LSP communication failed: {e}, falling back to direct calls[/yellow]"
|
|
325
|
+
)
|
|
326
|
+
return self._check_files_with_feedback(
|
|
327
|
+
file_paths, progress_callback, show_progress
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
def _run_async_lsp_check(
|
|
331
|
+
self,
|
|
332
|
+
file_paths: list[str],
|
|
333
|
+
progress_callback: ProgressCallback | None = None,
|
|
334
|
+
show_progress: bool = True,
|
|
335
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
336
|
+
"""Run async LSP check in a new event loop."""
|
|
337
|
+
return asyncio.run(
|
|
338
|
+
self._async_check_files_via_lsp(
|
|
339
|
+
file_paths, progress_callback, show_progress
|
|
340
|
+
)
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
async def _async_check_files_via_lsp(
|
|
344
|
+
self,
|
|
345
|
+
file_paths: list[str],
|
|
346
|
+
progress_callback: ProgressCallback | None = None,
|
|
347
|
+
show_progress: bool = True,
|
|
348
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
349
|
+
"""Async implementation of LSP-based file checking."""
|
|
350
|
+
# Validate prerequisites
|
|
351
|
+
if not await self._validate_lsp_prerequisites():
|
|
352
|
+
return self._check_files_with_feedback(
|
|
353
|
+
file_paths, progress_callback, show_progress
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
try:
|
|
357
|
+
# Initialize LSP workspace
|
|
358
|
+
await self._initialize_lsp_workspace(file_paths)
|
|
359
|
+
|
|
360
|
+
# Process files with appropriate progress handling
|
|
361
|
+
return await self._process_files_via_lsp(
|
|
362
|
+
file_paths, progress_callback, show_progress
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
except Exception as e:
|
|
366
|
+
self.console.print(
|
|
367
|
+
f"[yellow]⚠️ LSP protocol error: {e}, falling back to direct calls[/yellow]"
|
|
368
|
+
)
|
|
369
|
+
return self._check_files_with_feedback(
|
|
370
|
+
file_paths, progress_callback, show_progress
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
async def _validate_lsp_prerequisites(self) -> bool:
|
|
374
|
+
"""Validate that LSP service and client are ready."""
|
|
375
|
+
if not await self._ensure_lsp_service():
|
|
376
|
+
return False
|
|
377
|
+
|
|
378
|
+
if not self._jsonrpc_client:
|
|
379
|
+
return False
|
|
380
|
+
|
|
381
|
+
return True
|
|
382
|
+
|
|
383
|
+
async def _initialize_lsp_workspace(self, file_paths: list[str]) -> None:
|
|
384
|
+
"""Initialize LSP workspace with project root."""
|
|
385
|
+
assert self._jsonrpc_client is not None, "LSP client must be initialized"
|
|
386
|
+
project_root = (
|
|
387
|
+
str(Path(file_paths[0]).parent) if file_paths else str(Path.cwd())
|
|
388
|
+
)
|
|
389
|
+
await self._jsonrpc_client.initialize(project_root)
|
|
390
|
+
|
|
391
|
+
async def _process_files_via_lsp(
|
|
392
|
+
self,
|
|
393
|
+
file_paths: list[str],
|
|
394
|
+
progress_callback: ProgressCallback | None = None,
|
|
395
|
+
show_progress: bool = True,
|
|
396
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
397
|
+
"""Process files via LSP with progress tracking."""
|
|
398
|
+
total_files = len(file_paths)
|
|
399
|
+
|
|
400
|
+
if show_progress and total_files > 1:
|
|
401
|
+
return await self._process_files_with_progress(
|
|
402
|
+
file_paths, progress_callback, total_files
|
|
403
|
+
)
|
|
404
|
+
return await self._process_files_simple(file_paths, progress_callback)
|
|
405
|
+
|
|
406
|
+
async def _process_files_with_progress(
|
|
407
|
+
self,
|
|
408
|
+
file_paths: list[str],
|
|
409
|
+
progress_callback: ProgressCallback | None,
|
|
410
|
+
total_files: int,
|
|
411
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
412
|
+
"""Process files with progress display."""
|
|
413
|
+
diagnostics = {}
|
|
414
|
+
feedback = RealTimeTypingFeedback()
|
|
415
|
+
|
|
416
|
+
with feedback.create_progress_display() as progress:
|
|
417
|
+
task = progress.add_task("LSP type checking files...", total=total_files)
|
|
418
|
+
|
|
419
|
+
for file_path in file_paths:
|
|
420
|
+
diagnostics.update(
|
|
421
|
+
await self._process_single_file_with_callback(
|
|
422
|
+
file_path, progress_callback
|
|
423
|
+
)
|
|
424
|
+
)
|
|
425
|
+
progress.update(task, advance=1)
|
|
426
|
+
|
|
427
|
+
return diagnostics
|
|
428
|
+
|
|
429
|
+
async def _process_files_simple(
|
|
430
|
+
self,
|
|
431
|
+
file_paths: list[str],
|
|
432
|
+
progress_callback: ProgressCallback | None,
|
|
433
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
434
|
+
"""Process files without progress display."""
|
|
435
|
+
diagnostics = {}
|
|
436
|
+
|
|
437
|
+
for file_path in file_paths:
|
|
438
|
+
diagnostics.update(
|
|
439
|
+
await self._process_single_file_with_callback(
|
|
440
|
+
file_path, progress_callback
|
|
441
|
+
)
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
return diagnostics
|
|
445
|
+
|
|
446
|
+
async def _process_single_file_with_callback(
|
|
447
|
+
self,
|
|
448
|
+
file_path: str,
|
|
449
|
+
progress_callback: ProgressCallback | None,
|
|
450
|
+
) -> dict[str, list[dict[str, t.Any]]]:
|
|
451
|
+
"""Process a single file with progress callback handling."""
|
|
452
|
+
if progress_callback:
|
|
453
|
+
progress_callback.on_file_start(file_path)
|
|
454
|
+
|
|
455
|
+
file_diagnostics = await self._check_file_via_lsp(file_path)
|
|
456
|
+
|
|
457
|
+
if progress_callback:
|
|
458
|
+
progress_callback.on_file_complete(file_path, len(file_diagnostics))
|
|
459
|
+
|
|
460
|
+
return {file_path: file_diagnostics}
|
|
461
|
+
|
|
462
|
+
async def _check_file_via_lsp(self, file_path: str) -> list[dict[str, t.Any]]:
|
|
463
|
+
"""Check a single file via LSP protocol."""
|
|
464
|
+
if not self._jsonrpc_client:
|
|
465
|
+
# Fallback to direct zuban call
|
|
466
|
+
return self._check_file_with_zuban(file_path)
|
|
467
|
+
|
|
468
|
+
try:
|
|
469
|
+
# Notify server about file
|
|
470
|
+
await self._jsonrpc_client.did_open(file_path)
|
|
471
|
+
|
|
472
|
+
# Wait briefly for diagnostics to be published
|
|
473
|
+
await asyncio.sleep(0.1)
|
|
474
|
+
|
|
475
|
+
# For now, since we don't have diagnostic collection implemented,
|
|
476
|
+
# we'll fall back to the direct zuban call but log that we used LSP
|
|
477
|
+
# In a full implementation, we'd collect diagnostics from LSP notifications
|
|
478
|
+
diagnostics = self._check_file_with_zuban(file_path)
|
|
479
|
+
|
|
480
|
+
# Clean up
|
|
481
|
+
await self._jsonrpc_client.did_close(file_path)
|
|
482
|
+
|
|
483
|
+
return diagnostics
|
|
484
|
+
|
|
485
|
+
except Exception:
|
|
486
|
+
# Fallback to direct zuban call on any LSP error
|
|
487
|
+
return self._check_file_with_zuban(file_path)
|
|
488
|
+
|
|
489
|
+
def _check_file_with_zuban(self, file_path: str) -> list[dict[str, t.Any]]:
|
|
490
|
+
"""
|
|
491
|
+
Check a single file using zuban directly.
|
|
492
|
+
|
|
493
|
+
This is a temporary implementation that calls zuban directly.
|
|
494
|
+
A full LSP integration would use JSON-RPC protocol.
|
|
495
|
+
"""
|
|
496
|
+
try:
|
|
497
|
+
result = self._execute_zuban_check(file_path)
|
|
498
|
+
|
|
499
|
+
if result.returncode == 0:
|
|
500
|
+
return [] # No errors
|
|
501
|
+
|
|
502
|
+
return self._parse_zuban_output(result.stderr)
|
|
503
|
+
|
|
504
|
+
except (
|
|
505
|
+
subprocess.TimeoutExpired,
|
|
506
|
+
subprocess.CalledProcessError,
|
|
507
|
+
FileNotFoundError,
|
|
508
|
+
):
|
|
509
|
+
return []
|
|
510
|
+
|
|
511
|
+
def _execute_zuban_check(self, file_path: str) -> subprocess.CompletedProcess[str]:
|
|
512
|
+
"""Execute zuban check command for a file."""
|
|
513
|
+
return subprocess.run(
|
|
514
|
+
["zuban", "check", file_path],
|
|
515
|
+
capture_output=True,
|
|
516
|
+
text=True,
|
|
517
|
+
timeout=30,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
def _parse_zuban_output(self, stderr_output: str) -> list[dict[str, t.Any]]:
|
|
521
|
+
"""Parse zuban stderr output into diagnostic format."""
|
|
522
|
+
diagnostics = []
|
|
523
|
+
|
|
524
|
+
for line in stderr_output.splitlines():
|
|
525
|
+
if self._is_error_line(line):
|
|
526
|
+
diagnostic = self._parse_error_line(line)
|
|
527
|
+
if diagnostic:
|
|
528
|
+
diagnostics.append(diagnostic)
|
|
529
|
+
|
|
530
|
+
return diagnostics
|
|
531
|
+
|
|
532
|
+
def _is_error_line(self, line: str) -> bool:
|
|
533
|
+
"""Check if a line contains an error message."""
|
|
534
|
+
return ":" in line and "error:" in line.lower()
|
|
535
|
+
|
|
536
|
+
def _parse_error_line(self, line: str) -> dict[str, t.Any] | None:
|
|
537
|
+
"""Parse a single error line into diagnostic format."""
|
|
538
|
+
# Format: file:line:column: error: message
|
|
539
|
+
parts = line.split(":", 4)
|
|
540
|
+
if len(parts) < 4:
|
|
541
|
+
return None
|
|
542
|
+
|
|
543
|
+
try:
|
|
544
|
+
line_num = int(parts[1])
|
|
545
|
+
col_num = int(parts[2]) if parts[2].strip() else 1
|
|
546
|
+
message = parts[4].strip() if len(parts) > 4 else parts[3].strip()
|
|
547
|
+
|
|
548
|
+
return {
|
|
549
|
+
"line": line_num,
|
|
550
|
+
"column": col_num,
|
|
551
|
+
"severity": "error",
|
|
552
|
+
"message": message,
|
|
553
|
+
"code": "type-error",
|
|
554
|
+
}
|
|
555
|
+
except (ValueError, IndexError):
|
|
556
|
+
return None
|
|
557
|
+
|
|
558
|
+
def format_diagnostics(self, diagnostics: dict[str, list[dict[str, t.Any]]]) -> str:
|
|
559
|
+
"""Format diagnostics for display."""
|
|
560
|
+
if not diagnostics or all(not diags for diags in diagnostics.values()):
|
|
561
|
+
return "✅ No type errors found"
|
|
562
|
+
|
|
563
|
+
lines = []
|
|
564
|
+
total_errors = sum(len(diags) for diags in diagnostics.values())
|
|
565
|
+
lines.append(f"❌ Found {total_errors} type error(s):")
|
|
566
|
+
|
|
567
|
+
for file_path, file_diagnostics in diagnostics.items():
|
|
568
|
+
if file_diagnostics:
|
|
569
|
+
lines.append(f"\n📄 {file_path}:")
|
|
570
|
+
for diag in file_diagnostics:
|
|
571
|
+
severity_icon = "🔴" if diag["severity"] == "error" else "🟡"
|
|
572
|
+
lines.append(
|
|
573
|
+
f" {severity_icon} Line {diag['line']}:{diag['column']} - {diag['message']}"
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
return "\n".join(lines)
|
|
577
|
+
|
|
578
|
+
def get_project_files(self, project_path: Path) -> list[str]:
|
|
579
|
+
"""Get Python files in the project that should be type-checked."""
|
|
580
|
+
python_files = []
|
|
581
|
+
|
|
582
|
+
# Focus on crackerjack source files (matching pre-commit config)
|
|
583
|
+
crackerjack_dir = project_path / "crackerjack"
|
|
584
|
+
if crackerjack_dir.exists():
|
|
585
|
+
for py_file in crackerjack_dir.rglob("*.py"):
|
|
586
|
+
# Exclude patterns from pre-commit config
|
|
587
|
+
rel_path = py_file.relative_to(project_path)
|
|
588
|
+
rel_str = str(rel_path)
|
|
589
|
+
|
|
590
|
+
# Skip excluded directories
|
|
591
|
+
if "/mcp/" in rel_str or "/plugins/" in rel_str:
|
|
592
|
+
continue
|
|
593
|
+
if "code_cleaner.py" in rel_str:
|
|
594
|
+
continue
|
|
595
|
+
|
|
596
|
+
python_files.append(str(py_file))
|
|
597
|
+
|
|
598
|
+
return python_files
|
|
599
|
+
|
|
600
|
+
def check_project_with_feedback(
|
|
601
|
+
self, project_path: Path, show_progress: bool = True
|
|
602
|
+
) -> tuple[dict[str, list[dict[str, t.Any]]], str]:
|
|
603
|
+
"""
|
|
604
|
+
Check an entire project with real-time feedback.
|
|
605
|
+
|
|
606
|
+
Returns:
|
|
607
|
+
Tuple of (diagnostics dict[str, t.Any], summary message)
|
|
608
|
+
"""
|
|
609
|
+
python_files = self.get_project_files(project_path)
|
|
610
|
+
if not python_files:
|
|
611
|
+
return {}, "📁 No Python files found to check"
|
|
612
|
+
|
|
613
|
+
feedback = RealTimeTypingFeedback()
|
|
614
|
+
|
|
615
|
+
self.console.print(
|
|
616
|
+
f"🔍 Starting type check of {len(python_files)} files...", style="bold blue"
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
diagnostics = self.check_files(
|
|
620
|
+
python_files,
|
|
621
|
+
progress_callback=feedback if show_progress else None,
|
|
622
|
+
show_progress=show_progress,
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
summary = feedback.get_summary()
|
|
626
|
+
self.console.print(f"\n{summary}", style="bold")
|
|
627
|
+
|
|
628
|
+
return diagnostics, summary
|