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,405 @@
|
|
|
1
|
+
"""Pyscn adapter for ACB QA framework - Python security static analysis.
|
|
2
|
+
|
|
3
|
+
Pyscn is a Python static code analyzer for security that detects:
|
|
4
|
+
- Security vulnerabilities in Python code
|
|
5
|
+
- Common security anti-patterns
|
|
6
|
+
- Potential injection attacks
|
|
7
|
+
- Insecure cryptography usage
|
|
8
|
+
- Authentication and authorization issues
|
|
9
|
+
|
|
10
|
+
ACB Patterns:
|
|
11
|
+
- MODULE_ID and MODULE_STATUS at module level
|
|
12
|
+
- depends.set() registration after class definition
|
|
13
|
+
- Extends BaseToolAdapter for tool execution
|
|
14
|
+
- Async execution with JSON output parsing
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import logging
|
|
21
|
+
import typing as t
|
|
22
|
+
from contextlib import suppress
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from uuid import UUID
|
|
25
|
+
|
|
26
|
+
from acb.depends import depends
|
|
27
|
+
from pydantic import Field
|
|
28
|
+
|
|
29
|
+
from crackerjack.adapters._tool_adapter_base import (
|
|
30
|
+
BaseToolAdapter,
|
|
31
|
+
ToolAdapterSettings,
|
|
32
|
+
ToolExecutionResult,
|
|
33
|
+
ToolIssue,
|
|
34
|
+
)
|
|
35
|
+
from crackerjack.models.qa_results import QACheckType
|
|
36
|
+
|
|
37
|
+
if t.TYPE_CHECKING:
|
|
38
|
+
from crackerjack.models.qa_config import QACheckConfig
|
|
39
|
+
|
|
40
|
+
# ACB Module Registration (REQUIRED)
|
|
41
|
+
MODULE_ID = UUID(
|
|
42
|
+
"01937d86-6b2c-7d3e-8f4a-b5c6d7e8f9a3"
|
|
43
|
+
) # Static UUID7 for reproducible module identity
|
|
44
|
+
MODULE_STATUS = "experimental"
|
|
45
|
+
|
|
46
|
+
# Module-level logger for structured logging
|
|
47
|
+
logger = logging.getLogger(__name__)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class PyscnSettings(ToolAdapterSettings):
|
|
51
|
+
"""Settings for Pyscn adapter."""
|
|
52
|
+
|
|
53
|
+
tool_name: str = "pyscn"
|
|
54
|
+
use_json_output: bool = True
|
|
55
|
+
severity_threshold: str = "low" # low, medium, high, critical
|
|
56
|
+
confidence_threshold: str = "low" # low, medium, high
|
|
57
|
+
exclude_rules: list[str] = Field(default_factory=list)
|
|
58
|
+
include_rules: list[str] = Field(default_factory=list)
|
|
59
|
+
recursive: bool = True
|
|
60
|
+
max_depth: int | None = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class PyscnAdapter(BaseToolAdapter):
|
|
64
|
+
"""Adapter for Pyscn - Python security static analyzer.
|
|
65
|
+
|
|
66
|
+
Performs static security analysis to detect vulnerabilities in Python code:
|
|
67
|
+
- Security anti-patterns and insecure practices
|
|
68
|
+
- Potential injection vulnerabilities
|
|
69
|
+
- Weak cryptography implementations
|
|
70
|
+
- Authentication/authorization flaws
|
|
71
|
+
- Privilege escalation risks
|
|
72
|
+
- Data exposure issues
|
|
73
|
+
|
|
74
|
+
Features:
|
|
75
|
+
- JSON output for structured issue reporting
|
|
76
|
+
- Configurable severity and confidence thresholds
|
|
77
|
+
- Rule inclusion/exclusion filtering
|
|
78
|
+
- Recursive directory scanning with depth control
|
|
79
|
+
|
|
80
|
+
Example:
|
|
81
|
+
```python
|
|
82
|
+
settings = PyscnSettings(
|
|
83
|
+
severity_threshold="medium",
|
|
84
|
+
confidence_threshold="medium",
|
|
85
|
+
exclude_rules=["SCN001"],
|
|
86
|
+
recursive=True,
|
|
87
|
+
)
|
|
88
|
+
adapter = PyscnAdapter(settings=settings)
|
|
89
|
+
await adapter.init()
|
|
90
|
+
result = await adapter.check(files=[Path("src/")])
|
|
91
|
+
```
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
settings: PyscnSettings | None = None
|
|
95
|
+
|
|
96
|
+
def __init__(self, settings: PyscnSettings | None = None) -> None:
|
|
97
|
+
"""Initialize Pyscn adapter.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
settings: Optional settings override
|
|
101
|
+
"""
|
|
102
|
+
super().__init__(settings=settings)
|
|
103
|
+
logger.debug(
|
|
104
|
+
"PyscnAdapter initialized", extra={"has_settings": settings is not None}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
async def init(self) -> None:
|
|
108
|
+
"""Initialize adapter with default settings."""
|
|
109
|
+
if not self.settings:
|
|
110
|
+
self.settings = PyscnSettings()
|
|
111
|
+
logger.info("Using default PyscnSettings")
|
|
112
|
+
await super().init()
|
|
113
|
+
logger.debug(
|
|
114
|
+
"PyscnAdapter initialization complete",
|
|
115
|
+
extra={
|
|
116
|
+
"severity_threshold": self.settings.severity_threshold,
|
|
117
|
+
"confidence_threshold": self.settings.confidence_threshold,
|
|
118
|
+
"recursive": self.settings.recursive,
|
|
119
|
+
},
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def adapter_name(self) -> str:
|
|
124
|
+
"""Human-readable adapter name."""
|
|
125
|
+
return "Pyscn (Security Analysis)"
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def module_id(self) -> UUID:
|
|
129
|
+
"""Reference to module-level MODULE_ID."""
|
|
130
|
+
return MODULE_ID
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def tool_name(self) -> str:
|
|
134
|
+
"""CLI tool name."""
|
|
135
|
+
return "pyscn"
|
|
136
|
+
|
|
137
|
+
def build_command(
|
|
138
|
+
self,
|
|
139
|
+
files: list[Path],
|
|
140
|
+
config: QACheckConfig | None = None,
|
|
141
|
+
) -> list[str]:
|
|
142
|
+
"""Build Pyscn command.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
files: Files/directories to scan
|
|
146
|
+
config: Optional configuration override
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Command as list of strings
|
|
150
|
+
"""
|
|
151
|
+
if not self.settings:
|
|
152
|
+
raise RuntimeError("Settings not initialized")
|
|
153
|
+
|
|
154
|
+
cmd = [self.tool_name]
|
|
155
|
+
|
|
156
|
+
# JSON output
|
|
157
|
+
if self.settings.use_json_output:
|
|
158
|
+
cmd.extend(["--format", "json"])
|
|
159
|
+
|
|
160
|
+
# Severity threshold
|
|
161
|
+
cmd.extend(["--severity", self.settings.severity_threshold])
|
|
162
|
+
|
|
163
|
+
# Confidence threshold
|
|
164
|
+
cmd.extend(["--confidence", self.settings.confidence_threshold])
|
|
165
|
+
|
|
166
|
+
# Exclude rules
|
|
167
|
+
for rule in self.settings.exclude_rules:
|
|
168
|
+
cmd.extend(["--exclude", rule])
|
|
169
|
+
|
|
170
|
+
# Include rules
|
|
171
|
+
for rule in self.settings.include_rules:
|
|
172
|
+
cmd.extend(["--include", rule])
|
|
173
|
+
|
|
174
|
+
# Recursive scanning
|
|
175
|
+
if self.settings.recursive:
|
|
176
|
+
cmd.append("--recursive")
|
|
177
|
+
|
|
178
|
+
# Max depth
|
|
179
|
+
if self.settings.max_depth is not None:
|
|
180
|
+
cmd.extend(["--max-depth", str(self.settings.max_depth)])
|
|
181
|
+
|
|
182
|
+
# Add targets
|
|
183
|
+
cmd.extend([str(f) for f in files])
|
|
184
|
+
|
|
185
|
+
logger.info(
|
|
186
|
+
"Built Pyscn command",
|
|
187
|
+
extra={
|
|
188
|
+
"file_count": len(files),
|
|
189
|
+
"severity_threshold": self.settings.severity_threshold,
|
|
190
|
+
"confidence_threshold": self.settings.confidence_threshold,
|
|
191
|
+
"recursive": self.settings.recursive,
|
|
192
|
+
},
|
|
193
|
+
)
|
|
194
|
+
return cmd
|
|
195
|
+
|
|
196
|
+
async def parse_output(
|
|
197
|
+
self,
|
|
198
|
+
result: ToolExecutionResult,
|
|
199
|
+
) -> list[ToolIssue]:
|
|
200
|
+
"""Parse Pyscn JSON output into standardized issues.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
result: Raw execution result from Pyscn
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
List of parsed issues
|
|
207
|
+
"""
|
|
208
|
+
if not result.raw_output:
|
|
209
|
+
logger.debug("No output to parse")
|
|
210
|
+
return []
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
data = json.loads(result.raw_output)
|
|
214
|
+
logger.debug(
|
|
215
|
+
"Parsed Pyscn JSON output",
|
|
216
|
+
extra={"issues_count": len(data.get("issues", []))},
|
|
217
|
+
)
|
|
218
|
+
except json.JSONDecodeError as e:
|
|
219
|
+
logger.debug(
|
|
220
|
+
"JSON parse failed, falling back to text parsing",
|
|
221
|
+
extra={"error": str(e), "output_preview": result.raw_output[:200]},
|
|
222
|
+
)
|
|
223
|
+
return self._parse_text_output(result.raw_output)
|
|
224
|
+
|
|
225
|
+
issues = []
|
|
226
|
+
|
|
227
|
+
# Pyscn JSON format:
|
|
228
|
+
# {
|
|
229
|
+
# "issues": [
|
|
230
|
+
# {
|
|
231
|
+
# "file": "path/to/file.py",
|
|
232
|
+
# "line": 42,
|
|
233
|
+
# "column": 10,
|
|
234
|
+
# "message": "Potential security vulnerability...",
|
|
235
|
+
# "severity": "high",
|
|
236
|
+
# "confidence": "medium",
|
|
237
|
+
# "rule_id": "SCN123",
|
|
238
|
+
# "rule_name": "insecure_crypto"
|
|
239
|
+
# }
|
|
240
|
+
# ]
|
|
241
|
+
# }
|
|
242
|
+
|
|
243
|
+
for issue_data in data.get("issues", []):
|
|
244
|
+
issue = ToolIssue(
|
|
245
|
+
file_path=Path(issue_data.get("file", "")),
|
|
246
|
+
line_number=issue_data.get("line"),
|
|
247
|
+
column_number=issue_data.get("column"),
|
|
248
|
+
message=issue_data.get("message", ""),
|
|
249
|
+
code=issue_data.get("rule_id"),
|
|
250
|
+
severity=issue_data.get("severity", "error"),
|
|
251
|
+
)
|
|
252
|
+
issues.append(issue)
|
|
253
|
+
|
|
254
|
+
logger.info(
|
|
255
|
+
"Parsed Pyscn output",
|
|
256
|
+
extra={
|
|
257
|
+
"total_issues": len(issues),
|
|
258
|
+
"errors": sum(1 for i in issues if i.severity == "error"),
|
|
259
|
+
"warnings": sum(1 for i in issues if i.severity == "warning"),
|
|
260
|
+
"files_affected": len({str(i.file_path) for i in issues}),
|
|
261
|
+
},
|
|
262
|
+
)
|
|
263
|
+
return issues
|
|
264
|
+
|
|
265
|
+
def _parse_text_output(self, output: str) -> list[ToolIssue]:
|
|
266
|
+
"""Parse Pyscn text output (fallback).
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
output: Text output from Pyscn
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
List of ToolIssue objects
|
|
273
|
+
"""
|
|
274
|
+
issues = []
|
|
275
|
+
lines = output.strip().split("\n")
|
|
276
|
+
|
|
277
|
+
for line in lines:
|
|
278
|
+
# Pyscn text format: "file.py:10:5: error: Potential security vulnerability..."
|
|
279
|
+
if ":" not in line:
|
|
280
|
+
continue
|
|
281
|
+
|
|
282
|
+
issue = self._parse_text_line(line)
|
|
283
|
+
if issue:
|
|
284
|
+
issues.append(issue)
|
|
285
|
+
|
|
286
|
+
logger.info(
|
|
287
|
+
"Parsed Pyscn text output (fallback)",
|
|
288
|
+
extra={
|
|
289
|
+
"total_issues": len(issues),
|
|
290
|
+
"files_with_issues": len({str(i.file_path) for i in issues}),
|
|
291
|
+
},
|
|
292
|
+
)
|
|
293
|
+
return issues
|
|
294
|
+
|
|
295
|
+
def _parse_text_line(self, line: str) -> ToolIssue | None:
|
|
296
|
+
"""Parse a single text output line.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
line: Line of text output
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
ToolIssue if parsing successful, None otherwise
|
|
303
|
+
"""
|
|
304
|
+
parts = line.split(":", maxsplit=4)
|
|
305
|
+
if len(parts) < 4:
|
|
306
|
+
return None
|
|
307
|
+
|
|
308
|
+
try:
|
|
309
|
+
file_path = Path(parts[0].strip())
|
|
310
|
+
line_number = int(parts[1].strip())
|
|
311
|
+
column_number = (
|
|
312
|
+
int(parts[2].strip()) if parts[2].strip().isdigit() else None
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
severity_and_message = parts[3].strip() if len(parts) > 3 else ""
|
|
316
|
+
message = parts[4].strip() if len(parts) > 4 else ""
|
|
317
|
+
|
|
318
|
+
severity = self._parse_severity(severity_and_message, message)
|
|
319
|
+
message = self._extract_message(severity_and_message, message, severity)
|
|
320
|
+
|
|
321
|
+
return ToolIssue(
|
|
322
|
+
file_path=file_path,
|
|
323
|
+
line_number=line_number,
|
|
324
|
+
column_number=column_number,
|
|
325
|
+
message=message,
|
|
326
|
+
severity=severity,
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
except (ValueError, IndexError):
|
|
330
|
+
return None
|
|
331
|
+
|
|
332
|
+
def _parse_severity(self, severity_and_message: str, message: str) -> str:
|
|
333
|
+
"""Parse severity from text line.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
severity_and_message: Part containing severity
|
|
337
|
+
message: Explicit message if present
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
Severity level (error or warning)
|
|
341
|
+
"""
|
|
342
|
+
if severity_and_message.lower().startswith("warning"):
|
|
343
|
+
return "warning"
|
|
344
|
+
return "error"
|
|
345
|
+
|
|
346
|
+
def _extract_message(
|
|
347
|
+
self, severity_and_message: str, message: str, severity: str
|
|
348
|
+
) -> str:
|
|
349
|
+
"""Extract message from text line.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
severity_and_message: Part containing severity and possibly message
|
|
353
|
+
message: Explicit message if present
|
|
354
|
+
severity: Parsed severity level
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
Extracted message
|
|
358
|
+
"""
|
|
359
|
+
if message:
|
|
360
|
+
return message
|
|
361
|
+
|
|
362
|
+
# Extract from severity_and_message
|
|
363
|
+
if severity_and_message.lower().startswith(severity):
|
|
364
|
+
return severity_and_message[len(severity) :].strip()
|
|
365
|
+
|
|
366
|
+
return severity_and_message
|
|
367
|
+
|
|
368
|
+
def _get_check_type(self) -> QACheckType:
|
|
369
|
+
"""Return SAST check type."""
|
|
370
|
+
return QACheckType.SAST
|
|
371
|
+
|
|
372
|
+
def get_default_config(self) -> QACheckConfig:
|
|
373
|
+
"""Get default configuration for Pyscn adapter.
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
QACheckConfig with sensible defaults
|
|
377
|
+
"""
|
|
378
|
+
from crackerjack.models.qa_config import QACheckConfig
|
|
379
|
+
|
|
380
|
+
return QACheckConfig(
|
|
381
|
+
check_id=MODULE_ID,
|
|
382
|
+
check_name=self.adapter_name,
|
|
383
|
+
check_type=QACheckType.SAST,
|
|
384
|
+
enabled=False, # Disabled by default as experimental
|
|
385
|
+
file_patterns=["**/*.py"],
|
|
386
|
+
exclude_patterns=[
|
|
387
|
+
"**/.venv/**",
|
|
388
|
+
"**/venv/**",
|
|
389
|
+
"**/build/**",
|
|
390
|
+
"**/dist/**",
|
|
391
|
+
],
|
|
392
|
+
timeout_seconds=120, # Security scanning can be slower
|
|
393
|
+
parallel_safe=True,
|
|
394
|
+
stage="comprehensive", # Security checks in comprehensive stage
|
|
395
|
+
settings={
|
|
396
|
+
"severity_threshold": "medium",
|
|
397
|
+
"confidence_threshold": "medium",
|
|
398
|
+
"recursive": True,
|
|
399
|
+
},
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# ACB Registration (REQUIRED at module level)
|
|
404
|
+
with suppress(Exception):
|
|
405
|
+
depends.set(PyscnAdapter)
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Semgrep adapter for ACB QA framework - Modern security scanning.
|
|
2
|
+
|
|
3
|
+
Semgrep is a fast, open-source, static analysis tool for finding bugs,
|
|
4
|
+
enforcing code standards, and finding security vulnerabilities.
|
|
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 JSON 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-4f2a-7b3c-8d9e-f3b4d3c2b1a1"
|
|
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 SemgrepSettings(ToolAdapterSettings):
|
|
46
|
+
"""Settings for Semgrep adapter."""
|
|
47
|
+
|
|
48
|
+
tool_name: str = "semgrep"
|
|
49
|
+
use_json_output: bool = True
|
|
50
|
+
config: str = "p/python" # Default ruleset
|
|
51
|
+
exclude_tests: bool = True
|
|
52
|
+
timeout_seconds: int = 1200
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class SemgrepAdapter(BaseToolAdapter):
|
|
56
|
+
"""Adapter for Semgrep - Modern static analysis."""
|
|
57
|
+
|
|
58
|
+
settings: SemgrepSettings | None = None
|
|
59
|
+
|
|
60
|
+
def __init__(self, settings: SemgrepSettings | None = None) -> None:
|
|
61
|
+
"""Initialize Semgrep adapter."""
|
|
62
|
+
super().__init__(settings=settings)
|
|
63
|
+
logger.debug(
|
|
64
|
+
"SemgrepAdapter initialized", extra={"has_settings": settings is not None}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
async def init(self) -> None:
|
|
68
|
+
"""Initialize adapter with default settings."""
|
|
69
|
+
if not self.settings:
|
|
70
|
+
self.settings = SemgrepSettings()
|
|
71
|
+
logger.info("Using default SemgrepSettings")
|
|
72
|
+
await super().init()
|
|
73
|
+
logger.debug(
|
|
74
|
+
"SemgrepAdapter initialization complete",
|
|
75
|
+
extra={
|
|
76
|
+
"config": self.settings.config,
|
|
77
|
+
"exclude_tests": self.settings.exclude_tests,
|
|
78
|
+
},
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def adapter_name(self) -> str:
|
|
83
|
+
"""Human-readable adapter name."""
|
|
84
|
+
return "Semgrep (Security)"
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def module_id(self) -> UUID:
|
|
88
|
+
"""Reference to module-level MODULE_ID."""
|
|
89
|
+
return MODULE_ID
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def tool_name(self) -> str:
|
|
93
|
+
"""CLI tool name."""
|
|
94
|
+
return "semgrep"
|
|
95
|
+
|
|
96
|
+
def build_command(
|
|
97
|
+
self,
|
|
98
|
+
files: list[Path],
|
|
99
|
+
config: QACheckConfig | None = None,
|
|
100
|
+
) -> list[str]:
|
|
101
|
+
"""Build Semgrep command."""
|
|
102
|
+
if not self.settings:
|
|
103
|
+
raise RuntimeError("Settings not initialized")
|
|
104
|
+
|
|
105
|
+
cmd = [self.tool_name, "scan"]
|
|
106
|
+
|
|
107
|
+
# JSON output
|
|
108
|
+
if self.settings.use_json_output:
|
|
109
|
+
cmd.append("--json")
|
|
110
|
+
|
|
111
|
+
# Config
|
|
112
|
+
cmd.extend(["--config", self.settings.config])
|
|
113
|
+
|
|
114
|
+
# Add targets
|
|
115
|
+
cmd.extend([str(f) for f in files])
|
|
116
|
+
|
|
117
|
+
logger.info(
|
|
118
|
+
"Built Semgrep command",
|
|
119
|
+
extra={
|
|
120
|
+
"file_count": len(files),
|
|
121
|
+
"config": self.settings.config,
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
return cmd
|
|
125
|
+
|
|
126
|
+
async def parse_output(
|
|
127
|
+
self,
|
|
128
|
+
result: ToolExecutionResult,
|
|
129
|
+
) -> list[ToolIssue]:
|
|
130
|
+
"""Parse Semgrep JSON output into standardized issues."""
|
|
131
|
+
if not result.raw_output:
|
|
132
|
+
logger.debug("No output to parse")
|
|
133
|
+
return []
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
data = json.loads(result.raw_output)
|
|
137
|
+
logger.debug(
|
|
138
|
+
"Parsed Semgrep JSON output",
|
|
139
|
+
extra={"results_count": len(data.get("results", []))},
|
|
140
|
+
)
|
|
141
|
+
except json.JSONDecodeError as e:
|
|
142
|
+
logger.debug(
|
|
143
|
+
"JSON parse failed, falling back to text parsing",
|
|
144
|
+
extra={"error": str(e), "output_preview": result.raw_output[:200]},
|
|
145
|
+
)
|
|
146
|
+
return [] # No text parsing for semgrep for now
|
|
147
|
+
|
|
148
|
+
issues = []
|
|
149
|
+
for item in data.get("results", []):
|
|
150
|
+
file_path = Path(item.get("path", ""))
|
|
151
|
+
start_line = item.get("start", {}).get("line")
|
|
152
|
+
message = item.get("extra", {}).get("message", "")
|
|
153
|
+
code = item.get("check_id")
|
|
154
|
+
severity = item.get("extra", {}).get("severity", "WARNING").lower()
|
|
155
|
+
|
|
156
|
+
issue = ToolIssue(
|
|
157
|
+
file_path=file_path,
|
|
158
|
+
line_number=start_line,
|
|
159
|
+
message=message,
|
|
160
|
+
code=code,
|
|
161
|
+
severity=severity,
|
|
162
|
+
)
|
|
163
|
+
issues.append(issue)
|
|
164
|
+
|
|
165
|
+
logger.info(
|
|
166
|
+
"Parsed Semgrep output",
|
|
167
|
+
extra={
|
|
168
|
+
"total_issues": len(issues),
|
|
169
|
+
"files_affected": len({str(i.file_path) for i in issues}),
|
|
170
|
+
},
|
|
171
|
+
)
|
|
172
|
+
return issues
|
|
173
|
+
|
|
174
|
+
def _get_check_type(self) -> QACheckType:
|
|
175
|
+
"""Return SAST check type."""
|
|
176
|
+
return QACheckType.SAST
|
|
177
|
+
|
|
178
|
+
def _detect_package_directory(self) -> str:
|
|
179
|
+
"""Detect the package directory name from pyproject.toml.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Package directory name (e.g., 'crackerjack', 'session_mgmt_mcp')
|
|
183
|
+
"""
|
|
184
|
+
from contextlib import suppress
|
|
185
|
+
|
|
186
|
+
current_dir = Path.cwd()
|
|
187
|
+
|
|
188
|
+
# Try to read package name from pyproject.toml
|
|
189
|
+
pyproject_path = current_dir / "pyproject.toml"
|
|
190
|
+
if pyproject_path.exists():
|
|
191
|
+
with suppress(Exception):
|
|
192
|
+
import tomllib
|
|
193
|
+
|
|
194
|
+
with pyproject_path.open("rb") as f:
|
|
195
|
+
data = tomllib.load(f)
|
|
196
|
+
|
|
197
|
+
if "project" in data and "name" in data["project"]:
|
|
198
|
+
# Convert package name to directory name (replace - with _)
|
|
199
|
+
package_name = str(data["project"]["name"]).replace("-", "_")
|
|
200
|
+
|
|
201
|
+
# Verify directory exists
|
|
202
|
+
if (current_dir / package_name).exists():
|
|
203
|
+
return package_name
|
|
204
|
+
|
|
205
|
+
# Fallback to directory name if package dir exists
|
|
206
|
+
if (current_dir / current_dir.name).exists():
|
|
207
|
+
return current_dir.name
|
|
208
|
+
|
|
209
|
+
# Default fallback
|
|
210
|
+
return "src"
|
|
211
|
+
|
|
212
|
+
def get_default_config(self) -> QACheckConfig:
|
|
213
|
+
"""Get default configuration for Semgrep adapter."""
|
|
214
|
+
from crackerjack.models.qa_config import QACheckConfig
|
|
215
|
+
|
|
216
|
+
# Dynamically detect package directory
|
|
217
|
+
package_dir = self._detect_package_directory()
|
|
218
|
+
|
|
219
|
+
return QACheckConfig(
|
|
220
|
+
check_id=MODULE_ID,
|
|
221
|
+
check_name=self.adapter_name,
|
|
222
|
+
check_type=QACheckType.SAST,
|
|
223
|
+
enabled=True,
|
|
224
|
+
file_patterns=[f"{package_dir}/**/*.py"],
|
|
225
|
+
exclude_patterns=[
|
|
226
|
+
"**/test_*.py",
|
|
227
|
+
"**/tests/**",
|
|
228
|
+
],
|
|
229
|
+
timeout_seconds=1200,
|
|
230
|
+
parallel_safe=True,
|
|
231
|
+
stage="comprehensive",
|
|
232
|
+
settings={
|
|
233
|
+
"config": "p/python",
|
|
234
|
+
"exclude_tests": True,
|
|
235
|
+
},
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# ACB Registration (REQUIRED at module level)
|
|
240
|
+
with suppress(Exception):
|
|
241
|
+
depends.set(SemgrepAdapter)
|