claude-mpm 4.1.26__py3-none-any.whl → 5.0.9__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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (792) hide show
  1. claude_mpm/BUILD_NUMBER +1 -1
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/__init__.py +20 -5
  4. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +118 -0
  5. claude_mpm/agents/BASE_DOCUMENTATION.md +53 -0
  6. claude_mpm/agents/BASE_ENGINEER.md +658 -0
  7. claude_mpm/agents/BASE_OPS.md +219 -0
  8. claude_mpm/agents/BASE_PM.md +432 -158
  9. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +787 -0
  10. claude_mpm/agents/BASE_QA.md +167 -0
  11. claude_mpm/agents/BASE_RESEARCH.md +53 -0
  12. claude_mpm/agents/OUTPUT_STYLE.md +254 -29
  13. claude_mpm/agents/PM_INSTRUCTIONS.md +969 -0
  14. claude_mpm/agents/PM_INSTRUCTIONS_TEACH.md +1322 -0
  15. claude_mpm/agents/WORKFLOW.md +355 -191
  16. claude_mpm/agents/__init__.py +6 -0
  17. claude_mpm/agents/agent_loader.py +41 -14
  18. claude_mpm/agents/agent_loader_integration.py +3 -2
  19. claude_mpm/agents/async_agent_loader.py +3 -3
  20. claude_mpm/agents/base_agent.json +6 -3
  21. claude_mpm/agents/base_agent_loader.py +21 -44
  22. claude_mpm/agents/frontmatter_validator.py +292 -252
  23. claude_mpm/agents/system_agent_config.py +3 -2
  24. claude_mpm/agents/templates/README.md +465 -0
  25. claude_mpm/agents/templates/circuit-breakers.md +1005 -0
  26. claude_mpm/agents/templates/context-management-examples.md +544 -0
  27. claude_mpm/agents/templates/git-file-tracking.md +584 -0
  28. claude_mpm/agents/templates/pm-examples.md +474 -0
  29. claude_mpm/agents/templates/pm-red-flags.md +310 -0
  30. claude_mpm/agents/templates/pr-workflow-examples.md +427 -0
  31. claude_mpm/agents/templates/research-gate-examples.md +669 -0
  32. claude_mpm/agents/templates/response-format.md +583 -0
  33. claude_mpm/agents/templates/structured-questions-examples.md +615 -0
  34. claude_mpm/agents/templates/ticket-completeness-examples.md +139 -0
  35. claude_mpm/agents/templates/ticketing-examples.md +277 -0
  36. claude_mpm/agents/templates/validation-templates.md +312 -0
  37. claude_mpm/cli/__init__.py +72 -376
  38. claude_mpm/cli/commands/__init__.py +4 -0
  39. claude_mpm/cli/commands/agent_manager.py +675 -20
  40. claude_mpm/cli/commands/agent_source.py +774 -0
  41. claude_mpm/cli/commands/agent_state_manager.py +344 -0
  42. claude_mpm/cli/commands/agents.py +1673 -178
  43. claude_mpm/cli/commands/agents_cleanup.py +210 -0
  44. claude_mpm/cli/commands/agents_detect.py +380 -0
  45. claude_mpm/cli/commands/agents_discover.py +338 -0
  46. claude_mpm/cli/commands/agents_recommend.py +309 -0
  47. claude_mpm/cli/commands/aggregate.py +11 -7
  48. claude_mpm/cli/commands/analyze.py +18 -13
  49. claude_mpm/cli/commands/analyze_code.py +8 -4
  50. claude_mpm/cli/commands/auto_configure.py +566 -0
  51. claude_mpm/cli/commands/cleanup.py +12 -12
  52. claude_mpm/cli/commands/config.py +54 -17
  53. claude_mpm/cli/commands/configure.py +1184 -1055
  54. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  55. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  56. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  57. claude_mpm/cli/commands/configure_models.py +18 -0
  58. claude_mpm/cli/commands/configure_navigation.py +184 -0
  59. claude_mpm/cli/commands/configure_paths.py +104 -0
  60. claude_mpm/cli/commands/configure_persistence.py +254 -0
  61. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  62. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  63. claude_mpm/cli/commands/configure_validators.py +73 -0
  64. claude_mpm/cli/commands/dashboard.py +50 -52
  65. claude_mpm/cli/commands/debug.py +19 -19
  66. claude_mpm/cli/commands/doctor.py +51 -7
  67. claude_mpm/cli/commands/hook_errors.py +277 -0
  68. claude_mpm/cli/commands/info.py +3 -4
  69. claude_mpm/cli/commands/local_deploy.py +534 -0
  70. claude_mpm/cli/commands/mcp.py +17 -10
  71. claude_mpm/cli/commands/mcp_command_router.py +11 -0
  72. claude_mpm/cli/commands/mcp_config.py +154 -0
  73. claude_mpm/cli/commands/mcp_external_commands.py +249 -0
  74. claude_mpm/cli/commands/mcp_install_commands.py +101 -32
  75. claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
  76. claude_mpm/cli/commands/mcp_setup_external.py +868 -0
  77. claude_mpm/cli/commands/memory.py +55 -21
  78. claude_mpm/cli/commands/monitor.py +160 -70
  79. claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
  80. claude_mpm/cli/commands/mpm_init/core.py +573 -0
  81. claude_mpm/cli/commands/mpm_init/display.py +341 -0
  82. claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
  83. claude_mpm/cli/commands/mpm_init/modes.py +397 -0
  84. claude_mpm/cli/commands/mpm_init/prompts.py +442 -0
  85. claude_mpm/cli/commands/mpm_init_cli.py +396 -0
  86. claude_mpm/cli/commands/mpm_init_handler.py +114 -4
  87. claude_mpm/cli/commands/postmortem.py +401 -0
  88. claude_mpm/cli/commands/run.py +252 -167
  89. claude_mpm/cli/commands/search.py +458 -0
  90. claude_mpm/cli/commands/skill_source.py +694 -0
  91. claude_mpm/cli/commands/skills.py +1225 -0
  92. claude_mpm/cli/commands/uninstall.py +176 -0
  93. claude_mpm/cli/commands/upgrade.py +152 -0
  94. claude_mpm/cli/commands/verify.py +119 -0
  95. claude_mpm/cli/executor.py +279 -0
  96. claude_mpm/cli/helpers.py +105 -0
  97. claude_mpm/cli/interactive/__init__.py +21 -0
  98. claude_mpm/cli/interactive/agent_wizard.py +1872 -0
  99. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  100. claude_mpm/cli/parser.py +79 -2
  101. claude_mpm/cli/parsers/__init__.py +7 -1
  102. claude_mpm/cli/parsers/agent_manager_parser.py +161 -1
  103. claude_mpm/cli/parsers/agent_source_parser.py +171 -0
  104. claude_mpm/cli/parsers/agents_parser.py +369 -1
  105. claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
  106. claude_mpm/cli/parsers/base_parser.py +196 -3
  107. claude_mpm/cli/parsers/config_parser.py +96 -43
  108. claude_mpm/cli/parsers/configure_parser.py +11 -15
  109. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  110. claude_mpm/cli/parsers/mcp_parser.py +15 -0
  111. claude_mpm/cli/parsers/monitor_parser.py +12 -2
  112. claude_mpm/cli/parsers/mpm_init_parser.py +179 -9
  113. claude_mpm/cli/parsers/run_parser.py +5 -0
  114. claude_mpm/cli/parsers/search_parser.py +245 -0
  115. claude_mpm/cli/parsers/skill_source_parser.py +169 -0
  116. claude_mpm/cli/parsers/skills_parser.py +282 -0
  117. claude_mpm/cli/parsers/source_parser.py +138 -0
  118. claude_mpm/cli/shared/argument_patterns.py +20 -13
  119. claude_mpm/cli/shared/base_command.py +2 -2
  120. claude_mpm/cli/shared/output_formatters.py +28 -19
  121. claude_mpm/cli/startup.py +994 -0
  122. claude_mpm/cli/startup_display.py +480 -0
  123. claude_mpm/cli/startup_logging.py +179 -13
  124. claude_mpm/cli/utils.py +54 -3
  125. claude_mpm/cli_module/commands.py +1 -1
  126. claude_mpm/commands/mpm-agents-auto-configure.md +278 -0
  127. claude_mpm/commands/mpm-agents-detect.md +177 -0
  128. claude_mpm/commands/mpm-agents-list.md +131 -0
  129. claude_mpm/commands/mpm-agents-recommend.md +223 -0
  130. claude_mpm/commands/mpm-config-view.md +150 -0
  131. claude_mpm/commands/mpm-doctor.md +9 -0
  132. claude_mpm/commands/mpm-help.md +297 -5
  133. claude_mpm/commands/mpm-init.md +401 -17
  134. claude_mpm/commands/mpm-monitor.md +418 -0
  135. claude_mpm/commands/mpm-postmortem.md +123 -0
  136. claude_mpm/commands/mpm-session-resume.md +381 -0
  137. claude_mpm/commands/mpm-status.md +79 -8
  138. claude_mpm/commands/mpm-ticket-organize.md +304 -0
  139. claude_mpm/commands/mpm-ticket-view.md +552 -0
  140. claude_mpm/commands/mpm-version.md +122 -0
  141. claude_mpm/commands/mpm.md +12 -0
  142. claude_mpm/config/agent_config.py +4 -4
  143. claude_mpm/config/agent_presets.py +488 -0
  144. claude_mpm/config/agent_sources.py +325 -0
  145. claude_mpm/config/experimental_features.py +7 -7
  146. claude_mpm/config/model_config.py +428 -0
  147. claude_mpm/config/paths.py +3 -2
  148. claude_mpm/config/skill_presets.py +392 -0
  149. claude_mpm/config/skill_sources.py +590 -0
  150. claude_mpm/config/socketio_config.py +3 -3
  151. claude_mpm/constants.py +28 -1
  152. claude_mpm/core/__init__.py +53 -17
  153. claude_mpm/core/agent_name_normalizer.py +3 -2
  154. claude_mpm/core/agent_registry.py +2 -2
  155. claude_mpm/core/agent_session_manager.py +10 -10
  156. claude_mpm/core/api_validator.py +330 -0
  157. claude_mpm/core/base_service.py +33 -23
  158. claude_mpm/core/cache.py +9 -9
  159. claude_mpm/core/claude_runner.py +24 -42
  160. claude_mpm/core/config.py +101 -8
  161. claude_mpm/core/config_aliases.py +7 -6
  162. claude_mpm/core/constants.py +66 -1
  163. claude_mpm/core/container.py +11 -5
  164. claude_mpm/core/enums.py +452 -0
  165. claude_mpm/core/error_handler.py +623 -0
  166. claude_mpm/core/factories.py +1 -1
  167. claude_mpm/core/file_utils.py +764 -0
  168. claude_mpm/core/framework/__init__.py +25 -0
  169. claude_mpm/core/framework/formatters/__init__.py +11 -0
  170. claude_mpm/core/framework/formatters/capability_generator.py +367 -0
  171. claude_mpm/core/framework/formatters/content_formatter.py +288 -0
  172. claude_mpm/core/framework/formatters/context_generator.py +185 -0
  173. claude_mpm/core/framework/loaders/__init__.py +13 -0
  174. claude_mpm/core/framework/loaders/agent_loader.py +210 -0
  175. claude_mpm/core/framework/loaders/file_loader.py +176 -0
  176. claude_mpm/core/framework/loaders/instruction_loader.py +181 -0
  177. claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
  178. claude_mpm/core/framework/processors/__init__.py +11 -0
  179. claude_mpm/core/framework/processors/memory_processor.py +230 -0
  180. claude_mpm/core/framework/processors/metadata_processor.py +146 -0
  181. claude_mpm/core/framework/processors/template_processor.py +244 -0
  182. claude_mpm/core/framework_loader.py +321 -1631
  183. claude_mpm/core/hook_error_memory.py +381 -0
  184. claude_mpm/core/hook_manager.py +49 -8
  185. claude_mpm/core/injectable_service.py +11 -8
  186. claude_mpm/core/instruction_reinforcement_hook.py +4 -3
  187. claude_mpm/core/interactive_session.py +146 -18
  188. claude_mpm/core/interfaces.py +56 -1
  189. claude_mpm/core/lazy.py +3 -3
  190. claude_mpm/core/log_manager.py +92 -23
  191. claude_mpm/core/logger.py +22 -15
  192. claude_mpm/core/logging_config.py +6 -2
  193. claude_mpm/core/logging_utils.py +520 -0
  194. claude_mpm/core/oneshot_session.py +122 -15
  195. claude_mpm/core/optimized_agent_loader.py +9 -9
  196. claude_mpm/core/optimized_startup.py +1 -1
  197. claude_mpm/core/output_style_manager.py +12 -192
  198. claude_mpm/core/pm_hook_interceptor.py +18 -12
  199. claude_mpm/core/protocols/__init__.py +23 -0
  200. claude_mpm/core/protocols/runner_protocol.py +103 -0
  201. claude_mpm/core/protocols/session_protocol.py +131 -0
  202. claude_mpm/core/service_registry.py +7 -3
  203. claude_mpm/core/session_manager.py +14 -12
  204. claude_mpm/core/shared/config_loader.py +1 -1
  205. claude_mpm/core/shared/singleton_manager.py +11 -4
  206. claude_mpm/core/socketio_pool.py +15 -15
  207. claude_mpm/core/system_context.py +38 -0
  208. claude_mpm/core/tool_access_control.py +3 -2
  209. claude_mpm/core/types.py +4 -11
  210. claude_mpm/core/typing_utils.py +7 -6
  211. claude_mpm/core/unified_agent_registry.py +115 -11
  212. claude_mpm/core/unified_config.py +6 -6
  213. claude_mpm/core/unified_paths.py +23 -20
  214. claude_mpm/dashboard/analysis_runner.py +4 -4
  215. claude_mpm/dashboard/api/simple_directory.py +261 -0
  216. claude_mpm/dashboard/static/css/activity.css +69 -69
  217. claude_mpm/dashboard/static/css/connection-status.css +10 -10
  218. claude_mpm/dashboard/static/css/dashboard.css +600 -18
  219. claude_mpm/dashboard/static/js/components/activity-tree.js +181 -195
  220. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +105 -102
  221. claude_mpm/dashboard/static/js/components/agent-inference.js +34 -31
  222. claude_mpm/dashboard/static/js/components/build-tracker.js +67 -59
  223. claude_mpm/dashboard/static/js/components/code-simple.js +857 -0
  224. claude_mpm/dashboard/static/js/components/connection-debug.js +101 -101
  225. claude_mpm/dashboard/static/js/components/diff-viewer.js +891 -0
  226. claude_mpm/dashboard/static/js/components/event-processor.js +3 -0
  227. claude_mpm/dashboard/static/js/components/event-viewer.js +50 -13
  228. claude_mpm/dashboard/static/js/components/export-manager.js +3 -0
  229. claude_mpm/dashboard/static/js/components/file-change-tracker.js +443 -0
  230. claude_mpm/dashboard/static/js/components/file-change-viewer.js +690 -0
  231. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +36 -16
  232. claude_mpm/dashboard/static/js/components/file-viewer.js +580 -0
  233. claude_mpm/dashboard/static/js/components/module-viewer.js +49 -23
  234. claude_mpm/dashboard/static/js/components/session-manager.js +19 -19
  235. claude_mpm/dashboard/static/js/components/socket-manager.js +5 -1
  236. claude_mpm/dashboard/static/js/components/ui-state-manager.js +356 -41
  237. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +520 -88
  238. claude_mpm/dashboard/static/js/components/working-directory.js +46 -11
  239. claude_mpm/dashboard/static/js/connection-manager.js +76 -76
  240. claude_mpm/dashboard/static/js/dashboard.js +309 -178
  241. claude_mpm/dashboard/static/js/extension-error-handler.js +22 -22
  242. claude_mpm/dashboard/static/js/socket-client.js +183 -139
  243. claude_mpm/dashboard/static/js/tab-isolation-fix.js +185 -0
  244. claude_mpm/dashboard/static/socket.io.min.js +7 -0
  245. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +7 -0
  246. claude_mpm/dashboard/templates/code_simple.html +153 -0
  247. claude_mpm/dashboard/templates/index.html +125 -122
  248. claude_mpm/experimental/cli_enhancements.py +5 -7
  249. claude_mpm/generators/agent_profile_generator.py +5 -3
  250. claude_mpm/hooks/__init__.py +37 -1
  251. claude_mpm/hooks/base_hook.py +5 -4
  252. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  253. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  254. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  255. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  256. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  257. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  258. claude_mpm/hooks/claude_hooks/connection_pool.py +4 -4
  259. claude_mpm/hooks/claude_hooks/event_handlers.py +24 -19
  260. claude_mpm/hooks/claude_hooks/hook_handler.py +29 -22
  261. claude_mpm/hooks/claude_hooks/installer.py +67 -22
  262. claude_mpm/hooks/claude_hooks/memory_integration.py +3 -3
  263. claude_mpm/hooks/claude_hooks/response_tracking.py +57 -17
  264. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  265. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  266. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  267. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  268. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  269. claude_mpm/hooks/claude_hooks/services/connection_manager.py +62 -64
  270. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +140 -76
  271. claude_mpm/hooks/claude_hooks/services/state_manager.py +11 -9
  272. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +3 -3
  273. claude_mpm/hooks/failure_learning/__init__.py +54 -0
  274. claude_mpm/hooks/failure_learning/failure_detection_hook.py +230 -0
  275. claude_mpm/hooks/failure_learning/fix_detection_hook.py +212 -0
  276. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +281 -0
  277. claude_mpm/hooks/instruction_reinforcement.py +301 -0
  278. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  279. claude_mpm/hooks/kuzu_memory_hook.py +386 -0
  280. claude_mpm/hooks/kuzu_response_hook.py +179 -0
  281. claude_mpm/hooks/memory_integration_hook.py +1 -1
  282. claude_mpm/hooks/session_resume_hook.py +121 -0
  283. claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
  284. claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
  285. claude_mpm/hooks/tool_call_interceptor.py +8 -5
  286. claude_mpm/hooks/validation_hooks.py +3 -3
  287. claude_mpm/init.py +23 -4
  288. claude_mpm/models/agent_session.py +8 -6
  289. claude_mpm/models/git_repository.py +198 -0
  290. claude_mpm/models/resume_log.py +340 -0
  291. claude_mpm/scripts/claude-hook-handler.sh +35 -9
  292. claude_mpm/scripts/launch_monitor.py +85 -0
  293. claude_mpm/scripts/mcp_server.py +3 -5
  294. claude_mpm/scripts/mpm_doctor.py +3 -2
  295. claude_mpm/scripts/socketio_daemon.py +159 -512
  296. claude_mpm/scripts/start_activity_logging.py +3 -1
  297. claude_mpm/services/__init__.py +144 -160
  298. claude_mpm/services/agents/__init__.py +18 -5
  299. claude_mpm/services/agents/agent_builder.py +56 -18
  300. claude_mpm/services/agents/agent_preset_service.py +238 -0
  301. claude_mpm/services/agents/agent_selection_service.py +484 -0
  302. claude_mpm/services/agents/auto_config_manager.py +796 -0
  303. claude_mpm/services/agents/auto_deploy_index_parser.py +569 -0
  304. claude_mpm/services/agents/cache_git_manager.py +621 -0
  305. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  306. claude_mpm/services/agents/deployment/agent_deployment.py +164 -17
  307. claude_mpm/services/agents/deployment/agent_discovery_service.py +191 -41
  308. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +5 -5
  309. claude_mpm/services/agents/deployment/agent_format_converter.py +56 -12
  310. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -7
  311. claude_mpm/services/agents/deployment/agent_metrics_collector.py +3 -3
  312. claude_mpm/services/agents/deployment/agent_operation_service.py +2 -2
  313. claude_mpm/services/agents/deployment/agent_record_service.py +4 -4
  314. claude_mpm/services/agents/deployment/agent_restore_handler.py +1 -4
  315. claude_mpm/services/agents/deployment/agent_state_service.py +2 -2
  316. claude_mpm/services/agents/deployment/agent_template_builder.py +939 -50
  317. claude_mpm/services/agents/deployment/agent_validator.py +31 -7
  318. claude_mpm/services/agents/deployment/agent_version_manager.py +8 -5
  319. claude_mpm/services/agents/deployment/agent_versioning.py +1 -1
  320. claude_mpm/services/agents/deployment/agents_directory_resolver.py +101 -15
  321. claude_mpm/services/agents/deployment/async_agent_deployment.py +3 -2
  322. claude_mpm/services/agents/deployment/deployment_config_loader.py +131 -7
  323. claude_mpm/services/agents/deployment/deployment_type_detector.py +10 -14
  324. claude_mpm/services/agents/deployment/deployment_wrapper.py +58 -0
  325. claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -3
  326. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  327. claude_mpm/services/agents/deployment/local_template_deployment.py +360 -0
  328. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +249 -53
  329. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +2 -2
  330. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +8 -7
  331. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  332. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  333. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +7 -5
  334. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  335. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +10 -10
  336. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +363 -0
  337. claude_mpm/services/agents/deployment/single_agent_deployer.py +2 -2
  338. claude_mpm/services/agents/deployment/system_instructions_deployer.py +168 -43
  339. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  340. claude_mpm/services/agents/deployment/validation/deployment_validator.py +2 -2
  341. claude_mpm/services/agents/deployment/validation/template_validator.py +64 -44
  342. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  343. claude_mpm/services/agents/git_source_manager.py +629 -0
  344. claude_mpm/services/agents/loading/agent_profile_loader.py +10 -9
  345. claude_mpm/services/agents/loading/base_agent_manager.py +16 -6
  346. claude_mpm/services/agents/loading/framework_agent_loader.py +11 -14
  347. claude_mpm/services/agents/local_template_manager.py +784 -0
  348. claude_mpm/services/agents/management/agent_capabilities_generator.py +3 -2
  349. claude_mpm/services/agents/management/agent_management_service.py +5 -5
  350. claude_mpm/services/agents/memory/agent_memory_manager.py +32 -29
  351. claude_mpm/services/agents/memory/content_manager.py +17 -9
  352. claude_mpm/services/agents/memory/memory_categorization_service.py +4 -2
  353. claude_mpm/services/agents/memory/memory_file_service.py +32 -6
  354. claude_mpm/services/agents/memory/memory_format_service.py +6 -4
  355. claude_mpm/services/agents/memory/memory_limits_service.py +4 -2
  356. claude_mpm/services/agents/memory/template_generator.py +3 -3
  357. claude_mpm/services/agents/observers.py +547 -0
  358. claude_mpm/services/agents/recommender.py +615 -0
  359. claude_mpm/services/agents/registry/deployed_agent_discovery.py +3 -3
  360. claude_mpm/services/agents/registry/modification_tracker.py +30 -19
  361. claude_mpm/services/agents/single_tier_deployment_service.py +696 -0
  362. claude_mpm/services/agents/sources/__init__.py +13 -0
  363. claude_mpm/services/agents/sources/agent_sync_state.py +516 -0
  364. claude_mpm/services/agents/sources/git_source_sync_service.py +1087 -0
  365. claude_mpm/services/agents/startup_sync.py +239 -0
  366. claude_mpm/services/agents/toolchain_detector.py +474 -0
  367. claude_mpm/services/analysis/__init__.py +25 -0
  368. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  369. claude_mpm/services/analysis/postmortem_service.py +765 -0
  370. claude_mpm/services/async_session_logger.py +141 -98
  371. claude_mpm/services/claude_session_logger.py +82 -74
  372. claude_mpm/services/cli/agent_cleanup_service.py +5 -0
  373. claude_mpm/services/cli/agent_listing_service.py +5 -5
  374. claude_mpm/services/cli/agent_validation_service.py +3 -1
  375. claude_mpm/services/cli/memory_crud_service.py +12 -7
  376. claude_mpm/services/cli/memory_output_formatter.py +2 -2
  377. claude_mpm/services/cli/resume_service.py +617 -0
  378. claude_mpm/services/cli/session_manager.py +104 -13
  379. claude_mpm/services/cli/session_pause_manager.py +504 -0
  380. claude_mpm/services/cli/session_resume_helper.py +372 -0
  381. claude_mpm/services/cli/startup_checker.py +13 -10
  382. claude_mpm/services/cli/unified_dashboard_manager.py +439 -0
  383. claude_mpm/services/command_deployment_service.py +209 -13
  384. claude_mpm/services/command_handler_service.py +11 -5
  385. claude_mpm/services/core/__init__.py +33 -1
  386. claude_mpm/services/core/base.py +31 -11
  387. claude_mpm/services/core/interfaces/__init__.py +88 -3
  388. claude_mpm/services/core/interfaces/agent.py +184 -0
  389. claude_mpm/services/core/interfaces/health.py +169 -0
  390. claude_mpm/services/core/interfaces/model.py +281 -0
  391. claude_mpm/services/core/interfaces/process.py +372 -0
  392. claude_mpm/services/core/interfaces/project.py +121 -0
  393. claude_mpm/services/core/interfaces/restart.py +307 -0
  394. claude_mpm/services/core/interfaces/stability.py +260 -0
  395. claude_mpm/services/core/interfaces.py +56 -1
  396. claude_mpm/services/core/memory_manager.py +92 -47
  397. claude_mpm/services/core/models/__init__.py +70 -0
  398. claude_mpm/services/core/models/agent_config.py +384 -0
  399. claude_mpm/services/core/models/health.py +162 -0
  400. claude_mpm/services/core/models/process.py +239 -0
  401. claude_mpm/services/core/models/restart.py +302 -0
  402. claude_mpm/services/core/models/stability.py +264 -0
  403. claude_mpm/services/core/models/toolchain.py +306 -0
  404. claude_mpm/services/core/path_resolver.py +36 -14
  405. claude_mpm/services/diagnostics/__init__.py +2 -2
  406. claude_mpm/services/diagnostics/checks/__init__.py +8 -2
  407. claude_mpm/services/diagnostics/checks/agent_check.py +30 -34
  408. claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
  409. claude_mpm/services/diagnostics/checks/claude_code_check.py +270 -0
  410. claude_mpm/services/diagnostics/checks/common_issues_check.py +28 -27
  411. claude_mpm/services/diagnostics/checks/configuration_check.py +26 -25
  412. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  413. claude_mpm/services/diagnostics/checks/installation_check.py +165 -60
  414. claude_mpm/services/diagnostics/checks/instructions_check.py +21 -21
  415. claude_mpm/services/diagnostics/checks/mcp_check.py +57 -44
  416. claude_mpm/services/diagnostics/checks/mcp_services_check.py +1058 -0
  417. claude_mpm/services/diagnostics/checks/monitor_check.py +24 -24
  418. claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
  419. claude_mpm/services/diagnostics/checks/startup_log_check.py +14 -11
  420. claude_mpm/services/diagnostics/diagnostic_runner.py +31 -13
  421. claude_mpm/services/diagnostics/doctor_reporter.py +305 -47
  422. claude_mpm/services/diagnostics/models.py +37 -21
  423. claude_mpm/services/event_aggregator.py +5 -3
  424. claude_mpm/services/event_bus/direct_relay.py +11 -7
  425. claude_mpm/services/event_bus/event_bus.py +51 -9
  426. claude_mpm/services/event_bus/relay.py +33 -14
  427. claude_mpm/services/events/consumers/dead_letter.py +7 -5
  428. claude_mpm/services/events/consumers/logging.py +1 -2
  429. claude_mpm/services/events/core.py +5 -6
  430. claude_mpm/services/events/producers/hook.py +6 -6
  431. claude_mpm/services/events/producers/system.py +8 -8
  432. claude_mpm/services/exceptions.py +5 -5
  433. claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
  434. claude_mpm/services/framework_claude_md_generator/content_assembler.py +5 -5
  435. claude_mpm/services/framework_claude_md_generator/content_validator.py +2 -2
  436. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
  437. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +2 -2
  438. claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
  439. claude_mpm/services/git/__init__.py +21 -0
  440. claude_mpm/services/git/git_operations_service.py +494 -0
  441. claude_mpm/services/github/__init__.py +21 -0
  442. claude_mpm/services/github/github_cli_service.py +397 -0
  443. claude_mpm/services/hook_installer_service.py +506 -0
  444. claude_mpm/services/hook_service.py +5 -6
  445. claude_mpm/services/infrastructure/context_preservation.py +13 -11
  446. claude_mpm/services/infrastructure/daemon_manager.py +9 -9
  447. claude_mpm/services/infrastructure/logging.py +2 -2
  448. claude_mpm/services/infrastructure/monitoring/__init__.py +2 -6
  449. claude_mpm/services/infrastructure/monitoring/aggregator.py +13 -18
  450. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  451. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  452. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  453. claude_mpm/services/infrastructure/monitoring/resources.py +8 -7
  454. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  455. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  456. claude_mpm/services/instructions/__init__.py +9 -0
  457. claude_mpm/services/instructions/instruction_cache_service.py +374 -0
  458. claude_mpm/services/local_ops/__init__.py +155 -0
  459. claude_mpm/services/local_ops/crash_detector.py +257 -0
  460. claude_mpm/services/local_ops/health_checks/__init__.py +26 -0
  461. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  462. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  463. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  464. claude_mpm/services/local_ops/health_manager.py +427 -0
  465. claude_mpm/services/local_ops/log_monitor.py +396 -0
  466. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  467. claude_mpm/services/local_ops/process_manager.py +595 -0
  468. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  469. claude_mpm/services/local_ops/restart_manager.py +401 -0
  470. claude_mpm/services/local_ops/restart_policy.py +387 -0
  471. claude_mpm/services/local_ops/state_manager.py +372 -0
  472. claude_mpm/services/local_ops/unified_manager.py +600 -0
  473. claude_mpm/services/mcp_config_manager.py +1542 -0
  474. claude_mpm/services/mcp_gateway/__init__.py +97 -93
  475. claude_mpm/services/mcp_gateway/auto_configure.py +43 -38
  476. claude_mpm/services/mcp_gateway/config/config_loader.py +3 -3
  477. claude_mpm/services/mcp_gateway/config/configuration.py +24 -5
  478. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  479. claude_mpm/services/mcp_gateway/core/base.py +20 -33
  480. claude_mpm/services/mcp_gateway/core/process_pool.py +591 -31
  481. claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
  482. claude_mpm/services/mcp_gateway/core/startup_verification.py +3 -3
  483. claude_mpm/services/mcp_gateway/main.py +90 -15
  484. claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
  485. claude_mpm/services/mcp_gateway/registry/tool_registry.py +12 -9
  486. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +5 -10
  487. claude_mpm/services/mcp_gateway/server/stdio_server.py +9 -17
  488. claude_mpm/services/mcp_gateway/tools/__init__.py +14 -2
  489. claude_mpm/services/mcp_gateway/tools/base_adapter.py +15 -15
  490. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +10 -9
  491. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +654 -0
  492. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +36 -34
  493. claude_mpm/services/mcp_gateway/tools/hello_world.py +8 -8
  494. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +555 -0
  495. claude_mpm/services/mcp_gateway/utils/__init__.py +14 -0
  496. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +160 -0
  497. claude_mpm/services/mcp_gateway/utils/update_preferences.py +170 -0
  498. claude_mpm/services/mcp_service_verifier.py +732 -0
  499. claude_mpm/services/memory/builder.py +9 -8
  500. claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
  501. claude_mpm/services/memory/cache/simple_cache.py +2 -2
  502. claude_mpm/services/memory/failure_tracker.py +578 -0
  503. claude_mpm/services/memory/indexed_memory.py +8 -8
  504. claude_mpm/services/memory/optimizer.py +8 -9
  505. claude_mpm/services/memory/router.py +3 -3
  506. claude_mpm/services/memory_hook_service.py +165 -4
  507. claude_mpm/services/model/__init__.py +147 -0
  508. claude_mpm/services/model/base_provider.py +365 -0
  509. claude_mpm/services/model/claude_provider.py +412 -0
  510. claude_mpm/services/model/model_router.py +452 -0
  511. claude_mpm/services/model/ollama_provider.py +415 -0
  512. claude_mpm/services/monitor/__init__.py +20 -0
  513. claude_mpm/services/monitor/daemon.py +691 -0
  514. claude_mpm/services/monitor/daemon_manager.py +1040 -0
  515. claude_mpm/services/monitor/event_emitter.py +350 -0
  516. claude_mpm/services/monitor/handlers/__init__.py +21 -0
  517. claude_mpm/services/monitor/handlers/code_analysis.py +332 -0
  518. claude_mpm/services/monitor/handlers/dashboard.py +299 -0
  519. claude_mpm/services/monitor/handlers/file.py +264 -0
  520. claude_mpm/services/monitor/handlers/hooks.py +512 -0
  521. claude_mpm/services/monitor/management/__init__.py +18 -0
  522. claude_mpm/services/monitor/management/health.py +124 -0
  523. claude_mpm/services/monitor/management/lifecycle.py +724 -0
  524. claude_mpm/services/monitor/server.py +817 -0
  525. claude_mpm/services/monitor_build_service.py +2 -2
  526. claude_mpm/services/native_agent_converter.py +356 -0
  527. claude_mpm/services/orphan_detection.py +786 -0
  528. claude_mpm/services/port_manager.py +3 -3
  529. claude_mpm/services/pr/__init__.py +14 -0
  530. claude_mpm/services/pr/pr_template_service.py +329 -0
  531. claude_mpm/services/project/__init__.py +23 -0
  532. claude_mpm/services/project/analyzer.py +3 -3
  533. claude_mpm/services/project/architecture_analyzer.py +5 -5
  534. claude_mpm/services/project/archive_manager.py +1045 -0
  535. claude_mpm/services/project/dependency_analyzer.py +4 -4
  536. claude_mpm/services/project/detection_strategies.py +719 -0
  537. claude_mpm/services/project/documentation_manager.py +554 -0
  538. claude_mpm/services/project/enhanced_analyzer.py +572 -0
  539. claude_mpm/services/project/metrics_collector.py +4 -4
  540. claude_mpm/services/project/project_organizer.py +1005 -0
  541. claude_mpm/services/project/registry.py +13 -7
  542. claude_mpm/services/project/toolchain_analyzer.py +583 -0
  543. claude_mpm/services/project_port_allocator.py +596 -0
  544. claude_mpm/services/response_tracker.py +21 -10
  545. claude_mpm/services/runner_configuration_service.py +17 -3
  546. claude_mpm/services/self_upgrade_service.py +500 -0
  547. claude_mpm/services/session_management_service.py +23 -9
  548. claude_mpm/services/session_manager.py +380 -0
  549. claude_mpm/services/shared/__init__.py +2 -1
  550. claude_mpm/services/shared/async_service_base.py +16 -27
  551. claude_mpm/services/shared/config_service_base.py +17 -14
  552. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  553. claude_mpm/services/shared/service_factory.py +8 -5
  554. claude_mpm/services/skills/__init__.py +18 -0
  555. claude_mpm/services/skills/git_skill_source_manager.py +1169 -0
  556. claude_mpm/services/skills/skill_discovery_service.py +568 -0
  557. claude_mpm/services/skills_config.py +547 -0
  558. claude_mpm/services/skills_deployer.py +955 -0
  559. claude_mpm/services/socketio/client_proxy.py +60 -5
  560. claude_mpm/services/socketio/dashboard_server.py +361 -0
  561. claude_mpm/services/socketio/event_normalizer.py +10 -6
  562. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  563. claude_mpm/services/socketio/handlers/base.py +2 -2
  564. claude_mpm/services/socketio/handlers/code_analysis.py +90 -27
  565. claude_mpm/services/socketio/handlers/connection.py +22 -41
  566. claude_mpm/services/socketio/handlers/connection_handler.py +13 -10
  567. claude_mpm/services/socketio/handlers/file.py +46 -10
  568. claude_mpm/services/socketio/handlers/git.py +9 -9
  569. claude_mpm/services/socketio/handlers/hook.py +29 -17
  570. claude_mpm/services/socketio/handlers/registry.py +4 -2
  571. claude_mpm/services/socketio/monitor_client.py +364 -0
  572. claude_mpm/services/socketio/server/broadcaster.py +9 -7
  573. claude_mpm/services/socketio/server/connection_manager.py +2 -2
  574. claude_mpm/services/socketio/server/core.py +142 -8
  575. claude_mpm/services/socketio/server/eventbus_integration.py +20 -14
  576. claude_mpm/services/socketio/server/main.py +24 -24
  577. claude_mpm/services/socketio_client_manager.py +4 -4
  578. claude_mpm/services/subprocess_launcher_service.py +19 -15
  579. claude_mpm/services/system_instructions_service.py +3 -5
  580. claude_mpm/services/ticket_services/formatter_service.py +1 -1
  581. claude_mpm/services/ticket_services/validation_service.py +5 -5
  582. claude_mpm/services/unified/__init__.py +65 -0
  583. claude_mpm/services/unified/analyzer_strategies/__init__.py +44 -0
  584. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +518 -0
  585. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +680 -0
  586. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +900 -0
  587. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +745 -0
  588. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +733 -0
  589. claude_mpm/services/unified/config_strategies/__init__.py +175 -0
  590. claude_mpm/services/unified/config_strategies/config_schema.py +731 -0
  591. claude_mpm/services/unified/config_strategies/context_strategy.py +747 -0
  592. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1005 -0
  593. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +881 -0
  594. claude_mpm/services/unified/config_strategies/unified_config_service.py +823 -0
  595. claude_mpm/services/unified/config_strategies/validation_strategy.py +1148 -0
  596. claude_mpm/services/unified/deployment_strategies/__init__.py +97 -0
  597. claude_mpm/services/unified/deployment_strategies/base.py +553 -0
  598. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +573 -0
  599. claude_mpm/services/unified/deployment_strategies/local.py +607 -0
  600. claude_mpm/services/unified/deployment_strategies/utils.py +667 -0
  601. claude_mpm/services/unified/deployment_strategies/vercel.py +471 -0
  602. claude_mpm/services/unified/interfaces.py +475 -0
  603. claude_mpm/services/unified/migration.py +509 -0
  604. claude_mpm/services/unified/strategies.py +534 -0
  605. claude_mpm/services/unified/unified_analyzer.py +542 -0
  606. claude_mpm/services/unified/unified_config.py +691 -0
  607. claude_mpm/services/unified/unified_deployment.py +466 -0
  608. claude_mpm/services/utility_service.py +6 -3
  609. claude_mpm/services/version_control/branch_strategy.py +2 -2
  610. claude_mpm/services/version_control/conflict_resolution.py +14 -8
  611. claude_mpm/services/version_control/git_operations.py +26 -24
  612. claude_mpm/services/version_control/semantic_versioning.py +14 -14
  613. claude_mpm/services/version_control/version_parser.py +14 -11
  614. claude_mpm/services/version_service.py +104 -1
  615. claude_mpm/services/visualization/__init__.py +1 -5
  616. claude_mpm/services/visualization/mermaid_generator.py +2 -3
  617. claude_mpm/skills/__init__.py +42 -0
  618. claude_mpm/skills/agent_skills_injector.py +324 -0
  619. claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
  620. claude_mpm/skills/bundled/__init__.py +6 -0
  621. claude_mpm/skills/bundled/api-documentation.md +393 -0
  622. claude_mpm/skills/bundled/async-testing.md +571 -0
  623. claude_mpm/skills/bundled/code-review.md +143 -0
  624. claude_mpm/skills/bundled/database-migration.md +199 -0
  625. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  626. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  627. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  628. claude_mpm/skills/bundled/git-workflow.md +414 -0
  629. claude_mpm/skills/bundled/imagemagick.md +204 -0
  630. claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
  631. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  632. claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
  633. claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
  634. claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
  635. claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
  636. claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
  637. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  638. claude_mpm/skills/bundled/pdf.md +141 -0
  639. claude_mpm/skills/bundled/performance-profiling.md +573 -0
  640. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  641. claude_mpm/skills/bundled/security-scanning.md +327 -0
  642. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  643. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  644. claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
  645. claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
  646. claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
  647. claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
  648. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  649. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  650. claude_mpm/skills/bundled/xlsx.md +157 -0
  651. claude_mpm/skills/registry.py +286 -0
  652. claude_mpm/skills/skill_manager.py +310 -0
  653. claude_mpm/skills/skills_registry.py +347 -0
  654. claude_mpm/skills/skills_service.py +739 -0
  655. claude_mpm/storage/state_storage.py +31 -31
  656. claude_mpm/templates/questions/__init__.py +38 -0
  657. claude_mpm/templates/questions/base.py +193 -0
  658. claude_mpm/templates/questions/pr_strategy.py +311 -0
  659. claude_mpm/templates/questions/project_init.py +385 -0
  660. claude_mpm/templates/questions/ticket_mgmt.py +394 -0
  661. claude_mpm/tools/__main__.py +9 -9
  662. claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
  663. claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
  664. claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
  665. claude_mpm/tools/code_tree_analyzer/core.py +380 -0
  666. claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
  667. claude_mpm/tools/code_tree_analyzer/events.py +168 -0
  668. claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
  669. claude_mpm/tools/code_tree_analyzer/models.py +39 -0
  670. claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
  671. claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
  672. claude_mpm/tools/code_tree_builder.py +6 -6
  673. claude_mpm/tools/code_tree_events.py +14 -10
  674. claude_mpm/tools/socketio_debug.py +11 -11
  675. claude_mpm/utils/agent_dependency_loader.py +184 -36
  676. claude_mpm/utils/agent_filters.py +288 -0
  677. claude_mpm/utils/common.py +544 -0
  678. claude_mpm/utils/config_manager.py +12 -6
  679. claude_mpm/utils/database_connector.py +298 -0
  680. claude_mpm/utils/dependency_cache.py +5 -3
  681. claude_mpm/utils/dependency_strategies.py +15 -10
  682. claude_mpm/utils/display_helper.py +260 -0
  683. claude_mpm/utils/environment_context.py +4 -3
  684. claude_mpm/utils/error_handler.py +5 -3
  685. claude_mpm/utils/file_utils.py +13 -14
  686. claude_mpm/utils/git_analyzer.py +407 -0
  687. claude_mpm/utils/gitignore.py +241 -0
  688. claude_mpm/utils/log_cleanup.py +627 -0
  689. claude_mpm/utils/migration.py +372 -0
  690. claude_mpm/utils/path_operations.py +7 -4
  691. claude_mpm/utils/progress.py +387 -0
  692. claude_mpm/utils/robust_installer.py +131 -24
  693. claude_mpm/utils/session_logging.py +2 -2
  694. claude_mpm/utils/structured_questions.py +619 -0
  695. claude_mpm/utils/subprocess_utils.py +9 -8
  696. claude_mpm/validation/agent_validator.py +6 -6
  697. claude_mpm/validation/frontmatter_validator.py +6 -6
  698. claude_mpm-5.0.9.dist-info/METADATA +1028 -0
  699. claude_mpm-5.0.9.dist-info/RECORD +864 -0
  700. {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/entry_points.txt +1 -0
  701. claude_mpm/agents/INSTRUCTIONS.md +0 -261
  702. claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -17
  703. claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +0 -3
  704. claude_mpm/agents/templates/agent-manager.json +0 -270
  705. claude_mpm/agents/templates/agent-manager.md +0 -619
  706. claude_mpm/agents/templates/agentic_coder_optimizer.json +0 -222
  707. claude_mpm/agents/templates/api_qa.json +0 -171
  708. claude_mpm/agents/templates/code_analyzer.json +0 -95
  709. claude_mpm/agents/templates/data_engineer.json +0 -152
  710. claude_mpm/agents/templates/documentation.json +0 -175
  711. claude_mpm/agents/templates/engineer.json +0 -176
  712. claude_mpm/agents/templates/imagemagick.json +0 -261
  713. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +0 -39
  714. claude_mpm/agents/templates/memory_manager.json +0 -155
  715. claude_mpm/agents/templates/ops.json +0 -175
  716. claude_mpm/agents/templates/project_organizer.json +0 -130
  717. claude_mpm/agents/templates/qa.json +0 -223
  718. claude_mpm/agents/templates/refactoring_engineer.json +0 -266
  719. claude_mpm/agents/templates/research.json +0 -163
  720. claude_mpm/agents/templates/security.json +0 -153
  721. claude_mpm/agents/templates/ticketing.json +0 -169
  722. claude_mpm/agents/templates/vercel_ops_agent.json +0 -281
  723. claude_mpm/agents/templates/version_control.json +0 -147
  724. claude_mpm/agents/templates/web_qa.json +0 -254
  725. claude_mpm/agents/templates/web_ui.json +0 -176
  726. claude_mpm/cli/commands/configure_tui.py +0 -1927
  727. claude_mpm/cli/commands/mpm_init.py +0 -594
  728. claude_mpm/cli/commands/socketio_monitor.py +0 -233
  729. claude_mpm/commands/mpm-agents.md +0 -12
  730. claude_mpm/commands/mpm-config.md +0 -18
  731. claude_mpm/commands/mpm-tickets.md +0 -102
  732. claude_mpm/dashboard/.claude-mpm/socketio-instances.json +0 -1
  733. claude_mpm/dashboard/static/built/components/activity-tree.js +0 -2
  734. claude_mpm/dashboard/static/built/components/agent-inference.js +0 -2
  735. claude_mpm/dashboard/static/built/components/code-tree.js +0 -2
  736. claude_mpm/dashboard/static/built/components/code-viewer.js +0 -2
  737. claude_mpm/dashboard/static/built/components/event-processor.js +0 -2
  738. claude_mpm/dashboard/static/built/components/event-viewer.js +0 -2
  739. claude_mpm/dashboard/static/built/components/export-manager.js +0 -2
  740. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +0 -2
  741. claude_mpm/dashboard/static/built/components/hud-library-loader.js +0 -2
  742. claude_mpm/dashboard/static/built/components/hud-manager.js +0 -2
  743. claude_mpm/dashboard/static/built/components/hud-visualizer.js +0 -2
  744. claude_mpm/dashboard/static/built/components/module-viewer.js +0 -2
  745. claude_mpm/dashboard/static/built/components/session-manager.js +0 -2
  746. claude_mpm/dashboard/static/built/components/socket-manager.js +0 -2
  747. claude_mpm/dashboard/static/built/components/ui-state-manager.js +0 -2
  748. claude_mpm/dashboard/static/built/components/unified-data-viewer.js +0 -2
  749. claude_mpm/dashboard/static/built/components/working-directory.js +0 -2
  750. claude_mpm/dashboard/static/built/dashboard.js +0 -2
  751. claude_mpm/dashboard/static/built/socket-client.js +0 -2
  752. claude_mpm/dashboard/static/css/code-tree.css +0 -1408
  753. claude_mpm/dashboard/static/dist/components/activity-tree.js +0 -2
  754. claude_mpm/dashboard/static/dist/components/agent-inference.js +0 -2
  755. claude_mpm/dashboard/static/dist/components/code-tree.js +0 -2
  756. claude_mpm/dashboard/static/dist/components/code-viewer.js +0 -2
  757. claude_mpm/dashboard/static/dist/components/event-processor.js +0 -2
  758. claude_mpm/dashboard/static/dist/components/event-viewer.js +0 -2
  759. claude_mpm/dashboard/static/dist/components/export-manager.js +0 -2
  760. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +0 -2
  761. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +0 -2
  762. claude_mpm/dashboard/static/dist/components/hud-manager.js +0 -2
  763. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +0 -2
  764. claude_mpm/dashboard/static/dist/components/module-viewer.js +0 -2
  765. claude_mpm/dashboard/static/dist/components/session-manager.js +0 -2
  766. claude_mpm/dashboard/static/dist/components/socket-manager.js +0 -2
  767. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +0 -2
  768. claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +0 -2
  769. claude_mpm/dashboard/static/dist/components/working-directory.js +0 -2
  770. claude_mpm/dashboard/static/dist/dashboard.js +0 -2
  771. claude_mpm/dashboard/static/dist/socket-client.js +0 -2
  772. claude_mpm/dashboard/static/js/components/code-tree.js +0 -3220
  773. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -480
  774. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  775. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1040
  776. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  777. claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
  778. claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
  779. claude_mpm/scripts/socketio_server_manager.py +0 -349
  780. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  781. claude_mpm/services/cli/dashboard_launcher.py +0 -423
  782. claude_mpm/services/cli/socketio_manager.py +0 -537
  783. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +0 -286
  784. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +0 -645
  785. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +0 -602
  786. claude_mpm/services/project/analyzer_refactored.py +0 -450
  787. claude_mpm/tools/code_tree_analyzer.py +0 -1693
  788. claude_mpm-4.1.26.dist-info/METADATA +0 -332
  789. claude_mpm-4.1.26.dist-info/RECORD +0 -606
  790. {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/WHEEL +0 -0
  791. {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/licenses/LICENSE +0 -0
  792. {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/top_level.txt +0 -0
@@ -12,153 +12,53 @@ DESIGN DECISIONS:
12
12
  """
13
13
 
14
14
  import json
15
- import os
16
- import sys
17
15
  from pathlib import Path
18
16
  from typing import Dict, List, Optional
19
17
 
20
- from rich.box import ROUNDED
21
- from rich.columns import Columns
18
+ import questionary
19
+ from questionary import Style
22
20
  from rich.console import Console
23
- from rich.panel import Panel
24
21
  from rich.prompt import Confirm, Prompt
25
- from rich.syntax import Syntax
26
- from rich.table import Table
27
22
  from rich.text import Text
28
23
 
24
+ from ...core.config import Config
29
25
  from ...services.version_service import VersionService
26
+ from ...utils.agent_filters import apply_all_filters, get_deployed_agent_ids
30
27
  from ...utils.console import console as default_console
31
28
  from ..shared import BaseCommand, CommandResult
32
-
33
-
34
- class AgentConfig:
35
- """Simple agent configuration model."""
36
-
37
- def __init__(
38
- self, name: str, description: str = "", dependencies: Optional[List[str]] = None
39
- ):
40
- self.name = name
41
- self.description = description
42
- self.dependencies = dependencies or []
43
-
44
-
45
- class SimpleAgentManager:
46
- """Simple agent state management that discovers real agents from templates."""
47
-
48
- def __init__(self, config_dir: Path):
49
- self.config_dir = config_dir
50
- self.config_file = config_dir / "agent_states.json"
51
- self.config_dir.mkdir(parents=True, exist_ok=True)
52
- self._load_states()
53
- # Path to agent templates directory
54
- self.templates_dir = (
55
- Path(__file__).parent.parent.parent / "agents" / "templates"
56
- )
57
-
58
- def _load_states(self):
59
- """Load agent states from file."""
60
- if self.config_file.exists():
61
- with open(self.config_file) as f:
62
- self.states = json.load(f)
63
- else:
64
- self.states = {}
65
-
66
- def _save_states(self):
67
- """Save agent states to file."""
68
- with open(self.config_file, "w") as f:
69
- json.dump(self.states, f, indent=2)
70
-
71
- def is_agent_enabled(self, agent_name: str) -> bool:
72
- """Check if an agent is enabled."""
73
- return self.states.get(agent_name, {}).get("enabled", True)
74
-
75
- def set_agent_enabled(self, agent_name: str, enabled: bool):
76
- """Set agent enabled state."""
77
- if agent_name not in self.states:
78
- self.states[agent_name] = {}
79
- self.states[agent_name]["enabled"] = enabled
80
- self._save_states()
81
-
82
- def discover_agents(self) -> List[AgentConfig]:
83
- """Discover available agents from template JSON files."""
84
- agents = []
85
-
86
- # Scan templates directory for JSON files
87
- if not self.templates_dir.exists():
88
- # Fallback to a minimal set if templates dir doesn't exist
89
- return [
90
- AgentConfig("engineer", "Engineering agent (templates not found)", []),
91
- AgentConfig("research", "Research agent (templates not found)", []),
92
- ]
93
-
94
- try:
95
- # Read all JSON template files
96
- for template_file in sorted(self.templates_dir.glob("*.json")):
97
- # Skip backup files
98
- if "backup" in template_file.name.lower():
99
- continue
100
-
101
- try:
102
- with open(template_file) as f:
103
- template_data = json.load(f)
104
-
105
- # Extract agent information from template
106
- agent_id = template_data.get("agent_id", template_file.stem)
107
-
108
- # Get metadata for display info
109
- metadata = template_data.get("metadata", {})
110
- metadata.get("name", agent_id)
111
- description = metadata.get(
112
- "description", "No description available"
113
- )
114
-
115
- # Extract capabilities/tools as dependencies for display
116
- capabilities = template_data.get("capabilities", {})
117
- tools = capabilities.get("tools", [])
118
- # Show first few tools as "dependencies" for UI purposes
119
- display_tools = tools[:3] if len(tools) > 3 else tools
120
-
121
- # Normalize agent ID (remove -agent suffix if present, replace underscores)
122
- normalized_id = agent_id.replace("-agent", "").replace("_", "-")
123
-
124
- agents.append(
125
- AgentConfig(
126
- name=normalized_id,
127
- description=(
128
- description[:80] + "..."
129
- if len(description) > 80
130
- else description
131
- ),
132
- dependencies=display_tools,
133
- )
134
- )
135
-
136
- except (json.JSONDecodeError, KeyError):
137
- # Skip malformed templates
138
- continue
139
-
140
- except Exception as e:
141
- # If there's an error reading templates, return a minimal set
142
- return [
143
- AgentConfig("engineer", f"Error loading templates: {e!s}", []),
144
- AgentConfig("research", "Research agent", []),
145
- ]
146
-
147
- # Sort agents by name for consistent display
148
- agents.sort(key=lambda a: a.name)
149
-
150
- return (
151
- agents
152
- if agents
153
- else [
154
- AgentConfig("engineer", "No agents found in templates", []),
155
- ]
156
- )
29
+ from .agent_state_manager import SimpleAgentManager
30
+ from .configure_agent_display import AgentDisplay
31
+ from .configure_behavior_manager import BehaviorManager
32
+ from .configure_hook_manager import HookManager
33
+ from .configure_models import AgentConfig
34
+ from .configure_navigation import ConfigNavigation
35
+ from .configure_persistence import ConfigPersistence
36
+ from .configure_startup_manager import StartupManager
37
+ from .configure_template_editor import TemplateEditor
38
+ from .configure_validators import (
39
+ parse_id_selection,
40
+ validate_args as validate_configure_args,
41
+ )
157
42
 
158
43
 
159
44
  class ConfigureCommand(BaseCommand):
160
45
  """Interactive configuration management command."""
161
46
 
47
+ # Questionary style optimized for dark terminals (WCAG AAA compliant)
48
+ QUESTIONARY_STYLE = Style(
49
+ [
50
+ ("selected", "fg:#e0e0e0 bold"), # Light gray - excellent readability
51
+ ("pointer", "fg:#ffd700 bold"), # Gold/yellow - highly visible pointer
52
+ ("highlighted", "fg:#e0e0e0"), # Light gray - clear hover state
53
+ ("question", "fg:#e0e0e0 bold"), # Light gray bold - prominent questions
54
+ ("checkbox", "fg:#00ff00"), # Green - for checked boxes
55
+ (
56
+ "checkbox-selected",
57
+ "fg:#00ff00 bold",
58
+ ), # Green bold - for checked selected boxes
59
+ ]
60
+ )
61
+
162
62
  def __init__(self):
163
63
  super().__init__("configure")
164
64
  self.console = default_console
@@ -166,24 +66,88 @@ class ConfigureCommand(BaseCommand):
166
66
  self.current_scope = "project"
167
67
  self.project_dir = Path.cwd()
168
68
  self.agent_manager = None
69
+ self.hook_manager = HookManager(self.console)
70
+ self.behavior_manager = None # Initialized when scope is set
71
+ self._agent_display = None # Lazy-initialized
72
+ self._persistence = None # Lazy-initialized
73
+ self._navigation = None # Lazy-initialized
74
+ self._template_editor = None # Lazy-initialized
75
+ self._startup_manager = None # Lazy-initialized
169
76
 
170
77
  def validate_args(self, args) -> Optional[str]:
171
78
  """Validate command arguments."""
172
- # Check for conflicting direct navigation options
173
- nav_options = [
174
- getattr(args, "agents", False),
175
- getattr(args, "templates", False),
176
- getattr(args, "behaviors", False),
177
- getattr(args, "version_info", False),
178
- ]
179
- if sum(nav_options) > 1:
180
- return "Only one direct navigation option can be specified at a time"
181
-
182
- # Check for conflicting non-interactive options
183
- if getattr(args, "enable_agent", None) and getattr(args, "disable_agent", None):
184
- return "Cannot enable and disable agents at the same time"
185
-
186
- return None
79
+ return validate_configure_args(args)
80
+
81
+ @property
82
+ def agent_display(self) -> AgentDisplay:
83
+ """Lazy-initialize agent display handler."""
84
+ if self._agent_display is None:
85
+ if self.agent_manager is None:
86
+ raise RuntimeError(
87
+ "agent_manager must be initialized before agent_display"
88
+ )
89
+ self._agent_display = AgentDisplay(
90
+ self.console,
91
+ self.agent_manager,
92
+ self._get_agent_template_path,
93
+ self._display_header,
94
+ )
95
+ return self._agent_display
96
+
97
+ @property
98
+ def persistence(self) -> ConfigPersistence:
99
+ """Lazy-initialize persistence handler."""
100
+ if self._persistence is None:
101
+ # Note: agent_manager might be None for version_info calls
102
+ self._persistence = ConfigPersistence(
103
+ self.console,
104
+ self.version_service,
105
+ self.agent_manager, # Can be None for version operations
106
+ self._get_agent_template_path,
107
+ self._display_header,
108
+ self.current_scope,
109
+ self.project_dir,
110
+ )
111
+ return self._persistence
112
+
113
+ @property
114
+ def navigation(self) -> ConfigNavigation:
115
+ """Lazy-initialize navigation handler."""
116
+ if self._navigation is None:
117
+ self._navigation = ConfigNavigation(self.console, self.project_dir)
118
+ # Sync scope from main command
119
+ self._navigation.current_scope = self.current_scope
120
+ return self._navigation
121
+
122
+ @property
123
+ def template_editor(self) -> TemplateEditor:
124
+ """Lazy-initialize template editor."""
125
+ if self._template_editor is None:
126
+ if self.agent_manager is None:
127
+ raise RuntimeError(
128
+ "agent_manager must be initialized before template_editor"
129
+ )
130
+ self._template_editor = TemplateEditor(
131
+ self.console, self.agent_manager, self.current_scope, self.project_dir
132
+ )
133
+ return self._template_editor
134
+
135
+ @property
136
+ def startup_manager(self) -> StartupManager:
137
+ """Lazy-initialize startup manager."""
138
+ if self._startup_manager is None:
139
+ if self.agent_manager is None:
140
+ raise RuntimeError(
141
+ "agent_manager must be initialized before startup_manager"
142
+ )
143
+ self._startup_manager = StartupManager(
144
+ self.agent_manager,
145
+ self.console,
146
+ self.current_scope,
147
+ self.project_dir,
148
+ self._display_header,
149
+ )
150
+ return self._startup_manager
187
151
 
188
152
  def run(self, args) -> CommandResult:
189
153
  """Execute the configure command."""
@@ -192,12 +156,15 @@ class ConfigureCommand(BaseCommand):
192
156
  if getattr(args, "project_dir", None):
193
157
  self.project_dir = Path(args.project_dir)
194
158
 
195
- # Initialize agent manager with appropriate config directory
159
+ # Initialize agent manager and behavior manager with appropriate config directory
196
160
  if self.current_scope == "project":
197
161
  config_dir = self.project_dir / ".claude-mpm"
198
162
  else:
199
163
  config_dir = Path.home() / ".claude-mpm"
200
164
  self.agent_manager = SimpleAgentManager(config_dir)
165
+ self.behavior_manager = BehaviorManager(
166
+ config_dir, self.current_scope, self.console
167
+ )
201
168
 
202
169
  # Disable colors if requested
203
170
  if getattr(args, "no_colors", False):
@@ -242,39 +209,15 @@ class ConfigureCommand(BaseCommand):
242
209
  if getattr(args, "behaviors", False):
243
210
  return self._run_behavior_management()
244
211
 
212
+ if getattr(args, "startup", False):
213
+ return self._run_startup_configuration()
214
+
245
215
  # Launch interactive TUI
246
216
  return self._run_interactive_tui(args)
247
217
 
248
218
  def _run_interactive_tui(self, args) -> CommandResult:
249
- """Run the main interactive TUI."""
250
- # Check if we can use the modern Textual TUI
251
- use_textual = getattr(args, "use_textual", True)
252
- force_rich = getattr(args, "force_rich", False)
253
-
254
- if use_textual and not force_rich:
255
- try:
256
- # Try to import and use Textual TUI
257
- from .configure_tui import can_use_tui, launch_tui
258
-
259
- if can_use_tui():
260
- self.console.print(
261
- "[cyan]Launching full-screen configuration interface...[/cyan]"
262
- )
263
- return launch_tui(self.current_scope, self.project_dir)
264
- # Fall back to Rich TUI if terminal doesn't support full-screen
265
- self.console.print(
266
- "[yellow]Terminal doesn't support full-screen mode. Using menu interface.[/yellow]"
267
- )
268
- except ImportError:
269
- # Textual not available, fall back to Rich
270
- self.console.print(
271
- "[yellow]Textual not installed. Using menu interface.[/yellow]"
272
- )
273
- self.console.print(
274
- "[dim]Install textual for a better experience: pip install textual[/dim]"
275
- )
276
-
277
- # Original Rich-based TUI
219
+ """Run the main interactive menu interface."""
220
+ # Rich-based menu interface
278
221
  try:
279
222
  self.console.clear()
280
223
 
@@ -286,13 +229,49 @@ class ConfigureCommand(BaseCommand):
286
229
  if choice == "1":
287
230
  self._manage_agents()
288
231
  elif choice == "2":
289
- self._edit_templates()
232
+ self._manage_skills()
290
233
  elif choice == "3":
291
- self._manage_behaviors()
234
+ self._edit_templates()
292
235
  elif choice == "4":
293
- self._switch_scope()
236
+ self._manage_behaviors()
294
237
  elif choice == "5":
238
+ # If user saves and wants to proceed to startup, exit the configurator
239
+ if self._manage_startup_configuration():
240
+ self.console.print(
241
+ "\n[green]Configuration saved. Exiting configurator...[/green]"
242
+ )
243
+ break
244
+ elif choice == "6":
245
+ self._switch_scope()
246
+ elif choice == "7":
295
247
  self._show_version_info_interactive()
248
+ elif choice == "l":
249
+ # Check for pending agent changes
250
+ if self.agent_manager and self.agent_manager.has_pending_changes():
251
+ should_save = Confirm.ask(
252
+ "[yellow]You have unsaved agent changes. Save them before launching?[/yellow]",
253
+ default=True,
254
+ )
255
+ if should_save:
256
+ self.agent_manager.commit_deferred_changes()
257
+ self.console.print("[green]✓ Agent changes saved[/green]")
258
+ else:
259
+ self.agent_manager.discard_deferred_changes()
260
+ self.console.print(
261
+ "[yellow]⚠ Agent changes discarded[/yellow]"
262
+ )
263
+
264
+ # Save all configuration
265
+ self.console.print("\n[cyan]Saving configuration...[/cyan]")
266
+ if self._save_all_configuration():
267
+ # Launch Claude MPM (this will replace the process if successful)
268
+ self._launch_claude_mpm()
269
+ # If execvp fails, we'll return here and break
270
+ break
271
+ self.console.print(
272
+ "[red]✗ Failed to save configuration. Not launching.[/red]"
273
+ )
274
+ Prompt.ask("\nPress Enter to continue")
296
275
  elif choice == "q":
297
276
  self.console.print(
298
277
  "\n[green]Configuration complete. Goodbye![/green]"
@@ -312,777 +291,504 @@ class ConfigureCommand(BaseCommand):
312
291
 
313
292
  def _display_header(self) -> None:
314
293
  """Display the TUI header."""
315
- self.console.clear()
316
-
317
- # Create header panel
318
- header_text = Text()
319
- header_text.append("Claude MPM ", style="bold cyan")
320
- header_text.append("Configuration Interface", style="bold white")
321
-
322
- scope_text = Text(f"Scope: {self.current_scope.upper()}", style="yellow")
323
- dir_text = Text(f"Directory: {self.project_dir}", style="dim")
324
-
325
- header_content = Columns([header_text], align="center")
326
- subtitle_content = f"{scope_text} | {dir_text}"
327
-
328
- header_panel = Panel(
329
- header_content,
330
- subtitle=subtitle_content,
331
- box=ROUNDED,
332
- style="blue",
333
- padding=(1, 2),
334
- )
335
-
336
- self.console.print(header_panel)
337
- self.console.print()
294
+ # Sync scope to navigation before display
295
+ self.navigation.current_scope = self.current_scope
296
+ self.navigation.display_header()
338
297
 
339
298
  def _show_main_menu(self) -> str:
340
299
  """Show the main menu and get user choice."""
341
- menu_items = [
342
- ("1", "Agent Management", "Enable/disable agents and customize settings"),
343
- ("2", "Template Editing", "Edit agent JSON templates"),
344
- ("3", "Behavior Files", "Manage identity and workflow configurations"),
345
- ("4", "Switch Scope", f"Current: {self.current_scope}"),
346
- ("5", "Version Info", "Display MPM and Claude versions"),
347
- ("q", "Quit", "Exit configuration interface"),
348
- ]
349
-
350
- table = Table(show_header=False, box=None, padding=(0, 2))
351
- table.add_column("Key", style="cyan", width=3)
352
- table.add_column("Option", style="bold white", width=20)
353
- table.add_column("Description", style="dim")
354
-
355
- for key, option, desc in menu_items:
356
- table.add_row(f"[{key}]", option, desc)
357
-
358
- menu_panel = Panel(
359
- table, title="[bold]Main Menu[/bold]", box=ROUNDED, style="green"
360
- )
361
-
362
- self.console.print(menu_panel)
363
- self.console.print()
364
-
365
- return Prompt.ask("[bold cyan]Select an option[/bold cyan]", default="q")
300
+ # Sync scope to navigation before display
301
+ self.navigation.current_scope = self.current_scope
302
+ return self.navigation.show_main_menu()
366
303
 
367
304
  def _manage_agents(self) -> None:
368
- """Agent management interface."""
305
+ """Enhanced agent management with remote agent discovery and installation."""
369
306
  while True:
370
307
  self.console.clear()
371
- self._display_header()
372
-
373
- # Display available agents
374
- agents = self.agent_manager.discover_agents()
375
- self._display_agents_table(agents)
376
-
377
- # Show agent menu
378
- self.console.print("\n[bold]Agent Management Options:[/bold]")
379
- self.console.print(" [cyan][e][/cyan] Enable an agent")
380
- self.console.print(" [cyan][d][/cyan] Disable an agent")
381
- self.console.print(" [cyan][c][/cyan] Customize agent template")
382
- self.console.print(" [cyan][v][/cyan] View agent details")
383
- self.console.print(" [cyan][r][/cyan] Reset agent to defaults")
384
- self.console.print(" [cyan][b][/cyan] Back to main menu")
385
- self.console.print()
308
+ self.navigation.display_header()
309
+ self.console.print("\n[bold blue]═══ Agent Management ═══[/bold blue]\n")
310
+
311
+ # Step 1: Show configured sources
312
+ self.console.print("[bold white]═══ Agent Sources ═══[/bold white]\n")
313
+
314
+ sources = self._get_configured_sources()
315
+ if sources:
316
+ from rich.table import Table
317
+
318
+ sources_table = Table(show_header=True, header_style="bold white")
319
+ sources_table.add_column(
320
+ "Source",
321
+ style="bright_yellow",
322
+ width=40,
323
+ no_wrap=True,
324
+ overflow="ellipsis",
325
+ )
326
+ sources_table.add_column(
327
+ "Status", style="green", width=15, no_wrap=True
328
+ )
329
+ sources_table.add_column(
330
+ "Agents", style="yellow", width=10, no_wrap=True
331
+ )
386
332
 
387
- choice = Prompt.ask("[bold cyan]Select an option[/bold cyan]", default="b")
333
+ for source in sources:
334
+ status = "✓ Active" if source.get("enabled", True) else "Disabled"
335
+ agent_count = source.get("agent_count", "?")
336
+ sources_table.add_row(
337
+ source["identifier"], status, str(agent_count)
338
+ )
388
339
 
389
- if choice == "b":
390
- break
391
- if choice == "e":
392
- self._enable_agent_interactive(agents)
393
- elif choice == "d":
394
- self._disable_agent_interactive(agents)
395
- elif choice == "c":
396
- self._customize_agent_template(agents)
397
- elif choice == "v":
398
- self._view_agent_details(agents)
399
- elif choice == "r":
400
- self._reset_agent_defaults(agents)
340
+ self.console.print(sources_table)
401
341
  else:
402
- self.console.print("[red]Invalid choice.[/red]")
403
- Prompt.ask("Press Enter to continue")
404
-
405
- def _display_agents_table(self, agents: List[AgentConfig]) -> None:
406
- """Display a table of available agents."""
407
- table = Table(
408
- title=f"Available Agents ({len(agents)} total)",
409
- box=ROUNDED,
410
- show_lines=True,
411
- )
412
-
413
- table.add_column("ID", style="dim", width=3)
414
- table.add_column("Name", style="cyan", width=22)
415
- table.add_column("Status", width=12)
416
- table.add_column("Description", style="white", width=45)
417
- table.add_column("Model/Tools", style="dim", width=20)
418
-
419
- for idx, agent in enumerate(agents, 1):
420
- # Check if agent is enabled
421
- is_enabled = self.agent_manager.is_agent_enabled(agent.name)
422
- status = (
423
- "[green]✓ Enabled[/green]" if is_enabled else "[red]✗ Disabled[/red]"
424
- )
342
+ self.console.print("[yellow]No agent sources configured[/yellow]")
343
+ self.console.print(
344
+ "[dim]Default source 'bobmatnyc/claude-mpm-agents' will be used[/dim]\n"
345
+ )
425
346
 
426
- # Format tools/dependencies - show first 2 tools
427
- tools_display = ""
428
- if agent.dependencies:
429
- if len(agent.dependencies) > 2:
430
- tools_display = f"{', '.join(agent.dependencies[:2])}..."
431
- else:
432
- tools_display = ", ".join(agent.dependencies)
433
- else:
434
- # Try to get model from template
435
- try:
436
- template_path = self._get_agent_template_path(agent.name)
437
- if template_path.exists():
438
- with open(template_path) as f:
439
- template = json.load(f)
440
- model = template.get("capabilities", {}).get("model", "default")
441
- tools_display = f"Model: {model}"
442
- else:
443
- tools_display = "Default"
444
- except:
445
- tools_display = "Default"
446
-
447
- # Truncate description for table display
448
- desc_display = (
449
- agent.description[:42] + "..."
450
- if len(agent.description) > 42
451
- else agent.description
452
- )
347
+ # Step 2: Discover and display available agents
348
+ self.console.print("\n[bold white]═══ Available Agents ═══[/bold white]\n")
453
349
 
454
- table.add_row(str(idx), agent.name, status, desc_display, tools_display)
350
+ try:
351
+ # Discover agents (includes both local and remote)
352
+ agents = self.agent_manager.discover_agents(include_remote=True)
455
353
 
456
- self.console.print(table)
354
+ # Set deployment status on each agent for display
355
+ deployed_ids = get_deployed_agent_ids()
356
+ for agent in agents:
357
+ # Extract leaf name for comparison
358
+ agent_leaf_name = agent.name.split("/")[-1]
359
+ agent.is_deployed = agent_leaf_name in deployed_ids
457
360
 
458
- def _enable_agent_interactive(self, agents: List[AgentConfig]) -> None:
459
- """Interactive agent enabling."""
460
- agent_id = Prompt.ask("Enter agent ID to enable (or 'all' for all agents)")
361
+ # Filter BASE_AGENT from display (1M-502 Phase 1)
362
+ agents = self._filter_agent_configs(agents, filter_deployed=False)
461
363
 
462
- if agent_id.lower() == "all":
463
- if Confirm.ask("[yellow]Enable ALL agents?[/yellow]"):
464
- for agent in agents:
465
- self.agent_manager.set_agent_enabled(agent.name, True)
466
- self.console.print("[green]All agents enabled successfully![/green]")
467
- else:
468
- try:
469
- idx = int(agent_id) - 1
470
- if 0 <= idx < len(agents):
471
- agent = agents[idx]
472
- self.agent_manager.set_agent_enabled(agent.name, True)
364
+ if not agents:
365
+ self.console.print("[yellow]No agents found[/yellow]")
473
366
  self.console.print(
474
- f"[green]Agent '{agent.name}' enabled successfully![/green]"
367
+ "[dim]Configure sources with 'claude-mpm agent-source add'[/dim]\n"
475
368
  )
476
369
  else:
477
- self.console.print("[red]Invalid agent ID.[/red]")
478
- except ValueError:
479
- self.console.print("[red]Invalid input. Please enter a number.[/red]")
480
-
481
- Prompt.ask("Press Enter to continue")
370
+ # Display agents in a table (already filtered at line 339)
371
+ self._display_agents_with_source_info(agents)
482
372
 
483
- def _disable_agent_interactive(self, agents: List[AgentConfig]) -> None:
484
- """Interactive agent disabling."""
485
- agent_id = Prompt.ask("Enter agent ID to disable (or 'all' for all agents)")
373
+ except Exception as e:
374
+ self.console.print(f"[red]Error discovering agents: {e}[/red]")
375
+ self.logger.error(f"Agent discovery failed: {e}", exc_info=True)
486
376
 
487
- if agent_id.lower() == "all":
488
- if Confirm.ask("[yellow]Disable ALL agents?[/yellow]"):
489
- for agent in agents:
490
- self.agent_manager.set_agent_enabled(agent.name, False)
491
- self.console.print("[green]All agents disabled successfully![/green]")
492
- else:
377
+ # Step 3: Menu options with arrow-key navigation
378
+ self.console.print()
493
379
  try:
494
- idx = int(agent_id) - 1
495
- if 0 <= idx < len(agents):
496
- agent = agents[idx]
497
- self.agent_manager.set_agent_enabled(agent.name, False)
498
- self.console.print(
499
- f"[green]Agent '{agent.name}' disabled successfully![/green]"
500
- )
501
- else:
502
- self.console.print("[red]Invalid agent ID.[/red]")
503
- except ValueError:
504
- self.console.print("[red]Invalid input. Please enter a number.[/red]")
505
-
506
- Prompt.ask("Press Enter to continue")
380
+ choice = questionary.select(
381
+ "Agent Management:",
382
+ choices=[
383
+ "Manage sources (add/remove repositories)",
384
+ "Select Agents",
385
+ "Install preset (predefined sets)",
386
+ "Remove agents",
387
+ "View agent details",
388
+ "Toggle agents (legacy enable/disable)",
389
+ questionary.Separator(),
390
+ " Back to main menu",
391
+ ],
392
+ style=self.QUESTIONARY_STYLE,
393
+ ).ask()
394
+
395
+ if choice is None or choice == "← Back to main menu":
396
+ break
507
397
 
508
- def _customize_agent_template(self, agents: List[AgentConfig]) -> None:
509
- """Customize agent JSON template."""
510
- agent_id = Prompt.ask("Enter agent ID to customize")
398
+ agents_var = agents if "agents" in locals() else []
399
+
400
+ # Map selection to action
401
+ if choice == "Manage sources (add/remove repositories)":
402
+ self._manage_sources()
403
+ elif choice == "Select Agents":
404
+ self._deploy_agents_individual(agents_var)
405
+ elif choice == "Install preset (predefined sets)":
406
+ self._deploy_agents_preset()
407
+ elif choice == "Remove agents":
408
+ self._remove_agents(agents_var)
409
+ elif choice == "View agent details":
410
+ self._view_agent_details_enhanced(agents_var)
411
+ elif choice == "Toggle agents (legacy enable/disable)":
412
+ self._toggle_agents_interactive(agents_var)
413
+
414
+ except KeyboardInterrupt:
415
+ self.console.print("\n[yellow]Operation cancelled[/yellow]")
416
+ break
511
417
 
512
- try:
513
- idx = int(agent_id) - 1
514
- if 0 <= idx < len(agents):
515
- agent = agents[idx]
516
- self._edit_agent_template(agent)
517
- else:
518
- self.console.print("[red]Invalid agent ID.[/red]")
519
- Prompt.ask("Press Enter to continue")
520
- except ValueError:
521
- self.console.print("[red]Invalid input. Please enter a number.[/red]")
522
- Prompt.ask("Press Enter to continue")
418
+ def _display_agents_table(self, agents: List[AgentConfig]) -> None:
419
+ """Display a table of available agents."""
420
+ self.agent_display.display_agents_table(agents)
523
421
 
524
- def _edit_agent_template(self, agent: AgentConfig) -> None:
525
- """Edit an agent's JSON template."""
526
- self.console.clear()
527
- self.console.print(f"[bold]Editing template for: {agent.name}[/bold]\n")
422
+ def _display_agents_with_pending_states(self, agents: List[AgentConfig]) -> None:
423
+ """Display agents table with pending state indicators."""
424
+ self.agent_display.display_agents_with_pending_states(agents)
528
425
 
529
- # Get current template
530
- template_path = self._get_agent_template_path(agent.name)
426
+ def _toggle_agents_interactive(self, agents: List[AgentConfig]) -> None:
427
+ """Interactive multi-agent enable/disable with batch save."""
531
428
 
532
- if template_path.exists():
533
- with open(template_path) as f:
534
- template = json.load(f)
535
- is_system = str(template_path).startswith(
536
- str(self.agent_manager.templates_dir)
537
- )
538
- else:
539
- # Create a minimal template structure based on system templates
540
- template = {
541
- "schema_version": "1.2.0",
542
- "agent_id": agent.name,
543
- "agent_version": "1.0.0",
544
- "agent_type": agent.name.replace("-", "_"),
545
- "metadata": {
546
- "name": agent.name.replace("-", " ").title() + " Agent",
547
- "description": agent.description,
548
- "tags": [agent.name],
549
- "author": "Custom",
550
- "created_at": "",
551
- "updated_at": "",
552
- },
553
- "capabilities": {
554
- "model": "opus",
555
- "tools": (
556
- agent.dependencies
557
- if agent.dependencies
558
- else ["Read", "Write", "Edit", "Bash"]
559
- ),
560
- },
561
- "instructions": {
562
- "base_template": "BASE_AGENT_TEMPLATE.md",
563
- "custom_instructions": "",
564
- },
565
- }
566
- is_system = False
567
-
568
- # Display current template
569
- if is_system:
570
- self.console.print(
571
- "[yellow]Viewing SYSTEM template (read-only). Customization will create a local copy.[/yellow]\n"
572
- )
429
+ # Initialize pending states from current states
430
+ for agent in agents:
431
+ current_state = self.agent_manager.is_agent_enabled(agent.name)
432
+ self.agent_manager.set_agent_enabled_deferred(agent.name, current_state)
573
433
 
574
- self.console.print("[bold]Current Template:[/bold]")
575
- # Truncate for display if too large
576
- display_template = template.copy()
577
- if (
578
- "instructions" in display_template
579
- and isinstance(display_template["instructions"], dict)
580
- and (
581
- "custom_instructions" in display_template["instructions"]
582
- and len(str(display_template["instructions"]["custom_instructions"]))
583
- > 200
584
- )
585
- ):
586
- display_template["instructions"]["custom_instructions"] = (
587
- display_template["instructions"]["custom_instructions"][:200] + "..."
434
+ while True:
435
+ # Display table with pending states
436
+ self._display_agents_with_pending_states(agents)
437
+
438
+ # Show menu
439
+ self.console.print("\n[bold]Toggle Agent Status:[/bold]")
440
+ text_toggle = Text(" ")
441
+ text_toggle.append("[t]", style="bold blue")
442
+ text_toggle.append(" Enter agent IDs to toggle (e.g., '1,3,5' or '1-4')")
443
+ self.console.print(text_toggle)
444
+
445
+ text_all = Text(" ")
446
+ text_all.append("[a]", style="bold blue")
447
+ text_all.append(" Enable all agents")
448
+ self.console.print(text_all)
449
+
450
+ text_none = Text(" ")
451
+ text_none.append("[n]", style="bold blue")
452
+ text_none.append(" Disable all agents")
453
+ self.console.print(text_none)
454
+
455
+ text_save = Text(" ")
456
+ text_save.append("[s]", style="bold green")
457
+ text_save.append(" Save changes and return")
458
+ self.console.print(text_save)
459
+
460
+ text_cancel = Text(" ")
461
+ text_cancel.append("[c]", style="bold magenta")
462
+ text_cancel.append(" Cancel (discard changes)")
463
+ self.console.print(text_cancel)
464
+
465
+ choice = (
466
+ Prompt.ask("[bold blue]Select an option[/bold blue]", default="s")
467
+ .strip()
468
+ .lower()
588
469
  )
589
470
 
590
- json_str = json.dumps(display_template, indent=2)
591
- # Limit display to first 50 lines for readability
592
- lines = json_str.split("\n")
593
- if len(lines) > 50:
594
- json_str = "\n".join(lines[:50]) + "\n... (truncated for display)"
595
-
596
- syntax = Syntax(json_str, "json", theme="monokai", line_numbers=True)
597
- self.console.print(syntax)
598
- self.console.print()
599
-
600
- # Editing options
601
- self.console.print("[bold]Editing Options:[/bold]")
602
- if not is_system:
603
- self.console.print(" [cyan][1][/cyan] Edit in external editor")
604
- self.console.print(" [cyan][2][/cyan] Add/modify a field")
605
- self.console.print(" [cyan][3][/cyan] Remove a field")
606
- self.console.print(" [cyan][4][/cyan] Reset to defaults")
607
- else:
608
- self.console.print(" [cyan][1][/cyan] Create customized copy")
609
- self.console.print(" [cyan][2][/cyan] View full template")
610
- self.console.print(" [cyan][b][/cyan] Back")
611
- self.console.print()
471
+ if choice == "s":
472
+ if self.agent_manager.has_pending_changes():
473
+ self.agent_manager.commit_deferred_changes()
474
+ self.console.print("[green]✓ Changes saved successfully![/green]")
475
+ else:
476
+ self.console.print("[yellow]No changes to save.[/yellow]")
477
+ Prompt.ask("Press Enter to continue")
478
+ break
479
+ if choice == "c":
480
+ self.agent_manager.discard_deferred_changes()
481
+ self.console.print("[yellow]Changes discarded.[/yellow]")
482
+ Prompt.ask("Press Enter to continue")
483
+ break
484
+ if choice == "a":
485
+ for agent in agents:
486
+ self.agent_manager.set_agent_enabled_deferred(agent.name, True)
487
+ elif choice == "n":
488
+ for agent in agents:
489
+ self.agent_manager.set_agent_enabled_deferred(agent.name, False)
490
+ elif choice == "t" or choice.replace(",", "").replace("-", "").isdigit():
491
+ selected_ids = self._parse_id_selection(
492
+ choice if choice != "t" else Prompt.ask("Enter IDs"), len(agents)
493
+ )
494
+ for idx in selected_ids:
495
+ if 1 <= idx <= len(agents):
496
+ agent = agents[idx - 1]
497
+ current = self.agent_manager.get_pending_state(agent.name)
498
+ self.agent_manager.set_agent_enabled_deferred(
499
+ agent.name, not current
500
+ )
612
501
 
613
- choice = Prompt.ask("[bold cyan]Select an option[/bold cyan]", default="b")
502
+ def _customize_agent_template(self, agents: List[AgentConfig]) -> None:
503
+ """Customize agent JSON template."""
504
+ self.template_editor.customize_agent_template(agents)
614
505
 
615
- if is_system:
616
- if choice == "1":
617
- # Create a customized copy
618
- self._create_custom_template_copy(agent, template)
619
- elif choice == "2":
620
- # View full template
621
- self._view_full_template(template)
622
- elif choice == "1":
623
- self._edit_in_external_editor(template_path, template)
624
- elif choice == "2":
625
- self._modify_template_field(template, template_path)
626
- elif choice == "3":
627
- self._remove_template_field(template, template_path)
628
- elif choice == "4":
629
- self._reset_template(agent, template_path)
630
-
631
- if choice != "b":
632
- Prompt.ask("Press Enter to continue")
506
+ def _edit_agent_template(self, agent: AgentConfig) -> None:
507
+ """Edit an agent's JSON template."""
508
+ self.template_editor.edit_agent_template(agent)
633
509
 
634
510
  def _get_agent_template_path(self, agent_name: str) -> Path:
635
511
  """Get the path to an agent's template file."""
636
- # First check for custom template in project/user config
637
- if self.current_scope == "project":
638
- config_dir = self.project_dir / ".claude-mpm" / "agents"
639
- else:
640
- config_dir = Path.home() / ".claude-mpm" / "agents"
641
-
642
- config_dir.mkdir(parents=True, exist_ok=True)
643
- custom_template = config_dir / f"{agent_name}.json"
644
-
645
- # If custom template exists, return it
646
- if custom_template.exists():
647
- return custom_template
648
-
649
- # Otherwise, look for the system template
650
- # Handle various naming conventions
651
- possible_names = [
652
- f"{agent_name}.json",
653
- f"{agent_name.replace('-', '_')}.json",
654
- f"{agent_name}-agent.json",
655
- f"{agent_name.replace('-', '_')}_agent.json",
656
- ]
657
-
658
- for name in possible_names:
659
- system_template = self.agent_manager.templates_dir / name
660
- if system_template.exists():
661
- return system_template
662
-
663
- # Return the custom template path for new templates
664
- return custom_template
512
+ return self.template_editor.get_agent_template_path(agent_name)
665
513
 
666
514
  def _edit_in_external_editor(self, template_path: Path, template: Dict) -> None:
667
515
  """Open template in external editor."""
668
- import subprocess
669
- import tempfile
670
-
671
- # Write current template to temp file
672
- with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
673
- json.dump(template, f, indent=2)
674
- temp_path = f.name
675
-
676
- # Get editor from environment
677
- editor = os.environ.get("EDITOR", "nano")
678
-
679
- try:
680
- # Open in editor
681
- subprocess.call([editor, temp_path])
682
-
683
- # Read back the edited content
684
- with open(temp_path) as f:
685
- new_template = json.load(f)
686
-
687
- # Save to actual template path
688
- with open(template_path, "w") as f:
689
- json.dump(new_template, f, indent=2)
690
-
691
- self.console.print("[green]Template updated successfully![/green]")
692
-
693
- except Exception as e:
694
- self.console.print(f"[red]Error editing template: {e}[/red]")
695
- finally:
696
- # Clean up temp file
697
- Path(temp_path).unlink(missing_ok=True)
516
+ self.template_editor.edit_in_external_editor(template_path, template)
698
517
 
699
518
  def _modify_template_field(self, template: Dict, template_path: Path) -> None:
700
519
  """Add or modify a field in the template."""
701
- field_name = Prompt.ask(
702
- "Enter field name (use dot notation for nested, e.g., 'config.timeout')"
703
- )
704
- field_value = Prompt.ask("Enter field value (JSON format)")
705
-
706
- try:
707
- # Parse the value as JSON
708
- value = json.loads(field_value)
709
-
710
- # Navigate to the field location
711
- parts = field_name.split(".")
712
- current = template
713
-
714
- for part in parts[:-1]:
715
- if part not in current:
716
- current[part] = {}
717
- current = current[part]
718
-
719
- # Set the value
720
- current[parts[-1]] = value
721
-
722
- # Save the template
723
- with open(template_path, "w") as f:
724
- json.dump(template, f, indent=2)
725
-
726
- self.console.print(
727
- f"[green]Field '{field_name}' updated successfully![/green]"
728
- )
729
-
730
- except json.JSONDecodeError:
731
- self.console.print("[red]Invalid JSON value. Please try again.[/red]")
732
- except Exception as e:
733
- self.console.print(f"[red]Error updating field: {e}[/red]")
520
+ self.template_editor.modify_template_field(template, template_path)
734
521
 
735
522
  def _remove_template_field(self, template: Dict, template_path: Path) -> None:
736
523
  """Remove a field from the template."""
737
- field_name = Prompt.ask(
738
- "Enter field name to remove (use dot notation for nested)"
739
- )
740
-
741
- try:
742
- # Navigate to the field location
743
- parts = field_name.split(".")
744
- current = template
745
-
746
- for part in parts[:-1]:
747
- if part not in current:
748
- raise KeyError(f"Field '{field_name}' not found")
749
- current = current[part]
750
-
751
- # Remove the field
752
- if parts[-1] in current:
753
- del current[parts[-1]]
754
-
755
- # Save the template
756
- with open(template_path, "w") as f:
757
- json.dump(template, f, indent=2)
758
-
759
- self.console.print(
760
- f"[green]Field '{field_name}' removed successfully![/green]"
761
- )
762
- else:
763
- self.console.print(f"[red]Field '{field_name}' not found.[/red]")
764
-
765
- except Exception as e:
766
- self.console.print(f"[red]Error removing field: {e}[/red]")
524
+ self.template_editor.remove_template_field(template, template_path)
767
525
 
768
526
  def _reset_template(self, agent: AgentConfig, template_path: Path) -> None:
769
527
  """Reset template to defaults."""
770
- if Confirm.ask(f"[yellow]Reset '{agent.name}' template to defaults?[/yellow]"):
771
- # Remove custom template file
772
- template_path.unlink(missing_ok=True)
773
- self.console.print(
774
- f"[green]Template for '{agent.name}' reset to defaults![/green]"
775
- )
528
+ self.template_editor.reset_template(agent, template_path)
776
529
 
777
530
  def _create_custom_template_copy(self, agent: AgentConfig, template: Dict) -> None:
778
531
  """Create a customized copy of a system template."""
779
- if self.current_scope == "project":
780
- config_dir = self.project_dir / ".claude-mpm" / "agents"
781
- else:
782
- config_dir = Path.home() / ".claude-mpm" / "agents"
532
+ self.template_editor.create_custom_template_copy(agent, template)
783
533
 
784
- config_dir.mkdir(parents=True, exist_ok=True)
785
- custom_path = config_dir / f"{agent.name}.json"
534
+ def _view_full_template(self, template: Dict) -> None:
535
+ """View the full template without truncation."""
536
+ self.template_editor.view_full_template(template)
786
537
 
787
- if custom_path.exists() and not Confirm.ask(
788
- "[yellow]Custom template already exists. Overwrite?[/yellow]"
789
- ):
790
- return
538
+ def _reset_agent_defaults(self, agents: List[AgentConfig]) -> None:
539
+ """Reset an agent to default enabled state and remove custom template."""
540
+ self.template_editor.reset_agent_defaults(agents)
791
541
 
792
- # Save the template copy
793
- with open(custom_path, "w") as f:
794
- json.dump(template, f, indent=2)
542
+ def _edit_templates(self) -> None:
543
+ """Template editing interface."""
544
+ self.template_editor.edit_templates_interface()
795
545
 
796
- self.console.print(f"[green]Created custom template at: {custom_path}[/green]")
797
- self.console.print("[green]You can now edit this template.[/green]")
546
+ def _manage_behaviors(self) -> None:
547
+ """Behavior file management interface."""
548
+ # Note: BehaviorManager handles its own loop and clears screen
549
+ # but doesn't display our header. We'll need to update BehaviorManager
550
+ # to accept a header callback in the future. For now, just delegate.
551
+ self.behavior_manager.manage_behaviors()
798
552
 
799
- def _view_full_template(self, template: Dict) -> None:
800
- """View the full template without truncation."""
801
- self.console.clear()
802
- self.console.print("[bold]Full Template View:[/bold]\n")
553
+ def _manage_skills(self) -> None:
554
+ """Skills management interface."""
555
+ from ...cli.interactive.skills_wizard import SkillsWizard
556
+ from ...skills.skill_manager import get_manager
803
557
 
804
- json_str = json.dumps(template, indent=2)
805
- syntax = Syntax(json_str, "json", theme="monokai", line_numbers=True)
558
+ wizard = SkillsWizard()
559
+ manager = get_manager()
806
560
 
807
- # Use pager for long content
561
+ while True:
562
+ self.console.clear()
563
+ self._display_header()
808
564
 
809
- with self.console.pager():
810
- self.console.print(syntax)
565
+ self.console.print("\n[bold]Skills Management Options:[/bold]\n")
566
+ self.console.print(" [1] View Available Skills")
567
+ self.console.print(" [2] Configure Skills for Agents")
568
+ self.console.print(" [3] View Current Skill Mappings")
569
+ self.console.print(" [4] Auto-Link Skills to Agents")
570
+ self.console.print(" [b] Back to Main Menu")
571
+ self.console.print()
811
572
 
812
- def _view_agent_details(self, agents: List[AgentConfig]) -> None:
813
- """View detailed information about an agent."""
814
- agent_id = Prompt.ask("Enter agent ID to view")
573
+ choice = Prompt.ask("[bold blue]Select an option[/bold blue]", default="b")
815
574
 
816
- try:
817
- idx = int(agent_id) - 1
818
- if 0 <= idx < len(agents):
819
- agent = agents[idx]
575
+ if choice == "1":
576
+ # View available skills
577
+ self.console.clear()
578
+ self._display_header()
579
+ wizard.list_available_skills()
580
+ Prompt.ask("\nPress Enter to continue")
820
581
 
582
+ elif choice == "2":
583
+ # Configure skills interactively
821
584
  self.console.clear()
822
585
  self._display_header()
823
586
 
824
- # Try to load full template for more details
825
- template_path = self._get_agent_template_path(agent.name)
826
- extra_info = ""
587
+ # Get list of enabled agents
588
+ agents = self.agent_manager.discover_agents()
589
+ # Filter BASE_AGENT from all agent operations (1M-502 Phase 1)
590
+ agents = self._filter_agent_configs(agents, filter_deployed=False)
591
+ enabled_agents = [
592
+ a.name
593
+ for a in agents
594
+ if self.agent_manager.get_pending_state(a.name)
595
+ ]
596
+
597
+ if not enabled_agents:
598
+ self.console.print(
599
+ "[yellow]No agents are currently enabled.[/yellow]"
600
+ )
601
+ self.console.print(
602
+ "Please enable agents first in Agent Management."
603
+ )
604
+ Prompt.ask("\nPress Enter to continue")
605
+ continue
827
606
 
828
- if template_path.exists():
829
- try:
830
- with open(template_path) as f:
831
- template = json.load(f)
607
+ # Run skills wizard
608
+ success, mapping = wizard.run_interactive_selection(enabled_agents)
832
609
 
833
- # Extract additional information
834
- metadata = template.get("metadata", {})
835
- capabilities = template.get("capabilities", {})
610
+ if success:
611
+ # Save the configuration
612
+ manager.save_mappings_to_config()
613
+ self.console.print("\n[green]✓ Skills configuration saved![/green]")
614
+ else:
615
+ self.console.print(
616
+ "\n[yellow]Skills configuration cancelled.[/yellow]"
617
+ )
836
618
 
837
- # Get full description if available
838
- full_desc = metadata.get("description", agent.description)
619
+ Prompt.ask("\nPress Enter to continue")
839
620
 
840
- # Get model and tools
841
- model = capabilities.get("model", "default")
842
- tools = capabilities.get("tools", [])
621
+ elif choice == "3":
622
+ # View current mappings
623
+ self.console.clear()
624
+ self._display_header()
843
625
 
844
- # Get tags
845
- tags = metadata.get("tags", [])
626
+ self.console.print("\n[bold]Current Skill Mappings:[/bold]\n")
846
627
 
847
- # Get version info
848
- agent_version = template.get("agent_version", "N/A")
849
- schema_version = template.get("schema_version", "N/A")
628
+ mappings = manager.list_agent_skill_mappings()
629
+ if not mappings:
630
+ self.console.print("[dim]No skill mappings configured yet.[/dim]")
631
+ else:
632
+ from rich.table import Table
850
633
 
851
- extra_info = f"""
852
- [bold]Full Description:[/bold]
853
- {full_desc}
634
+ table = Table(show_header=True, header_style="bold white")
635
+ table.add_column("Agent", style="white", no_wrap=True)
636
+ table.add_column("Skills", style="green", no_wrap=True)
854
637
 
855
- [bold]Model:[/bold] {model}
856
- [bold]Agent Version:[/bold] {agent_version}
857
- [bold]Schema Version:[/bold] {schema_version}
858
- [bold]Tags:[/bold] {', '.join(tags) if tags else 'None'}
859
- [bold]Tools:[/bold] {', '.join(tools[:5]) if tools else 'None'}{'...' if len(tools) > 5 else ''}
860
- """
861
- except:
862
- pass
863
-
864
- # Create detail panel
865
- detail_text = f"""
866
- [bold]Name:[/bold] {agent.name}
867
- [bold]Status:[/bold] {'[green]Enabled[/green]' if self.agent_manager.is_agent_enabled(agent.name) else '[red]Disabled[/red]'}
868
- [bold]Template Path:[/bold] {template_path}
869
- [bold]Is System Template:[/bold] {'Yes' if str(template_path).startswith(str(self.agent_manager.templates_dir)) else 'No (Custom)'}
870
- {extra_info}
871
- """
872
-
873
- panel = Panel(
874
- detail_text.strip(),
875
- title=f"[bold]{agent.name} Details[/bold]",
876
- box=ROUNDED,
877
- style="cyan",
878
- )
638
+ for agent_id, skills in mappings.items():
639
+ skills_str = (
640
+ ", ".join(skills) if skills else "[dim](none)[/dim]"
641
+ )
642
+ table.add_row(agent_id, skills_str)
879
643
 
880
- self.console.print(panel)
644
+ self.console.print(table)
881
645
 
882
- else:
883
- self.console.print("[red]Invalid agent ID.[/red]")
646
+ Prompt.ask("\nPress Enter to continue")
884
647
 
885
- except ValueError:
886
- self.console.print("[red]Invalid input. Please enter a number.[/red]")
648
+ elif choice == "4":
649
+ # Auto-link skills
650
+ self.console.clear()
651
+ self._display_header()
887
652
 
888
- Prompt.ask("\nPress Enter to continue")
653
+ self.console.print("\n[bold]Auto-Linking Skills to Agents...[/bold]\n")
889
654
 
890
- def _edit_templates(self) -> None:
891
- """Template editing interface."""
892
- self.console.print("[yellow]Template editing interface - Coming soon![/yellow]")
893
- Prompt.ask("Press Enter to continue")
655
+ # Get enabled agents
656
+ agents = self.agent_manager.discover_agents()
657
+ # Filter BASE_AGENT from all agent operations (1M-502 Phase 1)
658
+ agents = self._filter_agent_configs(agents, filter_deployed=False)
659
+ enabled_agents = [
660
+ a.name
661
+ for a in agents
662
+ if self.agent_manager.get_pending_state(a.name)
663
+ ]
894
664
 
895
- def _manage_behaviors(self) -> None:
896
- """Behavior file management interface."""
897
- while True:
898
- self.console.clear()
899
- self._display_header()
665
+ if not enabled_agents:
666
+ self.console.print(
667
+ "[yellow]No agents are currently enabled.[/yellow]"
668
+ )
669
+ self.console.print(
670
+ "Please enable agents first in Agent Management."
671
+ )
672
+ Prompt.ask("\nPress Enter to continue")
673
+ continue
900
674
 
901
- self.console.print("[bold]Behavior File Management[/bold]\n")
675
+ # Auto-link
676
+ mapping = wizard._auto_link_skills(enabled_agents)
902
677
 
903
- # Display current behavior files
904
- self._display_behavior_files()
678
+ # Display preview
679
+ self.console.print("Auto-linked skills:\n")
680
+ for agent_id, skills in mapping.items():
681
+ self.console.print(f" [yellow]{agent_id}[/yellow]:")
682
+ for skill in skills:
683
+ self.console.print(f" - {skill}")
905
684
 
906
- # Show behavior menu
907
- self.console.print("\n[bold]Options:[/bold]")
908
- self.console.print(" [cyan][1][/cyan] Edit identity configuration")
909
- self.console.print(" [cyan][2][/cyan] Edit workflow configuration")
910
- self.console.print(" [cyan][3][/cyan] Import behavior file")
911
- self.console.print(" [cyan][4][/cyan] Export behavior file")
912
- self.console.print(" [cyan][b][/cyan] Back to main menu")
913
- self.console.print()
685
+ # Confirm
686
+ confirm = Confirm.ask("\nApply this configuration?", default=True)
914
687
 
915
- choice = Prompt.ask("[bold cyan]Select an option[/bold cyan]", default="b")
688
+ if confirm:
689
+ wizard._apply_skills_configuration(mapping)
690
+ manager.save_mappings_to_config()
691
+ self.console.print("\n[green]✓ Auto-linking complete![/green]")
692
+ else:
693
+ self.console.print("\n[yellow]Auto-linking cancelled.[/yellow]")
694
+
695
+ Prompt.ask("\nPress Enter to continue")
916
696
 
917
- if choice == "b":
697
+ elif choice == "b":
918
698
  break
919
- if choice == "1":
920
- self._edit_identity_config()
921
- elif choice == "2":
922
- self._edit_workflow_config()
923
- elif choice == "3":
924
- self._import_behavior_file()
925
- elif choice == "4":
926
- self._export_behavior_file()
927
699
  else:
928
- self.console.print("[red]Invalid choice.[/red]")
929
- Prompt.ask("Press Enter to continue")
700
+ self.console.print("[red]Invalid choice. Please try again.[/red]")
701
+ Prompt.ask("\nPress Enter to continue")
930
702
 
931
703
  def _display_behavior_files(self) -> None:
932
704
  """Display current behavior files."""
933
- if self.current_scope == "project":
934
- config_dir = self.project_dir / ".claude-mpm" / "behaviors"
935
- else:
936
- config_dir = Path.home() / ".claude-mpm" / "behaviors"
937
-
938
- config_dir.mkdir(parents=True, exist_ok=True)
939
-
940
- table = Table(title="Behavior Files", box=ROUNDED)
941
- table.add_column("File", style="cyan", width=30)
942
- table.add_column("Size", style="dim", width=10)
943
- table.add_column("Modified", style="white", width=20)
944
-
945
- identity_file = config_dir / "identity.yaml"
946
- workflow_file = config_dir / "workflow.yaml"
947
-
948
- for file_path in [identity_file, workflow_file]:
949
- if file_path.exists():
950
- stat = file_path.stat()
951
- size = f"{stat.st_size} bytes"
952
- modified = f"{stat.st_mtime:.0f}" # Simplified timestamp
953
- table.add_row(file_path.name, size, modified)
954
- else:
955
- table.add_row(file_path.name, "[dim]Not found[/dim]", "-")
956
-
957
- self.console.print(table)
705
+ self.behavior_manager.display_behavior_files()
958
706
 
959
707
  def _edit_identity_config(self) -> None:
960
708
  """Edit identity configuration."""
961
- self.console.print(
962
- "[yellow]Identity configuration editor - Coming soon![/yellow]"
963
- )
964
- Prompt.ask("Press Enter to continue")
709
+ self.behavior_manager.edit_identity_config()
965
710
 
966
711
  def _edit_workflow_config(self) -> None:
967
712
  """Edit workflow configuration."""
968
- self.console.print(
969
- "[yellow]Workflow configuration editor - Coming soon![/yellow]"
970
- )
971
- Prompt.ask("Press Enter to continue")
713
+ self.behavior_manager.edit_workflow_config()
972
714
 
973
715
  def _import_behavior_file(self) -> None:
974
716
  """Import a behavior file."""
975
- file_path = Prompt.ask("Enter path to behavior file to import")
717
+ self.behavior_manager.import_behavior_file()
976
718
 
977
- try:
978
- source = Path(file_path)
979
- if not source.exists():
980
- self.console.print(f"[red]File not found: {file_path}[/red]")
981
- return
719
+ def _export_behavior_file(self) -> None:
720
+ """Export a behavior file."""
721
+ self.behavior_manager.export_behavior_file()
982
722
 
983
- # Determine target directory
984
- if self.current_scope == "project":
985
- config_dir = self.project_dir / ".claude-mpm" / "behaviors"
986
- else:
987
- config_dir = Path.home() / ".claude-mpm" / "behaviors"
723
+ def _manage_startup_configuration(self) -> bool:
724
+ """Manage startup configuration for MCP services and agents."""
725
+ return self.startup_manager.manage_startup_configuration()
988
726
 
989
- config_dir.mkdir(parents=True, exist_ok=True)
727
+ def _load_startup_configuration(self, config: Config) -> Dict:
728
+ """Load current startup configuration from config."""
729
+ return self.startup_manager.load_startup_configuration(config)
990
730
 
991
- # Copy file
992
- import shutil
731
+ def _display_startup_configuration(self, startup_config: Dict) -> None:
732
+ """Display current startup configuration in a table."""
733
+ self.startup_manager.display_startup_configuration(startup_config)
993
734
 
994
- target = config_dir / source.name
995
- shutil.copy2(source, target)
735
+ def _configure_mcp_services(self, startup_config: Dict, config: Config) -> None:
736
+ """Configure which MCP services to enable at startup."""
737
+ self.startup_manager.configure_mcp_services(startup_config, config)
996
738
 
997
- self.console.print(f"[green]Successfully imported {source.name}![/green]")
739
+ def _configure_hook_services(self, startup_config: Dict, config: Config) -> None:
740
+ """Configure which hook services to enable at startup."""
741
+ self.startup_manager.configure_hook_services(startup_config, config)
998
742
 
999
- except Exception as e:
1000
- self.console.print(f"[red]Error importing file: {e}[/red]")
743
+ def _configure_system_agents(self, startup_config: Dict, config: Config) -> None:
744
+ """Configure which system agents to deploy at startup."""
745
+ self.startup_manager.configure_system_agents(startup_config, config)
1001
746
 
1002
- Prompt.ask("Press Enter to continue")
747
+ def _parse_id_selection(self, selection: str, max_id: int) -> List[int]:
748
+ """Parse ID selection string (e.g., '1,3,5' or '1-4')."""
749
+ return parse_id_selection(selection, max_id)
1003
750
 
1004
- def _export_behavior_file(self) -> None:
1005
- """Export a behavior file."""
1006
- self.console.print("[yellow]Behavior file export - Coming soon![/yellow]")
1007
- Prompt.ask("Press Enter to continue")
751
+ def _enable_all_services(self, startup_config: Dict, config: Config) -> None:
752
+ """Enable all services and agents."""
753
+ self.startup_manager.enable_all_services(startup_config, config)
1008
754
 
1009
- def _switch_scope(self) -> None:
1010
- """Switch between project and user scope."""
1011
- self.current_scope = "user" if self.current_scope == "project" else "project"
1012
- self.console.print(f"[green]Switched to {self.current_scope} scope[/green]")
1013
- Prompt.ask("Press Enter to continue")
755
+ def _disable_all_services(self, startup_config: Dict, config: Config) -> None:
756
+ """Disable all services and agents."""
757
+ self.startup_manager.disable_all_services(startup_config, config)
1014
758
 
1015
- def _show_version_info_interactive(self) -> None:
1016
- """Show version information in interactive mode."""
1017
- self.console.clear()
1018
- self._display_header()
759
+ def _reset_to_defaults(self, startup_config: Dict, config: Config) -> None:
760
+ """Reset startup configuration to defaults."""
761
+ self.startup_manager.reset_to_defaults(startup_config, config)
1019
762
 
1020
- # Get version information
1021
- mpm_version = self.version_service.get_version()
1022
- build_number = self.version_service.get_build_number()
763
+ def _save_startup_configuration(self, startup_config: Dict, config: Config) -> bool:
764
+ """Save startup configuration to config file and return whether to proceed to startup."""
765
+ return self.startup_manager.save_startup_configuration(startup_config, config)
1023
766
 
1024
- # Try to get Claude Code version using the installer's method
1025
- claude_version = "Unknown"
1026
- try:
1027
- from ...hooks.claude_hooks.installer import HookInstaller
1028
-
1029
- installer = HookInstaller()
1030
- detected_version = installer.get_claude_version()
1031
- if detected_version:
1032
- is_compatible, _ = installer.is_version_compatible()
1033
- claude_version = f"{detected_version} (Claude Code)"
1034
- if not is_compatible:
1035
- claude_version += (
1036
- f" - Monitoring requires {installer.MIN_CLAUDE_VERSION}+"
1037
- )
1038
- else:
1039
- # Fallback to direct subprocess call
1040
- import subprocess
1041
-
1042
- result = subprocess.run(
1043
- ["claude", "--version"],
1044
- capture_output=True,
1045
- text=True,
1046
- timeout=5,
1047
- check=False,
1048
- )
1049
- if result.returncode == 0:
1050
- claude_version = result.stdout.strip()
1051
- except:
1052
- pass
1053
-
1054
- # Create version panel
1055
- version_text = f"""
1056
- [bold cyan]Claude MPM[/bold cyan]
1057
- Version: {mpm_version}
1058
- Build: {build_number}
1059
-
1060
- [bold cyan]Claude Code[/bold cyan]
1061
- Version: {claude_version}
1062
-
1063
- [bold cyan]Python[/bold cyan]
1064
- Version: {sys.version.split()[0]}
1065
-
1066
- [bold cyan]Configuration[/bold cyan]
1067
- Scope: {self.current_scope}
1068
- Directory: {self.project_dir}
1069
- """
767
+ def _save_all_configuration(self) -> bool:
768
+ """Save all configuration changes across all contexts."""
769
+ return self.startup_manager.save_all_configuration()
1070
770
 
1071
- panel = Panel(
1072
- version_text.strip(),
1073
- title="[bold]Version Information[/bold]",
1074
- box=ROUNDED,
1075
- style="green",
1076
- )
771
+ def _launch_claude_mpm(self) -> None:
772
+ """Launch Claude MPM run command, replacing current process."""
773
+ self.navigation.launch_claude_mpm()
1077
774
 
1078
- self.console.print(panel)
1079
- Prompt.ask("\nPress Enter to continue")
775
+ def _switch_scope(self) -> None:
776
+ """Switch between project and user scope."""
777
+ self.navigation.switch_scope()
778
+ # Sync scope back from navigation
779
+ self.current_scope = self.navigation.current_scope
780
+
781
+ def _show_version_info_interactive(self) -> None:
782
+ """Show version information in interactive mode."""
783
+ self.persistence.show_version_info_interactive()
1080
784
 
1081
785
  # Non-interactive command methods
1082
786
 
1083
787
  def _list_agents_non_interactive(self) -> CommandResult:
1084
788
  """List agents in non-interactive mode."""
1085
789
  agents = self.agent_manager.discover_agents()
790
+ # Filter BASE_AGENT from all agent lists (1M-502 Phase 1)
791
+ agents = self._filter_agent_configs(agents, filter_deployed=False)
1086
792
 
1087
793
  data = []
1088
794
  for agent in agents:
@@ -1118,291 +824,714 @@ Directory: {self.project_dir}
1118
824
 
1119
825
  def _export_config(self, file_path: str) -> CommandResult:
1120
826
  """Export configuration to a file."""
1121
- try:
1122
- # Gather all configuration
1123
- config_data = {"scope": self.current_scope, "agents": {}, "behaviors": {}}
827
+ return self.persistence.export_config(file_path)
1124
828
 
1125
- # Get agent states
1126
- agents = self.agent_manager.discover_agents()
1127
- for agent in agents:
1128
- config_data["agents"][agent.name] = {
1129
- "enabled": self.agent_manager.is_agent_enabled(agent.name),
1130
- "template_path": str(self._get_agent_template_path(agent.name)),
1131
- }
829
+ def _import_config(self, file_path: str) -> CommandResult:
830
+ """Import configuration from a file."""
831
+ return self.persistence.import_config(file_path)
1132
832
 
1133
- # Write to file
1134
- output_path = Path(file_path)
1135
- with open(output_path, "w") as f:
1136
- json.dump(config_data, f, indent=2)
833
+ def _show_version_info(self) -> CommandResult:
834
+ """Show version information in non-interactive mode."""
835
+ return self.persistence.show_version_info()
1137
836
 
1138
- return CommandResult.success_result(
1139
- f"Configuration exported to {output_path}"
1140
- )
837
+ def _install_hooks(self, force: bool = False) -> CommandResult:
838
+ """Install Claude MPM hooks for Claude Code integration."""
839
+ # Share logger with hook manager for consistent error logging
840
+ self.hook_manager.logger = self.logger
841
+ return self.hook_manager.install_hooks(force=force)
1141
842
 
843
+ def _verify_hooks(self) -> CommandResult:
844
+ """Verify that Claude MPM hooks are properly installed."""
845
+ # Share logger with hook manager for consistent error logging
846
+ self.hook_manager.logger = self.logger
847
+ return self.hook_manager.verify_hooks()
848
+
849
+ def _uninstall_hooks(self) -> CommandResult:
850
+ """Uninstall Claude MPM hooks."""
851
+ # Share logger with hook manager for consistent error logging
852
+ self.hook_manager.logger = self.logger
853
+ return self.hook_manager.uninstall_hooks()
854
+
855
+ def _run_agent_management(self) -> CommandResult:
856
+ """Jump directly to agent management."""
857
+ try:
858
+ self._manage_agents()
859
+ return CommandResult.success_result("Agent management completed")
860
+ except KeyboardInterrupt:
861
+ return CommandResult.success_result("Agent management cancelled")
1142
862
  except Exception as e:
1143
- return CommandResult.error_result(f"Failed to export configuration: {e}")
863
+ return CommandResult.error_result(f"Agent management failed: {e}")
1144
864
 
1145
- def _import_config(self, file_path: str) -> CommandResult:
1146
- """Import configuration from a file."""
865
+ def _run_template_editing(self) -> CommandResult:
866
+ """Jump directly to template editing."""
1147
867
  try:
1148
- input_path = Path(file_path)
1149
- if not input_path.exists():
1150
- return CommandResult.error_result(f"File not found: {file_path}")
1151
-
1152
- with open(input_path) as f:
1153
- config_data = json.load(f)
1154
-
1155
- # Apply agent states
1156
- if "agents" in config_data:
1157
- for agent_name, agent_config in config_data["agents"].items():
1158
- if "enabled" in agent_config:
1159
- self.agent_manager.set_agent_enabled(
1160
- agent_name, agent_config["enabled"]
1161
- )
868
+ self._edit_templates()
869
+ return CommandResult.success_result("Template editing completed")
870
+ except KeyboardInterrupt:
871
+ return CommandResult.success_result("Template editing cancelled")
872
+ except Exception as e:
873
+ return CommandResult.error_result(f"Template editing failed: {e}")
1162
874
 
1163
- return CommandResult.success_result(
1164
- f"Configuration imported from {input_path}"
1165
- )
875
+ def _run_behavior_management(self) -> CommandResult:
876
+ """Jump directly to behavior management."""
877
+ return self.behavior_manager.run_behavior_management()
1166
878
 
879
+ def _run_startup_configuration(self) -> CommandResult:
880
+ """Jump directly to startup configuration."""
881
+ try:
882
+ proceed = self._manage_startup_configuration()
883
+ if proceed:
884
+ return CommandResult.success_result(
885
+ "Startup configuration saved, proceeding to startup"
886
+ )
887
+ return CommandResult.success_result("Startup configuration completed")
888
+ except KeyboardInterrupt:
889
+ return CommandResult.success_result("Startup configuration cancelled")
1167
890
  except Exception as e:
1168
- return CommandResult.error_result(f"Failed to import configuration: {e}")
891
+ return CommandResult.error_result(f"Startup configuration failed: {e}")
1169
892
 
1170
- def _show_version_info(self) -> CommandResult:
1171
- """Show version information in non-interactive mode."""
1172
- mpm_version = self.version_service.get_version()
1173
- build_number = self.version_service.get_build_number()
1174
-
1175
- data = {
1176
- "mpm_version": mpm_version,
1177
- "build_number": build_number,
1178
- "python_version": sys.version.split()[0],
1179
- }
893
+ # ========================================================================
894
+ # Enhanced Agent Management Methods (Remote Agent Discovery Integration)
895
+ # ========================================================================
1180
896
 
1181
- # Try to get Claude version
897
+ def _get_configured_sources(self) -> List[Dict]:
898
+ """Get list of configured agent sources with agent counts."""
1182
899
  try:
1183
- import subprocess
1184
-
1185
- result = subprocess.run(
1186
- ["claude", "--version"],
1187
- capture_output=True,
1188
- text=True,
1189
- timeout=5,
1190
- check=False,
900
+ from claude_mpm.config.agent_sources import AgentSourceConfiguration
901
+
902
+ config = AgentSourceConfiguration.load()
903
+
904
+ # Convert repositories to source dictionaries
905
+ sources = []
906
+ for repo in config.repositories:
907
+ # Extract identifier from repository
908
+ identifier = repo.identifier
909
+
910
+ # Count agents in cache
911
+ cache_dir = (
912
+ Path.home() / ".claude-mpm" / "cache" / "remote-agents" / identifier
913
+ )
914
+ agent_count = 0
915
+ if cache_dir.exists():
916
+ agents_dir = cache_dir / "agents"
917
+ if agents_dir.exists():
918
+ agent_count = len(list(agents_dir.rglob("*.md")))
919
+
920
+ sources.append(
921
+ {
922
+ "identifier": identifier,
923
+ "url": repo.url,
924
+ "enabled": repo.enabled,
925
+ "priority": repo.priority,
926
+ "agent_count": agent_count,
927
+ }
928
+ )
929
+
930
+ return sources
931
+ except Exception as e:
932
+ self.logger.warning(f"Failed to get configured sources: {e}")
933
+ return []
934
+
935
+ def _filter_agent_configs(
936
+ self, agents: List[AgentConfig], filter_deployed: bool = False
937
+ ) -> List[AgentConfig]:
938
+ """Filter AgentConfig objects using agent_filters utilities.
939
+
940
+ Converts AgentConfig objects to dictionaries for filtering,
941
+ then back to AgentConfig. Always filters BASE_AGENT.
942
+ Optionally filters deployed agents.
943
+
944
+ Args:
945
+ agents: List of AgentConfig objects
946
+ filter_deployed: Whether to filter out deployed agents (default: False)
947
+
948
+ Returns:
949
+ Filtered list of AgentConfig objects
950
+ """
951
+ # Convert AgentConfig to dict format for filtering
952
+ agent_dicts = []
953
+ for agent in agents:
954
+ agent_dicts.append(
955
+ {
956
+ "agent_id": agent.name,
957
+ "name": agent.name,
958
+ "description": agent.description,
959
+ "deployed": getattr(agent, "is_deployed", False),
960
+ }
1191
961
  )
1192
- if result.returncode == 0:
1193
- data["claude_version"] = result.stdout.strip()
1194
- except:
1195
- data["claude_version"] = "Unknown"
1196
962
 
1197
- # Print formatted output
1198
- self.console.print(
1199
- f"[bold]Claude MPM:[/bold] {mpm_version} (build {build_number})"
963
+ # Apply filters (always filter BASE_AGENT)
964
+ filtered_dicts = apply_all_filters(
965
+ agent_dicts, filter_base=True, filter_deployed=filter_deployed
1200
966
  )
967
+
968
+ # Convert back to AgentConfig objects
969
+ filtered_names = {d["agent_id"] for d in filtered_dicts}
970
+ return [a for a in agents if a.name in filtered_names]
971
+
972
+ def _display_agents_with_source_info(self, agents: List[AgentConfig]) -> None:
973
+ """Display agents table with source information and installation status."""
974
+ from rich.table import Table
975
+
976
+ agents_table = Table(show_header=True, header_style="bold white")
977
+ agents_table.add_column("#", style="dim", width=4, no_wrap=True)
978
+ agents_table.add_column(
979
+ "Agent ID", style="white", width=35, no_wrap=True, overflow="ellipsis"
980
+ )
981
+ agents_table.add_column(
982
+ "Name", style="white", width=25, no_wrap=True, overflow="ellipsis"
983
+ )
984
+ agents_table.add_column("Source", style="bright_yellow", width=20, no_wrap=True)
985
+ agents_table.add_column("Status", style="white", width=12, no_wrap=True)
986
+
987
+ for idx, agent in enumerate(agents, 1):
988
+ # Determine source with repo name
989
+ source_type = getattr(agent, "source_type", "local")
990
+
991
+ if source_type == "remote":
992
+ # Get repo name from agent metadata
993
+ source_dict = getattr(agent, "source_dict", {})
994
+ repo_url = source_dict.get("source", "")
995
+
996
+ # Extract repo name from URL
997
+ if (
998
+ "bobmatnyc/claude-mpm" in repo_url
999
+ or "claude-mpm" in repo_url.lower()
1000
+ ):
1001
+ source_label = "MPM Agents"
1002
+ elif "/" in repo_url:
1003
+ # Extract last part of org/repo
1004
+ parts = repo_url.rstrip("/").split("/")
1005
+ if len(parts) >= 2:
1006
+ source_label = f"{parts[-2]}/{parts[-1]}"
1007
+ else:
1008
+ source_label = "Community"
1009
+ else:
1010
+ source_label = "Community"
1011
+ else:
1012
+ source_label = "Local"
1013
+
1014
+ # Determine installation status (removed symbols for cleaner look)
1015
+ is_installed = getattr(agent, "is_deployed", False)
1016
+ if is_installed:
1017
+ status = "[green]Installed[/green]"
1018
+ else:
1019
+ status = "Available"
1020
+
1021
+ # Get display name (for remote agents, use display_name instead of agent_id)
1022
+ display_name = getattr(agent, "display_name", agent.name)
1023
+ # Let overflow="ellipsis" handle truncation automatically
1024
+
1025
+ agents_table.add_row(
1026
+ str(idx), agent.name, display_name, source_label, status
1027
+ )
1028
+
1029
+ self.console.print(agents_table)
1030
+ self.console.print(f"\n[dim]Total: {len(agents)} agents available[/dim]")
1031
+
1032
+ def _manage_sources(self) -> None:
1033
+ """Interactive source management."""
1034
+ self.console.print("\n[bold white]═══ Manage Agent Sources ═══[/bold white]\n")
1201
1035
  self.console.print(
1202
- f"[bold]Claude Code:[/bold] {data.get('claude_version', 'Unknown')}"
1036
+ "[dim]Use 'claude-mpm agent-source' command to add/remove sources[/dim]"
1203
1037
  )
1204
- self.console.print(f"[bold]Python:[/bold] {data['python_version']}")
1038
+ self.console.print("\nExamples:")
1039
+ self.console.print(" claude-mpm agent-source add <git-url>")
1040
+ self.console.print(" claude-mpm agent-source remove <identifier>")
1041
+ self.console.print(" claude-mpm agent-source list")
1042
+ Prompt.ask("\nPress Enter to continue")
1205
1043
 
1206
- return CommandResult.success_result("Version information displayed", data=data)
1044
+ def _deploy_agents_individual(self, agents: List[AgentConfig]) -> None:
1045
+ """Manage agent installation state (unified install/remove interface)."""
1046
+ if not agents:
1047
+ self.console.print("[yellow]No agents available[/yellow]")
1048
+ Prompt.ask("\nPress Enter to continue")
1049
+ return
1207
1050
 
1208
- def _install_hooks(self, force: bool = False) -> CommandResult:
1209
- """Install Claude MPM hooks for Claude Code integration."""
1210
- try:
1211
- from ...hooks.claude_hooks.installer import HookInstaller
1051
+ # Get ALL agents (filter BASE_AGENT but keep deployed agents visible)
1052
+ from claude_mpm.utils.agent_filters import (
1053
+ filter_base_agents,
1054
+ get_deployed_agent_ids,
1055
+ )
1056
+
1057
+ # Filter BASE_AGENT but keep deployed agents visible
1058
+ all_agents = filter_base_agents(
1059
+ [
1060
+ {
1061
+ "agent_id": a.name,
1062
+ "name": a.name,
1063
+ "description": a.description,
1064
+ "deployed": getattr(a, "is_deployed", False),
1065
+ }
1066
+ for a in agents
1067
+ ]
1068
+ )
1069
+
1070
+ # Get deployed agent IDs
1071
+ deployed_ids = get_deployed_agent_ids()
1072
+
1073
+ if not all_agents:
1074
+ self.console.print("[yellow]No agents available[/yellow]")
1075
+ Prompt.ask("\nPress Enter to continue")
1076
+ return
1077
+
1078
+ # Loop to allow adjusting selection
1079
+ while True:
1080
+ # Build checkbox choices with pre-selection
1081
+ agent_choices = []
1082
+ agent_map = {} # For lookup after selection
1083
+
1084
+ for agent in agents:
1085
+ if agent.name in {a["agent_id"] for a in all_agents}:
1086
+ display_name = getattr(agent, "display_name", agent.name)
1087
+
1088
+ # Pre-check if deployed
1089
+ # Extract leaf name from full path for comparison with deployed_ids
1090
+ agent_leaf_name = agent.name.split("/")[-1]
1091
+ is_deployed = agent_leaf_name in deployed_ids
1092
+
1093
+ # Simple format: "agent/path - Display Name"
1094
+ # Checkbox state (checked/unchecked) indicates installed status
1095
+ choice_text = f"{agent.name}"
1096
+ if display_name and display_name != agent.name:
1097
+ choice_text += f" - {display_name}"
1098
+
1099
+ # Create choice with checked=True for deployed agents
1100
+ # Note: questionary's default param is for single-select only
1101
+ # For multi-select, must use checked=True on Choice objects
1102
+ choice = questionary.Choice(
1103
+ title=choice_text, value=agent.name, checked=is_deployed
1104
+ )
1105
+
1106
+ agent_choices.append(choice)
1107
+ agent_map[agent.name] = agent
1108
+
1109
+ # Multi-select with pre-selection
1110
+ self.console.print("\n[bold cyan]Manage Agent Installation[/bold cyan]")
1111
+ self.console.print("[dim]✓ Checked = Installed (uncheck to remove)[/dim]")
1112
+ self.console.print("[dim]○ Unchecked = Available (check to install)[/dim]")
1113
+ self.console.print(
1114
+ "[dim]Use arrow keys to navigate, space to toggle, "
1115
+ "Enter to apply changes[/dim]\n"
1116
+ )
1117
+
1118
+ # Pre-selection via checked=True on Choice objects
1119
+ # (questionary's default param is for single-select only)
1120
+ selected_agent_ids = questionary.checkbox(
1121
+ "Agents:", choices=agent_choices, style=self.QUESTIONARY_STYLE
1122
+ ).ask()
1123
+
1124
+ # Handle Esc
1125
+ if selected_agent_ids is None:
1126
+ self.console.print("[yellow]No changes made[/yellow]")
1127
+ Prompt.ask("\nPress Enter to continue")
1128
+ return
1129
+
1130
+ # Convert to sets for comparison
1131
+ selected_set = set(selected_agent_ids)
1132
+ deployed_set = deployed_ids
1133
+
1134
+ # Determine actions
1135
+ to_deploy = selected_set - deployed_set # Selected but not deployed
1136
+ to_remove = deployed_set - selected_set # Deployed but not selected
1137
+
1138
+ if not to_deploy and not to_remove:
1139
+ self.console.print("[yellow]No changes made[/yellow]")
1140
+ Prompt.ask("\nPress Enter to continue")
1141
+ return
1142
+
1143
+ # Show what will happen
1144
+ self.console.print("\n[bold]Changes to apply:[/bold]")
1145
+ if to_deploy:
1146
+ self.console.print(f"[green]Install {len(to_deploy)} agent(s)[/green]")
1147
+ for agent_id in to_deploy:
1148
+ self.console.print(f" + {agent_id}")
1149
+ if to_remove:
1150
+ self.console.print(f"[red]Remove {len(to_remove)} agent(s)[/red]")
1151
+ for agent_id in to_remove:
1152
+ self.console.print(f" - {agent_id}")
1153
+
1154
+ # Ask user to confirm, adjust, or cancel
1155
+ action = questionary.select(
1156
+ "\nWhat would you like to do?",
1157
+ choices=[
1158
+ questionary.Choice("Apply these changes", value="apply"),
1159
+ questionary.Choice("Adjust selection", value="adjust"),
1160
+ questionary.Choice("Cancel", value="cancel"),
1161
+ ],
1162
+ default="apply",
1163
+ style=self.QUESTIONARY_STYLE,
1164
+ ).ask()
1165
+
1166
+ if action == "cancel":
1167
+ self.console.print("[yellow]Changes cancelled[/yellow]")
1168
+ Prompt.ask("\nPress Enter to continue")
1169
+ return
1170
+ if action == "adjust":
1171
+ # Loop back to agent selection
1172
+ continue
1173
+
1174
+ # Execute changes
1175
+ deploy_success = 0
1176
+ deploy_fail = 0
1177
+ remove_success = 0
1178
+ remove_fail = 0
1179
+
1180
+ # Install new agents
1181
+ for agent_id in to_deploy:
1182
+ agent = agent_map.get(agent_id)
1183
+ if agent and self._deploy_single_agent(agent, show_feedback=False):
1184
+ deploy_success += 1
1185
+ self.console.print(f"[green]✓ Installed: {agent_id}[/green]")
1186
+ else:
1187
+ deploy_fail += 1
1188
+ self.console.print(f"[red]✗ Failed to install: {agent_id}[/red]")
1212
1189
 
1213
- installer = HookInstaller()
1190
+ # Remove agents
1191
+ for agent_id in to_remove:
1192
+ try:
1193
+ import json
1194
+ from pathlib import Path
1214
1195
 
1215
- # Check Claude Code version compatibility first
1216
- is_compatible, version_message = installer.is_version_compatible()
1217
- self.console.print("[cyan]Checking Claude Code version...[/cyan]")
1218
- self.console.print(version_message)
1196
+ # Remove from project, legacy, and user locations
1197
+ project_path = (
1198
+ Path.cwd() / ".claude-mpm" / "agents" / f"{agent_id}.md"
1199
+ )
1200
+ legacy_path = Path.cwd() / ".claude" / "agents" / f"{agent_id}.md"
1201
+ user_path = Path.home() / ".claude" / "agents" / f"{agent_id}.md"
1202
+
1203
+ removed = False
1204
+ for path in [project_path, legacy_path, user_path]:
1205
+ if path.exists():
1206
+ path.unlink()
1207
+ removed = True
1208
+
1209
+ # Also remove from virtual deployment state
1210
+ deployment_state_paths = [
1211
+ Path.cwd() / ".claude" / "agents" / ".mpm_deployment_state",
1212
+ Path.home() / ".claude" / "agents" / ".mpm_deployment_state",
1213
+ ]
1214
+
1215
+ for state_path in deployment_state_paths:
1216
+ if state_path.exists():
1217
+ try:
1218
+ with state_path.open() as f:
1219
+ state = json.load(f)
1220
+
1221
+ # Remove agent from deployment state
1222
+ agents = state.get("last_check_results", {}).get(
1223
+ "agents", {}
1224
+ )
1225
+ if agent_id in agents:
1226
+ del agents[agent_id]
1227
+ removed = True
1228
+
1229
+ # Save updated state
1230
+ with state_path.open("w") as f:
1231
+ json.dump(state, f, indent=2)
1232
+ except (json.JSONDecodeError, KeyError) as e:
1233
+ # Log but don't fail - physical removal still counts
1234
+ self.logger.debug(
1235
+ f"Failed to update deployment state at {state_path}: {e}"
1236
+ )
1237
+
1238
+ if removed:
1239
+ remove_success += 1
1240
+ self.console.print(f"[green]✓ Removed: {agent_id}[/green]")
1241
+ else:
1242
+ remove_fail += 1
1243
+ self.console.print(f"[yellow]⚠ Not found: {agent_id}[/yellow]")
1244
+ except Exception as e:
1245
+ remove_fail += 1
1246
+ self.console.print(f"[red]✗ Failed to remove {agent_id}: {e}[/red]")
1219
1247
 
1220
- if not is_compatible:
1248
+ # Show summary
1249
+ self.console.print()
1250
+ if deploy_success > 0:
1221
1251
  self.console.print(
1222
- "\n[yellow] Hook monitoring is not available for your Claude Code version.[/yellow]"
1252
+ f"[green] Installed {deploy_success} agent(s)[/green]"
1223
1253
  )
1254
+ if deploy_fail > 0:
1224
1255
  self.console.print(
1225
- "The dashboard and other features will work without real-time monitoring."
1256
+ f"[red]✗ Failed to install {deploy_fail} agent(s)[/red]"
1226
1257
  )
1258
+ if remove_success > 0:
1227
1259
  self.console.print(
1228
- f"\n[dim]To enable monitoring, upgrade Claude Code to version {installer.MIN_CLAUDE_VERSION} or higher.[/dim]"
1260
+ f"[green] Removed {remove_success} agent(s)[/green]"
1229
1261
  )
1230
- return CommandResult.success_result(
1231
- "Version incompatible with hook monitoring",
1232
- data={"compatible": False, "message": version_message},
1262
+ if remove_fail > 0:
1263
+ self.console.print(
1264
+ f"[red]✗ Failed to remove {remove_fail} agent(s)[/red]"
1233
1265
  )
1234
1266
 
1235
- # Check current status
1236
- status = installer.get_status()
1237
- if status["installed"] and not force:
1238
- self.console.print("[yellow]Hooks are already installed.[/yellow]")
1239
- self.console.print("Use --force to reinstall.")
1267
+ Prompt.ask("\nPress Enter to continue")
1268
+ # Exit the loop after successful execution
1269
+ break
1240
1270
 
1241
- if not status["valid"]:
1242
- self.console.print("\n[red]However, there are issues:[/red]")
1243
- for issue in status["issues"]:
1244
- self.console.print(f" - {issue}")
1271
+ def _deploy_agents_preset(self) -> None:
1272
+ """Install agents using preset configuration."""
1273
+ try:
1274
+ from claude_mpm.services.agents.agent_preset_service import (
1275
+ AgentPresetService,
1276
+ )
1277
+ from claude_mpm.services.agents.git_source_manager import GitSourceManager
1245
1278
 
1246
- return CommandResult.success_result(
1247
- "Hooks already installed", data=status
1248
- )
1279
+ source_manager = GitSourceManager()
1280
+ preset_service = AgentPresetService(source_manager)
1249
1281
 
1250
- # Install hooks
1251
- self.console.print("[cyan]Installing Claude MPM hooks...[/cyan]")
1252
- success = installer.install_hooks(force=force)
1282
+ presets = preset_service.list_presets()
1253
1283
 
1254
- if success:
1255
- self.console.print("[green] Hooks installed successfully![/green]")
1256
- self.console.print("\nYou can now use /mpm commands in Claude Code:")
1257
- self.console.print(" /mpm - Show help")
1258
- self.console.print(" /mpm status - Show claude-mpm status")
1284
+ if not presets:
1285
+ self.console.print("[yellow]No presets available[/yellow]")
1286
+ Prompt.ask("\nPress Enter to continue")
1287
+ return
1259
1288
 
1260
- # Verify installation
1261
- is_valid, issues = installer.verify_hooks()
1262
- if not is_valid:
1263
- self.console.print(
1264
- "\n[yellow]Warning: Installation completed but verification found issues:[/yellow]"
1265
- )
1266
- for issue in issues:
1267
- self.console.print(f" - {issue}")
1289
+ self.console.print("\n[bold white]═══ Available Presets ═══[/bold white]\n")
1290
+ for idx, preset in enumerate(presets, 1):
1291
+ self.console.print(f" {idx}. [white]{preset['name']}[/white]")
1292
+ self.console.print(f" {preset['description']}")
1293
+ self.console.print(f" [dim]Agents: {len(preset['agents'])}[/dim]\n")
1268
1294
 
1269
- return CommandResult.success_result("Hooks installed successfully")
1270
- self.console.print("[red]✗ Hook installation failed[/red]")
1271
- return CommandResult.error_result("Hook installation failed")
1295
+ selection = Prompt.ask("\nEnter preset number (or 'c' to cancel)")
1296
+ if selection.lower() == "c":
1297
+ return
1272
1298
 
1273
- except ImportError:
1274
- self.console.print("[red]Error: HookInstaller module not found[/red]")
1275
- self.console.print("Please ensure claude-mpm is properly installed.")
1276
- return CommandResult.error_result("HookInstaller module not found")
1277
- except Exception as e:
1278
- self.logger.error(f"Hook installation error: {e}", exc_info=True)
1279
- return CommandResult.error_result(f"Hook installation failed: {e}")
1299
+ idx = int(selection) - 1
1300
+ if 0 <= idx < len(presets):
1301
+ preset_name = presets[idx]["name"]
1280
1302
 
1281
- def _verify_hooks(self) -> CommandResult:
1282
- """Verify that Claude MPM hooks are properly installed."""
1283
- try:
1284
- from ...hooks.claude_hooks.installer import HookInstaller
1303
+ # Resolve and deploy preset
1304
+ resolution = preset_service.resolve_agents(preset_name)
1285
1305
 
1286
- installer = HookInstaller()
1287
- status = installer.get_status()
1306
+ if resolution.get("missing_agents"):
1307
+ self.console.print(
1308
+ f"[red]Missing agents: {len(resolution['missing_agents'])}[/red]"
1309
+ )
1310
+ for agent_id in resolution["missing_agents"]:
1311
+ self.console.print(f" • {agent_id}")
1312
+ Prompt.ask("\nPress Enter to continue")
1313
+ return
1288
1314
 
1289
- self.console.print("[bold]Hook Installation Status[/bold]\n")
1315
+ # Confirm installation
1316
+ self.console.print(
1317
+ f"\n[bold]Preset '{preset_name}' includes {len(resolution['agents'])} agents[/bold]"
1318
+ )
1319
+ if Confirm.ask("Install all agents?", default=True):
1320
+ installed = 0
1321
+ for agent in resolution["agents"]:
1322
+ # Convert dict to AgentConfig-like object for installation
1323
+ agent_config = AgentConfig(
1324
+ name=agent.get("agent_id", "unknown"),
1325
+ description=agent.get("metadata", {}).get(
1326
+ "description", ""
1327
+ ),
1328
+ dependencies=[],
1329
+ )
1330
+ agent_config.source_dict = agent
1331
+ agent_config.full_agent_id = agent.get("agent_id", "unknown")
1332
+
1333
+ if self._deploy_single_agent(agent_config, show_feedback=False):
1334
+ installed += 1
1290
1335
 
1291
- # Show Claude Code version and compatibility
1292
- if status.get("claude_version"):
1293
- self.console.print(f"Claude Code Version: {status['claude_version']}")
1294
- if status.get("version_compatible"):
1295
1336
  self.console.print(
1296
- "[green]✓[/green] Version compatible with hook monitoring"
1337
+ f"\n[green]✓ Installed {installed}/{len(resolution['agents'])} agents[/green]"
1297
1338
  )
1339
+
1340
+ Prompt.ask("\nPress Enter to continue")
1341
+ else:
1342
+ self.console.print("[red]Invalid selection[/red]")
1343
+ Prompt.ask("\nPress Enter to continue")
1344
+
1345
+ except Exception as e:
1346
+ self.console.print(f"[red]Error installing preset: {e}[/red]")
1347
+ self.logger.error(f"Preset installation failed: {e}", exc_info=True)
1348
+ Prompt.ask("\nPress Enter to continue")
1349
+
1350
+ def _deploy_single_agent(
1351
+ self, agent: AgentConfig, show_feedback: bool = True
1352
+ ) -> bool:
1353
+ """Install a single agent to the appropriate location."""
1354
+ try:
1355
+ # Check if this is a remote agent with source_dict
1356
+ source_dict = getattr(agent, "source_dict", None)
1357
+ full_agent_id = getattr(agent, "full_agent_id", agent.name)
1358
+
1359
+ if source_dict:
1360
+ # Deploy remote agent using its source file
1361
+ source_file = Path(source_dict.get("source_file", ""))
1362
+ if not source_file.exists():
1363
+ if show_feedback:
1364
+ self.console.print(
1365
+ f"[red]✗ Source file not found: {source_file}[/red]"
1366
+ )
1367
+ return False
1368
+
1369
+ # Determine target file name (use leaf name from hierarchical ID)
1370
+ if "/" in full_agent_id:
1371
+ target_name = full_agent_id.split("/")[-1] + ".md"
1298
1372
  else:
1373
+ target_name = full_agent_id + ".md"
1374
+
1375
+ # Deploy to user-level agents directory
1376
+ target_dir = Path.home() / ".claude" / "agents"
1377
+ target_dir.mkdir(parents=True, exist_ok=True)
1378
+ target_file = target_dir / target_name
1379
+
1380
+ if show_feedback:
1299
1381
  self.console.print(
1300
- f"[yellow][/yellow] {status.get('version_message', 'Version incompatible')}"
1382
+ f"\n[white]Installing {full_agent_id}...[/white]"
1301
1383
  )
1302
- self.console.print()
1303
- else:
1304
- self.console.print(
1305
- "[yellow]Claude Code version could not be detected[/yellow]"
1306
- )
1307
- self.console.print()
1308
1384
 
1309
- if status["installed"]:
1310
- self.console.print(
1311
- f"[green]✓[/green] Hooks installed at: {status['hook_script']}"
1312
- )
1313
- else:
1314
- self.console.print("[red]✗[/red] Hooks not installed")
1385
+ # Copy the agent file
1386
+ import shutil
1315
1387
 
1316
- if status["settings_file"]:
1317
- self.console.print(
1318
- f"[green]✓[/green] Settings file: {status['settings_file']}"
1319
- )
1320
- else:
1321
- self.console.print("[red]✗[/red] Settings file not found")
1388
+ shutil.copy2(source_file, target_file)
1322
1389
 
1323
- if status.get("configured_events"):
1390
+ if show_feedback:
1391
+ self.console.print(
1392
+ f"[green]✓ Successfully installed {full_agent_id} to {target_file}[/green]"
1393
+ )
1394
+ Prompt.ask("\nPress Enter to continue")
1395
+
1396
+ return True
1397
+ # Legacy local template installation (not implemented here)
1398
+ if show_feedback:
1324
1399
  self.console.print(
1325
- f"[green]✓[/green] Configured events: {', '.join(status['configured_events'])}"
1400
+ "[yellow]Local template installation not yet implemented[/yellow]"
1326
1401
  )
1327
- else:
1328
- self.console.print("[red]✗[/red] No events configured")
1329
-
1330
- if status["valid"]:
1331
- self.console.print("\n[green]All checks passed![/green]")
1332
- else:
1333
- self.console.print("\n[red]Issues found:[/red]")
1334
- for issue in status["issues"]:
1335
- self.console.print(f" - {issue}")
1402
+ Prompt.ask("\nPress Enter to continue")
1403
+ return False
1336
1404
 
1337
- return CommandResult.success_result(
1338
- "Hook verification complete", data=status
1339
- )
1340
-
1341
- except ImportError:
1342
- self.console.print("[red]Error: HookInstaller module not found[/red]")
1343
- return CommandResult.error_result("HookInstaller module not found")
1344
1405
  except Exception as e:
1345
- self.logger.error(f"Hook verification error: {e}", exc_info=True)
1346
- return CommandResult.error_result(f"Hook verification failed: {e}")
1406
+ if show_feedback:
1407
+ self.console.print(f"[red]Error installing agent: {e}[/red]")
1408
+ self.logger.error(f"Agent installation failed: {e}", exc_info=True)
1409
+ Prompt.ask("\nPress Enter to continue")
1410
+ return False
1411
+
1412
+ def _remove_agents(self, agents: List[AgentConfig]) -> None:
1413
+ """Remove installed agents."""
1414
+ # Filter to installed agents only
1415
+ installed = [a for a in agents if getattr(a, "is_deployed", False)]
1416
+
1417
+ if not installed:
1418
+ self.console.print("[yellow]No agents are currently installed[/yellow]")
1419
+ Prompt.ask("\nPress Enter to continue")
1420
+ return
1347
1421
 
1348
- def _uninstall_hooks(self) -> CommandResult:
1349
- """Uninstall Claude MPM hooks."""
1350
- try:
1351
- from ...hooks.claude_hooks.installer import HookInstaller
1422
+ self.console.print(f"\n[bold]Installed agents ({len(installed)}):[/bold]")
1423
+ for idx, agent in enumerate(installed, 1):
1424
+ display_name = getattr(agent, "display_name", agent.name)
1425
+ self.console.print(f" {idx}. {agent.name} - {display_name}")
1352
1426
 
1353
- installer = HookInstaller()
1427
+ selection = Prompt.ask("\nEnter agent number to remove (or 'c' to cancel)")
1428
+ if selection.lower() == "c":
1429
+ return
1354
1430
 
1355
- # Confirm uninstallation
1356
- if not Confirm.ask(
1357
- "[yellow]Are you sure you want to uninstall Claude MPM hooks?[/yellow]"
1358
- ):
1359
- return CommandResult.success_result("Uninstallation cancelled")
1431
+ try:
1432
+ idx = int(selection) - 1
1433
+ if 0 <= idx < len(installed):
1434
+ agent = installed[idx]
1435
+ full_agent_id = getattr(agent, "full_agent_id", agent.name)
1436
+
1437
+ # Determine possible file names (hierarchical and leaf)
1438
+ file_names = [f"{full_agent_id}.md"]
1439
+ if "/" in full_agent_id:
1440
+ leaf_name = full_agent_id.split("/")[-1]
1441
+ file_names.append(f"{leaf_name}.md")
1442
+
1443
+ # Remove from both project and user directories
1444
+ removed = False
1445
+ project_agent_dir = Path.cwd() / ".claude-mpm" / "agents"
1446
+ user_agent_dir = Path.home() / ".claude" / "agents"
1447
+
1448
+ for file_name in file_names:
1449
+ project_file = project_agent_dir / file_name
1450
+ user_file = user_agent_dir / file_name
1451
+
1452
+ if project_file.exists():
1453
+ project_file.unlink()
1454
+ removed = True
1455
+ self.console.print(f"[green]✓ Removed {project_file}[/green]")
1456
+
1457
+ if user_file.exists():
1458
+ user_file.unlink()
1459
+ removed = True
1460
+ self.console.print(f"[green]✓ Removed {user_file}[/green]")
1461
+
1462
+ if removed:
1463
+ self.console.print(
1464
+ f"[green]✓ Successfully removed {full_agent_id}[/green]"
1465
+ )
1466
+ else:
1467
+ self.console.print("[yellow]Agent files not found[/yellow]")
1360
1468
 
1361
- self.console.print("[cyan]Uninstalling Claude MPM hooks...[/cyan]")
1362
- success = installer.uninstall_hooks()
1469
+ Prompt.ask("\nPress Enter to continue")
1470
+ else:
1471
+ self.console.print("[red]Invalid selection[/red]")
1472
+ Prompt.ask("\nPress Enter to continue")
1473
+
1474
+ except (ValueError, IndexError):
1475
+ self.console.print("[red]Invalid selection[/red]")
1476
+ Prompt.ask("\nPress Enter to continue")
1477
+
1478
+ def _view_agent_details_enhanced(self, agents: List[AgentConfig]) -> None:
1479
+ """View detailed agent information with enhanced remote agent details."""
1480
+ if not agents:
1481
+ self.console.print("[yellow]No agents available[/yellow]")
1482
+ Prompt.ask("\nPress Enter to continue")
1483
+ return
1363
1484
 
1364
- if success:
1365
- self.console.print("[green]✓ Hooks uninstalled successfully![/green]")
1366
- return CommandResult.success_result("Hooks uninstalled successfully")
1367
- self.console.print("[red]✗ Hook uninstallation failed[/red]")
1368
- return CommandResult.error_result("Hook uninstallation failed")
1485
+ self.console.print(f"\n[bold]Available agents ({len(agents)}):[/bold]")
1486
+ for idx, agent in enumerate(agents, 1):
1487
+ display_name = getattr(agent, "display_name", agent.name)
1488
+ self.console.print(f" {idx}. {agent.name} - {display_name}")
1369
1489
 
1370
- except ImportError:
1371
- self.console.print("[red]Error: HookInstaller module not found[/red]")
1372
- return CommandResult.error_result("HookInstaller module not found")
1373
- except Exception as e:
1374
- self.logger.error(f"Hook uninstallation error: {e}", exc_info=True)
1375
- return CommandResult.error_result(f"Hook uninstallation failed: {e}")
1490
+ selection = Prompt.ask("\nEnter agent number to view (or 'c' to cancel)")
1491
+ if selection.lower() == "c":
1492
+ return
1376
1493
 
1377
- def _run_agent_management(self) -> CommandResult:
1378
- """Jump directly to agent management."""
1379
1494
  try:
1380
- self._manage_agents()
1381
- return CommandResult.success_result("Agent management completed")
1382
- except KeyboardInterrupt:
1383
- return CommandResult.success_result("Agent management cancelled")
1384
- except Exception as e:
1385
- return CommandResult.error_result(f"Agent management failed: {e}")
1495
+ idx = int(selection) - 1
1496
+ if 0 <= idx < len(agents):
1497
+ agent = agents[idx]
1386
1498
 
1387
- def _run_template_editing(self) -> CommandResult:
1388
- """Jump directly to template editing."""
1389
- try:
1390
- self._edit_templates()
1391
- return CommandResult.success_result("Template editing completed")
1392
- except KeyboardInterrupt:
1393
- return CommandResult.success_result("Template editing cancelled")
1394
- except Exception as e:
1395
- return CommandResult.error_result(f"Template editing failed: {e}")
1499
+ self.console.clear()
1500
+ self.console.print("\n[bold white]═══ Agent Details ═══[/bold white]\n")
1501
+
1502
+ # Basic info
1503
+ self.console.print(f"[bold]ID:[/bold] {agent.name}")
1504
+ display_name = getattr(agent, "display_name", "N/A")
1505
+ self.console.print(f"[bold]Name:[/bold] {display_name}")
1506
+ self.console.print(f"[bold]Description:[/bold] {agent.description}")
1507
+
1508
+ # Source info
1509
+ source_type = getattr(agent, "source_type", "local")
1510
+ self.console.print(f"[bold]Source Type:[/bold] {source_type}")
1511
+
1512
+ if source_type == "remote":
1513
+ source_dict = getattr(agent, "source_dict", {})
1514
+ category = source_dict.get("category", "N/A")
1515
+ source = source_dict.get("source", "N/A")
1516
+ version = source_dict.get("version", "N/A")
1517
+
1518
+ self.console.print(f"[bold]Category:[/bold] {category}")
1519
+ self.console.print(f"[bold]Source:[/bold] {source}")
1520
+ self.console.print(f"[bold]Version:[/bold] {version[:16]}...")
1521
+
1522
+ # Installation status
1523
+ is_installed = getattr(agent, "is_deployed", False)
1524
+ status = "Installed" if is_installed else "Available"
1525
+ self.console.print(f"[bold]Status:[/bold] {status}")
1526
+
1527
+ Prompt.ask("\nPress Enter to continue")
1528
+ else:
1529
+ self.console.print("[red]Invalid selection[/red]")
1530
+ Prompt.ask("\nPress Enter to continue")
1396
1531
 
1397
- def _run_behavior_management(self) -> CommandResult:
1398
- """Jump directly to behavior management."""
1399
- try:
1400
- self._manage_behaviors()
1401
- return CommandResult.success_result("Behavior management completed")
1402
- except KeyboardInterrupt:
1403
- return CommandResult.success_result("Behavior management cancelled")
1404
- except Exception as e:
1405
- return CommandResult.error_result(f"Behavior management failed: {e}")
1532
+ except (ValueError, IndexError):
1533
+ self.console.print("[red]Invalid selection[/red]")
1534
+ Prompt.ask("\nPress Enter to continue")
1406
1535
 
1407
1536
 
1408
1537
  def manage_configure(args) -> int: