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,53 @@
|
|
|
1
|
+
> Crackerjack Docs: [Main](<../../../README.md>) | [Adapters](<../README.md>) | [Complexity](<./README.md>)
|
|
2
|
+
|
|
3
|
+
# Complexity Adapter
|
|
4
|
+
|
|
5
|
+
Code complexity analysis with thresholds and structured results. Uses a standardized interface built on the QA adapter base.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
- Reports cyclomatic and cognitive complexity, maintainability index, and LOC
|
|
10
|
+
- Threshold-driven warnings/errors for actionable feedback
|
|
11
|
+
- JSON parsing for precise per-function metrics
|
|
12
|
+
|
|
13
|
+
## Built-in Implementation
|
|
14
|
+
|
|
15
|
+
| Module | Description | Status |
|
|
16
|
+
| ------ | ----------- | ------ |
|
|
17
|
+
| `complexipy.py` | Complexity analysis with configurable thresholds and metrics | Stable |
|
|
18
|
+
|
|
19
|
+
## Settings
|
|
20
|
+
|
|
21
|
+
Settings class: `ComplexipySettings`
|
|
22
|
+
|
|
23
|
+
- `max_complexity` (int; default 15)
|
|
24
|
+
- `include_cognitive` (bool; default True)
|
|
25
|
+
- `include_maintainability` (bool; default True)
|
|
26
|
+
- `sort_by` (str; `complexity`/`cognitive`/`name`)
|
|
27
|
+
|
|
28
|
+
## Basic Usage
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
from crackerjack.adapters.complexity.complexipy import (
|
|
33
|
+
ComplexipyAdapter,
|
|
34
|
+
ComplexipySettings,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def analyze_complexity() -> None:
|
|
39
|
+
adapter = ComplexipyAdapter(settings=ComplexipySettings(max_complexity=15))
|
|
40
|
+
await adapter.init()
|
|
41
|
+
result = await adapter.check(files=[Path("src/")])
|
|
42
|
+
print(result.status, result.issues_found)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Tips
|
|
46
|
+
|
|
47
|
+
- Start with `max_complexity=15` (Crackerjack standard), tighten as code matures
|
|
48
|
+
- Use results to prioritize refactors (high complexity or low maintainability)
|
|
49
|
+
|
|
50
|
+
## Related
|
|
51
|
+
|
|
52
|
+
- [Refactor](<../refactor/README.md>) — Tools to modernize and reduce complexity
|
|
53
|
+
- [Format](<../format/README.md>) — Formatting often improves readability and maintainability
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Complexity adapters for code complexity analysis.
|
|
2
|
+
|
|
3
|
+
Adapters:
|
|
4
|
+
- complexipy: Multi-metric complexity analyzer (cyclomatic, cognitive, maintainability)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# ACB will auto-discover these adapters via depends.set() in module files
|
|
8
|
+
# No explicit imports needed here
|
|
9
|
+
|
|
10
|
+
__all__ = []
|
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
"""Complexipy adapter for ACB QA framework - code complexity analysis.
|
|
2
|
+
|
|
3
|
+
Complexipy analyzes Python code complexity using multiple metrics:
|
|
4
|
+
- Cyclomatic complexity (McCabe)
|
|
5
|
+
- Cognitive complexity
|
|
6
|
+
- Maintainability index
|
|
7
|
+
- Lines of code metrics
|
|
8
|
+
|
|
9
|
+
ACB Patterns:
|
|
10
|
+
- MODULE_ID and MODULE_STATUS at module level
|
|
11
|
+
- depends.set() registration after class definition
|
|
12
|
+
- Extends BaseToolAdapter for tool execution
|
|
13
|
+
- Async execution with JSON output parsing
|
|
14
|
+
- Centralized output file management via AdapterOutputPaths
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import logging
|
|
21
|
+
import shutil
|
|
22
|
+
import typing as t
|
|
23
|
+
from contextlib import suppress
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from uuid import UUID
|
|
26
|
+
|
|
27
|
+
from acb.depends import depends
|
|
28
|
+
|
|
29
|
+
from crackerjack.adapters._output_paths import AdapterOutputPaths
|
|
30
|
+
from crackerjack.adapters._tool_adapter_base import (
|
|
31
|
+
BaseToolAdapter,
|
|
32
|
+
ToolAdapterSettings,
|
|
33
|
+
ToolExecutionResult,
|
|
34
|
+
ToolIssue,
|
|
35
|
+
)
|
|
36
|
+
from crackerjack.models.qa_results import QACheckType
|
|
37
|
+
|
|
38
|
+
if t.TYPE_CHECKING:
|
|
39
|
+
from crackerjack.models.qa_config import QACheckConfig
|
|
40
|
+
|
|
41
|
+
# ACB Module Registration (REQUIRED)
|
|
42
|
+
MODULE_ID = UUID(
|
|
43
|
+
"01937d86-9e5f-a6b7-c8d9-e0f1a2b3c4d5"
|
|
44
|
+
) # Static UUID7 for reproducible module identity
|
|
45
|
+
MODULE_STATUS = "stable"
|
|
46
|
+
|
|
47
|
+
# Module-level logger for structured logging
|
|
48
|
+
logger = logging.getLogger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ComplexipySettings(ToolAdapterSettings):
|
|
52
|
+
"""Settings for Complexipy adapter."""
|
|
53
|
+
|
|
54
|
+
tool_name: str = "complexipy"
|
|
55
|
+
use_json_output: bool = True
|
|
56
|
+
max_complexity: int = 15 # crackerjack standard
|
|
57
|
+
include_cognitive: bool = True
|
|
58
|
+
include_maintainability: bool = True
|
|
59
|
+
sort_by: str = (
|
|
60
|
+
"desc" # Valid options: asc, desc, name (sorts by complexity descending)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ComplexipyAdapter(BaseToolAdapter):
|
|
65
|
+
"""Adapter for Complexipy - code complexity analyzer.
|
|
66
|
+
|
|
67
|
+
Analyzes code complexity using multiple metrics:
|
|
68
|
+
- Cyclomatic complexity (control flow branches)
|
|
69
|
+
- Cognitive complexity (how hard code is to understand)
|
|
70
|
+
- Maintainability index (overall code quality score)
|
|
71
|
+
- Lines of code (LOC, SLOC)
|
|
72
|
+
|
|
73
|
+
Features:
|
|
74
|
+
- JSON output for structured analysis
|
|
75
|
+
- Configurable complexity thresholds
|
|
76
|
+
- Multiple complexity metrics
|
|
77
|
+
- Sortable results
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
```python
|
|
81
|
+
settings = ComplexipySettings(
|
|
82
|
+
max_complexity=15,
|
|
83
|
+
include_cognitive=True,
|
|
84
|
+
include_maintainability=True,
|
|
85
|
+
)
|
|
86
|
+
adapter = ComplexipyAdapter(settings=settings)
|
|
87
|
+
await adapter.init()
|
|
88
|
+
result = await adapter.check(files=[Path("src/")])
|
|
89
|
+
```
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
settings: ComplexipySettings | None = None
|
|
93
|
+
|
|
94
|
+
def __init__(self, settings: ComplexipySettings | None = None) -> None:
|
|
95
|
+
"""Initialize Complexipy adapter.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
settings: Optional settings override
|
|
99
|
+
"""
|
|
100
|
+
super().__init__(settings=settings)
|
|
101
|
+
logger.debug(
|
|
102
|
+
"ComplexipyAdapter initialized",
|
|
103
|
+
extra={"has_settings": settings is not None},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
async def init(self) -> None:
|
|
107
|
+
"""Initialize adapter with default settings."""
|
|
108
|
+
if not self.settings:
|
|
109
|
+
# Load max_complexity from pyproject.toml and use it to initialize settings
|
|
110
|
+
config_data = self._load_config_from_pyproject()
|
|
111
|
+
max_complexity = config_data.get("max_complexity", 15)
|
|
112
|
+
self.settings = ComplexipySettings(max_complexity=max_complexity)
|
|
113
|
+
logger.info(
|
|
114
|
+
"Using default ComplexipySettings",
|
|
115
|
+
extra={"max_complexity": max_complexity},
|
|
116
|
+
)
|
|
117
|
+
await super().init()
|
|
118
|
+
logger.debug(
|
|
119
|
+
"ComplexipyAdapter initialization complete",
|
|
120
|
+
extra={
|
|
121
|
+
"max_complexity": self.settings.max_complexity,
|
|
122
|
+
"include_cognitive": self.settings.include_cognitive,
|
|
123
|
+
"include_maintainability": self.settings.include_maintainability,
|
|
124
|
+
"sort_by": self.settings.sort_by,
|
|
125
|
+
},
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def adapter_name(self) -> str:
|
|
130
|
+
"""Human-readable adapter name."""
|
|
131
|
+
return "Complexipy (Complexity)"
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def module_id(self) -> UUID:
|
|
135
|
+
"""Reference to module-level MODULE_ID."""
|
|
136
|
+
return MODULE_ID
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def tool_name(self) -> str:
|
|
140
|
+
"""CLI tool name."""
|
|
141
|
+
return "complexipy"
|
|
142
|
+
|
|
143
|
+
def build_command(
|
|
144
|
+
self,
|
|
145
|
+
files: list[Path],
|
|
146
|
+
config: QACheckConfig | None = None,
|
|
147
|
+
) -> list[str]:
|
|
148
|
+
"""Build Complexipy command.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
files: Files/directories to analyze
|
|
152
|
+
config: Optional configuration override
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Command as list of strings
|
|
156
|
+
"""
|
|
157
|
+
if not self.settings:
|
|
158
|
+
raise RuntimeError("Settings not initialized")
|
|
159
|
+
|
|
160
|
+
cmd = [self.tool_name]
|
|
161
|
+
|
|
162
|
+
# JSON output (complexipy creates timestamped files automatically)
|
|
163
|
+
# We'll move the file to centralized location after execution
|
|
164
|
+
if self.settings.use_json_output:
|
|
165
|
+
cmd.append("--output-json")
|
|
166
|
+
|
|
167
|
+
# Max complexity threshold (correct flag: --max-complexity-allowed, not --max-complexity)
|
|
168
|
+
# Load max_complexity from pyproject.toml configuration instead of settings
|
|
169
|
+
config_data = self._load_config_from_pyproject()
|
|
170
|
+
max_complexity = config_data.get("max_complexity", self.settings.max_complexity)
|
|
171
|
+
cmd.extend(["--max-complexity-allowed", str(max_complexity)])
|
|
172
|
+
|
|
173
|
+
# NOTE: --cognitive and --maintainability flags don't exist in complexipy
|
|
174
|
+
# Complexity tool only reports cyclomatic complexity, not cognitive/maintainability
|
|
175
|
+
# These settings are kept in ComplexipySettings for backwards compatibility but ignored
|
|
176
|
+
|
|
177
|
+
# Sort results
|
|
178
|
+
cmd.extend(["--sort", self.settings.sort_by])
|
|
179
|
+
|
|
180
|
+
# Add targets - files are already filtered by _get_target_files based on config
|
|
181
|
+
cmd.extend([str(f) for f in files])
|
|
182
|
+
|
|
183
|
+
logger.info(
|
|
184
|
+
"Built Complexipy command",
|
|
185
|
+
extra={
|
|
186
|
+
"file_count": len(files),
|
|
187
|
+
"max_complexity": max_complexity,
|
|
188
|
+
"include_cognitive": self.settings.include_cognitive,
|
|
189
|
+
"include_maintainability": self.settings.include_maintainability,
|
|
190
|
+
},
|
|
191
|
+
)
|
|
192
|
+
return cmd
|
|
193
|
+
|
|
194
|
+
async def parse_output(
|
|
195
|
+
self,
|
|
196
|
+
result: ToolExecutionResult,
|
|
197
|
+
) -> list[ToolIssue]:
|
|
198
|
+
"""Parse Complexipy JSON output into standardized issues.
|
|
199
|
+
|
|
200
|
+
Complexipy with --output-json saves JSON to timestamped files in the current
|
|
201
|
+
directory. We move these files to .crackerjack/outputs/complexipy/ for
|
|
202
|
+
centralized management.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
result: Raw execution result from Complexipy
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
List of parsed issues
|
|
209
|
+
"""
|
|
210
|
+
# Move generated complexipy files to centralized location
|
|
211
|
+
json_file = self._move_complexipy_results_to_output_dir()
|
|
212
|
+
|
|
213
|
+
if self.settings.use_json_output and json_file and json_file.exists():
|
|
214
|
+
try:
|
|
215
|
+
with json_file.open() as f:
|
|
216
|
+
data = json.load(f)
|
|
217
|
+
logger.debug(
|
|
218
|
+
"Read Complexipy JSON file",
|
|
219
|
+
extra={
|
|
220
|
+
"file": str(json_file),
|
|
221
|
+
"entries": len(data) if isinstance(data, list) else "N/A",
|
|
222
|
+
},
|
|
223
|
+
)
|
|
224
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
225
|
+
logger.warning(
|
|
226
|
+
"Failed to read JSON file, falling back to stdout parsing",
|
|
227
|
+
extra={"error": str(e), "file": str(json_file)},
|
|
228
|
+
)
|
|
229
|
+
return self._parse_text_output(result.raw_output)
|
|
230
|
+
else:
|
|
231
|
+
# Fall back to parsing stdout (legacy mode or if JSON file not found)
|
|
232
|
+
if not result.raw_output:
|
|
233
|
+
logger.debug("No output to parse")
|
|
234
|
+
return []
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
data = json.loads(result.raw_output)
|
|
238
|
+
logger.debug(
|
|
239
|
+
"Parsed Complexipy JSON from stdout",
|
|
240
|
+
extra={"entries": len(data) if isinstance(data, list) else "N/A"},
|
|
241
|
+
)
|
|
242
|
+
except json.JSONDecodeError as e:
|
|
243
|
+
logger.debug(
|
|
244
|
+
"JSON parse failed, falling back to text parsing",
|
|
245
|
+
extra={"error": str(e), "output_preview": result.raw_output[:200]},
|
|
246
|
+
)
|
|
247
|
+
return self._parse_text_output(result.raw_output)
|
|
248
|
+
|
|
249
|
+
issues = self._process_complexipy_data(data)
|
|
250
|
+
|
|
251
|
+
logger.info(
|
|
252
|
+
"Parsed Complexipy output",
|
|
253
|
+
extra={
|
|
254
|
+
"total_issues": len(issues),
|
|
255
|
+
"high_complexity": sum(1 for i in issues if i.severity == "error"),
|
|
256
|
+
"files_affected": len({str(i.file_path) for i in issues}),
|
|
257
|
+
},
|
|
258
|
+
)
|
|
259
|
+
return issues
|
|
260
|
+
|
|
261
|
+
def _process_complexipy_data(self, data: list | dict) -> list[ToolIssue]:
|
|
262
|
+
"""Process the complexipy JSON data to extract issues.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
data: Parsed JSON data from complexipy (flat list or legacy nested dict)
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
List of ToolIssue objects
|
|
269
|
+
"""
|
|
270
|
+
issues = []
|
|
271
|
+
|
|
272
|
+
# Handle flat list structure (current complexipy format)
|
|
273
|
+
if isinstance(data, list):
|
|
274
|
+
for func in data:
|
|
275
|
+
complexity = func.get("complexity", 0)
|
|
276
|
+
if complexity <= self.settings.max_complexity:
|
|
277
|
+
continue
|
|
278
|
+
|
|
279
|
+
file_path = Path(func.get("path", ""))
|
|
280
|
+
function_name = func.get("function_name", "unknown")
|
|
281
|
+
severity = (
|
|
282
|
+
"error"
|
|
283
|
+
if complexity > self.settings.max_complexity * 2
|
|
284
|
+
else "warning"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
issue = ToolIssue(
|
|
288
|
+
file_path=file_path,
|
|
289
|
+
line_number=None, # complexipy JSON doesn't include line numbers
|
|
290
|
+
message=f"Function '{function_name}' - Complexity: {complexity}",
|
|
291
|
+
code="COMPLEXITY",
|
|
292
|
+
severity=severity,
|
|
293
|
+
suggestion=f"Consider refactoring to reduce complexity below {self.settings.max_complexity}",
|
|
294
|
+
)
|
|
295
|
+
issues.append(issue)
|
|
296
|
+
return issues
|
|
297
|
+
|
|
298
|
+
# Handle legacy nested structure (backwards compatibility)
|
|
299
|
+
for file_data in data.get("files", []):
|
|
300
|
+
file_path = Path(file_data.get("path", ""))
|
|
301
|
+
issues.extend(
|
|
302
|
+
self._process_file_data(file_path, file_data.get("functions", []))
|
|
303
|
+
)
|
|
304
|
+
return issues
|
|
305
|
+
|
|
306
|
+
def _process_file_data(
|
|
307
|
+
self, file_path: Path, functions: list[dict]
|
|
308
|
+
) -> list[ToolIssue]:
|
|
309
|
+
"""Process function data for a specific file.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
file_path: Path of the file being analyzed
|
|
313
|
+
functions: List of function data from complexipy
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
List of ToolIssue objects for this file
|
|
317
|
+
"""
|
|
318
|
+
issues = []
|
|
319
|
+
for func in functions:
|
|
320
|
+
issue = self._create_issue_if_needed(file_path, func)
|
|
321
|
+
if issue:
|
|
322
|
+
issues.append(issue)
|
|
323
|
+
return issues
|
|
324
|
+
|
|
325
|
+
def _create_issue_if_needed(self, file_path: Path, func: dict) -> ToolIssue | None:
|
|
326
|
+
"""Create an issue if complexity exceeds threshold.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
file_path: Path of the file containing the function
|
|
330
|
+
func: Function data from complexipy
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
ToolIssue if needed, otherwise None
|
|
334
|
+
"""
|
|
335
|
+
complexity = func.get("complexity", 0)
|
|
336
|
+
|
|
337
|
+
# Only report if exceeds threshold
|
|
338
|
+
if complexity <= self.settings.max_complexity:
|
|
339
|
+
return None
|
|
340
|
+
|
|
341
|
+
message = self._build_issue_message(func, complexity)
|
|
342
|
+
severity = self._determine_issue_severity(complexity)
|
|
343
|
+
|
|
344
|
+
return ToolIssue(
|
|
345
|
+
file_path=file_path,
|
|
346
|
+
line_number=func.get("line"),
|
|
347
|
+
message=message,
|
|
348
|
+
code="COMPLEXITY",
|
|
349
|
+
severity=severity,
|
|
350
|
+
suggestion=f"Consider refactoring to reduce complexity below {self.settings.max_complexity}",
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
def _build_issue_message(self, func: dict, complexity: int) -> str:
|
|
354
|
+
"""Build the message for a complexity issue.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
func: Function data from complexipy
|
|
358
|
+
complexity: Complexity value
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
Formatted message string
|
|
362
|
+
"""
|
|
363
|
+
message_parts = [f"Complexity: {complexity}"]
|
|
364
|
+
|
|
365
|
+
if self.settings.include_cognitive:
|
|
366
|
+
cognitive = func.get("cognitive_complexity", 0)
|
|
367
|
+
message_parts.append(f"Cognitive: {cognitive}")
|
|
368
|
+
|
|
369
|
+
if self.settings.include_maintainability:
|
|
370
|
+
maintainability = func.get("maintainability", 100)
|
|
371
|
+
message_parts.append(f"Maintainability: {maintainability:.1f}")
|
|
372
|
+
|
|
373
|
+
return f"Function '{func.get('name', 'unknown')}' - " + ", ".join(message_parts)
|
|
374
|
+
|
|
375
|
+
def _determine_issue_severity(self, complexity: int) -> str:
|
|
376
|
+
"""Determine the severity of the issue based on complexity.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
complexity: Complexity value
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
"error" or "warning" based on threshold
|
|
383
|
+
"""
|
|
384
|
+
if complexity > self.settings.max_complexity * 2:
|
|
385
|
+
return "error"
|
|
386
|
+
return "warning"
|
|
387
|
+
|
|
388
|
+
def _parse_text_output(self, output: str) -> list[ToolIssue]:
|
|
389
|
+
"""Parse Complexipy text output (fallback).
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
output: Text output from Complexipy
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
List of ToolIssue objects
|
|
396
|
+
"""
|
|
397
|
+
issues = []
|
|
398
|
+
lines = output.strip().split("\n")
|
|
399
|
+
current_file: Path | None = None
|
|
400
|
+
|
|
401
|
+
for line in lines:
|
|
402
|
+
current_file = self._update_current_file(line, current_file)
|
|
403
|
+
if "complexity" in line.lower() and current_file:
|
|
404
|
+
issue = self._parse_complexity_line(line, current_file)
|
|
405
|
+
if issue:
|
|
406
|
+
issues.append(issue)
|
|
407
|
+
|
|
408
|
+
logger.info(
|
|
409
|
+
"Parsed Complexipy text output (fallback)",
|
|
410
|
+
extra={
|
|
411
|
+
"total_issues": len(issues),
|
|
412
|
+
"files_with_issues": len({str(i.file_path) for i in issues}),
|
|
413
|
+
},
|
|
414
|
+
)
|
|
415
|
+
return issues
|
|
416
|
+
|
|
417
|
+
def _update_current_file(self, line: str, current_file: Path | None) -> Path | None:
|
|
418
|
+
"""Update current file based on line content.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
line: Current line from output
|
|
422
|
+
current_file: Current file path
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
Updated file path
|
|
426
|
+
"""
|
|
427
|
+
if line.strip().startswith("File:"):
|
|
428
|
+
file_str = line.strip().replace("File:", "").strip()
|
|
429
|
+
return Path(file_str)
|
|
430
|
+
return current_file
|
|
431
|
+
|
|
432
|
+
def _parse_complexity_line(self, line: str, current_file: Path) -> ToolIssue | None:
|
|
433
|
+
"""Parse a line containing complexity information.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
line: Line from text output
|
|
437
|
+
current_file: Current file path
|
|
438
|
+
|
|
439
|
+
Returns:
|
|
440
|
+
ToolIssue if valid complexity data found, otherwise None
|
|
441
|
+
"""
|
|
442
|
+
with suppress(ValueError, IndexError):
|
|
443
|
+
func_data = self._extract_function_data(line)
|
|
444
|
+
if func_data:
|
|
445
|
+
func_name, line_number, complexity = func_data
|
|
446
|
+
if complexity > self.settings.max_complexity:
|
|
447
|
+
severity = (
|
|
448
|
+
"error"
|
|
449
|
+
if complexity > self.settings.max_complexity * 2
|
|
450
|
+
else "warning"
|
|
451
|
+
)
|
|
452
|
+
return ToolIssue(
|
|
453
|
+
file_path=current_file,
|
|
454
|
+
line_number=line_number,
|
|
455
|
+
message=f"Function '{func_name}' has complexity {complexity}",
|
|
456
|
+
code="COMPLEXITY",
|
|
457
|
+
severity=severity,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
return None
|
|
461
|
+
|
|
462
|
+
def _extract_function_data(self, line: str) -> tuple[str, int, int] | None:
|
|
463
|
+
"""Extract function name, line number, and complexity from a line.
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
line: Line from text output
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
Tuple of (function name, line number, complexity) or None
|
|
470
|
+
"""
|
|
471
|
+
line = line.strip()
|
|
472
|
+
if "(" in line and ")" in line and "complexity" in line.lower():
|
|
473
|
+
func_name = line.split("(")[0].strip()
|
|
474
|
+
line_part = line.split("(")[1].split(")")[0]
|
|
475
|
+
line_number = int(line_part.replace("line", "").strip())
|
|
476
|
+
complexity_part = line.split("complexity")[1].strip()
|
|
477
|
+
complexity = int(complexity_part.split()[0])
|
|
478
|
+
return func_name, line_number, complexity
|
|
479
|
+
|
|
480
|
+
return None
|
|
481
|
+
|
|
482
|
+
def _get_check_type(self) -> QACheckType:
|
|
483
|
+
"""Return complexity check type."""
|
|
484
|
+
return QACheckType.COMPLEXITY
|
|
485
|
+
|
|
486
|
+
def get_default_config(self) -> QACheckConfig:
|
|
487
|
+
"""Get default configuration for Complexipy adapter.
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
QACheckConfig with sensible defaults
|
|
491
|
+
"""
|
|
492
|
+
from crackerjack.models.qa_config import QACheckConfig
|
|
493
|
+
|
|
494
|
+
# Load configuration from pyproject.toml to get actual exclude patterns and max_complexity
|
|
495
|
+
config_data = self._load_config_from_pyproject()
|
|
496
|
+
exclude_patterns = config_data.get(
|
|
497
|
+
"exclude_patterns", ["**/.venv/**", "**/venv/**", "**/tests/**"]
|
|
498
|
+
)
|
|
499
|
+
max_complexity = config_data.get("max_complexity", 15)
|
|
500
|
+
|
|
501
|
+
return QACheckConfig(
|
|
502
|
+
check_id=MODULE_ID,
|
|
503
|
+
check_name=self.adapter_name,
|
|
504
|
+
check_type=QACheckType.COMPLEXITY,
|
|
505
|
+
enabled=True,
|
|
506
|
+
file_patterns=["**/*.py"],
|
|
507
|
+
exclude_patterns=exclude_patterns,
|
|
508
|
+
timeout_seconds=90,
|
|
509
|
+
parallel_safe=True,
|
|
510
|
+
stage="comprehensive", # Complexity analysis in comprehensive stage
|
|
511
|
+
settings={
|
|
512
|
+
"max_complexity": max_complexity,
|
|
513
|
+
"include_cognitive": True,
|
|
514
|
+
"include_maintainability": True,
|
|
515
|
+
"sort_by": "complexity",
|
|
516
|
+
},
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
def _load_config_from_pyproject(self) -> dict:
|
|
520
|
+
"""Load complexipy configuration from pyproject.toml.
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
Dictionary with complexipy configuration or defaults
|
|
524
|
+
"""
|
|
525
|
+
import tomllib
|
|
526
|
+
from pathlib import Path
|
|
527
|
+
|
|
528
|
+
pyproject_path = Path.cwd() / "pyproject.toml"
|
|
529
|
+
config = {
|
|
530
|
+
"exclude_patterns": ["**/.venv/**", "**/venv/**", "**/tests/**"],
|
|
531
|
+
"max_complexity": 15,
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if pyproject_path.exists():
|
|
535
|
+
try:
|
|
536
|
+
with pyproject_path.open("rb") as f:
|
|
537
|
+
toml_config = tomllib.load(f)
|
|
538
|
+
complexipy_config = toml_config.get("tool", {}).get("complexipy", {})
|
|
539
|
+
|
|
540
|
+
# Load exclude patterns if specified
|
|
541
|
+
exclude_patterns = complexipy_config.get("exclude_patterns")
|
|
542
|
+
if exclude_patterns:
|
|
543
|
+
config["exclude_patterns"] = exclude_patterns
|
|
544
|
+
logger.info(
|
|
545
|
+
"Loaded exclude patterns from pyproject.toml",
|
|
546
|
+
extra={"exclude_patterns": exclude_patterns},
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
# Load max complexity if specified
|
|
550
|
+
max_complexity = complexipy_config.get("max_complexity")
|
|
551
|
+
if max_complexity is not None:
|
|
552
|
+
config["max_complexity"] = max_complexity
|
|
553
|
+
logger.info(
|
|
554
|
+
"Loaded max_complexity from pyproject.toml",
|
|
555
|
+
extra={"max_complexity": max_complexity},
|
|
556
|
+
)
|
|
557
|
+
except (tomllib.TOMLDecodeError, OSError) as e:
|
|
558
|
+
logger.warning(
|
|
559
|
+
"Failed to load complexipy config from pyproject.toml, using defaults",
|
|
560
|
+
extra={"error": str(e)},
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
return config
|
|
564
|
+
|
|
565
|
+
def _load_exclude_patterns_from_config(self) -> list[str]:
|
|
566
|
+
"""Load exclude patterns from pyproject.toml configuration.
|
|
567
|
+
|
|
568
|
+
Returns:
|
|
569
|
+
List of exclude patterns from pyproject.toml or defaults
|
|
570
|
+
"""
|
|
571
|
+
config = self._load_config_from_pyproject()
|
|
572
|
+
return config.get(
|
|
573
|
+
"exclude_patterns", ["**/.venv/**", "**/venv/**", "**/tests/**"]
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
def _move_complexipy_results_to_output_dir(self) -> Path | None:
|
|
577
|
+
"""Move complexipy-generated result files to centralized output directory.
|
|
578
|
+
|
|
579
|
+
Complexipy creates timestamped files like:
|
|
580
|
+
complexipy_results_2025_12_09__07:08:33.json
|
|
581
|
+
|
|
582
|
+
This method:
|
|
583
|
+
1. Finds the most recent complexipy_results_*.json file in project root
|
|
584
|
+
2. Moves it to .crackerjack/outputs/complexipy/
|
|
585
|
+
3. Returns the new path
|
|
586
|
+
|
|
587
|
+
Returns:
|
|
588
|
+
Path to moved file in centralized location, or None if no file found
|
|
589
|
+
"""
|
|
590
|
+
# Find all complexipy result files in project root
|
|
591
|
+
project_root = Path.cwd()
|
|
592
|
+
result_files = sorted(
|
|
593
|
+
project_root.glob("complexipy_results_*.json"),
|
|
594
|
+
key=lambda p: p.stat().st_mtime,
|
|
595
|
+
reverse=True,
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
if not result_files:
|
|
599
|
+
logger.debug("No complexipy result files found in project root")
|
|
600
|
+
return None
|
|
601
|
+
|
|
602
|
+
# Get the most recent file
|
|
603
|
+
source_file = result_files[0]
|
|
604
|
+
|
|
605
|
+
# Determine destination in centralized output directory
|
|
606
|
+
output_dir = AdapterOutputPaths.get_output_dir("complexipy")
|
|
607
|
+
dest_file = output_dir / source_file.name
|
|
608
|
+
|
|
609
|
+
try:
|
|
610
|
+
# Move the file to centralized location
|
|
611
|
+
shutil.move(str(source_file), str(dest_file))
|
|
612
|
+
logger.info(
|
|
613
|
+
"Moved complexipy results to centralized location",
|
|
614
|
+
extra={
|
|
615
|
+
"source": str(source_file),
|
|
616
|
+
"destination": str(dest_file),
|
|
617
|
+
},
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
# Clean up old result files (keep only latest 5)
|
|
621
|
+
AdapterOutputPaths.cleanup_old_outputs(
|
|
622
|
+
"complexipy", "complexipy_results_*.json", keep_latest=5
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
return dest_file
|
|
626
|
+
except (OSError, shutil.Error) as e:
|
|
627
|
+
logger.warning(
|
|
628
|
+
"Failed to move complexipy results file",
|
|
629
|
+
extra={
|
|
630
|
+
"error": str(e),
|
|
631
|
+
"source": str(source_file),
|
|
632
|
+
"destination": str(dest_file),
|
|
633
|
+
},
|
|
634
|
+
)
|
|
635
|
+
# Return source file if move fails (fallback)
|
|
636
|
+
return source_file
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
# ACB Registration (REQUIRED at module level)
|
|
640
|
+
with suppress(Exception):
|
|
641
|
+
depends.set(ComplexipyAdapter)
|