claude-mpm 4.1.1__py3-none-any.whl → 4.1.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 (389) hide show
  1. claude_mpm/BUILD_NUMBER +1 -1
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/__main__.py +1 -1
  4. claude_mpm/agents/BASE_PM.md +74 -46
  5. claude_mpm/agents/INSTRUCTIONS.md +11 -153
  6. claude_mpm/agents/WORKFLOW.md +61 -321
  7. claude_mpm/agents/__init__.py +11 -11
  8. claude_mpm/agents/agent_loader.py +23 -20
  9. claude_mpm/agents/agent_loader_integration.py +1 -1
  10. claude_mpm/agents/agents_metadata.py +27 -0
  11. claude_mpm/agents/async_agent_loader.py +5 -8
  12. claude_mpm/agents/base_agent_loader.py +36 -25
  13. claude_mpm/agents/frontmatter_validator.py +6 -6
  14. claude_mpm/agents/schema/agent_schema.json +1 -1
  15. claude_mpm/agents/system_agent_config.py +9 -9
  16. claude_mpm/agents/templates/api_qa.json +47 -2
  17. claude_mpm/agents/templates/engineer.json +33 -11
  18. claude_mpm/agents/templates/imagemagick.json +256 -0
  19. claude_mpm/agents/templates/qa.json +41 -2
  20. claude_mpm/agents/templates/ticketing.json +5 -5
  21. claude_mpm/agents/templates/web_qa.json +50 -2
  22. claude_mpm/cli/__init__.py +51 -46
  23. claude_mpm/cli/__main__.py +1 -1
  24. claude_mpm/cli/commands/__init__.py +10 -12
  25. claude_mpm/cli/commands/agent_manager.py +186 -181
  26. claude_mpm/cli/commands/agents.py +648 -1098
  27. claude_mpm/cli/commands/aggregate.py +30 -29
  28. claude_mpm/cli/commands/cleanup.py +50 -44
  29. claude_mpm/cli/commands/cleanup_orphaned_agents.py +25 -25
  30. claude_mpm/cli/commands/config.py +162 -127
  31. claude_mpm/cli/commands/doctor.py +52 -62
  32. claude_mpm/cli/commands/info.py +37 -25
  33. claude_mpm/cli/commands/mcp.py +3 -7
  34. claude_mpm/cli/commands/mcp_command_router.py +14 -18
  35. claude_mpm/cli/commands/mcp_install_commands.py +28 -23
  36. claude_mpm/cli/commands/mcp_pipx_config.py +58 -49
  37. claude_mpm/cli/commands/mcp_server_commands.py +23 -17
  38. claude_mpm/cli/commands/memory.py +339 -967
  39. claude_mpm/cli/commands/monitor.py +117 -88
  40. claude_mpm/cli/commands/run.py +233 -542
  41. claude_mpm/cli/commands/socketio_monitor.py +17 -19
  42. claude_mpm/cli/commands/tickets.py +92 -92
  43. claude_mpm/cli/parser.py +1 -5
  44. claude_mpm/cli/parsers/__init__.py +1 -1
  45. claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
  46. claude_mpm/cli/parsers/agents_parser.py +2 -3
  47. claude_mpm/cli/parsers/base_parser.py +7 -5
  48. claude_mpm/cli/parsers/mcp_parser.py +4 -2
  49. claude_mpm/cli/parsers/monitor_parser.py +26 -18
  50. claude_mpm/cli/shared/__init__.py +10 -10
  51. claude_mpm/cli/shared/argument_patterns.py +57 -71
  52. claude_mpm/cli/shared/base_command.py +61 -53
  53. claude_mpm/cli/shared/error_handling.py +62 -58
  54. claude_mpm/cli/shared/output_formatters.py +78 -77
  55. claude_mpm/cli/startup_logging.py +280 -172
  56. claude_mpm/cli/utils.py +10 -11
  57. claude_mpm/cli_module/__init__.py +1 -1
  58. claude_mpm/cli_module/args.py +1 -1
  59. claude_mpm/cli_module/migration_example.py +5 -5
  60. claude_mpm/config/__init__.py +9 -9
  61. claude_mpm/config/agent_config.py +15 -14
  62. claude_mpm/config/experimental_features.py +4 -4
  63. claude_mpm/config/paths.py +0 -1
  64. claude_mpm/config/socketio_config.py +5 -6
  65. claude_mpm/constants.py +1 -2
  66. claude_mpm/core/__init__.py +8 -8
  67. claude_mpm/core/agent_name_normalizer.py +1 -1
  68. claude_mpm/core/agent_registry.py +22 -29
  69. claude_mpm/core/agent_session_manager.py +3 -3
  70. claude_mpm/core/base_service.py +7 -15
  71. claude_mpm/core/cache.py +4 -6
  72. claude_mpm/core/claude_runner.py +85 -113
  73. claude_mpm/core/config.py +43 -28
  74. claude_mpm/core/config_aliases.py +0 -9
  75. claude_mpm/core/config_constants.py +52 -30
  76. claude_mpm/core/constants.py +0 -1
  77. claude_mpm/core/container.py +18 -27
  78. claude_mpm/core/exceptions.py +2 -2
  79. claude_mpm/core/factories.py +10 -12
  80. claude_mpm/core/framework_loader.py +500 -680
  81. claude_mpm/core/hook_manager.py +26 -22
  82. claude_mpm/core/hook_performance_config.py +58 -47
  83. claude_mpm/core/injectable_service.py +1 -1
  84. claude_mpm/core/interactive_session.py +61 -152
  85. claude_mpm/core/interfaces.py +1 -100
  86. claude_mpm/core/lazy.py +5 -5
  87. claude_mpm/core/log_manager.py +587 -0
  88. claude_mpm/core/logger.py +125 -8
  89. claude_mpm/core/logging_config.py +15 -17
  90. claude_mpm/core/minimal_framework_loader.py +5 -8
  91. claude_mpm/core/oneshot_session.py +15 -33
  92. claude_mpm/core/optimized_agent_loader.py +4 -6
  93. claude_mpm/core/optimized_startup.py +2 -1
  94. claude_mpm/core/output_style_manager.py +147 -106
  95. claude_mpm/core/pm_hook_interceptor.py +0 -1
  96. claude_mpm/core/service_registry.py +11 -8
  97. claude_mpm/core/session_manager.py +1 -2
  98. claude_mpm/core/shared/__init__.py +1 -1
  99. claude_mpm/core/shared/config_loader.py +101 -97
  100. claude_mpm/core/shared/path_resolver.py +72 -68
  101. claude_mpm/core/shared/singleton_manager.py +56 -50
  102. claude_mpm/core/socketio_pool.py +26 -6
  103. claude_mpm/core/tool_access_control.py +4 -5
  104. claude_mpm/core/typing_utils.py +50 -59
  105. claude_mpm/core/unified_agent_registry.py +14 -19
  106. claude_mpm/core/unified_config.py +4 -6
  107. claude_mpm/core/unified_paths.py +197 -109
  108. claude_mpm/dashboard/open_dashboard.py +2 -4
  109. claude_mpm/experimental/cli_enhancements.py +51 -36
  110. claude_mpm/generators/agent_profile_generator.py +2 -4
  111. claude_mpm/hooks/base_hook.py +1 -2
  112. claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
  113. claude_mpm/hooks/claude_hooks/event_handlers.py +99 -154
  114. claude_mpm/hooks/claude_hooks/hook_handler.py +110 -720
  115. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
  116. claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
  117. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
  118. claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
  119. claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
  120. claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
  121. claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
  122. claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
  123. claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
  124. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
  125. claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
  126. claude_mpm/hooks/memory_integration_hook.py +5 -5
  127. claude_mpm/hooks/tool_call_interceptor.py +1 -1
  128. claude_mpm/hooks/validation_hooks.py +4 -4
  129. claude_mpm/init.py +4 -9
  130. claude_mpm/models/__init__.py +2 -2
  131. claude_mpm/models/agent_session.py +11 -14
  132. claude_mpm/scripts/mcp_server.py +20 -11
  133. claude_mpm/scripts/mcp_wrapper.py +5 -5
  134. claude_mpm/scripts/mpm_doctor.py +321 -0
  135. claude_mpm/scripts/socketio_daemon.py +28 -25
  136. claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
  137. claude_mpm/scripts/socketio_server_manager.py +116 -95
  138. claude_mpm/services/__init__.py +49 -49
  139. claude_mpm/services/agent_capabilities_service.py +12 -18
  140. claude_mpm/services/agents/__init__.py +22 -22
  141. claude_mpm/services/agents/agent_builder.py +140 -119
  142. claude_mpm/services/agents/deployment/__init__.py +3 -3
  143. claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
  144. claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
  145. claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
  146. claude_mpm/services/agents/deployment/agent_deployment.py +129 -511
  147. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
  148. claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
  149. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
  150. claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
  151. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
  152. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
  153. claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
  154. claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
  155. claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
  156. claude_mpm/services/agents/deployment/agent_validator.py +0 -1
  157. claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
  158. claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
  159. claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
  160. claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
  161. claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
  162. claude_mpm/services/agents/deployment/config/__init__.py +1 -1
  163. claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
  164. claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
  165. claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
  166. claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
  167. claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
  168. claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
  169. claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
  170. claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
  171. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
  172. claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
  173. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
  174. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
  175. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
  176. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
  177. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
  178. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
  179. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
  180. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
  181. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
  182. claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
  183. claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
  184. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
  185. claude_mpm/services/agents/deployment/results/__init__.py +1 -1
  186. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
  187. claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
  188. claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
  189. claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
  190. claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
  191. claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
  192. claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
  193. claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
  194. claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
  195. claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
  196. claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
  197. claude_mpm/services/agents/loading/__init__.py +1 -1
  198. claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
  199. claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
  200. claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
  201. claude_mpm/services/agents/management/__init__.py +1 -1
  202. claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
  203. claude_mpm/services/agents/management/agent_management_service.py +5 -9
  204. claude_mpm/services/agents/memory/__init__.py +4 -4
  205. claude_mpm/services/agents/memory/agent_memory_manager.py +157 -503
  206. claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
  207. claude_mpm/services/agents/memory/content_manager.py +44 -38
  208. claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
  209. claude_mpm/services/agents/memory/memory_file_service.py +103 -0
  210. claude_mpm/services/agents/memory/memory_format_service.py +201 -0
  211. claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
  212. claude_mpm/services/agents/memory/template_generator.py +4 -6
  213. claude_mpm/services/agents/registry/__init__.py +11 -7
  214. claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
  215. claude_mpm/services/agents/registry/modification_tracker.py +3 -6
  216. claude_mpm/services/async_session_logger.py +1 -2
  217. claude_mpm/services/claude_session_logger.py +1 -2
  218. claude_mpm/services/cli/__init__.py +18 -0
  219. claude_mpm/services/cli/agent_cleanup_service.py +407 -0
  220. claude_mpm/services/cli/agent_dependency_service.py +395 -0
  221. claude_mpm/services/cli/agent_listing_service.py +463 -0
  222. claude_mpm/services/cli/agent_output_formatter.py +605 -0
  223. claude_mpm/services/cli/agent_validation_service.py +589 -0
  224. claude_mpm/services/cli/dashboard_launcher.py +424 -0
  225. claude_mpm/services/cli/memory_crud_service.py +617 -0
  226. claude_mpm/services/cli/memory_output_formatter.py +604 -0
  227. claude_mpm/services/cli/session_manager.py +513 -0
  228. claude_mpm/services/cli/socketio_manager.py +498 -0
  229. claude_mpm/services/cli/startup_checker.py +370 -0
  230. claude_mpm/services/command_deployment_service.py +173 -0
  231. claude_mpm/services/command_handler_service.py +20 -22
  232. claude_mpm/services/core/__init__.py +25 -25
  233. claude_mpm/services/core/base.py +0 -5
  234. claude_mpm/services/core/cache_manager.py +311 -0
  235. claude_mpm/services/core/interfaces/__init__.py +32 -32
  236. claude_mpm/services/core/interfaces/agent.py +0 -21
  237. claude_mpm/services/core/interfaces/communication.py +0 -27
  238. claude_mpm/services/core/interfaces/infrastructure.py +0 -56
  239. claude_mpm/services/core/interfaces/service.py +0 -29
  240. claude_mpm/services/core/memory_manager.py +637 -0
  241. claude_mpm/services/core/path_resolver.py +498 -0
  242. claude_mpm/services/core/service_container.py +520 -0
  243. claude_mpm/services/core/service_interfaces.py +436 -0
  244. claude_mpm/services/diagnostics/__init__.py +1 -1
  245. claude_mpm/services/diagnostics/checks/__init__.py +6 -6
  246. claude_mpm/services/diagnostics/checks/agent_check.py +152 -97
  247. claude_mpm/services/diagnostics/checks/base_check.py +12 -16
  248. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
  249. claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
  250. claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
  251. claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
  252. claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
  253. claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
  254. claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
  255. claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
  256. claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
  257. claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
  258. claude_mpm/services/diagnostics/models.py +21 -19
  259. claude_mpm/services/event_aggregator.py +10 -17
  260. claude_mpm/services/event_bus/__init__.py +1 -1
  261. claude_mpm/services/event_bus/config.py +54 -35
  262. claude_mpm/services/event_bus/event_bus.py +76 -71
  263. claude_mpm/services/event_bus/relay.py +74 -64
  264. claude_mpm/services/events/__init__.py +11 -11
  265. claude_mpm/services/events/consumers/__init__.py +3 -3
  266. claude_mpm/services/events/consumers/dead_letter.py +71 -63
  267. claude_mpm/services/events/consumers/logging.py +39 -37
  268. claude_mpm/services/events/consumers/metrics.py +56 -57
  269. claude_mpm/services/events/consumers/socketio.py +82 -81
  270. claude_mpm/services/events/core.py +110 -99
  271. claude_mpm/services/events/interfaces.py +56 -72
  272. claude_mpm/services/events/producers/__init__.py +1 -1
  273. claude_mpm/services/events/producers/hook.py +38 -38
  274. claude_mpm/services/events/producers/system.py +46 -44
  275. claude_mpm/services/exceptions.py +81 -80
  276. claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
  277. claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
  278. claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
  279. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
  280. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
  281. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
  282. claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
  283. claude_mpm/services/hook_service.py +6 -9
  284. claude_mpm/services/infrastructure/__init__.py +1 -1
  285. claude_mpm/services/infrastructure/context_preservation.py +8 -12
  286. claude_mpm/services/infrastructure/monitoring.py +21 -23
  287. claude_mpm/services/mcp_gateway/__init__.py +37 -37
  288. claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
  289. claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
  290. claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
  291. claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
  292. claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
  293. claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
  294. claude_mpm/services/mcp_gateway/core/base.py +0 -3
  295. claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
  296. claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
  297. claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
  298. claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
  299. claude_mpm/services/mcp_gateway/main.py +2 -1
  300. claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
  301. claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
  302. claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
  303. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
  304. claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
  305. claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
  306. claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
  307. claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
  308. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
  309. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
  310. claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
  311. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
  312. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
  313. claude_mpm/services/memory/__init__.py +3 -3
  314. claude_mpm/services/memory/builder.py +3 -6
  315. claude_mpm/services/memory/cache/__init__.py +1 -1
  316. claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
  317. claude_mpm/services/memory/cache/simple_cache.py +1 -1
  318. claude_mpm/services/memory/indexed_memory.py +5 -7
  319. claude_mpm/services/memory/optimizer.py +7 -10
  320. claude_mpm/services/memory/router.py +8 -9
  321. claude_mpm/services/memory_hook_service.py +48 -34
  322. claude_mpm/services/monitor_build_service.py +77 -73
  323. claude_mpm/services/port_manager.py +130 -108
  324. claude_mpm/services/project/analyzer.py +12 -10
  325. claude_mpm/services/project/registry.py +11 -11
  326. claude_mpm/services/recovery_manager.py +10 -19
  327. claude_mpm/services/response_tracker.py +0 -1
  328. claude_mpm/services/runner_configuration_service.py +19 -20
  329. claude_mpm/services/session_management_service.py +7 -11
  330. claude_mpm/services/shared/__init__.py +1 -1
  331. claude_mpm/services/shared/async_service_base.py +58 -50
  332. claude_mpm/services/shared/config_service_base.py +73 -67
  333. claude_mpm/services/shared/lifecycle_service_base.py +82 -78
  334. claude_mpm/services/shared/manager_base.py +94 -82
  335. claude_mpm/services/shared/service_factory.py +96 -98
  336. claude_mpm/services/socketio/__init__.py +3 -3
  337. claude_mpm/services/socketio/client_proxy.py +5 -5
  338. claude_mpm/services/socketio/event_normalizer.py +199 -181
  339. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  340. claude_mpm/services/socketio/handlers/base.py +5 -4
  341. claude_mpm/services/socketio/handlers/connection.py +163 -136
  342. claude_mpm/services/socketio/handlers/file.py +13 -14
  343. claude_mpm/services/socketio/handlers/git.py +12 -7
  344. claude_mpm/services/socketio/handlers/hook.py +49 -44
  345. claude_mpm/services/socketio/handlers/memory.py +0 -1
  346. claude_mpm/services/socketio/handlers/project.py +0 -1
  347. claude_mpm/services/socketio/handlers/registry.py +37 -19
  348. claude_mpm/services/socketio/migration_utils.py +98 -84
  349. claude_mpm/services/socketio/server/__init__.py +1 -1
  350. claude_mpm/services/socketio/server/broadcaster.py +81 -87
  351. claude_mpm/services/socketio/server/core.py +65 -54
  352. claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
  353. claude_mpm/services/socketio/server/main.py +64 -38
  354. claude_mpm/services/socketio_client_manager.py +10 -12
  355. claude_mpm/services/subprocess_launcher_service.py +4 -7
  356. claude_mpm/services/system_instructions_service.py +13 -14
  357. claude_mpm/services/ticket_manager.py +2 -2
  358. claude_mpm/services/utility_service.py +5 -13
  359. claude_mpm/services/version_control/__init__.py +16 -16
  360. claude_mpm/services/version_control/branch_strategy.py +5 -8
  361. claude_mpm/services/version_control/conflict_resolution.py +9 -23
  362. claude_mpm/services/version_control/git_operations.py +5 -7
  363. claude_mpm/services/version_control/semantic_versioning.py +16 -17
  364. claude_mpm/services/version_control/version_parser.py +13 -18
  365. claude_mpm/services/version_service.py +10 -11
  366. claude_mpm/storage/__init__.py +1 -1
  367. claude_mpm/storage/state_storage.py +22 -28
  368. claude_mpm/utils/__init__.py +6 -6
  369. claude_mpm/utils/agent_dependency_loader.py +47 -33
  370. claude_mpm/utils/config_manager.py +11 -14
  371. claude_mpm/utils/dependency_cache.py +1 -1
  372. claude_mpm/utils/dependency_manager.py +13 -17
  373. claude_mpm/utils/dependency_strategies.py +8 -10
  374. claude_mpm/utils/environment_context.py +3 -9
  375. claude_mpm/utils/error_handler.py +3 -13
  376. claude_mpm/utils/file_utils.py +1 -1
  377. claude_mpm/utils/path_operations.py +8 -12
  378. claude_mpm/utils/robust_installer.py +110 -33
  379. claude_mpm/utils/subprocess_utils.py +5 -6
  380. claude_mpm/validation/agent_validator.py +3 -6
  381. claude_mpm/validation/frontmatter_validator.py +1 -1
  382. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/METADATA +1 -1
  383. claude_mpm-4.1.3.dist-info/RECORD +528 -0
  384. claude_mpm/cli/commands/run_config_checker.py +0 -160
  385. claude_mpm-4.1.1.dist-info/RECORD +0 -494
  386. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/WHEEL +0 -0
  387. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/entry_points.txt +0 -0
  388. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/licenses/LICENSE +0 -0
  389. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/top_level.txt +0 -0
@@ -12,18 +12,17 @@ DESIGN DECISIONS:
12
12
  """
13
13
 
14
14
  import json
15
- import os
16
- from pathlib import Path
17
- from typing import Any, Dict, Optional
18
15
 
19
- import yaml
20
-
21
- from ...agents.frontmatter_validator import FrontmatterValidator
22
16
  from ...constants import AgentCommands
23
- from ...core.agent_registry import AgentRegistryAdapter
24
- from ...core.config import Config
25
- from ...core.shared.config_loader import ConfigLoader
26
- from ..shared import AgentCommand, CommandResult, add_agent_arguments, add_output_arguments
17
+ from ...services.cli.agent_cleanup_service import AgentCleanupService
18
+ from ...services.cli.agent_dependency_service import AgentDependencyService
19
+ from ...services.cli.agent_listing_service import AgentListingService
20
+ from ...services.cli.agent_output_formatter import AgentOutputFormatter
21
+ from ...services.cli.agent_validation_service import AgentValidationService
22
+ from ..shared import (
23
+ AgentCommand,
24
+ CommandResult,
25
+ )
27
26
  from ..utils import get_agent_versions_display
28
27
 
29
28
 
@@ -33,6 +32,11 @@ class AgentsCommand(AgentCommand):
33
32
  def __init__(self):
34
33
  super().__init__("agents")
35
34
  self._deployment_service = None
35
+ self._listing_service = None
36
+ self._validation_service = None
37
+ self._dependency_service = None
38
+ self._cleanup_service = None
39
+ self._formatter = AgentOutputFormatter()
36
40
 
37
41
  @property
38
42
  def deployment_service(self):
@@ -40,13 +44,48 @@ class AgentsCommand(AgentCommand):
40
44
  if self._deployment_service is None:
41
45
  try:
42
46
  from ...services import AgentDeploymentService
43
- from ...services.agents.deployment.deployment_wrapper import DeploymentServiceWrapper
47
+ from ...services.agents.deployment.deployment_wrapper import (
48
+ DeploymentServiceWrapper,
49
+ )
50
+
44
51
  base_service = AgentDeploymentService()
45
52
  self._deployment_service = DeploymentServiceWrapper(base_service)
46
53
  except ImportError:
47
54
  raise ImportError("Agent deployment service not available")
48
55
  return self._deployment_service
49
56
 
57
+ @property
58
+ def listing_service(self):
59
+ """Get listing service instance (lazy loaded)."""
60
+ if self._listing_service is None:
61
+ self._listing_service = AgentListingService(
62
+ deployment_service=self.deployment_service
63
+ )
64
+ return self._listing_service
65
+
66
+ @property
67
+ def validation_service(self):
68
+ """Get validation service instance (lazy loaded)."""
69
+ if self._validation_service is None:
70
+ self._validation_service = AgentValidationService()
71
+ return self._validation_service
72
+
73
+ @property
74
+ def dependency_service(self):
75
+ """Get dependency service instance (lazy loaded)."""
76
+ if self._dependency_service is None:
77
+ self._dependency_service = AgentDependencyService()
78
+ return self._dependency_service
79
+
80
+ @property
81
+ def cleanup_service(self):
82
+ """Get cleanup service instance (lazy loaded)."""
83
+ if self._cleanup_service is None:
84
+ self._cleanup_service = AgentCleanupService(
85
+ deployment_service=self.deployment_service
86
+ )
87
+ return self._cleanup_service
88
+
50
89
  def validate_args(self, args) -> str:
51
90
  """Validate command arguments."""
52
91
  # Most agent commands are optional, so basic validation
@@ -56,14 +95,18 @@ class AgentsCommand(AgentCommand):
56
95
  """Execute the agent command."""
57
96
  try:
58
97
  # Handle default case (no subcommand)
59
- if not hasattr(args, 'agents_command') or not args.agents_command:
98
+ if not hasattr(args, "agents_command") or not args.agents_command:
60
99
  return self._show_agent_versions(args)
61
100
 
62
101
  # Route to appropriate subcommand
63
102
  command_map = {
64
103
  AgentCommands.LIST.value: self._list_agents,
65
- AgentCommands.DEPLOY.value: lambda a: self._deploy_agents(a, force=False),
66
- AgentCommands.FORCE_DEPLOY.value: lambda a: self._deploy_agents(a, force=True),
104
+ AgentCommands.DEPLOY.value: lambda a: self._deploy_agents(
105
+ a, force=False
106
+ ),
107
+ AgentCommands.FORCE_DEPLOY.value: lambda a: self._deploy_agents(
108
+ a, force=True
109
+ ),
67
110
  AgentCommands.CLEAN.value: self._clean_agents,
68
111
  AgentCommands.VIEW.value: self._view_agent,
69
112
  AgentCommands.FIX.value: self._fix_agents,
@@ -76,10 +119,11 @@ class AgentsCommand(AgentCommand):
76
119
 
77
120
  if args.agents_command in command_map:
78
121
  return command_map[args.agents_command](args)
79
- else:
80
- return CommandResult.error_result(f"Unknown agent command: {args.agents_command}")
122
+ return CommandResult.error_result(
123
+ f"Unknown agent command: {args.agents_command}"
124
+ )
81
125
 
82
- except ImportError as e:
126
+ except ImportError:
83
127
  self.logger.error("Agent deployment service not available")
84
128
  return CommandResult.error_result("Agent deployment service not available")
85
129
  except Exception as e:
@@ -91,53 +135,70 @@ class AgentsCommand(AgentCommand):
91
135
  try:
92
136
  agent_versions = get_agent_versions_display()
93
137
 
94
- output_format = getattr(args, 'format', 'text')
95
- if output_format in ['json', 'yaml']:
138
+ output_format = getattr(args, "format", "text")
139
+ if output_format in ["json", "yaml"]:
96
140
  # Parse the agent versions display into structured data
97
141
  if agent_versions:
98
142
  data = {"agent_versions": agent_versions, "has_agents": True}
99
- return CommandResult.success_result("Agent versions retrieved", data=data)
100
- else:
101
- data = {"agent_versions": None, "has_agents": False, "suggestion": "To deploy agents, run: claude-mpm --mpm:agents deploy"}
102
- return CommandResult.success_result("No deployed agents found", data=data)
103
- else:
104
- # Text output
105
- if agent_versions:
106
- print(agent_versions)
107
- return CommandResult.success_result("Agent versions displayed")
108
- else:
109
- print("No deployed agents found")
110
- print("\nTo deploy agents, run: claude-mpm --mpm:agents deploy")
111
- return CommandResult.success_result("No deployed agents found")
143
+ formatted = (
144
+ self._formatter.format_as_json(data)
145
+ if output_format == "json"
146
+ else self._formatter.format_as_yaml(data)
147
+ )
148
+ print(formatted)
149
+ return CommandResult.success_result(
150
+ "Agent versions retrieved", data=data
151
+ )
152
+ data = {
153
+ "agent_versions": None,
154
+ "has_agents": False,
155
+ "suggestion": "To deploy agents, run: claude-mpm --mpm:agents deploy",
156
+ }
157
+ formatted = (
158
+ self._formatter.format_as_json(data)
159
+ if output_format == "json"
160
+ else self._formatter.format_as_yaml(data)
161
+ )
162
+ print(formatted)
163
+ return CommandResult.success_result(
164
+ "No deployed agents found", data=data
165
+ )
166
+ # Text output
167
+ if agent_versions:
168
+ print(agent_versions)
169
+ return CommandResult.success_result("Agent versions displayed")
170
+ print("No deployed agents found")
171
+ print("\nTo deploy agents, run: claude-mpm --mpm:agents deploy")
172
+ return CommandResult.success_result("No deployed agents found")
112
173
 
113
174
  except Exception as e:
114
175
  self.logger.error(f"Error getting agent versions: {e}", exc_info=True)
115
176
  return CommandResult.error_result(f"Error getting agent versions: {e}")
116
177
 
117
-
118
178
  def _list_agents(self, args) -> CommandResult:
119
179
  """List available or deployed agents."""
120
180
  try:
121
- output_format = getattr(args, 'format', 'text')
181
+ output_format = getattr(args, "format", "text")
122
182
 
123
183
  if hasattr(args, "by_tier") and args.by_tier:
124
184
  return self._list_agents_by_tier(args)
125
- elif getattr(args, 'system', False):
185
+ if getattr(args, "system", False):
126
186
  return self._list_system_agents(args)
127
- elif getattr(args, 'deployed', False):
187
+ if getattr(args, "deployed", False):
128
188
  return self._list_deployed_agents(args)
129
- else:
130
- # Default: show usage
131
- usage_msg = "Use --system to list system agents, --deployed to list deployed agents, or --by-tier to group by precedence"
189
+ # Default: show usage
190
+ usage_msg = "Use --system to list system agents, --deployed to list deployed agents, or --by-tier to group by precedence"
132
191
 
133
- if output_format in ['json', 'yaml']:
134
- return CommandResult.error_result(
135
- "No list option specified",
136
- data={"usage": usage_msg, "available_options": ["--system", "--deployed", "--by-tier"]}
137
- )
138
- else:
139
- print(usage_msg)
140
- return CommandResult.error_result("No list option specified")
192
+ if output_format in ["json", "yaml"]:
193
+ return CommandResult.error_result(
194
+ "No list option specified",
195
+ data={
196
+ "usage": usage_msg,
197
+ "available_options": ["--system", "--deployed", "--by-tier"],
198
+ },
199
+ )
200
+ print(usage_msg)
201
+ return CommandResult.error_result("No list option specified")
141
202
 
142
203
  except Exception as e:
143
204
  self.logger.error(f"Error listing agents: {e}", exc_info=True)
@@ -146,32 +207,34 @@ class AgentsCommand(AgentCommand):
146
207
  def _list_system_agents(self, args) -> CommandResult:
147
208
  """List available agent templates."""
148
209
  try:
149
- agents = self.deployment_service.list_available_agents()
150
- output_format = getattr(args, 'format', 'text')
210
+ verbose = getattr(args, "verbose", False)
211
+ agents = self.listing_service.list_system_agents(verbose=verbose)
212
+
213
+ output_format = getattr(args, "format", "text")
214
+ quiet = getattr(args, "quiet", False)
215
+
216
+ # Convert AgentInfo objects to dicts for formatter
217
+ agents_data = [
218
+ {
219
+ "name": agent.name,
220
+ "type": agent.type,
221
+ "path": agent.path,
222
+ "description": agent.description,
223
+ "specializations": agent.specializations,
224
+ "version": agent.version,
225
+ }
226
+ for agent in agents
227
+ ]
228
+
229
+ formatted = self._formatter.format_agent_list(
230
+ agents_data, output_format=output_format, verbose=verbose, quiet=quiet
231
+ )
232
+ print(formatted)
151
233
 
152
- if output_format in ['json', 'yaml']:
153
- return CommandResult.success_result(
154
- f"Found {len(agents)} agent templates",
155
- data={"agents": agents, "count": len(agents)}
156
- )
157
- else:
158
- # Text output
159
- print("Available Agent Templates:")
160
- print("-" * 80)
161
- if not agents:
162
- print("No agent templates found")
163
- else:
164
- for agent in agents:
165
- print(f"📄 {agent['file']}")
166
- if "name" in agent:
167
- print(f" Name: {agent['name']}")
168
- if "description" in agent:
169
- print(f" Description: {agent['description']}")
170
- if "version" in agent:
171
- print(f" Version: {agent['version']}")
172
- print()
173
-
174
- return CommandResult.success_result(f"Listed {len(agents)} agent templates")
234
+ return CommandResult.success_result(
235
+ f"Listed {len(agents)} agent templates",
236
+ data={"agents": agents_data, "count": len(agents)},
237
+ )
175
238
 
176
239
  except Exception as e:
177
240
  self.logger.error(f"Error listing system agents: {e}", exc_info=True)
@@ -180,39 +243,48 @@ class AgentsCommand(AgentCommand):
180
243
  def _list_deployed_agents(self, args) -> CommandResult:
181
244
  """List deployed agents."""
182
245
  try:
183
- verification = self.deployment_service.verify_deployment()
184
- output_format = getattr(args, 'format', 'text')
185
-
186
- if output_format in ['json', 'yaml']:
187
- return CommandResult.success_result(
188
- f"Found {len(verification['agents_found'])} deployed agents",
189
- data={
190
- "agents": verification["agents_found"],
191
- "warnings": verification.get("warnings", []),
192
- "count": len(verification["agents_found"])
193
- }
194
- )
195
- else:
196
- # Text output
197
- print("Deployed Agents:")
198
- print("-" * 80)
199
- if not verification["agents_found"]:
200
- print("No deployed agents found")
201
- else:
202
- for agent in verification["agents_found"]:
203
- print(f"📄 {agent['file']}")
204
- if "name" in agent:
205
- print(f" Name: {agent['name']}")
206
- if "path" in agent:
207
- print(f" Path: {agent['path']}")
208
- print()
209
-
210
- if verification["warnings"]:
211
- print("\nWarnings:")
212
- for warning in verification["warnings"]:
213
- print(f" ⚠️ {warning}")
246
+ verbose = getattr(args, "verbose", False)
247
+ agents, warnings = self.listing_service.list_deployed_agents(
248
+ verbose=verbose
249
+ )
214
250
 
215
- return CommandResult.success_result(f"Listed {len(verification['agents_found'])} deployed agents")
251
+ output_format = getattr(args, "format", "text")
252
+ quiet = getattr(args, "quiet", False)
253
+
254
+ # Convert AgentInfo objects to dicts for formatter
255
+ agents_data = [
256
+ {
257
+ "name": agent.name,
258
+ "type": agent.type,
259
+ "tier": agent.tier,
260
+ "path": agent.path,
261
+ "description": agent.description,
262
+ "specializations": agent.specializations,
263
+ "version": agent.version,
264
+ }
265
+ for agent in agents
266
+ ]
267
+
268
+ # Format the agent list
269
+ formatted = self._formatter.format_agent_list(
270
+ agents_data, output_format=output_format, verbose=verbose, quiet=quiet
271
+ )
272
+ print(formatted)
273
+
274
+ # Add warnings for text output
275
+ if output_format == "text" and warnings:
276
+ print("\nWarnings:")
277
+ for warning in warnings:
278
+ print(f" ⚠️ {warning}")
279
+
280
+ return CommandResult.success_result(
281
+ f"Listed {len(agents)} deployed agents",
282
+ data={
283
+ "agents": agents_data,
284
+ "warnings": warnings,
285
+ "count": len(agents),
286
+ },
287
+ )
216
288
 
217
289
  except Exception as e:
218
290
  self.logger.error(f"Error listing deployed agents: {e}", exc_info=True)
@@ -221,29 +293,58 @@ class AgentsCommand(AgentCommand):
221
293
  def _list_agents_by_tier(self, args) -> CommandResult:
222
294
  """List agents grouped by tier/precedence."""
223
295
  try:
224
- agents_by_tier = self.deployment_service.list_agents_by_tier()
225
- output_format = getattr(args, 'format', 'text')
296
+ tier_info = self.listing_service.list_agents_by_tier()
297
+ output_format = getattr(args, "format", "text")
298
+
299
+ # Convert to format expected by formatter
300
+ agents_by_tier = {
301
+ "project": [
302
+ {
303
+ "name": agent.name,
304
+ "type": agent.type,
305
+ "path": agent.path,
306
+ "active": agent.active,
307
+ "overridden_by": agent.overridden_by,
308
+ }
309
+ for agent in tier_info.project
310
+ ],
311
+ "user": [
312
+ {
313
+ "name": agent.name,
314
+ "type": agent.type,
315
+ "path": agent.path,
316
+ "active": agent.active,
317
+ "overridden_by": agent.overridden_by,
318
+ }
319
+ for agent in tier_info.user
320
+ ],
321
+ "system": [
322
+ {
323
+ "name": agent.name,
324
+ "type": agent.type,
325
+ "path": agent.path,
326
+ "active": agent.active,
327
+ "overridden_by": agent.overridden_by,
328
+ }
329
+ for agent in tier_info.system
330
+ ],
331
+ "summary": {
332
+ "total_count": tier_info.total_count,
333
+ "active_count": tier_info.active_count,
334
+ "project_count": len(tier_info.project),
335
+ "user_count": len(tier_info.user),
336
+ "system_count": len(tier_info.system),
337
+ },
338
+ }
226
339
 
227
- if output_format in ['json', 'yaml']:
228
- return CommandResult.success_result(
229
- "Agents listed by tier",
230
- data=agents_by_tier
231
- )
232
- else:
233
- # Text output
234
- print("Agents by Tier/Precedence:")
235
- print("=" * 50)
236
-
237
- for tier, agents in agents_by_tier.items():
238
- print(f"\n{tier.upper()}:")
239
- print("-" * 20)
240
- if agents:
241
- for agent in agents:
242
- print(f" • {agent}")
243
- else:
244
- print(" (none)")
340
+ formatted = self._formatter.format_agents_by_tier(
341
+ agents_by_tier, output_format=output_format
342
+ )
343
+ print(formatted)
245
344
 
246
- return CommandResult.success_result("Agents listed by tier")
345
+ return CommandResult.success_result(
346
+ "Agents listed by tier", data=agents_by_tier
347
+ )
247
348
 
248
349
  except Exception as e:
249
350
  self.logger.error(f"Error listing agents by tier: {e}", exc_info=True)
@@ -259,29 +360,39 @@ class AgentsCommand(AgentCommand):
259
360
  project_result = self.deployment_service.deploy_project_agents(force=force)
260
361
 
261
362
  # Combine results
262
- total_deployed = system_result.get('deployed_count', 0) + project_result.get('deployed_count', 0)
263
-
264
- output_format = getattr(args, 'format', 'text')
265
- if output_format in ['json', 'yaml']:
266
- return CommandResult.success_result(
267
- f"Deployed {total_deployed} agents",
268
- data={
269
- "system_agents": system_result,
270
- "project_agents": project_result,
271
- "total_deployed": total_deployed
272
- }
273
- )
274
- else:
275
- # Text output
276
- if system_result.get('deployed_count', 0) > 0:
277
- print(f"✓ Deployed {system_result['deployed_count']} system agents")
278
- if project_result.get('deployed_count', 0) > 0:
279
- print(f"✓ Deployed {project_result['deployed_count']} project agents")
363
+ combined_result = {
364
+ "deployed_count": system_result.get("deployed_count", 0)
365
+ + project_result.get("deployed_count", 0),
366
+ "deployed": system_result.get("deployed", [])
367
+ + project_result.get("deployed", []),
368
+ "updated_count": system_result.get("updated_count", 0)
369
+ + project_result.get("updated_count", 0),
370
+ "updated": system_result.get("updated", [])
371
+ + project_result.get("updated", []),
372
+ "skipped": system_result.get("skipped", [])
373
+ + project_result.get("skipped", []),
374
+ "errors": system_result.get("errors", [])
375
+ + project_result.get("errors", []),
376
+ "target_dir": system_result.get("target_dir")
377
+ or project_result.get("target_dir"),
378
+ }
280
379
 
281
- if total_deployed == 0:
282
- print("No agents were deployed (all up to date)")
380
+ output_format = getattr(args, "format", "text")
381
+ verbose = getattr(args, "verbose", False)
283
382
 
284
- return CommandResult.success_result(f"Deployed {total_deployed} agents")
383
+ formatted = self._formatter.format_deployment_result(
384
+ combined_result, output_format=output_format, verbose=verbose
385
+ )
386
+ print(formatted)
387
+
388
+ return CommandResult.success_result(
389
+ f"Deployed {combined_result['deployed_count']} agents",
390
+ data={
391
+ "system_agents": system_result,
392
+ "project_agents": project_result,
393
+ "total_deployed": combined_result["deployed_count"],
394
+ },
395
+ )
285
396
 
286
397
  except Exception as e:
287
398
  self.logger.error(f"Error deploying agents: {e}", exc_info=True)
@@ -290,23 +401,20 @@ class AgentsCommand(AgentCommand):
290
401
  def _clean_agents(self, args) -> CommandResult:
291
402
  """Clean deployed agents."""
292
403
  try:
293
- result = self.deployment_service.clean_deployment()
404
+ result = self.cleanup_service.clean_deployed_agents()
294
405
 
295
- output_format = getattr(args, 'format', 'text')
296
- if output_format in ['json', 'yaml']:
297
- return CommandResult.success_result(
298
- f"Cleaned {result.get('cleaned_count', 0)} agents",
299
- data=result
300
- )
301
- else:
302
- # Text output
303
- cleaned_count = result.get('cleaned_count', 0)
304
- if cleaned_count > 0:
305
- print(f"✓ Cleaned {cleaned_count} deployed agents")
306
- else:
307
- print("No deployed agents to clean")
406
+ output_format = getattr(args, "format", "text")
407
+ dry_run = False # Regular clean is not a dry run
308
408
 
309
- return CommandResult.success_result(f"Cleaned {cleaned_count} agents")
409
+ formatted = self._formatter.format_cleanup_result(
410
+ result, output_format=output_format, dry_run=dry_run
411
+ )
412
+ print(formatted)
413
+
414
+ cleaned_count = result.get("cleaned_count", 0)
415
+ return CommandResult.success_result(
416
+ f"Cleaned {cleaned_count} agents", data=result
417
+ )
310
418
 
311
419
  except Exception as e:
312
420
  self.logger.error(f"Error cleaning agents: {e}", exc_info=True)
@@ -315,51 +423,184 @@ class AgentsCommand(AgentCommand):
315
423
  def _view_agent(self, args) -> CommandResult:
316
424
  """View details of a specific agent."""
317
425
  try:
318
- agent_name = getattr(args, 'agent_name', None)
426
+ agent_name = getattr(args, "agent_name", None)
319
427
  if not agent_name:
320
- return CommandResult.error_result("Agent name is required for view command")
428
+ return CommandResult.error_result(
429
+ "Agent name is required for view command"
430
+ )
321
431
 
322
- # Get agent details from deployment service
323
- agent_details = self.deployment_service.get_agent_details(agent_name)
432
+ # Get agent details from listing service
433
+ agent_details = self.listing_service.get_agent_details(agent_name)
324
434
 
325
- output_format = getattr(args, 'format', 'text')
326
- if output_format in ['json', 'yaml']:
327
- return CommandResult.success_result(
328
- f"Agent details for {agent_name}",
329
- data=agent_details
435
+ if not agent_details:
436
+ # Try to find the agent to provide helpful error message
437
+ agent = self.listing_service.find_agent(agent_name)
438
+ if not agent:
439
+ return CommandResult.error_result(f"Agent '{agent_name}' not found")
440
+ return CommandResult.error_result(
441
+ f"Could not retrieve details for agent '{agent_name}'"
330
442
  )
331
- else:
332
- # Text output
333
- print(f"Agent: {agent_name}")
334
- print("-" * 40)
335
- for key, value in agent_details.items():
336
- print(f"{key}: {value}")
337
443
 
338
- return CommandResult.success_result(f"Displayed details for {agent_name}")
444
+ output_format = getattr(args, "format", "text")
445
+ verbose = getattr(args, "verbose", False)
446
+
447
+ formatted = self._formatter.format_agent_details(
448
+ agent_details, output_format=output_format, verbose=verbose
449
+ )
450
+ print(formatted)
451
+
452
+ return CommandResult.success_result(
453
+ f"Displayed details for {agent_name}", data=agent_details
454
+ )
339
455
 
340
456
  except Exception as e:
341
457
  self.logger.error(f"Error viewing agent: {e}", exc_info=True)
342
458
  return CommandResult.error_result(f"Error viewing agent: {e}")
343
459
 
344
460
  def _fix_agents(self, args) -> CommandResult:
345
- """Fix agent deployment issues."""
461
+ """Fix agent frontmatter issues using validation service."""
346
462
  try:
347
- result = self.deployment_service.fix_deployment()
463
+ dry_run = getattr(args, "dry_run", False)
464
+ agent_name = getattr(args, "agent_name", None)
465
+ fix_all = getattr(args, "all", False)
466
+
467
+ output_format = getattr(args, "format", "text")
468
+
469
+ # Determine what to fix
470
+ if fix_all:
471
+ # Fix all agents
472
+ result = self.validation_service.fix_all_agents(dry_run=dry_run)
473
+
474
+ if output_format in ["json", "yaml"]:
475
+ formatted = (
476
+ self._formatter.format_as_json(result)
477
+ if output_format == "json"
478
+ else self._formatter.format_as_yaml(result)
479
+ )
480
+ print(formatted)
481
+ else:
482
+ # Text output
483
+ mode = "DRY RUN" if dry_run else "FIX"
484
+ print(
485
+ f"\n🔧 {mode}: Checking {result.get('total_agents', 0)} agent(s) for frontmatter issues...\n"
486
+ )
348
487
 
349
- output_format = getattr(args, 'format', 'text')
350
- if output_format in ['json', 'yaml']:
351
- return CommandResult.success_result(
352
- "Agent deployment fixed",
353
- data=result
488
+ if result.get("results"):
489
+ for agent_result in result["results"]:
490
+ print(f"📄 {agent_result['agent']}:")
491
+ if agent_result.get("skipped"):
492
+ print(
493
+ f" ⚠️ Skipped: {agent_result.get('reason', 'Unknown reason')}"
494
+ )
495
+ elif agent_result.get("was_valid"):
496
+ print(" ✓ No issues found")
497
+ else:
498
+ if agent_result.get("errors_found", 0) > 0:
499
+ print(
500
+ f" ❌ Errors found: {agent_result['errors_found']}"
501
+ )
502
+ if agent_result.get("warnings_found", 0) > 0:
503
+ print(
504
+ f" ⚠️ Warnings found: {agent_result['warnings_found']}"
505
+ )
506
+ if dry_run:
507
+ if agent_result.get("corrections_available", 0) > 0:
508
+ print(
509
+ f" 🔧 Would fix: {agent_result['corrections_available']} issues"
510
+ )
511
+ elif agent_result.get("corrections_made", 0) > 0:
512
+ print(
513
+ f" ✓ Fixed: {agent_result['corrections_made']} issues"
514
+ )
515
+ print()
516
+
517
+ # Summary
518
+ print("=" * 80)
519
+ print("SUMMARY:")
520
+ print(f" Agents checked: {result.get('agents_checked', 0)}")
521
+ print(
522
+ f" Total issues found: {result.get('total_issues_found', 0)}"
523
+ )
524
+ if dry_run:
525
+ print(
526
+ f" Issues that would be fixed: {result.get('total_corrections_available', 0)}"
527
+ )
528
+ print("\n💡 Run without --dry-run to apply fixes")
529
+ else:
530
+ print(
531
+ f" Issues fixed: {result.get('total_corrections_made', 0)}"
532
+ )
533
+ if result.get("total_corrections_made", 0) > 0:
534
+ print("\n✓ Frontmatter issues have been fixed!")
535
+ print("=" * 80 + "\n")
536
+
537
+ msg = f"{'Would fix' if dry_run else 'Fixed'} {result.get('total_corrections_available' if dry_run else 'total_corrections_made', 0)} issues"
538
+ return CommandResult.success_result(msg, data=result)
539
+
540
+ if agent_name:
541
+ # Fix specific agent
542
+ result = self.validation_service.fix_agent_frontmatter(
543
+ agent_name, dry_run=dry_run
354
544
  )
355
- else:
356
- # Text output
357
- print("✓ Agent deployment issues fixed")
358
- if result.get('fixes_applied'):
359
- for fix in result['fixes_applied']:
360
- print(f" - {fix}")
361
545
 
362
- return CommandResult.success_result("Agent deployment fixed")
546
+ if not result.get("success"):
547
+ return CommandResult.error_result(
548
+ result.get("error", "Failed to fix agent")
549
+ )
550
+
551
+ if output_format in ["json", "yaml"]:
552
+ formatted = (
553
+ self._formatter.format_as_json(result)
554
+ if output_format == "json"
555
+ else self._formatter.format_as_yaml(result)
556
+ )
557
+ print(formatted)
558
+ else:
559
+ # Text output
560
+ mode = "DRY RUN" if dry_run else "FIX"
561
+ print(
562
+ f"\n🔧 {mode}: Checking agent '{agent_name}' for frontmatter issues...\n"
563
+ )
564
+
565
+ print(f"📄 {agent_name}:")
566
+ if result.get("was_valid"):
567
+ print(" ✓ No issues found")
568
+ else:
569
+ if result.get("errors_found"):
570
+ print(" ❌ Errors:")
571
+ for error in result["errors_found"]:
572
+ print(f" - {error}")
573
+ if result.get("warnings_found"):
574
+ print(" ⚠️ Warnings:")
575
+ for warning in result["warnings_found"]:
576
+ print(f" - {warning}")
577
+ if dry_run:
578
+ if result.get("corrections_available"):
579
+ print(" 🔧 Would fix:")
580
+ for correction in result["corrections_available"]:
581
+ print(f" - {correction}")
582
+ elif result.get("corrections_made"):
583
+ print(" ✓ Fixed:")
584
+ for correction in result["corrections_made"]:
585
+ print(f" - {correction}")
586
+ print()
587
+
588
+ if dry_run and result.get("corrections_available"):
589
+ print("💡 Run without --dry-run to apply fixes\n")
590
+ elif not dry_run and result.get("corrections_made"):
591
+ print("✓ Frontmatter issues have been fixed!\n")
592
+
593
+ msg = f"{'Would fix' if dry_run else 'Fixed'} agent '{agent_name}'"
594
+ return CommandResult.success_result(msg, data=result)
595
+
596
+ # No agent specified and not --all
597
+ usage_msg = "Please specify an agent name or use --all to fix all agents\nUsage: claude-mpm agents fix [agent_name] [--dry-run] [--all]"
598
+ if output_format in ["json", "yaml"]:
599
+ return CommandResult.error_result(
600
+ "No agent specified", data={"usage": usage_msg}
601
+ )
602
+ print(f"❌ {usage_msg}")
603
+ return CommandResult.error_result("No agent specified")
363
604
 
364
605
  except Exception as e:
365
606
  self.logger.error(f"Error fixing agents: {e}", exc_info=True)
@@ -368,26 +609,25 @@ class AgentsCommand(AgentCommand):
368
609
  def _check_agent_dependencies(self, args) -> CommandResult:
369
610
  """Check agent dependencies."""
370
611
  try:
371
- result = self.deployment_service.check_dependencies()
612
+ agent_name = getattr(args, "agent", None)
613
+ result = self.dependency_service.check_dependencies(agent_name=agent_name)
372
614
 
373
- output_format = getattr(args, 'format', 'text')
374
- if output_format in ['json', 'yaml']:
375
- return CommandResult.success_result(
376
- "Dependency check completed",
377
- data=result
615
+ if not result["success"]:
616
+ if "available_agents" in result:
617
+ print(f"❌ Agent '{agent_name}' is not deployed")
618
+ print(
619
+ f" Available agents: {', '.join(result['available_agents'])}"
620
+ )
621
+ return CommandResult.error_result(
622
+ result.get("error", "Dependency check failed")
378
623
  )
379
- else:
380
- # Text output
381
- print("Agent Dependencies Check:")
382
- print("-" * 40)
383
- if result.get('missing_dependencies'):
384
- print("Missing dependencies:")
385
- for dep in result['missing_dependencies']:
386
- print(f" - {dep}")
387
- else:
388
- print("✓ All dependencies satisfied")
389
624
 
390
- return CommandResult.success_result("Dependency check completed")
625
+ # Print the formatted report
626
+ print(result["report"])
627
+
628
+ return CommandResult.success_result(
629
+ "Dependency check completed", data=result
630
+ )
391
631
 
392
632
  except Exception as e:
393
633
  self.logger.error(f"Error checking dependencies: {e}", exc_info=True)
@@ -396,23 +636,46 @@ class AgentsCommand(AgentCommand):
396
636
  def _install_agent_dependencies(self, args) -> CommandResult:
397
637
  """Install agent dependencies."""
398
638
  try:
399
- result = self.deployment_service.install_dependencies()
639
+ agent_name = getattr(args, "agent", None)
640
+ dry_run = getattr(args, "dry_run", False)
641
+ result = self.dependency_service.install_dependencies(
642
+ agent_name=agent_name, dry_run=dry_run
643
+ )
400
644
 
401
- output_format = getattr(args, 'format', 'text')
402
- if output_format in ['json', 'yaml']:
403
- return CommandResult.success_result(
404
- f"Installed {result.get('installed_count', 0)} dependencies",
405
- data=result
645
+ if not result["success"]:
646
+ if "available_agents" in result:
647
+ print(f"❌ Agent '{agent_name}' is not deployed")
648
+ print(
649
+ f" Available agents: {', '.join(result['available_agents'])}"
650
+ )
651
+ return CommandResult.error_result(
652
+ result.get("error", "Installation failed")
653
+ )
654
+
655
+ if result.get("missing_count") == 0:
656
+ print("✅ All Python dependencies are already installed")
657
+ elif dry_run:
658
+ print(
659
+ f"Found {len(result['missing_dependencies'])} missing dependencies:"
406
660
  )
661
+ for dep in result["missing_dependencies"]:
662
+ print(f" - {dep}")
663
+ print("\n--dry-run specified, not installing anything")
664
+ print(f"Would install: {result['install_command']}")
407
665
  else:
408
- # Text output
409
- installed_count = result.get('installed_count', 0)
410
- if installed_count > 0:
411
- print(f"✓ Installed {installed_count} dependencies")
412
- else:
413
- print("No dependencies needed installation")
666
+ print(
667
+ f"✅ Successfully installed {len(result.get('installed', []))} dependencies"
668
+ )
669
+ if result.get("still_missing"):
670
+ print(
671
+ f"⚠️ {len(result['still_missing'])} dependencies still missing after installation"
672
+ )
673
+ elif result.get("fully_resolved"):
674
+ print("✅ All dependencies verified after installation")
414
675
 
415
- return CommandResult.success_result(f"Installed {installed_count} dependencies")
676
+ return CommandResult.success_result(
677
+ "Dependency installation completed", data=result
678
+ )
416
679
 
417
680
  except Exception as e:
418
681
  self.logger.error(f"Error installing dependencies: {e}", exc_info=True)
@@ -421,27 +684,58 @@ class AgentsCommand(AgentCommand):
421
684
  def _list_agent_dependencies(self, args) -> CommandResult:
422
685
  """List agent dependencies."""
423
686
  try:
424
- result = self.deployment_service.list_dependencies()
687
+ output_format = getattr(args, "format", "text")
688
+ result = self.dependency_service.list_dependencies(
689
+ format_type=output_format
690
+ )
425
691
 
426
- output_format = getattr(args, 'format', 'text')
427
- if output_format in ['json', 'yaml']:
428
- return CommandResult.success_result(
429
- f"Found {len(result.get('dependencies', []))} dependencies",
430
- data=result
431
- )
432
- else:
433
- # Text output
434
- dependencies = result.get('dependencies', [])
435
- print("Agent Dependencies:")
436
- print("-" * 40)
437
- if dependencies:
438
- for dep in dependencies:
439
- status = "✓" if dep.get('installed') else "✗"
440
- print(f"{status} {dep.get('name', 'Unknown')}")
441
- else:
442
- print("No dependencies found")
692
+ if not result["success"]:
693
+ return CommandResult.error_result(result.get("error", "Listing failed"))
694
+
695
+ # Format output based on requested format
696
+ if output_format == "pip":
697
+ for dep in result["dependencies"]:
698
+ print(dep)
699
+ elif output_format == "json":
700
+ print(json.dumps(result["data"], indent=2))
701
+ else: # text format
702
+ print("=" * 60)
703
+ print("DEPENDENCIES FROM DEPLOYED AGENTS")
704
+ print("=" * 60)
705
+ print()
706
+
707
+ if result["python_dependencies"]:
708
+ print(
709
+ f"Python Dependencies ({len(result['python_dependencies'])}):"
710
+ )
711
+ print("-" * 30)
712
+ for dep in result["python_dependencies"]:
713
+ print(f" {dep}")
714
+ print()
715
+
716
+ if result["system_dependencies"]:
717
+ print(
718
+ f"System Dependencies ({len(result['system_dependencies'])}):"
719
+ )
720
+ print("-" * 30)
721
+ for dep in result["system_dependencies"]:
722
+ print(f" {dep}")
723
+ print()
443
724
 
444
- return CommandResult.success_result(f"Listed {len(dependencies)} dependencies")
725
+ print("Per-Agent Dependencies:")
726
+ print("-" * 30)
727
+ for agent_id in sorted(result["per_agent"].keys()):
728
+ deps = result["per_agent"][agent_id]
729
+ python_count = len(deps.get("python", []))
730
+ system_count = len(deps.get("system", []))
731
+ if python_count or system_count:
732
+ print(
733
+ f" {agent_id}: {python_count} Python, {system_count} System"
734
+ )
735
+
736
+ return CommandResult.success_result(
737
+ "Dependency listing completed", data=result
738
+ )
445
739
 
446
740
  except Exception as e:
447
741
  self.logger.error(f"Error listing dependencies: {e}", exc_info=True)
@@ -450,104 +744,134 @@ class AgentsCommand(AgentCommand):
450
744
  def _fix_agent_dependencies(self, args) -> CommandResult:
451
745
  """Fix agent dependency issues."""
452
746
  try:
453
- result = self.deployment_service.fix_dependencies()
747
+ max_retries = getattr(args, "max_retries", 3)
748
+ agent_name = getattr(args, "agent", None)
454
749
 
455
- output_format = getattr(args, 'format', 'text')
456
- if output_format in ['json', 'yaml']:
457
- return CommandResult.success_result(
458
- "Dependency issues fixed",
459
- data=result
750
+ print("=" * 70)
751
+ print("FIXING AGENT DEPENDENCIES WITH RETRY LOGIC")
752
+ print("=" * 70)
753
+ print()
754
+
755
+ result = self.dependency_service.fix_dependencies(
756
+ max_retries=max_retries, agent_name=agent_name
757
+ )
758
+
759
+ if not result["success"]:
760
+ if "error" in result and "not deployed" in result["error"]:
761
+ print(f"❌ {result['error']}")
762
+ return CommandResult.error_result(result.get("error", "Fix failed"))
763
+
764
+ if result.get("message") == "No deployed agents found":
765
+ print("No deployed agents found")
766
+ return CommandResult.success_result("No agents to fix")
767
+
768
+ if result.get("message") == "All dependencies are already satisfied":
769
+ print("\n✅ All dependencies are already satisfied!")
770
+ return CommandResult.success_result("All dependencies satisfied")
771
+
772
+ # Show what's missing
773
+ if result.get("missing_python"):
774
+ print(f"\n❌ Missing Python packages: {len(result['missing_python'])}")
775
+ for pkg in result["missing_python"][:10]:
776
+ print(f" - {pkg}")
777
+ if len(result["missing_python"]) > 10:
778
+ print(f" ... and {len(result['missing_python']) - 10} more")
779
+
780
+ if result.get("missing_system"):
781
+ print(f"\n❌ Missing system commands: {len(result['missing_system'])}")
782
+ for cmd in result["missing_system"]:
783
+ print(f" - {cmd}")
784
+ print("\n⚠️ System dependencies must be installed manually:")
785
+ print(f" macOS: brew install {' '.join(result['missing_system'])}")
786
+ print(f" Ubuntu: apt-get install {' '.join(result['missing_system'])}")
787
+
788
+ # Show incompatible packages
789
+ if result.get("incompatible"):
790
+ print(
791
+ f"\n⚠️ Skipping {len(result['incompatible'])} incompatible packages:"
460
792
  )
461
- else:
462
- # Text output
463
- print(" Agent dependency issues fixed")
464
- if result.get('fixes_applied'):
465
- for fix in result['fixes_applied']:
466
- print(f" - {fix}")
793
+ for pkg in result["incompatible"][:5]:
794
+ print(f" - {pkg}")
795
+ if len(result["incompatible"]) > 5:
796
+ print(f" ... and {len(result['incompatible']) - 5} more")
797
+
798
+ # Show installation results
799
+ if result.get("fixed_python") or result.get("failed_python"):
800
+ print("\n" + "=" * 70)
801
+ print("INSTALLATION RESULTS:")
802
+ print("=" * 70)
803
+
804
+ if result.get("fixed_python"):
805
+ print(
806
+ f"✅ Successfully installed: {len(result['fixed_python'])} packages"
807
+ )
808
+
809
+ if result.get("failed_python"):
810
+ print(
811
+ f"❌ Failed to install: {len(result['failed_python'])} packages"
812
+ )
813
+ errors = result.get("errors", {})
814
+ for pkg in result["failed_python"]:
815
+ print(f" - {pkg}: {errors.get(pkg, 'Unknown error')}")
816
+
817
+ # Final verification
818
+ if result.get("still_missing") is not None:
819
+ if not result["still_missing"]:
820
+ print("\n✅ All Python dependencies are now satisfied!")
821
+ else:
822
+ print(
823
+ f"\n⚠️ Still missing {len(result['still_missing'])} packages"
824
+ )
825
+ print("\nTry running again or install manually:")
826
+ missing_sample = result["still_missing"][:3]
827
+ print(f" pip install {' '.join(missing_sample)}")
467
828
 
468
- return CommandResult.success_result("Dependency issues fixed")
829
+ print("\n" + "=" * 70)
830
+ print("DONE")
831
+ print("=" * 70)
832
+
833
+ return CommandResult.success_result("Dependency fix completed", data=result)
469
834
 
470
835
  except Exception as e:
471
836
  self.logger.error(f"Error fixing dependencies: {e}", exc_info=True)
472
837
  return CommandResult.error_result(f"Error fixing dependencies: {e}")
473
-
838
+
474
839
  def _cleanup_orphaned_agents(self, args) -> CommandResult:
475
840
  """Clean up orphaned agents that don't have templates."""
476
841
  try:
477
- from ...services.agents.deployment.multi_source_deployment_service import (
478
- MultiSourceAgentDeploymentService
479
- )
480
-
481
842
  # Determine agents directory
482
- if hasattr(args, 'agents_dir') and args.agents_dir:
843
+ agents_dir = None
844
+ if hasattr(args, "agents_dir") and args.agents_dir:
483
845
  agents_dir = args.agents_dir
484
- else:
485
- # Check for project-level .claude/agents first
486
- project_agents_dir = Path.cwd() / ".claude" / "agents"
487
- if project_agents_dir.exists():
488
- agents_dir = project_agents_dir
489
- else:
490
- # Fall back to user home directory
491
- agents_dir = Path.home() / ".claude" / "agents"
492
-
493
- if not agents_dir.exists():
494
- return CommandResult.success_result(f"Agents directory not found: {agents_dir}")
495
-
496
- # Initialize service
497
- service = MultiSourceAgentDeploymentService()
498
-
846
+
499
847
  # Determine if we're doing a dry run
500
- dry_run = getattr(args, 'dry_run', True)
501
- if hasattr(args, 'force') and args.force:
848
+ dry_run = getattr(args, "dry_run", True)
849
+ if hasattr(args, "force") and args.force:
502
850
  dry_run = False
503
-
504
- # Perform cleanup
505
- results = service.cleanup_orphaned_agents(agents_dir, dry_run=dry_run)
506
-
507
- output_format = getattr(args, 'format', 'text')
508
- quiet = getattr(args, 'quiet', False)
509
-
510
- if output_format in ['json', 'yaml']:
511
- return CommandResult.success_result(
512
- f"Found {len(results.get('orphaned', []))} orphaned agents",
513
- data=results
514
- )
515
- else:
516
- # Text output
517
- if not results.get("orphaned"):
518
- print("✅ No orphaned agents found")
519
- return CommandResult.success_result("No orphaned agents found")
520
-
521
- if not quiet:
522
- print(f"\nFound {len(results['orphaned'])} orphaned agent(s):")
523
- for orphan in results["orphaned"]:
524
- print(f" - {orphan['name']} v{orphan['version']}")
525
-
526
- if dry_run:
527
- print(
528
- f"\n📝 This was a dry run. Use --force to actually remove "
529
- f"{len(results['orphaned'])} orphaned agent(s)"
530
- )
531
- else:
532
- if results.get("removed"):
533
- print(
534
- f"\n✅ Successfully removed {len(results['removed'])} orphaned agent(s)"
535
- )
536
-
537
- if results.get("errors"):
538
- print(f"\n❌ Encountered {len(results['errors'])} error(s):")
539
- for error in results["errors"]:
540
- print(f" - {error}")
541
- return CommandResult.error_result(
542
- f"Cleanup completed with {len(results['errors'])} errors",
543
- data=results
544
- )
545
-
546
- return CommandResult.success_result(
547
- f"Cleanup {'preview' if dry_run else 'completed'}",
548
- data=results
851
+
852
+ # Perform cleanup using the cleanup service
853
+ results = self.cleanup_service.clean_orphaned_agents(
854
+ agents_dir=agents_dir, dry_run=dry_run
855
+ )
856
+
857
+ output_format = getattr(args, "format", "text")
858
+
859
+ formatted = self._formatter.format_cleanup_result(
860
+ results, output_format=output_format, dry_run=dry_run
861
+ )
862
+ print(formatted)
863
+
864
+ # Determine success/error based on results
865
+ if results.get("errors") and not dry_run:
866
+ return CommandResult.error_result(
867
+ f"Cleanup completed with {len(results['errors'])} errors",
868
+ data=results,
549
869
  )
550
-
870
+
871
+ return CommandResult.success_result(
872
+ f"Cleanup {'preview' if dry_run else 'completed'}", data=results
873
+ )
874
+
551
875
  except Exception as e:
552
876
  self.logger.error(f"Error during cleanup: {e}", exc_info=True)
553
877
  return CommandResult.error_result(f"Error during cleanup: {e}")
@@ -563,781 +887,7 @@ def manage_agents(args):
563
887
  result = command.execute(args)
564
888
 
565
889
  # Print result if structured output format is requested
566
- if hasattr(args, 'format') and args.format in ['json', 'yaml']:
890
+ if hasattr(args, "format") and args.format in ["json", "yaml"]:
567
891
  command.print_result(result, args)
568
892
 
569
893
  return result.exit_code
570
-
571
-
572
- def _list_agents(args, deployment_service):
573
- """Legacy function for backward compatibility."""
574
- command = AgentsCommand()
575
- command._deployment_service = deployment_service
576
- result = command._list_agents(args)
577
- return result.exit_code
578
-
579
-
580
- def _deploy_agents(args, deployment_service, force=False):
581
- """
582
- Deploy both system and project agents.
583
-
584
- WHY: Agents need to be deployed to the working directory for Claude Code to use them.
585
- This function handles both regular and forced deployment, including project-specific agents.
586
-
587
- Args:
588
- args: Command arguments with optional 'target' path
589
- deployment_service: Agent deployment service instance
590
- force: Whether to force rebuild all agents
591
- """
592
- # Load configuration to get exclusion settings using ConfigLoader
593
- config_loader = ConfigLoader()
594
- config = config_loader.load_main_config()
595
-
596
- # Check if user wants to override exclusions
597
- if hasattr(args, "include_all") and args.include_all:
598
- # Clear exclusion list if --include-all flag is set
599
- config.set("agent_deployment.excluded_agents", [])
600
- print("✅ Including all agents (exclusion configuration overridden)\n")
601
- else:
602
- excluded_agents = config.get("agent_deployment.excluded_agents", [])
603
-
604
- # Display exclusion information if agents are being excluded
605
- if excluded_agents:
606
- logger = get_logger("cli")
607
- logger.info(f"Configured agent exclusions: {excluded_agents}")
608
- print(
609
- f"\n⚠️ Excluding agents from deployment: {', '.join(excluded_agents)}"
610
- )
611
-
612
- # Warn if commonly used agents are being excluded
613
- common_agents = {"engineer", "qa", "security", "documentation"}
614
- excluded_common = set(a.lower() for a in excluded_agents) & common_agents
615
- if excluded_common:
616
- print(
617
- f"⚠️ Warning: Common agents are being excluded: {', '.join(excluded_common)}"
618
- )
619
- print(
620
- " This may affect normal operations. Use 'claude-mpm agents deploy --include-all' to override.\n"
621
- )
622
-
623
- # Deploy system agents first
624
- if force:
625
- print("Force deploying all system agents...")
626
- else:
627
- print("Deploying system agents...")
628
-
629
- results = deployment_service.deploy_agents(None, force_rebuild=force, config=config)
630
-
631
- # Also deploy project agents if they exist
632
- import os
633
- from pathlib import Path
634
-
635
- # Use the user's working directory if available
636
- if "CLAUDE_MPM_USER_PWD" in os.environ:
637
- project_dir = Path(os.environ["CLAUDE_MPM_USER_PWD"])
638
- else:
639
- project_dir = Path.cwd()
640
-
641
- project_agents_dir = project_dir / ".claude-mpm" / "agents"
642
- if project_agents_dir.exists():
643
- json_files = list(project_agents_dir.glob("*.json"))
644
- if json_files:
645
- print(f"\nDeploying {len(json_files)} project agents...")
646
- from claude_mpm.services.agents.deployment.agent_deployment import (
647
- AgentDeploymentService,
648
- )
649
-
650
- project_service = AgentDeploymentService(
651
- templates_dir=project_agents_dir,
652
- base_agent_path=(
653
- project_agents_dir / "base_agent.json"
654
- if (project_agents_dir / "base_agent.json").exists()
655
- else None
656
- ),
657
- working_directory=project_dir, # Pass the project directory
658
- )
659
- # Pass the same configuration to project agent deployment
660
- # For project agents, let the service determine they should stay in project directory
661
- project_results = project_service.deploy_agents(
662
- target_dir=None, # Let service detect it's a project deployment
663
- force_rebuild=force,
664
- deployment_mode="project",
665
- config=config,
666
- )
667
-
668
- # Merge project results into main results
669
- if project_results.get("deployed"):
670
- results["deployed"].extend(project_results["deployed"])
671
- print(f"✓ Deployed {len(project_results['deployed'])} project agents")
672
- if project_results.get("updated"):
673
- results["updated"].extend(project_results["updated"])
674
- print(f"✓ Updated {len(project_results['updated'])} project agents")
675
- if project_results.get("errors"):
676
- results["errors"].extend(project_results["errors"])
677
-
678
- if results["deployed"]:
679
- print(
680
- f"\n✓ Successfully deployed {len(results['deployed'])} agents to {results['target_dir']}"
681
- )
682
- for agent in results["deployed"]:
683
- print(f" - {agent['name']}")
684
-
685
- if force and results.get("updated", []):
686
- print(f"\n✓ Updated {len(results['updated'])} agents")
687
- for agent in results["updated"]:
688
- print(f" - {agent['name']}")
689
-
690
- if force and results.get("skipped", []):
691
- print(f"\n✓ Skipped {len(results['skipped'])} up-to-date agents")
692
-
693
- if results["errors"]:
694
- print("\n❌ Errors during deployment:")
695
- for error in results["errors"]:
696
- print(f" - {error}")
697
-
698
- if force:
699
- # Set environment for force deploy
700
- env_vars = deployment_service.set_claude_environment(
701
- args.target.parent if args.target else None
702
- )
703
- print(f"\n✓ Set Claude environment variables:")
704
- for key, value in env_vars.items():
705
- print(f" - {key}={value}")
706
-
707
-
708
- def _clean_agents(args, deployment_service):
709
- """
710
- Clean deployed system agents.
711
-
712
- WHY: Users may want to remove deployed agents to start fresh or clean up
713
- their working directory.
714
-
715
- Args:
716
- args: Command arguments with optional 'target' path
717
- deployment_service: Agent deployment service instance
718
- """
719
- print("Cleaning deployed system agents...")
720
- results = deployment_service.clean_deployment(args.target)
721
-
722
- if results["removed"]:
723
- print(f"\n✓ Removed {len(results['removed'])} agents")
724
- for path in results["removed"]:
725
- print(f" - {Path(path).name}")
726
- else:
727
- print("No system agents found to remove")
728
-
729
- if results["errors"]:
730
- print("\n❌ Errors during cleanup:")
731
- for error in results["errors"]:
732
- print(f" - {error}")
733
-
734
-
735
- def _list_agents_by_tier():
736
- """
737
- List agents grouped by precedence tier.
738
-
739
- WHY: Users need to understand which agents are active across different tiers
740
- and which version takes precedence when multiple versions exist.
741
- """
742
- try:
743
- adapter = AgentRegistryAdapter()
744
- if not adapter.registry:
745
- print("❌ Could not initialize agent registry")
746
- return
747
-
748
- # Get all agents and group by tier
749
- all_agents = adapter.registry.list_agents()
750
-
751
- # Group agents by tier and name
752
- tiers = {"project": {}, "user": {}, "system": {}}
753
- agent_names = set()
754
-
755
- for agent_id, metadata in all_agents.items():
756
- tier = metadata.get("tier", "system")
757
- if tier in tiers:
758
- tiers[tier][agent_id] = metadata
759
- agent_names.add(agent_id)
760
-
761
- # Display header
762
- print("\n" + "=" * 80)
763
- print(" " * 25 + "AGENT HIERARCHY BY TIER")
764
- print("=" * 80)
765
- print("\nPrecedence: PROJECT > USER > SYSTEM")
766
- print("(Agents in higher tiers override those in lower tiers)\n")
767
-
768
- # Display each tier
769
- tier_order = [("PROJECT", "project"), ("USER", "user"), ("SYSTEM", "system")]
770
-
771
- for tier_display, tier_key in tier_order:
772
- agents = tiers[tier_key]
773
- print(f"\n{'─' * 35} {tier_display} TIER {'─' * 35}")
774
-
775
- if not agents:
776
- print(f" No agents at {tier_key} level")
777
- else:
778
- # Check paths to determine actual locations
779
- if tier_key == "project":
780
- print(f" Location: .claude-mpm/agents/ (in current project)")
781
- elif tier_key == "user":
782
- print(f" Location: ~/.claude-mpm/agents/")
783
- else:
784
- print(f" Location: Built-in framework agents")
785
-
786
- print(f"\n Found {len(agents)} agent(s):\n")
787
-
788
- for agent_id, metadata in sorted(agents.items()):
789
- # Check if this agent is overridden by higher tiers
790
- is_active = True
791
- overridden_by = []
792
-
793
- for check_tier_display, check_tier_key in tier_order:
794
- if check_tier_key == tier_key:
795
- break
796
- if agent_id in tiers[check_tier_key]:
797
- is_active = False
798
- overridden_by.append(check_tier_display)
799
-
800
- # Display agent info
801
- status = (
802
- "✓ ACTIVE"
803
- if is_active
804
- else f"⊗ OVERRIDDEN by {', '.join(overridden_by)}"
805
- )
806
- print(f" 📄 {agent_id:<20} [{status}]")
807
-
808
- # Show metadata
809
- if "description" in metadata:
810
- print(f" Description: {metadata['description']}")
811
- if "path" in metadata:
812
- path = Path(metadata["path"])
813
- print(f" File: {path.name}")
814
- print()
815
-
816
- # Summary
817
- print("\n" + "=" * 80)
818
- print("SUMMARY:")
819
- print(f" Total unique agents: {len(agent_names)}")
820
- print(f" Project agents: {len(tiers['project'])}")
821
- print(f" User agents: {len(tiers['user'])}")
822
- print(f" System agents: {len(tiers['system'])}")
823
- print("=" * 80 + "\n")
824
-
825
- except Exception as e:
826
- print(f"❌ Error listing agents by tier: {e}")
827
-
828
-
829
- def _view_agent(args):
830
- """
831
- View detailed information about a specific agent.
832
-
833
- WHY: Users need to inspect agent configurations, frontmatter, and instructions
834
- to understand what an agent does and how it's configured.
835
-
836
- Args:
837
- args: Command arguments with 'agent_name' attribute
838
- """
839
- if not hasattr(args, "agent_name") or not args.agent_name:
840
- print("❌ Please specify an agent name to view")
841
- print("Usage: claude-mpm agents view <agent_name>")
842
- return
843
-
844
- try:
845
- adapter = AgentRegistryAdapter()
846
- if not adapter.registry:
847
- print("❌ Could not initialize agent registry")
848
- return
849
-
850
- # Get the agent
851
- agent = adapter.registry.get_agent(args.agent_name)
852
- if not agent:
853
- print(f"❌ Agent '{args.agent_name}' not found")
854
- print("\nAvailable agents:")
855
- all_agents = adapter.registry.list_agents()
856
- for agent_id in sorted(all_agents.keys()):
857
- print(f" - {agent_id}")
858
- return
859
-
860
- # Read the agent file
861
- agent_path = Path(agent.path)
862
- if not agent_path.exists():
863
- print(f"❌ Agent file not found: {agent_path}")
864
- return
865
-
866
- with open(agent_path, "r") as f:
867
- content = f.read()
868
-
869
- # Display agent information
870
- print("\n" + "=" * 80)
871
- print(f" AGENT: {agent.name}")
872
- print("=" * 80)
873
-
874
- # Basic info
875
- print(f"\n📋 BASIC INFORMATION:")
876
- print(f" Name: {agent.name}")
877
- print(f" Type: {agent.type}")
878
- print(f" Tier: {agent.tier.upper()}")
879
- print(f" Path: {agent_path}")
880
- if agent.description:
881
- print(f" Description: {agent.description}")
882
- if agent.specializations:
883
- print(f" Specializations: {', '.join(agent.specializations)}")
884
-
885
- # Extract and display frontmatter
886
- if content.startswith("---"):
887
- try:
888
- end_marker = content.find("\n---\n", 4)
889
- if end_marker == -1:
890
- end_marker = content.find("\n---\r\n", 4)
891
-
892
- if end_marker != -1:
893
- frontmatter_str = content[4:end_marker]
894
- frontmatter = yaml.safe_load(frontmatter_str)
895
-
896
- print(f"\n📝 FRONTMATTER:")
897
- for key, value in frontmatter.items():
898
- if isinstance(value, list):
899
- print(f" {key}: [{', '.join(str(v) for v in value)}]")
900
- elif isinstance(value, dict):
901
- print(f" {key}:")
902
- for k, v in value.items():
903
- print(f" {k}: {v}")
904
- else:
905
- print(f" {key}: {value}")
906
-
907
- # Extract instructions preview
908
- instructions_start = end_marker + 5
909
- instructions = content[instructions_start:].strip()
910
-
911
- if instructions:
912
- print(f"\n📖 INSTRUCTIONS PREVIEW (first 500 chars):")
913
- print(" " + "-" * 76)
914
- preview = instructions[:500]
915
- if len(instructions) > 500:
916
- preview += "...\n\n [Truncated - {:.1f}KB total]".format(
917
- len(instructions) / 1024
918
- )
919
-
920
- for line in preview.split("\n"):
921
- print(f" {line}")
922
- print(" " + "-" * 76)
923
- except Exception as e:
924
- print(f"\n⚠️ Could not parse frontmatter: {e}")
925
- else:
926
- print(f"\n⚠️ No frontmatter found in agent file")
927
-
928
- # File stats
929
- import os
930
-
931
- stat = os.stat(agent_path)
932
- from datetime import datetime
933
-
934
- modified = datetime.fromtimestamp(stat.st_mtime).strftime("%Y-%m-%d %H:%M:%S")
935
- print(f"\n📊 FILE STATS:")
936
- print(f" Size: {stat.st_size:,} bytes")
937
- print(f" Last modified: {modified}")
938
-
939
- print("\n" + "=" * 80 + "\n")
940
-
941
- except Exception as e:
942
- print(f"❌ Error viewing agent: {e}")
943
-
944
-
945
- def _fix_agents(args):
946
- """
947
- Fix agent frontmatter issues using FrontmatterValidator.
948
-
949
- WHY: Agent files may have formatting issues in their frontmatter that prevent
950
- proper loading. This command automatically fixes common issues.
951
-
952
- Args:
953
- args: Command arguments with 'agent_name', 'dry_run', and 'all' flags
954
- """
955
- validator = FrontmatterValidator()
956
-
957
- try:
958
- adapter = AgentRegistryAdapter()
959
- if not adapter.registry:
960
- print("❌ Could not initialize agent registry")
961
- return
962
-
963
- # Determine which agents to fix
964
- agents_to_fix = []
965
-
966
- if hasattr(args, "all") and args.all:
967
- # Fix all agents
968
- all_agents = adapter.registry.list_agents()
969
- for agent_id, metadata in all_agents.items():
970
- agents_to_fix.append((agent_id, metadata["path"]))
971
- print(
972
- f"\n🔧 Checking {len(agents_to_fix)} agent(s) for frontmatter issues...\n"
973
- )
974
- elif hasattr(args, "agent_name") and args.agent_name:
975
- # Fix specific agent
976
- agent = adapter.registry.get_agent(args.agent_name)
977
- if not agent:
978
- print(f"❌ Agent '{args.agent_name}' not found")
979
- return
980
- agents_to_fix.append((agent.name, agent.path))
981
- print(f"\n🔧 Checking agent '{agent.name}' for frontmatter issues...\n")
982
- else:
983
- print("❌ Please specify an agent name or use --all to fix all agents")
984
- print("Usage: claude-mpm agents fix [agent_name] [--dry-run] [--all]")
985
- return
986
-
987
- dry_run = hasattr(args, "dry_run") and args.dry_run
988
- if dry_run:
989
- print("🔍 DRY RUN MODE - No changes will be made\n")
990
-
991
- # Process each agent
992
- total_issues = 0
993
- total_fixed = 0
994
-
995
- for agent_name, agent_path in agents_to_fix:
996
- path = Path(agent_path)
997
- if not path.exists():
998
- print(f"⚠️ Skipping {agent_name}: File not found at {path}")
999
- continue
1000
-
1001
- print(f"📄 {agent_name}:")
1002
-
1003
- # Validate and potentially fix
1004
- result = validator.correct_file(path, dry_run=dry_run)
1005
-
1006
- if result.is_valid and not result.corrections:
1007
- print(" ✓ No issues found")
1008
- else:
1009
- if result.errors:
1010
- print(" ❌ Errors:")
1011
- for error in result.errors:
1012
- print(f" - {error}")
1013
- total_issues += len(result.errors)
1014
-
1015
- if result.warnings:
1016
- print(" ⚠️ Warnings:")
1017
- for warning in result.warnings:
1018
- print(f" - {warning}")
1019
- total_issues += len(result.warnings)
1020
-
1021
- if result.corrections:
1022
- if dry_run:
1023
- print(" 🔧 Would fix:")
1024
- else:
1025
- print(" ✓ Fixed:")
1026
- total_fixed += len(result.corrections)
1027
- for correction in result.corrections:
1028
- print(f" - {correction}")
1029
-
1030
- print()
1031
-
1032
- # Summary
1033
- print("=" * 80)
1034
- print("SUMMARY:")
1035
- print(f" Agents checked: {len(agents_to_fix)}")
1036
- print(f" Total issues found: {total_issues}")
1037
- if dry_run:
1038
- print(
1039
- f" Issues that would be fixed: {sum(1 for _, path in agents_to_fix if validator.validate_file(Path(path)).corrections)}"
1040
- )
1041
- print("\n💡 Run without --dry-run to apply fixes")
1042
- else:
1043
- print(f" Issues fixed: {total_fixed}")
1044
- if total_fixed > 0:
1045
- print("\n✓ Frontmatter issues have been fixed!")
1046
- print("=" * 80 + "\n")
1047
-
1048
- except Exception as e:
1049
- print(f"❌ Error fixing agents: {e}")
1050
-
1051
-
1052
- def _check_agent_dependencies(args):
1053
- """
1054
- Check dependencies for deployed agents.
1055
-
1056
- Args:
1057
- args: Parsed command line arguments
1058
- """
1059
- from ...utils.agent_dependency_loader import AgentDependencyLoader
1060
-
1061
- verbose = getattr(args, "verbose", False)
1062
- specific_agent = getattr(args, "agent", None)
1063
-
1064
- loader = AgentDependencyLoader(auto_install=False)
1065
-
1066
- # Discover deployed agents
1067
- loader.discover_deployed_agents()
1068
-
1069
- # Filter to specific agent if requested
1070
- if specific_agent:
1071
- if specific_agent not in loader.deployed_agents:
1072
- print(f"❌ Agent '{specific_agent}' is not deployed")
1073
- print(f" Available agents: {', '.join(loader.deployed_agents.keys())}")
1074
- return
1075
- # Keep only the specified agent
1076
- loader.deployed_agents = {
1077
- specific_agent: loader.deployed_agents[specific_agent]
1078
- }
1079
-
1080
- # Load dependencies and check
1081
- loader.load_agent_dependencies()
1082
- results = loader.analyze_dependencies()
1083
-
1084
- # Print report
1085
- report = loader.format_report(results)
1086
- print(report)
1087
-
1088
-
1089
- def _install_agent_dependencies(args):
1090
- """
1091
- Install missing dependencies for deployed agents.
1092
-
1093
- Args:
1094
- args: Parsed command line arguments
1095
- """
1096
- import sys
1097
-
1098
- from ...utils.agent_dependency_loader import AgentDependencyLoader
1099
-
1100
- specific_agent = getattr(args, "agent", None)
1101
- dry_run = getattr(args, "dry_run", False)
1102
-
1103
- loader = AgentDependencyLoader(auto_install=not dry_run)
1104
-
1105
- # Discover deployed agents
1106
- loader.discover_deployed_agents()
1107
-
1108
- # Filter to specific agent if requested
1109
- if specific_agent:
1110
- if specific_agent not in loader.deployed_agents:
1111
- print(f"❌ Agent '{specific_agent}' is not deployed")
1112
- print(f" Available agents: {', '.join(loader.deployed_agents.keys())}")
1113
- return
1114
- loader.deployed_agents = {
1115
- specific_agent: loader.deployed_agents[specific_agent]
1116
- }
1117
-
1118
- # Load dependencies
1119
- loader.load_agent_dependencies()
1120
- results = loader.analyze_dependencies()
1121
-
1122
- missing_deps = results["summary"]["missing_python"]
1123
-
1124
- if not missing_deps:
1125
- print("✅ All Python dependencies are already installed")
1126
- return
1127
-
1128
- print(f"Found {len(missing_deps)} missing dependencies:")
1129
- for dep in missing_deps:
1130
- print(f" - {dep}")
1131
-
1132
- if dry_run:
1133
- print("\n--dry-run specified, not installing anything")
1134
- print(f"Would install: pip install {' '.join(missing_deps)}")
1135
- else:
1136
- print(f"\nInstalling {len(missing_deps)} dependencies...")
1137
- success, error = loader.install_missing_dependencies(missing_deps)
1138
-
1139
- if success:
1140
- print("✅ Successfully installed all dependencies")
1141
-
1142
- # Re-check after installation
1143
- loader.checked_packages.clear()
1144
- results = loader.analyze_dependencies()
1145
-
1146
- if results["summary"]["missing_python"]:
1147
- print(
1148
- f"⚠️ {len(results['summary']['missing_python'])} dependencies still missing after installation"
1149
- )
1150
- else:
1151
- print("✅ All dependencies verified after installation")
1152
- else:
1153
- print(f"❌ Failed to install dependencies: {error}")
1154
-
1155
-
1156
- def _list_agent_dependencies(args):
1157
- """
1158
- List all dependencies from deployed agents.
1159
-
1160
- Args:
1161
- args: Parsed command line arguments
1162
- """
1163
- import json
1164
-
1165
- from ...utils.agent_dependency_loader import AgentDependencyLoader
1166
-
1167
- output_format = getattr(args, "format", "text")
1168
-
1169
- loader = AgentDependencyLoader(auto_install=False)
1170
-
1171
- # Discover and load
1172
- loader.discover_deployed_agents()
1173
- loader.load_agent_dependencies()
1174
-
1175
- # Collect all unique dependencies
1176
- all_python_deps = set()
1177
- all_system_deps = set()
1178
-
1179
- for agent_id, deps in loader.agent_dependencies.items():
1180
- if "python" in deps:
1181
- all_python_deps.update(deps["python"])
1182
- if "system" in deps:
1183
- all_system_deps.update(deps["system"])
1184
-
1185
- # Format output based on requested format
1186
- if output_format == "pip":
1187
- # Output pip-installable format
1188
- for dep in sorted(all_python_deps):
1189
- print(dep)
1190
-
1191
- elif output_format == "json":
1192
- # Output JSON format
1193
- output = {
1194
- "python": sorted(list(all_python_deps)),
1195
- "system": sorted(list(all_system_deps)),
1196
- "agents": {},
1197
- }
1198
- for agent_id, deps in loader.agent_dependencies.items():
1199
- output["agents"][agent_id] = deps
1200
- print(json.dumps(output, indent=2))
1201
-
1202
- else: # text format
1203
- print("=" * 60)
1204
- print("DEPENDENCIES FROM DEPLOYED AGENTS")
1205
- print("=" * 60)
1206
- print()
1207
-
1208
- if all_python_deps:
1209
- print(f"Python Dependencies ({len(all_python_deps)}):")
1210
- print("-" * 30)
1211
- for dep in sorted(all_python_deps):
1212
- print(f" {dep}")
1213
- print()
1214
-
1215
- if all_system_deps:
1216
- print(f"System Dependencies ({len(all_system_deps)}):")
1217
- print("-" * 30)
1218
- for dep in sorted(all_system_deps):
1219
- print(f" {dep}")
1220
- print()
1221
-
1222
- print("Per-Agent Dependencies:")
1223
- print("-" * 30)
1224
- for agent_id in sorted(loader.agent_dependencies.keys()):
1225
- deps = loader.agent_dependencies[agent_id]
1226
- python_count = len(deps.get("python", []))
1227
- system_count = len(deps.get("system", []))
1228
- if python_count or system_count:
1229
- print(f" {agent_id}: {python_count} Python, {system_count} System")
1230
-
1231
-
1232
- def _fix_agent_dependencies(args):
1233
- """
1234
- Fix missing agent dependencies with robust retry logic.
1235
-
1236
- WHY: Network issues and temporary package unavailability can cause
1237
- dependency installation to fail. This command uses robust retry logic
1238
- to maximize success rate.
1239
-
1240
- Args:
1241
- args: Parsed command line arguments
1242
- """
1243
- from ...utils.agent_dependency_loader import AgentDependencyLoader
1244
- from ...utils.robust_installer import RobustPackageInstaller
1245
-
1246
- max_retries = getattr(args, "max_retries", 3)
1247
-
1248
- print("=" * 70)
1249
- print("FIXING AGENT DEPENDENCIES WITH RETRY LOGIC")
1250
- print("=" * 70)
1251
- print()
1252
-
1253
- loader = AgentDependencyLoader(auto_install=False)
1254
-
1255
- # Discover and analyze
1256
- print("Discovering deployed agents...")
1257
- loader.discover_deployed_agents()
1258
-
1259
- if not loader.deployed_agents:
1260
- print("No deployed agents found")
1261
- return
1262
-
1263
- print(f"Found {len(loader.deployed_agents)} deployed agents")
1264
- print("Analyzing dependencies...")
1265
-
1266
- loader.load_agent_dependencies()
1267
- results = loader.analyze_dependencies()
1268
-
1269
- missing_python = results["summary"]["missing_python"]
1270
- missing_system = results["summary"]["missing_system"]
1271
-
1272
- if not missing_python and not missing_system:
1273
- print("\n✅ All dependencies are already satisfied!")
1274
- return
1275
-
1276
- # Show what's missing
1277
- if missing_python:
1278
- print(f"\n❌ Missing Python packages: {len(missing_python)}")
1279
- for pkg in missing_python[:10]:
1280
- print(f" - {pkg}")
1281
- if len(missing_python) > 10:
1282
- print(f" ... and {len(missing_python) - 10} more")
1283
-
1284
- if missing_system:
1285
- print(f"\n❌ Missing system commands: {len(missing_system)}")
1286
- for cmd in missing_system:
1287
- print(f" - {cmd}")
1288
- print("\n⚠️ System dependencies must be installed manually:")
1289
- print(f" macOS: brew install {' '.join(missing_system)}")
1290
- print(f" Ubuntu: apt-get install {' '.join(missing_system)}")
1291
-
1292
- # Fix Python dependencies with robust installer
1293
- if missing_python:
1294
- print(
1295
- f"\n🔧 Fixing Python dependencies with {max_retries} retries per package..."
1296
- )
1297
-
1298
- # Check compatibility
1299
- compatible, incompatible = loader.check_python_compatibility(missing_python)
1300
-
1301
- if incompatible:
1302
- print(f"\n⚠️ Skipping {len(incompatible)} incompatible packages:")
1303
- for pkg in incompatible[:5]:
1304
- print(f" - {pkg}")
1305
- if len(incompatible) > 5:
1306
- print(f" ... and {len(incompatible) - 5} more")
1307
-
1308
- if compatible:
1309
- installer = RobustPackageInstaller(
1310
- max_retries=max_retries, retry_delay=2.0, timeout=300
1311
- )
1312
-
1313
- print(f"\nInstalling {len(compatible)} compatible packages...")
1314
- successful, failed, errors = installer.install_packages(compatible)
1315
-
1316
- print("\n" + "=" * 70)
1317
- print("INSTALLATION RESULTS:")
1318
- print("=" * 70)
1319
-
1320
- if successful:
1321
- print(f"✅ Successfully installed: {len(successful)} packages")
1322
-
1323
- if failed:
1324
- print(f"❌ Failed to install: {len(failed)} packages")
1325
- for pkg in failed:
1326
- print(f" - {pkg}: {errors.get(pkg, 'Unknown error')}")
1327
-
1328
- # Re-check
1329
- print("\nVerifying installation...")
1330
- loader.checked_packages.clear()
1331
- final_results = loader.analyze_dependencies()
1332
-
1333
- final_missing = final_results["summary"]["missing_python"]
1334
- if not final_missing:
1335
- print("✅ All Python dependencies are now satisfied!")
1336
- else:
1337
- print(f"⚠️ Still missing {len(final_missing)} packages")
1338
- print("\nTry running again or install manually:")
1339
- print(f" pip install {' '.join(final_missing[:3])}")
1340
-
1341
- print("\n" + "=" * 70)
1342
- print("DONE")
1343
- print("=" * 70)