crackerjack 0.37.9__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 +30 -1
- crackerjack/__main__.py +342 -1263
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +27 -5
- 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/{rust_tool_manager.py → lsp/_manager.py} +3 -3
- crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
- crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
- 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 +40 -12
- crackerjack/agents/base.py +1 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +49 -53
- crackerjack/agents/dry_agent.py +187 -3
- 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 +6 -8
- 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/performance_agent.py +121 -1152
- crackerjack/agents/refactoring_agent.py +156 -655
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +19 -1605
- crackerjack/api.py +5 -7
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +1 -1
- crackerjack/cli/cache_handlers.py +23 -18
- crackerjack/cli/cache_handlers_enhanced.py +1 -4
- crackerjack/cli/facade.py +70 -8
- 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 +249 -49
- crackerjack/cli/interactive.py +8 -5
- crackerjack/cli/options.py +203 -110
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +60 -24
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +256 -0
- crackerjack/config/global_lock_config.py +191 -54
- crackerjack/config/hooks.py +188 -16
- 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/async_workflow_orchestrator.py +79 -53
- crackerjack/core/autofix_coordinator.py +22 -9
- crackerjack/core/container.py +10 -9
- crackerjack/core/enhanced_container.py +9 -9
- crackerjack/core/performance.py +1 -1
- crackerjack/core/performance_monitor.py +5 -3
- crackerjack/core/phase_coordinator.py +1018 -634
- crackerjack/core/proactive_workflow.py +3 -3
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +167 -23
- crackerjack/core/session_coordinator.py +187 -382
- crackerjack/core/timeout_manager.py +161 -44
- 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 +1247 -953
- 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/README.md +11 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/ai_templates.py +1 -1
- crackerjack/documentation/dual_output_generator.py +11 -9
- crackerjack/documentation/reference_generator.py +104 -59
- crackerjack/dynamic_config.py +52 -61
- crackerjack/errors.py +1 -1
- 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 +2 -0
- crackerjack/executors/async_hook_executor.py +539 -77
- crackerjack/executors/cached_hook_executor.py +3 -3
- crackerjack/executors/hook_executor.py +967 -102
- crackerjack/executors/hook_lock_manager.py +31 -22
- crackerjack/executors/individual_hook_executor.py +66 -32
- crackerjack/executors/lsp_aware_hook_executor.py +136 -57
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +23 -7
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +8 -9
- crackerjack/intelligence/README.md +557 -0
- crackerjack/interactive.py +37 -10
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/async_hook_manager.py +41 -57
- crackerjack/managers/hook_manager.py +449 -79
- crackerjack/managers/publish_manager.py +81 -36
- crackerjack/managers/test_command_builder.py +290 -12
- crackerjack/managers/test_executor.py +93 -8
- crackerjack/managers/test_manager.py +1082 -75
- crackerjack/managers/test_progress.py +118 -26
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/cache.py +25 -2
- crackerjack/mcp/client_runner.py +35 -18
- crackerjack/mcp/context.py +9 -9
- crackerjack/mcp/dashboard.py +24 -8
- crackerjack/mcp/enhanced_progress_monitor.py +34 -23
- crackerjack/mcp/file_monitor.py +27 -6
- crackerjack/mcp/progress_components.py +45 -34
- crackerjack/mcp/progress_monitor.py +6 -9
- crackerjack/mcp/rate_limiter.py +11 -7
- crackerjack/mcp/server.py +2 -0
- crackerjack/mcp/server_core.py +187 -55
- crackerjack/mcp/service_watchdog.py +12 -9
- crackerjack/mcp/task_manager.py +2 -2
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +2 -0
- crackerjack/mcp/tools/core_tools.py +75 -52
- crackerjack/mcp/tools/execution_tools.py +87 -31
- crackerjack/mcp/tools/intelligence_tools.py +2 -2
- crackerjack/mcp/tools/proactive_tools.py +1 -1
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +180 -132
- crackerjack/mcp/tools/workflow_executor.py +87 -46
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/app.py +11 -1
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +27 -4
- 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 +16 -2930
- crackerjack/mcp/websocket/server.py +1 -3
- crackerjack/mcp/websocket/websocket_handler.py +107 -6
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +10 -1
- crackerjack/models/config.py +639 -22
- crackerjack/models/config_adapter.py +6 -6
- crackerjack/models/protocols.py +1167 -23
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +19 -3
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/ai_agent_watchdog.py +5 -4
- crackerjack/monitoring/metrics_collector.py +4 -3
- crackerjack/monitoring/regression_prevention.py +4 -3
- crackerjack/monitoring/websocket_server.py +4 -241
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +20 -67
- 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 +13 -6
- crackerjack/orchestration/execution_strategies.py +6 -6
- 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 +1 -1
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/hooks.py +3 -2
- crackerjack/plugins/loader.py +3 -3
- crackerjack/plugins/managers.py +1 -1
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +8 -21
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
- 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/api_extractor.py +5 -3
- crackerjack/services/bounded_status_operations.py +45 -5
- crackerjack/services/cache.py +249 -318
- crackerjack/services/changelog_automation.py +7 -3
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +83 -39
- crackerjack/services/config_merge.py +9 -6
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +13 -26
- crackerjack/services/coverage_badge_service.py +6 -4
- crackerjack/services/coverage_ratchet.py +53 -27
- crackerjack/services/debug.py +18 -7
- crackerjack/services/dependency_analyzer.py +4 -4
- crackerjack/services/dependency_monitor.py +13 -13
- crackerjack/services/documentation_generator.py +4 -2
- crackerjack/services/documentation_service.py +62 -33
- crackerjack/services/enhanced_filesystem.py +81 -27
- crackerjack/services/enterprise_optimizer.py +1 -1
- crackerjack/services/error_pattern_analyzer.py +10 -10
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +5 -7
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +80 -109
- crackerjack/services/git.py +99 -5
- crackerjack/services/health_metrics.py +4 -6
- crackerjack/services/heatmap_generator.py +12 -3
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +101 -49
- crackerjack/services/log_manager.py +2 -2
- crackerjack/services/logging.py +120 -68
- crackerjack/services/lsp_client.py +12 -12
- crackerjack/services/memory_optimizer.py +27 -22
- 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/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
- crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
- crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
- crackerjack/services/parallel_executor.py +166 -55
- 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 +21 -8
- 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_baseline.py → quality/quality_baseline.py} +163 -2
- crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
- crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
- crackerjack/services/regex_patterns.py +58 -2987
- crackerjack/services/regex_utils.py +55 -29
- crackerjack/services/secure_status_formatter.py +42 -15
- crackerjack/services/secure_subprocess.py +35 -2
- crackerjack/services/security.py +16 -8
- crackerjack/services/server_manager.py +40 -51
- crackerjack/services/smart_scheduling.py +46 -6
- crackerjack/services/status_authentication.py +3 -3
- crackerjack/services/thread_safe_status_collector.py +1 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +9 -5
- crackerjack/services/unified_config.py +43 -351
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +6 -4
- crackerjack/services/version_checker.py +14 -8
- crackerjack/services/zuban_lsp_service.py +5 -4
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/init.md +2 -12
- crackerjack/slash_commands/run.md +84 -50
- 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_regex_patterns.py +7 -3
- crackerjack/ui/README.md +11 -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.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack/managers/test_manager_backup.py +0 -1075
- crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
- crackerjack/mixins/__init__.py +0 -3
- crackerjack/mixins/error_handling.py +0 -145
- crackerjack/services/config.py +0 -358
- crackerjack/ui/server_panels.py +0 -125
- crackerjack-0.37.9.dist-info/RECORD +0 -231
- /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
- /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
"""Skylos adapter for ACB QA framework - dead code detection.
|
|
2
|
+
|
|
3
|
+
Skylos identifies unused imports, functions, classes, and variables in Python codebases.
|
|
4
|
+
It helps maintain clean code by detecting elements that are no longer used.
|
|
5
|
+
|
|
6
|
+
ACB Patterns:
|
|
7
|
+
- MODULE_ID and MODULE_STATUS at module level
|
|
8
|
+
- depends.set() registration after class definition
|
|
9
|
+
- Extends BaseToolAdapter for tool execution
|
|
10
|
+
- Async execution with output parsing
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
import typing as t
|
|
18
|
+
from contextlib import suppress
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from uuid import UUID
|
|
21
|
+
|
|
22
|
+
from acb.depends import depends
|
|
23
|
+
|
|
24
|
+
from crackerjack.adapters._tool_adapter_base import (
|
|
25
|
+
BaseToolAdapter,
|
|
26
|
+
ToolAdapterSettings,
|
|
27
|
+
ToolExecutionResult,
|
|
28
|
+
ToolIssue,
|
|
29
|
+
)
|
|
30
|
+
from crackerjack.models.qa_results import QACheckType
|
|
31
|
+
|
|
32
|
+
if t.TYPE_CHECKING:
|
|
33
|
+
from crackerjack.models.qa_config import QACheckConfig
|
|
34
|
+
|
|
35
|
+
# ACB Module Registration (REQUIRED)
|
|
36
|
+
MODULE_ID = UUID(
|
|
37
|
+
"01937d86-5f2a-7b3c-9d1e-a2b3c4d5e6f8"
|
|
38
|
+
) # Static UUID7 for reproducible module identity
|
|
39
|
+
MODULE_STATUS = "stable"
|
|
40
|
+
|
|
41
|
+
# Module-level logger for structured logging
|
|
42
|
+
logger = logging.getLogger(__name__)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SkylosSettings(ToolAdapterSettings):
|
|
46
|
+
"""Settings for Skylos adapter."""
|
|
47
|
+
|
|
48
|
+
tool_name: str = "skylos"
|
|
49
|
+
use_json_output: bool = True # Skylos supports JSON output
|
|
50
|
+
confidence_threshold: int = 86
|
|
51
|
+
web_dashboard_port: int = 5090
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class SkylosAdapter(BaseToolAdapter):
|
|
55
|
+
"""Adapter for Skylos - dead code detector.
|
|
56
|
+
|
|
57
|
+
Identifies unused imports, functions, classes, variables, and other code elements.
|
|
58
|
+
Helps maintain clean codebases by detecting dead code that can be safely removed.
|
|
59
|
+
|
|
60
|
+
Features:
|
|
61
|
+
- Confidence-based detection
|
|
62
|
+
- JSON output for structured analysis
|
|
63
|
+
- Integration with web dashboard
|
|
64
|
+
- Configurable confidence thresholds
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
```python
|
|
68
|
+
settings = SkylosSettings(
|
|
69
|
+
confidence_threshold=90,
|
|
70
|
+
web_dashboard_port=5091,
|
|
71
|
+
)
|
|
72
|
+
adapter = SkylosAdapter(settings=settings)
|
|
73
|
+
await adapter.init()
|
|
74
|
+
result = await adapter.check(files=[Path("src/")])
|
|
75
|
+
```
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
settings: SkylosSettings | None = None
|
|
79
|
+
|
|
80
|
+
def __init__(self, settings: SkylosSettings | None = None) -> None:
|
|
81
|
+
"""Initialize Skylos adapter.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
settings: Optional settings override
|
|
85
|
+
"""
|
|
86
|
+
super().__init__(settings=settings)
|
|
87
|
+
logger.debug(
|
|
88
|
+
"SkylosAdapter initialized", extra={"has_settings": settings is not None}
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
async def init(self) -> None:
|
|
92
|
+
"""Initialize adapter with default settings."""
|
|
93
|
+
if not self.settings:
|
|
94
|
+
self.settings = SkylosSettings()
|
|
95
|
+
logger.info("Using default SkylosSettings")
|
|
96
|
+
await super().init()
|
|
97
|
+
logger.debug(
|
|
98
|
+
"SkylosAdapter initialization complete",
|
|
99
|
+
extra={"confidence_threshold": self.settings.confidence_threshold},
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def adapter_name(self) -> str:
|
|
104
|
+
"""Human-readable adapter name."""
|
|
105
|
+
return "Skylos (Dead Code)"
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def module_id(self) -> UUID:
|
|
109
|
+
"""Reference to module-level MODULE_ID."""
|
|
110
|
+
return MODULE_ID
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def tool_name(self) -> str:
|
|
114
|
+
"""CLI tool name."""
|
|
115
|
+
return "skylos"
|
|
116
|
+
|
|
117
|
+
def build_command(
|
|
118
|
+
self,
|
|
119
|
+
files: list[Path],
|
|
120
|
+
config: QACheckConfig | None = None,
|
|
121
|
+
) -> list[str]:
|
|
122
|
+
"""Build Skylos command.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
files: Files/directories to scan for dead code
|
|
126
|
+
config: Optional configuration override
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Command as list of strings
|
|
130
|
+
"""
|
|
131
|
+
if not self.settings:
|
|
132
|
+
raise RuntimeError("Settings not initialized")
|
|
133
|
+
|
|
134
|
+
cmd = ["uv", "run", "skylos"]
|
|
135
|
+
|
|
136
|
+
# Add confidence threshold
|
|
137
|
+
cmd.extend(["--confidence", str(self.settings.confidence_threshold)])
|
|
138
|
+
|
|
139
|
+
# JSON output for structured parsing
|
|
140
|
+
if self.settings.use_json_output:
|
|
141
|
+
cmd.append("--json")
|
|
142
|
+
|
|
143
|
+
# Add targets - use package directory to avoid scanning .venv
|
|
144
|
+
target = self._determine_scan_target(files)
|
|
145
|
+
cmd.append(target)
|
|
146
|
+
|
|
147
|
+
logger.info(
|
|
148
|
+
"Built Skylos command",
|
|
149
|
+
extra={
|
|
150
|
+
"file_count": len(files) if files else 1,
|
|
151
|
+
"confidence_threshold": self.settings.confidence_threshold,
|
|
152
|
+
"target_directory": target,
|
|
153
|
+
},
|
|
154
|
+
)
|
|
155
|
+
return cmd
|
|
156
|
+
|
|
157
|
+
def _determine_scan_target(self, files: list[Path]) -> str:
|
|
158
|
+
"""Determine the target directory or files for scanning.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
files: Files specified by user
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Target string for Skylos command
|
|
165
|
+
"""
|
|
166
|
+
if files:
|
|
167
|
+
# Join multiple files into a single string
|
|
168
|
+
return " ".join(str(f) for f in files)
|
|
169
|
+
|
|
170
|
+
# Auto-detect package directory
|
|
171
|
+
package_name = self._detect_package_name()
|
|
172
|
+
return f"./{package_name}"
|
|
173
|
+
|
|
174
|
+
def _detect_package_name(self) -> str:
|
|
175
|
+
"""Detect package name from pyproject.toml or directory structure.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Package name to scan
|
|
179
|
+
"""
|
|
180
|
+
cwd = Path.cwd()
|
|
181
|
+
|
|
182
|
+
# Try reading from pyproject.toml
|
|
183
|
+
package_name = self._read_package_from_toml(cwd)
|
|
184
|
+
if package_name:
|
|
185
|
+
return package_name
|
|
186
|
+
|
|
187
|
+
# Fallback: find first directory with __init__.py
|
|
188
|
+
package_name = self._find_package_directory(cwd)
|
|
189
|
+
if package_name:
|
|
190
|
+
return package_name
|
|
191
|
+
|
|
192
|
+
# Default fallback
|
|
193
|
+
return "crackerjack"
|
|
194
|
+
|
|
195
|
+
def _read_package_from_toml(self, cwd: Path) -> str | None:
|
|
196
|
+
"""Read package name from pyproject.toml.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
cwd: Current working directory
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Package name if found, None otherwise
|
|
203
|
+
"""
|
|
204
|
+
import tomllib
|
|
205
|
+
from contextlib import suppress
|
|
206
|
+
|
|
207
|
+
pyproject_path = cwd / "pyproject.toml"
|
|
208
|
+
if not pyproject_path.exists():
|
|
209
|
+
return None
|
|
210
|
+
|
|
211
|
+
with suppress(Exception):
|
|
212
|
+
with pyproject_path.open("rb") as f:
|
|
213
|
+
data = tomllib.load(f)
|
|
214
|
+
project_name = data.get("project", {}).get("name")
|
|
215
|
+
if project_name:
|
|
216
|
+
return project_name.replace("-", "_")
|
|
217
|
+
|
|
218
|
+
return None
|
|
219
|
+
|
|
220
|
+
def _find_package_directory(self, cwd: Path) -> str | None:
|
|
221
|
+
"""Find package directory with __init__.py.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
cwd: Current working directory
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Package directory name if found, None otherwise
|
|
228
|
+
"""
|
|
229
|
+
excluded = {"tests", "docs", ".venv", "venv", "build", "dist"}
|
|
230
|
+
|
|
231
|
+
for item in cwd.iterdir():
|
|
232
|
+
if item.is_dir() and (item / "__init__.py").exists():
|
|
233
|
+
if item.name not in excluded:
|
|
234
|
+
return item.name
|
|
235
|
+
|
|
236
|
+
return None
|
|
237
|
+
|
|
238
|
+
async def parse_output(
|
|
239
|
+
self,
|
|
240
|
+
result: ToolExecutionResult,
|
|
241
|
+
) -> list[ToolIssue]:
|
|
242
|
+
"""Parse Skylos output into standardized issues.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
result: Raw execution result from Skylos
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
List of parsed issues
|
|
249
|
+
"""
|
|
250
|
+
if not result.raw_output:
|
|
251
|
+
logger.debug("No output to parse")
|
|
252
|
+
return []
|
|
253
|
+
|
|
254
|
+
# Try JSON parsing first, fallback to text parsing
|
|
255
|
+
try:
|
|
256
|
+
issues = self._parse_json_output(result.raw_output)
|
|
257
|
+
except json.JSONDecodeError:
|
|
258
|
+
logger.debug(
|
|
259
|
+
"JSON parse failed, falling back to text parsing",
|
|
260
|
+
extra={"output_preview": result.raw_output[:200]},
|
|
261
|
+
)
|
|
262
|
+
issues = self._parse_text_output(result.raw_output)
|
|
263
|
+
|
|
264
|
+
logger.info(
|
|
265
|
+
"Parsed Skylos output",
|
|
266
|
+
extra={
|
|
267
|
+
"total_issues": len(issues),
|
|
268
|
+
"files_affected": len({str(i.file_path) for i in issues}),
|
|
269
|
+
},
|
|
270
|
+
)
|
|
271
|
+
return issues
|
|
272
|
+
|
|
273
|
+
def _parse_json_output(self, output: str) -> list[ToolIssue]:
|
|
274
|
+
"""Parse Skylos JSON output.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
output: JSON output from Skylos
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
List of parsed issues
|
|
281
|
+
"""
|
|
282
|
+
data = json.loads(output)
|
|
283
|
+
logger.debug(
|
|
284
|
+
"Parsed Skylos JSON output",
|
|
285
|
+
extra={"results_count": len(data.get("dead_code", []))},
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
issues = []
|
|
289
|
+
for item in data.get("dead_code", []):
|
|
290
|
+
issue = self._create_issue_from_json(item)
|
|
291
|
+
issues.append(issue)
|
|
292
|
+
|
|
293
|
+
return issues
|
|
294
|
+
|
|
295
|
+
def _create_issue_from_json(self, item: dict) -> ToolIssue:
|
|
296
|
+
"""Create ToolIssue from JSON item.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
item: JSON item representing dead code
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
ToolIssue object
|
|
303
|
+
"""
|
|
304
|
+
file_path = Path(item.get("file", ""))
|
|
305
|
+
code_type = item.get("type", "code")
|
|
306
|
+
code_name = item.get("name", "")
|
|
307
|
+
confidence = item.get("confidence", "unknown")
|
|
308
|
+
|
|
309
|
+
return ToolIssue(
|
|
310
|
+
file_path=file_path,
|
|
311
|
+
line_number=item.get("line"),
|
|
312
|
+
message=f"Dead {code_type}: {code_name}",
|
|
313
|
+
code=code_type,
|
|
314
|
+
severity="warning", # Dead code is typically a warning
|
|
315
|
+
suggestion=f"Confidence: {confidence}%",
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
def _parse_text_output(self, output: str) -> list[ToolIssue]:
|
|
319
|
+
"""Parse Skylos text output (fallback).
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
output: Text output from Skylos
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
List of ToolIssue objects
|
|
326
|
+
"""
|
|
327
|
+
issues = []
|
|
328
|
+
lines = output.strip().split("\n")
|
|
329
|
+
|
|
330
|
+
for line in lines:
|
|
331
|
+
line = line.strip()
|
|
332
|
+
if not line or ":" not in line:
|
|
333
|
+
continue
|
|
334
|
+
|
|
335
|
+
issue = self._parse_text_line(line)
|
|
336
|
+
if issue:
|
|
337
|
+
issues.append(issue)
|
|
338
|
+
|
|
339
|
+
logger.info(
|
|
340
|
+
"Parsed Skylos text output (fallback)",
|
|
341
|
+
extra={"total_issues": len(issues)},
|
|
342
|
+
)
|
|
343
|
+
return issues
|
|
344
|
+
|
|
345
|
+
def _parse_text_line(self, line: str) -> ToolIssue | None:
|
|
346
|
+
"""Parse a single Skylos text output line.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
line: Line of text output
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
ToolIssue if parsing successful, None otherwise
|
|
353
|
+
"""
|
|
354
|
+
try:
|
|
355
|
+
# Parse format: "file.py:line: message (confidence: XX%)"
|
|
356
|
+
parts = line.split(":", 2)
|
|
357
|
+
if len(parts) < 3:
|
|
358
|
+
return None
|
|
359
|
+
|
|
360
|
+
file_path = Path(parts[0].strip())
|
|
361
|
+
line_number = self._parse_line_number(parts[1])
|
|
362
|
+
message_part = parts[2].strip()
|
|
363
|
+
|
|
364
|
+
confidence = self._extract_confidence_from_message(message_part)
|
|
365
|
+
|
|
366
|
+
return ToolIssue(
|
|
367
|
+
file_path=file_path,
|
|
368
|
+
line_number=line_number,
|
|
369
|
+
message=message_part,
|
|
370
|
+
severity="warning",
|
|
371
|
+
suggestion=f"Confidence: {confidence}",
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
except (ValueError, IndexError):
|
|
375
|
+
return None
|
|
376
|
+
|
|
377
|
+
def _parse_line_number(self, line_part: str) -> int | None:
|
|
378
|
+
"""Parse line number from text part.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
line_part: Part containing line number
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
Line number if valid, None otherwise
|
|
385
|
+
"""
|
|
386
|
+
try:
|
|
387
|
+
return int(line_part.strip())
|
|
388
|
+
except ValueError:
|
|
389
|
+
return None
|
|
390
|
+
|
|
391
|
+
def _extract_confidence_from_message(self, message_part: str) -> str:
|
|
392
|
+
"""Extract confidence percentage from message.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
message_part: Message containing confidence info
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
Confidence value or "unknown"
|
|
399
|
+
"""
|
|
400
|
+
if "(confidence:" not in message_part:
|
|
401
|
+
return "unknown"
|
|
402
|
+
|
|
403
|
+
conf_start = message_part.find("(confidence:") + len("(confidence:")
|
|
404
|
+
conf_end = message_part.find(")", conf_start)
|
|
405
|
+
if conf_end != -1:
|
|
406
|
+
return message_part[conf_start:conf_end].strip()
|
|
407
|
+
|
|
408
|
+
return "unknown"
|
|
409
|
+
|
|
410
|
+
def _get_check_type(self) -> QACheckType:
|
|
411
|
+
"""Return refactor check type."""
|
|
412
|
+
return QACheckType.REFACTOR
|
|
413
|
+
|
|
414
|
+
def _detect_package_directory(self) -> str:
|
|
415
|
+
"""Detect the package directory name from pyproject.toml.
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
Package directory name (e.g., 'crackerjack', 'session_mgmt_mcp')
|
|
419
|
+
"""
|
|
420
|
+
from contextlib import suppress
|
|
421
|
+
|
|
422
|
+
current_dir = Path.cwd()
|
|
423
|
+
|
|
424
|
+
# Try to read package name from pyproject.toml
|
|
425
|
+
pyproject_path = current_dir / "pyproject.toml"
|
|
426
|
+
if pyproject_path.exists():
|
|
427
|
+
with suppress(Exception):
|
|
428
|
+
import tomllib
|
|
429
|
+
|
|
430
|
+
with pyproject_path.open("rb") as f:
|
|
431
|
+
data = tomllib.load(f)
|
|
432
|
+
|
|
433
|
+
if "project" in data and "name" in data["project"]:
|
|
434
|
+
# Convert package name to directory name (replace - with _)
|
|
435
|
+
package_name = str(data["project"]["name"]).replace("-", "_")
|
|
436
|
+
|
|
437
|
+
# Verify directory exists
|
|
438
|
+
if (current_dir / package_name).exists():
|
|
439
|
+
return package_name
|
|
440
|
+
|
|
441
|
+
# Fallback to directory name if package dir exists
|
|
442
|
+
if (current_dir / current_dir.name).exists():
|
|
443
|
+
return current_dir.name
|
|
444
|
+
|
|
445
|
+
# Default fallback
|
|
446
|
+
return "src"
|
|
447
|
+
|
|
448
|
+
def get_default_config(self) -> QACheckConfig:
|
|
449
|
+
"""Get default configuration for Skylos adapter.
|
|
450
|
+
|
|
451
|
+
Returns:
|
|
452
|
+
QACheckConfig with sensible defaults
|
|
453
|
+
"""
|
|
454
|
+
from crackerjack.models.qa_config import QACheckConfig
|
|
455
|
+
|
|
456
|
+
# Dynamically detect package directory
|
|
457
|
+
package_dir = self._detect_package_directory()
|
|
458
|
+
|
|
459
|
+
return QACheckConfig(
|
|
460
|
+
check_id=MODULE_ID,
|
|
461
|
+
check_name=self.adapter_name,
|
|
462
|
+
check_type=QACheckType.REFACTOR, # Dead code detection is refactoring
|
|
463
|
+
enabled=True,
|
|
464
|
+
file_patterns=[
|
|
465
|
+
f"{package_dir}/**/*.py"
|
|
466
|
+
], # Dynamically detected package directory
|
|
467
|
+
exclude_patterns=[
|
|
468
|
+
"**/test_*.py",
|
|
469
|
+
"**/tests/**",
|
|
470
|
+
"**/.venv/**",
|
|
471
|
+
"**/venv/**",
|
|
472
|
+
"**/build/**",
|
|
473
|
+
"**/dist/**",
|
|
474
|
+
"**/__pycache__/**",
|
|
475
|
+
"**/.git/**",
|
|
476
|
+
"**/node_modules/**",
|
|
477
|
+
"**/.tox/**",
|
|
478
|
+
"**/.pytest_cache/**",
|
|
479
|
+
"**/htmlcov/**",
|
|
480
|
+
"**/.coverage*",
|
|
481
|
+
],
|
|
482
|
+
timeout_seconds=300,
|
|
483
|
+
parallel_safe=True,
|
|
484
|
+
stage="comprehensive", # Dead code detection in comprehensive stage
|
|
485
|
+
settings={
|
|
486
|
+
"confidence_threshold": 86,
|
|
487
|
+
"web_dashboard_port": 5090,
|
|
488
|
+
},
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
# ACB Registration (REQUIRED at module level)
|
|
493
|
+
with suppress(Exception):
|
|
494
|
+
depends.set(SkylosAdapter)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
> Crackerjack Docs: [Main](<../../../README.md>) | [Adapters](<../README.md>) | [SAST](<./README.md>)
|
|
2
|
+
|
|
3
|
+
# SAST Adapters
|
|
4
|
+
|
|
5
|
+
Static Application Security Testing tools for finding code vulnerabilities and security anti-patterns.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
SAST (Static Application Security Testing) adapters analyze source code for security vulnerabilities, insecure practices, and exploitable weaknesses. Unlike secret detection (gitleaks), SAST tools focus on application code quality and security patterns.
|
|
10
|
+
|
|
11
|
+
## Built-in Implementations
|
|
12
|
+
|
|
13
|
+
| Module | Description | Status | Recommended |
|
|
14
|
+
| ------ | ----------- | ------ | ----------- |
|
|
15
|
+
| `semgrep.py` | Multi-language static analysis with extensive rulesets | Stable | ✅ Primary |
|
|
16
|
+
| `bandit.py` | Python security linter with severity/confidence thresholds | Stable | Legacy |
|
|
17
|
+
| `pyscn.py` | Python security static analyzer | Experimental | ❌ No |
|
|
18
|
+
|
|
19
|
+
## Protocol Interface
|
|
20
|
+
|
|
21
|
+
All SAST adapters implement `SASTAdapterProtocol` defined in `_base.py`:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from crackerjack.adapters.sast import SASTAdapterProtocol, SemgrepAdapter
|
|
25
|
+
|
|
26
|
+
adapter: SASTAdapterProtocol = SemgrepAdapter()
|
|
27
|
+
await adapter.init()
|
|
28
|
+
result = await adapter.check(files=[Path("src/")])
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Recommended: Semgrep
|
|
32
|
+
|
|
33
|
+
Semgrep is the recommended SAST tool for Crackerjack due to:
|
|
34
|
+
|
|
35
|
+
- **Multi-language support** (Python, JS, Go, etc.)
|
|
36
|
+
- **Extensive rulesets** (`p/security-audit`, `p/python`, custom rules)
|
|
37
|
+
- **Active development** (r2c/semgrep community)
|
|
38
|
+
- **Better accuracy** than legacy Python-only tools
|
|
39
|
+
|
|
40
|
+
### Example: Semgrep
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from pathlib import Path
|
|
44
|
+
from crackerjack.adapters.sast.semgrep import SemgrepAdapter, SemgrepSettings
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def run_semgrep() -> None:
|
|
48
|
+
adapter = SemgrepAdapter(
|
|
49
|
+
settings=SemgrepSettings(
|
|
50
|
+
config="p/security-audit", # Comprehensive security ruleset
|
|
51
|
+
exclude_tests=True,
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
await adapter.init()
|
|
55
|
+
result = await adapter.check(files=[Path("src/")])
|
|
56
|
+
|
|
57
|
+
for issue in result.issues:
|
|
58
|
+
print(f"{issue.severity}: {issue.file_path}:{issue.line_number}")
|
|
59
|
+
print(f" {issue.message}")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Legacy: Bandit
|
|
63
|
+
|
|
64
|
+
Bandit remains available but is superseded by Semgrep:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from pathlib import Path
|
|
68
|
+
from crackerjack.adapters.sast.bandit import BanditAdapter, BanditSettings
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def run_bandit() -> None:
|
|
72
|
+
adapter = BanditAdapter(
|
|
73
|
+
settings=BanditSettings(
|
|
74
|
+
severity_level="high", # Reduce noise
|
|
75
|
+
confidence_level="high",
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
await adapter.init()
|
|
79
|
+
result = await adapter.check(files=[Path("src/")])
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Configuration
|
|
83
|
+
|
|
84
|
+
### SAST in Comprehensive Hooks
|
|
85
|
+
|
|
86
|
+
SAST tools run in the **comprehensive stage** (after fast hooks):
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
# crackerjack/config/hooks.py
|
|
90
|
+
HookDefinition(
|
|
91
|
+
name="semgrep",
|
|
92
|
+
command=[],
|
|
93
|
+
timeout=1200, # 20 minutes for thorough scanning
|
|
94
|
+
stage=HookStage.COMPREHENSIVE,
|
|
95
|
+
manual_stage=True,
|
|
96
|
+
security_level=SecurityLevel.CRITICAL,
|
|
97
|
+
use_precommit_legacy=False,
|
|
98
|
+
accepts_file_paths=True,
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### QACheckType
|
|
103
|
+
|
|
104
|
+
All SAST adapters use `QACheckType.SAST`:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from crackerjack.models.qa_results import QACheckType
|
|
108
|
+
|
|
109
|
+
# SAST = code vulnerability analysis
|
|
110
|
+
# SECURITY = secret leak prevention (gitleaks)
|
|
111
|
+
assert adapter._get_check_type() == QACheckType.SAST
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Comparison: SAST vs Security
|
|
115
|
+
|
|
116
|
+
| Category | Purpose | Tools | Stage | Scope |
|
|
117
|
+
|----------|---------|-------|-------|-------|
|
|
118
|
+
| **SAST** | Find code vulnerabilities | Semgrep, Bandit, Pyscn | Comprehensive | `**/*.py` |
|
|
119
|
+
| **Security** | Prevent credential leaks | Gitleaks | Fast | `**/*` |
|
|
120
|
+
|
|
121
|
+
## Notes
|
|
122
|
+
|
|
123
|
+
- **Semgrep is recommended** for new projects and comprehensive coverage
|
|
124
|
+
- Bandit legacy support maintained for existing workflows
|
|
125
|
+
- Pyscn is experimental and disabled by default
|
|
126
|
+
- SAST tools complement (not replace) secret detection
|
|
127
|
+
|
|
128
|
+
## Related
|
|
129
|
+
|
|
130
|
+
- [Security](<../security/README.md>) — Secret leak prevention with Gitleaks
|
|
131
|
+
- [Type](<../type/README.md>) — Type safety prevents classes of vulnerabilities
|
|
132
|
+
- [Lint](<../lint/README.md>) — Code quality supports secure coding practices
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""SAST (Static Application Security Testing) adapters for ACB QA framework.
|
|
2
|
+
|
|
3
|
+
This package contains adapters for static application security testing tools
|
|
4
|
+
that analyze source code for vulnerabilities, security anti-patterns, and
|
|
5
|
+
exploitable weaknesses.
|
|
6
|
+
|
|
7
|
+
Tools:
|
|
8
|
+
- Semgrep: Multi-language static analysis with custom rules (recommended)
|
|
9
|
+
- Bandit: Python-specific security linter (legacy)
|
|
10
|
+
- Pyscn: Python security static analyzer (experimental)
|
|
11
|
+
|
|
12
|
+
Protocol:
|
|
13
|
+
- SASTAdapterProtocol: Protocol interface for all SAST adapters
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from crackerjack.adapters.sast._base import SASTAdapter, SASTAdapterProtocol
|
|
17
|
+
from crackerjack.adapters.sast.bandit import BanditAdapter, BanditSettings
|
|
18
|
+
from crackerjack.adapters.sast.pyscn import PyscnAdapter, PyscnSettings
|
|
19
|
+
from crackerjack.adapters.sast.semgrep import SemgrepAdapter, SemgrepSettings
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
# Protocol
|
|
23
|
+
"SASTAdapter",
|
|
24
|
+
"SASTAdapterProtocol",
|
|
25
|
+
# Adapters
|
|
26
|
+
"BanditAdapter",
|
|
27
|
+
"BanditSettings",
|
|
28
|
+
"PyscnAdapter",
|
|
29
|
+
"PyscnSettings",
|
|
30
|
+
"SemgrepAdapter",
|
|
31
|
+
"SemgrepSettings",
|
|
32
|
+
]
|