claude-mpm 3.9.11__py3-none-any.whl → 4.0.4__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 (434) 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 +2 -2
  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 +330 -86
  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 +363 -220
  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 +124 -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/built/components/agent-inference.js +2 -0
  113. claude_mpm/dashboard/static/built/components/event-processor.js +2 -0
  114. claude_mpm/dashboard/static/built/components/event-viewer.js +2 -0
  115. claude_mpm/dashboard/static/built/components/export-manager.js +2 -0
  116. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +2 -0
  117. claude_mpm/dashboard/static/built/components/hud-library-loader.js +2 -0
  118. claude_mpm/dashboard/static/built/components/hud-manager.js +2 -0
  119. claude_mpm/dashboard/static/built/components/hud-visualizer.js +2 -0
  120. claude_mpm/dashboard/static/built/components/module-viewer.js +2 -0
  121. claude_mpm/dashboard/static/built/components/session-manager.js +2 -0
  122. claude_mpm/dashboard/static/built/components/socket-manager.js +2 -0
  123. claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -0
  124. claude_mpm/dashboard/static/built/components/working-directory.js +2 -0
  125. claude_mpm/dashboard/static/built/dashboard.js +2 -0
  126. claude_mpm/dashboard/static/built/socket-client.js +2 -0
  127. claude_mpm/dashboard/static/css/dashboard.css +27 -8
  128. claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
  129. claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
  130. claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
  131. claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
  132. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
  133. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
  134. claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
  135. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
  136. claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
  137. claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
  138. claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
  139. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
  140. claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
  141. claude_mpm/dashboard/static/dist/dashboard.js +2 -0
  142. claude_mpm/dashboard/static/dist/socket-client.js +2 -0
  143. claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
  144. claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
  145. claude_mpm/dashboard/static/js/components/event-viewer.js +93 -72
  146. claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
  147. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +110 -96
  148. claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
  149. claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
  150. claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
  151. claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
  152. claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
  153. claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
  154. claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
  155. claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
  156. claude_mpm/dashboard/static/js/dashboard.js +178 -453
  157. claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
  158. claude_mpm/dashboard/static/js/socket-client.js +133 -53
  159. claude_mpm/dashboard/templates/index.html +40 -50
  160. claude_mpm/experimental/cli_enhancements.py +60 -58
  161. claude_mpm/generators/__init__.py +1 -1
  162. claude_mpm/generators/agent_profile_generator.py +75 -65
  163. claude_mpm/hooks/__init__.py +1 -1
  164. claude_mpm/hooks/base_hook.py +33 -28
  165. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  166. claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
  167. claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
  168. claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
  169. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
  170. claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
  171. claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
  172. claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
  173. claude_mpm/hooks/memory_integration_hook.py +140 -100
  174. claude_mpm/hooks/tool_call_interceptor.py +89 -76
  175. claude_mpm/hooks/validation_hooks.py +57 -49
  176. claude_mpm/init.py +145 -121
  177. claude_mpm/models/__init__.py +9 -9
  178. claude_mpm/models/agent_definition.py +33 -23
  179. claude_mpm/models/agent_session.py +228 -200
  180. claude_mpm/scripts/__init__.py +1 -1
  181. claude_mpm/scripts/socketio_daemon.py +192 -75
  182. claude_mpm/scripts/socketio_server_manager.py +328 -0
  183. claude_mpm/scripts/start_activity_logging.py +25 -22
  184. claude_mpm/services/__init__.py +68 -43
  185. claude_mpm/services/agent_capabilities_service.py +271 -0
  186. claude_mpm/services/agents/__init__.py +23 -32
  187. claude_mpm/services/agents/deployment/__init__.py +3 -3
  188. claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
  189. claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
  190. claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
  191. claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
  192. claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
  193. claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
  194. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
  195. claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
  196. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
  197. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
  198. claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
  199. claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
  200. claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
  201. claude_mpm/services/agents/deployment/agent_validator.py +352 -0
  202. claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
  203. claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
  204. claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
  205. claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
  206. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  207. claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
  208. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  209. claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
  210. claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
  211. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  212. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  213. claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
  214. claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
  215. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  216. claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
  217. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  218. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  219. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  220. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  221. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
  222. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  223. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  224. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
  225. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
  226. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
  227. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
  228. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
  229. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  230. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
  231. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  232. claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
  233. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
  234. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  235. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  236. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  237. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  238. claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
  239. claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
  240. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  241. claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
  242. claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
  243. claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
  244. claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
  245. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  246. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  247. claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
  248. claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
  249. claude_mpm/services/agents/loading/__init__.py +2 -2
  250. claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
  251. claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
  252. claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
  253. claude_mpm/services/agents/management/__init__.py +2 -2
  254. claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
  255. claude_mpm/services/agents/management/agent_management_service.py +209 -156
  256. claude_mpm/services/agents/memory/__init__.py +9 -6
  257. claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
  258. claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
  259. claude_mpm/services/agents/memory/analyzer.py +430 -0
  260. claude_mpm/services/agents/memory/content_manager.py +376 -0
  261. claude_mpm/services/agents/memory/template_generator.py +468 -0
  262. claude_mpm/services/agents/registry/__init__.py +7 -10
  263. claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
  264. claude_mpm/services/agents/registry/modification_tracker.py +351 -285
  265. claude_mpm/services/async_session_logger.py +187 -153
  266. claude_mpm/services/claude_session_logger.py +87 -72
  267. claude_mpm/services/command_handler_service.py +217 -0
  268. claude_mpm/services/communication/__init__.py +3 -2
  269. claude_mpm/services/core/__init__.py +50 -97
  270. claude_mpm/services/core/base.py +60 -53
  271. claude_mpm/services/core/interfaces/__init__.py +188 -0
  272. claude_mpm/services/core/interfaces/agent.py +351 -0
  273. claude_mpm/services/core/interfaces/communication.py +343 -0
  274. claude_mpm/services/core/interfaces/infrastructure.py +413 -0
  275. claude_mpm/services/core/interfaces/service.py +434 -0
  276. claude_mpm/services/core/interfaces.py +19 -944
  277. claude_mpm/services/event_aggregator.py +208 -170
  278. claude_mpm/services/exceptions.py +387 -308
  279. claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
  280. claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
  281. claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
  282. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
  283. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
  284. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
  285. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
  286. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
  287. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  288. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  289. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  290. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  291. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
  292. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  293. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  294. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
  295. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
  296. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  297. claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
  298. claude_mpm/services/hook_service.py +106 -114
  299. claude_mpm/services/infrastructure/__init__.py +7 -5
  300. claude_mpm/services/infrastructure/context_preservation.py +233 -199
  301. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  302. claude_mpm/services/infrastructure/logging.py +83 -76
  303. claude_mpm/services/infrastructure/monitoring.py +547 -404
  304. claude_mpm/services/mcp_gateway/__init__.py +30 -13
  305. claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
  306. claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
  307. claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
  308. claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
  309. claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
  310. claude_mpm/services/mcp_gateway/core/base.py +80 -67
  311. claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
  312. claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
  313. claude_mpm/services/mcp_gateway/main.py +287 -137
  314. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  315. claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
  316. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  317. claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
  318. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
  319. claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
  320. claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
  321. claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
  322. claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
  323. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
  324. claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
  325. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
  326. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
  327. claude_mpm/services/memory/__init__.py +2 -2
  328. claude_mpm/services/memory/builder.py +451 -362
  329. claude_mpm/services/memory/cache/__init__.py +2 -2
  330. claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
  331. claude_mpm/services/memory/cache/simple_cache.py +107 -93
  332. claude_mpm/services/memory/indexed_memory.py +195 -193
  333. claude_mpm/services/memory/optimizer.py +267 -234
  334. claude_mpm/services/memory/router.py +571 -263
  335. claude_mpm/services/memory_hook_service.py +237 -0
  336. claude_mpm/services/port_manager.py +575 -0
  337. claude_mpm/services/project/__init__.py +3 -3
  338. claude_mpm/services/project/analyzer.py +451 -305
  339. claude_mpm/services/project/registry.py +262 -240
  340. claude_mpm/services/recovery_manager.py +287 -231
  341. claude_mpm/services/response_tracker.py +87 -67
  342. claude_mpm/services/runner_configuration_service.py +587 -0
  343. claude_mpm/services/session_management_service.py +304 -0
  344. claude_mpm/services/socketio/__init__.py +4 -4
  345. claude_mpm/services/socketio/client_proxy.py +174 -0
  346. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  347. claude_mpm/services/socketio/handlers/base.py +44 -30
  348. claude_mpm/services/socketio/handlers/connection.py +166 -64
  349. claude_mpm/services/socketio/handlers/file.py +123 -108
  350. claude_mpm/services/socketio/handlers/git.py +607 -373
  351. claude_mpm/services/socketio/handlers/hook.py +185 -0
  352. claude_mpm/services/socketio/handlers/memory.py +4 -4
  353. claude_mpm/services/socketio/handlers/project.py +4 -4
  354. claude_mpm/services/socketio/handlers/registry.py +53 -38
  355. claude_mpm/services/socketio/server/__init__.py +18 -0
  356. claude_mpm/services/socketio/server/broadcaster.py +252 -0
  357. claude_mpm/services/socketio/server/core.py +399 -0
  358. claude_mpm/services/socketio/server/main.py +323 -0
  359. claude_mpm/services/socketio_client_manager.py +160 -133
  360. claude_mpm/services/socketio_server.py +36 -1885
  361. claude_mpm/services/subprocess_launcher_service.py +316 -0
  362. claude_mpm/services/system_instructions_service.py +258 -0
  363. claude_mpm/services/ticket_manager.py +19 -533
  364. claude_mpm/services/utility_service.py +285 -0
  365. claude_mpm/services/version_control/__init__.py +18 -21
  366. claude_mpm/services/version_control/branch_strategy.py +20 -10
  367. claude_mpm/services/version_control/conflict_resolution.py +37 -13
  368. claude_mpm/services/version_control/git_operations.py +52 -21
  369. claude_mpm/services/version_control/semantic_versioning.py +92 -53
  370. claude_mpm/services/version_control/version_parser.py +145 -125
  371. claude_mpm/services/version_service.py +270 -0
  372. claude_mpm/storage/__init__.py +2 -2
  373. claude_mpm/storage/state_storage.py +177 -181
  374. claude_mpm/ticket_wrapper.py +2 -2
  375. claude_mpm/utils/__init__.py +2 -2
  376. claude_mpm/utils/agent_dependency_loader.py +453 -243
  377. claude_mpm/utils/config_manager.py +157 -118
  378. claude_mpm/utils/console.py +1 -1
  379. claude_mpm/utils/dependency_cache.py +102 -107
  380. claude_mpm/utils/dependency_manager.py +52 -47
  381. claude_mpm/utils/dependency_strategies.py +131 -96
  382. claude_mpm/utils/environment_context.py +110 -102
  383. claude_mpm/utils/error_handler.py +75 -55
  384. claude_mpm/utils/file_utils.py +80 -67
  385. claude_mpm/utils/framework_detection.py +12 -11
  386. claude_mpm/utils/import_migration_example.py +12 -60
  387. claude_mpm/utils/imports.py +48 -45
  388. claude_mpm/utils/path_operations.py +100 -93
  389. claude_mpm/utils/robust_installer.py +172 -164
  390. claude_mpm/utils/session_logging.py +30 -23
  391. claude_mpm/utils/subprocess_utils.py +99 -61
  392. claude_mpm/validation/__init__.py +1 -1
  393. claude_mpm/validation/agent_validator.py +151 -111
  394. claude_mpm/validation/frontmatter_validator.py +92 -71
  395. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/METADATA +90 -22
  396. claude_mpm-4.0.4.dist-info/RECORD +417 -0
  397. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/entry_points.txt +1 -0
  398. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/licenses/LICENSE +1 -1
  399. claude_mpm/cli/commands/run_guarded.py +0 -511
  400. claude_mpm/config/memory_guardian_config.py +0 -325
  401. claude_mpm/config/memory_guardian_yaml.py +0 -335
  402. claude_mpm/core/config_paths.py +0 -150
  403. claude_mpm/core/memory_aware_runner.py +0 -353
  404. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  405. claude_mpm/deployment_paths.py +0 -261
  406. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  407. claude_mpm/models/state_models.py +0 -433
  408. claude_mpm/services/agent/__init__.py +0 -24
  409. claude_mpm/services/agent/deployment.py +0 -2548
  410. claude_mpm/services/agent/management.py +0 -598
  411. claude_mpm/services/agent/registry.py +0 -813
  412. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  413. claude_mpm/services/communication/socketio.py +0 -1935
  414. claude_mpm/services/communication/websocket.py +0 -479
  415. claude_mpm/services/framework_claude_md_generator.py +0 -624
  416. claude_mpm/services/health_monitor.py +0 -893
  417. claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
  418. claude_mpm/services/infrastructure/health_monitor.py +0 -775
  419. claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
  420. claude_mpm/services/infrastructure/memory_guardian.py +0 -944
  421. claude_mpm/services/infrastructure/restart_protection.py +0 -642
  422. claude_mpm/services/infrastructure/state_manager.py +0 -774
  423. claude_mpm/services/mcp_gateway/manager.py +0 -334
  424. claude_mpm/services/optimized_hook_service.py +0 -542
  425. claude_mpm/services/project_analyzer.py +0 -864
  426. claude_mpm/services/project_registry.py +0 -608
  427. claude_mpm/services/standalone_socketio_server.py +0 -1300
  428. claude_mpm/services/ticket_manager_di.py +0 -318
  429. claude_mpm/services/ticketing_service_original.py +0 -510
  430. claude_mpm/utils/paths.py +0 -395
  431. claude_mpm/utils/platform_memory.py +0 -524
  432. claude_mpm-3.9.11.dist-info/RECORD +0 -306
  433. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/WHEEL +0 -0
  434. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,5 @@
1
+ from pathlib import Path
2
+
1
3
  """Async Agent Deployment Service for high-performance parallel operations.
2
4
 
3
5
  This module provides async versions of agent deployment operations to dramatically
@@ -19,114 +21,116 @@ DESIGN DECISIONS:
19
21
 
20
22
  import asyncio
21
23
  import json
22
- import logging
23
24
  import os
24
25
  import time
25
- from pathlib import Path
26
- from typing import Dict, Any, List, Optional, Tuple
27
- import aiofiles
28
26
  from concurrent.futures import ThreadPoolExecutor
27
+ from typing import Any, Dict, List, Optional, Tuple
28
+
29
+ import aiofiles
29
30
 
30
- from claude_mpm.core.logger import get_logger
31
- from claude_mpm.constants import EnvironmentVars, Paths
32
31
  from claude_mpm.config.paths import paths
32
+ from claude_mpm.constants import EnvironmentVars, Paths
33
33
  from claude_mpm.core.config import Config
34
+ from claude_mpm.core.logger import get_logger
34
35
 
35
36
 
36
37
  class AsyncAgentDeploymentService:
37
38
  """Async service for high-performance agent deployment.
38
-
39
+
39
40
  WHY: This async version provides:
40
41
  - 50-70% reduction in startup time
41
42
  - Parallel agent file discovery and processing
42
43
  - Non-blocking I/O for all file operations
43
44
  - Efficient batching of operations
44
45
  - Seamless integration with existing sync code
45
-
46
+
46
47
  PERFORMANCE METRICS:
47
48
  - Sync discovery: ~500ms for 10 agents across 3 directories
48
49
  - Async discovery: ~150ms for same (70% reduction)
49
50
  - Sync JSON parsing: ~200ms for 10 files
50
51
  - Async JSON parsing: ~50ms for same (75% reduction)
51
52
  """
52
-
53
- def __init__(self, templates_dir: Optional[Path] = None,
54
- base_agent_path: Optional[Path] = None,
55
- working_directory: Optional[Path] = None):
53
+
54
+ def __init__(
55
+ self,
56
+ templates_dir: Optional[Path] = None,
57
+ base_agent_path: Optional[Path] = None,
58
+ working_directory: Optional[Path] = None,
59
+ ):
56
60
  """Initialize async agent deployment service.
57
-
61
+
58
62
  Args:
59
63
  templates_dir: Directory containing agent JSON files
60
64
  base_agent_path: Path to base_agent.md file
61
65
  working_directory: User's working directory (for project agents)
62
66
  """
63
67
  self.logger = get_logger(self.__class__.__name__)
64
-
68
+
65
69
  # Determine working directory
66
70
  if working_directory:
67
71
  self.working_directory = Path(working_directory)
68
- elif 'CLAUDE_MPM_USER_PWD' in os.environ:
69
- self.working_directory = Path(os.environ['CLAUDE_MPM_USER_PWD'])
72
+ elif "CLAUDE_MPM_USER_PWD" in os.environ:
73
+ self.working_directory = Path(os.environ["CLAUDE_MPM_USER_PWD"])
70
74
  else:
71
75
  self.working_directory = Path.cwd()
72
-
76
+
73
77
  # Set template and base agent paths
74
78
  if templates_dir:
75
79
  self.templates_dir = Path(templates_dir)
76
80
  else:
77
81
  self.templates_dir = paths.agents_dir / "templates"
78
-
82
+
79
83
  if base_agent_path:
80
84
  self.base_agent_path = Path(base_agent_path)
81
85
  else:
82
86
  self.base_agent_path = paths.agents_dir / "base_agent.json"
83
-
87
+
84
88
  # Thread pool for CPU-bound JSON parsing
85
89
  self.executor = ThreadPoolExecutor(max_workers=4)
86
-
90
+
87
91
  # Performance metrics
88
92
  self._metrics = {
89
- 'async_operations': 0,
90
- 'parallel_files_processed': 0,
91
- 'time_saved_ms': 0.0
93
+ "async_operations": 0,
94
+ "parallel_files_processed": 0,
95
+ "time_saved_ms": 0.0,
92
96
  }
93
-
94
- async def discover_agents_async(self, directories: List[Path]) -> Dict[str, List[Path]]:
97
+
98
+ async def discover_agents_async(
99
+ self, directories: List[Path]
100
+ ) -> Dict[str, List[Path]]:
95
101
  """Discover agent files across multiple directories in parallel.
96
-
102
+
97
103
  WHY: Parallel directory scanning reduces I/O wait time significantly.
98
104
  Each directory scan can take 50-100ms sequentially, but parallel
99
105
  scanning completes all directories in the time of the slowest one.
100
-
106
+
101
107
  Args:
102
108
  directories: List of directories to scan
103
-
109
+
104
110
  Returns:
105
111
  Dictionary mapping directory paths to lists of agent files
106
112
  """
107
113
  start_time = time.time()
108
-
114
+
109
115
  async def scan_directory(directory: Path) -> Tuple[str, List[Path]]:
110
116
  """Scan a single directory for agent files asynchronously."""
111
117
  if not directory.exists():
112
118
  return str(directory), []
113
-
119
+
114
120
  # Use asyncio to run glob in executor (since Path.glob is blocking)
115
121
  loop = asyncio.get_event_loop()
116
122
  files = await loop.run_in_executor(
117
- self.executor,
118
- lambda: list(directory.glob("*.json"))
123
+ self.executor, lambda: list(directory.glob("*.json"))
119
124
  )
120
-
125
+
121
126
  self.logger.debug(f"Found {len(files)} agents in {directory}")
122
127
  return str(directory), files
123
-
128
+
124
129
  # Scan all directories in parallel
125
130
  results = await asyncio.gather(
126
- *[scan_directory(d) for d in directories],
127
- return_exceptions=True
131
+ *[scan_directory(d) for d in directories], return_exceptions=True
128
132
  )
129
-
133
+
130
134
  # Process results
131
135
  discovered = {}
132
136
  for result in results:
@@ -135,92 +139,92 @@ class AsyncAgentDeploymentService:
135
139
  continue
136
140
  dir_path, files = result
137
141
  discovered[dir_path] = files
138
-
142
+
139
143
  elapsed = (time.time() - start_time) * 1000
140
- self._metrics['time_saved_ms'] += max(0, (len(directories) * 75) - elapsed)
144
+ self._metrics["time_saved_ms"] += max(0, (len(directories) * 75) - elapsed)
141
145
  self.logger.info(f"Discovered agents in {elapsed:.1f}ms (parallel scan)")
142
-
146
+
143
147
  return discovered
144
-
145
- async def load_agent_files_async(self, file_paths: List[Path]) -> List[Dict[str, Any]]:
148
+
149
+ async def load_agent_files_async(
150
+ self, file_paths: List[Path]
151
+ ) -> List[Dict[str, Any]]:
146
152
  """Load and parse multiple agent files in parallel.
147
-
153
+
148
154
  WHY: JSON parsing is CPU-bound but file reading is I/O-bound.
149
155
  By separating these operations and parallelizing, we achieve:
150
156
  - Non-blocking file reads with aiofiles
151
157
  - Parallel JSON parsing in thread pool
152
158
  - Batch processing for efficiency
153
-
159
+
154
160
  Args:
155
161
  file_paths: List of agent file paths to load
156
-
162
+
157
163
  Returns:
158
164
  List of parsed agent configurations
159
165
  """
160
166
  start_time = time.time()
161
-
167
+
162
168
  async def load_single_file(file_path: Path) -> Optional[Dict[str, Any]]:
163
169
  """Load and parse a single agent file asynchronously."""
164
170
  try:
165
171
  # Non-blocking file read
166
- async with aiofiles.open(file_path, 'r') as f:
172
+ async with aiofiles.open(file_path, "r") as f:
167
173
  content = await f.read()
168
-
174
+
169
175
  # Parse JSON in thread pool (CPU-bound)
170
176
  loop = asyncio.get_event_loop()
171
- data = await loop.run_in_executor(
172
- self.executor,
173
- json.loads,
174
- content
175
- )
176
-
177
+ data = await loop.run_in_executor(self.executor, json.loads, content)
178
+
177
179
  # Add file metadata
178
- data['_source_file'] = str(file_path)
179
- data['_agent_name'] = file_path.stem
180
-
180
+ data["_source_file"] = str(file_path)
181
+ data["_agent_name"] = file_path.stem
182
+
181
183
  return data
182
-
184
+
183
185
  except Exception as e:
184
186
  self.logger.error(f"Failed to load {file_path}: {e}")
185
187
  return None
186
-
188
+
187
189
  # Load all files in parallel
188
190
  agents = await asyncio.gather(
189
- *[load_single_file(fp) for fp in file_paths],
190
- return_exceptions=False
191
+ *[load_single_file(fp) for fp in file_paths], return_exceptions=False
191
192
  )
192
-
193
+
193
194
  # Filter out None values (failed loads)
194
195
  valid_agents = [a for a in agents if a is not None]
195
-
196
+
196
197
  elapsed = (time.time() - start_time) * 1000
197
- self._metrics['parallel_files_processed'] += len(file_paths)
198
- self._metrics['async_operations'] += len(file_paths)
199
-
198
+ self._metrics["parallel_files_processed"] += len(file_paths)
199
+ self._metrics["async_operations"] += len(file_paths)
200
+
200
201
  self.logger.info(
201
202
  f"Loaded {len(valid_agents)}/{len(file_paths)} agents "
202
203
  f"in {elapsed:.1f}ms (parallel load)"
203
204
  )
204
-
205
+
205
206
  return valid_agents
206
-
207
- async def validate_agents_async(self, agents: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
207
+
208
+ async def validate_agents_async(
209
+ self, agents: List[Dict[str, Any]]
210
+ ) -> List[Dict[str, Any]]:
208
211
  """Validate multiple agents in parallel.
209
-
212
+
210
213
  WHY: Agent validation involves checking schemas and constraints.
211
214
  Parallel validation reduces time from O(n) to O(1) for the batch.
212
-
215
+
213
216
  Args:
214
217
  agents: List of agent configurations to validate
215
-
218
+
216
219
  Returns:
217
220
  List of valid agent configurations
218
221
  """
222
+
219
223
  async def validate_single(agent: Dict[str, Any]) -> Optional[Dict[str, Any]]:
220
224
  """Validate a single agent configuration."""
221
225
  try:
222
226
  # Basic validation (extend as needed)
223
- required_fields = ['agent_id', 'instructions']
227
+ required_fields = ["agent_id", "instructions"]
224
228
  if all(field in agent for field in required_fields):
225
229
  return agent
226
230
  else:
@@ -233,239 +237,281 @@ class AsyncAgentDeploymentService:
233
237
  except Exception as e:
234
238
  self.logger.error(f"Validation error: {e}")
235
239
  return None
236
-
240
+
237
241
  # Validate all agents in parallel
238
242
  validated = await asyncio.gather(
239
- *[validate_single(a) for a in agents],
240
- return_exceptions=False
243
+ *[validate_single(a) for a in agents], return_exceptions=False
241
244
  )
242
-
245
+
243
246
  return [a for a in validated if a is not None]
244
-
245
- async def deploy_agents_async(self, target_dir: Optional[Path] = None,
246
- force_rebuild: bool = False,
247
- config: Optional[Config] = None) -> Dict[str, Any]:
247
+
248
+ async def deploy_agents_async(
249
+ self,
250
+ target_dir: Optional[Path] = None,
251
+ force_rebuild: bool = False,
252
+ config: Optional[Config] = None,
253
+ ) -> Dict[str, Any]:
248
254
  """Deploy agents using async operations for maximum performance.
249
-
255
+
250
256
  WHY: This async deployment method provides:
251
257
  - Parallel file discovery across all tiers
252
258
  - Concurrent agent loading and validation
253
259
  - Batch processing for efficiency
254
260
  - 50-70% reduction in total deployment time
255
-
261
+
256
262
  Args:
257
263
  target_dir: Target directory for agents
258
264
  force_rebuild: Force rebuild even if agents exist
259
265
  config: Optional configuration object
260
-
266
+
261
267
  Returns:
262
268
  Dictionary with deployment results
263
269
  """
264
270
  start_time = time.time()
265
-
271
+
266
272
  # Load configuration
267
273
  if config is None:
268
274
  config = Config()
269
-
275
+
270
276
  # Get exclusion configuration
271
- excluded_agents = config.get('agent_deployment.excluded_agents', [])
272
- case_sensitive = config.get('agent_deployment.case_sensitive', False)
273
-
277
+ excluded_agents = config.get("agent_deployment.excluded_agents", [])
278
+ case_sensitive = config.get("agent_deployment.case_sensitive", False)
279
+
274
280
  results = {
275
281
  "deployed": [],
276
282
  "errors": [],
277
283
  "skipped": [],
278
284
  "updated": [],
279
- "metrics": {
280
- "async_mode": True,
281
- "start_time": start_time
282
- }
285
+ "metrics": {"async_mode": True, "start_time": start_time},
283
286
  }
284
-
287
+
285
288
  try:
286
289
  # Determine target directory
287
290
  if not target_dir:
288
291
  agents_dir = self.working_directory / ".claude" / "agents"
289
292
  else:
290
293
  agents_dir = self._resolve_agents_dir(target_dir)
291
-
292
- agents_dir.mkdir(parents=True, exist_ok=True)
293
-
294
+
295
+ # Create directory asynchronously
296
+ await self._create_directory_async(agents_dir)
297
+
294
298
  # Step 1: Discover agent files in parallel
295
299
  search_dirs = [
296
300
  self.working_directory / ".claude-mpm" / "agents", # PROJECT
297
301
  Path.home() / ".claude-mpm" / "agents", # USER
298
- self.templates_dir # SYSTEM
302
+ self.templates_dir, # SYSTEM
299
303
  ]
300
-
304
+
301
305
  discovered = await self.discover_agents_async(
302
306
  [d for d in search_dirs if d.exists()]
303
307
  )
304
-
308
+
305
309
  # Step 2: Load all agent files in parallel
306
310
  all_files = []
307
311
  for files in discovered.values():
308
312
  all_files.extend(files)
309
-
313
+
310
314
  if not all_files:
311
315
  self.logger.warning("No agent files found")
312
316
  return results
313
-
317
+
314
318
  agents = await self.load_agent_files_async(all_files)
315
-
319
+
316
320
  # Step 3: Filter excluded agents
317
321
  filtered_agents = self._filter_excluded_agents(
318
322
  agents, excluded_agents, case_sensitive
319
323
  )
320
-
324
+
321
325
  # Step 4: Validate agents in parallel
322
326
  valid_agents = await self.validate_agents_async(filtered_agents)
323
-
324
- # Step 5: Deploy valid agents (this part remains sync for file writes)
325
- # Could be made async with aiofiles if needed
326
- for agent in valid_agents:
327
- agent_name = agent.get('_agent_name', 'unknown')
328
- target_file = agents_dir / f"{agent_name}.md"
329
-
330
- # Build markdown content (sync operation - could be parallelized)
331
- content = self._build_agent_markdown_sync(agent)
332
-
333
- # Write file (could use aiofiles for true async)
334
- target_file.write_text(content)
335
-
336
- results["deployed"].append(agent_name)
337
-
327
+
328
+ # Step 5: Deploy valid agents using async file operations
329
+ await self._deploy_agents_async(valid_agents, agents_dir, results)
330
+
338
331
  except Exception as e:
339
332
  self.logger.error(f"Async deployment failed: {e}")
340
333
  results["errors"].append(str(e))
341
-
334
+
342
335
  # Calculate metrics
343
336
  elapsed = (time.time() - start_time) * 1000
344
337
  results["metrics"]["duration_ms"] = elapsed
345
338
  results["metrics"]["async_stats"] = self._metrics.copy()
346
-
339
+
347
340
  self.logger.info(
348
341
  f"Async deployment completed in {elapsed:.1f}ms "
349
342
  f"({len(results['deployed'])} deployed, "
350
343
  f"{len(results['errors'])} errors)"
351
344
  )
352
-
345
+
353
346
  return results
354
-
347
+
355
348
  def _resolve_agents_dir(self, target_dir: Path) -> Path:
356
349
  """Resolve the agents directory from target directory."""
357
350
  target_dir = Path(target_dir)
358
-
351
+
359
352
  if target_dir.name == "agents":
360
353
  return target_dir
361
354
  elif target_dir.name in [".claude-mpm", ".claude"]:
362
355
  return target_dir / "agents"
363
356
  else:
364
357
  return target_dir / ".claude" / "agents"
365
-
366
- def _filter_excluded_agents(self, agents: List[Dict[str, Any]],
367
- excluded_agents: List[str],
368
- case_sensitive: bool) -> List[Dict[str, Any]]:
358
+
359
+ def _filter_excluded_agents(
360
+ self,
361
+ agents: List[Dict[str, Any]],
362
+ excluded_agents: List[str],
363
+ case_sensitive: bool,
364
+ ) -> List[Dict[str, Any]]:
369
365
  """Filter out excluded agents from the list."""
370
366
  if not excluded_agents:
371
367
  return agents
372
-
368
+
373
369
  # Normalize exclusion list
374
370
  if not case_sensitive:
375
371
  excluded_agents = [a.lower() for a in excluded_agents]
376
-
372
+
377
373
  filtered = []
378
374
  for agent in agents:
379
- agent_name = agent.get('_agent_name', '')
375
+ agent_name = agent.get("_agent_name", "")
380
376
  compare_name = agent_name if case_sensitive else agent_name.lower()
381
-
377
+
382
378
  if compare_name not in excluded_agents:
383
379
  filtered.append(agent)
384
380
  else:
385
381
  self.logger.debug(f"Excluding agent: {agent_name}")
386
-
382
+
387
383
  return filtered
388
-
384
+
385
+ async def _create_directory_async(self, directory: Path) -> None:
386
+ """Create directory asynchronously using thread pool."""
387
+ loop = asyncio.get_event_loop()
388
+ await loop.run_in_executor(
389
+ self.executor, lambda: directory.mkdir(parents=True, exist_ok=True)
390
+ )
391
+
392
+ async def _deploy_agents_async(
393
+ self, agents: List[Dict[str, Any]], agents_dir: Path, results: Dict[str, Any]
394
+ ) -> None:
395
+ """Deploy agents using async file operations."""
396
+
397
+ async def deploy_single_agent(agent: Dict[str, Any]) -> Optional[str]:
398
+ """Deploy a single agent asynchronously."""
399
+ try:
400
+ agent_name = agent.get("_agent_name", "unknown")
401
+ target_file = agents_dir / f"{agent_name}.md"
402
+
403
+ # Build markdown content in thread pool (CPU-bound)
404
+ loop = asyncio.get_event_loop()
405
+ content = await loop.run_in_executor(
406
+ self.executor, self._build_agent_markdown_sync, agent
407
+ )
408
+
409
+ # Write file asynchronously
410
+ async with aiofiles.open(target_file, "w") as f:
411
+ await f.write(content)
412
+
413
+ return agent_name
414
+
415
+ except Exception as e:
416
+ self.logger.error(
417
+ f"Failed to deploy agent {agent.get('_agent_name', 'unknown')}: {e}"
418
+ )
419
+ return None
420
+
421
+ # Deploy all agents in parallel
422
+ deployed_names = await asyncio.gather(
423
+ *[deploy_single_agent(agent) for agent in agents], return_exceptions=False
424
+ )
425
+
426
+ # Update results with successful deployments
427
+ for name in deployed_names:
428
+ if name is not None:
429
+ results["deployed"].append(name)
430
+
389
431
  def _build_agent_markdown_sync(self, agent_data: Dict[str, Any]) -> str:
390
432
  """Build agent markdown content matching the synchronous deployment format."""
391
433
  from datetime import datetime
392
-
434
+
393
435
  # Extract agent info from the loaded JSON data
394
- agent_name = agent_data.get('_agent_name', 'unknown')
395
-
436
+ agent_name = agent_data.get("_agent_name", "unknown")
437
+
396
438
  # Extract proper agent_id from template data (not filename)
397
- agent_id = agent_data.get('agent_id', agent_name)
398
-
439
+ agent_id = agent_data.get("agent_id", agent_name)
440
+
399
441
  # Handle both 'agent_version' (new format) and 'version' (old format)
400
- agent_version = self._parse_version(agent_data.get('agent_version') or agent_data.get('version', '1.0.0'))
442
+ agent_version = self._parse_version(
443
+ agent_data.get("agent_version") or agent_data.get("version", "1.0.0")
444
+ )
401
445
  base_version = (0, 1, 0) # Default base version for async deployment
402
-
446
+
403
447
  # Format version string as semantic version
404
448
  version_string = self._format_version_display(agent_version)
405
-
449
+
406
450
  # Extract metadata using the same logic as synchronous deployment
407
451
  # Check new format first (metadata.description), then old format
408
452
  description = (
409
- agent_data.get('metadata', {}).get('description') or
410
- agent_data.get('configuration_fields', {}).get('description') or
411
- agent_data.get('description') or
412
- 'Agent for specialized tasks'
453
+ agent_data.get("metadata", {}).get("description")
454
+ or agent_data.get("configuration_fields", {}).get("description")
455
+ or agent_data.get("description")
456
+ or "Agent for specialized tasks"
413
457
  )
414
-
458
+
415
459
  # Get tags from new format (metadata.tags) or old format
416
460
  tags = (
417
- agent_data.get('metadata', {}).get('tags') or
418
- agent_data.get('configuration_fields', {}).get('tags') or
419
- agent_data.get('tags') or
420
- [agent_id, 'mpm-framework']
461
+ agent_data.get("metadata", {}).get("tags")
462
+ or agent_data.get("configuration_fields", {}).get("tags")
463
+ or agent_data.get("tags")
464
+ or [agent_id, "mpm-framework"]
421
465
  )
422
-
466
+
423
467
  # Get tools from capabilities.tools in new format
424
468
  tools = (
425
- agent_data.get('capabilities', {}).get('tools') or
426
- agent_data.get('configuration_fields', {}).get('tools') or
427
- ["Read", "Write", "Edit", "Grep", "Glob", "LS"] # Default fallback
469
+ agent_data.get("capabilities", {}).get("tools")
470
+ or agent_data.get("configuration_fields", {}).get("tools")
471
+ or ["Read", "Write", "Edit", "Grep", "Glob", "LS"] # Default fallback
428
472
  )
429
-
473
+
430
474
  # Get model from capabilities.model in new format
431
475
  model = (
432
- agent_data.get('capabilities', {}).get('model') or
433
- agent_data.get('configuration_fields', {}).get('model') or
434
- "sonnet" # Default fallback
476
+ agent_data.get("capabilities", {}).get("model")
477
+ or agent_data.get("configuration_fields", {}).get("model")
478
+ or "sonnet" # Default fallback
435
479
  )
436
-
480
+
437
481
  # Simplify model name for Claude Code
438
482
  model_map = {
439
- 'claude-4-sonnet-20250514': 'sonnet',
440
- 'claude-sonnet-4-20250514': 'sonnet',
441
- 'claude-opus-4-20250514': 'opus',
442
- 'claude-3-opus-20240229': 'opus',
443
- 'claude-3-haiku-20240307': 'haiku',
444
- 'claude-3.5-sonnet': 'sonnet',
445
- 'claude-3-sonnet': 'sonnet'
483
+ "claude-4-sonnet-20250514": "sonnet",
484
+ "claude-sonnet-4-20250514": "sonnet",
485
+ "claude-opus-4-20250514": "opus",
486
+ "claude-3-opus-20240229": "opus",
487
+ "claude-3-haiku-20240307": "haiku",
488
+ "claude-3.5-sonnet": "sonnet",
489
+ "claude-3-sonnet": "sonnet",
446
490
  }
447
491
  # Better fallback: extract the model type (opus/sonnet/haiku) from the string
448
492
  if model not in model_map:
449
- if 'opus' in model.lower():
450
- model = 'opus'
451
- elif 'sonnet' in model.lower():
452
- model = 'sonnet'
453
- elif 'haiku' in model.lower():
454
- model = 'haiku'
493
+ if "opus" in model.lower():
494
+ model = "opus"
495
+ elif "sonnet" in model.lower():
496
+ model = "sonnet"
497
+ elif "haiku" in model.lower():
498
+ model = "haiku"
455
499
  else:
456
500
  # Last resort: try to extract from hyphenated format
457
- model = model_map.get(model, model.split('-')[-1] if '-' in model else model)
501
+ model = model_map.get(
502
+ model, model.split("-")[-1] if "-" in model else model
503
+ )
458
504
  else:
459
505
  model = model_map[model]
460
-
506
+
461
507
  # Convert tools list to comma-separated string for Claude Code compatibility
462
508
  # IMPORTANT: No spaces after commas - Claude Code requires exact format
463
- tools_str = ','.join(tools) if isinstance(tools, list) else str(tools)
464
-
509
+ tools_str = ",".join(tools) if isinstance(tools, list) else str(tools)
510
+
465
511
  # Convert agent_id to Claude Code compatible name (replace underscores with hyphens)
466
512
  # Claude Code requires name to match pattern: ^[a-z0-9]+(-[a-z0-9]+)*$
467
- claude_code_name = agent_id.replace('_', '-').lower()
468
-
513
+ claude_code_name = agent_id.replace("_", "-").lower()
514
+
469
515
  # Build frontmatter with only the fields Claude Code uses
470
516
  frontmatter_lines = [
471
517
  "---",
@@ -475,84 +521,82 @@ class AsyncAgentDeploymentService:
475
521
  f"base_version: {self._format_version_display(base_version)}",
476
522
  f"author: claude-mpm", # Identify as system agent for deployment
477
523
  f"tools: {tools_str}",
478
- f"model: {model}"
524
+ f"model: {model}",
479
525
  ]
480
-
526
+
481
527
  # Add optional fields if present
482
528
  # Check for color in metadata section (new format) or root (old format)
483
- color = (
484
- agent_data.get('metadata', {}).get('color') or
485
- agent_data.get('color')
486
- )
529
+ color = agent_data.get("metadata", {}).get("color") or agent_data.get("color")
487
530
  if color:
488
531
  frontmatter_lines.append(f"color: {color}")
489
-
532
+
490
533
  frontmatter_lines.append("---")
491
534
  frontmatter_lines.append("")
492
535
  frontmatter_lines.append("")
493
-
494
- frontmatter = '\n'.join(frontmatter_lines)
495
-
536
+
537
+ frontmatter = "\n".join(frontmatter_lines)
538
+
496
539
  # Get the main content (instructions)
497
540
  # Check multiple possible locations for instructions
498
541
  content = (
499
- agent_data.get('instructions') or
500
- agent_data.get('narrative_fields', {}).get('instructions') or
501
- agent_data.get('content') or
502
- f"You are the {agent_id} agent. Perform tasks related to {agent_data.get('description', 'your specialization')}."
542
+ agent_data.get("instructions")
543
+ or agent_data.get("narrative_fields", {}).get("instructions")
544
+ or agent_data.get("content")
545
+ or f"You are the {agent_id} agent. Perform tasks related to {agent_data.get('description', 'your specialization')}."
503
546
  )
504
-
547
+
505
548
  return frontmatter + content
506
-
549
+
507
550
  def _parse_version(self, version_value: Any) -> tuple:
508
551
  """
509
552
  Parse version from various formats to semantic version tuple.
510
-
553
+
511
554
  Handles:
512
555
  - Integer values: 5 -> (0, 5, 0)
513
556
  - String integers: "5" -> (0, 5, 0)
514
557
  - Semantic versions: "2.1.0" -> (2, 1, 0)
515
558
  - Invalid formats: returns (0, 0, 0)
516
-
559
+
517
560
  Args:
518
561
  version_value: Version in various formats
519
-
562
+
520
563
  Returns:
521
564
  Tuple of (major, minor, patch) for comparison
522
565
  """
523
566
  if isinstance(version_value, int):
524
567
  # Legacy integer version - treat as minor version
525
568
  return (0, version_value, 0)
526
-
569
+
527
570
  if isinstance(version_value, str):
528
571
  # Try to parse as simple integer
529
572
  if version_value.isdigit():
530
573
  return (0, int(version_value), 0)
531
-
574
+
532
575
  # Try to parse semantic version (e.g., "2.1.0" or "v2.1.0")
533
576
  import re
534
- sem_ver_match = re.match(r'^v?(\d+)\.(\d+)\.(\d+)', version_value)
577
+
578
+ sem_ver_match = re.match(r"^v?(\d+)\.(\d+)\.(\d+)", version_value)
535
579
  if sem_ver_match:
536
580
  major = int(sem_ver_match.group(1))
537
581
  minor = int(sem_ver_match.group(2))
538
582
  patch = int(sem_ver_match.group(3))
539
583
  return (major, minor, patch)
540
-
584
+
541
585
  # Try to extract first number from string as minor version
542
- num_match = re.search(r'(\d+)', version_value)
586
+ num_match = re.search(r"(\d+)", version_value)
543
587
  if num_match:
544
588
  return (0, int(num_match.group(1)), 0)
545
-
589
+
546
590
  # Default to 0.0.0 for invalid formats
547
591
  return (0, 0, 0)
548
-
592
+
549
593
  def _format_version_display(self, version_tuple: tuple) -> str:
550
594
  """
551
595
  Format version tuple for display.
552
-
596
+
553
597
  Args:
554
598
  version_tuple: Tuple of (major, minor, patch)
555
-
599
+
556
600
  Returns:
557
601
  Formatted version string
558
602
  """
@@ -562,61 +606,81 @@ class AsyncAgentDeploymentService:
562
606
  else:
563
607
  # Fallback for legacy format
564
608
  return str(version_tuple)
565
-
609
+
566
610
  async def cleanup(self):
567
611
  """Clean up resources."""
568
612
  self.executor.shutdown(wait=False)
569
613
 
570
614
 
571
615
  # Convenience function to run async deployment from sync code
572
- def deploy_agents_async_wrapper(templates_dir: Optional[Path] = None,
573
- base_agent_path: Optional[Path] = None,
574
- working_directory: Optional[Path] = None,
575
- target_dir: Optional[Path] = None,
576
- force_rebuild: bool = False,
577
- config: Optional[Config] = None) -> Dict[str, Any]:
616
+ def deploy_agents_async_wrapper(
617
+ templates_dir: Optional[Path] = None,
618
+ base_agent_path: Optional[Path] = None,
619
+ working_directory: Optional[Path] = None,
620
+ target_dir: Optional[Path] = None,
621
+ force_rebuild: bool = False,
622
+ config: Optional[Config] = None,
623
+ ) -> Dict[str, Any]:
578
624
  """Wrapper to run async deployment from synchronous code.
579
-
625
+
580
626
  WHY: This wrapper allows the async deployment to be called from
581
627
  existing synchronous code without requiring a full async refactor.
582
628
  It manages the event loop and ensures proper cleanup.
583
-
629
+
584
630
  Args:
585
631
  Same as AsyncAgentDeploymentService.deploy_agents_async()
586
-
632
+
587
633
  Returns:
588
634
  Deployment results dictionary
589
635
  """
636
+
590
637
  async def run_deployment():
591
638
  service = AsyncAgentDeploymentService(
592
639
  templates_dir=templates_dir,
593
640
  base_agent_path=base_agent_path,
594
- working_directory=working_directory
641
+ working_directory=working_directory,
595
642
  )
596
-
643
+
597
644
  try:
598
645
  results = await service.deploy_agents_async(
599
- target_dir=target_dir,
600
- force_rebuild=force_rebuild,
601
- config=config
646
+ target_dir=target_dir, force_rebuild=force_rebuild, config=config
602
647
  )
603
648
  return results
604
649
  finally:
605
650
  await service.cleanup()
606
-
607
- # Run in event loop
651
+
652
+ # Run in event loop with proper handling
608
653
  try:
609
- # Try to get existing event loop
610
- loop = asyncio.get_event_loop()
611
- if loop.is_running():
612
- # If loop is already running, create a new task
654
+ # Check if we're already in an async context
655
+ try:
656
+ loop = asyncio.get_running_loop()
657
+ # We're in an async context, run in thread pool to avoid blocking
613
658
  import concurrent.futures
659
+
614
660
  with concurrent.futures.ThreadPoolExecutor() as executor:
615
661
  future = executor.submit(asyncio.run, run_deployment())
616
662
  return future.result()
617
- else:
618
- # Run in existing loop
619
- return loop.run_until_complete(run_deployment())
620
- except RuntimeError:
621
- # No event loop, create new one
622
- return asyncio.run(run_deployment())
663
+ except RuntimeError:
664
+ # No running loop, safe to create new one
665
+ return asyncio.run(run_deployment())
666
+ except Exception as e:
667
+ # Fallback to synchronous deployment if async fails
668
+ from claude_mpm.core.logger import get_logger
669
+
670
+ logger = get_logger("AsyncAgentDeploymentWrapper")
671
+ logger.warning(f"Async deployment failed, falling back to sync: {e}")
672
+
673
+ # Import and use synchronous deployment as fallback
674
+ from .agent_deployment import AgentDeploymentService
675
+
676
+ sync_service = AgentDeploymentService(
677
+ templates_dir=templates_dir,
678
+ base_agent_path=base_agent_path,
679
+ working_directory=working_directory,
680
+ )
681
+ return sync_service.deploy_agents(
682
+ target_dir=target_dir,
683
+ force_rebuild=force_rebuild,
684
+ config=config,
685
+ use_async=False, # Explicitly disable async to avoid recursion
686
+ )