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,198 @@
|
|
|
1
|
+
"""Generic configuration loading and validation service."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import yaml
|
|
8
|
+
from loguru import logger
|
|
9
|
+
from pydantic import BaseModel, ValidationError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ConfigService:
|
|
13
|
+
"""Generic configuration loading and validation service."""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def load_config(path: str | Path) -> dict[str, Any]:
|
|
17
|
+
"""
|
|
18
|
+
Load configuration file based on extension.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
path: Path to the configuration file
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Dictionary with configuration data
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
ValueError: If file extension is not supported
|
|
28
|
+
FileNotFoundError: If the file doesn't exist
|
|
29
|
+
Exception: For other file loading errors
|
|
30
|
+
"""
|
|
31
|
+
path = Path(path)
|
|
32
|
+
|
|
33
|
+
if not path.exists():
|
|
34
|
+
raise FileNotFoundError(f"Configuration file does not exist: {path}")
|
|
35
|
+
|
|
36
|
+
if path.suffix.lower() == ".json":
|
|
37
|
+
return ConfigService._load_json(path)
|
|
38
|
+
elif path.suffix.lower() in (".yml", ".yaml"):
|
|
39
|
+
return ConfigService._load_yaml(path)
|
|
40
|
+
elif path.suffix.lower() == ".toml":
|
|
41
|
+
return ConfigService._load_toml(path)
|
|
42
|
+
else:
|
|
43
|
+
raise ValueError(f"Unsupported config format: {path.suffix}")
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
async def load_config_async(path: str | Path) -> dict[str, Any]:
|
|
47
|
+
"""
|
|
48
|
+
Asynchronously load configuration file based on extension.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
path: Path to the configuration file
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Dictionary with configuration data
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
ValueError: If file extension is not supported
|
|
58
|
+
FileNotFoundError: If the file doesn't exist
|
|
59
|
+
Exception: For other file loading errors
|
|
60
|
+
"""
|
|
61
|
+
from crackerjack.services.file_io_service import FileIOService
|
|
62
|
+
|
|
63
|
+
path = Path(path)
|
|
64
|
+
|
|
65
|
+
if not path.exists():
|
|
66
|
+
raise FileNotFoundError(f"Configuration file does not exist: {path}")
|
|
67
|
+
|
|
68
|
+
if path.suffix.lower() == ".json":
|
|
69
|
+
content = await FileIOService.read_text_file(path)
|
|
70
|
+
return json.loads(content)
|
|
71
|
+
elif path.suffix.lower() in (".yml", ".yaml"):
|
|
72
|
+
content = await FileIOService.read_text_file(path)
|
|
73
|
+
return yaml.safe_load(content)
|
|
74
|
+
elif path.suffix.lower() == ".toml":
|
|
75
|
+
content = await FileIOService.read_text_file(path)
|
|
76
|
+
import toml
|
|
77
|
+
|
|
78
|
+
return toml.loads(content)
|
|
79
|
+
else:
|
|
80
|
+
raise ValueError(f"Unsupported config format: {path.suffix}")
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _load_json(path: Path) -> dict[str, Any]:
|
|
84
|
+
"""Load JSON configuration."""
|
|
85
|
+
with path.open(encoding="utf-8") as f:
|
|
86
|
+
return json.load(f)
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def _load_yaml(path: Path) -> dict[str, Any]:
|
|
90
|
+
"""Load YAML configuration."""
|
|
91
|
+
with path.open(encoding="utf-8") as f:
|
|
92
|
+
return yaml.safe_load(f)
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _load_toml(path: Path) -> dict[str, Any]:
|
|
96
|
+
"""Load TOML configuration."""
|
|
97
|
+
import toml
|
|
98
|
+
|
|
99
|
+
with path.open("r", encoding="utf-8") as f:
|
|
100
|
+
return toml.load(f)
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def validate_config(
|
|
104
|
+
config: dict[str, Any], model_class: type[BaseModel]
|
|
105
|
+
) -> BaseModel:
|
|
106
|
+
"""
|
|
107
|
+
Validate configuration against a Pydantic model.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
config: Configuration dictionary to validate
|
|
111
|
+
model_class: Pydantic model class to validate against
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Validated Pydantic model instance
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
ValidationError: If the configuration doesn't match the model
|
|
118
|
+
"""
|
|
119
|
+
try:
|
|
120
|
+
return model_class.model_validate(config)
|
|
121
|
+
except ValidationError as e:
|
|
122
|
+
logger.error(f"Config validation failed: {e}")
|
|
123
|
+
raise
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def save_config(
|
|
127
|
+
config: dict[str, Any], path: str | Path, format: str | None = None
|
|
128
|
+
) -> None:
|
|
129
|
+
"""
|
|
130
|
+
Save configuration to file.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
config: Configuration dictionary to save
|
|
134
|
+
path: Path to save the configuration to
|
|
135
|
+
format: Format to save as ('json', 'yaml', 'toml'). If None, inferred from path extension.
|
|
136
|
+
"""
|
|
137
|
+
path = Path(path)
|
|
138
|
+
format = format or path.suffix.lower().lstrip(".")
|
|
139
|
+
|
|
140
|
+
# Create parent directories if they don't exist
|
|
141
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
142
|
+
|
|
143
|
+
if format == "json":
|
|
144
|
+
ConfigService._save_json(config, path)
|
|
145
|
+
elif format in ("yml", "yaml"):
|
|
146
|
+
ConfigService._save_yaml(config, path)
|
|
147
|
+
elif format == "toml":
|
|
148
|
+
ConfigService._save_toml(config, path)
|
|
149
|
+
else:
|
|
150
|
+
raise ValueError(f"Unsupported config format: {format}")
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def _save_json(config: dict[str, Any], path: Path) -> None:
|
|
154
|
+
"""Save configuration as JSON."""
|
|
155
|
+
with path.open("w", encoding="utf-8") as f:
|
|
156
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def _save_yaml(config: dict[str, Any], path: Path) -> None:
|
|
160
|
+
"""Save configuration as YAML."""
|
|
161
|
+
with path.open("w", encoding="utf-8") as f:
|
|
162
|
+
yaml.dump(config, f, default_flow_style=False, allow_unicode=True)
|
|
163
|
+
|
|
164
|
+
@staticmethod
|
|
165
|
+
def _save_toml(config: dict[str, Any], path: Path) -> None:
|
|
166
|
+
"""Save configuration as TOML."""
|
|
167
|
+
import toml
|
|
168
|
+
|
|
169
|
+
with path.open("w", encoding="utf-8") as f:
|
|
170
|
+
toml.dump(config, f)
|
|
171
|
+
|
|
172
|
+
@staticmethod
|
|
173
|
+
def merge_configs(
|
|
174
|
+
base_config: dict[str, Any], override_config: dict[str, Any]
|
|
175
|
+
) -> dict[str, Any]:
|
|
176
|
+
"""
|
|
177
|
+
Recursively merge two configuration dictionaries.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
base_config: Base configuration
|
|
181
|
+
override_config: Configuration to merge on top of base
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Merged configuration dictionary
|
|
185
|
+
"""
|
|
186
|
+
result = base_config.copy()
|
|
187
|
+
|
|
188
|
+
for key, value in override_config.items():
|
|
189
|
+
if (
|
|
190
|
+
key in result
|
|
191
|
+
and isinstance(result[key], dict)
|
|
192
|
+
and isinstance(value, dict)
|
|
193
|
+
):
|
|
194
|
+
result[key] = ConfigService.merge_configs(result[key], value)
|
|
195
|
+
else:
|
|
196
|
+
result[key] = value
|
|
197
|
+
|
|
198
|
+
return result
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import typing as t
|
|
3
|
+
from contextlib import suppress
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import tomli
|
|
9
|
+
import yaml
|
|
10
|
+
from acb.console import Console
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class ConfigUpdateInfo:
|
|
15
|
+
config_type: str
|
|
16
|
+
current_version: str
|
|
17
|
+
latest_version: str
|
|
18
|
+
needs_update: bool
|
|
19
|
+
diff_preview: str = ""
|
|
20
|
+
last_updated: datetime | None = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class ConfigVersion:
|
|
25
|
+
version: str
|
|
26
|
+
config_data: dict[str, t.Any]
|
|
27
|
+
dependencies: list[str] = field(default_factory=list)
|
|
28
|
+
description: str = ""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ConfigTemplateService:
|
|
32
|
+
"""Version-based configuration template management service."""
|
|
33
|
+
|
|
34
|
+
def __init__(self, console: Console, pkg_path: Path) -> None:
|
|
35
|
+
self.console = console
|
|
36
|
+
self.pkg_path = pkg_path
|
|
37
|
+
self.templates = self._load_config_templates()
|
|
38
|
+
|
|
39
|
+
def _load_config_templates(self) -> dict[str, ConfigVersion]:
|
|
40
|
+
"""Load configuration templates as structured data."""
|
|
41
|
+
return {
|
|
42
|
+
"pre-commit": self._create_precommit_template(),
|
|
43
|
+
"pyproject": self._create_pyproject_template(),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def _create_precommit_template(self) -> ConfigVersion:
|
|
47
|
+
"""Create pre-commit configuration template."""
|
|
48
|
+
return ConfigVersion(
|
|
49
|
+
version="3.0.0",
|
|
50
|
+
description="Pre-commit hooks configuration with modern tools",
|
|
51
|
+
config_data={"repos": self._build_precommit_repos()},
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def _create_pyproject_template(self) -> ConfigVersion:
|
|
55
|
+
"""Create pyproject.toml configuration template."""
|
|
56
|
+
return ConfigVersion(
|
|
57
|
+
version="1.2.0",
|
|
58
|
+
description="Modern Python project configuration with Ruff and pytest",
|
|
59
|
+
config_data={"tool": self._build_pyproject_tools()},
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def _build_precommit_repos(self) -> list[dict[str, t.Any]]:
|
|
63
|
+
"""Build pre-commit repository configurations."""
|
|
64
|
+
return [
|
|
65
|
+
{
|
|
66
|
+
"repo": "local",
|
|
67
|
+
"hooks": self._build_local_precommit_hooks(),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"repo": "https://github.com/pre-commit/pre-commit-hooks",
|
|
71
|
+
"rev": "v6.0.0",
|
|
72
|
+
"hooks": self._build_standard_precommit_hooks(),
|
|
73
|
+
},
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
def _build_local_precommit_hooks(self) -> list[dict[str, t.Any]]:
|
|
77
|
+
"""Build local pre-commit hook configurations."""
|
|
78
|
+
return [
|
|
79
|
+
{
|
|
80
|
+
"id": "validate-regex-patterns",
|
|
81
|
+
"name": "validate-regex-patterns",
|
|
82
|
+
"entry": "uv run python -m crackerjack.tools.validate_regex_patterns",
|
|
83
|
+
"language": "system",
|
|
84
|
+
"files": r"\.py$",
|
|
85
|
+
"exclude": r"^\.venv/",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"id": "skylos",
|
|
89
|
+
"name": "skylos-dead-code-detection",
|
|
90
|
+
"entry": "skylos",
|
|
91
|
+
"language": "system",
|
|
92
|
+
"args": ["crackerjack"],
|
|
93
|
+
"pass_filenames": False,
|
|
94
|
+
"stages": ["pre-push", "manual"],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"id": "zuban",
|
|
98
|
+
"name": "zuban-type-checking",
|
|
99
|
+
"entry": "uv run zuban check",
|
|
100
|
+
"language": "system",
|
|
101
|
+
"args": ["--config-file", "mypy.ini", "./crackerjack"],
|
|
102
|
+
"pass_filenames": False,
|
|
103
|
+
"exclude": r"^tests/|^src/",
|
|
104
|
+
"stages": ["pre-push", "manual"],
|
|
105
|
+
},
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
def _build_standard_precommit_hooks(self) -> list[dict[str, t.Any]]:
|
|
109
|
+
"""Build standard pre-commit hook configurations."""
|
|
110
|
+
exclude_pattern = r"^\.venv/"
|
|
111
|
+
return [
|
|
112
|
+
{
|
|
113
|
+
"id": "trailing-whitespace",
|
|
114
|
+
"name": "trailing-whitespace",
|
|
115
|
+
"exclude": exclude_pattern,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"id": "end-of-file-fixer",
|
|
119
|
+
"name": "end-of-file-fixer",
|
|
120
|
+
"exclude": exclude_pattern,
|
|
121
|
+
},
|
|
122
|
+
{"id": "check-yaml", "name": "check-yaml", "exclude": exclude_pattern},
|
|
123
|
+
{"id": "check-toml", "name": "check-toml", "exclude": exclude_pattern},
|
|
124
|
+
{
|
|
125
|
+
"id": "check-added-large-files",
|
|
126
|
+
"name": "check-added-large-files",
|
|
127
|
+
"exclude": exclude_pattern,
|
|
128
|
+
},
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
def _build_pyproject_tools(self) -> dict[str, t.Any]:
|
|
132
|
+
"""Build pyproject.toml tool configurations."""
|
|
133
|
+
return {
|
|
134
|
+
"ruff": self._build_ruff_config(),
|
|
135
|
+
"pytest": self._build_pytest_config(),
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
def _build_ruff_config(self) -> dict[str, t.Any]:
|
|
139
|
+
"""Build Ruff configuration."""
|
|
140
|
+
return {
|
|
141
|
+
"target-version": "py313",
|
|
142
|
+
"line-length": 88,
|
|
143
|
+
"fix": True,
|
|
144
|
+
"unsafe-fixes": True,
|
|
145
|
+
"show-fixes": True,
|
|
146
|
+
"output-format": "full",
|
|
147
|
+
"format": {"docstring-code-format": True},
|
|
148
|
+
"lint": {
|
|
149
|
+
"extend-select": ["C901", "F", "I", "UP"],
|
|
150
|
+
"ignore": ["E402", "F821"],
|
|
151
|
+
"fixable": ["ALL"],
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
def _build_pytest_config(self) -> dict[str, t.Any]:
|
|
156
|
+
"""Build pytest configuration."""
|
|
157
|
+
return {
|
|
158
|
+
"ini_options": {
|
|
159
|
+
"asyncio_mode": "auto",
|
|
160
|
+
"timeout": 300,
|
|
161
|
+
"addopts": "--cov=crackerjack --cov-report=term-missing:skip-covered",
|
|
162
|
+
"testpaths": ["tests"],
|
|
163
|
+
"markers": [
|
|
164
|
+
"unit: marks test as a unit test",
|
|
165
|
+
"integration: marks test as an integration test",
|
|
166
|
+
"no_leaks: detect asyncio task leaks",
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
def get_template(
|
|
172
|
+
self, config_type: str, version: str | None = None
|
|
173
|
+
) -> ConfigVersion | None:
|
|
174
|
+
"""Get configuration template by type and optional version."""
|
|
175
|
+
if config_type not in self.templates:
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
template = self.templates[config_type]
|
|
179
|
+
if version and template.version != version:
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
return template
|
|
183
|
+
|
|
184
|
+
def check_updates(self, project_path: Path) -> dict[str, ConfigUpdateInfo]:
|
|
185
|
+
"""Check if newer configuration versions are available."""
|
|
186
|
+
updates = {}
|
|
187
|
+
|
|
188
|
+
version_file = project_path / ".crackerjack-config.yaml"
|
|
189
|
+
current_versions = self._load_current_versions(version_file)
|
|
190
|
+
|
|
191
|
+
for config_type, template in self.templates.items():
|
|
192
|
+
current_version = current_versions.get(config_type, "0.0.0")
|
|
193
|
+
needs_update = self._version_compare(current_version, template.version) < 0
|
|
194
|
+
|
|
195
|
+
update_info = ConfigUpdateInfo(
|
|
196
|
+
config_type=config_type,
|
|
197
|
+
current_version=current_version,
|
|
198
|
+
latest_version=template.version,
|
|
199
|
+
needs_update=needs_update,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
if needs_update:
|
|
203
|
+
update_info.diff_preview = self._generate_diff_preview(
|
|
204
|
+
config_type, project_path
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
updates[config_type] = update_info
|
|
208
|
+
|
|
209
|
+
return updates
|
|
210
|
+
|
|
211
|
+
def _load_current_versions(self, version_file: Path) -> dict[str, str]:
|
|
212
|
+
"""Load current configuration versions from tracking file."""
|
|
213
|
+
if not version_file.exists():
|
|
214
|
+
return {}
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
with version_file.open() as f:
|
|
218
|
+
data = yaml.safe_load(f)
|
|
219
|
+
if not isinstance(data, dict):
|
|
220
|
+
return {}
|
|
221
|
+
configs = data.get("configs", {})
|
|
222
|
+
if not isinstance(configs, dict):
|
|
223
|
+
return {}
|
|
224
|
+
return {
|
|
225
|
+
name: config.get("version", "0.0.0")
|
|
226
|
+
for name, config in configs.items()
|
|
227
|
+
if isinstance(config, dict)
|
|
228
|
+
}
|
|
229
|
+
except Exception:
|
|
230
|
+
return {}
|
|
231
|
+
|
|
232
|
+
def _version_compare(self, version1: str, version2: str) -> int:
|
|
233
|
+
"""Compare two semantic versions. Returns -1, 0, or 1."""
|
|
234
|
+
|
|
235
|
+
def version_tuple(v: str) -> tuple[int, ...]:
|
|
236
|
+
return tuple(int(x) for x in v.split("."))
|
|
237
|
+
|
|
238
|
+
v1_tuple = version_tuple(version1)
|
|
239
|
+
v2_tuple = version_tuple(version2)
|
|
240
|
+
|
|
241
|
+
if v1_tuple < v2_tuple:
|
|
242
|
+
return -1
|
|
243
|
+
elif v1_tuple > v2_tuple:
|
|
244
|
+
return 1
|
|
245
|
+
return 0
|
|
246
|
+
|
|
247
|
+
def _generate_diff_preview(self, config_type: str, project_path: Path) -> str:
|
|
248
|
+
"""Generate a preview of changes that would be made."""
|
|
249
|
+
if config_type == "pre-commit":
|
|
250
|
+
config_file = project_path / ".pre-commit-config.yaml"
|
|
251
|
+
elif config_type == "pyproject":
|
|
252
|
+
config_file = project_path / "pyproject.toml"
|
|
253
|
+
else:
|
|
254
|
+
return "Diff preview not available for this config type"
|
|
255
|
+
|
|
256
|
+
if not config_file.exists():
|
|
257
|
+
return f"Would create new {config_file.name} file"
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
with config_file.open() as f:
|
|
261
|
+
if config_type == "pre-commit":
|
|
262
|
+
current_config = yaml.safe_load(f)
|
|
263
|
+
else:
|
|
264
|
+
content = f.read()
|
|
265
|
+
current_config = tomli.loads(content)
|
|
266
|
+
|
|
267
|
+
template = self.get_template(config_type)
|
|
268
|
+
if not template:
|
|
269
|
+
return "Template not found"
|
|
270
|
+
|
|
271
|
+
return self._create_config_diff(current_config, template.config_data)
|
|
272
|
+
except Exception as e:
|
|
273
|
+
return f"Error generating diff preview: {e}"
|
|
274
|
+
|
|
275
|
+
def _create_config_diff(
|
|
276
|
+
self, current: dict[str, t.Any], new: dict[str, t.Any]
|
|
277
|
+
) -> str:
|
|
278
|
+
"""Create a simple diff between two configurations."""
|
|
279
|
+
changes: list[str] = []
|
|
280
|
+
self._collect_config_changes(current, new, changes)
|
|
281
|
+
|
|
282
|
+
if not changes:
|
|
283
|
+
return "No changes detected"
|
|
284
|
+
|
|
285
|
+
return "\n".join(changes[:10]) # Limit to first 10 changes
|
|
286
|
+
|
|
287
|
+
def _collect_config_changes(
|
|
288
|
+
self,
|
|
289
|
+
current: dict[str, t.Any],
|
|
290
|
+
new: dict[str, t.Any],
|
|
291
|
+
changes: list[str],
|
|
292
|
+
path: str = "",
|
|
293
|
+
) -> None:
|
|
294
|
+
"""Collect configuration changes recursively."""
|
|
295
|
+
self._collect_additions_and_modifications(current, new, changes, path)
|
|
296
|
+
self._collect_removals(current, new, changes, path)
|
|
297
|
+
|
|
298
|
+
def _collect_additions_and_modifications(
|
|
299
|
+
self,
|
|
300
|
+
current: dict[str, t.Any],
|
|
301
|
+
new: dict[str, t.Any],
|
|
302
|
+
changes: list[str],
|
|
303
|
+
path: str,
|
|
304
|
+
) -> None:
|
|
305
|
+
"""Collect additions and modifications in configuration."""
|
|
306
|
+
for key, value in new.items():
|
|
307
|
+
key_path = f"{path}.{key}" if path else key
|
|
308
|
+
|
|
309
|
+
if key not in current:
|
|
310
|
+
changes.append(f"+ Add {key_path}: {value}")
|
|
311
|
+
elif self._is_nested_dict(value, current[key]):
|
|
312
|
+
self._collect_config_changes(current[key], value, changes, key_path)
|
|
313
|
+
elif current[key] != value:
|
|
314
|
+
changes.append(f"~ Change {key_path}: {current[key]} → {value}")
|
|
315
|
+
|
|
316
|
+
def _collect_removals(
|
|
317
|
+
self,
|
|
318
|
+
current: dict[str, t.Any],
|
|
319
|
+
new: dict[str, t.Any],
|
|
320
|
+
changes: list[str],
|
|
321
|
+
path: str,
|
|
322
|
+
) -> None:
|
|
323
|
+
"""Collect removals in configuration."""
|
|
324
|
+
for key in current:
|
|
325
|
+
if key not in new:
|
|
326
|
+
key_path = f"{path}.{key}" if path else key
|
|
327
|
+
changes.append(f"- Remove {key_path}")
|
|
328
|
+
|
|
329
|
+
def _is_nested_dict(self, new_value: t.Any, current_value: t.Any) -> bool:
|
|
330
|
+
"""Check if both values are dictionaries for nested comparison."""
|
|
331
|
+
return isinstance(new_value, dict) and isinstance(current_value, dict)
|
|
332
|
+
|
|
333
|
+
def apply_update(
|
|
334
|
+
self,
|
|
335
|
+
config_type: str,
|
|
336
|
+
project_path: Path,
|
|
337
|
+
interactive: bool = False,
|
|
338
|
+
) -> bool:
|
|
339
|
+
"""Apply configuration update to project."""
|
|
340
|
+
template = self.get_template(config_type)
|
|
341
|
+
if not template:
|
|
342
|
+
self.console.print(f"[red]❌[/red] Template not found: {config_type}")
|
|
343
|
+
return False
|
|
344
|
+
|
|
345
|
+
try:
|
|
346
|
+
if config_type == "pre-commit":
|
|
347
|
+
return self._apply_precommit_update(template, project_path, interactive)
|
|
348
|
+
elif config_type == "pyproject":
|
|
349
|
+
return self._apply_pyproject_update(template, project_path, interactive)
|
|
350
|
+
else:
|
|
351
|
+
self.console.print(
|
|
352
|
+
f"[yellow]⚠️[/yellow] Unsupported config type: {config_type}"
|
|
353
|
+
)
|
|
354
|
+
return False
|
|
355
|
+
except Exception as e:
|
|
356
|
+
self.console.print(f"[red]❌[/red] Failed to apply update: {e}")
|
|
357
|
+
return False
|
|
358
|
+
|
|
359
|
+
def _apply_precommit_update(
|
|
360
|
+
self, template: ConfigVersion, project_path: Path, interactive: bool
|
|
361
|
+
) -> bool:
|
|
362
|
+
"""Apply pre-commit configuration update."""
|
|
363
|
+
config_file = project_path / ".pre-commit-config.yaml"
|
|
364
|
+
|
|
365
|
+
if interactive and config_file.exists():
|
|
366
|
+
self.console.print(f"\n[bold cyan]Updating {config_file.name}[/bold cyan]")
|
|
367
|
+
diff = self._generate_diff_preview("pre-commit", project_path)
|
|
368
|
+
self.console.print(f"Changes:\n{diff}")
|
|
369
|
+
|
|
370
|
+
if not self._confirm_update():
|
|
371
|
+
return False
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
with config_file.open("w") as f:
|
|
375
|
+
yaml.dump(
|
|
376
|
+
template.config_data, f, default_flow_style=False, sort_keys=False
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
self._update_version_tracking(project_path, "pre-commit", template.version)
|
|
380
|
+
self._invalidate_cache(project_path)
|
|
381
|
+
|
|
382
|
+
self.console.print(
|
|
383
|
+
f"[green]✅[/green] Updated {config_file.name} to version {template.version}"
|
|
384
|
+
)
|
|
385
|
+
return True
|
|
386
|
+
except Exception as e:
|
|
387
|
+
self.console.print(f"[red]❌[/red] Failed to write config: {e}")
|
|
388
|
+
return False
|
|
389
|
+
|
|
390
|
+
def _apply_pyproject_update(
|
|
391
|
+
self, template: ConfigVersion, project_path: Path, interactive: bool
|
|
392
|
+
) -> bool:
|
|
393
|
+
"""Apply pyproject.toml configuration update."""
|
|
394
|
+
config_file = project_path / "pyproject.toml"
|
|
395
|
+
|
|
396
|
+
if not config_file.exists():
|
|
397
|
+
self.console.print(
|
|
398
|
+
f"[yellow]⚠️[/yellow] pyproject.toml not found at {project_path}"
|
|
399
|
+
)
|
|
400
|
+
return False
|
|
401
|
+
|
|
402
|
+
if interactive:
|
|
403
|
+
self.console.print(f"\n[bold cyan]Updating {config_file.name}[/bold cyan]")
|
|
404
|
+
diff = self._generate_diff_preview("pyproject", project_path)
|
|
405
|
+
self.console.print(f"Changes:\n{diff}")
|
|
406
|
+
|
|
407
|
+
if not self._confirm_update():
|
|
408
|
+
return False
|
|
409
|
+
|
|
410
|
+
try:
|
|
411
|
+
# Read existing config
|
|
412
|
+
with config_file.open() as f:
|
|
413
|
+
content = f.read()
|
|
414
|
+
existing_config = tomli.loads(content)
|
|
415
|
+
|
|
416
|
+
# Merge tool sections from template
|
|
417
|
+
if "tool" not in existing_config:
|
|
418
|
+
existing_config["tool"] = {}
|
|
419
|
+
|
|
420
|
+
for tool_name, tool_config in template.config_data.get("tool", {}).items():
|
|
421
|
+
existing_config["tool"][tool_name] = tool_config
|
|
422
|
+
|
|
423
|
+
# Write back using tomli_w
|
|
424
|
+
from tomli_w import dumps
|
|
425
|
+
|
|
426
|
+
updated_content = dumps(existing_config)
|
|
427
|
+
|
|
428
|
+
with config_file.open("w") as f:
|
|
429
|
+
f.write(updated_content)
|
|
430
|
+
|
|
431
|
+
self._update_version_tracking(project_path, "pyproject", template.version)
|
|
432
|
+
|
|
433
|
+
self.console.print(
|
|
434
|
+
f"[green]✅[/green] Updated {config_file.name} to version {template.version}"
|
|
435
|
+
)
|
|
436
|
+
return True
|
|
437
|
+
except Exception as e:
|
|
438
|
+
self.console.print(f"[red]❌[/red] Failed to update pyproject.toml: {e}")
|
|
439
|
+
return False
|
|
440
|
+
|
|
441
|
+
def _confirm_update(self) -> bool:
|
|
442
|
+
"""Ask user to confirm update."""
|
|
443
|
+
try:
|
|
444
|
+
response = input("\nApply this update? [y/N]: ").strip().lower()
|
|
445
|
+
return response in ("y", "yes")
|
|
446
|
+
except (EOFError, KeyboardInterrupt):
|
|
447
|
+
return False
|
|
448
|
+
|
|
449
|
+
def _update_version_tracking(
|
|
450
|
+
self, project_path: Path, config_type: str, version: str
|
|
451
|
+
) -> None:
|
|
452
|
+
"""Update version tracking file."""
|
|
453
|
+
version_file = project_path / ".crackerjack-config.yaml"
|
|
454
|
+
|
|
455
|
+
data: dict[str, t.Any] = {"version": "1.0.0", "configs": {}}
|
|
456
|
+
if version_file.exists():
|
|
457
|
+
with suppress(Exception):
|
|
458
|
+
with version_file.open() as f:
|
|
459
|
+
existing_data = yaml.safe_load(f)
|
|
460
|
+
if isinstance(existing_data, dict):
|
|
461
|
+
data = existing_data
|
|
462
|
+
|
|
463
|
+
if "configs" not in data:
|
|
464
|
+
data["configs"] = {}
|
|
465
|
+
|
|
466
|
+
data["configs"][config_type] = {
|
|
467
|
+
"version": version,
|
|
468
|
+
"last_updated": datetime.now().isoformat(),
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
with suppress(Exception):
|
|
472
|
+
with version_file.open("w") as f:
|
|
473
|
+
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
|
|
474
|
+
|
|
475
|
+
def _invalidate_cache(self, project_path: Path) -> None:
|
|
476
|
+
"""Invalidate cache to ensure fresh environment."""
|
|
477
|
+
# No-op in the new system - cache invalidation handled differently
|
|
478
|
+
pass
|
|
479
|
+
|
|
480
|
+
def get_config_hash(self, config_path: Path) -> str:
|
|
481
|
+
"""Generate hash of configuration file for cache invalidation."""
|
|
482
|
+
if not config_path.exists():
|
|
483
|
+
return ""
|
|
484
|
+
|
|
485
|
+
try:
|
|
486
|
+
content = config_path.read_text()
|
|
487
|
+
return hashlib.sha256(content.encode()).hexdigest()[:16]
|
|
488
|
+
except Exception:
|
|
489
|
+
return ""
|
|
490
|
+
|
|
491
|
+
def list_available_templates(self) -> dict[str, str]:
|
|
492
|
+
"""List all available configuration templates."""
|
|
493
|
+
return {name: template.description for name, template in self.templates.items()}
|