claude-mpm 4.7.4__py3-none-any.whl → 4.18.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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +118 -0
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +106 -1
- claude_mpm/agents/OUTPUT_STYLE.md +329 -11
- claude_mpm/agents/PM_INSTRUCTIONS.md +397 -459
- claude_mpm/agents/agent_loader.py +17 -5
- claude_mpm/agents/frontmatter_validator.py +284 -253
- claude_mpm/agents/templates/README.md +465 -0
- claude_mpm/agents/templates/agent-manager.json +4 -1
- claude_mpm/agents/templates/agentic-coder-optimizer.json +13 -3
- claude_mpm/agents/templates/api_qa.json +11 -2
- claude_mpm/agents/templates/circuit_breakers.md +638 -0
- claude_mpm/agents/templates/clerk-ops.json +12 -2
- claude_mpm/agents/templates/code_analyzer.json +8 -2
- claude_mpm/agents/templates/content-agent.json +358 -0
- claude_mpm/agents/templates/dart_engineer.json +15 -2
- claude_mpm/agents/templates/data_engineer.json +15 -2
- claude_mpm/agents/templates/documentation.json +10 -2
- claude_mpm/agents/templates/engineer.json +21 -1
- claude_mpm/agents/templates/gcp_ops_agent.json +12 -2
- claude_mpm/agents/templates/git_file_tracking.md +584 -0
- claude_mpm/agents/templates/golang_engineer.json +270 -0
- claude_mpm/agents/templates/imagemagick.json +4 -1
- claude_mpm/agents/templates/java_engineer.json +346 -0
- claude_mpm/agents/templates/local_ops_agent.json +1227 -6
- claude_mpm/agents/templates/memory_manager.json +4 -1
- claude_mpm/agents/templates/nextjs_engineer.json +141 -133
- claude_mpm/agents/templates/ops.json +12 -2
- claude_mpm/agents/templates/php-engineer.json +270 -174
- claude_mpm/agents/templates/pm_examples.md +474 -0
- claude_mpm/agents/templates/pm_red_flags.md +240 -0
- claude_mpm/agents/templates/product_owner.json +338 -0
- claude_mpm/agents/templates/project_organizer.json +14 -4
- claude_mpm/agents/templates/prompt-engineer.json +13 -2
- claude_mpm/agents/templates/python_engineer.json +174 -81
- claude_mpm/agents/templates/qa.json +11 -2
- claude_mpm/agents/templates/react_engineer.json +16 -3
- claude_mpm/agents/templates/refactoring_engineer.json +12 -2
- claude_mpm/agents/templates/research.json +34 -21
- claude_mpm/agents/templates/response_format.md +583 -0
- claude_mpm/agents/templates/ruby-engineer.json +129 -192
- claude_mpm/agents/templates/rust_engineer.json +270 -0
- claude_mpm/agents/templates/security.json +10 -2
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/agents/templates/ticketing.json +10 -2
- claude_mpm/agents/templates/typescript_engineer.json +116 -125
- claude_mpm/agents/templates/validation_templates.md +312 -0
- claude_mpm/agents/templates/vercel_ops_agent.json +12 -2
- claude_mpm/agents/templates/version_control.json +12 -2
- claude_mpm/agents/templates/web_qa.json +11 -2
- claude_mpm/agents/templates/web_ui.json +15 -2
- claude_mpm/cli/__init__.py +34 -614
- claude_mpm/cli/commands/agent_manager.py +25 -12
- claude_mpm/cli/commands/agent_state_manager.py +186 -0
- claude_mpm/cli/commands/agents.py +235 -148
- claude_mpm/cli/commands/agents_detect.py +380 -0
- claude_mpm/cli/commands/agents_recommend.py +309 -0
- claude_mpm/cli/commands/aggregate.py +7 -3
- claude_mpm/cli/commands/analyze.py +9 -4
- claude_mpm/cli/commands/analyze_code.py +7 -2
- claude_mpm/cli/commands/auto_configure.py +570 -0
- claude_mpm/cli/commands/config.py +47 -13
- claude_mpm/cli/commands/configure.py +419 -1571
- claude_mpm/cli/commands/configure_agent_display.py +261 -0
- claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
- claude_mpm/cli/commands/configure_hook_manager.py +225 -0
- claude_mpm/cli/commands/configure_models.py +18 -0
- claude_mpm/cli/commands/configure_navigation.py +167 -0
- claude_mpm/cli/commands/configure_paths.py +104 -0
- claude_mpm/cli/commands/configure_persistence.py +254 -0
- claude_mpm/cli/commands/configure_startup_manager.py +646 -0
- claude_mpm/cli/commands/configure_template_editor.py +497 -0
- claude_mpm/cli/commands/configure_validators.py +73 -0
- claude_mpm/cli/commands/local_deploy.py +537 -0
- claude_mpm/cli/commands/memory.py +54 -20
- claude_mpm/cli/commands/mpm_init.py +585 -196
- claude_mpm/cli/commands/mpm_init_handler.py +37 -3
- claude_mpm/cli/commands/search.py +170 -4
- claude_mpm/cli/commands/upgrade.py +152 -0
- claude_mpm/cli/executor.py +202 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +3 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parsers/__init__.py +7 -1
- claude_mpm/cli/parsers/agents_parser.py +9 -0
- claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
- claude_mpm/cli/parsers/base_parser.py +110 -3
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +65 -5
- claude_mpm/cli/shared/output_formatters.py +28 -19
- claude_mpm/cli/startup.py +481 -0
- claude_mpm/cli/utils.py +52 -1
- claude_mpm/commands/mpm-agents-detect.md +168 -0
- claude_mpm/commands/mpm-agents-recommend.md +214 -0
- claude_mpm/commands/mpm-agents.md +75 -1
- claude_mpm/commands/mpm-auto-configure.md +217 -0
- claude_mpm/commands/mpm-help.md +163 -0
- claude_mpm/commands/mpm-init.md +148 -3
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +1 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/constants.py +1 -0
- claude_mpm/core/base_service.py +13 -12
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/instruction_reinforcement_hook.py +2 -1
- claude_mpm/core/interactive_session.py +9 -3
- claude_mpm/core/log_manager.py +2 -0
- claude_mpm/core/logging_config.py +6 -2
- claude_mpm/core/oneshot_session.py +8 -4
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/core/output_style_manager.py +12 -192
- claude_mpm/core/service_registry.py +5 -1
- claude_mpm/core/types.py +2 -9
- claude_mpm/core/typing_utils.py +7 -6
- claude_mpm/dashboard/static/js/dashboard.js +0 -14
- claude_mpm/dashboard/templates/index.html +3 -41
- claude_mpm/hooks/__init__.py +20 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +4 -2
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +23 -2
- claude_mpm/hooks/failure_learning/__init__.py +60 -0
- claude_mpm/hooks/failure_learning/failure_detection_hook.py +235 -0
- claude_mpm/hooks/failure_learning/fix_detection_hook.py +217 -0
- claude_mpm/hooks/failure_learning/learning_extraction_hook.py +286 -0
- claude_mpm/hooks/instruction_reinforcement.py +7 -2
- claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
- claude_mpm/hooks/kuzu_memory_hook.py +37 -12
- claude_mpm/hooks/kuzu_response_hook.py +183 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/__init__.py +18 -5
- claude_mpm/services/agents/auto_config_manager.py +796 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
- claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
- claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
- claude_mpm/services/agents/observers.py +547 -0
- claude_mpm/services/agents/recommender.py +568 -0
- claude_mpm/services/agents/registry/modification_tracker.py +5 -2
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/__init__.py +33 -1
- claude_mpm/services/core/interfaces/__init__.py +90 -3
- claude_mpm/services/core/interfaces/agent.py +184 -0
- claude_mpm/services/core/interfaces/health.py +172 -0
- claude_mpm/services/core/interfaces/model.py +281 -0
- claude_mpm/services/core/interfaces/process.py +372 -0
- claude_mpm/services/core/interfaces/project.py +121 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/memory_manager.py +11 -24
- claude_mpm/services/core/models/__init__.py +79 -0
- claude_mpm/services/core/models/agent_config.py +381 -0
- claude_mpm/services/core/models/health.py +162 -0
- claude_mpm/services/core/models/process.py +235 -0
- claude_mpm/services/core/models/restart.py +302 -0
- claude_mpm/services/core/models/stability.py +264 -0
- claude_mpm/services/core/models/toolchain.py +306 -0
- claude_mpm/services/core/path_resolver.py +23 -7
- claude_mpm/services/diagnostics/__init__.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
- claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
- claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
- claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
- claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
- claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
- claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +38 -33
- claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
- claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
- claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
- claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
- claude_mpm/services/diagnostics/models.py +19 -24
- claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
- claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
- claude_mpm/services/infrastructure/monitoring/base.py +5 -13
- claude_mpm/services/infrastructure/monitoring/network.py +7 -6
- claude_mpm/services/infrastructure/monitoring/process.py +13 -12
- claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
- claude_mpm/services/infrastructure/monitoring/service.py +16 -15
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +163 -0
- claude_mpm/services/local_ops/crash_detector.py +257 -0
- claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
- claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
- claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
- claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
- claude_mpm/services/local_ops/health_manager.py +430 -0
- claude_mpm/services/local_ops/log_monitor.py +396 -0
- claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
- claude_mpm/services/local_ops/process_manager.py +595 -0
- claude_mpm/services/local_ops/resource_monitor.py +331 -0
- claude_mpm/services/local_ops/restart_manager.py +401 -0
- claude_mpm/services/local_ops/restart_policy.py +387 -0
- claude_mpm/services/local_ops/state_manager.py +372 -0
- claude_mpm/services/local_ops/unified_manager.py +600 -0
- claude_mpm/services/mcp_config_manager.py +9 -4
- claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
- claude_mpm/services/mcp_gateway/core/base.py +18 -31
- claude_mpm/services/mcp_gateway/main.py +30 -0
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +206 -32
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +25 -5
- claude_mpm/services/mcp_service_verifier.py +1 -1
- claude_mpm/services/memory/failure_tracker.py +563 -0
- claude_mpm/services/memory_hook_service.py +165 -4
- claude_mpm/services/model/__init__.py +147 -0
- claude_mpm/services/model/base_provider.py +365 -0
- claude_mpm/services/model/claude_provider.py +412 -0
- claude_mpm/services/model/model_router.py +453 -0
- claude_mpm/services/model/ollama_provider.py +415 -0
- claude_mpm/services/monitor/daemon_manager.py +3 -2
- claude_mpm/services/monitor/handlers/dashboard.py +2 -1
- claude_mpm/services/monitor/handlers/hooks.py +2 -1
- claude_mpm/services/monitor/management/lifecycle.py +3 -2
- claude_mpm/services/monitor/server.py +2 -1
- claude_mpm/services/project/__init__.py +23 -0
- claude_mpm/services/project/detection_strategies.py +719 -0
- claude_mpm/services/project/toolchain_analyzer.py +581 -0
- claude_mpm/services/self_upgrade_service.py +342 -0
- claude_mpm/services/session_management_service.py +3 -2
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/shared/async_service_base.py +16 -27
- claude_mpm/services/shared/lifecycle_service_base.py +1 -14
- claude_mpm/services/socketio/handlers/__init__.py +5 -2
- claude_mpm/services/socketio/handlers/hook.py +13 -2
- claude_mpm/services/socketio/handlers/registry.py +4 -2
- claude_mpm/services/socketio/server/main.py +10 -8
- claude_mpm/services/subprocess_launcher_service.py +14 -5
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
- claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
- claude_mpm/services/unified/deployment_strategies/local.py +6 -5
- claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
- claude_mpm/services/unified/interfaces.py +3 -1
- claude_mpm/services/unified/unified_analyzer.py +14 -10
- claude_mpm/services/unified/unified_config.py +2 -1
- claude_mpm/services/unified/unified_deployment.py +9 -4
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -0
- claude_mpm/skills/bundled/__init__.py +6 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +567 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +286 -0
- claude_mpm/skills/skill_manager.py +310 -0
- claude_mpm/storage/state_storage.py +15 -15
- claude_mpm/tools/code_tree_analyzer.py +177 -141
- claude_mpm/tools/code_tree_events.py +4 -2
- claude_mpm/utils/agent_dependency_loader.py +40 -20
- claude_mpm/utils/display_helper.py +260 -0
- claude_mpm/utils/git_analyzer.py +407 -0
- claude_mpm/utils/robust_installer.py +73 -19
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +129 -12
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +295 -193
- claude_mpm/dashboard/static/css/code-tree.css +0 -1639
- claude_mpm/dashboard/static/index-hub-backup.html +0 -713
- claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
- claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
- claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
- claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
- claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
- claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
- claude_mpm/services/project/analyzer_refactored.py +0 -450
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Toolchain Analyzer Service for Claude MPM Framework
|
|
3
|
+
===================================================
|
|
4
|
+
|
|
5
|
+
WHY: This service orchestrates multiple detection strategies to analyze
|
|
6
|
+
project toolchains. It provides comprehensive analysis of languages,
|
|
7
|
+
frameworks, and deployment targets for auto-configuration.
|
|
8
|
+
|
|
9
|
+
DESIGN DECISION: Uses Strategy pattern for pluggable detection strategies,
|
|
10
|
+
enabling easy addition of new language detectors. Implements dependency
|
|
11
|
+
injection for integration with existing ProjectAnalyzer.
|
|
12
|
+
|
|
13
|
+
Part of TSK-0054: Auto-Configuration Feature - Phase 2
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import time
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Dict, List, Optional
|
|
19
|
+
|
|
20
|
+
from ...core.base_service import BaseService
|
|
21
|
+
from ..core.interfaces.project import IToolchainAnalyzer
|
|
22
|
+
from ..core.models.toolchain import (
|
|
23
|
+
ConfidenceLevel,
|
|
24
|
+
DeploymentTarget,
|
|
25
|
+
Framework,
|
|
26
|
+
LanguageDetection,
|
|
27
|
+
ToolchainAnalysis,
|
|
28
|
+
ToolchainComponent,
|
|
29
|
+
)
|
|
30
|
+
from .detection_strategies import (
|
|
31
|
+
GoDetectionStrategy,
|
|
32
|
+
IToolchainDetectionStrategy,
|
|
33
|
+
NodeJSDetectionStrategy,
|
|
34
|
+
PythonDetectionStrategy,
|
|
35
|
+
RustDetectionStrategy,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ToolchainAnalyzerService(BaseService, IToolchainAnalyzer):
|
|
40
|
+
"""Service for analyzing project toolchains using pluggable strategies.
|
|
41
|
+
|
|
42
|
+
WHY: Understanding project toolchain is essential for intelligent agent
|
|
43
|
+
recommendations. This service provides comprehensive analysis with
|
|
44
|
+
confidence scoring and evidence tracking.
|
|
45
|
+
|
|
46
|
+
DESIGN DECISION: Separates detection logic into pluggable strategies,
|
|
47
|
+
making it easy to add new language support. Uses dependency injection
|
|
48
|
+
to integrate with existing ProjectAnalyzer for enhanced detection.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
project_analyzer: Optional[any] = None,
|
|
54
|
+
dependency_analyzer: Optional[any] = None,
|
|
55
|
+
config: Optional[Dict] = None,
|
|
56
|
+
):
|
|
57
|
+
"""Initialize the toolchain analyzer.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
project_analyzer: Optional ProjectAnalyzer for enhanced detection
|
|
61
|
+
dependency_analyzer: Optional DependencyAnalyzer for dependency parsing
|
|
62
|
+
config: Optional configuration dictionary
|
|
63
|
+
"""
|
|
64
|
+
super().__init__(
|
|
65
|
+
name="ToolchainAnalyzer",
|
|
66
|
+
config=config,
|
|
67
|
+
enable_enhanced_features=False,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Store dependencies
|
|
71
|
+
self.project_analyzer = project_analyzer
|
|
72
|
+
self.dependency_analyzer = dependency_analyzer
|
|
73
|
+
|
|
74
|
+
# Initialize detection strategies
|
|
75
|
+
self._strategies: Dict[str, IToolchainDetectionStrategy] = {}
|
|
76
|
+
self._register_default_strategies()
|
|
77
|
+
|
|
78
|
+
# Analysis cache
|
|
79
|
+
self._cache: Dict[str, ToolchainAnalysis] = {}
|
|
80
|
+
self._cache_ttl = 300 # 5 minutes
|
|
81
|
+
|
|
82
|
+
def _register_default_strategies(self) -> None:
|
|
83
|
+
"""Register default detection strategies."""
|
|
84
|
+
self.register_strategy("nodejs", NodeJSDetectionStrategy())
|
|
85
|
+
self.register_strategy("python", PythonDetectionStrategy())
|
|
86
|
+
self.register_strategy("rust", RustDetectionStrategy())
|
|
87
|
+
self.register_strategy("go", GoDetectionStrategy())
|
|
88
|
+
|
|
89
|
+
def register_strategy(
|
|
90
|
+
self, name: str, strategy: IToolchainDetectionStrategy
|
|
91
|
+
) -> None:
|
|
92
|
+
"""Register a new detection strategy.
|
|
93
|
+
|
|
94
|
+
WHY: Allows runtime registration of new detection strategies,
|
|
95
|
+
enabling extensibility without modifying core code.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
name: Unique name for the strategy
|
|
99
|
+
strategy: Detection strategy instance
|
|
100
|
+
"""
|
|
101
|
+
self._strategies[name] = strategy
|
|
102
|
+
self.logger.debug(f"Registered detection strategy: {name}")
|
|
103
|
+
|
|
104
|
+
async def _initialize(self) -> None:
|
|
105
|
+
"""Initialize the service."""
|
|
106
|
+
self.logger.info("ToolchainAnalyzerService initialized")
|
|
107
|
+
|
|
108
|
+
async def _cleanup(self) -> None:
|
|
109
|
+
"""Cleanup service resources."""
|
|
110
|
+
self._cache.clear()
|
|
111
|
+
self.logger.info("ToolchainAnalyzerService cleaned up")
|
|
112
|
+
|
|
113
|
+
def analyze_toolchain(self, project_path: Path) -> ToolchainAnalysis:
|
|
114
|
+
"""Analyze project toolchain and dependencies.
|
|
115
|
+
|
|
116
|
+
WHY: Provides complete toolchain analysis including languages,
|
|
117
|
+
frameworks, build tools, and deployment targets in a single call.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
project_path: Path to the project root directory
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
ToolchainAnalysis: Complete analysis result with confidence scores
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
FileNotFoundError: If project_path does not exist
|
|
127
|
+
PermissionError: If project_path is not readable
|
|
128
|
+
"""
|
|
129
|
+
# Validate project path
|
|
130
|
+
if not project_path.exists():
|
|
131
|
+
raise FileNotFoundError(f"Project path does not exist: {project_path}")
|
|
132
|
+
if not project_path.is_dir():
|
|
133
|
+
raise ValueError(f"Project path is not a directory: {project_path}")
|
|
134
|
+
|
|
135
|
+
# Check cache
|
|
136
|
+
cache_key = str(project_path.absolute())
|
|
137
|
+
cached = self._get_from_cache(cache_key)
|
|
138
|
+
if cached:
|
|
139
|
+
self.logger.debug(f"Using cached analysis for {project_path}")
|
|
140
|
+
return cached
|
|
141
|
+
|
|
142
|
+
self.logger.info(f"Analyzing toolchain for project: {project_path}")
|
|
143
|
+
start_time = time.time()
|
|
144
|
+
|
|
145
|
+
# Detect language
|
|
146
|
+
language_detection = self.detect_language(project_path)
|
|
147
|
+
|
|
148
|
+
# Detect frameworks
|
|
149
|
+
frameworks = self.detect_frameworks(project_path)
|
|
150
|
+
|
|
151
|
+
# Detect build tools and package managers
|
|
152
|
+
build_tools = self._detect_build_tools(project_path)
|
|
153
|
+
package_managers = self._detect_package_managers(project_path)
|
|
154
|
+
|
|
155
|
+
# Detect development tools
|
|
156
|
+
dev_tools = self._detect_development_tools(project_path)
|
|
157
|
+
|
|
158
|
+
# Detect deployment target
|
|
159
|
+
deployment_target = self.detect_deployment_target(project_path)
|
|
160
|
+
|
|
161
|
+
# Calculate overall confidence
|
|
162
|
+
overall_confidence = self._calculate_overall_confidence(
|
|
163
|
+
language_detection, frameworks, deployment_target
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Create analysis result
|
|
167
|
+
analysis = ToolchainAnalysis(
|
|
168
|
+
project_path=project_path,
|
|
169
|
+
language_detection=language_detection,
|
|
170
|
+
frameworks=frameworks,
|
|
171
|
+
deployment_target=deployment_target,
|
|
172
|
+
build_tools=build_tools,
|
|
173
|
+
package_managers=package_managers,
|
|
174
|
+
development_tools=dev_tools,
|
|
175
|
+
overall_confidence=overall_confidence,
|
|
176
|
+
analysis_timestamp=time.time(),
|
|
177
|
+
metadata={
|
|
178
|
+
"analysis_duration_ms": (time.time() - start_time) * 1000,
|
|
179
|
+
"strategies_used": list(self._strategies.keys()),
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Cache the result
|
|
184
|
+
self._add_to_cache(cache_key, analysis)
|
|
185
|
+
|
|
186
|
+
duration_ms = (time.time() - start_time) * 1000
|
|
187
|
+
self.logger.info(
|
|
188
|
+
f"Toolchain analysis complete in {duration_ms:.2f}ms: "
|
|
189
|
+
f"{language_detection.primary_language} with {len(frameworks)} frameworks"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
return analysis
|
|
193
|
+
|
|
194
|
+
def detect_language(self, project_path: Path) -> LanguageDetection:
|
|
195
|
+
"""Detect primary and secondary languages used in the project.
|
|
196
|
+
|
|
197
|
+
WHY: Language detection is foundational for all other analysis.
|
|
198
|
+
Multiple strategies are tried to ensure accurate detection.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
project_path: Path to the project root directory
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
LanguageDetection: Detected languages with confidence scores
|
|
205
|
+
|
|
206
|
+
Raises:
|
|
207
|
+
FileNotFoundError: If project_path does not exist
|
|
208
|
+
"""
|
|
209
|
+
if not project_path.exists():
|
|
210
|
+
raise FileNotFoundError(f"Project path does not exist: {project_path}")
|
|
211
|
+
|
|
212
|
+
detections: List[LanguageDetection] = []
|
|
213
|
+
|
|
214
|
+
# Run all strategies that can detect
|
|
215
|
+
for strategy_name, strategy in self._strategies.items():
|
|
216
|
+
try:
|
|
217
|
+
if strategy.can_detect(project_path):
|
|
218
|
+
detection = strategy.detect_language(project_path)
|
|
219
|
+
if detection:
|
|
220
|
+
detections.append(detection)
|
|
221
|
+
self.logger.debug(
|
|
222
|
+
f"Strategy '{strategy_name}' detected: "
|
|
223
|
+
f"{detection.primary_language} "
|
|
224
|
+
f"(confidence: {detection.primary_confidence.value})"
|
|
225
|
+
)
|
|
226
|
+
except Exception as e:
|
|
227
|
+
self.logger.warning(
|
|
228
|
+
f"Strategy '{strategy_name}' failed: {e}", exc_info=True
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# If no detections, return unknown
|
|
232
|
+
if not detections:
|
|
233
|
+
self.logger.warning(f"No language detected for {project_path}")
|
|
234
|
+
return LanguageDetection(
|
|
235
|
+
primary_language="Unknown",
|
|
236
|
+
primary_confidence=ConfidenceLevel.VERY_LOW,
|
|
237
|
+
secondary_languages=[],
|
|
238
|
+
language_percentages={"Unknown": 100.0},
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# If multiple detections, choose highest confidence
|
|
242
|
+
if len(detections) > 1:
|
|
243
|
+
# Sort by confidence level
|
|
244
|
+
confidence_order = {
|
|
245
|
+
ConfidenceLevel.HIGH: 4,
|
|
246
|
+
ConfidenceLevel.MEDIUM: 3,
|
|
247
|
+
ConfidenceLevel.LOW: 2,
|
|
248
|
+
ConfidenceLevel.VERY_LOW: 1,
|
|
249
|
+
}
|
|
250
|
+
detections.sort(
|
|
251
|
+
key=lambda d: confidence_order.get(d.primary_confidence, 0),
|
|
252
|
+
reverse=True,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# Merge secondary languages from other detections
|
|
256
|
+
primary = detections[0]
|
|
257
|
+
for other in detections[1:]:
|
|
258
|
+
# Add other primary languages as secondary
|
|
259
|
+
if other.primary_language != primary.primary_language:
|
|
260
|
+
primary.secondary_languages.append(
|
|
261
|
+
ToolchainComponent(
|
|
262
|
+
name=other.primary_language,
|
|
263
|
+
version=other.primary_version,
|
|
264
|
+
confidence=other.primary_confidence,
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
return detections[0]
|
|
269
|
+
|
|
270
|
+
def detect_frameworks(self, project_path: Path) -> List[Framework]:
|
|
271
|
+
"""Detect frameworks and their versions.
|
|
272
|
+
|
|
273
|
+
WHY: Framework detection enables targeted agent recommendations
|
|
274
|
+
and provides context for development assistance.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
project_path: Path to the project root directory
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
List[Framework]: List of detected frameworks with versions and types
|
|
281
|
+
|
|
282
|
+
Raises:
|
|
283
|
+
FileNotFoundError: If project_path does not exist
|
|
284
|
+
"""
|
|
285
|
+
if not project_path.exists():
|
|
286
|
+
raise FileNotFoundError(f"Project path does not exist: {project_path}")
|
|
287
|
+
|
|
288
|
+
all_frameworks: List[Framework] = []
|
|
289
|
+
seen_frameworks: set = set()
|
|
290
|
+
|
|
291
|
+
# Run all strategies that can detect
|
|
292
|
+
for strategy_name, strategy in self._strategies.items():
|
|
293
|
+
try:
|
|
294
|
+
if strategy.can_detect(project_path):
|
|
295
|
+
frameworks = strategy.detect_frameworks(project_path)
|
|
296
|
+
for fw in frameworks:
|
|
297
|
+
# Deduplicate by name (case-insensitive)
|
|
298
|
+
fw_key = fw.name.lower()
|
|
299
|
+
if fw_key not in seen_frameworks:
|
|
300
|
+
all_frameworks.append(fw)
|
|
301
|
+
seen_frameworks.add(fw_key)
|
|
302
|
+
self.logger.debug(
|
|
303
|
+
f"Detected framework: {fw.name} "
|
|
304
|
+
f"(type: {fw.framework_type}, version: {fw.version})"
|
|
305
|
+
)
|
|
306
|
+
except Exception as e:
|
|
307
|
+
self.logger.warning(
|
|
308
|
+
f"Framework detection failed for '{strategy_name}': {e}",
|
|
309
|
+
exc_info=True,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Sort by confidence and popularity
|
|
313
|
+
all_frameworks.sort(
|
|
314
|
+
key=lambda f: (
|
|
315
|
+
(
|
|
316
|
+
4
|
|
317
|
+
if f.confidence == ConfidenceLevel.HIGH
|
|
318
|
+
else (
|
|
319
|
+
3
|
|
320
|
+
if f.confidence == ConfidenceLevel.MEDIUM
|
|
321
|
+
else 2 if f.confidence == ConfidenceLevel.LOW else 1
|
|
322
|
+
)
|
|
323
|
+
),
|
|
324
|
+
f.popularity_score,
|
|
325
|
+
),
|
|
326
|
+
reverse=True,
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
return all_frameworks
|
|
330
|
+
|
|
331
|
+
def detect_deployment_target(
|
|
332
|
+
self, project_path: Path
|
|
333
|
+
) -> Optional[DeploymentTarget]:
|
|
334
|
+
"""Detect intended deployment environment.
|
|
335
|
+
|
|
336
|
+
WHY: Deployment target affects agent recommendations (e.g., DevOps
|
|
337
|
+
agents for Kubernetes, serverless agents for Lambda).
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
project_path: Path to the project root directory
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
Optional[DeploymentTarget]: Detected deployment target or None if unclear
|
|
344
|
+
|
|
345
|
+
Raises:
|
|
346
|
+
FileNotFoundError: If project_path does not exist
|
|
347
|
+
"""
|
|
348
|
+
if not project_path.exists():
|
|
349
|
+
raise FileNotFoundError(f"Project path does not exist: {project_path}")
|
|
350
|
+
|
|
351
|
+
# Check for Docker
|
|
352
|
+
if (project_path / "Dockerfile").exists() or (
|
|
353
|
+
project_path / "docker-compose.yml"
|
|
354
|
+
).exists():
|
|
355
|
+
return DeploymentTarget(
|
|
356
|
+
target_type="container",
|
|
357
|
+
platform="docker",
|
|
358
|
+
confidence=ConfidenceLevel.HIGH,
|
|
359
|
+
requires_ops_agent=True,
|
|
360
|
+
metadata={
|
|
361
|
+
"docker_compose": (project_path / "docker-compose.yml").exists()
|
|
362
|
+
},
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Check for Kubernetes
|
|
366
|
+
if (project_path / "k8s").exists() or (project_path / "kubernetes").exists():
|
|
367
|
+
return DeploymentTarget(
|
|
368
|
+
target_type="container",
|
|
369
|
+
platform="kubernetes",
|
|
370
|
+
confidence=ConfidenceLevel.HIGH,
|
|
371
|
+
requires_ops_agent=True,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
# Check for Vercel
|
|
375
|
+
if (project_path / "vercel.json").exists():
|
|
376
|
+
return DeploymentTarget(
|
|
377
|
+
target_type="serverless",
|
|
378
|
+
platform="vercel",
|
|
379
|
+
confidence=ConfidenceLevel.HIGH,
|
|
380
|
+
requires_ops_agent=False,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# Check for AWS
|
|
384
|
+
if (project_path / "serverless.yml").exists():
|
|
385
|
+
return DeploymentTarget(
|
|
386
|
+
target_type="serverless",
|
|
387
|
+
platform="aws",
|
|
388
|
+
confidence=ConfidenceLevel.MEDIUM,
|
|
389
|
+
requires_ops_agent=True,
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
# Check for Terraform
|
|
393
|
+
if list(project_path.glob("*.tf")):
|
|
394
|
+
return DeploymentTarget(
|
|
395
|
+
target_type="cloud",
|
|
396
|
+
platform="terraform",
|
|
397
|
+
confidence=ConfidenceLevel.MEDIUM,
|
|
398
|
+
requires_ops_agent=True,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
# Check for GCP
|
|
402
|
+
if (project_path / "app.yaml").exists():
|
|
403
|
+
return DeploymentTarget(
|
|
404
|
+
target_type="cloud",
|
|
405
|
+
platform="gcp",
|
|
406
|
+
confidence=ConfidenceLevel.MEDIUM,
|
|
407
|
+
requires_ops_agent=False,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# No clear deployment target
|
|
411
|
+
self.logger.debug(f"No deployment target detected for {project_path}")
|
|
412
|
+
return None
|
|
413
|
+
|
|
414
|
+
def _detect_build_tools(self, project_path: Path) -> List[ToolchainComponent]:
|
|
415
|
+
"""Detect build tools used in the project."""
|
|
416
|
+
build_tools = []
|
|
417
|
+
|
|
418
|
+
# Check for common build tools
|
|
419
|
+
build_indicators = {
|
|
420
|
+
"webpack": ["webpack.config.js", "webpack.config.ts"],
|
|
421
|
+
"vite": ["vite.config.js", "vite.config.ts"],
|
|
422
|
+
"rollup": ["rollup.config.js"],
|
|
423
|
+
"parcel": [".parcelrc"],
|
|
424
|
+
"make": ["Makefile"],
|
|
425
|
+
"cmake": ["CMakeLists.txt"],
|
|
426
|
+
"gradle": ["build.gradle", "build.gradle.kts"],
|
|
427
|
+
"maven": ["pom.xml"],
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
for tool_name, indicators in build_indicators.items():
|
|
431
|
+
for indicator in indicators:
|
|
432
|
+
if (project_path / indicator).exists():
|
|
433
|
+
build_tools.append(
|
|
434
|
+
ToolchainComponent(
|
|
435
|
+
name=tool_name,
|
|
436
|
+
confidence=ConfidenceLevel.HIGH,
|
|
437
|
+
)
|
|
438
|
+
)
|
|
439
|
+
break
|
|
440
|
+
|
|
441
|
+
return build_tools
|
|
442
|
+
|
|
443
|
+
def _detect_package_managers(self, project_path: Path) -> List[ToolchainComponent]:
|
|
444
|
+
"""Detect package managers used in the project."""
|
|
445
|
+
package_managers = []
|
|
446
|
+
|
|
447
|
+
# Check for package manager indicators
|
|
448
|
+
pm_indicators = {
|
|
449
|
+
"npm": ["package-lock.json"],
|
|
450
|
+
"yarn": ["yarn.lock"],
|
|
451
|
+
"pnpm": ["pnpm-lock.yaml"],
|
|
452
|
+
"pip": ["requirements.txt"],
|
|
453
|
+
"poetry": ["poetry.lock"],
|
|
454
|
+
"pipenv": ["Pipfile.lock"],
|
|
455
|
+
"cargo": ["Cargo.lock"],
|
|
456
|
+
"go modules": ["go.sum"],
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
for pm_name, indicators in pm_indicators.items():
|
|
460
|
+
for indicator in indicators:
|
|
461
|
+
if (project_path / indicator).exists():
|
|
462
|
+
package_managers.append(
|
|
463
|
+
ToolchainComponent(
|
|
464
|
+
name=pm_name,
|
|
465
|
+
confidence=ConfidenceLevel.HIGH,
|
|
466
|
+
)
|
|
467
|
+
)
|
|
468
|
+
break
|
|
469
|
+
|
|
470
|
+
return package_managers
|
|
471
|
+
|
|
472
|
+
def _detect_development_tools(self, project_path: Path) -> List[ToolchainComponent]:
|
|
473
|
+
"""Detect development tools and utilities."""
|
|
474
|
+
dev_tools = []
|
|
475
|
+
|
|
476
|
+
# Check for common dev tools
|
|
477
|
+
tool_indicators = {
|
|
478
|
+
"docker": ["Dockerfile", "docker-compose.yml"],
|
|
479
|
+
"kubernetes": ["k8s", "kubernetes"],
|
|
480
|
+
"terraform": ["*.tf"],
|
|
481
|
+
"git": [".git"],
|
|
482
|
+
"pre-commit": [".pre-commit-config.yaml"],
|
|
483
|
+
"eslint": [".eslintrc.js", ".eslintrc.json"],
|
|
484
|
+
"prettier": [".prettierrc", ".prettierrc.json"],
|
|
485
|
+
"black": ["pyproject.toml"], # Could check for [tool.black]
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
for tool_name, indicators in tool_indicators.items():
|
|
489
|
+
for indicator in indicators:
|
|
490
|
+
if "*" in indicator:
|
|
491
|
+
# Glob pattern
|
|
492
|
+
if list(project_path.glob(indicator)):
|
|
493
|
+
dev_tools.append(
|
|
494
|
+
ToolchainComponent(
|
|
495
|
+
name=tool_name,
|
|
496
|
+
confidence=ConfidenceLevel.HIGH,
|
|
497
|
+
)
|
|
498
|
+
)
|
|
499
|
+
break
|
|
500
|
+
# Direct path
|
|
501
|
+
elif (project_path / indicator).exists():
|
|
502
|
+
dev_tools.append(
|
|
503
|
+
ToolchainComponent(
|
|
504
|
+
name=tool_name,
|
|
505
|
+
confidence=ConfidenceLevel.HIGH,
|
|
506
|
+
)
|
|
507
|
+
)
|
|
508
|
+
break
|
|
509
|
+
|
|
510
|
+
return dev_tools
|
|
511
|
+
|
|
512
|
+
def _calculate_overall_confidence(
|
|
513
|
+
self,
|
|
514
|
+
language_detection: LanguageDetection,
|
|
515
|
+
frameworks: List[Framework],
|
|
516
|
+
deployment_target: Optional[DeploymentTarget],
|
|
517
|
+
) -> ConfidenceLevel:
|
|
518
|
+
"""Calculate overall confidence for the analysis.
|
|
519
|
+
|
|
520
|
+
WHY: Overall confidence helps users understand reliability of
|
|
521
|
+
the complete analysis, not just individual components.
|
|
522
|
+
"""
|
|
523
|
+
# Start with language detection confidence
|
|
524
|
+
confidence_scores = {
|
|
525
|
+
ConfidenceLevel.HIGH: 4,
|
|
526
|
+
ConfidenceLevel.MEDIUM: 3,
|
|
527
|
+
ConfidenceLevel.LOW: 2,
|
|
528
|
+
ConfidenceLevel.VERY_LOW: 1,
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
scores = [confidence_scores.get(language_detection.primary_confidence, 1)]
|
|
532
|
+
|
|
533
|
+
# Add framework confidence scores
|
|
534
|
+
if frameworks:
|
|
535
|
+
framework_avg = sum(
|
|
536
|
+
confidence_scores.get(fw.confidence, 1) for fw in frameworks
|
|
537
|
+
) / len(frameworks)
|
|
538
|
+
scores.append(framework_avg)
|
|
539
|
+
|
|
540
|
+
# Add deployment target confidence if available
|
|
541
|
+
if deployment_target:
|
|
542
|
+
scores.append(confidence_scores.get(deployment_target.confidence, 1))
|
|
543
|
+
|
|
544
|
+
# Calculate average
|
|
545
|
+
avg_score = sum(scores) / len(scores)
|
|
546
|
+
|
|
547
|
+
# Convert back to confidence level
|
|
548
|
+
if avg_score >= 3.5:
|
|
549
|
+
return ConfidenceLevel.HIGH
|
|
550
|
+
if avg_score >= 2.5:
|
|
551
|
+
return ConfidenceLevel.MEDIUM
|
|
552
|
+
if avg_score >= 1.5:
|
|
553
|
+
return ConfidenceLevel.LOW
|
|
554
|
+
return ConfidenceLevel.VERY_LOW
|
|
555
|
+
|
|
556
|
+
def _get_from_cache(self, cache_key: str) -> Optional[ToolchainAnalysis]:
|
|
557
|
+
"""Get analysis from cache if valid."""
|
|
558
|
+
if cache_key in self._cache:
|
|
559
|
+
cached = self._cache[cache_key]
|
|
560
|
+
# Check if cache is still valid
|
|
561
|
+
if cached.analysis_timestamp:
|
|
562
|
+
age = time.time() - cached.analysis_timestamp
|
|
563
|
+
if age < self._cache_ttl:
|
|
564
|
+
return cached
|
|
565
|
+
# Remove stale cache
|
|
566
|
+
del self._cache[cache_key]
|
|
567
|
+
return None
|
|
568
|
+
|
|
569
|
+
def _add_to_cache(self, cache_key: str, analysis: ToolchainAnalysis) -> None:
|
|
570
|
+
"""Add analysis to cache."""
|
|
571
|
+
self._cache[cache_key] = analysis
|
|
572
|
+
|
|
573
|
+
# Limit cache size
|
|
574
|
+
if len(self._cache) > 100:
|
|
575
|
+
# Remove oldest entries
|
|
576
|
+
sorted_items = sorted(
|
|
577
|
+
self._cache.items(),
|
|
578
|
+
key=lambda x: x[1].analysis_timestamp or 0,
|
|
579
|
+
)
|
|
580
|
+
for key, _ in sorted_items[:-50]: # Keep only 50 most recent
|
|
581
|
+
del self._cache[key]
|