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,312 @@
|
|
|
1
|
+
# Orchestration Cache
|
|
2
|
+
|
|
3
|
+
> Crackerjack Docs: [Main](<../../../README.md>) | [CLAUDE.md](../../../docs/guides/CLAUDE.md) | [Orchestration](<../README.md>) | [Cache](<./README.md>)
|
|
4
|
+
|
|
5
|
+
High-performance caching infrastructure for hook execution results with content-based invalidation.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The orchestration cache provides intelligent result caching for pre-commit hooks and QA checks. By using content-based cache keys (file hashes + configuration), it achieves **~70% cache hit rates** in typical workflows, dramatically reducing execution time for unchanged code.
|
|
10
|
+
|
|
11
|
+
## Key Components
|
|
12
|
+
|
|
13
|
+
### tool_proxy_cache.py - Content-Based Caching
|
|
14
|
+
|
|
15
|
+
**ToolProxyCacheAdapter** - Main cache implementation:
|
|
16
|
+
|
|
17
|
+
- Content-based cache keys using SHA256 file hashing
|
|
18
|
+
- Configurable TTL (time-to-live) per result
|
|
19
|
+
- Automatic invalidation on file content changes
|
|
20
|
+
- Integration with existing tool_proxy infrastructure
|
|
21
|
+
- Compression support for large results
|
|
22
|
+
- In-memory storage with optional disk persistence
|
|
23
|
+
|
|
24
|
+
**Cache Key Format:**
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
{hook_name}:{config_hash}:{content_hash}
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
ruff-format:a3f21b8c:7d9e4a2f
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**ToolProxyCacheSettings** - Configuration model:
|
|
34
|
+
|
|
35
|
+
- `default_ttl` - Default time-to-live in seconds (default: 3600)
|
|
36
|
+
- `max_cache_size_mb` - Maximum cache size in MB (default: 100)
|
|
37
|
+
- `enable_compression` - Enable result compression (default: True)
|
|
38
|
+
|
|
39
|
+
### memory_cache.py - In-Memory Cache
|
|
40
|
+
|
|
41
|
+
**MemoryCacheAdapter** - Simple LRU cache:
|
|
42
|
+
|
|
43
|
+
- Fast in-memory storage
|
|
44
|
+
- Least Recently Used (LRU) eviction
|
|
45
|
+
- No persistence (ephemeral)
|
|
46
|
+
- Useful for testing and development
|
|
47
|
+
|
|
48
|
+
## Usage Examples
|
|
49
|
+
|
|
50
|
+
### Basic Caching
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from pathlib import Path
|
|
54
|
+
from crackerjack.orchestration.cache import ToolProxyCacheAdapter
|
|
55
|
+
from crackerjack.config.hooks import HookDefinition
|
|
56
|
+
from crackerjack.models.task import HookResult
|
|
57
|
+
|
|
58
|
+
# Initialize cache
|
|
59
|
+
cache = ToolProxyCacheAdapter()
|
|
60
|
+
await cache.init()
|
|
61
|
+
|
|
62
|
+
# Define hook and files
|
|
63
|
+
hook = HookDefinition(name="ruff-format", command=["ruff", "format"])
|
|
64
|
+
files = [Path("src/main.py"), Path("src/utils.py")]
|
|
65
|
+
|
|
66
|
+
# Compute cache key
|
|
67
|
+
cache_key = cache.compute_key(hook, files)
|
|
68
|
+
|
|
69
|
+
# Check cache before execution
|
|
70
|
+
cached_result = await cache.get(cache_key)
|
|
71
|
+
if cached_result:
|
|
72
|
+
print(f"Cache hit! Skipping {hook.name}")
|
|
73
|
+
return cached_result
|
|
74
|
+
|
|
75
|
+
# Execute hook and cache result
|
|
76
|
+
result = await execute_hook(hook)
|
|
77
|
+
await cache.set(cache_key, result, ttl=3600)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Custom TTL Configuration
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from crackerjack.orchestration.cache import (
|
|
84
|
+
ToolProxyCacheAdapter,
|
|
85
|
+
ToolProxyCacheSettings,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Configure cache with custom settings
|
|
89
|
+
settings = ToolProxyCacheSettings(
|
|
90
|
+
default_ttl=7200, # 2 hours
|
|
91
|
+
max_cache_size_mb=250,
|
|
92
|
+
enable_compression=True,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
cache = ToolProxyCacheAdapter(settings=settings)
|
|
96
|
+
await cache.init()
|
|
97
|
+
|
|
98
|
+
# Cache critical hook results longer
|
|
99
|
+
security_result = await run_security_check()
|
|
100
|
+
await cache.set(cache_key, security_result, ttl=86400) # 24 hours
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Cache Statistics
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
# Get cache performance metrics
|
|
107
|
+
stats = await cache.get_stats()
|
|
108
|
+
|
|
109
|
+
print(f"Total entries: {stats['total_entries']}")
|
|
110
|
+
print(f"Active entries: {stats['active_entries']}")
|
|
111
|
+
print(f"Expired entries: {stats['expired_entries']}")
|
|
112
|
+
print(f"Cache directory: {stats['cache_dir']}")
|
|
113
|
+
|
|
114
|
+
# Calculate hit rate
|
|
115
|
+
hit_rate = stats["active_entries"] / stats["total_entries"]
|
|
116
|
+
print(f"Cache hit rate: {hit_rate:.1%}")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Manual Cache Invalidation
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# Clear all cached results
|
|
123
|
+
await cache.clear()
|
|
124
|
+
|
|
125
|
+
# Clear specific hook results
|
|
126
|
+
for key in cache._cache.keys():
|
|
127
|
+
if key.startswith("ruff-format:"):
|
|
128
|
+
del cache._cache[key]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Cache Key Computation
|
|
132
|
+
|
|
133
|
+
The cache key is computed from three components:
|
|
134
|
+
|
|
135
|
+
1. **Hook Name**: Identifies the tool (ruff-format, pyright, etc.)
|
|
136
|
+
1. **Config Hash**: SHA256 of hook configuration (command, timeout, stage, security level)
|
|
137
|
+
1. **Content Hash**: SHA256 of all input file contents
|
|
138
|
+
|
|
139
|
+
**Example computation:**
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
# Hook configuration
|
|
143
|
+
config_data = {
|
|
144
|
+
"name": "ruff-format",
|
|
145
|
+
"command": ["ruff", "format", "--check"],
|
|
146
|
+
"timeout": 60,
|
|
147
|
+
"stage": "fast",
|
|
148
|
+
"security_level": "low",
|
|
149
|
+
}
|
|
150
|
+
config_hash = hashlib.sha256(
|
|
151
|
+
json.dumps(config_data, sort_keys=True).encode()
|
|
152
|
+
).hexdigest()[:16]
|
|
153
|
+
|
|
154
|
+
# File contents
|
|
155
|
+
content_hasher = hashlib.sha256()
|
|
156
|
+
for file_path in sorted(files):
|
|
157
|
+
content_hasher.update(file_path.read_bytes())
|
|
158
|
+
content_hash = content_hasher.hexdigest()[:16]
|
|
159
|
+
|
|
160
|
+
# Final key
|
|
161
|
+
cache_key = f"{hook.name}:{config_hash}:{content_hash}"
|
|
162
|
+
# Result: "ruff-format:a3f21b8c7d9e4a2f:7b8c3e1d9f2a6b4c"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Cache Invalidation Strategy
|
|
166
|
+
|
|
167
|
+
Cache entries are automatically invalidated when:
|
|
168
|
+
|
|
169
|
+
1. **TTL Expiration**: Entry exceeds configured time-to-live
|
|
170
|
+
1. **Content Changes**: File content hash changes
|
|
171
|
+
1. **Configuration Changes**: Hook configuration modified
|
|
172
|
+
1. **Manual Invalidation**: Explicit `clear()` call
|
|
173
|
+
|
|
174
|
+
**Automatic Invalidation:**
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
# First execution - cache miss
|
|
178
|
+
files = [Path("src/main.py")]
|
|
179
|
+
key = cache.compute_key(hook, files)
|
|
180
|
+
result1 = await cache.get(key) # None
|
|
181
|
+
|
|
182
|
+
await execute_and_cache(hook, files, key)
|
|
183
|
+
|
|
184
|
+
# Second execution - cache hit (same content)
|
|
185
|
+
result2 = await cache.get(key) # HookResult (cached)
|
|
186
|
+
|
|
187
|
+
# Modify file
|
|
188
|
+
Path("src/main.py").write_text("# Modified")
|
|
189
|
+
|
|
190
|
+
# Third execution - cache miss (content changed)
|
|
191
|
+
key_new = cache.compute_key(hook, files) # Different content_hash
|
|
192
|
+
result3 = await cache.get(key_new) # None
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Configuration
|
|
196
|
+
|
|
197
|
+
### Via Settings YAML
|
|
198
|
+
|
|
199
|
+
```yaml
|
|
200
|
+
# settings/crackerjack.yaml
|
|
201
|
+
cache:
|
|
202
|
+
default_ttl: 3600
|
|
203
|
+
max_cache_size_mb: 100
|
|
204
|
+
enable_compression: true
|
|
205
|
+
cache_dir: .crackerjack/cache
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Via Code
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from pathlib import Path
|
|
212
|
+
from crackerjack.orchestration.cache import (
|
|
213
|
+
ToolProxyCacheAdapter,
|
|
214
|
+
ToolProxyCacheSettings,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
settings = ToolProxyCacheSettings(
|
|
218
|
+
default_ttl=3600,
|
|
219
|
+
max_cache_size_mb=100,
|
|
220
|
+
enable_compression=True,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
cache = ToolProxyCacheAdapter(
|
|
224
|
+
settings=settings,
|
|
225
|
+
cache_dir=Path.cwd() / ".crackerjack" / "cache",
|
|
226
|
+
)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Performance Impact
|
|
230
|
+
|
|
231
|
+
**Before Caching:**
|
|
232
|
+
|
|
233
|
+
- Every hook executes on every run
|
|
234
|
+
- ~30-60s for comprehensive hooks
|
|
235
|
+
- Redundant work on unchanged files
|
|
236
|
+
|
|
237
|
+
**With 70% Cache Hit Rate:**
|
|
238
|
+
|
|
239
|
+
- Only 30% of hooks execute
|
|
240
|
+
- ~10-20s for comprehensive hooks
|
|
241
|
+
- 2-3x faster workflow execution
|
|
242
|
+
|
|
243
|
+
**Example Workflow:**
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
Without cache: 45s total (15 hooks × 3s each)
|
|
247
|
+
With cache: 15s total (5 misses × 3s each)
|
|
248
|
+
Speedup: 3x faster
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Best Practices
|
|
252
|
+
|
|
253
|
+
1. **Use Content-Based Keys**: Always compute keys from file content, not timestamps
|
|
254
|
+
1. **Configure TTL Appropriately**:
|
|
255
|
+
- Fast hooks: 1 hour (3600s)
|
|
256
|
+
- Comprehensive hooks: 2-4 hours (7200-14400s)
|
|
257
|
+
- Security checks: 24 hours (86400s)
|
|
258
|
+
1. **Monitor Hit Rates**: Aim for 60-80% hit rates in typical development
|
|
259
|
+
1. **Clear Stale Entries**: Periodically clear expired entries
|
|
260
|
+
1. **Size Management**: Monitor cache size, adjust `max_cache_size_mb` if needed
|
|
261
|
+
1. **Compression**: Enable for large results (security scans, type checking)
|
|
262
|
+
|
|
263
|
+
## Architecture Integration
|
|
264
|
+
|
|
265
|
+
The cache integrates with:
|
|
266
|
+
|
|
267
|
+
- **Hook Orchestration**: `ExecutionStrategyProtocol` implementations
|
|
268
|
+
- **QA Adapters**: `QAAdapterProtocol` implementations
|
|
269
|
+
- **Performance Monitoring**: Cache hit rates tracked in metrics
|
|
270
|
+
- **ACB Module System**: Registered via `MODULE_ID` (UUID7)
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
# ACB Integration
|
|
274
|
+
from acb.depends import depends
|
|
275
|
+
from crackerjack.orchestration.cache import ToolProxyCacheAdapter
|
|
276
|
+
|
|
277
|
+
# Register cache adapter
|
|
278
|
+
depends.set(ToolProxyCacheAdapter)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
# Use via dependency injection
|
|
282
|
+
@depends.inject
|
|
283
|
+
async def execute_with_cache(
|
|
284
|
+
cache: ToolProxyCacheAdapter = depends(),
|
|
285
|
+
) -> HookResult:
|
|
286
|
+
"""Execute hook with caching."""
|
|
287
|
+
key = cache.compute_key(hook, files)
|
|
288
|
+
|
|
289
|
+
# Check cache
|
|
290
|
+
if cached := await cache.get(key):
|
|
291
|
+
return cached
|
|
292
|
+
|
|
293
|
+
# Execute and cache
|
|
294
|
+
result = await execute_hook(hook)
|
|
295
|
+
await cache.set(key, result)
|
|
296
|
+
return result
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Related Documentation
|
|
300
|
+
|
|
301
|
+
- [Orchestration Strategies](<../strategies/README.md>) - Execution strategies using cache
|
|
302
|
+
- [Hook Orchestration](<../README.md>) - Overall orchestration
|
|
303
|
+
- [Models](<../../models/README.md>) - HookResult and cache protocols
|
|
304
|
+
- [CLAUDE.md](../../../docs/guides/CLAUDE.md) - Architecture patterns
|
|
305
|
+
|
|
306
|
+
## Future Enhancements
|
|
307
|
+
|
|
308
|
+
- Redis backend for distributed caching
|
|
309
|
+
- Cache warming strategies (pre-populate common files)
|
|
310
|
+
- Adaptive TTL based on change frequency
|
|
311
|
+
- Cache compression for disk persistence
|
|
312
|
+
- Multi-level caching (L1: memory, L2: disk, L3: Redis)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Cache adapters for hook orchestration.
|
|
2
|
+
|
|
3
|
+
This package provides caching strategies for hook results:
|
|
4
|
+
- ToolProxyCacheAdapter: Bridges to existing tool_proxy cache infrastructure
|
|
5
|
+
- MemoryCacheAdapter: In-memory LRU cache for testing
|
|
6
|
+
- RedisCacheAdapter: Redis-backed caching (Phase 4+)
|
|
7
|
+
|
|
8
|
+
Cache Strategy:
|
|
9
|
+
- Content-based keys: {hook_name}:{config_hash}:{content_hash}
|
|
10
|
+
- TTL support: Configurable time-to-live per result
|
|
11
|
+
- Cache invalidation: Automatic on file content changes
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ToolProxyCacheAdapter",
|
|
18
|
+
"MemoryCacheAdapter",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Lazy imports
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def __getattr__(name: str) -> Any:
|
|
27
|
+
if name == "ToolProxyCacheAdapter":
|
|
28
|
+
from crackerjack.orchestration.cache.tool_proxy_cache import (
|
|
29
|
+
ToolProxyCacheAdapter,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
return ToolProxyCacheAdapter
|
|
33
|
+
elif name == "MemoryCacheAdapter":
|
|
34
|
+
from crackerjack.orchestration.cache.memory_cache import MemoryCacheAdapter
|
|
35
|
+
|
|
36
|
+
return MemoryCacheAdapter
|
|
37
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""In-memory LRU cache adapter for testing and development.
|
|
2
|
+
|
|
3
|
+
Provides a simple in-memory cache with LRU eviction for testing orchestration
|
|
4
|
+
without persisting to disk. Useful for unit tests and development workflows.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
import typing as t
|
|
13
|
+
from collections import OrderedDict
|
|
14
|
+
from contextlib import suppress
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from uuid import UUID
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
|
|
20
|
+
from crackerjack.models.task import HookResult
|
|
21
|
+
|
|
22
|
+
if t.TYPE_CHECKING:
|
|
23
|
+
from crackerjack.config.hooks import HookDefinition
|
|
24
|
+
|
|
25
|
+
# ACB Module Registration (REQUIRED)
|
|
26
|
+
MODULE_ID = UUID("01937d86-ace0-7000-8000-000000000005") # Static UUID7
|
|
27
|
+
MODULE_STATUS = "stable"
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class MemoryCacheSettings(BaseModel):
|
|
33
|
+
"""Settings for in-memory cache adapter."""
|
|
34
|
+
|
|
35
|
+
max_entries: int = Field(default=100, ge=10, le=1000)
|
|
36
|
+
default_ttl: int = Field(default=3600, ge=60, le=86400)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MemoryCacheAdapter:
|
|
40
|
+
"""In-memory LRU cache adapter for testing.
|
|
41
|
+
|
|
42
|
+
Features:
|
|
43
|
+
- LRU eviction when max_entries reached
|
|
44
|
+
- TTL-based expiration
|
|
45
|
+
- No disk persistence (ephemeral)
|
|
46
|
+
- Thread-safe operations
|
|
47
|
+
|
|
48
|
+
Use Cases:
|
|
49
|
+
- Unit testing orchestration without disk I/O
|
|
50
|
+
- Development workflows requiring fast cache
|
|
51
|
+
- CI/CD pipelines with ephemeral environments
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
```python
|
|
55
|
+
cache = MemoryCacheAdapter(settings=MemoryCacheSettings(max_entries=50))
|
|
56
|
+
await cache.init()
|
|
57
|
+
|
|
58
|
+
# Cache operations work identically to ToolProxyCacheAdapter
|
|
59
|
+
result = await cache.get(key)
|
|
60
|
+
if not result:
|
|
61
|
+
result = await execute_hook(hook)
|
|
62
|
+
await cache.set(key, result)
|
|
63
|
+
```
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
settings: MemoryCacheSettings | None = None,
|
|
69
|
+
) -> None:
|
|
70
|
+
"""Initialize in-memory cache adapter.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
settings: Optional cache settings
|
|
74
|
+
"""
|
|
75
|
+
self.settings = settings or MemoryCacheSettings()
|
|
76
|
+
self._cache: OrderedDict[str, tuple[HookResult, float]] = OrderedDict()
|
|
77
|
+
self._initialized = False
|
|
78
|
+
|
|
79
|
+
logger.debug(
|
|
80
|
+
"MemoryCacheAdapter initializing",
|
|
81
|
+
extra={
|
|
82
|
+
"max_entries": self.settings.max_entries,
|
|
83
|
+
"default_ttl": self.settings.default_ttl,
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
async def init(self) -> None:
|
|
88
|
+
"""Initialize cache adapter (no-op for memory cache)."""
|
|
89
|
+
if self._initialized:
|
|
90
|
+
logger.debug("Memory cache already initialized")
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
self._initialized = True
|
|
94
|
+
|
|
95
|
+
logger.info(
|
|
96
|
+
"MemoryCacheAdapter initialized",
|
|
97
|
+
extra={
|
|
98
|
+
"max_entries": self.settings.max_entries,
|
|
99
|
+
"default_ttl": self.settings.default_ttl,
|
|
100
|
+
},
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
async def get(self, key: str) -> HookResult | None:
|
|
104
|
+
"""Retrieve cached hook result.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
key: Cache key
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Cached HookResult if found and not expired, None otherwise
|
|
111
|
+
"""
|
|
112
|
+
if not self._initialized:
|
|
113
|
+
logger.warning("Memory cache not initialized, returning None")
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
import time
|
|
118
|
+
|
|
119
|
+
if key in self._cache:
|
|
120
|
+
result, expiry = self._cache[key]
|
|
121
|
+
|
|
122
|
+
# Check expiration
|
|
123
|
+
if time.time() < expiry:
|
|
124
|
+
# Move to end (LRU update)
|
|
125
|
+
self._cache.move_to_end(key)
|
|
126
|
+
|
|
127
|
+
logger.debug(
|
|
128
|
+
"Cache hit",
|
|
129
|
+
extra={
|
|
130
|
+
"key": key,
|
|
131
|
+
"hook_name": result.name,
|
|
132
|
+
"status": result.status,
|
|
133
|
+
},
|
|
134
|
+
)
|
|
135
|
+
return result
|
|
136
|
+
else:
|
|
137
|
+
# Expired - remove from cache
|
|
138
|
+
del self._cache[key]
|
|
139
|
+
logger.debug("Cache entry expired", extra={"key": key})
|
|
140
|
+
|
|
141
|
+
logger.debug("Cache miss", extra={"key": key})
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
except Exception as e:
|
|
145
|
+
logger.error(
|
|
146
|
+
"Failed to retrieve from cache",
|
|
147
|
+
extra={
|
|
148
|
+
"key": key,
|
|
149
|
+
"error": str(e),
|
|
150
|
+
},
|
|
151
|
+
)
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
async def set(
|
|
155
|
+
self,
|
|
156
|
+
key: str,
|
|
157
|
+
result: HookResult,
|
|
158
|
+
ttl: int | None = None,
|
|
159
|
+
) -> None:
|
|
160
|
+
"""Cache hook result with TTL and LRU eviction.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
key: Cache key
|
|
164
|
+
result: HookResult to cache
|
|
165
|
+
ttl: Optional time-to-live in seconds
|
|
166
|
+
"""
|
|
167
|
+
if not self._initialized:
|
|
168
|
+
logger.warning("Memory cache not initialized, skipping cache write")
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
import time
|
|
173
|
+
|
|
174
|
+
ttl_sec = ttl or self.settings.default_ttl
|
|
175
|
+
expiry = time.time() + ttl_sec
|
|
176
|
+
|
|
177
|
+
# LRU eviction if at capacity
|
|
178
|
+
if len(self._cache) >= self.settings.max_entries and key not in self._cache:
|
|
179
|
+
# Remove oldest entry (first item in OrderedDict)
|
|
180
|
+
evicted_key, _ = self._cache.popitem(last=False)
|
|
181
|
+
logger.debug(
|
|
182
|
+
"LRU eviction",
|
|
183
|
+
extra={
|
|
184
|
+
"evicted_key": evicted_key,
|
|
185
|
+
"cache_size": len(self._cache),
|
|
186
|
+
},
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
self._cache[key] = (result, expiry)
|
|
190
|
+
# Move to end (most recently used)
|
|
191
|
+
self._cache.move_to_end(key)
|
|
192
|
+
|
|
193
|
+
logger.debug(
|
|
194
|
+
"Cache write",
|
|
195
|
+
extra={
|
|
196
|
+
"key": key,
|
|
197
|
+
"hook_name": result.name,
|
|
198
|
+
"status": result.status,
|
|
199
|
+
"ttl": ttl_sec,
|
|
200
|
+
"cache_size": len(self._cache),
|
|
201
|
+
},
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
except Exception as e:
|
|
205
|
+
logger.error(
|
|
206
|
+
"Failed to write to cache",
|
|
207
|
+
extra={
|
|
208
|
+
"key": key,
|
|
209
|
+
"error": str(e),
|
|
210
|
+
},
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def compute_key(
|
|
214
|
+
self,
|
|
215
|
+
hook: HookDefinition,
|
|
216
|
+
files: list[Path],
|
|
217
|
+
) -> str:
|
|
218
|
+
"""Compute content-based cache key.
|
|
219
|
+
|
|
220
|
+
Uses same algorithm as ToolProxyCacheAdapter for consistency.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
hook: Hook definition
|
|
224
|
+
files: List of files to be checked by hook
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Cache key string
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
# Hash hook configuration (Phase 8+ direct invocation API)
|
|
231
|
+
config_data = {
|
|
232
|
+
"name": hook.name,
|
|
233
|
+
"command": hook.command, # Direct tool invocation command
|
|
234
|
+
"timeout": hook.timeout,
|
|
235
|
+
"stage": hook.stage.value
|
|
236
|
+
if hasattr(hook.stage, "value")
|
|
237
|
+
else str(hook.stage),
|
|
238
|
+
"security_level": hook.security_level.value
|
|
239
|
+
if hasattr(hook.security_level, "value")
|
|
240
|
+
else str(hook.security_level),
|
|
241
|
+
}
|
|
242
|
+
config_json = json.dumps(config_data, sort_keys=True)
|
|
243
|
+
config_hash = hashlib.sha256(config_json.encode()).hexdigest()[:16]
|
|
244
|
+
|
|
245
|
+
# Hash file contents
|
|
246
|
+
content_hasher = hashlib.sha256()
|
|
247
|
+
for file_path in sorted(files):
|
|
248
|
+
try:
|
|
249
|
+
if file_path.exists() and file_path.is_file():
|
|
250
|
+
content_hasher.update(file_path.read_bytes())
|
|
251
|
+
except Exception as e:
|
|
252
|
+
logger.warning(
|
|
253
|
+
f"Failed to hash file {file_path}: {e}",
|
|
254
|
+
extra={"file": str(file_path), "error": str(e)},
|
|
255
|
+
)
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
content_hash = content_hasher.hexdigest()[:16]
|
|
259
|
+
|
|
260
|
+
cache_key = f"{hook.name}:{config_hash}:{content_hash}"
|
|
261
|
+
|
|
262
|
+
logger.debug(
|
|
263
|
+
"Cache key computed",
|
|
264
|
+
extra={
|
|
265
|
+
"hook_name": hook.name,
|
|
266
|
+
"file_count": len(files),
|
|
267
|
+
"config_hash": config_hash,
|
|
268
|
+
"content_hash": content_hash,
|
|
269
|
+
},
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
return cache_key
|
|
273
|
+
|
|
274
|
+
except Exception as e:
|
|
275
|
+
logger.error(
|
|
276
|
+
"Failed to compute cache key",
|
|
277
|
+
extra={
|
|
278
|
+
"hook_name": hook.name,
|
|
279
|
+
"error": str(e),
|
|
280
|
+
},
|
|
281
|
+
)
|
|
282
|
+
return f"{hook.name}:error"
|
|
283
|
+
|
|
284
|
+
async def clear(self) -> None:
|
|
285
|
+
"""Clear all cached results."""
|
|
286
|
+
try:
|
|
287
|
+
self._cache.clear()
|
|
288
|
+
logger.info("Memory cache cleared")
|
|
289
|
+
except Exception as e:
|
|
290
|
+
logger.error("Failed to clear cache", extra={"error": str(e)})
|
|
291
|
+
|
|
292
|
+
async def get_stats(self) -> dict[str, t.Any]:
|
|
293
|
+
"""Get cache statistics.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Dictionary with cache statistics
|
|
297
|
+
"""
|
|
298
|
+
import time
|
|
299
|
+
|
|
300
|
+
try:
|
|
301
|
+
total_entries = len(self._cache)
|
|
302
|
+
expired_entries = sum(
|
|
303
|
+
1 for _, expiry in self._cache.values() if time.time() >= expiry
|
|
304
|
+
)
|
|
305
|
+
active_entries = total_entries - expired_entries
|
|
306
|
+
|
|
307
|
+
stats = {
|
|
308
|
+
"total_entries": total_entries,
|
|
309
|
+
"active_entries": active_entries,
|
|
310
|
+
"expired_entries": expired_entries,
|
|
311
|
+
"max_entries": self.settings.max_entries,
|
|
312
|
+
"default_ttl": self.settings.default_ttl,
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
logger.debug("Memory cache statistics", extra=stats)
|
|
316
|
+
|
|
317
|
+
return stats
|
|
318
|
+
|
|
319
|
+
except Exception as e:
|
|
320
|
+
logger.error("Failed to get cache statistics", extra={"error": str(e)})
|
|
321
|
+
return {}
|
|
322
|
+
|
|
323
|
+
@property
|
|
324
|
+
def module_id(self) -> UUID:
|
|
325
|
+
"""Reference to module-level MODULE_ID."""
|
|
326
|
+
return MODULE_ID
|
|
327
|
+
|
|
328
|
+
@property
|
|
329
|
+
def adapter_name(self) -> str:
|
|
330
|
+
"""Human-readable adapter name."""
|
|
331
|
+
return "MemoryCacheAdapter"
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
# ACB Registration (REQUIRED at module level)
|
|
335
|
+
with suppress(Exception):
|
|
336
|
+
from acb.depends import depends
|
|
337
|
+
|
|
338
|
+
depends.set(MemoryCacheAdapter)
|