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.
Files changed (308) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +118 -0
  3. claude_mpm/agents/BASE_ENGINEER.md +286 -0
  4. claude_mpm/agents/BASE_PM.md +106 -1
  5. claude_mpm/agents/OUTPUT_STYLE.md +329 -11
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +397 -459
  7. claude_mpm/agents/agent_loader.py +17 -5
  8. claude_mpm/agents/frontmatter_validator.py +284 -253
  9. claude_mpm/agents/templates/README.md +465 -0
  10. claude_mpm/agents/templates/agent-manager.json +4 -1
  11. claude_mpm/agents/templates/agentic-coder-optimizer.json +13 -3
  12. claude_mpm/agents/templates/api_qa.json +11 -2
  13. claude_mpm/agents/templates/circuit_breakers.md +638 -0
  14. claude_mpm/agents/templates/clerk-ops.json +12 -2
  15. claude_mpm/agents/templates/code_analyzer.json +8 -2
  16. claude_mpm/agents/templates/content-agent.json +358 -0
  17. claude_mpm/agents/templates/dart_engineer.json +15 -2
  18. claude_mpm/agents/templates/data_engineer.json +15 -2
  19. claude_mpm/agents/templates/documentation.json +10 -2
  20. claude_mpm/agents/templates/engineer.json +21 -1
  21. claude_mpm/agents/templates/gcp_ops_agent.json +12 -2
  22. claude_mpm/agents/templates/git_file_tracking.md +584 -0
  23. claude_mpm/agents/templates/golang_engineer.json +270 -0
  24. claude_mpm/agents/templates/imagemagick.json +4 -1
  25. claude_mpm/agents/templates/java_engineer.json +346 -0
  26. claude_mpm/agents/templates/local_ops_agent.json +1227 -6
  27. claude_mpm/agents/templates/memory_manager.json +4 -1
  28. claude_mpm/agents/templates/nextjs_engineer.json +141 -133
  29. claude_mpm/agents/templates/ops.json +12 -2
  30. claude_mpm/agents/templates/php-engineer.json +270 -174
  31. claude_mpm/agents/templates/pm_examples.md +474 -0
  32. claude_mpm/agents/templates/pm_red_flags.md +240 -0
  33. claude_mpm/agents/templates/product_owner.json +338 -0
  34. claude_mpm/agents/templates/project_organizer.json +14 -4
  35. claude_mpm/agents/templates/prompt-engineer.json +13 -2
  36. claude_mpm/agents/templates/python_engineer.json +174 -81
  37. claude_mpm/agents/templates/qa.json +11 -2
  38. claude_mpm/agents/templates/react_engineer.json +16 -3
  39. claude_mpm/agents/templates/refactoring_engineer.json +12 -2
  40. claude_mpm/agents/templates/research.json +34 -21
  41. claude_mpm/agents/templates/response_format.md +583 -0
  42. claude_mpm/agents/templates/ruby-engineer.json +129 -192
  43. claude_mpm/agents/templates/rust_engineer.json +270 -0
  44. claude_mpm/agents/templates/security.json +10 -2
  45. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  46. claude_mpm/agents/templates/ticketing.json +10 -2
  47. claude_mpm/agents/templates/typescript_engineer.json +116 -125
  48. claude_mpm/agents/templates/validation_templates.md +312 -0
  49. claude_mpm/agents/templates/vercel_ops_agent.json +12 -2
  50. claude_mpm/agents/templates/version_control.json +12 -2
  51. claude_mpm/agents/templates/web_qa.json +11 -2
  52. claude_mpm/agents/templates/web_ui.json +15 -2
  53. claude_mpm/cli/__init__.py +34 -614
  54. claude_mpm/cli/commands/agent_manager.py +25 -12
  55. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  56. claude_mpm/cli/commands/agents.py +235 -148
  57. claude_mpm/cli/commands/agents_detect.py +380 -0
  58. claude_mpm/cli/commands/agents_recommend.py +309 -0
  59. claude_mpm/cli/commands/aggregate.py +7 -3
  60. claude_mpm/cli/commands/analyze.py +9 -4
  61. claude_mpm/cli/commands/analyze_code.py +7 -2
  62. claude_mpm/cli/commands/auto_configure.py +570 -0
  63. claude_mpm/cli/commands/config.py +47 -13
  64. claude_mpm/cli/commands/configure.py +419 -1571
  65. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  66. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  67. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  68. claude_mpm/cli/commands/configure_models.py +18 -0
  69. claude_mpm/cli/commands/configure_navigation.py +167 -0
  70. claude_mpm/cli/commands/configure_paths.py +104 -0
  71. claude_mpm/cli/commands/configure_persistence.py +254 -0
  72. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  73. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  74. claude_mpm/cli/commands/configure_validators.py +73 -0
  75. claude_mpm/cli/commands/local_deploy.py +537 -0
  76. claude_mpm/cli/commands/memory.py +54 -20
  77. claude_mpm/cli/commands/mpm_init.py +585 -196
  78. claude_mpm/cli/commands/mpm_init_handler.py +37 -3
  79. claude_mpm/cli/commands/search.py +170 -4
  80. claude_mpm/cli/commands/upgrade.py +152 -0
  81. claude_mpm/cli/executor.py +202 -0
  82. claude_mpm/cli/helpers.py +105 -0
  83. claude_mpm/cli/interactive/__init__.py +3 -0
  84. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  85. claude_mpm/cli/parsers/__init__.py +7 -1
  86. claude_mpm/cli/parsers/agents_parser.py +9 -0
  87. claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
  88. claude_mpm/cli/parsers/base_parser.py +110 -3
  89. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  90. claude_mpm/cli/parsers/mpm_init_parser.py +65 -5
  91. claude_mpm/cli/shared/output_formatters.py +28 -19
  92. claude_mpm/cli/startup.py +481 -0
  93. claude_mpm/cli/utils.py +52 -1
  94. claude_mpm/commands/mpm-agents-detect.md +168 -0
  95. claude_mpm/commands/mpm-agents-recommend.md +214 -0
  96. claude_mpm/commands/mpm-agents.md +75 -1
  97. claude_mpm/commands/mpm-auto-configure.md +217 -0
  98. claude_mpm/commands/mpm-help.md +163 -0
  99. claude_mpm/commands/mpm-init.md +148 -3
  100. claude_mpm/commands/mpm-version.md +113 -0
  101. claude_mpm/commands/mpm.md +1 -0
  102. claude_mpm/config/agent_config.py +2 -2
  103. claude_mpm/config/model_config.py +428 -0
  104. claude_mpm/constants.py +1 -0
  105. claude_mpm/core/base_service.py +13 -12
  106. claude_mpm/core/enums.py +452 -0
  107. claude_mpm/core/factories.py +1 -1
  108. claude_mpm/core/instruction_reinforcement_hook.py +2 -1
  109. claude_mpm/core/interactive_session.py +9 -3
  110. claude_mpm/core/log_manager.py +2 -0
  111. claude_mpm/core/logging_config.py +6 -2
  112. claude_mpm/core/oneshot_session.py +8 -4
  113. claude_mpm/core/optimized_agent_loader.py +3 -3
  114. claude_mpm/core/output_style_manager.py +12 -192
  115. claude_mpm/core/service_registry.py +5 -1
  116. claude_mpm/core/types.py +2 -9
  117. claude_mpm/core/typing_utils.py +7 -6
  118. claude_mpm/dashboard/static/js/dashboard.js +0 -14
  119. claude_mpm/dashboard/templates/index.html +3 -41
  120. claude_mpm/hooks/__init__.py +20 -0
  121. claude_mpm/hooks/claude_hooks/event_handlers.py +4 -2
  122. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  123. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +23 -2
  124. claude_mpm/hooks/failure_learning/__init__.py +60 -0
  125. claude_mpm/hooks/failure_learning/failure_detection_hook.py +235 -0
  126. claude_mpm/hooks/failure_learning/fix_detection_hook.py +217 -0
  127. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +286 -0
  128. claude_mpm/hooks/instruction_reinforcement.py +7 -2
  129. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  130. claude_mpm/hooks/kuzu_memory_hook.py +37 -12
  131. claude_mpm/hooks/kuzu_response_hook.py +183 -0
  132. claude_mpm/models/resume_log.py +340 -0
  133. claude_mpm/services/agents/__init__.py +18 -5
  134. claude_mpm/services/agents/auto_config_manager.py +796 -0
  135. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  136. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  137. claude_mpm/services/agents/deployment/agent_validator.py +17 -1
  138. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  139. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  140. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  141. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
  142. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  143. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  144. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
  145. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  146. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  147. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  148. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  149. claude_mpm/services/agents/local_template_manager.py +1 -1
  150. claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
  151. claude_mpm/services/agents/observers.py +547 -0
  152. claude_mpm/services/agents/recommender.py +568 -0
  153. claude_mpm/services/agents/registry/modification_tracker.py +5 -2
  154. claude_mpm/services/command_handler_service.py +11 -5
  155. claude_mpm/services/core/__init__.py +33 -1
  156. claude_mpm/services/core/interfaces/__init__.py +90 -3
  157. claude_mpm/services/core/interfaces/agent.py +184 -0
  158. claude_mpm/services/core/interfaces/health.py +172 -0
  159. claude_mpm/services/core/interfaces/model.py +281 -0
  160. claude_mpm/services/core/interfaces/process.py +372 -0
  161. claude_mpm/services/core/interfaces/project.py +121 -0
  162. claude_mpm/services/core/interfaces/restart.py +307 -0
  163. claude_mpm/services/core/interfaces/stability.py +260 -0
  164. claude_mpm/services/core/memory_manager.py +11 -24
  165. claude_mpm/services/core/models/__init__.py +79 -0
  166. claude_mpm/services/core/models/agent_config.py +381 -0
  167. claude_mpm/services/core/models/health.py +162 -0
  168. claude_mpm/services/core/models/process.py +235 -0
  169. claude_mpm/services/core/models/restart.py +302 -0
  170. claude_mpm/services/core/models/stability.py +264 -0
  171. claude_mpm/services/core/models/toolchain.py +306 -0
  172. claude_mpm/services/core/path_resolver.py +23 -7
  173. claude_mpm/services/diagnostics/__init__.py +2 -2
  174. claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
  175. claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
  176. claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
  177. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
  178. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  179. claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
  180. claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
  181. claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
  182. claude_mpm/services/diagnostics/checks/mcp_services_check.py +38 -33
  183. claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
  184. claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
  185. claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
  186. claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
  187. claude_mpm/services/diagnostics/models.py +19 -24
  188. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
  189. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  190. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  191. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  192. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  193. claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
  194. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  195. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  196. claude_mpm/services/local_ops/__init__.py +163 -0
  197. claude_mpm/services/local_ops/crash_detector.py +257 -0
  198. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  199. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  200. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  201. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  202. claude_mpm/services/local_ops/health_manager.py +430 -0
  203. claude_mpm/services/local_ops/log_monitor.py +396 -0
  204. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  205. claude_mpm/services/local_ops/process_manager.py +595 -0
  206. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  207. claude_mpm/services/local_ops/restart_manager.py +401 -0
  208. claude_mpm/services/local_ops/restart_policy.py +387 -0
  209. claude_mpm/services/local_ops/state_manager.py +372 -0
  210. claude_mpm/services/local_ops/unified_manager.py +600 -0
  211. claude_mpm/services/mcp_config_manager.py +9 -4
  212. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  213. claude_mpm/services/mcp_gateway/core/base.py +18 -31
  214. claude_mpm/services/mcp_gateway/main.py +30 -0
  215. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +206 -32
  216. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
  217. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +25 -5
  218. claude_mpm/services/mcp_service_verifier.py +1 -1
  219. claude_mpm/services/memory/failure_tracker.py +563 -0
  220. claude_mpm/services/memory_hook_service.py +165 -4
  221. claude_mpm/services/model/__init__.py +147 -0
  222. claude_mpm/services/model/base_provider.py +365 -0
  223. claude_mpm/services/model/claude_provider.py +412 -0
  224. claude_mpm/services/model/model_router.py +453 -0
  225. claude_mpm/services/model/ollama_provider.py +415 -0
  226. claude_mpm/services/monitor/daemon_manager.py +3 -2
  227. claude_mpm/services/monitor/handlers/dashboard.py +2 -1
  228. claude_mpm/services/monitor/handlers/hooks.py +2 -1
  229. claude_mpm/services/monitor/management/lifecycle.py +3 -2
  230. claude_mpm/services/monitor/server.py +2 -1
  231. claude_mpm/services/project/__init__.py +23 -0
  232. claude_mpm/services/project/detection_strategies.py +719 -0
  233. claude_mpm/services/project/toolchain_analyzer.py +581 -0
  234. claude_mpm/services/self_upgrade_service.py +342 -0
  235. claude_mpm/services/session_management_service.py +3 -2
  236. claude_mpm/services/session_manager.py +205 -1
  237. claude_mpm/services/shared/async_service_base.py +16 -27
  238. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  239. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  240. claude_mpm/services/socketio/handlers/hook.py +13 -2
  241. claude_mpm/services/socketio/handlers/registry.py +4 -2
  242. claude_mpm/services/socketio/server/main.py +10 -8
  243. claude_mpm/services/subprocess_launcher_service.py +14 -5
  244. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
  245. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
  246. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
  247. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
  248. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
  249. claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
  250. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
  251. claude_mpm/services/unified/deployment_strategies/local.py +6 -5
  252. claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
  253. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
  254. claude_mpm/services/unified/interfaces.py +3 -1
  255. claude_mpm/services/unified/unified_analyzer.py +14 -10
  256. claude_mpm/services/unified/unified_config.py +2 -1
  257. claude_mpm/services/unified/unified_deployment.py +9 -4
  258. claude_mpm/services/version_service.py +104 -1
  259. claude_mpm/skills/__init__.py +21 -0
  260. claude_mpm/skills/bundled/__init__.py +6 -0
  261. claude_mpm/skills/bundled/api-documentation.md +393 -0
  262. claude_mpm/skills/bundled/async-testing.md +571 -0
  263. claude_mpm/skills/bundled/code-review.md +143 -0
  264. claude_mpm/skills/bundled/database-migration.md +199 -0
  265. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  266. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  267. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  268. claude_mpm/skills/bundled/git-workflow.md +414 -0
  269. claude_mpm/skills/bundled/imagemagick.md +204 -0
  270. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  271. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  272. claude_mpm/skills/bundled/pdf.md +141 -0
  273. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  274. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  275. claude_mpm/skills/bundled/security-scanning.md +327 -0
  276. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  277. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  278. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  279. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  280. claude_mpm/skills/bundled/xlsx.md +157 -0
  281. claude_mpm/skills/registry.py +286 -0
  282. claude_mpm/skills/skill_manager.py +310 -0
  283. claude_mpm/storage/state_storage.py +15 -15
  284. claude_mpm/tools/code_tree_analyzer.py +177 -141
  285. claude_mpm/tools/code_tree_events.py +4 -2
  286. claude_mpm/utils/agent_dependency_loader.py +40 -20
  287. claude_mpm/utils/display_helper.py +260 -0
  288. claude_mpm/utils/git_analyzer.py +407 -0
  289. claude_mpm/utils/robust_installer.py +73 -19
  290. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +129 -12
  291. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +295 -193
  292. claude_mpm/dashboard/static/css/code-tree.css +0 -1639
  293. claude_mpm/dashboard/static/index-hub-backup.html +0 -713
  294. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
  295. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
  296. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
  297. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
  298. claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
  299. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
  300. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  301. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
  302. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  303. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  304. claude_mpm/services/project/analyzer_refactored.py +0 -450
  305. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
  306. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
  307. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
  308. {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]