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,132 @@
|
|
|
1
|
+
import tomllib
|
|
2
|
+
import typing as t
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from acb.console import Console
|
|
6
|
+
from acb.depends import Inject, depends
|
|
7
|
+
|
|
8
|
+
from crackerjack.exceptions.config import ConfigIntegrityError
|
|
9
|
+
from crackerjack.models.protocols import ConfigIntegrityServiceProtocol, ServiceProtocol
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ConfigIntegrityService(ConfigIntegrityServiceProtocol, ServiceProtocol):
|
|
13
|
+
@depends.inject
|
|
14
|
+
def __init__(self, console: Inject[Console], project_path: Path) -> None:
|
|
15
|
+
self.console = console
|
|
16
|
+
self.project_path = project_path
|
|
17
|
+
self.cache_dir = Path.home() / ".cache" / "crackerjack"
|
|
18
|
+
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
19
|
+
|
|
20
|
+
def initialize(self) -> None:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
def cleanup(self) -> None:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def health_check(self) -> bool:
|
|
27
|
+
return True
|
|
28
|
+
|
|
29
|
+
def shutdown(self) -> None:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
def metrics(self) -> dict[str, t.Any]:
|
|
33
|
+
return {}
|
|
34
|
+
|
|
35
|
+
def is_healthy(self) -> bool:
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
def register_resource(self, resource: t.Any) -> None:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def cleanup_resource(self, resource: t.Any) -> None:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
def record_error(self, error: Exception) -> None:
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
def increment_requests(self) -> None:
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
def get_custom_metric(self, name: str) -> t.Any:
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
def set_custom_metric(self, name: str, value: t.Any) -> None:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
def check_config_integrity(self) -> bool:
|
|
57
|
+
config_files = [
|
|
58
|
+
".pre-commit-config.yaml",
|
|
59
|
+
"pyproject.toml",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
drift_detected = False
|
|
63
|
+
|
|
64
|
+
for file_name in config_files:
|
|
65
|
+
file_path = self.project_path / file_name
|
|
66
|
+
if file_path.exists():
|
|
67
|
+
try:
|
|
68
|
+
if self._check_file_drift(file_path):
|
|
69
|
+
drift_detected = True
|
|
70
|
+
except ConfigIntegrityError as e:
|
|
71
|
+
self.console.print(
|
|
72
|
+
f"[red]❌ Error checking {file_path.name}: {e}[/ red]"
|
|
73
|
+
)
|
|
74
|
+
drift_detected = True
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
if not self._has_required_config_sections():
|
|
78
|
+
drift_detected = True
|
|
79
|
+
except ConfigIntegrityError as e:
|
|
80
|
+
self.console.print(f"[red]❌ Configuration integrity error: {e}[/ red]")
|
|
81
|
+
drift_detected = True
|
|
82
|
+
|
|
83
|
+
return drift_detected
|
|
84
|
+
|
|
85
|
+
def _check_file_drift(self, file_path: Path) -> bool:
|
|
86
|
+
cache_file = self.cache_dir / f"{file_path.name}.hash"
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
current_content = file_path.read_text()
|
|
90
|
+
current_hash = hash(current_content)
|
|
91
|
+
|
|
92
|
+
if cache_file.exists():
|
|
93
|
+
cached_hash = int(cache_file.read_text().strip())
|
|
94
|
+
if current_hash != cached_hash:
|
|
95
|
+
self.console.print(
|
|
96
|
+
f"[yellow]⚠️ {file_path.name} has been modified manually[/ yellow]",
|
|
97
|
+
)
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
cache_file.write_text(str(current_hash))
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
except OSError as e:
|
|
104
|
+
raise ConfigIntegrityError(
|
|
105
|
+
f"Failed to check file drift for {file_path.name}: {e}"
|
|
106
|
+
) from e
|
|
107
|
+
|
|
108
|
+
def _has_required_config_sections(self) -> bool:
|
|
109
|
+
pyproject = self.project_path / "pyproject.toml"
|
|
110
|
+
if not pyproject.exists():
|
|
111
|
+
raise ConfigIntegrityError("pyproject.toml not found.")
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
with pyproject.open("rb") as f:
|
|
115
|
+
config = tomllib.load(f)
|
|
116
|
+
except Exception as e:
|
|
117
|
+
raise ConfigIntegrityError(f"Error parsing pyproject.toml: {e}") from e
|
|
118
|
+
|
|
119
|
+
required = ["tool.ruff", "tool.pyright", "tool.pytest.ini_options"]
|
|
120
|
+
|
|
121
|
+
for section in required:
|
|
122
|
+
keys = section.split(".")
|
|
123
|
+
current = config
|
|
124
|
+
|
|
125
|
+
for key in keys:
|
|
126
|
+
if key not in current:
|
|
127
|
+
raise ConfigIntegrityError(
|
|
128
|
+
f"Missing required config section: {section} in pyproject.toml"
|
|
129
|
+
)
|
|
130
|
+
current = current[key]
|
|
131
|
+
|
|
132
|
+
return True
|
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import io
|
|
3
|
+
import typing as t
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import tomli
|
|
7
|
+
import tomli_w
|
|
8
|
+
import yaml
|
|
9
|
+
from acb.console import Console
|
|
10
|
+
from acb.depends import Inject, depends
|
|
11
|
+
from acb.logger import Logger
|
|
12
|
+
|
|
13
|
+
from crackerjack.models.protocols import (
|
|
14
|
+
ConfigMergeServiceProtocol,
|
|
15
|
+
FileSystemInterface,
|
|
16
|
+
GitInterface,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ConfigMergeService(ConfigMergeServiceProtocol):
|
|
21
|
+
@depends.inject
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
console: Inject[Console],
|
|
25
|
+
filesystem: Inject[FileSystemInterface],
|
|
26
|
+
git_service: Inject[GitInterface],
|
|
27
|
+
logger: Inject[Logger],
|
|
28
|
+
) -> None:
|
|
29
|
+
self.console = console
|
|
30
|
+
self.filesystem = filesystem
|
|
31
|
+
self.git_service = git_service
|
|
32
|
+
self.logger = logger
|
|
33
|
+
|
|
34
|
+
def smart_merge_pyproject(
|
|
35
|
+
self,
|
|
36
|
+
source_content: dict[str, t.Any],
|
|
37
|
+
target_path: str | t.Any,
|
|
38
|
+
project_name: str,
|
|
39
|
+
) -> dict[str, t.Any]:
|
|
40
|
+
target_path = Path(target_path)
|
|
41
|
+
|
|
42
|
+
if not target_path.exists():
|
|
43
|
+
return t.cast(
|
|
44
|
+
dict[str, t.Any],
|
|
45
|
+
self._replace_project_name_in_config_value(
|
|
46
|
+
source_content, project_name
|
|
47
|
+
),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
with target_path.open("rb") as f:
|
|
51
|
+
target_content = tomli.load(f)
|
|
52
|
+
|
|
53
|
+
self._ensure_crackerjack_dev_dependency(target_content, source_content)
|
|
54
|
+
|
|
55
|
+
self._merge_tool_configurations(target_content, source_content, project_name)
|
|
56
|
+
|
|
57
|
+
self._remove_fixed_coverage_requirements(target_content)
|
|
58
|
+
|
|
59
|
+
self.logger.info("Smart merged pyproject.toml", project_name=project_name)
|
|
60
|
+
return target_content
|
|
61
|
+
|
|
62
|
+
def smart_merge_pre_commit_config(
|
|
63
|
+
self,
|
|
64
|
+
source_content: dict[str, t.Any],
|
|
65
|
+
target_path: str | t.Any,
|
|
66
|
+
project_name: str,
|
|
67
|
+
) -> dict[str, t.Any]:
|
|
68
|
+
target_path = Path(target_path)
|
|
69
|
+
|
|
70
|
+
if not target_path.exists():
|
|
71
|
+
# Process source content for project-specific references
|
|
72
|
+
processed_source = copy.deepcopy(source_content)
|
|
73
|
+
source_repos = processed_source.get("repos", [])
|
|
74
|
+
processed_source["repos"] = self._process_pre_commit_repos_for_project(
|
|
75
|
+
source_repos, project_name
|
|
76
|
+
)
|
|
77
|
+
return processed_source
|
|
78
|
+
|
|
79
|
+
with target_path.open() as f:
|
|
80
|
+
loaded_config = yaml.safe_load(f)
|
|
81
|
+
target_content: dict[str, t.Any] = (
|
|
82
|
+
loaded_config if isinstance(loaded_config, dict) else {}
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if not isinstance(target_content, dict):
|
|
86
|
+
self.logger.warning(
|
|
87
|
+
f"Target config is not a dictionary, using source: {type(target_content)}"
|
|
88
|
+
)
|
|
89
|
+
return source_content
|
|
90
|
+
|
|
91
|
+
source_repos = source_content.get("repos", [])
|
|
92
|
+
target_repos = target_content.get("repos", [])
|
|
93
|
+
|
|
94
|
+
if not isinstance(target_repos, list):
|
|
95
|
+
target_repos = []
|
|
96
|
+
|
|
97
|
+
existing_repo_urls = {
|
|
98
|
+
repo.get("repo", "") for repo in target_repos if isinstance(repo, dict)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
new_repos = [
|
|
102
|
+
repo
|
|
103
|
+
for repo in source_repos
|
|
104
|
+
if isinstance(repo, dict) and repo.get("repo", "") not in existing_repo_urls
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
if new_repos:
|
|
108
|
+
# Replace project-specific references in new repos before adding them
|
|
109
|
+
processed_new_repos = self._process_pre_commit_repos_for_project(
|
|
110
|
+
new_repos, project_name
|
|
111
|
+
)
|
|
112
|
+
target_repos.extend(processed_new_repos)
|
|
113
|
+
target_content["repos"] = target_repos
|
|
114
|
+
self.logger.info(
|
|
115
|
+
"Merged .pre-commit-config.yaml",
|
|
116
|
+
new_repos_count=len(new_repos),
|
|
117
|
+
project_name=project_name,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return target_content
|
|
121
|
+
|
|
122
|
+
def smart_append_file(
|
|
123
|
+
self,
|
|
124
|
+
source_content: str,
|
|
125
|
+
target_path: str | t.Any,
|
|
126
|
+
start_marker: str,
|
|
127
|
+
end_marker: str,
|
|
128
|
+
force: bool = False,
|
|
129
|
+
) -> str:
|
|
130
|
+
target_path = Path(target_path)
|
|
131
|
+
|
|
132
|
+
if not target_path.exists():
|
|
133
|
+
return f"{start_marker}\n{source_content.strip()}\n{end_marker}\n"
|
|
134
|
+
|
|
135
|
+
existing_content = target_path.read_text()
|
|
136
|
+
|
|
137
|
+
if start_marker in existing_content:
|
|
138
|
+
if force:
|
|
139
|
+
start_idx = existing_content.find(start_marker)
|
|
140
|
+
end_idx = existing_content.find(end_marker)
|
|
141
|
+
if end_idx != -1:
|
|
142
|
+
end_idx += len(end_marker)
|
|
143
|
+
existing_content = (
|
|
144
|
+
existing_content[:start_idx] + existing_content[end_idx:]
|
|
145
|
+
).strip()
|
|
146
|
+
else:
|
|
147
|
+
return existing_content
|
|
148
|
+
|
|
149
|
+
merged_content = existing_content.strip() + "\n\n" + start_marker + "\n"
|
|
150
|
+
merged_content += source_content.strip() + "\n"
|
|
151
|
+
merged_content += end_marker + "\n"
|
|
152
|
+
|
|
153
|
+
self.logger.info("Smart appended file with markers", path=str(target_path))
|
|
154
|
+
return merged_content
|
|
155
|
+
|
|
156
|
+
def smart_merge_gitignore(
|
|
157
|
+
self,
|
|
158
|
+
patterns: list[str],
|
|
159
|
+
target_path: str | t.Any,
|
|
160
|
+
) -> str:
|
|
161
|
+
target_path = Path(target_path)
|
|
162
|
+
|
|
163
|
+
if not target_path.exists():
|
|
164
|
+
return self._create_new_gitignore(target_path, patterns)
|
|
165
|
+
|
|
166
|
+
lines = target_path.read_text().splitlines()
|
|
167
|
+
|
|
168
|
+
parsed_content = self._parse_existing_gitignore_content(lines)
|
|
169
|
+
|
|
170
|
+
merged_content = self._build_merged_gitignore_content(parsed_content, patterns)
|
|
171
|
+
|
|
172
|
+
target_path.write_text(merged_content)
|
|
173
|
+
new_patterns_count = len(
|
|
174
|
+
[p for p in patterns if p not in parsed_content.existing_patterns]
|
|
175
|
+
)
|
|
176
|
+
all_patterns_count = len(parsed_content.existing_patterns) + new_patterns_count
|
|
177
|
+
|
|
178
|
+
self.logger.info(
|
|
179
|
+
"Smart merged .gitignore (cleaned duplicates)",
|
|
180
|
+
new_patterns_count=new_patterns_count,
|
|
181
|
+
total_crackerjack_patterns=all_patterns_count,
|
|
182
|
+
)
|
|
183
|
+
return merged_content
|
|
184
|
+
|
|
185
|
+
def _create_new_gitignore(self, target_path: Path, patterns: list[str]) -> str:
|
|
186
|
+
merged_content = "# Crackerjack patterns\n"
|
|
187
|
+
for pattern in patterns:
|
|
188
|
+
merged_content += f"{pattern}\n"
|
|
189
|
+
target_path.write_text(merged_content)
|
|
190
|
+
self.logger.info("Created .gitignore", new_patterns_count=len(patterns))
|
|
191
|
+
return merged_content
|
|
192
|
+
|
|
193
|
+
def _parse_existing_gitignore_content(self, lines: list[str]) -> t.Any:
|
|
194
|
+
class ParsedContent:
|
|
195
|
+
def __init__(self) -> None:
|
|
196
|
+
self.cleaned_lines: list[str] = []
|
|
197
|
+
self.existing_patterns: set[str] = set()
|
|
198
|
+
|
|
199
|
+
parsed = ParsedContent()
|
|
200
|
+
parser_state = self._init_parser_state()
|
|
201
|
+
|
|
202
|
+
for line in lines:
|
|
203
|
+
parser_state = self._process_gitignore_line(line, parsed, parser_state)
|
|
204
|
+
|
|
205
|
+
return parsed
|
|
206
|
+
|
|
207
|
+
def _init_parser_state(self) -> dict[str, bool]:
|
|
208
|
+
return {
|
|
209
|
+
"inside_crackerjack_section": False,
|
|
210
|
+
"skip_empty_after_crackerjack": False,
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
def _process_gitignore_line(
|
|
214
|
+
self, line: str, parsed: t.Any, state: dict[str, bool]
|
|
215
|
+
) -> dict[str, bool]:
|
|
216
|
+
stripped = line.strip()
|
|
217
|
+
|
|
218
|
+
if self._is_crackerjack_header(stripped):
|
|
219
|
+
return self._handle_crackerjack_header(state)
|
|
220
|
+
|
|
221
|
+
if self._should_skip_empty_line(stripped, state):
|
|
222
|
+
state["skip_empty_after_crackerjack"] = False
|
|
223
|
+
return state
|
|
224
|
+
|
|
225
|
+
state["skip_empty_after_crackerjack"] = False
|
|
226
|
+
|
|
227
|
+
self._collect_pattern_if_present(stripped, parsed, state)
|
|
228
|
+
self._add_line_if_non_crackerjack(line, parsed, state)
|
|
229
|
+
|
|
230
|
+
return state
|
|
231
|
+
|
|
232
|
+
def _handle_crackerjack_header(self, state: dict[str, bool]) -> dict[str, bool]:
|
|
233
|
+
if not state["inside_crackerjack_section"]:
|
|
234
|
+
state["inside_crackerjack_section"] = True
|
|
235
|
+
state["skip_empty_after_crackerjack"] = True
|
|
236
|
+
return state
|
|
237
|
+
|
|
238
|
+
def _should_skip_empty_line(self, stripped: str, state: dict[str, bool]) -> bool:
|
|
239
|
+
return state["skip_empty_after_crackerjack"] and not stripped
|
|
240
|
+
|
|
241
|
+
def _collect_pattern_if_present(
|
|
242
|
+
self, stripped: str, parsed: t.Any, state: dict[str, bool]
|
|
243
|
+
) -> None:
|
|
244
|
+
if stripped and not stripped.startswith("#"):
|
|
245
|
+
parsed.existing_patterns.add(stripped)
|
|
246
|
+
|
|
247
|
+
def _add_line_if_non_crackerjack(
|
|
248
|
+
self, line: str, parsed: t.Any, state: dict[str, bool]
|
|
249
|
+
) -> None:
|
|
250
|
+
if not state["inside_crackerjack_section"]:
|
|
251
|
+
parsed.cleaned_lines.append(line)
|
|
252
|
+
|
|
253
|
+
def _is_crackerjack_header(self, line: str) -> bool:
|
|
254
|
+
return line in ("# Crackerjack patterns", "# Crackerjack generated files")
|
|
255
|
+
|
|
256
|
+
def _build_merged_gitignore_content(
|
|
257
|
+
self, parsed_content: t.Any, new_patterns: list[str]
|
|
258
|
+
) -> str:
|
|
259
|
+
if parsed_content.cleaned_lines and not parsed_content.cleaned_lines[-1]:
|
|
260
|
+
parsed_content.cleaned_lines.pop()
|
|
261
|
+
|
|
262
|
+
merged_content = "\n".join(parsed_content.cleaned_lines)
|
|
263
|
+
if merged_content:
|
|
264
|
+
merged_content += "\n"
|
|
265
|
+
|
|
266
|
+
all_crackerjack_patterns = self._get_consolidated_patterns(
|
|
267
|
+
parsed_content.existing_patterns, new_patterns
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
if all_crackerjack_patterns:
|
|
271
|
+
merged_content += "\n# Crackerjack patterns\n"
|
|
272
|
+
for pattern in sorted(all_crackerjack_patterns):
|
|
273
|
+
merged_content += f"{pattern}\n"
|
|
274
|
+
|
|
275
|
+
return merged_content
|
|
276
|
+
|
|
277
|
+
def _get_consolidated_patterns(
|
|
278
|
+
self, existing_patterns: set[str], new_patterns: list[str]
|
|
279
|
+
) -> list[str]:
|
|
280
|
+
new_patterns_to_add = [p for p in new_patterns if p not in existing_patterns]
|
|
281
|
+
return list[t.Any](existing_patterns) + new_patterns_to_add
|
|
282
|
+
|
|
283
|
+
def write_pyproject_config(
|
|
284
|
+
self,
|
|
285
|
+
config: dict[str, t.Any],
|
|
286
|
+
target_path: str | t.Any,
|
|
287
|
+
) -> None:
|
|
288
|
+
target_path = Path(target_path)
|
|
289
|
+
|
|
290
|
+
buffer = io.BytesIO()
|
|
291
|
+
tomli_w.dump(config, buffer)
|
|
292
|
+
content = buffer.getvalue().decode("utf-8")
|
|
293
|
+
|
|
294
|
+
from crackerjack.services.filesystem import FileSystemService
|
|
295
|
+
|
|
296
|
+
content = FileSystemService.clean_trailing_whitespace_and_newlines(content)
|
|
297
|
+
|
|
298
|
+
with target_path.open("w", encoding="utf-8") as f:
|
|
299
|
+
f.write(content)
|
|
300
|
+
|
|
301
|
+
self.logger.debug("Wrote pyproject.toml config", path=str(target_path))
|
|
302
|
+
|
|
303
|
+
def write_pre_commit_config(
|
|
304
|
+
self,
|
|
305
|
+
config: dict[str, t.Any],
|
|
306
|
+
target_path: str | t.Any,
|
|
307
|
+
) -> None:
|
|
308
|
+
target_path = Path(target_path)
|
|
309
|
+
|
|
310
|
+
yaml_content = yaml.dump(
|
|
311
|
+
config,
|
|
312
|
+
default_flow_style=False,
|
|
313
|
+
sort_keys=False,
|
|
314
|
+
width=float("inf"),
|
|
315
|
+
)
|
|
316
|
+
content = yaml_content
|
|
317
|
+
|
|
318
|
+
from crackerjack.services.filesystem import FileSystemService
|
|
319
|
+
|
|
320
|
+
content = FileSystemService.clean_trailing_whitespace_and_newlines(content)
|
|
321
|
+
|
|
322
|
+
with target_path.open("w") as f:
|
|
323
|
+
f.write(content)
|
|
324
|
+
|
|
325
|
+
self.logger.debug("Wrote .pre-commit-config.yaml", path=str(target_path))
|
|
326
|
+
|
|
327
|
+
def _ensure_crackerjack_dev_dependency(
|
|
328
|
+
self,
|
|
329
|
+
target_config: dict[str, t.Any],
|
|
330
|
+
source_config: dict[str, t.Any],
|
|
331
|
+
) -> None:
|
|
332
|
+
if "dependency-groups" not in target_config:
|
|
333
|
+
target_config["dependency-groups"] = {}
|
|
334
|
+
|
|
335
|
+
if "dev" not in target_config["dependency-groups"]:
|
|
336
|
+
target_config["dependency-groups"]["dev"] = []
|
|
337
|
+
|
|
338
|
+
dev_deps = target_config["dependency-groups"]["dev"]
|
|
339
|
+
if "crackerjack" not in str(dev_deps):
|
|
340
|
+
dev_deps.append("crackerjack")
|
|
341
|
+
self.logger.debug("Added crackerjack to dev dependencies")
|
|
342
|
+
|
|
343
|
+
def _merge_tool_configurations(
|
|
344
|
+
self,
|
|
345
|
+
target_config: dict[str, t.Any],
|
|
346
|
+
source_config: dict[str, t.Any],
|
|
347
|
+
project_name: str,
|
|
348
|
+
) -> None:
|
|
349
|
+
source_tools = source_config.get("tool", {})
|
|
350
|
+
|
|
351
|
+
if "tool" not in target_config:
|
|
352
|
+
target_config["tool"] = {}
|
|
353
|
+
|
|
354
|
+
target_tools = target_config["tool"]
|
|
355
|
+
|
|
356
|
+
tools_to_merge = [
|
|
357
|
+
"ruff",
|
|
358
|
+
"pyright",
|
|
359
|
+
"bandit",
|
|
360
|
+
"vulture",
|
|
361
|
+
"refurb",
|
|
362
|
+
"complexipy",
|
|
363
|
+
"codespell",
|
|
364
|
+
"creosote",
|
|
365
|
+
]
|
|
366
|
+
|
|
367
|
+
for tool_name in tools_to_merge:
|
|
368
|
+
if tool_name in source_tools:
|
|
369
|
+
if tool_name not in target_tools:
|
|
370
|
+
target_tools[tool_name] = self._replace_project_name_in_tool_config(
|
|
371
|
+
source_tools[tool_name], project_name
|
|
372
|
+
)
|
|
373
|
+
self.console.print(
|
|
374
|
+
f"[green]➕[/green] Added [tool.{tool_name}] configuration"
|
|
375
|
+
)
|
|
376
|
+
else:
|
|
377
|
+
self._merge_tool_settings(
|
|
378
|
+
target_tools[tool_name],
|
|
379
|
+
source_tools[tool_name],
|
|
380
|
+
tool_name,
|
|
381
|
+
project_name,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
self._merge_pytest_markers(target_tools, source_tools)
|
|
385
|
+
|
|
386
|
+
def _merge_tool_settings(
|
|
387
|
+
self,
|
|
388
|
+
target_tool: dict[str, t.Any],
|
|
389
|
+
source_tool: dict[str, t.Any],
|
|
390
|
+
tool_name: str,
|
|
391
|
+
project_name: str,
|
|
392
|
+
) -> None:
|
|
393
|
+
updated_keys = []
|
|
394
|
+
|
|
395
|
+
for key, value in source_tool.items():
|
|
396
|
+
if key not in target_tool:
|
|
397
|
+
target_tool[key] = self._replace_project_name_in_config_value(
|
|
398
|
+
value, project_name
|
|
399
|
+
)
|
|
400
|
+
updated_keys.append(key)
|
|
401
|
+
|
|
402
|
+
if updated_keys:
|
|
403
|
+
self.console.print(
|
|
404
|
+
f"[yellow]🔄[/yellow] Updated [tool.{tool_name}] with: {', '.join(updated_keys)}"
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
def _merge_pytest_markers(
|
|
408
|
+
self,
|
|
409
|
+
target_tools: dict[str, t.Any],
|
|
410
|
+
source_tools: dict[str, t.Any],
|
|
411
|
+
) -> None:
|
|
412
|
+
if "pytest" not in source_tools or "pytest" not in target_tools:
|
|
413
|
+
return
|
|
414
|
+
|
|
415
|
+
source_pytest = source_tools["pytest"]
|
|
416
|
+
target_pytest = target_tools["pytest"]
|
|
417
|
+
|
|
418
|
+
if "ini_options" not in source_pytest or "ini_options" not in target_pytest:
|
|
419
|
+
return
|
|
420
|
+
|
|
421
|
+
source_markers = source_pytest["ini_options"].get("markers", [])
|
|
422
|
+
target_markers = target_pytest["ini_options"].get("markers", [])
|
|
423
|
+
|
|
424
|
+
existing_marker_names = {marker.split(": ")[0] for marker in target_markers}
|
|
425
|
+
new_markers = [
|
|
426
|
+
marker
|
|
427
|
+
for marker in source_markers
|
|
428
|
+
if marker.split(": ")[0] not in existing_marker_names
|
|
429
|
+
]
|
|
430
|
+
|
|
431
|
+
if new_markers:
|
|
432
|
+
target_markers.extend(new_markers)
|
|
433
|
+
self.console.print(
|
|
434
|
+
f"[green]➕[/green] Added pytest markers: {len(new_markers)}"
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
def _remove_fixed_coverage_requirements(
|
|
438
|
+
self,
|
|
439
|
+
target_config: dict[str, t.Any],
|
|
440
|
+
) -> None:
|
|
441
|
+
target_coverage = (
|
|
442
|
+
target_config.get("tool", {}).get("pytest", {}).get("ini_options", {})
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
addopts = target_coverage.get("addopts", "")
|
|
446
|
+
if isinstance(addopts, str):
|
|
447
|
+
original_addopts = addopts
|
|
448
|
+
|
|
449
|
+
from crackerjack.services.regex_patterns import remove_coverage_fail_under
|
|
450
|
+
|
|
451
|
+
addopts = remove_coverage_fail_under(addopts).strip()
|
|
452
|
+
addopts = " ".join(addopts.split())
|
|
453
|
+
|
|
454
|
+
if original_addopts != addopts:
|
|
455
|
+
target_coverage["addopts"] = addopts
|
|
456
|
+
self.console.print(
|
|
457
|
+
"[green]🔄[/green] Removed fixed coverage requirement (using ratchet system)"
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
coverage_report = (
|
|
461
|
+
target_config.get("tool", {}).get("coverage", {}).get("report", {})
|
|
462
|
+
)
|
|
463
|
+
if "fail_under" in coverage_report:
|
|
464
|
+
original_fail_under = coverage_report["fail_under"]
|
|
465
|
+
coverage_report["fail_under"] = 0
|
|
466
|
+
self.console.print(
|
|
467
|
+
f"[green]🔄[/green] Reset coverage.report.fail_under from {original_fail_under} to 0 (ratchet system)"
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
def _replace_project_name_in_tool_config(
|
|
471
|
+
self, tool_config: dict[str, t.Any], project_name: str
|
|
472
|
+
) -> dict[str, t.Any]:
|
|
473
|
+
if project_name == "crackerjack":
|
|
474
|
+
return tool_config
|
|
475
|
+
|
|
476
|
+
result = copy.deepcopy(tool_config)
|
|
477
|
+
return t.cast(
|
|
478
|
+
dict[str, t.Any],
|
|
479
|
+
self._replace_project_name_in_config_value(result, project_name),
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
def _replace_project_name_in_config_value(
|
|
483
|
+
self, value: t.Any, project_name: str
|
|
484
|
+
) -> t.Any:
|
|
485
|
+
if project_name == "crackerjack":
|
|
486
|
+
return value
|
|
487
|
+
|
|
488
|
+
if isinstance(value, str):
|
|
489
|
+
return value.replace("crackerjack", project_name)
|
|
490
|
+
elif isinstance(value, list):
|
|
491
|
+
return [
|
|
492
|
+
self._replace_project_name_in_config_value(item, project_name)
|
|
493
|
+
for item in value
|
|
494
|
+
]
|
|
495
|
+
elif isinstance(value, dict):
|
|
496
|
+
return {
|
|
497
|
+
key: self._replace_project_name_in_config_value(val, project_name)
|
|
498
|
+
for key, val in value.items()
|
|
499
|
+
}
|
|
500
|
+
return value
|
|
501
|
+
|
|
502
|
+
def _process_pre_commit_repos_for_project(
|
|
503
|
+
self, repos: list[dict[str, t.Any]], project_name: str
|
|
504
|
+
) -> list[dict[str, t.Any]]:
|
|
505
|
+
"""Process pre-commit repos to replace project-specific references."""
|
|
506
|
+
if project_name == "crackerjack":
|
|
507
|
+
return repos # No changes needed for crackerjack itself
|
|
508
|
+
|
|
509
|
+
processed_repos = []
|
|
510
|
+
for repo in repos:
|
|
511
|
+
processed_repo = copy.deepcopy(repo)
|
|
512
|
+
self._process_repo_hooks(processed_repo, project_name)
|
|
513
|
+
processed_repos.append(processed_repo)
|
|
514
|
+
|
|
515
|
+
return processed_repos
|
|
516
|
+
|
|
517
|
+
def _process_repo_hooks(self, repo: dict[str, t.Any], project_name: str) -> None:
|
|
518
|
+
"""Process hooks within a repo to replace project-specific references."""
|
|
519
|
+
hooks = repo.get("hooks", [])
|
|
520
|
+
for hook in hooks:
|
|
521
|
+
if isinstance(hook, dict):
|
|
522
|
+
self._process_hook_args(hook, project_name)
|
|
523
|
+
self._process_hook_files(hook, project_name)
|
|
524
|
+
# Special handling for validate-regex-patterns hook - keep it pointing to crackerjack package
|
|
525
|
+
# This should reference the installed crackerjack package, not the current project
|
|
526
|
+
# The entry already uses "uv run python -m crackerjack.tools.validate_regex_patterns"
|
|
527
|
+
# which is correct - it runs from the installed crackerjack package
|
|
528
|
+
|
|
529
|
+
def _process_hook_args(self, hook: dict[str, t.Any], project_name: str) -> None:
|
|
530
|
+
"""Process hook args to replace project-specific references."""
|
|
531
|
+
if "args" in hook:
|
|
532
|
+
hook["args"] = [
|
|
533
|
+
arg.replace("crackerjack", project_name)
|
|
534
|
+
if isinstance(arg, str)
|
|
535
|
+
else arg
|
|
536
|
+
for arg in hook["args"]
|
|
537
|
+
]
|
|
538
|
+
|
|
539
|
+
def _process_hook_files(self, hook: dict[str, t.Any], project_name: str) -> None:
|
|
540
|
+
"""Process hook files pattern to replace project-specific references."""
|
|
541
|
+
if "files" in hook:
|
|
542
|
+
files_pattern = hook["files"]
|
|
543
|
+
if isinstance(files_pattern, str):
|
|
544
|
+
hook["files"] = files_pattern.replace(
|
|
545
|
+
"^crackerjack/", f"^{project_name}/"
|
|
546
|
+
)
|