claude-mpm 3.9.11__py3-none-any.whl → 4.0.3__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 (419) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +2 -2
  3. claude_mpm/__main__.py +3 -2
  4. claude_mpm/agents/__init__.py +85 -79
  5. claude_mpm/agents/agent_loader.py +464 -1003
  6. claude_mpm/agents/agent_loader_integration.py +45 -45
  7. claude_mpm/agents/agents_metadata.py +29 -30
  8. claude_mpm/agents/async_agent_loader.py +156 -138
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/base_agent_loader.py +179 -151
  11. claude_mpm/agents/frontmatter_validator.py +229 -130
  12. claude_mpm/agents/schema/agent_schema.json +1 -1
  13. claude_mpm/agents/system_agent_config.py +213 -147
  14. claude_mpm/agents/templates/__init__.py +13 -13
  15. claude_mpm/agents/templates/code_analyzer.json +2 -2
  16. claude_mpm/agents/templates/data_engineer.json +1 -1
  17. claude_mpm/agents/templates/documentation.json +23 -11
  18. claude_mpm/agents/templates/engineer.json +22 -6
  19. claude_mpm/agents/templates/memory_manager.json +1 -1
  20. claude_mpm/agents/templates/ops.json +2 -2
  21. claude_mpm/agents/templates/project_organizer.json +1 -1
  22. claude_mpm/agents/templates/qa.json +1 -1
  23. claude_mpm/agents/templates/refactoring_engineer.json +222 -0
  24. claude_mpm/agents/templates/research.json +20 -14
  25. claude_mpm/agents/templates/security.json +1 -1
  26. claude_mpm/agents/templates/ticketing.json +1 -1
  27. claude_mpm/agents/templates/version_control.json +1 -1
  28. claude_mpm/agents/templates/web_qa.json +3 -1
  29. claude_mpm/agents/templates/web_ui.json +2 -2
  30. claude_mpm/cli/__init__.py +79 -51
  31. claude_mpm/cli/__main__.py +3 -2
  32. claude_mpm/cli/commands/__init__.py +20 -20
  33. claude_mpm/cli/commands/agents.py +279 -247
  34. claude_mpm/cli/commands/aggregate.py +138 -157
  35. claude_mpm/cli/commands/cleanup.py +147 -147
  36. claude_mpm/cli/commands/config.py +93 -76
  37. claude_mpm/cli/commands/info.py +17 -16
  38. claude_mpm/cli/commands/mcp.py +140 -905
  39. claude_mpm/cli/commands/mcp_command_router.py +139 -0
  40. claude_mpm/cli/commands/mcp_config_commands.py +20 -0
  41. claude_mpm/cli/commands/mcp_install_commands.py +20 -0
  42. claude_mpm/cli/commands/mcp_server_commands.py +175 -0
  43. claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
  44. claude_mpm/cli/commands/memory.py +239 -203
  45. claude_mpm/cli/commands/monitor.py +203 -81
  46. claude_mpm/cli/commands/run.py +380 -429
  47. claude_mpm/cli/commands/run_config_checker.py +160 -0
  48. claude_mpm/cli/commands/socketio_monitor.py +235 -0
  49. claude_mpm/cli/commands/tickets.py +305 -197
  50. claude_mpm/cli/parser.py +24 -1156
  51. claude_mpm/cli/parsers/__init__.py +29 -0
  52. claude_mpm/cli/parsers/agents_parser.py +136 -0
  53. claude_mpm/cli/parsers/base_parser.py +331 -0
  54. claude_mpm/cli/parsers/config_parser.py +85 -0
  55. claude_mpm/cli/parsers/mcp_parser.py +152 -0
  56. claude_mpm/cli/parsers/memory_parser.py +138 -0
  57. claude_mpm/cli/parsers/monitor_parser.py +104 -0
  58. claude_mpm/cli/parsers/run_parser.py +147 -0
  59. claude_mpm/cli/parsers/tickets_parser.py +203 -0
  60. claude_mpm/cli/ticket_cli.py +7 -3
  61. claude_mpm/cli/utils.py +55 -37
  62. claude_mpm/cli_module/__init__.py +6 -6
  63. claude_mpm/cli_module/args.py +188 -140
  64. claude_mpm/cli_module/commands.py +79 -70
  65. claude_mpm/cli_module/migration_example.py +38 -60
  66. claude_mpm/config/__init__.py +32 -25
  67. claude_mpm/config/agent_config.py +151 -119
  68. claude_mpm/config/experimental_features.py +71 -73
  69. claude_mpm/config/paths.py +94 -208
  70. claude_mpm/config/socketio_config.py +84 -73
  71. claude_mpm/constants.py +35 -18
  72. claude_mpm/core/__init__.py +9 -6
  73. claude_mpm/core/agent_name_normalizer.py +68 -71
  74. claude_mpm/core/agent_registry.py +372 -521
  75. claude_mpm/core/agent_session_manager.py +74 -63
  76. claude_mpm/core/base_service.py +116 -87
  77. claude_mpm/core/cache.py +119 -153
  78. claude_mpm/core/claude_runner.py +425 -1120
  79. claude_mpm/core/config.py +263 -168
  80. claude_mpm/core/config_aliases.py +69 -61
  81. claude_mpm/core/config_constants.py +292 -0
  82. claude_mpm/core/constants.py +57 -99
  83. claude_mpm/core/container.py +211 -178
  84. claude_mpm/core/exceptions.py +233 -89
  85. claude_mpm/core/factories.py +92 -54
  86. claude_mpm/core/framework_loader.py +378 -220
  87. claude_mpm/core/hook_manager.py +198 -83
  88. claude_mpm/core/hook_performance_config.py +136 -0
  89. claude_mpm/core/injectable_service.py +61 -55
  90. claude_mpm/core/interactive_session.py +165 -155
  91. claude_mpm/core/interfaces.py +221 -195
  92. claude_mpm/core/lazy.py +96 -96
  93. claude_mpm/core/logger.py +133 -107
  94. claude_mpm/core/logging_config.py +185 -157
  95. claude_mpm/core/minimal_framework_loader.py +20 -15
  96. claude_mpm/core/mixins.py +30 -29
  97. claude_mpm/core/oneshot_session.py +215 -181
  98. claude_mpm/core/optimized_agent_loader.py +134 -138
  99. claude_mpm/core/optimized_startup.py +159 -157
  100. claude_mpm/core/pm_hook_interceptor.py +85 -72
  101. claude_mpm/core/service_registry.py +103 -101
  102. claude_mpm/core/session_manager.py +97 -87
  103. claude_mpm/core/socketio_pool.py +212 -158
  104. claude_mpm/core/tool_access_control.py +58 -51
  105. claude_mpm/core/types.py +46 -24
  106. claude_mpm/core/typing_utils.py +166 -82
  107. claude_mpm/core/unified_agent_registry.py +721 -0
  108. claude_mpm/core/unified_config.py +550 -0
  109. claude_mpm/core/unified_paths.py +549 -0
  110. claude_mpm/dashboard/index.html +1 -1
  111. claude_mpm/dashboard/open_dashboard.py +51 -17
  112. claude_mpm/dashboard/static/css/dashboard.css +27 -8
  113. claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
  114. claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
  115. claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
  116. claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
  117. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
  118. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
  119. claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
  120. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
  121. claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
  122. claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
  123. claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
  124. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
  125. claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
  126. claude_mpm/dashboard/static/dist/dashboard.js +2 -0
  127. claude_mpm/dashboard/static/dist/socket-client.js +2 -0
  128. claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
  129. claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
  130. claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
  131. claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
  132. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
  133. claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
  134. claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
  135. claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
  136. claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
  137. claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
  138. claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
  139. claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
  140. claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
  141. claude_mpm/dashboard/static/js/dashboard.js +178 -453
  142. claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
  143. claude_mpm/dashboard/static/js/socket-client.js +120 -54
  144. claude_mpm/dashboard/templates/index.html +40 -50
  145. claude_mpm/experimental/cli_enhancements.py +60 -58
  146. claude_mpm/generators/__init__.py +1 -1
  147. claude_mpm/generators/agent_profile_generator.py +75 -65
  148. claude_mpm/hooks/__init__.py +1 -1
  149. claude_mpm/hooks/base_hook.py +33 -28
  150. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  151. claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
  152. claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
  153. claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
  154. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
  155. claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
  156. claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
  157. claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
  158. claude_mpm/hooks/memory_integration_hook.py +140 -100
  159. claude_mpm/hooks/tool_call_interceptor.py +89 -76
  160. claude_mpm/hooks/validation_hooks.py +57 -49
  161. claude_mpm/init.py +145 -121
  162. claude_mpm/models/__init__.py +9 -9
  163. claude_mpm/models/agent_definition.py +33 -23
  164. claude_mpm/models/agent_session.py +228 -200
  165. claude_mpm/scripts/__init__.py +1 -1
  166. claude_mpm/scripts/socketio_daemon.py +192 -75
  167. claude_mpm/scripts/socketio_server_manager.py +328 -0
  168. claude_mpm/scripts/start_activity_logging.py +25 -22
  169. claude_mpm/services/__init__.py +68 -43
  170. claude_mpm/services/agent_capabilities_service.py +271 -0
  171. claude_mpm/services/agents/__init__.py +23 -32
  172. claude_mpm/services/agents/deployment/__init__.py +3 -3
  173. claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
  174. claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
  175. claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
  176. claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
  177. claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
  178. claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
  179. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
  180. claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
  181. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
  182. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
  183. claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
  184. claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
  185. claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
  186. claude_mpm/services/agents/deployment/agent_validator.py +352 -0
  187. claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
  188. claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
  189. claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
  190. claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
  191. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  192. claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
  193. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  194. claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
  195. claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
  196. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  197. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  198. claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
  199. claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
  200. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  201. claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
  202. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  203. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  204. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  205. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  206. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
  207. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  208. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  209. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
  210. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
  211. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
  212. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
  213. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
  214. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  215. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
  216. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  217. claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
  218. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
  219. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  220. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  221. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  222. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  223. claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
  224. claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
  225. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  226. claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
  227. claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
  228. claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
  229. claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
  230. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  231. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  232. claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
  233. claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
  234. claude_mpm/services/agents/loading/__init__.py +2 -2
  235. claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
  236. claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
  237. claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
  238. claude_mpm/services/agents/management/__init__.py +2 -2
  239. claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
  240. claude_mpm/services/agents/management/agent_management_service.py +209 -156
  241. claude_mpm/services/agents/memory/__init__.py +9 -6
  242. claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
  243. claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
  244. claude_mpm/services/agents/memory/analyzer.py +430 -0
  245. claude_mpm/services/agents/memory/content_manager.py +376 -0
  246. claude_mpm/services/agents/memory/template_generator.py +468 -0
  247. claude_mpm/services/agents/registry/__init__.py +7 -10
  248. claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
  249. claude_mpm/services/agents/registry/modification_tracker.py +351 -285
  250. claude_mpm/services/async_session_logger.py +187 -153
  251. claude_mpm/services/claude_session_logger.py +87 -72
  252. claude_mpm/services/command_handler_service.py +217 -0
  253. claude_mpm/services/communication/__init__.py +3 -2
  254. claude_mpm/services/core/__init__.py +50 -97
  255. claude_mpm/services/core/base.py +60 -53
  256. claude_mpm/services/core/interfaces/__init__.py +188 -0
  257. claude_mpm/services/core/interfaces/agent.py +351 -0
  258. claude_mpm/services/core/interfaces/communication.py +343 -0
  259. claude_mpm/services/core/interfaces/infrastructure.py +413 -0
  260. claude_mpm/services/core/interfaces/service.py +434 -0
  261. claude_mpm/services/core/interfaces.py +19 -944
  262. claude_mpm/services/event_aggregator.py +208 -170
  263. claude_mpm/services/exceptions.py +387 -308
  264. claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
  265. claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
  266. claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
  267. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
  268. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
  269. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
  270. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
  271. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
  272. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  273. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  274. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  275. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  276. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
  277. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  278. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  279. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
  280. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
  281. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  282. claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
  283. claude_mpm/services/hook_service.py +106 -114
  284. claude_mpm/services/infrastructure/__init__.py +7 -5
  285. claude_mpm/services/infrastructure/context_preservation.py +233 -199
  286. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  287. claude_mpm/services/infrastructure/logging.py +83 -76
  288. claude_mpm/services/infrastructure/monitoring.py +547 -404
  289. claude_mpm/services/mcp_gateway/__init__.py +30 -13
  290. claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
  291. claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
  292. claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
  293. claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
  294. claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
  295. claude_mpm/services/mcp_gateway/core/base.py +80 -67
  296. claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
  297. claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
  298. claude_mpm/services/mcp_gateway/main.py +287 -137
  299. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  300. claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
  301. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  302. claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
  303. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
  304. claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
  305. claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
  306. claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
  307. claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
  308. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
  309. claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
  310. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
  311. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
  312. claude_mpm/services/memory/__init__.py +2 -2
  313. claude_mpm/services/memory/builder.py +451 -362
  314. claude_mpm/services/memory/cache/__init__.py +2 -2
  315. claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
  316. claude_mpm/services/memory/cache/simple_cache.py +107 -93
  317. claude_mpm/services/memory/indexed_memory.py +195 -193
  318. claude_mpm/services/memory/optimizer.py +267 -234
  319. claude_mpm/services/memory/router.py +571 -263
  320. claude_mpm/services/memory_hook_service.py +237 -0
  321. claude_mpm/services/port_manager.py +223 -0
  322. claude_mpm/services/project/__init__.py +3 -3
  323. claude_mpm/services/project/analyzer.py +451 -305
  324. claude_mpm/services/project/registry.py +262 -240
  325. claude_mpm/services/recovery_manager.py +287 -231
  326. claude_mpm/services/response_tracker.py +87 -67
  327. claude_mpm/services/runner_configuration_service.py +587 -0
  328. claude_mpm/services/session_management_service.py +304 -0
  329. claude_mpm/services/socketio/__init__.py +4 -4
  330. claude_mpm/services/socketio/client_proxy.py +174 -0
  331. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  332. claude_mpm/services/socketio/handlers/base.py +44 -30
  333. claude_mpm/services/socketio/handlers/connection.py +145 -65
  334. claude_mpm/services/socketio/handlers/file.py +123 -108
  335. claude_mpm/services/socketio/handlers/git.py +607 -373
  336. claude_mpm/services/socketio/handlers/hook.py +170 -0
  337. claude_mpm/services/socketio/handlers/memory.py +4 -4
  338. claude_mpm/services/socketio/handlers/project.py +4 -4
  339. claude_mpm/services/socketio/handlers/registry.py +53 -38
  340. claude_mpm/services/socketio/server/__init__.py +18 -0
  341. claude_mpm/services/socketio/server/broadcaster.py +252 -0
  342. claude_mpm/services/socketio/server/core.py +399 -0
  343. claude_mpm/services/socketio/server/main.py +323 -0
  344. claude_mpm/services/socketio_client_manager.py +160 -133
  345. claude_mpm/services/socketio_server.py +36 -1885
  346. claude_mpm/services/subprocess_launcher_service.py +316 -0
  347. claude_mpm/services/system_instructions_service.py +258 -0
  348. claude_mpm/services/ticket_manager.py +19 -533
  349. claude_mpm/services/utility_service.py +285 -0
  350. claude_mpm/services/version_control/__init__.py +18 -21
  351. claude_mpm/services/version_control/branch_strategy.py +20 -10
  352. claude_mpm/services/version_control/conflict_resolution.py +37 -13
  353. claude_mpm/services/version_control/git_operations.py +52 -21
  354. claude_mpm/services/version_control/semantic_versioning.py +92 -53
  355. claude_mpm/services/version_control/version_parser.py +145 -125
  356. claude_mpm/services/version_service.py +270 -0
  357. claude_mpm/storage/__init__.py +2 -2
  358. claude_mpm/storage/state_storage.py +177 -181
  359. claude_mpm/ticket_wrapper.py +2 -2
  360. claude_mpm/utils/__init__.py +2 -2
  361. claude_mpm/utils/agent_dependency_loader.py +453 -243
  362. claude_mpm/utils/config_manager.py +157 -118
  363. claude_mpm/utils/console.py +1 -1
  364. claude_mpm/utils/dependency_cache.py +102 -107
  365. claude_mpm/utils/dependency_manager.py +52 -47
  366. claude_mpm/utils/dependency_strategies.py +131 -96
  367. claude_mpm/utils/environment_context.py +110 -102
  368. claude_mpm/utils/error_handler.py +75 -55
  369. claude_mpm/utils/file_utils.py +80 -67
  370. claude_mpm/utils/framework_detection.py +12 -11
  371. claude_mpm/utils/import_migration_example.py +12 -60
  372. claude_mpm/utils/imports.py +48 -45
  373. claude_mpm/utils/path_operations.py +100 -93
  374. claude_mpm/utils/robust_installer.py +172 -164
  375. claude_mpm/utils/session_logging.py +30 -23
  376. claude_mpm/utils/subprocess_utils.py +99 -61
  377. claude_mpm/validation/__init__.py +1 -1
  378. claude_mpm/validation/agent_validator.py +151 -111
  379. claude_mpm/validation/frontmatter_validator.py +92 -71
  380. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +27 -1
  381. claude_mpm-4.0.3.dist-info/RECORD +402 -0
  382. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
  383. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
  384. claude_mpm/cli/commands/run_guarded.py +0 -511
  385. claude_mpm/config/memory_guardian_config.py +0 -325
  386. claude_mpm/config/memory_guardian_yaml.py +0 -335
  387. claude_mpm/core/config_paths.py +0 -150
  388. claude_mpm/core/memory_aware_runner.py +0 -353
  389. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  390. claude_mpm/deployment_paths.py +0 -261
  391. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  392. claude_mpm/models/state_models.py +0 -433
  393. claude_mpm/services/agent/__init__.py +0 -24
  394. claude_mpm/services/agent/deployment.py +0 -2548
  395. claude_mpm/services/agent/management.py +0 -598
  396. claude_mpm/services/agent/registry.py +0 -813
  397. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  398. claude_mpm/services/communication/socketio.py +0 -1935
  399. claude_mpm/services/communication/websocket.py +0 -479
  400. claude_mpm/services/framework_claude_md_generator.py +0 -624
  401. claude_mpm/services/health_monitor.py +0 -893
  402. claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
  403. claude_mpm/services/infrastructure/health_monitor.py +0 -775
  404. claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
  405. claude_mpm/services/infrastructure/memory_guardian.py +0 -944
  406. claude_mpm/services/infrastructure/restart_protection.py +0 -642
  407. claude_mpm/services/infrastructure/state_manager.py +0 -774
  408. claude_mpm/services/mcp_gateway/manager.py +0 -334
  409. claude_mpm/services/optimized_hook_service.py +0 -542
  410. claude_mpm/services/project_analyzer.py +0 -864
  411. claude_mpm/services/project_registry.py +0 -608
  412. claude_mpm/services/standalone_socketio_server.py +0 -1300
  413. claude_mpm/services/ticket_manager_di.py +0 -318
  414. claude_mpm/services/ticketing_service_original.py +0 -510
  415. claude_mpm/utils/paths.py +0 -395
  416. claude_mpm/utils/platform_memory.py +0 -524
  417. claude_mpm-3.9.11.dist-info/RECORD +0 -306
  418. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
  419. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,5 @@
1
+ from pathlib import Path
2
+
1
3
  """
2
4
  Dynamic agent dependency loader for runtime dependency management.
3
5
 
@@ -6,15 +8,15 @@ at runtime, rather than requiring all possible agent dependencies to be
6
8
  installed upfront.
7
9
  """
8
10
 
11
+ import hashlib
9
12
  import json
13
+ import logging
10
14
  import subprocess
11
15
  import sys
12
- import hashlib
13
16
  import time
14
- from pathlib import Path
15
- from typing import Dict, List, Set, Tuple, Optional
16
- import logging
17
- from packaging.requirements import Requirement, InvalidRequirement
17
+ from typing import Dict, List, Optional, Set, Tuple
18
+
19
+ from packaging.requirements import InvalidRequirement, Requirement
18
20
 
19
21
  from ..core.logger import get_logger
20
22
 
@@ -24,15 +26,15 @@ logger = get_logger(__name__)
24
26
  class AgentDependencyLoader:
25
27
  """
26
28
  Dynamically loads and manages dependencies for deployed agents.
27
-
29
+
28
30
  Only checks/installs dependencies for agents that are actually deployed
29
31
  and being used, rather than all possible agents.
30
32
  """
31
-
33
+
32
34
  def __init__(self, auto_install: bool = False):
33
35
  """
34
36
  Initialize the agent dependency loader.
35
-
37
+
36
38
  Args:
37
39
  auto_install: If True, automatically install missing dependencies.
38
40
  If False, only check and report missing dependencies.
@@ -42,665 +44,873 @@ class AgentDependencyLoader:
42
44
  self.agent_dependencies: Dict[str, Dict] = {}
43
45
  self.missing_dependencies: Dict[str, List[str]] = {}
44
46
  self.checked_packages: Set[str] = set()
45
- self.deployment_state_file = Path.cwd() / ".claude" / "agents" / ".mpm_deployment_state"
46
-
47
+ self.deployment_state_file = (
48
+ Path.cwd() / ".claude" / "agents" / ".mpm_deployment_state"
49
+ )
50
+
47
51
  def discover_deployed_agents(self) -> Dict[str, Path]:
48
52
  """
49
53
  Discover which agents are currently deployed in .claude/agents/
50
-
54
+
51
55
  Returns:
52
56
  Dictionary mapping agent IDs to their file paths
53
57
  """
54
58
  deployed_agents = {}
55
59
  claude_agents_dir = Path.cwd() / ".claude" / "agents"
56
-
60
+
57
61
  if not claude_agents_dir.exists():
58
62
  logger.debug("No .claude/agents directory found")
59
63
  return deployed_agents
60
-
64
+
61
65
  # Scan for deployed agent markdown files
62
66
  for agent_file in claude_agents_dir.glob("*.md"):
63
67
  agent_id = agent_file.stem
64
68
  deployed_agents[agent_id] = agent_file
65
69
  logger.debug(f"Found deployed agent: {agent_id}")
66
-
70
+
67
71
  logger.info(f"Discovered {len(deployed_agents)} deployed agents")
68
72
  self.deployed_agents = deployed_agents
69
73
  return deployed_agents
70
-
74
+
71
75
  def load_agent_dependencies(self) -> Dict[str, Dict]:
72
76
  """
73
77
  Load dependency information for deployed agents from their source configs.
74
-
78
+
75
79
  Returns:
76
80
  Dictionary mapping agent IDs to their dependency requirements
77
81
  """
78
82
  agent_dependencies = {}
79
-
83
+
80
84
  # Define paths to check for agent configs (in precedence order)
81
85
  config_paths = [
82
86
  Path.cwd() / ".claude-mpm" / "agents", # PROJECT
83
87
  Path.home() / ".claude-mpm" / "agents", # USER
84
- Path.cwd() / "src" / "claude_mpm" / "agents" / "templates" # SYSTEM
88
+ Path.cwd() / "src" / "claude_mpm" / "agents" / "templates", # SYSTEM
85
89
  ]
86
-
90
+
87
91
  for agent_id in self.deployed_agents:
88
92
  # Try to find the agent's JSON config
89
93
  for config_dir in config_paths:
90
94
  config_file = config_dir / f"{agent_id}.json"
91
95
  if config_file.exists():
92
96
  try:
93
- with open(config_file, 'r') as f:
97
+ with open(config_file, "r") as f:
94
98
  config = json.load(f)
95
- if 'dependencies' in config:
96
- agent_dependencies[agent_id] = config['dependencies']
99
+ if "dependencies" in config:
100
+ agent_dependencies[agent_id] = config["dependencies"]
97
101
  logger.debug(f"Loaded dependencies for {agent_id}")
98
102
  break
99
103
  except Exception as e:
100
104
  logger.warning(f"Failed to load config for {agent_id}: {e}")
101
-
105
+
102
106
  self.agent_dependencies = agent_dependencies
103
107
  logger.info(f"Loaded dependencies for {len(agent_dependencies)} agents")
104
108
  return agent_dependencies
105
-
109
+
106
110
  def check_python_dependency(self, package_spec: str) -> Tuple[bool, Optional[str]]:
107
111
  """
108
112
  Check if a Python package dependency is satisfied.
109
-
113
+
110
114
  Args:
111
115
  package_spec: Package specification (e.g., "pandas>=2.0.0")
112
-
116
+
113
117
  Returns:
114
118
  Tuple of (is_satisfied, installed_version)
115
119
  """
116
120
  try:
117
121
  req = Requirement(package_spec)
118
122
  package_name = req.name
119
-
123
+
120
124
  # Skip if already checked
121
125
  if package_name in self.checked_packages:
122
126
  return True, None
123
-
127
+
128
+ # Check if it's a built-in module first
129
+ if self._is_builtin_module(package_name):
130
+ self.checked_packages.add(package_name)
131
+ return True, "built-in"
132
+
124
133
  # Try to import and check version
125
134
  try:
126
135
  import importlib.metadata
136
+
127
137
  try:
128
138
  version = importlib.metadata.version(package_name)
129
139
  self.checked_packages.add(package_name)
130
-
140
+
131
141
  # Check if version satisfies requirement
132
142
  if req.specifier.contains(version):
133
143
  return True, version
134
144
  else:
135
- logger.debug(f"{package_name} {version} does not satisfy {req.specifier}")
145
+ logger.debug(
146
+ f"{package_name} {version} does not satisfy {req.specifier}"
147
+ )
136
148
  return False, version
137
-
149
+
138
150
  except importlib.metadata.PackageNotFoundError:
139
151
  return False, None
140
-
152
+
141
153
  except ImportError:
142
154
  # Fallback for older Python versions
143
155
  try:
144
156
  import pkg_resources
157
+
145
158
  version = pkg_resources.get_distribution(package_name).version
146
159
  self.checked_packages.add(package_name)
147
-
160
+
148
161
  if req.specifier.contains(version):
149
162
  return True, version
150
163
  else:
151
164
  return False, version
152
-
165
+
153
166
  except pkg_resources.DistributionNotFound:
154
167
  return False, None
155
-
168
+
156
169
  except InvalidRequirement as e:
157
170
  logger.warning(f"Invalid requirement specification: {package_spec}: {e}")
158
171
  return False, None
159
-
172
+
173
+ def _is_builtin_module(self, module_name: str) -> bool:
174
+ """
175
+ Check if a module is a built-in Python module.
176
+
177
+ Args:
178
+ module_name: Name of the module to check
179
+
180
+ Returns:
181
+ True if the module is built-in, False otherwise
182
+ """
183
+ # List of common built-in modules that don't have distribution metadata
184
+ builtin_modules = {
185
+ "json",
186
+ "pathlib",
187
+ "os",
188
+ "sys",
189
+ "datetime",
190
+ "time",
191
+ "math",
192
+ "random",
193
+ "collections",
194
+ "itertools",
195
+ "functools",
196
+ "operator",
197
+ "copy",
198
+ "pickle",
199
+ "sqlite3",
200
+ "urllib",
201
+ "http",
202
+ "email",
203
+ "html",
204
+ "xml",
205
+ "csv",
206
+ "configparser",
207
+ "logging",
208
+ "unittest",
209
+ "doctest",
210
+ "pdb",
211
+ "profile",
212
+ "timeit",
213
+ "trace",
214
+ "gc",
215
+ "weakref",
216
+ "types",
217
+ "inspect",
218
+ "importlib",
219
+ "pkgutil",
220
+ "modulefinder",
221
+ "runpy",
222
+ "ast",
223
+ "symtable",
224
+ "keyword",
225
+ "token",
226
+ "tokenize",
227
+ "tabnanny",
228
+ "pyclbr",
229
+ "py_compile",
230
+ "compileall",
231
+ "dis",
232
+ "pickletools",
233
+ "platform",
234
+ "ctypes",
235
+ "struct",
236
+ "codecs",
237
+ "unicodedata",
238
+ "stringprep",
239
+ "readline",
240
+ "rlcompleter",
241
+ "subprocess",
242
+ "sched",
243
+ "queue",
244
+ "threading",
245
+ "multiprocessing",
246
+ "concurrent",
247
+ "asyncio",
248
+ "socket",
249
+ "ssl",
250
+ "select",
251
+ "selectors",
252
+ "signal",
253
+ "mmap",
254
+ "errno",
255
+ "io",
256
+ "tempfile",
257
+ "glob",
258
+ "fnmatch",
259
+ "linecache",
260
+ "shutil",
261
+ "stat",
262
+ "filecmp",
263
+ "tarfile",
264
+ "zipfile",
265
+ "gzip",
266
+ "bz2",
267
+ "lzma",
268
+ "zlib",
269
+ "hashlib",
270
+ "hmac",
271
+ "secrets",
272
+ "base64",
273
+ "binascii",
274
+ "quopri",
275
+ "uu",
276
+ "string",
277
+ "re",
278
+ "difflib",
279
+ "textwrap",
280
+ "unicodedata",
281
+ "stringprep",
282
+ "calendar",
283
+ "locale",
284
+ "gettext",
285
+ "argparse",
286
+ "optparse",
287
+ "getopt",
288
+ "shlex",
289
+ "cmd",
290
+ "pprint",
291
+ "reprlib",
292
+ "enum",
293
+ "numbers",
294
+ "decimal",
295
+ "fractions",
296
+ "statistics",
297
+ "array",
298
+ "bisect",
299
+ "heapq",
300
+ "contextlib",
301
+ "abc",
302
+ "atexit",
303
+ "traceback",
304
+ "warnings",
305
+ "dataclasses",
306
+ "graphlib",
307
+ }
308
+
309
+ # Check if it's in our known built-in modules
310
+ if module_name in builtin_modules:
311
+ return True
312
+
313
+ # Try to import it and check if it's a built-in module
314
+ try:
315
+ import importlib.util
316
+
317
+ spec = importlib.util.find_spec(module_name)
318
+ if spec is not None and spec.origin is None:
319
+ # Built-in modules have spec.origin as None
320
+ return True
321
+ except (ImportError, ModuleNotFoundError, ValueError):
322
+ pass
323
+
324
+ return False
325
+
160
326
  def check_system_dependency(self, command: str) -> bool:
161
327
  """
162
328
  Check if a system command is available in PATH.
163
-
329
+
164
330
  Args:
165
331
  command: System command to check (e.g., "git")
166
-
332
+
167
333
  Returns:
168
334
  True if command is available, False otherwise
169
335
  """
170
336
  try:
171
337
  result = subprocess.run(
172
- ["which", command],
173
- capture_output=True,
174
- text=True,
175
- timeout=5
338
+ ["which", command], capture_output=True, text=True, timeout=5
176
339
  )
177
340
  return result.returncode == 0
178
341
  except Exception:
179
342
  return False
180
-
343
+
181
344
  def analyze_dependencies(self) -> Dict[str, Dict]:
182
345
  """
183
346
  Analyze dependencies for all deployed agents.
184
-
347
+
185
348
  Returns:
186
349
  Analysis results including missing and satisfied dependencies
187
350
  """
188
351
  results = {
189
- 'agents': {},
190
- 'summary': {
191
- 'total_agents': len(self.deployed_agents),
192
- 'agents_with_deps': 0,
193
- 'missing_python': [],
194
- 'missing_system': [],
195
- 'satisfied_python': [],
196
- 'satisfied_system': []
197
- }
352
+ "agents": {},
353
+ "summary": {
354
+ "total_agents": len(self.deployed_agents),
355
+ "agents_with_deps": 0,
356
+ "missing_python": [],
357
+ "missing_system": [],
358
+ "satisfied_python": [],
359
+ "satisfied_system": [],
360
+ },
198
361
  }
199
-
362
+
200
363
  for agent_id, deps in self.agent_dependencies.items():
201
364
  agent_result = {
202
- 'python': {'satisfied': [], 'missing': [], 'outdated': []},
203
- 'system': {'satisfied': [], 'missing': []}
365
+ "python": {"satisfied": [], "missing": [], "outdated": []},
366
+ "system": {"satisfied": [], "missing": []},
204
367
  }
205
-
368
+
206
369
  # Check Python dependencies
207
- if 'python' in deps:
208
- for dep_spec in deps['python']:
370
+ if "python" in deps:
371
+ for dep_spec in deps["python"]:
209
372
  is_satisfied, version = self.check_python_dependency(dep_spec)
210
373
  if is_satisfied:
211
- agent_result['python']['satisfied'].append(dep_spec)
212
- if dep_spec not in results['summary']['satisfied_python']:
213
- results['summary']['satisfied_python'].append(dep_spec)
374
+ agent_result["python"]["satisfied"].append(dep_spec)
375
+ if dep_spec not in results["summary"]["satisfied_python"]:
376
+ results["summary"]["satisfied_python"].append(dep_spec)
214
377
  else:
215
378
  if version: # Installed but wrong version
216
- agent_result['python']['outdated'].append(f"{dep_spec} (have {version})")
379
+ agent_result["python"]["outdated"].append(
380
+ f"{dep_spec} (have {version})"
381
+ )
217
382
  else: # Not installed
218
- agent_result['python']['missing'].append(dep_spec)
219
- if dep_spec not in results['summary']['missing_python']:
220
- results['summary']['missing_python'].append(dep_spec)
221
-
383
+ agent_result["python"]["missing"].append(dep_spec)
384
+ if dep_spec not in results["summary"]["missing_python"]:
385
+ results["summary"]["missing_python"].append(dep_spec)
386
+
222
387
  # Check system dependencies
223
- if 'system' in deps:
224
- for command in deps['system']:
388
+ if "system" in deps:
389
+ for command in deps["system"]:
225
390
  if self.check_system_dependency(command):
226
- agent_result['system']['satisfied'].append(command)
227
- if command not in results['summary']['satisfied_system']:
228
- results['summary']['satisfied_system'].append(command)
391
+ agent_result["system"]["satisfied"].append(command)
392
+ if command not in results["summary"]["satisfied_system"]:
393
+ results["summary"]["satisfied_system"].append(command)
229
394
  else:
230
- agent_result['system']['missing'].append(command)
231
- if command not in results['summary']['missing_system']:
232
- results['summary']['missing_system'].append(command)
233
-
234
- results['agents'][agent_id] = agent_result
235
- if 'python' in deps or 'system' in deps:
236
- results['summary']['agents_with_deps'] += 1
237
-
395
+ agent_result["system"]["missing"].append(command)
396
+ if command not in results["summary"]["missing_system"]:
397
+ results["summary"]["missing_system"].append(command)
398
+
399
+ results["agents"][agent_id] = agent_result
400
+ if "python" in deps or "system" in deps:
401
+ results["summary"]["agents_with_deps"] += 1
402
+
238
403
  return results
239
-
240
- def check_python_compatibility(self, dependencies: List[str]) -> Tuple[List[str], List[str]]:
404
+
405
+ def check_python_compatibility(
406
+ self, dependencies: List[str]
407
+ ) -> Tuple[List[str], List[str]]:
241
408
  """
242
409
  Check which dependencies are compatible with current Python version.
243
-
410
+
244
411
  Args:
245
412
  dependencies: List of package specifications to check
246
-
413
+
247
414
  Returns:
248
415
  Tuple of (compatible_deps, incompatible_deps)
249
416
  """
250
417
  import sys
418
+
251
419
  current_version = f"{sys.version_info.major}.{sys.version_info.minor}"
252
420
  compatible = []
253
421
  incompatible = []
254
-
422
+
255
423
  for dep in dependencies:
256
424
  try:
257
425
  # For known problematic packages in Python 3.13
258
426
  req = Requirement(dep)
259
427
  package_name = req.name.lower()
260
-
428
+
261
429
  # Known Python 3.13 incompatibilities
262
430
  if sys.version_info >= (3, 13):
263
- if package_name in ['ydata-profiling', 'pandas-profiling']:
431
+ if package_name in ["ydata-profiling", "pandas-profiling"]:
264
432
  incompatible.append(f"{dep} (requires Python <3.13)")
265
433
  continue
266
- elif package_name == 'apache-airflow':
434
+ elif package_name == "apache-airflow":
267
435
  incompatible.append(f"{dep} (requires Python <3.13)")
268
436
  continue
269
-
437
+
270
438
  # Default to compatible if we don't know
271
439
  compatible.append(dep)
272
-
440
+
273
441
  except Exception as e:
274
442
  logger.warning(f"Could not check compatibility for {dep}: {e}")
275
443
  compatible.append(dep) # Assume compatible if we can't check
276
-
444
+
277
445
  return compatible, incompatible
278
-
446
+
279
447
  def install_missing_dependencies(self, dependencies: List[str]) -> Tuple[bool, str]:
280
448
  """
281
449
  Install missing Python dependencies using robust retry logic.
282
-
450
+
283
451
  WHY: Network issues and temporary package unavailability can cause
284
452
  installation failures. Using the robust installer with retries
285
453
  significantly improves success rate.
286
-
454
+
287
455
  Args:
288
456
  dependencies: List of package specifications to install
289
-
457
+
290
458
  Returns:
291
459
  Tuple of (success, error_message)
292
460
  """
293
461
  if not dependencies:
294
462
  return True, ""
295
-
463
+
296
464
  # Check Python version compatibility first
297
465
  compatible, incompatible = self.check_python_compatibility(dependencies)
298
-
466
+
299
467
  if incompatible:
300
468
  logger.warning(f"Skipping {len(incompatible)} incompatible packages:")
301
469
  for dep in incompatible:
302
470
  logger.warning(f" - {dep}")
303
-
471
+
304
472
  if not compatible:
305
473
  return True, "No compatible packages to install"
306
-
474
+
307
475
  # Use robust installer with retry logic
308
476
  try:
309
477
  from .robust_installer import RobustPackageInstaller
310
-
311
- logger.info(f"Installing {len(compatible)} compatible dependencies with retry logic...")
478
+
479
+ logger.info(
480
+ f"Installing {len(compatible)} compatible dependencies with retry logic..."
481
+ )
312
482
  if incompatible:
313
- logger.info(f"(Skipping {len(incompatible)} incompatible with Python {sys.version_info.major}.{sys.version_info.minor})")
314
-
483
+ logger.info(
484
+ f"(Skipping {len(incompatible)} incompatible with Python {sys.version_info.major}.{sys.version_info.minor})"
485
+ )
486
+
315
487
  # Create installer with sensible defaults
316
488
  installer = RobustPackageInstaller(
317
- max_retries=3,
318
- retry_delay=2.0,
319
- timeout=300
489
+ max_retries=3, retry_delay=2.0, timeout=300
320
490
  )
321
-
491
+
322
492
  # Install packages
323
493
  successful, failed, errors = installer.install_packages(compatible)
324
-
494
+
325
495
  if failed:
326
496
  # Provide detailed error information
327
497
  error_details = []
328
498
  for pkg in failed:
329
499
  error_details.append(f"{pkg}: {errors.get(pkg, 'Unknown error')}")
330
-
331
- error_msg = f"Failed to install {len(failed)} packages:\n" + "\n".join(error_details)
500
+
501
+ error_msg = f"Failed to install {len(failed)} packages:\n" + "\n".join(
502
+ error_details
503
+ )
332
504
  logger.error(error_msg)
333
-
505
+
334
506
  # Partial success handling
335
507
  if successful:
336
508
  partial_msg = f"Partially successful: installed {len(successful)} of {len(compatible)} packages"
337
509
  logger.info(partial_msg)
338
510
  if incompatible:
339
- return True, f"{partial_msg}. Also skipped {len(incompatible)} incompatible"
511
+ return (
512
+ True,
513
+ f"{partial_msg}. Also skipped {len(incompatible)} incompatible",
514
+ )
340
515
  return True, partial_msg
341
-
516
+
342
517
  return False, error_msg
343
-
344
- logger.info(f"Successfully installed all {len(successful)} compatible dependencies")
518
+
519
+ logger.info(
520
+ f"Successfully installed all {len(successful)} compatible dependencies"
521
+ )
345
522
  if incompatible:
346
- return True, f"Installed {len(compatible)} packages, skipped {len(incompatible)} incompatible"
523
+ return (
524
+ True,
525
+ f"Installed {len(compatible)} packages, skipped {len(incompatible)} incompatible",
526
+ )
347
527
  return True, ""
348
-
528
+
349
529
  except ImportError:
350
530
  # Fallback to simple installation if robust installer not available
351
- logger.warning("Robust installer not available, falling back to simple installation")
531
+ logger.warning(
532
+ "Robust installer not available, falling back to simple installation"
533
+ )
352
534
  try:
353
535
  cmd = [sys.executable, "-m", "pip", "install"] + compatible
354
-
536
+
355
537
  result = subprocess.run(
356
- cmd,
357
- capture_output=True,
358
- text=True,
359
- timeout=300
538
+ cmd, capture_output=True, text=True, timeout=300
360
539
  )
361
-
540
+
362
541
  if result.returncode == 0:
363
542
  logger.info("Successfully installed compatible dependencies")
364
543
  if incompatible:
365
- return True, f"Installed {len(compatible)} packages, skipped {len(incompatible)} incompatible"
544
+ return (
545
+ True,
546
+ f"Installed {len(compatible)} packages, skipped {len(incompatible)} incompatible",
547
+ )
366
548
  return True, ""
367
549
  else:
368
550
  error_msg = f"Installation failed: {result.stderr}"
369
551
  logger.error(error_msg)
370
552
  return False, error_msg
371
-
553
+
372
554
  except Exception as e:
373
555
  error_msg = f"Failed to install dependencies: {e}"
374
556
  logger.error(error_msg)
375
557
  return False, error_msg
376
-
558
+
377
559
  except Exception as e:
378
560
  error_msg = f"Failed to install dependencies: {e}"
379
561
  logger.error(error_msg)
380
562
  return False, error_msg
381
-
563
+
382
564
  def load_and_check(self) -> Dict[str, Dict]:
383
565
  """
384
566
  Complete workflow: discover agents, load dependencies, and check them.
385
-
567
+
386
568
  Returns:
387
569
  Complete analysis results
388
570
  """
389
571
  # Discover deployed agents
390
572
  self.discover_deployed_agents()
391
-
573
+
392
574
  if not self.deployed_agents:
393
575
  logger.info("No deployed agents found")
394
- return {'agents': {}, 'summary': {'total_agents': 0}}
395
-
576
+ return {"agents": {}, "summary": {"total_agents": 0}}
577
+
396
578
  # Load their dependencies
397
579
  self.load_agent_dependencies()
398
-
580
+
399
581
  # Analyze what's missing
400
582
  results = self.analyze_dependencies()
401
-
583
+
402
584
  # Optionally auto-install missing dependencies
403
- if self.auto_install and results['summary']['missing_python']:
404
- logger.info(f"Auto-installing {len(results['summary']['missing_python'])} missing dependencies...")
405
- success, error = self.install_missing_dependencies(results['summary']['missing_python'])
585
+ if self.auto_install and results["summary"]["missing_python"]:
586
+ logger.info(
587
+ f"Auto-installing {len(results['summary']['missing_python'])} missing dependencies..."
588
+ )
589
+ success, error = self.install_missing_dependencies(
590
+ results["summary"]["missing_python"]
591
+ )
406
592
  if success:
407
593
  # Re-analyze after installation
408
594
  self.checked_packages.clear()
409
595
  results = self.analyze_dependencies()
410
-
596
+
411
597
  return results
412
-
598
+
413
599
  def format_report(self, results: Dict[str, Dict]) -> str:
414
600
  """
415
601
  Format a human-readable dependency report.
416
-
602
+
417
603
  Args:
418
604
  results: Analysis results from analyze_dependencies()
419
-
605
+
420
606
  Returns:
421
607
  Formatted report string
422
608
  """
423
609
  import sys
610
+
424
611
  lines = []
425
612
  lines.append("=" * 80)
426
613
  lines.append("AGENT DEPENDENCY ANALYSIS REPORT")
427
614
  lines.append("=" * 80)
428
615
  lines.append("")
429
-
616
+
430
617
  # Python version info
431
- lines.append(f"Python Version: {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
618
+ lines.append(
619
+ f"Python Version: {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
620
+ )
432
621
  lines.append("")
433
-
622
+
434
623
  # Summary
435
- summary = results['summary']
624
+ summary = results["summary"]
436
625
  lines.append(f"Deployed Agents: {summary['total_agents']}")
437
626
  lines.append(f"Agents with Dependencies: {summary['agents_with_deps']}")
438
627
  lines.append("")
439
-
628
+
440
629
  # Missing dependencies summary
441
- if summary['missing_python'] or summary['missing_system']:
630
+ if summary["missing_python"] or summary["missing_system"]:
442
631
  lines.append("⚠️ MISSING DEPENDENCIES:")
443
- if summary['missing_python']:
632
+ if summary["missing_python"]:
444
633
  lines.append(f" Python packages: {len(summary['missing_python'])}")
445
- for dep in summary['missing_python'][:5]: # Show first 5
634
+ for dep in summary["missing_python"][:5]: # Show first 5
446
635
  lines.append(f" - {dep}")
447
- if len(summary['missing_python']) > 5:
448
- lines.append(f" ... and {len(summary['missing_python']) - 5} more")
449
-
450
- if summary['missing_system']:
636
+ if len(summary["missing_python"]) > 5:
637
+ lines.append(
638
+ f" ... and {len(summary['missing_python']) - 5} more"
639
+ )
640
+
641
+ if summary["missing_system"]:
451
642
  lines.append(f" System commands: {len(summary['missing_system'])}")
452
- for cmd in summary['missing_system']:
643
+ for cmd in summary["missing_system"]:
453
644
  lines.append(f" - {cmd}")
454
645
  lines.append("")
455
-
646
+
456
647
  # Per-agent details (only for agents with issues)
457
648
  agents_with_issues = {
458
- agent_id: info for agent_id, info in results['agents'].items()
459
- if info['python']['missing'] or info['python']['outdated'] or info['system']['missing']
649
+ agent_id: info
650
+ for agent_id, info in results["agents"].items()
651
+ if info["python"]["missing"]
652
+ or info["python"]["outdated"]
653
+ or info["system"]["missing"]
460
654
  }
461
-
655
+
462
656
  if agents_with_issues:
463
657
  lines.append("AGENT-SPECIFIC ISSUES:")
464
658
  lines.append("-" * 40)
465
659
  for agent_id, info in agents_with_issues.items():
466
660
  lines.append(f"\n📦 {agent_id}:")
467
-
468
- if info['python']['missing']:
469
- lines.append(f" Missing Python: {', '.join(info['python']['missing'])}")
470
- if info['python']['outdated']:
471
- lines.append(f" Outdated Python: {', '.join(info['python']['outdated'])}")
472
- if info['system']['missing']:
473
- lines.append(f" Missing System: {', '.join(info['system']['missing'])}")
474
-
661
+
662
+ if info["python"]["missing"]:
663
+ lines.append(
664
+ f" Missing Python: {', '.join(info['python']['missing'])}"
665
+ )
666
+ if info["python"]["outdated"]:
667
+ lines.append(
668
+ f" Outdated Python: {', '.join(info['python']['outdated'])}"
669
+ )
670
+ if info["system"]["missing"]:
671
+ lines.append(
672
+ f" Missing System: {', '.join(info['system']['missing'])}"
673
+ )
674
+
475
675
  else:
476
676
  lines.append("✅ All agent dependencies are satisfied!")
477
-
677
+
478
678
  # Installation instructions
479
- if summary['missing_python']:
679
+ if summary["missing_python"]:
480
680
  lines.append("")
481
681
  lines.append("TO INSTALL MISSING PYTHON DEPENDENCIES:")
482
682
  lines.append("-" * 40)
483
-
683
+
484
684
  # Check for Python 3.13 compatibility issues
485
685
  import sys
686
+
486
687
  if sys.version_info >= (3, 13):
487
- compatible, incompatible = self.check_python_compatibility(summary['missing_python'])
688
+ compatible, incompatible = self.check_python_compatibility(
689
+ summary["missing_python"]
690
+ )
488
691
  if incompatible:
489
692
  lines.append("⚠️ Python 3.13 Compatibility Warning:")
490
- lines.append(f" {len(incompatible)} packages are not yet compatible with Python 3.13:")
693
+ lines.append(
694
+ f" {len(incompatible)} packages are not yet compatible with Python 3.13:"
695
+ )
491
696
  for dep in incompatible[:3]:
492
697
  lines.append(f" - {dep}")
493
698
  if len(incompatible) > 3:
494
699
  lines.append(f" ... and {len(incompatible) - 3} more")
495
700
  lines.append("")
496
- lines.append(" Consider using Python 3.12 or earlier for full compatibility.")
701
+ lines.append(
702
+ " Consider using Python 3.12 or earlier for full compatibility."
703
+ )
497
704
  lines.append("")
498
-
705
+
499
706
  lines.append("Option 1: Install all agent dependencies:")
500
707
  lines.append(' pip install "claude-mpm[agents]"')
501
708
  lines.append("")
502
709
  lines.append("Option 2: Install only what's needed:")
503
- deps_str = ' '.join(f'"{dep}"' for dep in summary['missing_python'][:3])
710
+ deps_str = " ".join(f'"{dep}"' for dep in summary["missing_python"][:3])
504
711
  lines.append(f" pip install {deps_str}")
505
- if len(summary['missing_python']) > 3:
712
+ if len(summary["missing_python"]) > 3:
506
713
  lines.append(f" # ... and {len(summary['missing_python']) - 3} more")
507
-
508
- if summary['missing_system']:
714
+
715
+ if summary["missing_system"]:
509
716
  lines.append("")
510
717
  lines.append("TO INSTALL MISSING SYSTEM DEPENDENCIES:")
511
718
  lines.append("-" * 40)
512
719
  lines.append("Use your system package manager:")
513
- lines.append(" # macOS: brew install " + ' '.join(summary['missing_system']))
514
- lines.append(" # Ubuntu: apt-get install " + ' '.join(summary['missing_system']))
515
-
720
+ lines.append(
721
+ " # macOS: brew install " + " ".join(summary["missing_system"])
722
+ )
723
+ lines.append(
724
+ " # Ubuntu: apt-get install " + " ".join(summary["missing_system"])
725
+ )
726
+
516
727
  lines.append("")
517
728
  lines.append("=" * 80)
518
-
519
- return '\n'.join(lines)
520
-
729
+
730
+ return "\n".join(lines)
731
+
521
732
  def calculate_deployment_hash(self) -> str:
522
733
  """
523
734
  Calculate a hash of the current agent deployment state.
524
-
735
+
525
736
  WHY: We use SHA256 hash of agent files to detect when agents have changed.
526
737
  This allows us to skip dependency checks when nothing has changed,
527
738
  improving startup performance.
528
-
739
+
529
740
  Returns:
530
741
  SHA256 hash of all deployed agent files and their content.
531
742
  """
532
743
  hash_obj = hashlib.sha256()
533
-
744
+
534
745
  # Discover current agents if not already done
535
746
  if not self.deployed_agents:
536
747
  self.discover_deployed_agents()
537
-
748
+
538
749
  # Sort agent IDs for consistent hashing
539
750
  for agent_id in sorted(self.deployed_agents.keys()):
540
751
  agent_path = self.deployed_agents[agent_id]
541
-
752
+
542
753
  # Include agent ID in hash
543
- hash_obj.update(agent_id.encode('utf-8'))
544
-
754
+ hash_obj.update(agent_id.encode("utf-8"))
755
+
545
756
  # Include file modification time and size for quick change detection
546
757
  try:
547
758
  stat = agent_path.stat()
548
- hash_obj.update(str(stat.st_mtime).encode('utf-8'))
549
- hash_obj.update(str(stat.st_size).encode('utf-8'))
550
-
759
+ hash_obj.update(str(stat.st_mtime).encode("utf-8"))
760
+ hash_obj.update(str(stat.st_size).encode("utf-8"))
761
+
551
762
  # Include file content for comprehensive change detection
552
- with open(agent_path, 'rb') as f:
763
+ with open(agent_path, "rb") as f:
553
764
  hash_obj.update(f.read())
554
765
  except Exception as e:
555
766
  logger.debug(f"Could not hash agent file {agent_path}: {e}")
556
767
  # Include error in hash to force recheck on next run
557
- hash_obj.update(f"error:{agent_id}:{e}".encode('utf-8'))
558
-
768
+ hash_obj.update(f"error:{agent_id}:{e}".encode("utf-8"))
769
+
559
770
  return hash_obj.hexdigest()
560
-
771
+
561
772
  def load_deployment_state(self) -> Dict:
562
773
  """
563
774
  Load the saved deployment state.
564
-
775
+
565
776
  Returns:
566
777
  Dictionary with deployment state or empty dict if not found.
567
778
  """
568
779
  if not self.deployment_state_file.exists():
569
780
  return {}
570
-
781
+
571
782
  try:
572
- with open(self.deployment_state_file, 'r') as f:
783
+ with open(self.deployment_state_file, "r") as f:
573
784
  return json.load(f)
574
785
  except Exception as e:
575
786
  logger.debug(f"Could not load deployment state: {e}")
576
787
  return {}
577
-
788
+
578
789
  def save_deployment_state(self, state: Dict) -> None:
579
790
  """
580
791
  Save the deployment state to disk.
581
-
792
+
582
793
  Args:
583
794
  state: Deployment state dictionary to save.
584
795
  """
585
796
  try:
586
797
  # Ensure directory exists
587
798
  self.deployment_state_file.parent.mkdir(parents=True, exist_ok=True)
588
-
589
- with open(self.deployment_state_file, 'w') as f:
799
+
800
+ with open(self.deployment_state_file, "w") as f:
590
801
  json.dump(state, f, indent=2)
591
802
  except Exception as e:
592
803
  logger.debug(f"Could not save deployment state: {e}")
593
-
804
+
594
805
  def has_agents_changed(self) -> Tuple[bool, str]:
595
806
  """
596
807
  Check if agents have changed since last dependency check.
597
-
808
+
598
809
  WHY: This is the core of our smart checking system. We only want to
599
810
  check dependencies when agents have actually changed, not on every run.
600
-
811
+
601
812
  Returns:
602
813
  Tuple of (has_changed, current_hash)
603
814
  """
604
815
  current_hash = self.calculate_deployment_hash()
605
816
  state = self.load_deployment_state()
606
-
607
- last_hash = state.get('deployment_hash')
608
- last_check_time = state.get('last_check_time', 0)
609
-
817
+
818
+ last_hash = state.get("deployment_hash")
819
+ last_check_time = state.get("last_check_time", 0)
820
+
610
821
  # Check if hash has changed
611
822
  if last_hash != current_hash:
612
823
  logger.info("Agent deployment has changed since last check")
613
824
  return True, current_hash
614
-
825
+
615
826
  # Also check if it's been more than 24 hours (optional staleness check)
616
827
  current_time = time.time()
617
828
  if current_time - last_check_time > 86400: # 24 hours
618
829
  logger.debug("Over 24 hours since last dependency check")
619
830
  return True, current_hash
620
-
831
+
621
832
  logger.debug("No agent changes detected, skipping dependency check")
622
833
  return False, current_hash
623
-
624
- def mark_deployment_checked(self, deployment_hash: str, check_results: Dict) -> None:
834
+
835
+ def mark_deployment_checked(
836
+ self, deployment_hash: str, check_results: Dict
837
+ ) -> None:
625
838
  """
626
839
  Mark the current deployment as checked.
627
-
840
+
628
841
  Args:
629
842
  deployment_hash: Hash of the current deployment
630
843
  check_results: Results of the dependency check
631
844
  """
632
845
  state = {
633
- 'deployment_hash': deployment_hash,
634
- 'last_check_time': time.time(),
635
- 'last_check_results': check_results,
636
- 'agent_count': len(self.deployed_agents)
846
+ "deployment_hash": deployment_hash,
847
+ "last_check_time": time.time(),
848
+ "last_check_results": check_results,
849
+ "agent_count": len(self.deployed_agents),
637
850
  }
638
851
  self.save_deployment_state(state)
639
-
852
+
640
853
  def get_cached_check_results(self) -> Optional[Dict]:
641
854
  """
642
855
  Get cached dependency check results if still valid.
643
-
856
+
644
857
  Returns:
645
858
  Cached results or None if not available/valid.
646
859
  """
647
860
  has_changed, current_hash = self.has_agents_changed()
648
-
861
+
649
862
  if not has_changed:
650
863
  state = self.load_deployment_state()
651
- cached_results = state.get('last_check_results')
864
+ cached_results = state.get("last_check_results")
652
865
  if cached_results:
653
866
  logger.debug("Using cached dependency check results")
654
867
  return cached_results
655
-
868
+
656
869
  return None
657
870
 
658
871
 
659
- def check_deployed_agent_dependencies(auto_install: bool = False, verbose: bool = False) -> None:
872
+ def check_deployed_agent_dependencies(
873
+ auto_install: bool = False, verbose: bool = False
874
+ ) -> None:
660
875
  """
661
876
  Check dependencies for currently deployed agents.
662
-
877
+
663
878
  Args:
664
879
  auto_install: If True, automatically install missing Python dependencies
665
880
  verbose: If True, enable verbose logging
666
881
  """
667
882
  if verbose:
668
883
  logging.getLogger().setLevel(logging.DEBUG)
669
-
884
+
670
885
  loader = AgentDependencyLoader(auto_install=auto_install)
671
886
  results = loader.load_and_check()
672
-
887
+
673
888
  # Print report
674
889
  report = loader.format_report(results)
675
890
  print(report)
676
-
891
+
677
892
  # Return status code based on missing dependencies
678
- if results['summary']['missing_python'] or results['summary']['missing_system']:
893
+ if results["summary"]["missing_python"] or results["summary"]["missing_system"]:
679
894
  return 1 # Missing dependencies
680
895
  return 0 # All satisfied
681
896
 
682
897
 
683
898
  if __name__ == "__main__":
684
899
  import argparse
685
-
900
+
686
901
  parser = argparse.ArgumentParser(
687
902
  description="Check and manage dependencies for deployed agents"
688
903
  )
689
904
  parser.add_argument(
690
905
  "--auto-install",
691
906
  action="store_true",
692
- help="Automatically install missing Python dependencies"
907
+ help="Automatically install missing Python dependencies",
693
908
  )
694
- parser.add_argument(
695
- "--verbose",
696
- action="store_true",
697
- help="Enable verbose logging"
698
- )
699
-
909
+ parser.add_argument("--verbose", action="store_true", help="Enable verbose logging")
910
+
700
911
  args = parser.parse_args()
701
-
912
+
702
913
  exit_code = check_deployed_agent_dependencies(
703
- auto_install=args.auto_install,
704
- verbose=args.verbose
914
+ auto_install=args.auto_install, verbose=args.verbose
705
915
  )
706
- sys.exit(exit_code)
916
+ sys.exit(exit_code)