claude-mpm 3.4.10__py3-none-any.whl → 5.4.55__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 (950) hide show
  1. claude_mpm/BUILD_NUMBER +1 -0
  2. claude_mpm/VERSION +1 -0
  3. claude_mpm/__init__.py +50 -12
  4. claude_mpm/__main__.py +7 -2
  5. claude_mpm/agents/BASE_AGENT.md +164 -0
  6. claude_mpm/agents/BASE_ENGINEER.md +658 -0
  7. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +290 -0
  8. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
  9. claude_mpm/agents/MEMORY.md +72 -0
  10. claude_mpm/agents/PM_INSTRUCTIONS.md +1402 -0
  11. claude_mpm/agents/WORKFLOW.md +111 -0
  12. claude_mpm/agents/__init__.py +92 -80
  13. claude_mpm/agents/agent-template.yaml +83 -0
  14. claude_mpm/agents/agent_loader.py +560 -745
  15. claude_mpm/agents/agent_loader_integration.py +53 -55
  16. claude_mpm/agents/agents_metadata.py +186 -27
  17. claude_mpm/agents/async_agent_loader.py +436 -0
  18. claude_mpm/agents/base_agent.json +8 -4
  19. claude_mpm/agents/frontmatter_validator.py +754 -0
  20. claude_mpm/agents/system_agent_config.py +222 -155
  21. claude_mpm/agents/templates/README.md +465 -0
  22. claude_mpm/agents/templates/__init__.py +17 -13
  23. claude_mpm/agents/templates/circuit-breakers.md +1391 -0
  24. claude_mpm/agents/templates/context-management-examples.md +544 -0
  25. claude_mpm/agents/templates/git-file-tracking.md +584 -0
  26. claude_mpm/agents/templates/pm-examples.md +474 -0
  27. claude_mpm/agents/templates/pm-red-flags.md +310 -0
  28. claude_mpm/agents/templates/pr-workflow-examples.md +427 -0
  29. claude_mpm/agents/templates/research-gate-examples.md +669 -0
  30. claude_mpm/agents/templates/response-format.md +583 -0
  31. claude_mpm/agents/templates/structured-questions-examples.md +615 -0
  32. claude_mpm/agents/templates/ticket-completeness-examples.md +139 -0
  33. claude_mpm/agents/templates/ticketing-examples.md +277 -0
  34. claude_mpm/agents/templates/validation-templates.md +312 -0
  35. claude_mpm/cli/__init__.py +90 -128
  36. claude_mpm/cli/__main__.py +33 -0
  37. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  38. claude_mpm/cli/commands/__init__.py +36 -12
  39. claude_mpm/cli/commands/agent_manager.py +1403 -0
  40. claude_mpm/cli/commands/agent_source.py +774 -0
  41. claude_mpm/cli/commands/agent_state_manager.py +335 -0
  42. claude_mpm/cli/commands/agents.py +2503 -168
  43. claude_mpm/cli/commands/agents_cleanup.py +210 -0
  44. claude_mpm/cli/commands/agents_discover.py +338 -0
  45. claude_mpm/cli/commands/aggregate.py +540 -0
  46. claude_mpm/cli/commands/analyze.py +553 -0
  47. claude_mpm/cli/commands/analyze_code.py +528 -0
  48. claude_mpm/cli/commands/auto_configure.py +1053 -0
  49. claude_mpm/cli/commands/cleanup.py +588 -0
  50. claude_mpm/cli/commands/cleanup_orphaned_agents.py +150 -0
  51. claude_mpm/cli/commands/config.py +586 -0
  52. claude_mpm/cli/commands/configure.py +2654 -0
  53. claude_mpm/cli/commands/configure_agent_display.py +282 -0
  54. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  55. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  56. claude_mpm/cli/commands/configure_models.py +18 -0
  57. claude_mpm/cli/commands/configure_navigation.py +184 -0
  58. claude_mpm/cli/commands/configure_paths.py +104 -0
  59. claude_mpm/cli/commands/configure_persistence.py +254 -0
  60. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  61. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  62. claude_mpm/cli/commands/configure_validators.py +73 -0
  63. claude_mpm/cli/commands/dashboard.py +286 -0
  64. claude_mpm/cli/commands/debug.py +1386 -0
  65. claude_mpm/cli/commands/doctor.py +243 -0
  66. claude_mpm/cli/commands/hook_errors.py +277 -0
  67. claude_mpm/cli/commands/info.py +195 -74
  68. claude_mpm/cli/commands/local_deploy.py +534 -0
  69. claude_mpm/cli/commands/mcp.py +205 -0
  70. claude_mpm/cli/commands/mcp_command_router.py +161 -0
  71. claude_mpm/cli/commands/mcp_config.py +154 -0
  72. claude_mpm/cli/commands/mcp_config_commands.py +20 -0
  73. claude_mpm/cli/commands/mcp_external_commands.py +249 -0
  74. claude_mpm/cli/commands/mcp_install_commands.py +346 -0
  75. claude_mpm/cli/commands/mcp_pipx_config.py +208 -0
  76. claude_mpm/cli/commands/mcp_server_commands.py +155 -0
  77. claude_mpm/cli/commands/mcp_setup_external.py +868 -0
  78. claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
  79. claude_mpm/cli/commands/memory.py +585 -846
  80. claude_mpm/cli/commands/monitor.py +228 -310
  81. claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
  82. claude_mpm/cli/commands/mpm_init/core.py +759 -0
  83. claude_mpm/cli/commands/mpm_init/display.py +341 -0
  84. claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
  85. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  86. claude_mpm/cli/commands/mpm_init/modes.py +397 -0
  87. claude_mpm/cli/commands/mpm_init/prompts.py +722 -0
  88. claude_mpm/cli/commands/mpm_init_cli.py +396 -0
  89. claude_mpm/cli/commands/mpm_init_handler.py +195 -0
  90. claude_mpm/cli/commands/postmortem.py +401 -0
  91. claude_mpm/cli/commands/profile.py +276 -0
  92. claude_mpm/cli/commands/run.py +910 -488
  93. claude_mpm/cli/commands/search.py +458 -0
  94. claude_mpm/cli/commands/skill_source.py +694 -0
  95. claude_mpm/cli/commands/skills.py +1246 -0
  96. claude_mpm/cli/commands/summarize.py +413 -0
  97. claude_mpm/cli/commands/tickets.py +536 -53
  98. claude_mpm/cli/commands/uninstall.py +176 -0
  99. claude_mpm/cli/commands/upgrade.py +152 -0
  100. claude_mpm/cli/commands/verify.py +119 -0
  101. claude_mpm/cli/executor.py +297 -0
  102. claude_mpm/cli/helpers.py +105 -0
  103. claude_mpm/cli/interactive/__init__.py +21 -0
  104. claude_mpm/cli/interactive/agent_wizard.py +1947 -0
  105. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  106. claude_mpm/cli/parser.py +87 -563
  107. claude_mpm/cli/parsers/__init__.py +35 -0
  108. claude_mpm/cli/parsers/agent_manager_parser.py +393 -0
  109. claude_mpm/cli/parsers/agent_source_parser.py +171 -0
  110. claude_mpm/cli/parsers/agents_parser.py +575 -0
  111. claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
  112. claude_mpm/cli/parsers/analyze_parser.py +135 -0
  113. claude_mpm/cli/parsers/auto_configure_parser.py +120 -0
  114. claude_mpm/cli/parsers/base_parser.py +644 -0
  115. claude_mpm/cli/parsers/config_parser.py +208 -0
  116. claude_mpm/cli/parsers/configure_parser.py +138 -0
  117. claude_mpm/cli/parsers/dashboard_parser.py +113 -0
  118. claude_mpm/cli/parsers/debug_parser.py +319 -0
  119. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  120. claude_mpm/cli/parsers/mcp_parser.py +195 -0
  121. claude_mpm/cli/parsers/memory_parser.py +138 -0
  122. claude_mpm/cli/parsers/monitor_parser.py +142 -0
  123. claude_mpm/cli/parsers/mpm_init_parser.py +311 -0
  124. claude_mpm/cli/parsers/profile_parser.py +147 -0
  125. claude_mpm/cli/parsers/run_parser.py +157 -0
  126. claude_mpm/cli/parsers/search_parser.py +245 -0
  127. claude_mpm/cli/parsers/skill_source_parser.py +169 -0
  128. claude_mpm/cli/parsers/skills_parser.py +277 -0
  129. claude_mpm/cli/parsers/source_parser.py +138 -0
  130. claude_mpm/cli/parsers/tickets_parser.py +203 -0
  131. claude_mpm/cli/shared/__init__.py +40 -0
  132. claude_mpm/cli/shared/argument_patterns.py +205 -0
  133. claude_mpm/cli/shared/base_command.py +242 -0
  134. claude_mpm/cli/shared/error_handling.py +242 -0
  135. claude_mpm/cli/shared/output_formatters.py +241 -0
  136. claude_mpm/cli/startup.py +1743 -0
  137. claude_mpm/cli/startup_display.py +480 -0
  138. claude_mpm/cli/startup_logging.py +839 -0
  139. claude_mpm/cli/utils.py +136 -47
  140. claude_mpm/cli_module/__init__.py +6 -6
  141. claude_mpm/cli_module/args.py +188 -140
  142. claude_mpm/cli_module/commands.py +79 -70
  143. claude_mpm/cli_module/migration_example.py +42 -64
  144. claude_mpm/commands/__init__.py +14 -0
  145. claude_mpm/commands/mpm-config.md +28 -0
  146. claude_mpm/commands/mpm-doctor.md +20 -0
  147. claude_mpm/commands/mpm-help.md +20 -0
  148. claude_mpm/commands/mpm-init.md +120 -0
  149. claude_mpm/commands/mpm-monitor.md +31 -0
  150. claude_mpm/commands/mpm-organize.md +120 -0
  151. claude_mpm/commands/mpm-postmortem.md +21 -0
  152. claude_mpm/commands/mpm-session-resume.md +30 -0
  153. claude_mpm/commands/mpm-status.md +20 -0
  154. claude_mpm/commands/mpm-ticket-view.md +109 -0
  155. claude_mpm/commands/mpm-version.md +20 -0
  156. claude_mpm/commands/mpm.md +31 -0
  157. claude_mpm/config/__init__.py +42 -2
  158. claude_mpm/config/agent_config.py +402 -0
  159. claude_mpm/config/agent_presets.py +488 -0
  160. claude_mpm/config/agent_sources.py +352 -0
  161. claude_mpm/config/experimental_features.py +217 -0
  162. claude_mpm/config/model_config.py +428 -0
  163. claude_mpm/config/paths.py +258 -0
  164. claude_mpm/config/skill_presets.py +392 -0
  165. claude_mpm/config/skill_sources.py +590 -0
  166. claude_mpm/config/socketio_config.py +125 -83
  167. claude_mpm/constants.py +132 -22
  168. claude_mpm/core/__init__.py +62 -36
  169. claude_mpm/core/agent_name_normalizer.py +71 -73
  170. claude_mpm/core/agent_registry.py +385 -492
  171. claude_mpm/core/agent_session_manager.py +81 -70
  172. claude_mpm/core/api_validator.py +330 -0
  173. claude_mpm/core/base_service.py +159 -122
  174. claude_mpm/core/cache.py +560 -0
  175. claude_mpm/core/claude_runner.py +696 -916
  176. claude_mpm/core/config.py +613 -122
  177. claude_mpm/core/config_aliases.py +74 -73
  178. claude_mpm/core/config_constants.py +314 -0
  179. claude_mpm/core/constants.py +361 -0
  180. claude_mpm/core/container.py +646 -104
  181. claude_mpm/core/enums.py +452 -0
  182. claude_mpm/core/error_handler.py +623 -0
  183. claude_mpm/core/exceptions.py +536 -0
  184. claude_mpm/core/factories.py +105 -109
  185. claude_mpm/core/file_utils.py +764 -0
  186. claude_mpm/core/framework/__init__.py +25 -0
  187. claude_mpm/core/framework/formatters/__init__.py +11 -0
  188. claude_mpm/core/framework/formatters/capability_generator.py +367 -0
  189. claude_mpm/core/framework/formatters/content_formatter.py +278 -0
  190. claude_mpm/core/framework/formatters/context_generator.py +185 -0
  191. claude_mpm/core/framework/loaders/__init__.py +13 -0
  192. claude_mpm/core/framework/loaders/agent_loader.py +213 -0
  193. claude_mpm/core/framework/loaders/file_loader.py +176 -0
  194. claude_mpm/core/framework/loaders/instruction_loader.py +222 -0
  195. claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
  196. claude_mpm/core/framework/processors/__init__.py +11 -0
  197. claude_mpm/core/framework/processors/memory_processor.py +230 -0
  198. claude_mpm/core/framework/processors/metadata_processor.py +146 -0
  199. claude_mpm/core/framework/processors/template_processor.py +244 -0
  200. claude_mpm/core/framework_loader.py +485 -414
  201. claude_mpm/core/hook_error_memory.py +381 -0
  202. claude_mpm/core/hook_manager.py +246 -86
  203. claude_mpm/core/hook_performance_config.py +147 -0
  204. claude_mpm/core/injectable_service.py +72 -63
  205. claude_mpm/core/instruction_reinforcement_hook.py +267 -0
  206. claude_mpm/core/interactive_session.py +670 -0
  207. claude_mpm/core/interfaces.py +570 -164
  208. claude_mpm/core/lazy.py +467 -0
  209. claude_mpm/core/log_manager.py +707 -0
  210. claude_mpm/core/logger.py +295 -134
  211. claude_mpm/core/logging_config.py +474 -0
  212. claude_mpm/core/logging_utils.py +520 -0
  213. claude_mpm/core/minimal_framework_loader.py +24 -22
  214. claude_mpm/core/mixins.py +30 -29
  215. claude_mpm/core/oneshot_session.py +594 -0
  216. claude_mpm/core/optimized_agent_loader.py +479 -0
  217. claude_mpm/core/optimized_startup.py +554 -0
  218. claude_mpm/core/output_style_manager.py +483 -0
  219. claude_mpm/core/pm_hook_interceptor.py +197 -82
  220. claude_mpm/core/protocols/__init__.py +23 -0
  221. claude_mpm/core/protocols/runner_protocol.py +103 -0
  222. claude_mpm/core/protocols/session_protocol.py +131 -0
  223. claude_mpm/core/service_registry.py +153 -116
  224. claude_mpm/core/session_manager.py +179 -64
  225. claude_mpm/core/shared/__init__.py +17 -0
  226. claude_mpm/core/shared/config_loader.py +326 -0
  227. claude_mpm/core/shared/path_resolver.py +281 -0
  228. claude_mpm/core/shared/singleton_manager.py +221 -0
  229. claude_mpm/core/socketio_pool.py +400 -137
  230. claude_mpm/core/system_context.py +38 -0
  231. claude_mpm/core/tool_access_control.py +64 -57
  232. claude_mpm/core/types.py +307 -0
  233. claude_mpm/core/typing_utils.py +553 -0
  234. claude_mpm/core/unified_agent_registry.py +969 -0
  235. claude_mpm/core/unified_config.py +570 -0
  236. claude_mpm/core/unified_paths.py +941 -0
  237. claude_mpm/dashboard/__init__.py +12 -0
  238. claude_mpm/dashboard/api/simple_directory.py +261 -0
  239. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  240. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +1 -0
  241. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +1 -0
  242. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +1 -0
  243. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +24 -0
  244. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B0uc0UOD.js +36 -0
  245. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7RN905-.js +1 -0
  246. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7xVLGWV.js +2 -0
  247. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BIF9m_hv.js +61 -0
  248. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +1 -0
  249. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BPYeabCQ.js +1 -0
  250. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
  251. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BSNlmTZj.js +1 -0
  252. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Be7GpZd6.js +7 -0
  253. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bh0LDWpI.js +145 -0
  254. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BofRWZRR.js +10 -0
  255. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BovzEFCE.js +30 -0
  256. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C30mlcqg.js +165 -0
  257. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4B-KCzX.js +1 -0
  258. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4JcI4KD.js +122 -0
  259. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CBBdVcY8.js +1 -0
  260. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CDuw-vjf.js +1 -0
  261. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C_Usid8X.js +15 -0
  262. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cfqx1Qun.js +10 -0
  263. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CiIAseT4.js +128 -0
  264. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
  265. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CnA0NrzZ.js +1 -0
  266. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cs_tUR18.js +24 -0
  267. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
  268. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CyWMqx4W.js +43 -0
  269. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzZX-COe.js +220 -0
  270. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzeYkLYB.js +65 -0
  271. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D3k0OPJN.js +4 -0
  272. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9lljYKQ.js +1 -0
  273. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DGkLK5U1.js +267 -0
  274. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DI7hHRFL.js +1 -0
  275. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DLVjFsZ3.js +139 -0
  276. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUrLdbGD.js +89 -0
  277. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
  278. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DY1XQ8fi.js +2 -0
  279. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DZX00Y4g.js +1 -0
  280. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +1 -0
  281. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DaimHw_p.js +68 -0
  282. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +323 -0
  283. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dhb8PKl3.js +1 -0
  284. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dle-35c7.js +64 -0
  285. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DmxopI1J.js +1 -0
  286. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DwBR2MJi.js +60 -0
  287. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/GYwsonyD.js +1 -0
  288. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
  289. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/NqQ1dWOy.js +1 -0
  290. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/RJiighC3.js +1 -0
  291. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Vzk33B_K.js +2 -0
  292. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ZGh7QtNv.js +7 -0
  293. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bT1r9zLR.js +1 -0
  294. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bTOqqlTd.js +1 -0
  295. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/eNVUfhuA.js +1 -0
  296. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/iEWssX7S.js +162 -0
  297. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/sQeU3Y1z.js +1 -0
  298. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uuIeMWc-.js +1 -0
  299. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.D6-I5TpK.js +2 -0
  300. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +1 -0
  301. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.m1gL8KXf.js +1 -0
  302. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.CgNOuw-d.js +1 -0
  303. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +1 -0
  304. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  305. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  306. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  307. claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
  308. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
  309. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
  310. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
  311. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
  312. claude_mpm/experimental/__init__.py +10 -0
  313. claude_mpm/experimental/cli_enhancements.py +104 -89
  314. claude_mpm/generators/__init__.py +1 -1
  315. claude_mpm/generators/agent_profile_generator.py +76 -66
  316. claude_mpm/hooks/__init__.py +37 -1
  317. claude_mpm/hooks/base_hook.py +37 -32
  318. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  319. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  320. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  321. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  322. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  323. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  324. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  325. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  326. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  327. claude_mpm/hooks/claude_hooks/connection_pool.py +250 -0
  328. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  329. claude_mpm/hooks/claude_hooks/event_handlers.py +888 -0
  330. claude_mpm/hooks/claude_hooks/hook_handler.py +652 -875
  331. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +10 -7
  332. claude_mpm/hooks/claude_hooks/installer.py +806 -0
  333. claude_mpm/hooks/claude_hooks/memory_integration.py +249 -0
  334. claude_mpm/hooks/claude_hooks/response_tracking.py +412 -0
  335. claude_mpm/hooks/claude_hooks/services/__init__.py +15 -0
  336. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  337. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  338. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  339. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  340. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  341. claude_mpm/hooks/claude_hooks/services/connection_manager.py +229 -0
  342. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +254 -0
  343. claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
  344. claude_mpm/hooks/claude_hooks/services/state_manager.py +284 -0
  345. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
  346. claude_mpm/hooks/claude_hooks/tool_analysis.py +224 -0
  347. claude_mpm/hooks/failure_learning/__init__.py +54 -0
  348. claude_mpm/hooks/failure_learning/failure_detection_hook.py +230 -0
  349. claude_mpm/hooks/failure_learning/fix_detection_hook.py +212 -0
  350. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +281 -0
  351. claude_mpm/hooks/instruction_reinforcement.py +301 -0
  352. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  353. claude_mpm/hooks/kuzu_memory_hook.py +386 -0
  354. claude_mpm/hooks/kuzu_response_hook.py +179 -0
  355. claude_mpm/hooks/memory_integration_hook.py +201 -107
  356. claude_mpm/hooks/session_resume_hook.py +121 -0
  357. claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
  358. claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
  359. claude_mpm/hooks/tool_call_interceptor.py +92 -76
  360. claude_mpm/hooks/validation_hooks.py +62 -54
  361. claude_mpm/init.py +518 -83
  362. claude_mpm/models/__init__.py +9 -9
  363. claude_mpm/models/agent_definition.py +40 -23
  364. claude_mpm/models/agent_session.py +538 -0
  365. claude_mpm/models/git_repository.py +198 -0
  366. claude_mpm/models/resume_log.py +340 -0
  367. claude_mpm/schemas/__init__.py +12 -0
  368. claude_mpm/scripts/__init__.py +15 -0
  369. claude_mpm/scripts/claude-hook-handler.sh +227 -0
  370. claude_mpm/scripts/launch_monitor.py +165 -0
  371. claude_mpm/scripts/mpm_doctor.py +322 -0
  372. claude_mpm/scripts/socketio_daemon.py +189 -200
  373. claude_mpm/scripts/start_activity_logging.py +91 -0
  374. claude_mpm/services/__init__.py +208 -39
  375. claude_mpm/services/agent_capabilities_service.py +266 -0
  376. claude_mpm/services/agents/__init__.py +89 -0
  377. claude_mpm/services/agents/agent_builder.py +514 -0
  378. claude_mpm/services/agents/agent_preset_service.py +238 -0
  379. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  380. claude_mpm/services/agents/agent_review_service.py +280 -0
  381. claude_mpm/services/agents/agent_selection_service.py +484 -0
  382. claude_mpm/services/agents/auto_config_manager.py +796 -0
  383. claude_mpm/services/agents/auto_deploy_index_parser.py +569 -0
  384. claude_mpm/services/agents/cache_git_manager.py +621 -0
  385. claude_mpm/services/agents/deployment/__init__.py +21 -0
  386. claude_mpm/services/agents/deployment/agent_config_provider.py +410 -0
  387. claude_mpm/services/agents/deployment/agent_configuration_manager.py +358 -0
  388. claude_mpm/services/agents/deployment/agent_definition_factory.py +80 -0
  389. claude_mpm/services/agents/deployment/agent_deployment.py +1037 -0
  390. claude_mpm/services/agents/deployment/agent_discovery_service.py +546 -0
  391. claude_mpm/services/agents/deployment/agent_environment_manager.py +288 -0
  392. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +383 -0
  393. claude_mpm/services/agents/deployment/agent_format_converter.py +505 -0
  394. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +160 -0
  395. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +957 -0
  396. claude_mpm/services/agents/deployment/agent_metrics_collector.py +273 -0
  397. claude_mpm/services/agents/deployment/agent_operation_service.py +573 -0
  398. claude_mpm/services/agents/deployment/agent_record_service.py +418 -0
  399. claude_mpm/services/agents/deployment/agent_restore_handler.py +84 -0
  400. claude_mpm/services/agents/deployment/agent_state_service.py +381 -0
  401. claude_mpm/services/agents/deployment/agent_template_builder.py +1369 -0
  402. claude_mpm/services/agents/deployment/agent_validator.py +376 -0
  403. claude_mpm/services/agents/deployment/agent_version_manager.py +322 -0
  404. claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +10 -13
  405. claude_mpm/services/agents/deployment/agents_directory_resolver.py +149 -0
  406. claude_mpm/services/agents/deployment/async_agent_deployment.py +768 -0
  407. claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
  408. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  409. claude_mpm/services/agents/deployment/config/deployment_config.py +181 -0
  410. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  411. claude_mpm/services/agents/deployment/deployment_config_loader.py +178 -0
  412. claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
  413. claude_mpm/services/agents/deployment/deployment_type_detector.py +120 -0
  414. claude_mpm/services/agents/deployment/deployment_wrapper.py +129 -0
  415. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  416. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  417. claude_mpm/services/agents/deployment/facade/deployment_executor.py +70 -0
  418. claude_mpm/services/agents/deployment/facade/deployment_facade.py +269 -0
  419. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  420. claude_mpm/services/agents/deployment/interface_adapter.py +226 -0
  421. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  422. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  423. claude_mpm/services/agents/deployment/local_template_deployment.py +362 -0
  424. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +1478 -0
  425. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  426. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  427. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +162 -0
  428. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  429. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  430. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +240 -0
  431. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +110 -0
  432. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +80 -0
  433. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +92 -0
  434. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +101 -0
  435. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  436. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +102 -0
  437. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  438. claude_mpm/services/agents/deployment/processors/agent_processor.py +269 -0
  439. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +311 -0
  440. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +862 -0
  441. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  442. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  443. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  444. claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
  445. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  446. claude_mpm/services/agents/deployment/strategies/base_strategy.py +113 -0
  447. claude_mpm/services/agents/deployment/strategies/project_strategy.py +148 -0
  448. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  449. claude_mpm/services/agents/deployment/strategies/system_strategy.py +131 -0
  450. claude_mpm/services/agents/deployment/strategies/user_strategy.py +130 -0
  451. claude_mpm/services/agents/deployment/system_instructions_deployer.py +228 -0
  452. claude_mpm/services/agents/deployment/validation/__init__.py +21 -0
  453. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  454. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  455. claude_mpm/services/agents/deployment/validation/template_validator.py +319 -0
  456. claude_mpm/services/agents/deployment/validation/validation_result.py +214 -0
  457. claude_mpm/services/agents/git_source_manager.py +682 -0
  458. claude_mpm/services/agents/loading/__init__.py +11 -0
  459. claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +306 -228
  460. claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +106 -91
  461. claude_mpm/services/agents/loading/framework_agent_loader.py +433 -0
  462. claude_mpm/services/agents/local_template_manager.py +784 -0
  463. claude_mpm/services/agents/management/__init__.py +9 -0
  464. claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +92 -69
  465. claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +219 -168
  466. claude_mpm/services/agents/memory/__init__.py +22 -0
  467. claude_mpm/services/agents/memory/agent_memory_manager.py +784 -0
  468. claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +20 -18
  469. claude_mpm/services/agents/memory/content_manager.py +470 -0
  470. claude_mpm/services/agents/memory/memory_categorization_service.py +167 -0
  471. claude_mpm/services/agents/memory/memory_file_service.py +129 -0
  472. claude_mpm/services/agents/memory/memory_format_service.py +201 -0
  473. claude_mpm/services/agents/memory/memory_limits_service.py +101 -0
  474. claude_mpm/services/agents/memory/template_generator.py +83 -0
  475. claude_mpm/services/agents/observers.py +547 -0
  476. claude_mpm/services/agents/recommender.py +617 -0
  477. claude_mpm/services/agents/registry/__init__.py +30 -0
  478. claude_mpm/services/agents/registry/deployed_agent_discovery.py +273 -0
  479. claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +370 -295
  480. claude_mpm/services/agents/single_tier_deployment_service.py +696 -0
  481. claude_mpm/services/agents/sources/__init__.py +13 -0
  482. claude_mpm/services/agents/sources/agent_sync_state.py +516 -0
  483. claude_mpm/services/agents/sources/git_source_sync_service.py +1202 -0
  484. claude_mpm/services/agents/startup_sync.py +259 -0
  485. claude_mpm/services/agents/toolchain_detector.py +478 -0
  486. claude_mpm/services/analysis/__init__.py +35 -0
  487. claude_mpm/services/analysis/clone_detector.py +1030 -0
  488. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  489. claude_mpm/services/analysis/postmortem_service.py +765 -0
  490. claude_mpm/services/async_session_logger.py +665 -0
  491. claude_mpm/services/claude_session_logger.py +321 -0
  492. claude_mpm/services/cli/__init__.py +18 -0
  493. claude_mpm/services/cli/agent_cleanup_service.py +408 -0
  494. claude_mpm/services/cli/agent_dependency_service.py +395 -0
  495. claude_mpm/services/cli/agent_listing_service.py +463 -0
  496. claude_mpm/services/cli/agent_output_formatter.py +605 -0
  497. claude_mpm/services/cli/agent_validation_service.py +590 -0
  498. claude_mpm/services/cli/memory_crud_service.py +622 -0
  499. claude_mpm/services/cli/memory_output_formatter.py +604 -0
  500. claude_mpm/services/cli/resume_service.py +617 -0
  501. claude_mpm/services/cli/session_manager.py +604 -0
  502. claude_mpm/services/cli/session_pause_manager.py +504 -0
  503. claude_mpm/services/cli/session_resume_helper.py +372 -0
  504. claude_mpm/services/cli/startup_checker.py +362 -0
  505. claude_mpm/services/cli/unified_dashboard_manager.py +439 -0
  506. claude_mpm/services/command_deployment_service.py +446 -0
  507. claude_mpm/services/command_handler_service.py +221 -0
  508. claude_mpm/services/communication/__init__.py +22 -0
  509. claude_mpm/services/core/__init__.py +108 -0
  510. claude_mpm/services/core/base.py +269 -0
  511. claude_mpm/services/core/cache_manager.py +309 -0
  512. claude_mpm/services/core/interfaces/__init__.py +273 -0
  513. claude_mpm/services/core/interfaces/agent.py +514 -0
  514. claude_mpm/services/core/interfaces/communication.py +316 -0
  515. claude_mpm/services/core/interfaces/health.py +169 -0
  516. claude_mpm/services/core/interfaces/infrastructure.py +357 -0
  517. claude_mpm/services/core/interfaces/model.py +281 -0
  518. claude_mpm/services/core/interfaces/process.py +372 -0
  519. claude_mpm/services/core/interfaces/project.py +121 -0
  520. claude_mpm/services/core/interfaces/restart.py +307 -0
  521. claude_mpm/services/core/interfaces/service.py +405 -0
  522. claude_mpm/services/core/interfaces/stability.py +260 -0
  523. claude_mpm/services/core/interfaces.py +81 -0
  524. claude_mpm/services/core/memory_manager.py +682 -0
  525. claude_mpm/services/core/models/__init__.py +70 -0
  526. claude_mpm/services/core/models/agent_config.py +384 -0
  527. claude_mpm/services/core/models/health.py +162 -0
  528. claude_mpm/services/core/models/process.py +239 -0
  529. claude_mpm/services/core/models/restart.py +302 -0
  530. claude_mpm/services/core/models/stability.py +264 -0
  531. claude_mpm/services/core/models/toolchain.py +306 -0
  532. claude_mpm/services/core/path_resolver.py +517 -0
  533. claude_mpm/services/core/service_container.py +520 -0
  534. claude_mpm/services/core/service_interfaces.py +436 -0
  535. claude_mpm/services/diagnostics/__init__.py +18 -0
  536. claude_mpm/services/diagnostics/checks/__init__.py +38 -0
  537. claude_mpm/services/diagnostics/checks/agent_check.py +370 -0
  538. claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
  539. claude_mpm/services/diagnostics/checks/base_check.py +60 -0
  540. claude_mpm/services/diagnostics/checks/claude_code_check.py +270 -0
  541. claude_mpm/services/diagnostics/checks/common_issues_check.py +363 -0
  542. claude_mpm/services/diagnostics/checks/configuration_check.py +306 -0
  543. claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
  544. claude_mpm/services/diagnostics/checks/installation_check.py +520 -0
  545. claude_mpm/services/diagnostics/checks/instructions_check.py +415 -0
  546. claude_mpm/services/diagnostics/checks/mcp_check.py +330 -0
  547. claude_mpm/services/diagnostics/checks/mcp_services_check.py +1058 -0
  548. claude_mpm/services/diagnostics/checks/monitor_check.py +281 -0
  549. claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
  550. claude_mpm/services/diagnostics/checks/startup_log_check.py +319 -0
  551. claude_mpm/services/diagnostics/diagnostic_runner.py +286 -0
  552. claude_mpm/services/diagnostics/doctor_reporter.py +578 -0
  553. claude_mpm/services/diagnostics/models.py +138 -0
  554. claude_mpm/services/event_aggregator.py +582 -0
  555. claude_mpm/services/event_bus/__init__.py +18 -0
  556. claude_mpm/services/event_bus/config.py +186 -0
  557. claude_mpm/services/event_bus/direct_relay.py +312 -0
  558. claude_mpm/services/event_bus/event_bus.py +396 -0
  559. claude_mpm/services/event_bus/relay.py +326 -0
  560. claude_mpm/services/events/__init__.py +44 -0
  561. claude_mpm/services/events/consumers/__init__.py +18 -0
  562. claude_mpm/services/events/consumers/dead_letter.py +306 -0
  563. claude_mpm/services/events/consumers/logging.py +184 -0
  564. claude_mpm/services/events/consumers/metrics.py +241 -0
  565. claude_mpm/services/events/consumers/socketio.py +377 -0
  566. claude_mpm/services/events/core.py +480 -0
  567. claude_mpm/services/events/interfaces.py +214 -0
  568. claude_mpm/services/events/producers/__init__.py +14 -0
  569. claude_mpm/services/events/producers/hook.py +269 -0
  570. claude_mpm/services/events/producers/system.py +329 -0
  571. claude_mpm/services/exceptions.py +433 -353
  572. claude_mpm/services/framework_claude_md_generator/__init__.py +81 -80
  573. claude_mpm/services/framework_claude_md_generator/content_assembler.py +74 -67
  574. claude_mpm/services/framework_claude_md_generator/content_validator.py +66 -62
  575. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +82 -60
  576. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +36 -37
  577. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +41 -40
  578. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +15 -15
  579. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +5 -4
  580. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  581. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  582. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  583. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  584. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +5 -4
  585. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  586. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  587. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +26 -30
  588. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +6 -5
  589. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  590. claude_mpm/services/framework_claude_md_generator/version_manager.py +31 -30
  591. claude_mpm/services/git/__init__.py +21 -0
  592. claude_mpm/services/git/git_operations_service.py +579 -0
  593. claude_mpm/services/github/__init__.py +21 -0
  594. claude_mpm/services/github/github_cli_service.py +397 -0
  595. claude_mpm/services/hook_installer_service.py +506 -0
  596. claude_mpm/services/hook_service.py +159 -111
  597. claude_mpm/services/infrastructure/__init__.py +52 -0
  598. claude_mpm/services/infrastructure/context_preservation.py +569 -0
  599. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  600. claude_mpm/services/infrastructure/logging.py +209 -0
  601. claude_mpm/services/infrastructure/monitoring/__init__.py +39 -0
  602. claude_mpm/services/infrastructure/monitoring/aggregator.py +432 -0
  603. claude_mpm/services/infrastructure/monitoring/base.py +122 -0
  604. claude_mpm/services/infrastructure/monitoring/legacy.py +203 -0
  605. claude_mpm/services/infrastructure/monitoring/network.py +219 -0
  606. claude_mpm/services/infrastructure/monitoring/process.py +343 -0
  607. claude_mpm/services/infrastructure/monitoring/resources.py +244 -0
  608. claude_mpm/services/infrastructure/monitoring/service.py +368 -0
  609. claude_mpm/services/infrastructure/monitoring.py +71 -0
  610. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  611. claude_mpm/services/instructions/__init__.py +9 -0
  612. claude_mpm/services/instructions/instruction_cache_service.py +374 -0
  613. claude_mpm/services/local_ops/__init__.py +155 -0
  614. claude_mpm/services/local_ops/crash_detector.py +257 -0
  615. claude_mpm/services/local_ops/health_checks/__init__.py +26 -0
  616. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  617. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  618. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  619. claude_mpm/services/local_ops/health_manager.py +427 -0
  620. claude_mpm/services/local_ops/log_monitor.py +396 -0
  621. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  622. claude_mpm/services/local_ops/process_manager.py +595 -0
  623. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  624. claude_mpm/services/local_ops/restart_manager.py +401 -0
  625. claude_mpm/services/local_ops/restart_policy.py +387 -0
  626. claude_mpm/services/local_ops/state_manager.py +372 -0
  627. claude_mpm/services/local_ops/unified_manager.py +600 -0
  628. claude_mpm/services/mcp_config_manager.py +1542 -0
  629. claude_mpm/services/mcp_service_verifier.py +732 -0
  630. claude_mpm/services/memory/__init__.py +19 -0
  631. claude_mpm/services/{memory_builder.py → memory/builder.py} +465 -373
  632. claude_mpm/services/memory/cache/__init__.py +14 -0
  633. claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +237 -200
  634. claude_mpm/services/memory/cache/simple_cache.py +331 -0
  635. claude_mpm/services/memory/failure_tracker.py +578 -0
  636. claude_mpm/services/memory/indexed_memory.py +648 -0
  637. claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +272 -243
  638. claude_mpm/services/memory/router.py +951 -0
  639. claude_mpm/services/memory_hook_service.py +470 -0
  640. claude_mpm/services/model/__init__.py +147 -0
  641. claude_mpm/services/model/base_provider.py +365 -0
  642. claude_mpm/services/model/claude_provider.py +412 -0
  643. claude_mpm/services/model/model_router.py +452 -0
  644. claude_mpm/services/model/ollama_provider.py +415 -0
  645. claude_mpm/services/monitor/__init__.py +20 -0
  646. claude_mpm/services/monitor/daemon.py +698 -0
  647. claude_mpm/services/monitor/daemon_manager.py +1076 -0
  648. claude_mpm/services/monitor/event_emitter.py +350 -0
  649. claude_mpm/services/monitor/handlers/__init__.py +21 -0
  650. claude_mpm/services/monitor/handlers/code_analysis.py +332 -0
  651. claude_mpm/services/monitor/handlers/dashboard.py +299 -0
  652. claude_mpm/services/monitor/handlers/file.py +264 -0
  653. claude_mpm/services/monitor/handlers/hooks.py +512 -0
  654. claude_mpm/services/monitor/management/__init__.py +18 -0
  655. claude_mpm/services/monitor/management/health.py +124 -0
  656. claude_mpm/services/monitor/management/lifecycle.py +730 -0
  657. claude_mpm/services/monitor/server.py +1493 -0
  658. claude_mpm/services/monitor_build_service.py +349 -0
  659. claude_mpm/services/native_agent_converter.py +356 -0
  660. claude_mpm/services/orphan_detection.py +786 -0
  661. claude_mpm/services/pm_skills_deployer.py +707 -0
  662. claude_mpm/services/port_manager.py +597 -0
  663. claude_mpm/services/pr/__init__.py +14 -0
  664. claude_mpm/services/pr/pr_template_service.py +329 -0
  665. claude_mpm/services/profile_manager.py +337 -0
  666. claude_mpm/services/project/__init__.py +44 -0
  667. claude_mpm/services/{project_analyzer.py → project/analyzer.py} +541 -291
  668. claude_mpm/services/project/analyzer_v2.py +566 -0
  669. claude_mpm/services/project/architecture_analyzer.py +461 -0
  670. claude_mpm/services/project/archive_manager.py +1045 -0
  671. claude_mpm/services/project/dependency_analyzer.py +462 -0
  672. claude_mpm/services/project/detection_strategies.py +719 -0
  673. claude_mpm/services/project/documentation_manager.py +554 -0
  674. claude_mpm/services/project/enhanced_analyzer.py +572 -0
  675. claude_mpm/services/project/language_analyzer.py +265 -0
  676. claude_mpm/services/project/metrics_collector.py +407 -0
  677. claude_mpm/services/project/project_organizer.py +1009 -0
  678. claude_mpm/services/project/registry.py +636 -0
  679. claude_mpm/services/project/toolchain_analyzer.py +583 -0
  680. claude_mpm/services/project_port_allocator.py +596 -0
  681. claude_mpm/services/recovery_manager.py +293 -240
  682. claude_mpm/services/response_tracker.py +267 -0
  683. claude_mpm/services/runner_configuration_service.py +605 -0
  684. claude_mpm/services/self_upgrade_service.py +608 -0
  685. claude_mpm/services/session_management_service.py +314 -0
  686. claude_mpm/services/session_manager.py +380 -0
  687. claude_mpm/services/shared/__init__.py +21 -0
  688. claude_mpm/services/shared/async_service_base.py +216 -0
  689. claude_mpm/services/shared/config_service_base.py +301 -0
  690. claude_mpm/services/shared/lifecycle_service_base.py +308 -0
  691. claude_mpm/services/shared/manager_base.py +315 -0
  692. claude_mpm/services/shared/service_factory.py +309 -0
  693. claude_mpm/services/skills/__init__.py +21 -0
  694. claude_mpm/services/skills/git_skill_source_manager.py +1324 -0
  695. claude_mpm/services/skills/selective_skill_deployer.py +744 -0
  696. claude_mpm/services/skills/skill_discovery_service.py +568 -0
  697. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  698. claude_mpm/services/skills_config.py +547 -0
  699. claude_mpm/services/skills_deployer.py +1168 -0
  700. claude_mpm/services/socketio/__init__.py +25 -0
  701. claude_mpm/services/socketio/client_proxy.py +229 -0
  702. claude_mpm/services/socketio/dashboard_server.py +362 -0
  703. claude_mpm/services/socketio/event_normalizer.py +798 -0
  704. claude_mpm/services/socketio/handlers/__init__.py +30 -0
  705. claude_mpm/services/socketio/handlers/base.py +136 -0
  706. claude_mpm/services/socketio/handlers/code_analysis.py +682 -0
  707. claude_mpm/services/socketio/handlers/connection.py +643 -0
  708. claude_mpm/services/socketio/handlers/connection_handler.py +333 -0
  709. claude_mpm/services/socketio/handlers/file.py +263 -0
  710. claude_mpm/services/socketio/handlers/git.py +962 -0
  711. claude_mpm/services/socketio/handlers/hook.py +211 -0
  712. claude_mpm/services/socketio/handlers/memory.py +26 -0
  713. claude_mpm/services/socketio/handlers/project.py +24 -0
  714. claude_mpm/services/socketio/handlers/registry.py +214 -0
  715. claude_mpm/services/socketio/migration_utils.py +343 -0
  716. claude_mpm/services/socketio/monitor_client.py +364 -0
  717. claude_mpm/services/socketio/server/__init__.py +18 -0
  718. claude_mpm/services/socketio/server/broadcaster.py +569 -0
  719. claude_mpm/services/socketio/server/connection_manager.py +579 -0
  720. claude_mpm/services/socketio/server/core.py +1079 -0
  721. claude_mpm/services/socketio/server/eventbus_integration.py +245 -0
  722. claude_mpm/services/socketio/server/main.py +501 -0
  723. claude_mpm/services/socketio_client_manager.py +173 -143
  724. claude_mpm/services/socketio_server.py +38 -1657
  725. claude_mpm/services/subprocess_launcher_service.py +322 -0
  726. claude_mpm/services/system_instructions_service.py +270 -0
  727. claude_mpm/services/ticket_manager.py +25 -209
  728. claude_mpm/services/ticket_services/__init__.py +26 -0
  729. claude_mpm/services/ticket_services/crud_service.py +328 -0
  730. claude_mpm/services/ticket_services/formatter_service.py +290 -0
  731. claude_mpm/services/ticket_services/search_service.py +324 -0
  732. claude_mpm/services/ticket_services/validation_service.py +303 -0
  733. claude_mpm/services/ticket_services/workflow_service.py +244 -0
  734. claude_mpm/services/unified/__init__.py +65 -0
  735. claude_mpm/services/unified/analyzer_strategies/__init__.py +44 -0
  736. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +518 -0
  737. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +680 -0
  738. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +900 -0
  739. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +745 -0
  740. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +733 -0
  741. claude_mpm/services/unified/config_strategies/__init__.py +175 -0
  742. claude_mpm/services/unified/config_strategies/config_schema.py +731 -0
  743. claude_mpm/services/unified/config_strategies/context_strategy.py +747 -0
  744. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1005 -0
  745. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +881 -0
  746. claude_mpm/services/unified/config_strategies/unified_config_service.py +823 -0
  747. claude_mpm/services/unified/config_strategies/validation_strategy.py +1148 -0
  748. claude_mpm/services/unified/deployment_strategies/__init__.py +97 -0
  749. claude_mpm/services/unified/deployment_strategies/base.py +553 -0
  750. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +573 -0
  751. claude_mpm/services/unified/deployment_strategies/local.py +607 -0
  752. claude_mpm/services/unified/deployment_strategies/utils.py +667 -0
  753. claude_mpm/services/unified/deployment_strategies/vercel.py +471 -0
  754. claude_mpm/services/unified/interfaces.py +475 -0
  755. claude_mpm/services/unified/migration.py +509 -0
  756. claude_mpm/services/unified/strategies.py +534 -0
  757. claude_mpm/services/unified/unified_analyzer.py +542 -0
  758. claude_mpm/services/unified/unified_config.py +691 -0
  759. claude_mpm/services/unified/unified_deployment.py +466 -0
  760. claude_mpm/services/utility_service.py +280 -0
  761. claude_mpm/services/version_control/__init__.py +34 -37
  762. claude_mpm/services/version_control/branch_strategy.py +26 -17
  763. claude_mpm/services/version_control/conflict_resolution.py +52 -36
  764. claude_mpm/services/version_control/git_operations.py +183 -49
  765. claude_mpm/services/version_control/semantic_versioning.py +172 -61
  766. claude_mpm/services/version_control/version_parser.py +546 -0
  767. claude_mpm/services/version_service.py +379 -0
  768. claude_mpm/services/visualization/__init__.py +15 -0
  769. claude_mpm/services/visualization/mermaid_generator.py +937 -0
  770. claude_mpm/skills/__init__.py +42 -0
  771. claude_mpm/skills/agent_skills_injector.py +324 -0
  772. claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
  773. claude_mpm/skills/bundled/__init__.py +6 -0
  774. claude_mpm/skills/bundled/api-documentation.md +393 -0
  775. claude_mpm/skills/bundled/async-testing.md +571 -0
  776. claude_mpm/skills/bundled/code-review.md +143 -0
  777. claude_mpm/skills/bundled/database-migration.md +199 -0
  778. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  779. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  780. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  781. claude_mpm/skills/bundled/git-workflow.md +414 -0
  782. claude_mpm/skills/bundled/imagemagick.md +204 -0
  783. claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
  784. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  785. claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
  786. claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
  787. claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
  788. claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
  789. claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
  790. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  791. claude_mpm/skills/bundled/pdf.md +141 -0
  792. claude_mpm/skills/bundled/performance-profiling.md +573 -0
  793. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  794. claude_mpm/skills/bundled/security-scanning.md +439 -0
  795. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  796. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  797. claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
  798. claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
  799. claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
  800. claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
  801. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  802. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  803. claude_mpm/skills/bundled/xlsx.md +157 -0
  804. claude_mpm/skills/registry.py +286 -0
  805. claude_mpm/skills/skill_manager.py +405 -0
  806. claude_mpm/skills/skills_registry.py +347 -0
  807. claude_mpm/skills/skills_service.py +739 -0
  808. claude_mpm/storage/__init__.py +9 -0
  809. claude_mpm/storage/state_storage.py +546 -0
  810. claude_mpm/templates/.pre-commit-config.yaml +112 -0
  811. claude_mpm/templates/questions/__init__.py +38 -0
  812. claude_mpm/templates/questions/base.py +193 -0
  813. claude_mpm/templates/questions/pr_strategy.py +311 -0
  814. claude_mpm/templates/questions/project_init.py +385 -0
  815. claude_mpm/templates/questions/ticket_mgmt.py +394 -0
  816. claude_mpm/ticket_wrapper.py +2 -2
  817. claude_mpm/tools/__init__.py +10 -0
  818. claude_mpm/tools/__main__.py +208 -0
  819. claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
  820. claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
  821. claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
  822. claude_mpm/tools/code_tree_analyzer/core.py +380 -0
  823. claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
  824. claude_mpm/tools/code_tree_analyzer/events.py +168 -0
  825. claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
  826. claude_mpm/tools/code_tree_analyzer/models.py +39 -0
  827. claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
  828. claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
  829. claude_mpm/tools/code_tree_builder.py +631 -0
  830. claude_mpm/tools/code_tree_events.py +420 -0
  831. claude_mpm/tools/socketio_debug.py +671 -0
  832. claude_mpm/utils/__init__.py +8 -8
  833. claude_mpm/utils/agent_dependency_loader.py +1090 -0
  834. claude_mpm/utils/agent_filters.py +261 -0
  835. claude_mpm/utils/common.py +544 -0
  836. claude_mpm/utils/config_manager.py +168 -126
  837. claude_mpm/utils/console.py +11 -0
  838. claude_mpm/utils/database_connector.py +298 -0
  839. claude_mpm/utils/dependency_cache.py +373 -0
  840. claude_mpm/utils/dependency_manager.py +60 -59
  841. claude_mpm/utils/dependency_strategies.py +381 -0
  842. claude_mpm/utils/display_helper.py +260 -0
  843. claude_mpm/utils/environment_context.py +313 -0
  844. claude_mpm/utils/error_handler.py +78 -66
  845. claude_mpm/utils/file_utils.py +305 -0
  846. claude_mpm/utils/framework_detection.py +12 -11
  847. claude_mpm/utils/git_analyzer.py +407 -0
  848. claude_mpm/utils/gitignore.py +244 -0
  849. claude_mpm/utils/import_migration_example.py +12 -60
  850. claude_mpm/utils/imports.py +48 -45
  851. claude_mpm/utils/log_cleanup.py +627 -0
  852. claude_mpm/utils/migration.py +372 -0
  853. claude_mpm/utils/path_operations.py +110 -104
  854. claude_mpm/utils/progress.py +387 -0
  855. claude_mpm/utils/robust_installer.py +823 -0
  856. claude_mpm/utils/session_logging.py +121 -0
  857. claude_mpm/utils/structured_questions.py +619 -0
  858. claude_mpm/utils/subprocess_utils.py +343 -0
  859. claude_mpm/validation/__init__.py +1 -1
  860. claude_mpm/validation/agent_validator.py +214 -108
  861. claude_mpm/validation/frontmatter_validator.py +252 -0
  862. claude_mpm-5.4.55.dist-info/METADATA +999 -0
  863. claude_mpm-5.4.55.dist-info/RECORD +868 -0
  864. {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/entry_points.txt +1 -3
  865. claude_mpm-5.4.55.dist-info/licenses/LICENSE +94 -0
  866. claude_mpm-5.4.55.dist-info/licenses/LICENSE-FAQ.md +153 -0
  867. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -88
  868. claude_mpm/agents/INSTRUCTIONS.md +0 -352
  869. claude_mpm/agents/backups/INSTRUCTIONS.md +0 -352
  870. claude_mpm/agents/base_agent_loader.py +0 -529
  871. claude_mpm/agents/schema/agent_schema.json +0 -314
  872. claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
  873. claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +0 -46
  874. claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +0 -45
  875. claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +0 -49
  876. claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +0 -46
  877. claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +0 -45
  878. claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +0 -49
  879. claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +0 -46
  880. claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +0 -46
  881. claude_mpm/agents/templates/data_engineer.json +0 -110
  882. claude_mpm/agents/templates/documentation.json +0 -109
  883. claude_mpm/agents/templates/engineer.json +0 -113
  884. claude_mpm/agents/templates/ops.json +0 -109
  885. claude_mpm/agents/templates/pm.json +0 -25
  886. claude_mpm/agents/templates/qa.json +0 -111
  887. claude_mpm/agents/templates/research.json +0 -65
  888. claude_mpm/agents/templates/security.json +0 -113
  889. claude_mpm/agents/templates/test_integration.json +0 -112
  890. claude_mpm/agents/templates/version_control.json +0 -107
  891. claude_mpm/cli/commands/ui.py +0 -57
  892. claude_mpm/core/simple_runner.py +0 -1046
  893. claude_mpm/dashboard/open_dashboard.py +0 -34
  894. claude_mpm/deployment_paths.py +0 -261
  895. claude_mpm/hooks/builtin/__init__.py +0 -1
  896. claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
  897. claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
  898. claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
  899. claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
  900. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
  901. claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
  902. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
  903. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
  904. claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
  905. claude_mpm/orchestration/__init__.py +0 -6
  906. claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
  907. claude_mpm/orchestration/archive/factory.py +0 -215
  908. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
  909. claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
  910. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
  911. claude_mpm/orchestration/archive/orchestrator.py +0 -501
  912. claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
  913. claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
  914. claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
  915. claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
  916. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
  917. claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
  918. claude_mpm/schemas/workflow_validator.py +0 -411
  919. claude_mpm/services/agent_deployment.py +0 -1534
  920. claude_mpm/services/agent_lifecycle_manager.py +0 -1169
  921. claude_mpm/services/agent_memory_manager.py +0 -1415
  922. claude_mpm/services/agent_registry.py +0 -676
  923. claude_mpm/services/deployed_agent_discovery.py +0 -226
  924. claude_mpm/services/framework_agent_loader.py +0 -337
  925. claude_mpm/services/framework_claude_md_generator.py +0 -621
  926. claude_mpm/services/health_monitor.py +0 -892
  927. claude_mpm/services/memory_router.py +0 -538
  928. claude_mpm/services/parent_directory_manager/__init__.py +0 -577
  929. claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
  930. claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
  931. claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
  932. claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
  933. claude_mpm/services/parent_directory_manager/operations.py +0 -186
  934. claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
  935. claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
  936. claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
  937. claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
  938. claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
  939. claude_mpm/services/standalone_socketio_server.py +0 -1300
  940. claude_mpm/services/ticket_manager_di.py +0 -318
  941. claude_mpm/services/ticketing_service_original.py +0 -508
  942. claude_mpm/ui/__init__.py +0 -1
  943. claude_mpm/ui/rich_terminal_ui.py +0 -295
  944. claude_mpm/ui/terminal_ui.py +0 -328
  945. claude_mpm/utils/paths.py +0 -289
  946. claude_mpm-3.4.10.dist-info/METADATA +0 -183
  947. claude_mpm-3.4.10.dist-info/RECORD +0 -201
  948. claude_mpm-3.4.10.dist-info/licenses/LICENSE +0 -21
  949. {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/WHEEL +0 -0
  950. {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/top_level.txt +0 -0
@@ -2,190 +2,2525 @@
2
2
  Agents command implementation for claude-mpm.
3
3
 
4
4
  WHY: This module manages Claude Code native agents, including listing, deploying,
5
- and cleaning agent deployments.
5
+ and cleaning agent deployments. Refactored to use shared utilities for consistency.
6
+
7
+ DESIGN DECISIONS:
8
+ - Use AgentCommand base class for consistent CLI patterns
9
+ - Leverage shared utilities for argument parsing and output formatting
10
+ - Maintain backward compatibility with existing functionality
11
+ - Support multiple output formats (json, yaml, table, text)
6
12
  """
7
13
 
14
+ import json
8
15
  from pathlib import Path
9
16
 
10
- from ...core.logger import get_logger
11
17
  from ...constants import AgentCommands
18
+ from ...core.enums import OutputFormat
19
+ from ...services.cli.agent_cleanup_service import AgentCleanupService
20
+ from ...services.cli.agent_dependency_service import AgentDependencyService
21
+ from ...services.cli.agent_listing_service import AgentListingService
22
+ from ...services.cli.agent_output_formatter import AgentOutputFormatter
23
+ from ...services.cli.agent_validation_service import AgentValidationService
24
+ from ..shared import AgentCommand, CommandResult
12
25
  from ..utils import get_agent_versions_display
26
+ from .agents_cleanup import handle_agents_cleanup
13
27
 
14
28
 
15
- def manage_agents(args):
16
- """
17
- Manage Claude Code native agents.
18
-
19
- WHY: Claude Code agents need to be deployed and managed. This command provides
20
- a unified interface for all agent-related operations.
21
-
22
- DESIGN DECISION: When no subcommand is provided, we show the current agent
23
- versions as a quick status check. This matches the behavior users see at startup.
24
-
25
- Args:
26
- args: Parsed command line arguments with agents_command attribute
27
- """
28
- logger = get_logger("cli")
29
-
30
- try:
31
- from ...services.agent_deployment import AgentDeploymentService
32
- deployment_service = AgentDeploymentService()
33
-
34
- if not args.agents_command:
35
- # No subcommand - show agent versions
36
- # WHY: This provides a quick way for users to check deployed agent versions
37
- # without needing to specify additional subcommands
29
+ def _is_structured_output(args) -> bool:
30
+ """Check if args specify structured output format (JSON/YAML)."""
31
+ if hasattr(args, "format"):
32
+ fmt = str(args.format).lower()
33
+ return fmt in (OutputFormat.JSON, OutputFormat.YAML)
34
+ return False
35
+
36
+
37
+ class AgentsCommand(AgentCommand):
38
+ """Agent management command using shared utilities."""
39
+
40
+ def __init__(self):
41
+ super().__init__("agents")
42
+ self._deployment_service = None
43
+ self._listing_service = None
44
+ self._validation_service = None
45
+ self._dependency_service = None
46
+ self._cleanup_service = None
47
+ self._formatter = AgentOutputFormatter()
48
+
49
+ @property
50
+ def deployment_service(self):
51
+ """Get deployment service instance (lazy loaded)."""
52
+ if self._deployment_service is None:
53
+ try:
54
+ from ...services import AgentDeploymentService
55
+ from ...services.agents.deployment.deployment_wrapper import (
56
+ DeploymentServiceWrapper,
57
+ )
58
+
59
+ base_service = AgentDeploymentService()
60
+ self._deployment_service = DeploymentServiceWrapper(base_service)
61
+ except ImportError as e:
62
+ raise ImportError("Agent deployment service not available") from e
63
+ return self._deployment_service
64
+
65
+ @property
66
+ def listing_service(self):
67
+ """Get listing service instance (lazy loaded)."""
68
+ if self._listing_service is None:
69
+ self._listing_service = AgentListingService(
70
+ deployment_service=self.deployment_service
71
+ )
72
+ return self._listing_service
73
+
74
+ @property
75
+ def validation_service(self):
76
+ """Get validation service instance (lazy loaded)."""
77
+ if self._validation_service is None:
78
+ self._validation_service = AgentValidationService()
79
+ return self._validation_service
80
+
81
+ @property
82
+ def dependency_service(self):
83
+ """Get dependency service instance (lazy loaded)."""
84
+ if self._dependency_service is None:
85
+ self._dependency_service = AgentDependencyService()
86
+ return self._dependency_service
87
+
88
+ @property
89
+ def cleanup_service(self):
90
+ """Get cleanup service instance (lazy loaded)."""
91
+ if self._cleanup_service is None:
92
+ self._cleanup_service = AgentCleanupService(
93
+ deployment_service=self.deployment_service
94
+ )
95
+ return self._cleanup_service
96
+
97
+ def _get_output_format(self, args) -> str:
98
+ """
99
+ Get output format from args with enum default.
100
+
101
+ Args:
102
+ args: Command arguments
103
+
104
+ Returns:
105
+ Output format string (compatible with both enum and string usage)
106
+ """
107
+ return getattr(args, "format", OutputFormat.TEXT)
108
+
109
+ def _is_structured_format(self, format_str: str) -> bool:
110
+ """
111
+ Check if format is structured (JSON/YAML).
112
+
113
+ Args:
114
+ format_str: Format string to check
115
+
116
+ Returns:
117
+ True if format is JSON or YAML
118
+ """
119
+ fmt = str(format_str).lower()
120
+ return fmt in (OutputFormat.JSON, OutputFormat.YAML)
121
+
122
+ def validate_args(self, args) -> str:
123
+ """Validate command arguments."""
124
+ # Most agent commands are optional, so basic validation
125
+ return None
126
+
127
+ def run(self, args) -> CommandResult:
128
+ """Execute the agent command."""
129
+ try:
130
+ # Handle default case (no subcommand)
131
+ if not hasattr(args, "agents_command") or not args.agents_command:
132
+ return self._show_agent_versions(args)
133
+
134
+ # Route to appropriate subcommand
135
+ command_map = {
136
+ AgentCommands.LIST.value: self._list_agents,
137
+ AgentCommands.DEPLOY.value: lambda a: self._deploy_agents(
138
+ a, force=False
139
+ ),
140
+ AgentCommands.FORCE_DEPLOY.value: lambda a: self._deploy_agents(
141
+ a, force=True
142
+ ),
143
+ AgentCommands.CLEAN.value: self._clean_agents,
144
+ AgentCommands.VIEW.value: self._view_agent,
145
+ AgentCommands.FIX.value: self._fix_agents,
146
+ "deps-check": self._check_agent_dependencies,
147
+ "deps-install": self._install_agent_dependencies,
148
+ "deps-list": self._list_agent_dependencies,
149
+ "deps-fix": self._fix_agent_dependencies,
150
+ "cleanup": lambda a: self._handle_cleanup_command(a),
151
+ "cleanup-orphaned": self._cleanup_orphaned_agents,
152
+ # Local agent management commands
153
+ "create": self._create_local_agent,
154
+ "edit": self._edit_local_agent,
155
+ "delete": self._delete_local_agent,
156
+ "manage": self._manage_local_agents,
157
+ "configure": self._configure_deployment,
158
+ # Migration command (DEPRECATION support)
159
+ "migrate-to-project": self._migrate_to_project,
160
+ # Agent selection modes (Phase 3: 1M-382)
161
+ "deploy-minimal": self._deploy_minimal_configuration,
162
+ "deploy-auto": self._deploy_auto_configure,
163
+ # Agent source management (Phase 2: 1M-442)
164
+ "available": self._list_available_from_sources,
165
+ # Agent discovery with rich filtering (Phase 1: Discovery & Browsing)
166
+ "discover": self._discover_agents,
167
+ # NEW: Collection-based agent management
168
+ "list-collections": self._list_collections,
169
+ "deploy-collection": self._deploy_collection,
170
+ "list-by-collection": self._list_by_collection,
171
+ # Cache git management commands
172
+ "cache-status": self._cache_status,
173
+ "cache-pull": self._cache_pull,
174
+ "cache-push": self._cache_push,
175
+ "cache-sync": self._cache_sync,
176
+ "cache-commit": self._cache_commit,
177
+ }
178
+
179
+ if args.agents_command in command_map:
180
+ return command_map[args.agents_command](args)
181
+ return CommandResult.error_result(
182
+ f"Unknown agent command: {args.agents_command}"
183
+ )
184
+
185
+ except ImportError:
186
+ self.logger.error("Agent deployment service not available")
187
+ return CommandResult.error_result("Agent deployment service not available")
188
+ except Exception as e:
189
+ self.logger.error(f"Error managing agents: {e}", exc_info=True)
190
+ return CommandResult.error_result(f"Error managing agents: {e}")
191
+
192
+ def _show_agent_versions(self, args) -> CommandResult:
193
+ """Show current agent versions as default action."""
194
+ try:
38
195
  agent_versions = get_agent_versions_display()
196
+
197
+ output_format = self._get_output_format(args)
198
+ if self._is_structured_format(output_format):
199
+ # Parse the agent versions display into structured data
200
+ if agent_versions:
201
+ data = {"agent_versions": agent_versions, "has_agents": True}
202
+ formatted = (
203
+ self._formatter.format_as_json(data)
204
+ if str(output_format).lower() == OutputFormat.JSON
205
+ else self._formatter.format_as_yaml(data)
206
+ )
207
+ print(formatted)
208
+ return CommandResult.success_result(
209
+ "Agent versions retrieved", data=data
210
+ )
211
+ data = {
212
+ "agent_versions": None,
213
+ "has_agents": False,
214
+ "suggestion": "To deploy agents, run: claude-mpm --mpm:agents deploy",
215
+ }
216
+ formatted = (
217
+ self._formatter.format_as_json(data)
218
+ if str(output_format).lower() == OutputFormat.JSON
219
+ else self._formatter.format_as_yaml(data)
220
+ )
221
+ print(formatted)
222
+ return CommandResult.success_result(
223
+ "No deployed agents found", data=data
224
+ )
225
+ # Text output
39
226
  if agent_versions:
40
227
  print(agent_versions)
41
- else:
42
- print("No deployed agents found")
43
- print("\nTo deploy agents, run: claude-mpm --mpm:agents deploy")
44
- return
45
-
46
- if args.agents_command == AgentCommands.LIST.value:
47
- _list_agents(args, deployment_service)
48
-
49
- elif args.agents_command == AgentCommands.DEPLOY.value:
50
- _deploy_agents(args, deployment_service, force=False)
51
-
52
- elif args.agents_command == AgentCommands.FORCE_DEPLOY.value:
53
- _deploy_agents(args, deployment_service, force=True)
54
-
55
- elif args.agents_command == AgentCommands.CLEAN.value:
56
- _clean_agents(args, deployment_service)
57
-
58
- except ImportError:
59
- logger.error("Agent deployment service not available")
60
- print("Error: Agent deployment service not available")
61
- except Exception as e:
62
- logger.error(f"Error managing agents: {e}")
63
- print(f"Error: {e}")
64
-
65
-
66
- def _list_agents(args, deployment_service):
67
- """
68
- List available or deployed agents.
69
-
70
- WHY: Users need to see what agents are available in the system and what's
71
- currently deployed. This helps them understand the agent ecosystem.
72
-
73
- Args:
74
- args: Command arguments with 'system' and 'deployed' flags
75
- deployment_service: Agent deployment service instance
76
- """
77
- if args.system:
78
- # List available agent templates
79
- print("Available Agent Templates:")
80
- print("-" * 80)
81
- agents = deployment_service.list_available_agents()
82
- if not agents:
83
- print("No agent templates found")
84
- else:
85
- for agent in agents:
86
- print(f"šŸ“„ {agent['file']}")
87
- if 'name' in agent:
88
- print(f" Name: {agent['name']}")
89
- if 'description' in agent:
90
- print(f" Description: {agent['description']}")
91
- if 'version' in agent:
92
- print(f" Version: {agent['version']}")
93
- print()
94
-
95
- elif args.deployed:
96
- # List deployed agents
97
- print("Deployed Agents:")
98
- print("-" * 80)
99
- verification = deployment_service.verify_deployment()
100
- if not verification["agents_found"]:
228
+ return CommandResult.success_result("Agent versions displayed")
101
229
  print("No deployed agents found")
230
+ print("\nTo deploy agents, run: claude-mpm --mpm:agents deploy")
231
+ return CommandResult.success_result("No deployed agents found")
232
+
233
+ except Exception as e:
234
+ self.logger.error(f"Error getting agent versions: {e}", exc_info=True)
235
+ return CommandResult.error_result(f"Error getting agent versions: {e}")
236
+
237
+ def _list_agents(self, args) -> CommandResult:
238
+ """List available or deployed agents."""
239
+ try:
240
+ output_format = self._get_output_format(args)
241
+
242
+ if hasattr(args, "by_tier") and args.by_tier:
243
+ return self._list_agents_by_tier(args)
244
+ if getattr(args, "system", False):
245
+ return self._list_system_agents(args)
246
+ if getattr(args, "deployed", False):
247
+ return self._list_deployed_agents(args)
248
+ # Default: show usage
249
+ usage_msg = "Use --system to list system agents, --deployed to list deployed agents, or --by-tier to group by precedence"
250
+
251
+ if self._is_structured_format(output_format):
252
+ return CommandResult.error_result(
253
+ "No list option specified",
254
+ data={
255
+ "usage": usage_msg,
256
+ "available_options": ["--system", "--deployed", "--by-tier"],
257
+ },
258
+ )
259
+ print(usage_msg)
260
+ return CommandResult.error_result("No list option specified")
261
+
262
+ except Exception as e:
263
+ self.logger.error(f"Error listing agents: {e}", exc_info=True)
264
+ return CommandResult.error_result(f"Error listing agents: {e}")
265
+
266
+ def _list_system_agents(self, args) -> CommandResult:
267
+ """List available agent templates."""
268
+ try:
269
+ verbose = getattr(args, "verbose", False)
270
+ agents = self.listing_service.list_system_agents(verbose=verbose)
271
+
272
+ output_format = self._get_output_format(args)
273
+ quiet = getattr(args, "quiet", False)
274
+
275
+ # Convert AgentInfo objects to dicts for formatter
276
+ agents_data = [
277
+ {
278
+ "name": agent.name,
279
+ "type": agent.type,
280
+ "path": agent.path,
281
+ "file": Path(agent.path).name if agent.path else "Unknown",
282
+ "description": agent.description,
283
+ "specializations": agent.specializations,
284
+ "version": agent.version,
285
+ }
286
+ for agent in agents
287
+ ]
288
+
289
+ formatted = self._formatter.format_agent_list(
290
+ agents_data, output_format=output_format, verbose=verbose, quiet=quiet
291
+ )
292
+ print(formatted)
293
+
294
+ return CommandResult.success_result(
295
+ f"Listed {len(agents)} agent templates",
296
+ data={"agents": agents_data, "count": len(agents)},
297
+ )
298
+
299
+ except Exception as e:
300
+ self.logger.error(f"Error listing system agents: {e}", exc_info=True)
301
+ return CommandResult.error_result(f"Error listing system agents: {e}")
302
+
303
+ def _list_deployed_agents(self, args) -> CommandResult:
304
+ """List deployed agents."""
305
+ try:
306
+ verbose = getattr(args, "verbose", False)
307
+ agents, warnings = self.listing_service.list_deployed_agents(
308
+ verbose=verbose
309
+ )
310
+
311
+ output_format = self._get_output_format(args)
312
+ quiet = getattr(args, "quiet", False)
313
+
314
+ # Convert AgentInfo objects to dicts for formatter
315
+ agents_data = [
316
+ {
317
+ "name": agent.name,
318
+ "type": agent.type,
319
+ "tier": agent.tier,
320
+ "path": agent.path,
321
+ "file": Path(agent.path).name if agent.path else "Unknown",
322
+ "description": agent.description,
323
+ "specializations": agent.specializations,
324
+ "version": agent.version,
325
+ }
326
+ for agent in agents
327
+ ]
328
+
329
+ # Format the agent list
330
+ formatted = self._formatter.format_agent_list(
331
+ agents_data, output_format=output_format, verbose=verbose, quiet=quiet
332
+ )
333
+ print(formatted)
334
+
335
+ # Add warnings for text output
336
+ if str(output_format).lower() == OutputFormat.TEXT and warnings:
337
+ print("\nWarnings:")
338
+ for warning in warnings:
339
+ print(f" āš ļø {warning}")
340
+
341
+ return CommandResult.success_result(
342
+ f"Listed {len(agents)} deployed agents",
343
+ data={
344
+ "agents": agents_data,
345
+ "warnings": warnings,
346
+ "count": len(agents),
347
+ },
348
+ )
349
+
350
+ except Exception as e:
351
+ self.logger.error(f"Error listing deployed agents: {e}", exc_info=True)
352
+ return CommandResult.error_result(f"Error listing deployed agents: {e}")
353
+
354
+ def _list_agents_by_tier(self, args) -> CommandResult:
355
+ """List agents grouped by tier/precedence."""
356
+ try:
357
+ tier_info = self.listing_service.list_agents_by_tier()
358
+ output_format = self._get_output_format(args)
359
+
360
+ # Convert to format expected by formatter
361
+ agents_by_tier = {
362
+ "project": [
363
+ {
364
+ "name": agent.name,
365
+ "type": agent.type,
366
+ "path": agent.path,
367
+ "active": agent.active,
368
+ "overridden_by": agent.overridden_by,
369
+ }
370
+ for agent in tier_info.project
371
+ ],
372
+ "user": [
373
+ {
374
+ "name": agent.name,
375
+ "type": agent.type,
376
+ "path": agent.path,
377
+ "active": agent.active,
378
+ "overridden_by": agent.overridden_by,
379
+ }
380
+ for agent in tier_info.user
381
+ ],
382
+ "system": [
383
+ {
384
+ "name": agent.name,
385
+ "type": agent.type,
386
+ "path": agent.path,
387
+ "active": agent.active,
388
+ "overridden_by": agent.overridden_by,
389
+ }
390
+ for agent in tier_info.system
391
+ ],
392
+ "summary": {
393
+ "total_count": tier_info.total_count,
394
+ "active_count": tier_info.active_count,
395
+ "project_count": len(tier_info.project),
396
+ "user_count": len(tier_info.user),
397
+ "system_count": len(tier_info.system),
398
+ },
399
+ }
400
+
401
+ formatted = self._formatter.format_agents_by_tier(
402
+ agents_by_tier, output_format=output_format
403
+ )
404
+ print(formatted)
405
+
406
+ return CommandResult.success_result(
407
+ "Agents listed by tier", data=agents_by_tier
408
+ )
409
+
410
+ except Exception as e:
411
+ self.logger.error(f"Error listing agents by tier: {e}", exc_info=True)
412
+ return CommandResult.error_result(f"Error listing agents by tier: {e}")
413
+
414
+ def _list_available_from_sources(self, args) -> CommandResult:
415
+ """List available agents from all configured git sources.
416
+
417
+ This command shows agents discovered from configured agent sources
418
+ (Git repositories) after syncing their cache. Implements Phase 2 of 1M-442.
419
+
420
+ Args:
421
+ args: Command arguments with optional source filter and format
422
+
423
+ Returns:
424
+ CommandResult with agent list or error
425
+ """
426
+ try:
427
+ from ...config.agent_sources import AgentSourceConfiguration
428
+ from ...services.agents.git_source_manager import GitSourceManager
429
+
430
+ # Load agent sources configuration
431
+ config = AgentSourceConfiguration.load()
432
+ enabled_repos = [r for r in config.repositories if r.enabled]
433
+
434
+ if not enabled_repos:
435
+ message = (
436
+ "No agent sources configured.\n\n"
437
+ "Configure sources with:\n"
438
+ " claude-mpm agent-source add <url>\n\n"
439
+ "Example:\n"
440
+ " claude-mpm agent-source add https://github.com/owner/repo/agents"
441
+ )
442
+ print(message)
443
+ return CommandResult.error_result("No agent sources configured")
444
+
445
+ # Initialize git source manager
446
+ manager = GitSourceManager()
447
+
448
+ # Sync all configured sources (with timeout)
449
+ self.logger.info(f"Syncing {len(enabled_repos)} agent sources...")
450
+ sync_results = {}
451
+
452
+ for repo in enabled_repos:
453
+ try:
454
+ result = manager.sync_repository(repo, force=False)
455
+ sync_results[repo.identifier] = result
456
+ except Exception as e:
457
+ self.logger.warning(f"Failed to sync {repo.identifier}: {e}")
458
+ sync_results[repo.identifier] = {"synced": False, "error": str(e)}
459
+
460
+ # Get source filter from args
461
+ source_filter = getattr(args, "source", None)
462
+
463
+ # List all cached agents
464
+ all_agents = manager.list_cached_agents(repo_identifier=source_filter)
465
+
466
+ if not all_agents:
467
+ message = "No agents found in configured sources."
468
+ if sync_results:
469
+ failed_count = sum(
470
+ 1 for r in sync_results.values() if not r.get("synced")
471
+ )
472
+ if failed_count > 0:
473
+ message += f"\n\n{failed_count} source(s) failed to sync. Check logs for details."
474
+ print(message)
475
+ return CommandResult.success_result(message, data={"agents": []})
476
+
477
+ # Format output
478
+ output_format = getattr(args, "format", "table")
479
+
480
+ if output_format == "json":
481
+ import json
482
+
483
+ print(json.dumps(all_agents, indent=2))
484
+ elif output_format == "simple":
485
+ for agent in all_agents:
486
+ name = agent.get("metadata", {}).get(
487
+ "name", agent.get("agent_id", "unknown")
488
+ )
489
+ repo = agent.get("repository", "unknown")
490
+ print(f"{name} (from {repo})")
491
+ else: # table format
492
+ print(f"\n{'Agent Name':<30} {'Repository':<40} {'Version':<15}")
493
+ print("=" * 85)
494
+ for agent in all_agents:
495
+ name = agent.get("metadata", {}).get(
496
+ "name", agent.get("agent_id", "unknown")
497
+ )
498
+ repo = agent.get("repository", "unknown")
499
+ version = agent.get("version", "unknown")[:12]
500
+ print(f"{name:<30} {repo:<40} {version:<15}")
501
+ print(
502
+ f"\nTotal: {len(all_agents)} agents from {len(sync_results)} sources"
503
+ )
504
+
505
+ return CommandResult.success_result(
506
+ f"Listed {len(all_agents)} agents from sources",
507
+ data={"agents": all_agents, "sync_results": sync_results},
508
+ )
509
+
510
+ except Exception as e:
511
+ self.logger.error(f"Error listing available agents: {e}", exc_info=True)
512
+ return CommandResult.error_result(f"Error listing available agents: {e}")
513
+
514
+ def _discover_agents(self, args) -> CommandResult:
515
+ """Discover agents with rich filtering capabilities.
516
+
517
+ This command extends the 'available' command by adding semantic filtering
518
+ based on AUTO-DEPLOY-INDEX.md categories. Users can filter by category,
519
+ language, framework, platform, and specialization.
520
+
521
+ Design Decision: Delegate to agents_discover.py module
522
+
523
+ Rationale: Keep CLI command logic separate from routing logic for better
524
+ testability and maintainability. The discover_command function handles
525
+ all the complex filtering and formatting logic.
526
+
527
+ Args:
528
+ args: Command arguments with filter options:
529
+ - source: Source repository filter
530
+ - category: Category filter (e.g., 'engineer/backend')
531
+ - language: Language filter (e.g., 'python')
532
+ - framework: Framework filter (e.g., 'react')
533
+ - platform: Platform filter (e.g., 'vercel')
534
+ - specialization: Specialization filter (e.g., 'data')
535
+ - format: Output format (table, json, simple)
536
+ - verbose: Show descriptions and metadata
537
+
538
+ Returns:
539
+ CommandResult with filtered agent list or error
540
+
541
+ Example:
542
+ >>> # Called via: claude-mpm agents discover --category engineer/backend
543
+ >>> _discover_agents(args)
544
+ CommandResult(success=True, message="Discovered 8 agents")
545
+ """
546
+ try:
547
+ from .agents_discover import discover_command
548
+
549
+ # Call discover_command and convert exit code to CommandResult
550
+ exit_code = discover_command(args)
551
+
552
+ if exit_code == 0:
553
+ return CommandResult.success_result("Agent discovery complete")
554
+ return CommandResult.error_result("Agent discovery failed")
555
+
556
+ except Exception as e:
557
+ self.logger.error(f"Error discovering agents: {e}", exc_info=True)
558
+ return CommandResult.error_result(f"Error discovering agents: {e}")
559
+
560
+ def _deploy_agents(self, args, force=False) -> CommandResult:
561
+ """Deploy agents using two-phase sync: cache → deploy.
562
+
563
+ Phase 3 Integration (1M-486): Uses Git sync service for deployment.
564
+ - Phase 1: Sync agents to ~/.claude-mpm/cache/agents/ (if needed)
565
+ - Phase 2: Deploy from cache to project .claude-mpm/agents/
566
+
567
+ This replaces the old single-tier deployment with a multi-project
568
+ architecture where one cache serves multiple project deployments.
569
+ """
570
+ try:
571
+ # Handle preset deployment (uses different path)
572
+ if hasattr(args, "preset") and args.preset:
573
+ return self._deploy_preset(args)
574
+
575
+ from ...services.agents.sources.git_source_sync_service import (
576
+ GitSourceSyncService,
577
+ )
578
+
579
+ # Initialize git sync service
580
+ git_sync = GitSourceSyncService()
581
+ project_dir = Path.cwd()
582
+
583
+ self.logger.info("Phase 1: Syncing agents to cache...")
584
+
585
+ # Sync to cache (downloads from GitHub if needed)
586
+ sync_result = git_sync.sync_repository(force=force)
587
+
588
+ if not sync_result.get("synced"):
589
+ error_msg = sync_result.get("error", "Unknown sync error")
590
+ self.logger.error(f"Sync failed: {error_msg}")
591
+ return CommandResult.error_result(f"Sync failed: {error_msg}")
592
+
593
+ self.logger.info(
594
+ f"Phase 1 complete: {sync_result.get('agent_count', 0)} agents in cache"
595
+ )
596
+ self.logger.info(f"Phase 2: Deploying agents to {project_dir}...")
597
+
598
+ # Deploy from cache to project directory
599
+ deploy_result = git_sync.deploy_agents_to_project(
600
+ project_dir=project_dir,
601
+ agent_list=None, # Deploy all cached agents
602
+ force=force,
603
+ )
604
+
605
+ # Format combined results for output
606
+ combined_result = {
607
+ "deployed_count": len(deploy_result.get("deployed", []))
608
+ + len(deploy_result.get("updated", [])),
609
+ "deployed": deploy_result.get("deployed", []),
610
+ "updated": deploy_result.get("updated", []),
611
+ "skipped": deploy_result.get("skipped", []),
612
+ "errors": deploy_result.get("failed", []),
613
+ "target_dir": deploy_result.get("deployment_dir", ""),
614
+ "sync_info": {
615
+ "cached_agents": sync_result.get("agent_count", 0),
616
+ "cache_dir": sync_result.get("cache_dir", ""),
617
+ },
618
+ }
619
+
620
+ output_format = self._get_output_format(args)
621
+ verbose = getattr(args, "verbose", False)
622
+
623
+ formatted = self._formatter.format_deployment_result(
624
+ combined_result, output_format=output_format, verbose=verbose
625
+ )
626
+ print(formatted)
627
+
628
+ success_count = len(deploy_result["deployed"]) + len(
629
+ deploy_result["updated"]
630
+ )
631
+ return CommandResult.success_result(
632
+ f"Deployed {success_count} agents from cache",
633
+ data={
634
+ "sync_result": sync_result,
635
+ "deploy_result": deploy_result,
636
+ "total_deployed": success_count,
637
+ },
638
+ )
639
+
640
+ except Exception as e:
641
+ self.logger.error(f"Error deploying agents: {e}", exc_info=True)
642
+ return CommandResult.error_result(f"Error deploying agents: {e}")
643
+
644
+ def _deploy_preset(self, args) -> CommandResult:
645
+ """Deploy agents by preset name.
646
+
647
+ This method implements Phase 2 of the agents/skills CLI redesign,
648
+ enabling preset-based deployment like:
649
+ claude-mpm agents deploy --preset python-dev
650
+
651
+ Args:
652
+ args: Command arguments with preset name and optional flags
653
+
654
+ Returns:
655
+ CommandResult with deployment status
656
+ """
657
+ try:
658
+ from pathlib import Path
659
+
660
+ from ...config.agent_sources import AgentSourceConfiguration
661
+ from ...services.agents.agent_preset_service import AgentPresetService
662
+ from ...services.agents.git_source_manager import GitSourceManager
663
+ from ...services.agents.single_tier_deployment_service import (
664
+ SingleTierDeploymentService,
665
+ )
666
+
667
+ preset_name = args.preset
668
+ dry_run = getattr(args, "dry_run", False)
669
+
670
+ # Initialize services
671
+ config = AgentSourceConfiguration.load()
672
+ deployment_dir = Path.home() / ".claude" / "agents"
673
+ git_source_manager = GitSourceManager()
674
+ preset_service = AgentPresetService(git_source_manager)
675
+ deployment_service = SingleTierDeploymentService(config, deployment_dir)
676
+
677
+ # Validate preset
678
+ if not preset_service.validate_preset(preset_name):
679
+ available = preset_service.list_presets()
680
+ print(f"āŒ Unknown preset: {preset_name}")
681
+ print("\nšŸ“š Available presets:")
682
+ for preset in available:
683
+ print(
684
+ f" • {preset['name']}: {preset['description']} ({preset['agent_count']} agents)"
685
+ )
686
+ print(f" Use cases: {', '.join(preset['use_cases'])}")
687
+ return CommandResult.error_result(f"Unknown preset: {preset_name}")
688
+
689
+ # Resolve preset to agent list
690
+ print(f"\nšŸ” Resolving preset: {preset_name}")
691
+ resolution = preset_service.resolve_agents(
692
+ preset_name, validate_availability=True
693
+ )
694
+
695
+ # Show preset info
696
+ preset_info = resolution["preset_info"]
697
+ print(f"\nšŸŽÆ Preset: {preset_info['description']}")
698
+ print(f" Agents: {preset_info['agent_count']}")
699
+ print(f" Use cases: {', '.join(preset_info['use_cases'])}")
700
+
701
+ # Show warnings for missing agents
702
+ if resolution["missing_agents"]:
703
+ print("\nāš ļø Missing agents (not found in configured sources):")
704
+ for agent_id in resolution["missing_agents"]:
705
+ print(f" • {agent_id}")
706
+ print("\nšŸ’” These agents are not available in your configured sources.")
707
+ print(" Deployment will continue with available agents.")
708
+
709
+ # Show conflicts
710
+ if resolution["conflicts"]:
711
+ print("\nāš ļø Priority conflicts detected:")
712
+ for conflict in resolution["conflicts"]:
713
+ sources = ", ".join(conflict["sources"])
714
+ print(f" • {conflict['agent_id']} (found in: {sources})")
715
+ print(" Using highest priority source for each")
716
+
717
+ # Dry run mode
718
+ if dry_run:
719
+ print("\nšŸ” DRY RUN: Preview agent deployment\n")
720
+ print("Agents to deploy:")
721
+ for agent in resolution["agents"]:
722
+ source = agent.get("source", "unknown")
723
+ print(f" āœ“ {agent['agent_id']} (from {source})")
724
+ print(
725
+ "\nšŸ’” This is a dry run. Run without --dry-run to actually deploy."
726
+ )
727
+ return CommandResult.success_result(
728
+ "Dry run complete",
729
+ data={
730
+ "preset": preset_name,
731
+ "agents": resolution["agents"],
732
+ "missing": resolution["missing_agents"],
733
+ },
734
+ )
735
+
736
+ # Deploy agents
737
+ print(f"\nšŸ“¦ Deploying {len(resolution['agents'])} agents...")
738
+ deployed_count = 0
739
+ failed_count = 0
740
+ skipped_count = len(resolution["missing_agents"])
741
+ deployed_agents = []
742
+ failed_agents = []
743
+
744
+ for agent in resolution["agents"]:
745
+ agent_id = agent["agent_id"]
746
+ try:
747
+ # Deploy using single-tier deployment service
748
+ result = deployment_service.deploy_agent(
749
+ agent_id, source_repo=agent.get("source"), dry_run=False
750
+ )
751
+
752
+ if result.get("deployed"):
753
+ deployed_count += 1
754
+ deployed_agents.append(agent_id)
755
+ print(f" āœ“ {agent_id}")
756
+ else:
757
+ failed_count += 1
758
+ failed_agents.append(
759
+ {
760
+ "agent_id": agent_id,
761
+ "error": result.get("error", "Unknown"),
762
+ }
763
+ )
764
+ print(f" āœ— {agent_id}: {result.get('error', 'Failed')}")
765
+
766
+ except Exception as e:
767
+ failed_count += 1
768
+ failed_agents.append({"agent_id": agent_id, "error": str(e)})
769
+ print(f" āœ— {agent_id}: {e}")
770
+
771
+ # Summary
772
+ print(f"\n{'=' * 60}")
773
+ print("šŸ“Š Deployment Summary")
774
+ print(f"{'=' * 60}")
775
+ print(f" āœ… Deployed: {deployed_count}")
776
+ print(f" āŒ Failed: {failed_count}")
777
+ print(f" ā­ļø Skipped: {skipped_count} (missing from sources)")
778
+ print(f"{'=' * 60}\n")
779
+
780
+ if failed_agents:
781
+ print("āŒ Failed agents:")
782
+ for failure in failed_agents:
783
+ print(f" • {failure['agent_id']}: {failure['error']}")
784
+ print()
785
+
786
+ if deployed_count > 0:
787
+ print(f"āœ… Successfully deployed {deployed_count} agents!")
788
+ return CommandResult.success_result(
789
+ f"Deployed {deployed_count} agents from preset '{preset_name}'",
790
+ data={
791
+ "preset": preset_name,
792
+ "deployed": deployed_agents,
793
+ "failed": failed_agents,
794
+ "skipped": resolution["missing_agents"],
795
+ },
796
+ )
797
+ return CommandResult.error_result(
798
+ f"No agents deployed from preset '{preset_name}'",
799
+ data={
800
+ "preset": preset_name,
801
+ "failed": failed_agents,
802
+ "skipped": resolution["missing_agents"],
803
+ },
804
+ )
805
+
806
+ except Exception as e:
807
+ self.logger.error(f"Error deploying preset: {e}", exc_info=True)
808
+ print(f"\nāŒ Error deploying preset: {e}")
809
+ return CommandResult.error_result(f"Error deploying preset: {e}")
810
+
811
+ def _clean_agents(self, args) -> CommandResult:
812
+ """Clean deployed agents."""
813
+ try:
814
+ result = self.cleanup_service.clean_deployed_agents()
815
+
816
+ output_format = self._get_output_format(args)
817
+ dry_run = False # Regular clean is not a dry run
818
+
819
+ formatted = self._formatter.format_cleanup_result(
820
+ result, output_format=output_format, dry_run=dry_run
821
+ )
822
+ print(formatted)
823
+
824
+ cleaned_count = result.get("cleaned_count", 0)
825
+ return CommandResult.success_result(
826
+ f"Cleaned {cleaned_count} agents", data=result
827
+ )
828
+
829
+ except Exception as e:
830
+ self.logger.error(f"Error cleaning agents: {e}", exc_info=True)
831
+ return CommandResult.error_result(f"Error cleaning agents: {e}")
832
+
833
+ def _view_agent(self, args) -> CommandResult:
834
+ """View details of a specific agent."""
835
+ try:
836
+ agent_name = getattr(args, "agent_name", None)
837
+ if not agent_name:
838
+ return CommandResult.error_result(
839
+ "Agent name is required for view command"
840
+ )
841
+
842
+ # Get agent details from listing service
843
+ agent_details = self.listing_service.get_agent_details(agent_name)
844
+
845
+ if not agent_details:
846
+ # Try to find the agent to provide helpful error message
847
+ agent = self.listing_service.find_agent(agent_name)
848
+ if not agent:
849
+ return CommandResult.error_result(f"Agent '{agent_name}' not found")
850
+ return CommandResult.error_result(
851
+ f"Could not retrieve details for agent '{agent_name}'"
852
+ )
853
+
854
+ output_format = self._get_output_format(args)
855
+ verbose = getattr(args, "verbose", False)
856
+
857
+ formatted = self._formatter.format_agent_details(
858
+ agent_details, output_format=output_format, verbose=verbose
859
+ )
860
+ print(formatted)
861
+
862
+ return CommandResult.success_result(
863
+ f"Displayed details for {agent_name}", data=agent_details
864
+ )
865
+
866
+ except Exception as e:
867
+ self.logger.error(f"Error viewing agent: {e}", exc_info=True)
868
+ return CommandResult.error_result(f"Error viewing agent: {e}")
869
+
870
+ def _fix_agents(self, args) -> CommandResult:
871
+ """Fix agent frontmatter issues using validation service."""
872
+ try:
873
+ dry_run = getattr(args, "dry_run", False)
874
+ agent_name = getattr(args, "agent_name", None)
875
+ fix_all = getattr(args, "all", False)
876
+ output_format = self._get_output_format(args)
877
+
878
+ # Route to appropriate handler based on input
879
+ if fix_all:
880
+ return self._fix_all_agents(dry_run, output_format)
881
+ if agent_name:
882
+ return self._fix_single_agent(agent_name, dry_run, output_format)
883
+ return self._handle_no_agent_specified(output_format)
884
+
885
+ except Exception as e:
886
+ self.logger.error(f"Error fixing agents: {e}", exc_info=True)
887
+ return CommandResult.error_result(f"Error fixing agents: {e}")
888
+
889
+ def _fix_all_agents(self, dry_run: bool, output_format: str) -> CommandResult:
890
+ """Fix all agents' frontmatter issues."""
891
+ result = self.validation_service.fix_all_agents(dry_run=dry_run)
892
+
893
+ if self._is_structured_format(output_format):
894
+ self._print_structured_output(result, output_format)
102
895
  else:
103
- for agent in verification["agents_found"]:
104
- print(f"šŸ“„ {agent['file']}")
105
- if 'name' in agent:
106
- print(f" Name: {agent['name']}")
107
- print(f" Path: {agent['path']}")
896
+ self._print_all_agents_text_output(result, dry_run)
897
+
898
+ msg = f"{'Would fix' if dry_run else 'Fixed'} {result.get('total_corrections_available' if dry_run else 'total_corrections_made', 0)} issues"
899
+ return CommandResult.success_result(msg, data=result)
900
+
901
+ def _fix_single_agent(
902
+ self, agent_name: str, dry_run: bool, output_format: str
903
+ ) -> CommandResult:
904
+ """Fix a single agent's frontmatter issues."""
905
+ result = self.validation_service.fix_agent_frontmatter(
906
+ agent_name, dry_run=dry_run
907
+ )
908
+
909
+ if not result.get("success"):
910
+ return CommandResult.error_result(
911
+ result.get("error", "Failed to fix agent")
912
+ )
913
+
914
+ if self._is_structured_format(output_format):
915
+ self._print_structured_output(result, output_format)
916
+ else:
917
+ self._print_single_agent_text_output(agent_name, result, dry_run)
918
+
919
+ msg = f"{'Would fix' if dry_run else 'Fixed'} agent '{agent_name}'"
920
+ return CommandResult.success_result(msg, data=result)
921
+
922
+ def _handle_no_agent_specified(self, output_format: str) -> CommandResult:
923
+ """Handle case where no agent is specified."""
924
+ usage_msg = "Please specify an agent name or use --all to fix all agents\nUsage: claude-mpm agents fix [agent_name] [--dry-run] [--all]"
925
+ if self._is_structured_format(output_format):
926
+ return CommandResult.error_result(
927
+ "No agent specified", data={"usage": usage_msg}
928
+ )
929
+ print(f"āŒ {usage_msg}")
930
+ return CommandResult.error_result("No agent specified")
931
+
932
+ def _print_structured_output(self, result: dict, output_format: str) -> None:
933
+ """Print result in JSON or YAML format."""
934
+ formatted = (
935
+ self._formatter.format_as_json(result)
936
+ if str(output_format).lower() == OutputFormat.JSON
937
+ else self._formatter.format_as_yaml(result)
938
+ )
939
+ print(formatted)
940
+
941
+ def _print_all_agents_text_output(self, result: dict, dry_run: bool) -> None:
942
+ """Print text output for all agents fix operation."""
943
+ mode = "DRY RUN" if dry_run else "FIX"
944
+ print(
945
+ f"\nšŸ”§ {mode}: Checking {result.get('total_agents', 0)} agent(s) for frontmatter issues...\n"
946
+ )
947
+
948
+ if result.get("results"):
949
+ for agent_result in result["results"]:
950
+ self._print_agent_result(agent_result, dry_run)
951
+
952
+ self._print_all_agents_summary(result, dry_run)
953
+
954
+ def _print_agent_result(self, agent_result: dict, dry_run: bool) -> None:
955
+ """Print result for a single agent."""
956
+ print(f"šŸ“„ {agent_result['agent']}:")
957
+ if agent_result.get("skipped"):
958
+ print(f" āš ļø Skipped: {agent_result.get('reason', 'Unknown reason')}")
959
+ elif agent_result.get("was_valid"):
960
+ print(" āœ“ No issues found")
961
+ else:
962
+ self._print_agent_issues(agent_result, dry_run)
963
+ print()
964
+
965
+ def _print_agent_issues(self, agent_result: dict, dry_run: bool) -> None:
966
+ """Print issues found for an agent."""
967
+ if agent_result.get("errors_found", 0) > 0:
968
+ print(f" āŒ Errors found: {agent_result['errors_found']}")
969
+ if agent_result.get("warnings_found", 0) > 0:
970
+ print(f" āš ļø Warnings found: {agent_result['warnings_found']}")
971
+
972
+ if dry_run:
973
+ if agent_result.get("corrections_available", 0) > 0:
974
+ print(f" šŸ”§ Would fix: {agent_result['corrections_available']} issues")
975
+ elif agent_result.get("corrections_made", 0) > 0:
976
+ print(f" āœ“ Fixed: {agent_result['corrections_made']} issues")
977
+
978
+ def _print_all_agents_summary(self, result: dict, dry_run: bool) -> None:
979
+ """Print summary for all agents fix operation."""
980
+ print("=" * 80)
981
+ print("SUMMARY:")
982
+ print(f" Agents checked: {result.get('agents_checked', 0)}")
983
+ print(f" Total issues found: {result.get('total_issues_found', 0)}")
984
+
985
+ if dry_run:
986
+ print(
987
+ f" Issues that would be fixed: {result.get('total_corrections_available', 0)}"
988
+ )
989
+ print("\nšŸ’” Run without --dry-run to apply fixes")
990
+ else:
991
+ print(f" Issues fixed: {result.get('total_corrections_made', 0)}")
992
+ if result.get("total_corrections_made", 0) > 0:
993
+ print("\nāœ“ Frontmatter issues have been fixed!")
994
+ print("=" * 80 + "\n")
995
+
996
+ def _print_single_agent_text_output(
997
+ self, agent_name: str, result: dict, dry_run: bool
998
+ ) -> None:
999
+ """Print text output for single agent fix operation."""
1000
+ mode = "DRY RUN" if dry_run else "FIX"
1001
+ print(f"\nšŸ”§ {mode}: Checking agent '{agent_name}' for frontmatter issues...\n")
1002
+
1003
+ print(f"šŸ“„ {agent_name}:")
1004
+ if result.get("was_valid"):
1005
+ print(" āœ“ No issues found")
1006
+ else:
1007
+ self._print_single_agent_issues(result, dry_run)
1008
+ print()
1009
+
1010
+ self._print_single_agent_footer(result, dry_run)
1011
+
1012
+ def _print_single_agent_issues(self, result: dict, dry_run: bool) -> None:
1013
+ """Print issues for a single agent."""
1014
+ if result.get("errors_found"):
1015
+ print(" āŒ Errors:")
1016
+ for error in result["errors_found"]:
1017
+ print(f" - {error}")
1018
+
1019
+ if result.get("warnings_found"):
1020
+ print(" āš ļø Warnings:")
1021
+ for warning in result["warnings_found"]:
1022
+ print(f" - {warning}")
1023
+
1024
+ if dry_run:
1025
+ if result.get("corrections_available"):
1026
+ print(" šŸ”§ Would fix:")
1027
+ for correction in result["corrections_available"]:
1028
+ print(f" - {correction}")
1029
+ elif result.get("corrections_made"):
1030
+ print(" āœ“ Fixed:")
1031
+ for correction in result["corrections_made"]:
1032
+ print(f" - {correction}")
1033
+
1034
+ def _print_single_agent_footer(self, result: dict, dry_run: bool) -> None:
1035
+ """Print footer message for single agent fix."""
1036
+ if dry_run and result.get("corrections_available"):
1037
+ print("šŸ’” Run without --dry-run to apply fixes\n")
1038
+ elif not dry_run and result.get("corrections_made"):
1039
+ print("āœ“ Frontmatter issues have been fixed!\n")
1040
+
1041
+ def _check_agent_dependencies(self, args) -> CommandResult:
1042
+ """Check agent dependencies."""
1043
+ try:
1044
+ agent_name = getattr(args, "agent", None)
1045
+ result = self.dependency_service.check_dependencies(agent_name=agent_name)
1046
+
1047
+ if not result["success"]:
1048
+ if "available_agents" in result:
1049
+ print(f"āŒ Agent '{agent_name}' is not deployed")
1050
+ print(
1051
+ f" Available agents: {', '.join(result['available_agents'])}"
1052
+ )
1053
+ return CommandResult.error_result(
1054
+ result.get("error", "Dependency check failed")
1055
+ )
1056
+
1057
+ # Print the formatted report
1058
+ print(result["report"])
1059
+
1060
+ return CommandResult.success_result(
1061
+ "Dependency check completed", data=result
1062
+ )
1063
+
1064
+ except Exception as e:
1065
+ self.logger.error(f"Error checking dependencies: {e}", exc_info=True)
1066
+ return CommandResult.error_result(f"Error checking dependencies: {e}")
1067
+
1068
+ def _install_agent_dependencies(self, args) -> CommandResult:
1069
+ """Install agent dependencies."""
1070
+ try:
1071
+ agent_name = getattr(args, "agent", None)
1072
+ dry_run = getattr(args, "dry_run", False)
1073
+ result = self.dependency_service.install_dependencies(
1074
+ agent_name=agent_name, dry_run=dry_run
1075
+ )
1076
+
1077
+ if not result["success"]:
1078
+ if "available_agents" in result:
1079
+ print(f"āŒ Agent '{agent_name}' is not deployed")
1080
+ print(
1081
+ f" Available agents: {', '.join(result['available_agents'])}"
1082
+ )
1083
+ return CommandResult.error_result(
1084
+ result.get("error", "Installation failed")
1085
+ )
1086
+
1087
+ if result.get("missing_count") == 0:
1088
+ print("āœ… All Python dependencies are already installed")
1089
+ elif dry_run:
1090
+ print(
1091
+ f"Found {len(result['missing_dependencies'])} missing dependencies:"
1092
+ )
1093
+ for dep in result["missing_dependencies"]:
1094
+ print(f" - {dep}")
1095
+ print("\n--dry-run specified, not installing anything")
1096
+ print(f"Would install: {result['install_command']}")
1097
+ else:
1098
+ print(
1099
+ f"āœ… Successfully installed {len(result.get('installed', []))} dependencies"
1100
+ )
1101
+ if result.get("still_missing"):
1102
+ print(
1103
+ f"āš ļø {len(result['still_missing'])} dependencies still missing after installation"
1104
+ )
1105
+ elif result.get("fully_resolved"):
1106
+ print("āœ… All dependencies verified after installation")
1107
+
1108
+ return CommandResult.success_result(
1109
+ "Dependency installation completed", data=result
1110
+ )
1111
+
1112
+ except Exception as e:
1113
+ self.logger.error(f"Error installing dependencies: {e}", exc_info=True)
1114
+ return CommandResult.error_result(f"Error installing dependencies: {e}")
1115
+
1116
+ def _list_agent_dependencies(self, args) -> CommandResult:
1117
+ """List agent dependencies."""
1118
+ try:
1119
+ output_format = self._get_output_format(args)
1120
+ result = self.dependency_service.list_dependencies(
1121
+ format_type=output_format
1122
+ )
1123
+
1124
+ if not result["success"]:
1125
+ return CommandResult.error_result(result.get("error", "Listing failed"))
1126
+
1127
+ # Format output based on requested format
1128
+ if output_format == "pip":
1129
+ for dep in result["dependencies"]:
1130
+ print(dep)
1131
+ elif str(output_format).lower() == OutputFormat.JSON:
1132
+ print(json.dumps(result["data"], indent=2))
1133
+ else: # text format
1134
+ print("=" * 60)
1135
+ print("DEPENDENCIES FROM DEPLOYED AGENTS")
1136
+ print("=" * 60)
108
1137
  print()
109
-
110
- if verification["warnings"]:
111
- print("\nWarnings:")
112
- for warning in verification["warnings"]:
113
- print(f" āš ļø {warning}")
114
-
115
- else:
116
- # Default: show usage
117
- print("Use --system to list system agents or --deployed to list deployed agents")
118
-
119
-
120
- def _deploy_agents(args, deployment_service, force=False):
121
- """
122
- Deploy system agents.
123
-
124
- WHY: Agents need to be deployed to the working directory for Claude Code to use them.
125
- This function handles both regular and forced deployment.
126
-
127
- Args:
128
- args: Command arguments with optional 'target' path
129
- deployment_service: Agent deployment service instance
130
- force: Whether to force rebuild all agents
131
- """
132
- if force:
133
- print("Force deploying all system agents...")
134
- else:
135
- print("Deploying system agents...")
136
-
137
- results = deployment_service.deploy_agents(args.target, force_rebuild=force)
138
-
139
- if results["deployed"]:
140
- print(f"\nāœ“ Successfully deployed {len(results['deployed'])} agents to {results['target_dir']}")
141
- for agent in results["deployed"]:
142
- print(f" - {agent['name']}")
143
-
144
- if force and results.get("updated", []):
145
- print(f"\nāœ“ Updated {len(results['updated'])} agents")
146
- for agent in results["updated"]:
147
- print(f" - {agent['name']}")
148
-
149
- if force and results.get("skipped", []):
150
- print(f"\nāœ“ Skipped {len(results['skipped'])} up-to-date agents")
151
-
152
- if results["errors"]:
153
- print("\nāŒ Errors during deployment:")
154
- for error in results["errors"]:
155
- print(f" - {error}")
156
-
157
- if force:
158
- # Set environment for force deploy
159
- env_vars = deployment_service.set_claude_environment(
160
- args.target.parent if args.target else None
1138
+
1139
+ if result["python_dependencies"]:
1140
+ print(
1141
+ f"Python Dependencies ({len(result['python_dependencies'])}):"
1142
+ )
1143
+ print("-" * 30)
1144
+ for dep in result["python_dependencies"]:
1145
+ print(f" {dep}")
1146
+ print()
1147
+
1148
+ if result["system_dependencies"]:
1149
+ print(
1150
+ f"System Dependencies ({len(result['system_dependencies'])}):"
1151
+ )
1152
+ print("-" * 30)
1153
+ for dep in result["system_dependencies"]:
1154
+ print(f" {dep}")
1155
+ print()
1156
+
1157
+ print("Per-Agent Dependencies:")
1158
+ print("-" * 30)
1159
+ for agent_id in sorted(result["per_agent"].keys()):
1160
+ deps = result["per_agent"][agent_id]
1161
+ python_count = len(deps.get("python", []))
1162
+ system_count = len(deps.get("system", []))
1163
+ if python_count or system_count:
1164
+ print(
1165
+ f" {agent_id}: {python_count} Python, {system_count} System"
1166
+ )
1167
+
1168
+ return CommandResult.success_result(
1169
+ "Dependency listing completed", data=result
1170
+ )
1171
+
1172
+ except Exception as e:
1173
+ self.logger.error(f"Error listing dependencies: {e}", exc_info=True)
1174
+ return CommandResult.error_result(f"Error listing dependencies: {e}")
1175
+
1176
+ def _fix_agent_dependencies(self, args) -> CommandResult:
1177
+ """Fix agent dependency issues."""
1178
+ try:
1179
+ max_retries = getattr(args, "max_retries", 3)
1180
+ agent_name = getattr(args, "agent", None)
1181
+
1182
+ print("=" * 70)
1183
+ print("FIXING AGENT DEPENDENCIES WITH RETRY LOGIC")
1184
+ print("=" * 70)
1185
+ print()
1186
+
1187
+ result = self.dependency_service.fix_dependencies(
1188
+ max_retries=max_retries, agent_name=agent_name
1189
+ )
1190
+
1191
+ if not result["success"]:
1192
+ if "error" in result and "not deployed" in result["error"]:
1193
+ print(f"āŒ {result['error']}")
1194
+ return CommandResult.error_result(result.get("error", "Fix failed"))
1195
+
1196
+ if result.get("message") == "No deployed agents found":
1197
+ print("No deployed agents found")
1198
+ return CommandResult.success_result("No agents to fix")
1199
+
1200
+ if result.get("message") == "All dependencies are already satisfied":
1201
+ print("\nāœ… All dependencies are already satisfied!")
1202
+ return CommandResult.success_result("All dependencies satisfied")
1203
+
1204
+ # Show what's missing
1205
+ if result.get("missing_python"):
1206
+ print(f"\nāŒ Missing Python packages: {len(result['missing_python'])}")
1207
+ for pkg in result["missing_python"][:10]:
1208
+ print(f" - {pkg}")
1209
+ if len(result["missing_python"]) > 10:
1210
+ print(f" ... and {len(result['missing_python']) - 10} more")
1211
+
1212
+ if result.get("missing_system"):
1213
+ print(f"\nāŒ Missing system commands: {len(result['missing_system'])}")
1214
+ for cmd in result["missing_system"]:
1215
+ print(f" - {cmd}")
1216
+ print("\nāš ļø System dependencies must be installed manually:")
1217
+ print(f" macOS: brew install {' '.join(result['missing_system'])}")
1218
+ print(f" Ubuntu: apt-get install {' '.join(result['missing_system'])}")
1219
+
1220
+ # Show incompatible packages
1221
+ if result.get("incompatible"):
1222
+ print(
1223
+ f"\nāš ļø Skipping {len(result['incompatible'])} incompatible packages:"
1224
+ )
1225
+ for pkg in result["incompatible"][:5]:
1226
+ print(f" - {pkg}")
1227
+ if len(result["incompatible"]) > 5:
1228
+ print(f" ... and {len(result['incompatible']) - 5} more")
1229
+
1230
+ # Show installation results
1231
+ if result.get("fixed_python") or result.get("failed_python"):
1232
+ print("\n" + "=" * 70)
1233
+ print("INSTALLATION RESULTS:")
1234
+ print("=" * 70)
1235
+
1236
+ if result.get("fixed_python"):
1237
+ print(
1238
+ f"āœ… Successfully installed: {len(result['fixed_python'])} packages"
1239
+ )
1240
+
1241
+ if result.get("failed_python"):
1242
+ print(
1243
+ f"āŒ Failed to install: {len(result['failed_python'])} packages"
1244
+ )
1245
+ errors = result.get("errors", {})
1246
+ for pkg in result["failed_python"]:
1247
+ print(f" - {pkg}: {errors.get(pkg, 'Unknown error')}")
1248
+
1249
+ # Final verification
1250
+ if result.get("still_missing") is not None:
1251
+ if not result["still_missing"]:
1252
+ print("\nāœ… All Python dependencies are now satisfied!")
1253
+ else:
1254
+ print(
1255
+ f"\nāš ļø Still missing {len(result['still_missing'])} packages"
1256
+ )
1257
+ print("\nTry running again or install manually:")
1258
+ missing_sample = result["still_missing"][:3]
1259
+ print(f" pip install {' '.join(missing_sample)}")
1260
+
1261
+ print("\n" + "=" * 70)
1262
+ print("DONE")
1263
+ print("=" * 70)
1264
+
1265
+ return CommandResult.success_result("Dependency fix completed", data=result)
1266
+
1267
+ except Exception as e:
1268
+ self.logger.error(f"Error fixing dependencies: {e}", exc_info=True)
1269
+ return CommandResult.error_result(f"Error fixing dependencies: {e}")
1270
+
1271
+ def _handle_cleanup_command(self, args) -> CommandResult:
1272
+ """Handle cleanup command with proper result wrapping."""
1273
+ exit_code = handle_agents_cleanup(args)
1274
+ return CommandResult(
1275
+ success=exit_code == 0,
1276
+ message=(
1277
+ "Agent cleanup complete" if exit_code == 0 else "Agent cleanup failed"
1278
+ ),
161
1279
  )
162
- print(f"\nāœ“ Set Claude environment variables:")
163
- for key, value in env_vars.items():
164
- print(f" - {key}={value}")
165
1280
 
1281
+ def _cleanup_orphaned_agents(self, args) -> CommandResult:
1282
+ """Clean up orphaned agents that don't have templates."""
1283
+ try:
1284
+ # Determine agents directory
1285
+ agents_dir = None
1286
+ if hasattr(args, "agents_dir") and args.agents_dir:
1287
+ agents_dir = args.agents_dir
1288
+
1289
+ # Determine if we're doing a dry run
1290
+ dry_run = getattr(args, "dry_run", True)
1291
+ if hasattr(args, "force") and args.force:
1292
+ dry_run = False
1293
+
1294
+ # Perform cleanup using the cleanup service
1295
+ results = self.cleanup_service.clean_orphaned_agents(
1296
+ agents_dir=agents_dir, dry_run=dry_run
1297
+ )
1298
+
1299
+ output_format = self._get_output_format(args)
1300
+
1301
+ formatted = self._formatter.format_cleanup_result(
1302
+ results, output_format=output_format, dry_run=dry_run
1303
+ )
1304
+ print(formatted)
1305
+
1306
+ # Determine success/error based on results
1307
+ if results.get("errors") and not dry_run:
1308
+ return CommandResult.error_result(
1309
+ f"Cleanup completed with {len(results['errors'])} errors",
1310
+ data=results,
1311
+ )
1312
+
1313
+ return CommandResult.success_result(
1314
+ f"Cleanup {'preview' if dry_run else 'completed'}", data=results
1315
+ )
1316
+
1317
+ except Exception as e:
1318
+ self.logger.error(f"Error during cleanup: {e}", exc_info=True)
1319
+ return CommandResult.error_result(f"Error during cleanup: {e}")
1320
+
1321
+ def _create_local_agent(self, args) -> CommandResult:
1322
+ """Create a new local agent template."""
1323
+ try:
1324
+ if getattr(args, "interactive", False):
1325
+ # Launch interactive wizard
1326
+ from ..interactive.agent_wizard import run_interactive_agent_wizard
1327
+
1328
+ exit_code = run_interactive_agent_wizard()
1329
+ if exit_code == 0:
1330
+ return CommandResult.success_result("Agent created successfully")
1331
+ return CommandResult.error_result("Agent creation cancelled or failed")
1332
+
1333
+ # Non-interactive creation
1334
+ from ...services.agents.local_template_manager import (
1335
+ LocalAgentTemplateManager,
1336
+ )
1337
+
1338
+ agent_id = getattr(args, "agent_id", None)
1339
+ if not agent_id:
1340
+ return CommandResult.error_result(
1341
+ "--agent-id is required for non-interactive creation"
1342
+ )
1343
+
1344
+ manager = LocalAgentTemplateManager()
1345
+ name = getattr(args, "name", agent_id.replace("-", " ").title())
1346
+ model = getattr(args, "model", "sonnet")
1347
+ inherit_from = getattr(args, "inherit_from", None)
1348
+
1349
+ # Create basic template
1350
+ template = manager.create_local_template(
1351
+ agent_id=agent_id,
1352
+ name=name,
1353
+ description=f"Local agent: {name}",
1354
+ instructions="# Agent Instructions\n\nCustomize this agent's behavior here.",
1355
+ model=model,
1356
+ parent_agent=inherit_from,
1357
+ tier="project",
1358
+ )
1359
+
1360
+ if template:
1361
+ return CommandResult.success_result(
1362
+ f"Created local agent '{agent_id}' in .claude-mpm/agents/",
1363
+ data={
1364
+ "agent_id": agent_id,
1365
+ "path": f".claude-mpm/agents/{agent_id}.json",
1366
+ },
1367
+ )
1368
+ return CommandResult.error_result("Failed to create agent template")
1369
+
1370
+ except Exception as e:
1371
+ self.logger.error(f"Error creating local agent: {e}", exc_info=True)
1372
+ return CommandResult.error_result(f"Error creating local agent: {e}")
1373
+
1374
+ def _edit_local_agent(self, args) -> CommandResult:
1375
+ """Edit a local agent template."""
1376
+ try:
1377
+ agent_id = getattr(args, "agent_id", None)
1378
+ if not agent_id:
1379
+ return CommandResult.error_result("agent_id is required")
166
1380
 
167
- def _clean_agents(args, deployment_service):
1381
+ import os
1382
+ import subprocess
1383
+
1384
+ from ...services.agents.local_template_manager import (
1385
+ LocalAgentTemplateManager,
1386
+ )
1387
+
1388
+ manager = LocalAgentTemplateManager()
1389
+ template = manager.get_local_template(agent_id)
1390
+
1391
+ if not template:
1392
+ return CommandResult.error_result(f"Local agent '{agent_id}' not found")
1393
+
1394
+ # Get template file path
1395
+ template_file = None
1396
+ if template.tier == "project":
1397
+ template_file = manager.project_agents_dir / f"{agent_id}.json"
1398
+ else:
1399
+ template_file = manager.user_agents_dir / f"{agent_id}.json"
1400
+
1401
+ if not template_file or not template_file.exists():
1402
+ return CommandResult.error_result(
1403
+ f"Template file not found for '{agent_id}'"
1404
+ )
1405
+
1406
+ if getattr(args, "interactive", False):
1407
+ # Launch interactive editor
1408
+ from ..interactive.agent_wizard import AgentWizard
1409
+
1410
+ wizard = AgentWizard()
1411
+ success, message = wizard._edit_agent_config(template)
1412
+ if success:
1413
+ return CommandResult.success_result(message)
1414
+ return CommandResult.error_result(message)
1415
+
1416
+ # Use system editor
1417
+ editor = getattr(args, "editor", None) or os.environ.get("EDITOR", "nano")
1418
+ subprocess.run([editor, str(template_file)], check=True)
1419
+ return CommandResult.success_result(
1420
+ f"Agent '{agent_id}' edited successfully"
1421
+ )
1422
+
1423
+ except subprocess.CalledProcessError:
1424
+ return CommandResult.error_result("Editor exited with error")
1425
+ except Exception as e:
1426
+ self.logger.error(f"Error editing local agent: {e}", exc_info=True)
1427
+ return CommandResult.error_result(f"Error editing local agent: {e}")
1428
+
1429
+ def _delete_local_agent(self, args) -> CommandResult:
1430
+ """Delete local agent templates."""
1431
+ try:
1432
+ agent_ids = getattr(args, "agent_ids", [])
1433
+ if not agent_ids:
1434
+ return CommandResult.error_result("No agent IDs specified")
1435
+
1436
+ from ...services.agents.local_template_manager import (
1437
+ LocalAgentTemplateManager,
1438
+ )
1439
+
1440
+ manager = LocalAgentTemplateManager()
1441
+ force = getattr(args, "force", False)
1442
+ keep_deployment = getattr(args, "keep_deployment", False)
1443
+ backup = getattr(args, "backup", False)
1444
+
1445
+ # Confirmation if not forced
1446
+ if not force:
1447
+ print(f"\nāš ļø This will delete {len(agent_ids)} agent(s):")
1448
+ for agent_id in agent_ids:
1449
+ print(f" - {agent_id}")
1450
+ confirm = input("\nAre you sure? [y/N]: ").strip().lower()
1451
+ if confirm not in ["y", "yes"]:
1452
+ return CommandResult.error_result("Deletion cancelled")
1453
+
1454
+ # Delete agents
1455
+ if len(agent_ids) == 1:
1456
+ result = manager.delete_local_template(
1457
+ agent_id=agent_ids[0],
1458
+ tier="all",
1459
+ delete_deployment=not keep_deployment,
1460
+ backup_first=backup,
1461
+ )
1462
+ if result["success"]:
1463
+ message = f"Successfully deleted agent '{agent_ids[0]}'"
1464
+ if result["backup_location"]:
1465
+ message += f"\nBackup saved to: {result['backup_location']}"
1466
+ return CommandResult.success_result(message, data=result)
1467
+ return CommandResult.error_result(
1468
+ f"Failed to delete agent: {', '.join(result['errors'])}"
1469
+ )
1470
+ results = manager.delete_multiple_templates(
1471
+ agent_ids=agent_ids,
1472
+ tier="all",
1473
+ delete_deployment=not keep_deployment,
1474
+ backup_first=backup,
1475
+ )
1476
+
1477
+ message = ""
1478
+ if results["successful"]:
1479
+ message = (
1480
+ f"Successfully deleted {len(results['successful'])} agent(s):\n"
1481
+ )
1482
+ for agent_id in results["successful"]:
1483
+ message += f" - {agent_id}\n"
1484
+
1485
+ if results["failed"]:
1486
+ if message:
1487
+ message += "\n"
1488
+ message += f"Failed to delete {len(results['failed'])} agent(s):\n"
1489
+ for agent_id in results["failed"]:
1490
+ errors = results["details"][agent_id]["errors"]
1491
+ message += f" - {agent_id}: {', '.join(errors)}\n"
1492
+
1493
+ if results["successful"]:
1494
+ return CommandResult.success_result(message.strip(), data=results)
1495
+ return CommandResult.error_result(message.strip(), data=results)
1496
+
1497
+ except Exception as e:
1498
+ self.logger.error(f"Error deleting local agents: {e}", exc_info=True)
1499
+ return CommandResult.error_result(f"Error deleting local agents: {e}")
1500
+
1501
+ def _manage_local_agents(self, args) -> CommandResult:
1502
+ """Redirect to main configuration interface (DEPRECATED)."""
1503
+ try:
1504
+ from rich.console import Console
1505
+ from rich.prompt import Confirm
1506
+
1507
+ console = Console()
1508
+
1509
+ console.print(
1510
+ "\n[bold cyan]╭─────────────────────────────────────────╮[/bold cyan]"
1511
+ )
1512
+ console.print(
1513
+ "[bold cyan]│ Agent Management Has Moved! │[/bold cyan]"
1514
+ )
1515
+ console.print(
1516
+ "[bold cyan]╰─────────────────────────────────────────╯[/bold cyan]\n"
1517
+ )
1518
+
1519
+ console.print("For a better experience with integrated configuration:")
1520
+ console.print(" • Agent management")
1521
+ console.print(" • Skills management")
1522
+ console.print(" • Template editing")
1523
+ console.print(" • Behavior configuration")
1524
+ console.print(" • Startup settings\n")
1525
+
1526
+ console.print("Please use: [bold green]claude-mpm config[/bold green]\n")
1527
+
1528
+ if Confirm.ask("Launch configuration interface now?", default=True):
1529
+ # Import and run config command directly
1530
+ from claude_mpm.cli.commands.configure import ConfigureCommand
1531
+
1532
+ config_cmd = ConfigureCommand()
1533
+ return config_cmd.execute(args)
1534
+ console.print(
1535
+ "\n[dim]Run 'claude-mpm config' anytime to access agent management[/dim]"
1536
+ )
1537
+ return CommandResult.success_result("Redirected to config interface")
1538
+
1539
+ except Exception as e:
1540
+ self.logger.error(f"Error redirecting to config: {e}", exc_info=True)
1541
+ return CommandResult.error_result(f"Error redirecting to config: {e}")
1542
+
1543
+ def _configure_deployment(self, args) -> CommandResult:
1544
+ """Configure agent deployment settings."""
1545
+ try:
1546
+ from pathlib import Path
1547
+
1548
+ import yaml
1549
+
1550
+ from claude_mpm.core.config import Config
1551
+
1552
+ config = Config()
1553
+ config_path = Path.cwd() / ".claude-mpm" / "configuration.yaml"
1554
+
1555
+ # Handle show command
1556
+ if getattr(args, "show", False):
1557
+ from ...services.agents.deployment.deployment_config_loader import (
1558
+ DeploymentConfigLoader,
1559
+ )
1560
+
1561
+ loader = DeploymentConfigLoader(self.logger)
1562
+ settings = loader.get_deployment_settings(config)
1563
+
1564
+ print("\nšŸ“‹ Agent Deployment Configuration")
1565
+ print("=" * 50)
1566
+ print(f"Configuration file: {config_path}")
1567
+ print("\nšŸ”§ Deployment Settings:")
1568
+ print(f" Deploy system agents: {settings['deploy_system_agents']}")
1569
+ print(f" Deploy local agents: {settings['deploy_local_agents']}")
1570
+ print(f" Deploy user agents: {settings['deploy_user_agents']}")
1571
+ print(
1572
+ f" Prefer local over system: {settings['prefer_local_over_system']}"
1573
+ )
1574
+ print(f" Version comparison: {settings['version_comparison']}")
1575
+
1576
+ if settings["enabled_agents"]:
1577
+ print(
1578
+ f"\nāœ… Enabled agents: {', '.join(settings['enabled_agents'])}"
1579
+ )
1580
+ else:
1581
+ print("\nāœ… Enabled agents: All (no restrictions)")
1582
+
1583
+ if settings["disabled_agents"]:
1584
+ print(
1585
+ f"āŒ Disabled agents: {', '.join(settings['disabled_agents'])}"
1586
+ )
1587
+ else:
1588
+ print("āŒ Disabled agents: None")
1589
+
1590
+ print("\n" + "=" * 50)
1591
+ return CommandResult.success_result(
1592
+ "Displayed deployment configuration"
1593
+ )
1594
+
1595
+ # Handle interactive mode
1596
+ if getattr(args, "interactive", False):
1597
+ return self._configure_deployment_interactive(config_path)
1598
+
1599
+ # Load current configuration
1600
+ if not config_path.exists():
1601
+ config_path.parent.mkdir(parents=True, exist_ok=True)
1602
+ config_data = {}
1603
+ else:
1604
+ with config_path.open() as f:
1605
+ config_data = yaml.safe_load(f) or {}
1606
+
1607
+ # Ensure agent_deployment section exists
1608
+ if "agent_deployment" not in config_data:
1609
+ config_data["agent_deployment"] = {}
1610
+
1611
+ modified = False
1612
+
1613
+ # Handle enable/disable operations
1614
+ if getattr(args, "enable_all", False):
1615
+ config_data["agent_deployment"]["enabled_agents"] = []
1616
+ config_data["agent_deployment"]["disabled_agents"] = []
1617
+ print("āœ… Enabled all agents for deployment")
1618
+ modified = True
1619
+
1620
+ if getattr(args, "enable_system", False):
1621
+ config_data["agent_deployment"]["deploy_system_agents"] = True
1622
+ print("āœ… Enabled system agents for deployment")
1623
+ modified = True
1624
+
1625
+ if getattr(args, "disable_system", False):
1626
+ config_data["agent_deployment"]["deploy_system_agents"] = False
1627
+ print("āŒ Disabled system agents from deployment")
1628
+ modified = True
1629
+
1630
+ if getattr(args, "enable_local", False):
1631
+ config_data["agent_deployment"]["deploy_local_agents"] = True
1632
+ print("āœ… Enabled local agents for deployment")
1633
+ modified = True
1634
+
1635
+ if getattr(args, "disable_local", False):
1636
+ config_data["agent_deployment"]["deploy_local_agents"] = False
1637
+ print("āŒ Disabled local agents from deployment")
1638
+ modified = True
1639
+
1640
+ if getattr(args, "enable", None):
1641
+ enabled = config_data["agent_deployment"].get("enabled_agents", [])
1642
+ disabled = config_data["agent_deployment"].get("disabled_agents", [])
1643
+
1644
+ for agent_id in args.enable:
1645
+ if agent_id not in enabled:
1646
+ enabled.append(agent_id)
1647
+ if agent_id in disabled:
1648
+ disabled.remove(agent_id)
1649
+
1650
+ config_data["agent_deployment"]["enabled_agents"] = enabled
1651
+ config_data["agent_deployment"]["disabled_agents"] = disabled
1652
+ print(f"āœ… Enabled agents: {', '.join(args.enable)}")
1653
+ modified = True
1654
+
1655
+ if getattr(args, "disable", None):
1656
+ disabled = config_data["agent_deployment"].get("disabled_agents", [])
1657
+
1658
+ for agent_id in args.disable:
1659
+ if agent_id not in disabled:
1660
+ disabled.append(agent_id)
1661
+
1662
+ config_data["agent_deployment"]["disabled_agents"] = disabled
1663
+ print(f"āŒ Disabled agents: {', '.join(args.disable)}")
1664
+ modified = True
1665
+
1666
+ # Save configuration if modified
1667
+ if modified:
1668
+ with config_path.open("w") as f:
1669
+ yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)
1670
+ print(f"\nšŸ’¾ Configuration saved to {config_path}")
1671
+ return CommandResult.success_result("Deployment configuration updated")
1672
+
1673
+ # If no modifications were made and not showing, display help
1674
+ if not getattr(args, "show", False):
1675
+ print("No configuration changes specified. Use --help for options.")
1676
+ return CommandResult.success_result("No changes made")
1677
+
1678
+ except Exception as e:
1679
+ self.logger.error(f"Error configuring deployment: {e}", exc_info=True)
1680
+ return CommandResult.error_result(f"Error configuring deployment: {e}")
1681
+
1682
+ def _configure_deployment_interactive(self, config_path: Path) -> CommandResult:
1683
+ """Interactive mode for configuring agent deployment."""
1684
+ try:
1685
+ import yaml
1686
+
1687
+ from ...utils.ui_helpers import (
1688
+ prompt_choice,
1689
+ prompt_multiselect,
1690
+ prompt_yes_no,
1691
+ )
1692
+
1693
+ # Load current configuration
1694
+ if config_path.exists():
1695
+ with config_path.open() as f:
1696
+ config_data = yaml.safe_load(f) or {}
1697
+ else:
1698
+ config_data = {}
1699
+
1700
+ if "agent_deployment" not in config_data:
1701
+ config_data["agent_deployment"] = {}
1702
+
1703
+ settings = config_data["agent_deployment"]
1704
+
1705
+ print("\nšŸŽ® Interactive Agent Deployment Configuration")
1706
+ print("=" * 50)
1707
+
1708
+ # Configure source types
1709
+ settings["deploy_system_agents"] = prompt_yes_no(
1710
+ "Deploy system agents?",
1711
+ default=settings.get("deploy_system_agents", True),
1712
+ )
1713
+
1714
+ settings["deploy_local_agents"] = prompt_yes_no(
1715
+ "Deploy local project agents?",
1716
+ default=settings.get("deploy_local_agents", True),
1717
+ )
1718
+
1719
+ settings["deploy_user_agents"] = prompt_yes_no(
1720
+ "Deploy user-level agents?",
1721
+ default=settings.get("deploy_user_agents", True),
1722
+ )
1723
+
1724
+ # Configure version behavior
1725
+ settings["prefer_local_over_system"] = prompt_yes_no(
1726
+ "Should local agents override system agents with same ID?",
1727
+ default=settings.get("prefer_local_over_system", True),
1728
+ )
1729
+
1730
+ settings["version_comparison"] = prompt_yes_no(
1731
+ "Compare versions across sources and deploy highest?",
1732
+ default=settings.get("version_comparison", True),
1733
+ )
1734
+
1735
+ # Configure specific agents
1736
+ choice = prompt_choice(
1737
+ "How would you like to configure specific agents?",
1738
+ [
1739
+ "No restrictions (all agents enabled)",
1740
+ "Specify disabled agents",
1741
+ "Specify enabled agents only",
1742
+ ],
1743
+ )
1744
+
1745
+ if choice == "No restrictions (all agents enabled)":
1746
+ settings["enabled_agents"] = []
1747
+ settings["disabled_agents"] = []
1748
+ elif choice == "Specify disabled agents":
1749
+ # Get list of available agents
1750
+ from ...services.agents.listing_service import AgentListingService
1751
+
1752
+ listing_service = AgentListingService()
1753
+ agents, _ = listing_service.list_all_agents()
1754
+ agent_ids = sorted({agent.name for agent in agents})
1755
+
1756
+ if agent_ids:
1757
+ disabled = prompt_multiselect(
1758
+ "Select agents to disable:",
1759
+ agent_ids,
1760
+ default=settings.get("disabled_agents", []),
1761
+ )
1762
+ settings["disabled_agents"] = disabled
1763
+ settings["enabled_agents"] = []
1764
+ else:
1765
+ print("No agents found to configure")
1766
+ else: # Specify enabled agents only
1767
+ from ...services.agents.listing_service import AgentListingService
1768
+
1769
+ listing_service = AgentListingService()
1770
+ agents, _ = listing_service.list_all_agents()
1771
+ agent_ids = sorted({agent.name for agent in agents})
1772
+
1773
+ if agent_ids:
1774
+ enabled = prompt_multiselect(
1775
+ "Select agents to enable (others will be disabled):",
1776
+ agent_ids,
1777
+ default=settings.get("enabled_agents", []),
1778
+ )
1779
+ settings["enabled_agents"] = enabled
1780
+ settings["disabled_agents"] = []
1781
+ else:
1782
+ print("No agents found to configure")
1783
+
1784
+ # Save configuration
1785
+ config_data["agent_deployment"] = settings
1786
+
1787
+ # Ensure parent directory exists
1788
+ config_path.parent.mkdir(parents=True, exist_ok=True)
1789
+
1790
+ with config_path.open("w") as f:
1791
+ yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)
1792
+
1793
+ print(f"\nāœ… Configuration saved to {config_path}")
1794
+
1795
+ # Show summary
1796
+ print("\nšŸ“‹ New Configuration Summary:")
1797
+ print(
1798
+ f" System agents: {'Enabled' if settings.get('deploy_system_agents', True) else 'Disabled'}"
1799
+ )
1800
+ print(
1801
+ f" Local agents: {'Enabled' if settings.get('deploy_local_agents', True) else 'Disabled'}"
1802
+ )
1803
+ print(
1804
+ f" User agents: {'Enabled' if settings.get('deploy_user_agents', True) else 'Disabled'}"
1805
+ )
1806
+
1807
+ if settings.get("enabled_agents"):
1808
+ print(f" Enabled specific: {', '.join(settings['enabled_agents'])}")
1809
+ elif settings.get("disabled_agents"):
1810
+ print(f" Disabled specific: {', '.join(settings['disabled_agents'])}")
1811
+ else:
1812
+ print(" All agents enabled")
1813
+
1814
+ return CommandResult.success_result("Interactive configuration completed")
1815
+
1816
+ except KeyboardInterrupt:
1817
+ print("\n\nConfiguration cancelled.")
1818
+ return CommandResult.error_result("Configuration cancelled by user")
1819
+ except Exception as e:
1820
+ self.logger.error(f"Error in interactive configuration: {e}", exc_info=True)
1821
+ return CommandResult.error_result(
1822
+ f"Error in interactive configuration: {e}"
1823
+ )
1824
+
1825
+ def _migrate_to_project(self, args) -> CommandResult:
1826
+ """Migrate user-level agents to project-level.
1827
+
1828
+ DEPRECATION: User-level agents (~/.claude-mpm/agents/) are deprecated and
1829
+ will be removed in v5.0.0. This command migrates them to project-level
1830
+ (.claude-mpm/agents/) where they belong.
1831
+
1832
+ Args:
1833
+ args: Command arguments with dry_run and force flags
1834
+
1835
+ Returns:
1836
+ CommandResult with migration status
1837
+ """
1838
+ import shutil
1839
+
1840
+ try:
1841
+ user_agents_dir = Path.home() / ".claude-mpm" / "agents"
1842
+ project_agents_dir = Path.cwd() / ".claude-mpm" / "agents"
1843
+
1844
+ dry_run = getattr(args, "dry_run", False)
1845
+ force = getattr(args, "force", False)
1846
+
1847
+ # Check if user agents directory exists
1848
+ if not user_agents_dir.exists():
1849
+ print("āœ… No user-level agents found. Nothing to migrate.")
1850
+ return CommandResult.success_result("No user-level agents to migrate")
1851
+
1852
+ # Find all user agent files
1853
+ user_agent_files = list(user_agents_dir.glob("*.json")) + list(
1854
+ user_agents_dir.glob("*.md")
1855
+ )
1856
+
1857
+ if not user_agent_files:
1858
+ print("āœ… No user-level agents found. Nothing to migrate.")
1859
+ return CommandResult.success_result("No user-level agents to migrate")
1860
+
1861
+ # Display what we found
1862
+ print(f"\nšŸ“¦ Found {len(user_agent_files)} user-level agent(s) to migrate:")
1863
+ for agent_file in user_agent_files:
1864
+ print(f" - {agent_file.name}")
1865
+
1866
+ if dry_run:
1867
+ print("\nšŸ” DRY RUN: Would migrate to:")
1868
+ print(f" → {project_agents_dir}")
1869
+ print("\nRun without --dry-run to perform the migration.")
1870
+ return CommandResult.success_result(
1871
+ "Dry run completed",
1872
+ data={
1873
+ "user_agents_found": len(user_agent_files),
1874
+ "target_directory": str(project_agents_dir),
1875
+ },
1876
+ )
1877
+
1878
+ # Create project agents directory
1879
+ project_agents_dir.mkdir(parents=True, exist_ok=True)
1880
+
1881
+ # Migrate agents
1882
+ migrated = 0
1883
+ skipped = 0
1884
+ errors = []
1885
+
1886
+ for agent_file in user_agent_files:
1887
+ target_file = project_agents_dir / agent_file.name
1888
+
1889
+ # Check for conflicts
1890
+ if target_file.exists() and not force:
1891
+ print(f"āš ļø Skipping {agent_file.name} (already exists in project)")
1892
+ print(" Use --force to overwrite existing agents")
1893
+ skipped += 1
1894
+ continue
1895
+
1896
+ try:
1897
+ # Copy agent to project directory
1898
+ shutil.copy2(agent_file, target_file)
1899
+ migrated += 1
1900
+ print(f"āœ… Migrated {agent_file.name}")
1901
+ except Exception as e:
1902
+ error_msg = f"Failed to migrate {agent_file.name}: {e}"
1903
+ errors.append(error_msg)
1904
+ print(f"āŒ {error_msg}")
1905
+
1906
+ # Summary
1907
+ print("\nšŸ“Š Migration Summary:")
1908
+ print(f" āœ… Migrated: {migrated}/{len(user_agent_files)}")
1909
+ if skipped > 0:
1910
+ print(f" ā­ļø Skipped: {skipped} (already exist)")
1911
+ if errors:
1912
+ print(f" āŒ Errors: {len(errors)}")
1913
+
1914
+ if migrated > 0:
1915
+ print(f"\nāœ… Successfully migrated {migrated} agent(s) to:")
1916
+ print(f" {project_agents_dir}")
1917
+ print(
1918
+ "\nāš ļø IMPORTANT: Verify agents work correctly, then remove user-level agents:"
1919
+ )
1920
+ print(f" rm -rf {user_agents_dir}")
1921
+ print("\nšŸ’” Why this change?")
1922
+ print(" - Project isolation: Each project has its own agents")
1923
+ print(" - Version control: Agents can be versioned with your code")
1924
+ print(" - Team consistency: Everyone uses the same agents")
1925
+
1926
+ return CommandResult.success_result(
1927
+ f"Migrated {migrated} agents",
1928
+ data={
1929
+ "migrated": migrated,
1930
+ "skipped": skipped,
1931
+ "errors": errors,
1932
+ "total": len(user_agent_files),
1933
+ },
1934
+ )
1935
+
1936
+ except Exception as e:
1937
+ self.logger.error(f"Error migrating agents: {e}", exc_info=True)
1938
+ return CommandResult.error_result(f"Error migrating agents: {e}")
1939
+
1940
+ def _deploy_minimal_configuration(self, args) -> CommandResult:
1941
+ """Deploy minimal configuration (6 core agents).
1942
+
1943
+ Part of Phase 3 (1M-382): Agent Selection Modes.
1944
+ Deploy exactly 6 agents for basic Claude MPM workflow:
1945
+ engineer, documentation, qa, research, ops, ticketing.
1946
+ """
1947
+ try:
1948
+ from ...config.agent_sources import AgentSourceConfiguration
1949
+ from ...services.agents.agent_selection_service import AgentSelectionService
1950
+ from ...services.agents.single_tier_deployment_service import (
1951
+ SingleTierDeploymentService,
1952
+ )
1953
+
1954
+ # Initialize services
1955
+ config = AgentSourceConfiguration.load()
1956
+ deployment_dir = Path.home() / ".claude" / "agents"
1957
+ deployment_service = SingleTierDeploymentService(config, deployment_dir)
1958
+ selection_service = AgentSelectionService(deployment_service)
1959
+
1960
+ # Get dry_run flag
1961
+ dry_run = getattr(args, "dry_run", False)
1962
+
1963
+ # Deploy minimal configuration
1964
+ print("šŸŽÆ Deploying minimal configuration (6 core agents)...")
1965
+ if dry_run:
1966
+ print("šŸ” DRY RUN MODE - No agents will be deployed\n")
1967
+
1968
+ result = selection_service.deploy_minimal_configuration(dry_run=dry_run)
1969
+
1970
+ # Format output
1971
+ output_format = self._get_output_format(args)
1972
+ if self._is_structured_format(output_format):
1973
+ formatted = (
1974
+ self._formatter.format_as_json(result)
1975
+ if str(output_format).lower() == OutputFormat.JSON
1976
+ else self._formatter.format_as_yaml(result)
1977
+ )
1978
+ print(formatted)
1979
+ return CommandResult.success_result(
1980
+ f"Minimal configuration {result['status']}", data=result
1981
+ )
1982
+
1983
+ # Text output
1984
+ print(f"\n{'=' * 60}")
1985
+ print(f"Status: {result['status'].upper()}")
1986
+ print(f"Mode: {result['mode']}")
1987
+ print(f"{'=' * 60}")
1988
+ print(
1989
+ f"\nšŸ“Š Summary: {result['deployed_count']} deployed, "
1990
+ f"{result['failed_count']} failed, {result['missing_count']} missing"
1991
+ )
1992
+
1993
+ if result["deployed_agents"]:
1994
+ print(f"\nāœ… Deployed agents ({len(result['deployed_agents'])}):")
1995
+ for agent in result["deployed_agents"]:
1996
+ print(f" • {agent}")
1997
+
1998
+ if result["failed_agents"]:
1999
+ print(f"\nāŒ Failed agents ({len(result['failed_agents'])}):")
2000
+ for agent in result["failed_agents"]:
2001
+ print(f" • {agent}")
2002
+
2003
+ if result["missing_agents"]:
2004
+ print(f"\nāš ļø Missing agents ({len(result['missing_agents'])}):")
2005
+ for agent in result["missing_agents"]:
2006
+ print(f" • {agent}")
2007
+ print("\nThese agents are not available in configured sources.")
2008
+
2009
+ if dry_run:
2010
+ print(
2011
+ "\nšŸ’” This was a dry run. Run without --dry-run to deploy agents."
2012
+ )
2013
+
2014
+ return CommandResult.success_result(
2015
+ f"Minimal configuration {result['status']}", data=result
2016
+ )
2017
+
2018
+ except Exception as e:
2019
+ self.logger.error(
2020
+ f"Error deploying minimal configuration: {e}", exc_info=True
2021
+ )
2022
+ return CommandResult.error_result(
2023
+ f"Error deploying minimal configuration: {e}"
2024
+ )
2025
+
2026
+ def _deploy_auto_configure(self, args) -> CommandResult:
2027
+ """Auto-detect toolchain and deploy matching agents.
2028
+
2029
+ Part of Phase 3 (1M-382): Agent Selection Modes.
2030
+ Detect project toolchain (languages, frameworks, build tools) and
2031
+ deploy matching specialized agents.
2032
+ """
2033
+ try:
2034
+ from ...config.agent_sources import AgentSourceConfiguration
2035
+ from ...services.agents.agent_selection_service import AgentSelectionService
2036
+ from ...services.agents.single_tier_deployment_service import (
2037
+ SingleTierDeploymentService,
2038
+ )
2039
+
2040
+ # Initialize services
2041
+ config = AgentSourceConfiguration.load()
2042
+ deployment_dir = Path.home() / ".claude" / "agents"
2043
+ deployment_service = SingleTierDeploymentService(config, deployment_dir)
2044
+ selection_service = AgentSelectionService(deployment_service)
2045
+
2046
+ # Get arguments
2047
+ project_path = getattr(args, "path", Path.cwd())
2048
+ dry_run = getattr(args, "dry_run", False)
2049
+
2050
+ # Deploy auto-configure
2051
+ print(f"šŸ” Auto-detecting toolchain in {project_path}...")
2052
+ if dry_run:
2053
+ print("šŸ” DRY RUN MODE - No agents will be deployed\n")
2054
+
2055
+ result = selection_service.deploy_auto_configure(
2056
+ project_path=project_path, dry_run=dry_run
2057
+ )
2058
+
2059
+ # Format output
2060
+ output_format = self._get_output_format(args)
2061
+ if self._is_structured_format(output_format):
2062
+ formatted = (
2063
+ self._formatter.format_as_json(result)
2064
+ if str(output_format).lower() == OutputFormat.JSON
2065
+ else self._formatter.format_as_yaml(result)
2066
+ )
2067
+ print(formatted)
2068
+ return CommandResult.success_result(
2069
+ f"Auto-configure {result['status']}", data=result
2070
+ )
2071
+
2072
+ # Text output
2073
+ print(f"\n{'=' * 60}")
2074
+ print(f"Status: {result['status'].upper()}")
2075
+ print(f"Mode: {result['mode']}")
2076
+ print(f"{'=' * 60}")
2077
+
2078
+ # Show detected toolchain
2079
+ toolchain = result.get("toolchain", {})
2080
+ print("\nšŸ”§ Detected Toolchain:")
2081
+ if toolchain.get("languages"):
2082
+ print(f" Languages: {', '.join(toolchain['languages'])}")
2083
+ if toolchain.get("frameworks"):
2084
+ print(f" Frameworks: {', '.join(toolchain['frameworks'])}")
2085
+ if toolchain.get("build_tools"):
2086
+ print(f" Build Tools: {', '.join(toolchain['build_tools'])}")
2087
+
2088
+ if not any(toolchain.values()):
2089
+ print(" (No toolchain detected)")
2090
+
2091
+ # Show recommended agents
2092
+ recommended = result.get("recommended_agents", [])
2093
+ if recommended:
2094
+ print(f"\nšŸŽÆ Recommended agents ({len(recommended)}):")
2095
+ for agent in recommended:
2096
+ print(f" • {agent}")
2097
+
2098
+ # Show deployment summary
2099
+ print(
2100
+ f"\nšŸ“Š Summary: {result['deployed_count']} deployed, "
2101
+ f"{result['failed_count']} failed, {result['missing_count']} missing"
2102
+ )
2103
+
2104
+ if result.get("deployed_agents"):
2105
+ print(f"\nāœ… Deployed agents ({len(result['deployed_agents'])}):")
2106
+ for agent in result["deployed_agents"]:
2107
+ print(f" • {agent}")
2108
+
2109
+ if result.get("failed_agents"):
2110
+ print(f"\nāŒ Failed agents ({len(result['failed_agents'])}):")
2111
+ for agent in result["failed_agents"]:
2112
+ print(f" • {agent}")
2113
+
2114
+ if result.get("missing_agents"):
2115
+ print(f"\nāš ļø Missing agents ({len(result['missing_agents'])}):")
2116
+ for agent in result["missing_agents"]:
2117
+ print(f" • {agent}")
2118
+ print("\nThese agents are not available in configured sources.")
2119
+
2120
+ if dry_run:
2121
+ print(
2122
+ "\nšŸ’” This was a dry run. Run without --dry-run to deploy agents."
2123
+ )
2124
+
2125
+ return CommandResult.success_result(
2126
+ f"Auto-configure {result['status']}", data=result
2127
+ )
2128
+
2129
+ except Exception as e:
2130
+ self.logger.error(f"Error in auto-configure: {e}", exc_info=True)
2131
+ return CommandResult.error_result(f"Error in auto-configure: {e}")
2132
+
2133
+ def _list_collections(self, args) -> CommandResult:
2134
+ """List all available agent collections.
2135
+
2136
+ NEW: Shows all collections with agent counts and metadata.
2137
+ Enables discovery of available agent collections before deployment.
2138
+ """
2139
+ try:
2140
+ from pathlib import Path
2141
+
2142
+ from ...services.agents.deployment.remote_agent_discovery_service import (
2143
+ RemoteAgentDiscoveryService,
2144
+ )
2145
+
2146
+ # Get remote agents cache directory
2147
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
2148
+
2149
+ if not cache_dir.exists():
2150
+ return CommandResult.error_result(
2151
+ "No remote agent collections found. Run 'claude-mpm agents deploy' first."
2152
+ )
2153
+
2154
+ # Use RemoteAgentDiscoveryService to list collections
2155
+ remote_service = RemoteAgentDiscoveryService(cache_dir)
2156
+ collections = remote_service.list_collections()
2157
+
2158
+ if not collections:
2159
+ return CommandResult.success_result(
2160
+ "No agent collections found in cache.", data={"collections": []}
2161
+ )
2162
+
2163
+ # Format output
2164
+ output_lines = ["Available Agent Collections:\n"]
2165
+ for collection in collections:
2166
+ output_lines.append(
2167
+ f" • {collection['collection_id']} ({collection['agent_count']} agents)"
2168
+ )
2169
+
2170
+ return CommandResult.success_result(
2171
+ "\n".join(output_lines), data={"collections": collections}
2172
+ )
2173
+
2174
+ except Exception as e:
2175
+ self.logger.error(f"Error listing collections: {e}", exc_info=True)
2176
+ return CommandResult.error_result(f"Error listing collections: {e}")
2177
+
2178
+ def _deploy_collection(self, args) -> CommandResult:
2179
+ """Deploy all agents from a specific collection.
2180
+
2181
+ NEW: Enables bulk deployment of all agents from a named collection.
2182
+ Useful for deploying entire agent sets at once.
2183
+ """
2184
+ try:
2185
+ from pathlib import Path
2186
+
2187
+ from ...services.agents.deployment.multi_source_deployment_service import (
2188
+ MultiSourceAgentDeploymentService,
2189
+ )
2190
+
2191
+ collection_id = args.collection_id
2192
+
2193
+ # Get agents from collection
2194
+ service = MultiSourceAgentDeploymentService()
2195
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
2196
+ agents = service.get_agents_by_collection(collection_id, cache_dir)
2197
+
2198
+ if not agents:
2199
+ return CommandResult.error_result(
2200
+ f"No agents found in collection '{collection_id}'"
2201
+ )
2202
+
2203
+ # Dry run mode
2204
+ if getattr(args, "dry_run", False):
2205
+ agent_names = [
2206
+ agent.get("metadata", {}).get("name", "Unknown") for agent in agents
2207
+ ]
2208
+ output = f"Would deploy {len(agents)} agents from collection '{collection_id}':\n"
2209
+ for name in agent_names:
2210
+ output += f" • {name}\n"
2211
+ return CommandResult.success_result(
2212
+ output,
2213
+ data={"collection_id": collection_id, "agent_count": len(agents)},
2214
+ )
2215
+
2216
+ # Deploy agents
2217
+ # TODO: Implement actual deployment logic using deployment service
2218
+ # For now, show what would be deployed
2219
+ return CommandResult.success_result(
2220
+ f"Deployment of collection '{collection_id}' would deploy {len(agents)} agents.\n"
2221
+ f"(Full deployment implementation pending)",
2222
+ data={
2223
+ "collection_id": collection_id,
2224
+ "agent_count": len(agents),
2225
+ "status": "pending_implementation",
2226
+ },
2227
+ )
2228
+
2229
+ except Exception as e:
2230
+ self.logger.error(f"Error deploying collection: {e}", exc_info=True)
2231
+ return CommandResult.error_result(f"Error deploying collection: {e}")
2232
+
2233
+ def _list_by_collection(self, args) -> CommandResult:
2234
+ """List agents from a specific collection.
2235
+
2236
+ NEW: Shows detailed information about agents in a collection.
2237
+ Supports multiple output formats (table, json, yaml).
2238
+ """
2239
+ try:
2240
+ import json as json_lib
2241
+ from pathlib import Path
2242
+
2243
+ from ...services.agents.deployment.multi_source_deployment_service import (
2244
+ MultiSourceAgentDeploymentService,
2245
+ )
2246
+
2247
+ collection_id = args.collection_id
2248
+ output_format = getattr(args, "format", "table")
2249
+
2250
+ # Get agents from collection
2251
+ service = MultiSourceAgentDeploymentService()
2252
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
2253
+ agents = service.get_agents_by_collection(collection_id, cache_dir)
2254
+
2255
+ if not agents:
2256
+ return CommandResult.error_result(
2257
+ f"No agents found in collection '{collection_id}'"
2258
+ )
2259
+
2260
+ # Format output based on requested format
2261
+ if output_format == "json":
2262
+ return CommandResult.success_result(
2263
+ json_lib.dumps(agents, indent=2),
2264
+ data={"collection_id": collection_id, "agents": agents},
2265
+ )
2266
+ if output_format == "yaml":
2267
+ try:
2268
+ import yaml
2269
+
2270
+ return CommandResult.success_result(
2271
+ yaml.dump(agents, default_flow_style=False),
2272
+ data={"collection_id": collection_id, "agents": agents},
2273
+ )
2274
+ except ImportError:
2275
+ return CommandResult.error_result(
2276
+ "YAML support not available (install PyYAML)"
2277
+ )
2278
+
2279
+ # Table format (default)
2280
+ output_lines = [f"Agents in collection '{collection_id}':\n"]
2281
+ for agent in agents:
2282
+ metadata = agent.get("metadata", {})
2283
+ name = metadata.get("name", "Unknown")
2284
+ description = metadata.get("description", "No description")
2285
+ version = agent.get("version", "unknown")
2286
+ output_lines.append(f" • {name} (v{version})")
2287
+ output_lines.append(f" {description}\n")
2288
+
2289
+ return CommandResult.success_result(
2290
+ "\n".join(output_lines),
2291
+ data={"collection_id": collection_id, "agent_count": len(agents)},
2292
+ )
2293
+
2294
+ except Exception as e:
2295
+ self.logger.error(f"Error listing collection agents: {e}", exc_info=True)
2296
+ return CommandResult.error_result(f"Error listing collection agents: {e}")
2297
+
2298
+ def _cache_status(self, args) -> CommandResult:
2299
+ """Show git status of agent cache.
2300
+
2301
+ Displays current branch, uncommitted changes, unpushed commits, and
2302
+ remote URL for the agent cache repository.
2303
+ """
2304
+ try:
2305
+ from ...services.agents.cache_git_manager import CacheGitManager
2306
+
2307
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
2308
+ manager = CacheGitManager(cache_dir)
2309
+
2310
+ if not manager.is_git_repo():
2311
+ print("āŒ Cache is not a git repository")
2312
+ print(f"\nCache location: {cache_dir}")
2313
+ print(
2314
+ "\nšŸ’” This is expected if you haven't cloned the agents repository."
2315
+ )
2316
+ print(" The cache will be managed via HTTP sync instead.")
2317
+ return CommandResult.error_result("Cache is not a git repository")
2318
+
2319
+ status = manager.get_status()
2320
+ output_format = self._get_output_format(args)
2321
+
2322
+ if self._is_structured_format(output_format):
2323
+ formatted = (
2324
+ self._formatter.format_as_json(status)
2325
+ if str(output_format).lower() == OutputFormat.JSON
2326
+ else self._formatter.format_as_yaml(status)
2327
+ )
2328
+ print(formatted)
2329
+ return CommandResult.success_result(
2330
+ "Cache status retrieved", data=status
2331
+ )
2332
+
2333
+ # Text output
2334
+ print(f"\nšŸ“ Cache: {manager.repo_path}")
2335
+ print(f"🌿 Branch: {status.get('branch', 'unknown')}")
2336
+
2337
+ if status.get("remote_url"):
2338
+ print(f"šŸ”— Remote: {status['remote_url']}")
2339
+
2340
+ # Show sync status
2341
+ ahead = status.get("ahead", 0)
2342
+ behind = status.get("behind", 0)
2343
+
2344
+ if ahead > 0:
2345
+ print(f"šŸ“¤ Ahead of remote: {ahead} commit(s)")
2346
+ if behind > 0:
2347
+ print(f"šŸ“„ Behind remote: {behind} commit(s)")
2348
+
2349
+ if ahead == 0 and behind == 0:
2350
+ print("āœ… In sync with remote")
2351
+
2352
+ # Show uncommitted changes
2353
+ uncommitted = status.get("uncommitted", [])
2354
+ if uncommitted:
2355
+ print(f"\nāš ļø Uncommitted changes: {len(uncommitted)}")
2356
+ for file in uncommitted[:10]: # Show max 10 files
2357
+ print(f" - {file}")
2358
+ if len(uncommitted) > 10:
2359
+ print(f" ... and {len(uncommitted) - 10} more")
2360
+ else:
2361
+ print("\nāœ… No uncommitted changes")
2362
+
2363
+ # Overall status
2364
+ if status.get("is_clean"):
2365
+ print("\n✨ Cache is clean and up-to-date")
2366
+ else:
2367
+ print("\nšŸ’” Run 'claude-mpm agents cache-sync' to sync with remote")
2368
+
2369
+ return CommandResult.success_result("Cache status displayed", data=status)
2370
+
2371
+ except Exception as e:
2372
+ self.logger.error(f"Error getting cache status: {e}", exc_info=True)
2373
+ return CommandResult.error_result(f"Error getting cache status: {e}")
2374
+
2375
+ def _cache_pull(self, args) -> CommandResult:
2376
+ """Pull latest agents from remote repository."""
2377
+ try:
2378
+ from ...services.agents.cache_git_manager import CacheGitManager
2379
+
2380
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
2381
+ manager = CacheGitManager(cache_dir)
2382
+
2383
+ if not manager.is_git_repo():
2384
+ print("āŒ Cache is not a git repository")
2385
+ return CommandResult.error_result("Cache is not a git repository")
2386
+
2387
+ branch = getattr(args, "branch", "main")
2388
+ print(f"šŸ”„ Pulling latest changes from {branch}...")
2389
+
2390
+ success, msg = manager.pull_latest(branch)
2391
+
2392
+ if success:
2393
+ print(f"āœ… {msg}")
2394
+ return CommandResult.success_result(msg)
2395
+ print(f"āŒ {msg}")
2396
+ return CommandResult.error_result(msg)
2397
+
2398
+ except Exception as e:
2399
+ self.logger.error(f"Error pulling cache: {e}", exc_info=True)
2400
+ return CommandResult.error_result(f"Error pulling cache: {e}")
2401
+
2402
+ def _cache_commit(self, args) -> CommandResult:
2403
+ """Commit changes to cache repository."""
2404
+ try:
2405
+ from ...services.agents.cache_git_manager import CacheGitManager
2406
+
2407
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
2408
+ manager = CacheGitManager(cache_dir)
2409
+
2410
+ if not manager.is_git_repo():
2411
+ print("āŒ Cache is not a git repository")
2412
+ return CommandResult.error_result("Cache is not a git repository")
2413
+
2414
+ # Get commit message from args
2415
+ message = getattr(args, "message", None)
2416
+ if not message:
2417
+ # Default message
2418
+ message = "feat: update agents from local development"
2419
+
2420
+ print("šŸ’¾ Committing changes...")
2421
+ success, msg = manager.commit_changes(message)
2422
+
2423
+ if success:
2424
+ print(f"āœ… {msg}")
2425
+ print(f"\nšŸ’” Commit message: {message}")
2426
+ return CommandResult.success_result(msg)
2427
+ print(f"āŒ {msg}")
2428
+ return CommandResult.error_result(msg)
2429
+
2430
+ except Exception as e:
2431
+ self.logger.error(f"Error committing cache changes: {e}", exc_info=True)
2432
+ return CommandResult.error_result(f"Error committing cache changes: {e}")
2433
+
2434
+ def _cache_push(self, args) -> CommandResult:
2435
+ """Push local agent changes to remote."""
2436
+ try:
2437
+ from ...services.agents.cache_git_manager import CacheGitManager
2438
+
2439
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
2440
+ manager = CacheGitManager(cache_dir)
2441
+
2442
+ if not manager.is_git_repo():
2443
+ print("āŒ Cache is not a git repository")
2444
+ return CommandResult.error_result("Cache is not a git repository")
2445
+
2446
+ # Check for uncommitted changes
2447
+ if manager.has_uncommitted_changes():
2448
+ print("āš ļø You have uncommitted changes.")
2449
+ print("\nšŸ’” Commit changes first with:")
2450
+ print(" claude-mpm agents cache-commit --message 'your message'")
2451
+
2452
+ # Ask if user wants to commit first
2453
+ auto_commit = getattr(args, "auto_commit", False)
2454
+ if auto_commit:
2455
+ print("\nšŸ“ Auto-committing changes...")
2456
+ success, msg = manager.commit_changes("feat: update agents")
2457
+ if not success:
2458
+ print(f"āŒ Commit failed: {msg}")
2459
+ return CommandResult.error_result(f"Commit failed: {msg}")
2460
+ print(f"āœ… {msg}")
2461
+ else:
2462
+ return CommandResult.error_result(
2463
+ "Uncommitted changes detected. Commit first or use --auto-commit"
2464
+ )
2465
+
2466
+ # Push changes
2467
+ branch = getattr(args, "branch", "main")
2468
+ print(f"šŸ“¤ Pushing changes to {branch}...")
2469
+
2470
+ success, msg = manager.push_changes(branch)
2471
+
2472
+ if success:
2473
+ print(f"āœ… {msg}")
2474
+ return CommandResult.success_result(msg)
2475
+ print(f"āŒ {msg}")
2476
+ return CommandResult.error_result(msg)
2477
+
2478
+ except Exception as e:
2479
+ self.logger.error(f"Error pushing cache: {e}", exc_info=True)
2480
+ return CommandResult.error_result(f"Error pushing cache: {e}")
2481
+
2482
+ def _cache_sync(self, args) -> CommandResult:
2483
+ """Full cache sync: pull, commit (if needed), push."""
2484
+ try:
2485
+ from ...services.agents.cache_git_manager import CacheGitManager
2486
+
2487
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
2488
+ manager = CacheGitManager(cache_dir)
2489
+
2490
+ if not manager.is_git_repo():
2491
+ print("āŒ Cache is not a git repository")
2492
+ return CommandResult.error_result("Cache is not a git repository")
2493
+
2494
+ print("šŸ”„ Syncing cache with remote...\n")
2495
+
2496
+ success, msg = manager.sync_with_remote()
2497
+
2498
+ # Print detailed sync message
2499
+ print(msg)
2500
+
2501
+ if success:
2502
+ print("\n✨ Cache sync complete!")
2503
+ return CommandResult.success_result("Cache synced successfully")
2504
+
2505
+ print("\nāŒ Cache sync failed. See details above.")
2506
+ return CommandResult.error_result("Cache sync failed")
2507
+
2508
+ except Exception as e:
2509
+ self.logger.error(f"Error syncing cache: {e}", exc_info=True)
2510
+ return CommandResult.error_result(f"Error syncing cache: {e}")
2511
+
2512
+
2513
+ def manage_agents(args):
168
2514
  """
169
- Clean deployed system agents.
170
-
171
- WHY: Users may want to remove deployed agents to start fresh or clean up
172
- their working directory.
173
-
174
- Args:
175
- args: Command arguments with optional 'target' path
176
- deployment_service: Agent deployment service instance
2515
+ Main entry point for agent management commands.
2516
+
2517
+ This function maintains backward compatibility while using the new AgentCommand pattern.
177
2518
  """
178
- print("Cleaning deployed system agents...")
179
- results = deployment_service.clean_deployment(args.target)
180
-
181
- if results["removed"]:
182
- print(f"\nāœ“ Removed {len(results['removed'])} agents")
183
- for path in results["removed"]:
184
- print(f" - {Path(path).name}")
185
- else:
186
- print("No system agents found to remove")
187
-
188
- if results["errors"]:
189
- print("\nāŒ Errors during cleanup:")
190
- for error in results["errors"]:
191
- print(f" - {error}")
2519
+ command = AgentsCommand()
2520
+ result = command.execute(args)
2521
+
2522
+ # Print result if structured output format is requested
2523
+ if _is_structured_output(args):
2524
+ command.print_result(result, args)
2525
+
2526
+ return result.exit_code