claude-mpm 5.4.65__py3-none-any.whl → 5.6.10__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 (313) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
  3. claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
  4. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +107 -1928
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +119 -689
  6. claude_mpm/agents/WORKFLOW.md +2 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  8. claude_mpm/cli/__init__.py +5 -1
  9. claude_mpm/cli/commands/agents.py +2 -4
  10. claude_mpm/cli/commands/agents_reconcile.py +197 -0
  11. claude_mpm/cli/commands/autotodos.py +566 -0
  12. claude_mpm/cli/commands/commander.py +46 -0
  13. claude_mpm/cli/commands/configure.py +620 -21
  14. claude_mpm/cli/commands/hook_errors.py +60 -60
  15. claude_mpm/cli/commands/monitor.py +2 -2
  16. claude_mpm/cli/commands/mpm_init/core.py +2 -2
  17. claude_mpm/cli/commands/run.py +35 -3
  18. claude_mpm/cli/commands/skill_source.py +51 -2
  19. claude_mpm/cli/commands/skills.py +171 -17
  20. claude_mpm/cli/executor.py +120 -16
  21. claude_mpm/cli/interactive/__init__.py +10 -0
  22. claude_mpm/cli/interactive/agent_wizard.py +30 -50
  23. claude_mpm/cli/interactive/questionary_styles.py +65 -0
  24. claude_mpm/cli/interactive/skill_selector.py +481 -0
  25. claude_mpm/cli/parsers/base_parser.py +76 -1
  26. claude_mpm/cli/parsers/commander_parser.py +83 -0
  27. claude_mpm/cli/parsers/run_parser.py +10 -0
  28. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  29. claude_mpm/cli/parsers/skills_parser.py +5 -0
  30. claude_mpm/cli/startup.py +203 -359
  31. claude_mpm/cli/startup_display.py +72 -5
  32. claude_mpm/cli/startup_logging.py +2 -2
  33. claude_mpm/cli/utils.py +7 -3
  34. claude_mpm/commander/__init__.py +72 -0
  35. claude_mpm/commander/adapters/__init__.py +31 -0
  36. claude_mpm/commander/adapters/base.py +191 -0
  37. claude_mpm/commander/adapters/claude_code.py +361 -0
  38. claude_mpm/commander/adapters/communication.py +366 -0
  39. claude_mpm/commander/api/__init__.py +16 -0
  40. claude_mpm/commander/api/app.py +105 -0
  41. claude_mpm/commander/api/errors.py +133 -0
  42. claude_mpm/commander/api/routes/__init__.py +8 -0
  43. claude_mpm/commander/api/routes/events.py +184 -0
  44. claude_mpm/commander/api/routes/inbox.py +171 -0
  45. claude_mpm/commander/api/routes/messages.py +148 -0
  46. claude_mpm/commander/api/routes/projects.py +271 -0
  47. claude_mpm/commander/api/routes/sessions.py +228 -0
  48. claude_mpm/commander/api/routes/work.py +260 -0
  49. claude_mpm/commander/api/schemas.py +182 -0
  50. claude_mpm/commander/chat/__init__.py +7 -0
  51. claude_mpm/commander/chat/cli.py +107 -0
  52. claude_mpm/commander/chat/commands.py +96 -0
  53. claude_mpm/commander/chat/repl.py +310 -0
  54. claude_mpm/commander/config.py +49 -0
  55. claude_mpm/commander/config_loader.py +115 -0
  56. claude_mpm/commander/daemon.py +398 -0
  57. claude_mpm/commander/events/__init__.py +26 -0
  58. claude_mpm/commander/events/manager.py +332 -0
  59. claude_mpm/commander/frameworks/__init__.py +12 -0
  60. claude_mpm/commander/frameworks/base.py +143 -0
  61. claude_mpm/commander/frameworks/claude_code.py +58 -0
  62. claude_mpm/commander/frameworks/mpm.py +62 -0
  63. claude_mpm/commander/inbox/__init__.py +16 -0
  64. claude_mpm/commander/inbox/dedup.py +128 -0
  65. claude_mpm/commander/inbox/inbox.py +224 -0
  66. claude_mpm/commander/inbox/models.py +70 -0
  67. claude_mpm/commander/instance_manager.py +337 -0
  68. claude_mpm/commander/llm/__init__.py +6 -0
  69. claude_mpm/commander/llm/openrouter_client.py +167 -0
  70. claude_mpm/commander/llm/summarizer.py +70 -0
  71. claude_mpm/commander/models/__init__.py +18 -0
  72. claude_mpm/commander/models/events.py +121 -0
  73. claude_mpm/commander/models/project.py +162 -0
  74. claude_mpm/commander/models/work.py +214 -0
  75. claude_mpm/commander/parsing/__init__.py +20 -0
  76. claude_mpm/commander/parsing/extractor.py +132 -0
  77. claude_mpm/commander/parsing/output_parser.py +270 -0
  78. claude_mpm/commander/parsing/patterns.py +100 -0
  79. claude_mpm/commander/persistence/__init__.py +11 -0
  80. claude_mpm/commander/persistence/event_store.py +274 -0
  81. claude_mpm/commander/persistence/state_store.py +309 -0
  82. claude_mpm/commander/persistence/work_store.py +164 -0
  83. claude_mpm/commander/polling/__init__.py +13 -0
  84. claude_mpm/commander/polling/event_detector.py +104 -0
  85. claude_mpm/commander/polling/output_buffer.py +49 -0
  86. claude_mpm/commander/polling/output_poller.py +153 -0
  87. claude_mpm/commander/project_session.py +268 -0
  88. claude_mpm/commander/proxy/__init__.py +12 -0
  89. claude_mpm/commander/proxy/formatter.py +89 -0
  90. claude_mpm/commander/proxy/output_handler.py +191 -0
  91. claude_mpm/commander/proxy/relay.py +155 -0
  92. claude_mpm/commander/registry.py +404 -0
  93. claude_mpm/commander/runtime/__init__.py +10 -0
  94. claude_mpm/commander/runtime/executor.py +191 -0
  95. claude_mpm/commander/runtime/monitor.py +316 -0
  96. claude_mpm/commander/session/__init__.py +6 -0
  97. claude_mpm/commander/session/context.py +81 -0
  98. claude_mpm/commander/session/manager.py +59 -0
  99. claude_mpm/commander/tmux_orchestrator.py +361 -0
  100. claude_mpm/commander/web/__init__.py +1 -0
  101. claude_mpm/commander/work/__init__.py +30 -0
  102. claude_mpm/commander/work/executor.py +189 -0
  103. claude_mpm/commander/work/queue.py +405 -0
  104. claude_mpm/commander/workflow/__init__.py +27 -0
  105. claude_mpm/commander/workflow/event_handler.py +219 -0
  106. claude_mpm/commander/workflow/notifier.py +146 -0
  107. claude_mpm/commands/mpm-config.md +8 -0
  108. claude_mpm/commands/mpm-doctor.md +8 -0
  109. claude_mpm/commands/mpm-help.md +8 -0
  110. claude_mpm/commands/mpm-init.md +8 -0
  111. claude_mpm/commands/mpm-monitor.md +8 -0
  112. claude_mpm/commands/mpm-organize.md +8 -0
  113. claude_mpm/commands/mpm-postmortem.md +8 -0
  114. claude_mpm/commands/mpm-session-resume.md +9 -1
  115. claude_mpm/commands/mpm-status.md +8 -0
  116. claude_mpm/commands/mpm-ticket-view.md +8 -0
  117. claude_mpm/commands/mpm-version.md +8 -0
  118. claude_mpm/commands/mpm.md +8 -0
  119. claude_mpm/config/agent_presets.py +8 -7
  120. claude_mpm/config/skill_sources.py +16 -0
  121. claude_mpm/constants.py +1 -0
  122. claude_mpm/core/claude_runner.py +2 -2
  123. claude_mpm/core/config.py +32 -19
  124. claude_mpm/core/hook_manager.py +51 -3
  125. claude_mpm/core/interactive_session.py +7 -7
  126. claude_mpm/core/logger.py +26 -9
  127. claude_mpm/core/logging_utils.py +35 -11
  128. claude_mpm/core/output_style_manager.py +31 -13
  129. claude_mpm/core/unified_config.py +54 -8
  130. claude_mpm/core/unified_paths.py +95 -90
  131. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  132. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  133. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
  134. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
  135. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
  136. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
  137. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
  138. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
  139. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
  140. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
  141. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
  142. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
  143. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
  144. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
  145. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
  146. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  147. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  148. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
  149. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
  150. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
  151. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
  152. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
  153. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
  154. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
  155. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
  156. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  157. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
  158. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
  159. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
  160. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
  161. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
  162. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
  163. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
  164. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
  165. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
  166. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
  167. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
  168. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
  169. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
  170. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
  171. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
  172. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
  173. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
  174. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  188. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  189. claude_mpm/dashboard/static/svelte-build/index.html +9 -9
  190. claude_mpm/experimental/cli_enhancements.py +2 -1
  191. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  192. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  193. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  194. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  195. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
  196. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
  197. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  198. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
  199. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
  200. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  201. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
  202. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
  203. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  204. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
  205. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  206. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
  207. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
  208. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  209. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
  210. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
  211. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
  212. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  213. claude_mpm/hooks/claude_hooks/event_handlers.py +283 -87
  214. claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
  215. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  216. claude_mpm/hooks/claude_hooks/installer.py +116 -8
  217. claude_mpm/hooks/claude_hooks/memory_integration.py +51 -31
  218. claude_mpm/hooks/claude_hooks/response_tracking.py +42 -59
  219. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
  220. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  221. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
  222. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
  223. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
  224. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  225. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
  226. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
  227. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  228. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
  229. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
  230. claude_mpm/hooks/claude_hooks/services/connection_manager.py +39 -24
  231. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +36 -103
  232. claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
  233. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +73 -75
  234. claude_mpm/hooks/session_resume_hook.py +89 -1
  235. claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
  236. claude_mpm/init.py +1 -1
  237. claude_mpm/scripts/claude-hook-handler.sh +43 -16
  238. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  239. claude_mpm/services/agents/agent_selection_service.py +2 -2
  240. claude_mpm/services/agents/cache_git_manager.py +1 -1
  241. claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
  242. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
  243. claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
  244. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  245. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  246. claude_mpm/services/agents/startup_sync.py +5 -2
  247. claude_mpm/services/cli/__init__.py +3 -0
  248. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  249. claude_mpm/services/cli/session_resume_helper.py +10 -2
  250. claude_mpm/services/delegation_detector.py +175 -0
  251. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  252. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  253. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  254. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  255. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  256. claude_mpm/services/diagnostics/models.py +14 -1
  257. claude_mpm/services/event_log.py +325 -0
  258. claude_mpm/services/infrastructure/__init__.py +4 -0
  259. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  260. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  261. claude_mpm/services/monitor/daemon_manager.py +15 -4
  262. claude_mpm/services/monitor/management/lifecycle.py +8 -2
  263. claude_mpm/services/monitor/server.py +106 -16
  264. claude_mpm/services/pm_skills_deployer.py +259 -87
  265. claude_mpm/services/skills/git_skill_source_manager.py +135 -11
  266. claude_mpm/services/skills/selective_skill_deployer.py +142 -26
  267. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  268. claude_mpm/services/skills_deployer.py +31 -5
  269. claude_mpm/services/socketio/handlers/hook.py +14 -7
  270. claude_mpm/services/socketio/server/main.py +12 -4
  271. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  272. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  273. claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
  274. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  275. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  276. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  277. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  278. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  279. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  280. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  281. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  282. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  283. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  284. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  285. claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
  286. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  287. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  288. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  289. claude_mpm/skills/skill_manager.py +4 -4
  290. claude_mpm/utils/agent_dependency_loader.py +4 -2
  291. claude_mpm/utils/robust_installer.py +10 -6
  292. claude_mpm-5.6.10.dist-info/METADATA +391 -0
  293. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/RECORD +303 -181
  294. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
  295. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
  296. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
  297. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
  298. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
  299. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
  300. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
  301. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
  302. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
  303. claude_mpm-5.4.65.dist-info/METADATA +0 -999
  304. /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
  305. /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
  306. /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
  307. /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
  308. /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
  309. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/WHEEL +0 -0
  310. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/entry_points.txt +0 -0
  311. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE +0 -0
  312. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  313. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/top_level.txt +0 -0
@@ -57,33 +57,33 @@ def list_errors(format, hook_type):
57
57
 
58
58
  if not errors:
59
59
  if hook_type:
60
- click.echo(f"No errors recorded for hook type: {hook_type}")
60
+ click.echo(f"No errors recorded for hook type: {hook_type}", err=True)
61
61
  else:
62
- click.echo("No errors recorded. Hook system is healthy! ✅")
62
+ click.echo("No errors recorded. Hook system is healthy! ✅", err=True)
63
63
  return
64
64
 
65
65
  if format == "json":
66
66
  # JSON output
67
- click.echo(json.dumps(errors, indent=2))
67
+ click.echo(json.dumps(errors, indent=2), err=True)
68
68
  else:
69
69
  # Table output
70
- click.echo("\n" + "=" * 80)
71
- click.echo("Hook Error Memory Report")
72
- click.echo("=" * 80)
70
+ click.echo("\n" + "=" * 80, err=True)
71
+ click.echo("Hook Error Memory Report", err=True)
72
+ click.echo("=" * 80, err=True)
73
73
 
74
74
  for key, data in errors.items():
75
- click.echo(f"\n🔴 Error: {data['type']}")
76
- click.echo(f" Hook Type: {data['hook_type']}")
77
- click.echo(f" Details: {data['details']}")
78
- click.echo(f" Match: {data['match']}")
79
- click.echo(f" Count: {data['count']} occurrences")
80
- click.echo(f" First Seen: {data['first_seen']}")
81
- click.echo(f" Last Seen: {data['last_seen']}")
75
+ click.echo(f"\n🔴 Error: {data['type']}", err=True)
76
+ click.echo(f" Hook Type: {data['hook_type']}", err=True)
77
+ click.echo(f" Details: {data['details']}", err=True)
78
+ click.echo(f" Match: {data['match']}", err=True)
79
+ click.echo(f" Count: {data['count']} occurrences", err=True)
80
+ click.echo(f" First Seen: {data['first_seen']}", err=True)
81
+ click.echo(f" Last Seen: {data['last_seen']}", err=True)
82
82
 
83
- click.echo("\n" + "=" * 80)
84
- click.echo(f"Total unique errors: {len(errors)}")
85
- click.echo(f"Memory file: {error_memory.memory_file}")
86
- click.echo("\nTo clear errors: claude-mpm hook-errors clear")
83
+ click.echo("\n" + "=" * 80, err=True)
84
+ click.echo(f"Total unique errors: {len(errors)}", err=True)
85
+ click.echo(f"Memory file: {error_memory.memory_file}", err=True)
86
+ click.echo("\nTo clear errors: claude-mpm hook-errors clear", err=True)
87
87
 
88
88
 
89
89
  @hook_errors_group.command(name="summary")
@@ -99,28 +99,28 @@ def show_summary():
99
99
  summary = error_memory.get_error_summary()
100
100
 
101
101
  if summary["total_errors"] == 0:
102
- click.echo("No errors recorded. Hook system is healthy! ✅")
102
+ click.echo("No errors recorded. Hook system is healthy! ✅", err=True)
103
103
  return
104
104
 
105
- click.echo("\n" + "=" * 80)
106
- click.echo("Hook Error Summary")
107
- click.echo("=" * 80)
108
- click.echo("\n📊 Statistics:")
109
- click.echo(f" Total Errors: {summary['total_errors']}")
110
- click.echo(f" Unique Errors: {summary['unique_errors']}")
105
+ click.echo("\n" + "=" * 80, err=True)
106
+ click.echo("Hook Error Summary", err=True)
107
+ click.echo("=" * 80, err=True)
108
+ click.echo("\n📊 Statistics:", err=True)
109
+ click.echo(f" Total Errors: {summary['total_errors']}", err=True)
110
+ click.echo(f" Unique Errors: {summary['unique_errors']}", err=True)
111
111
 
112
112
  if summary["errors_by_type"]:
113
- click.echo("\n🔍 Errors by Type:")
113
+ click.echo("\n🔍 Errors by Type:", err=True)
114
114
  for error_type, count in summary["errors_by_type"].items():
115
- click.echo(f" {error_type}: {count}")
115
+ click.echo(f" {error_type}: {count}", err=True)
116
116
 
117
117
  if summary["errors_by_hook"]:
118
- click.echo("\n🎣 Errors by Hook Type:")
118
+ click.echo("\n🎣 Errors by Hook Type:", err=True)
119
119
  for hook_type, count in summary["errors_by_hook"].items():
120
- click.echo(f" {hook_type}: {count}")
120
+ click.echo(f" {hook_type}: {count}", err=True)
121
121
 
122
- click.echo(f"\n📁 Memory File: {summary['memory_file']}")
123
- click.echo("\nFor detailed list: claude-mpm hook-errors list")
122
+ click.echo(f"\n📁 Memory File: {summary['memory_file']}", err=True)
123
+ click.echo("\nFor detailed list: claude-mpm hook-errors list", err=True)
124
124
 
125
125
 
126
126
  @hook_errors_group.command(name="clear")
@@ -158,21 +158,21 @@ def clear_errors(hook_type, yes):
158
158
  scope = "all hook types"
159
159
 
160
160
  if count == 0:
161
- click.echo(f"No errors to clear {scope}.")
161
+ click.echo(f"No errors to clear {scope}.", err=True)
162
162
  return
163
163
 
164
164
  # Confirm if not using -y flag
165
165
  if not yes:
166
166
  message = f"Clear {count} error(s) {scope}?"
167
167
  if not click.confirm(message):
168
- click.echo("Cancelled.")
168
+ click.echo("Cancelled.", err=True)
169
169
  return
170
170
 
171
171
  # Clear errors
172
172
  error_memory.clear_errors(hook_type)
173
173
 
174
- click.echo(f"✅ Cleared {count} error(s) {scope}.")
175
- click.echo("\nHooks will be retried on next execution.")
174
+ click.echo(f"✅ Cleared {count} error(s) {scope}.", err=True)
175
+ click.echo("\nHooks will be retried on next execution.", err=True)
176
176
 
177
177
 
178
178
  @hook_errors_group.command(name="diagnose")
@@ -201,19 +201,19 @@ def diagnose_errors(hook_type):
201
201
 
202
202
  if not errors:
203
203
  if hook_type:
204
- click.echo(f"No errors to diagnose for hook type: {hook_type}")
204
+ click.echo(f"No errors to diagnose for hook type: {hook_type}", err=True)
205
205
  else:
206
- click.echo("No errors to diagnose. Hook system is healthy! ✅")
206
+ click.echo("No errors to diagnose. Hook system is healthy! ✅", err=True)
207
207
  return
208
208
 
209
- click.echo("\n" + "=" * 80)
210
- click.echo("Hook Error Diagnostics")
211
- click.echo("=" * 80)
209
+ click.echo("\n" + "=" * 80, err=True)
210
+ click.echo("Hook Error Diagnostics", err=True)
211
+ click.echo("=" * 80, err=True)
212
212
 
213
213
  for key, data in errors.items():
214
- click.echo(f"\n🔴 Error: {data['type']}")
215
- click.echo(f" Hook: {data['hook_type']}")
216
- click.echo(f" Count: {data['count']} failures")
214
+ click.echo(f"\n🔴 Error: {data['type']}", err=True)
215
+ click.echo(f" Hook: {data['hook_type']}", err=True)
216
+ click.echo(f" Count: {data['count']} failures", err=True)
217
217
 
218
218
  # Generate and show fix suggestion
219
219
  error_info = {
@@ -223,13 +223,13 @@ def diagnose_errors(hook_type):
223
223
  }
224
224
  suggestion = error_memory.suggest_fix(error_info)
225
225
 
226
- click.echo("\n" + "-" * 80)
227
- click.echo(suggestion)
228
- click.echo("-" * 80)
226
+ click.echo("\n" + "-" * 80, err=True)
227
+ click.echo(suggestion, err=True)
228
+ click.echo("-" * 80, err=True)
229
229
 
230
- click.echo("\n" + "=" * 80)
231
- click.echo("After fixing issues, clear errors to retry:")
232
- click.echo(" claude-mpm hook-errors clear")
230
+ click.echo("\n" + "=" * 80, err=True)
231
+ click.echo("After fixing issues, clear errors to retry:", err=True)
232
+ click.echo(" claude-mpm hook-errors clear", err=True)
233
233
 
234
234
 
235
235
  @hook_errors_group.command(name="status")
@@ -244,27 +244,27 @@ def show_status():
244
244
  error_memory = get_hook_error_memory()
245
245
  summary = error_memory.get_error_summary()
246
246
 
247
- click.echo("\n📊 Hook Error Memory Status")
248
- click.echo("=" * 80)
247
+ click.echo("\n📊 Hook Error Memory Status", err=True)
248
+ click.echo("=" * 80, err=True)
249
249
 
250
250
  if summary["total_errors"] == 0:
251
- click.echo("✅ Status: Healthy (no errors recorded)")
251
+ click.echo("✅ Status: Healthy (no errors recorded)", err=True)
252
252
  else:
253
- click.echo(f"⚠️ Status: {summary['total_errors']} error(s) recorded")
254
- click.echo(f" Unique errors: {summary['unique_errors']}")
253
+ click.echo(f"⚠️ Status: {summary['total_errors']} error(s) recorded", err=True)
254
+ click.echo(f" Unique errors: {summary['unique_errors']}", err=True)
255
255
 
256
256
  # Show which hooks are affected
257
257
  if summary["errors_by_hook"]:
258
258
  affected_hooks = list(summary["errors_by_hook"].keys())
259
- click.echo(f" Affected hooks: {', '.join(affected_hooks)}")
259
+ click.echo(f" Affected hooks: {', '.join(affected_hooks)}", err=True)
260
260
 
261
- click.echo(f"\n📁 Memory file: {summary['memory_file']}")
262
- click.echo(f" Exists: {Path(summary['memory_file']).exists()}")
261
+ click.echo(f"\n📁 Memory file: {summary['memory_file']}", err=True)
262
+ click.echo(f" Exists: {Path(summary['memory_file']).exists()}", err=True)
263
263
 
264
- click.echo("\nCommands:")
265
- click.echo(" claude-mpm hook-errors list # View detailed errors")
266
- click.echo(" claude-mpm hook-errors diagnose # Get fix suggestions")
267
- click.echo(" claude-mpm hook-errors clear # Clear and retry")
264
+ click.echo("\nCommands:", err=True)
265
+ click.echo(" claude-mpm hook-errors list # View detailed errors", err=True)
266
+ click.echo(" claude-mpm hook-errors diagnose # Get fix suggestions", err=True)
267
+ click.echo(" claude-mpm hook-errors clear # Clear and retry", err=True)
268
268
 
269
269
 
270
270
  # Register the command group
@@ -118,7 +118,7 @@ class MonitorCommand(BaseCommand):
118
118
  # Check if it's actually running
119
119
  if not self.daemon.lifecycle.is_running():
120
120
  return CommandResult.error_result(
121
- "Monitor daemon failed to start. Check ~/.claude-mpm/monitor-daemon.log for details."
121
+ "Monitor daemon failed to start. Check ~/.claude-mpm/logs/monitor-daemon-*.log for details."
122
122
  )
123
123
 
124
124
  # Get the actual PID
@@ -146,7 +146,7 @@ class MonitorCommand(BaseCommand):
146
146
  pass
147
147
 
148
148
  return CommandResult.error_result(
149
- "Failed to start unified monitor daemon. Check ~/.claude-mpm/monitor-daemon.log for details."
149
+ "Failed to start unified monitor daemon. Check ~/.claude-mpm/logs/monitor-daemon-*.log for details."
150
150
  )
151
151
 
152
152
  def _stop_monitor(self, args) -> CommandResult:
@@ -688,9 +688,9 @@ class MPMInitCommand:
688
688
  return len(text) // 4
689
689
 
690
690
  def _deploy_pm_skills(self) -> None:
691
- """Deploy PM skills templates to project .claude-mpm directory.
691
+ """Deploy PM skills templates to project .claude directory.
692
692
 
693
- Copies PM skills from bundled templates to .claude-mpm/skills/pm/
693
+ Copies PM skills from bundled templates to .claude/skills/
694
694
  with version tracking and checksum validation.
695
695
  """
696
696
  try:
@@ -13,7 +13,7 @@ DESIGN DECISIONS:
13
13
  - Support multiple output formats (json, yaml, table, text)
14
14
  """
15
15
 
16
- import subprocess
16
+ import subprocess # nosec B404 - required for process management
17
17
  import sys
18
18
  from datetime import datetime, timezone
19
19
  from typing import Optional
@@ -489,6 +489,18 @@ class RunCommand(BaseCommand):
489
489
  if hasattr(args, "claude_args") and args.claude_args:
490
490
  claude_args.extend(args.claude_args)
491
491
 
492
+ # Add --resume if flag is set
493
+ if getattr(args, "resume", False) and "--resume" not in claude_args:
494
+ claude_args.insert(0, "--resume")
495
+
496
+ # Add --chrome if flag is set
497
+ if getattr(args, "chrome", False) and "--chrome" not in claude_args:
498
+ claude_args.insert(0, "--chrome")
499
+
500
+ # Add --no-chrome if flag is set
501
+ if getattr(args, "no_chrome", False) and "--no-chrome" not in claude_args:
502
+ claude_args.insert(0, "--no-chrome")
503
+
492
504
  # Create runner
493
505
  runner = ClaudeRunner(
494
506
  enable_tickets=enable_tickets,
@@ -553,7 +565,7 @@ class RunCommand(BaseCommand):
553
565
  wrapper_path = get_scripts_dir() / "interactive_wrapper.py"
554
566
  if wrapper_path.exists():
555
567
  print("Starting interactive session with command interception...")
556
- subprocess.run([sys.executable, str(wrapper_path)], check=False)
568
+ subprocess.run([sys.executable, str(wrapper_path)], check=False) # nosec B603 - trusted internal paths
557
569
  else:
558
570
  self.logger.warning(
559
571
  "Interactive wrapper not found, falling back to normal mode"
@@ -907,6 +919,26 @@ def run_session_legacy(args):
907
919
  else:
908
920
  logger.info("[INFO]️ --resume already in claude_args")
909
921
 
922
+ # Add --chrome to claude_args if the flag is set
923
+ chrome_flag_present = getattr(args, "chrome", False)
924
+ if chrome_flag_present:
925
+ logger.info("📌 --chrome flag detected in args")
926
+ if "--chrome" not in raw_claude_args:
927
+ raw_claude_args = ["--chrome", *raw_claude_args]
928
+ logger.info("✅ Added --chrome to claude_args")
929
+ else:
930
+ logger.info("ℹ️ --chrome already in claude_args")
931
+
932
+ # Add --no-chrome to claude_args if the flag is set
933
+ no_chrome_flag_present = getattr(args, "no_chrome", False)
934
+ if no_chrome_flag_present:
935
+ logger.info("📌 --no-chrome flag detected in args")
936
+ if "--no-chrome" not in raw_claude_args:
937
+ raw_claude_args = ["--no-chrome", *raw_claude_args]
938
+ logger.info("✅ Added --no-chrome to claude_args")
939
+ else:
940
+ logger.info("ℹ️ --no-chrome already in claude_args")
941
+
910
942
  # Filter out claude-mpm specific flags before passing to Claude CLI
911
943
  logger.debug(f"Pre-filter claude_args: {raw_claude_args}")
912
944
  claude_args = filter_claude_mpm_args(raw_claude_args)
@@ -1044,7 +1076,7 @@ def run_session_legacy(args):
1044
1076
  wrapper_path = get_scripts_dir() / "interactive_wrapper.py"
1045
1077
  if wrapper_path.exists():
1046
1078
  print("Starting interactive session with command interception...")
1047
- subprocess.run([sys.executable, str(wrapper_path)], check=False)
1079
+ subprocess.run([sys.executable, str(wrapper_path)], check=False) # nosec B603 - trusted internal paths
1048
1080
  else:
1049
1081
  logger.warning("Interactive wrapper not found, falling back to normal mode")
1050
1082
  runner.run_interactive(context)
@@ -11,6 +11,7 @@ for better UX. Handles errors gracefully with actionable messages.
11
11
 
12
12
  import json
13
13
  import logging
14
+ import os
14
15
  import re
15
16
 
16
17
  from ...config.skill_sources import SkillSource, SkillSourceConfiguration
@@ -20,6 +21,33 @@ from ...services.skills.skill_discovery_service import SkillDiscoveryService
20
21
  logger = logging.getLogger(__name__)
21
22
 
22
23
 
24
+ def _get_github_token(source: SkillSource | None = None) -> str | None:
25
+ """Get GitHub token with source-specific override support.
26
+
27
+ Priority: source.token > GITHUB_TOKEN > GH_TOKEN
28
+
29
+ Args:
30
+ source: Optional SkillSource to check for per-source token
31
+
32
+ Returns:
33
+ GitHub token if found, None otherwise
34
+
35
+ Security Note:
36
+ Token is never logged or printed to avoid exposure.
37
+ """
38
+ # Priority 1: Per-source token (env var reference or direct)
39
+ if source and source.token:
40
+ if source.token.startswith("$"):
41
+ # Env var reference: $VAR_NAME -> os.environ.get("VAR_NAME")
42
+ env_var_name = source.token[1:]
43
+ return os.environ.get(env_var_name)
44
+ # Direct token (not recommended but supported)
45
+ return source.token
46
+
47
+ # Priority 2-3: Global environment variables
48
+ return os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
49
+
50
+
23
51
  def _test_skill_repository_access(source: SkillSource) -> dict:
24
52
  """Test if skill repository is accessible via GitHub API.
25
53
 
@@ -58,7 +86,13 @@ def _test_skill_repository_access(source: SkillSource) -> dict:
58
86
  # Test GitHub API access
59
87
  api_url = f"https://api.github.com/repos/{owner_repo}"
60
88
 
61
- response = requests.get(api_url, timeout=10)
89
+ # Build headers with authentication if token available
90
+ headers = {"Accept": "application/vnd.github+json"}
91
+ token = _get_github_token(source)
92
+ if token:
93
+ headers["Authorization"] = f"token {token}"
94
+
95
+ response = requests.get(api_url, headers=headers, timeout=10)
62
96
 
63
97
  if response.status_code == 200:
64
98
  return {"accessible": True, "error": None}
@@ -68,9 +102,14 @@ def _test_skill_repository_access(source: SkillSource) -> dict:
68
102
  "error": f"Repository not found: {owner_repo}",
69
103
  }
70
104
  if response.status_code == 403:
105
+ error_msg = "Access denied (private repository or rate limit)"
106
+ if not token:
107
+ error_msg += (
108
+ ". Try setting GITHUB_TOKEN environment variable for private repos"
109
+ )
71
110
  return {
72
111
  "accessible": False,
73
- "error": "Access denied (private repository or rate limit)",
112
+ "error": error_msg,
74
113
  }
75
114
  return {
76
115
  "accessible": False,
@@ -263,6 +302,15 @@ def handle_add_skill_source(args) -> int:
263
302
 
264
303
  # Create new source
265
304
  enabled = not args.disabled
305
+ token = getattr(args, "token", None)
306
+
307
+ # Security warning for direct tokens
308
+ if token and not token.startswith("$"):
309
+ print("⚠️ Warning: Direct token values in config are not recommended")
310
+ print(" Consider using environment variable reference instead:")
311
+ print(" --token $MY_PRIVATE_TOKEN")
312
+ print()
313
+
266
314
  source = SkillSource(
267
315
  id=source_id,
268
316
  type="git",
@@ -270,6 +318,7 @@ def handle_add_skill_source(args) -> int:
270
318
  branch=args.branch,
271
319
  priority=args.priority,
272
320
  enabled=enabled,
321
+ token=token,
273
322
  )
274
323
 
275
324
  # Determine if we should test
@@ -18,7 +18,7 @@ ARCHITECTURE:
18
18
  """
19
19
 
20
20
  import os
21
- import subprocess
21
+ import subprocess # nosec B404
22
22
  from typing import Optional
23
23
 
24
24
  from rich.console import Console
@@ -84,6 +84,7 @@ class SkillsManagementCommand(BaseCommand):
84
84
  SkillsCommands.INFO.value: self._show_skill_info,
85
85
  SkillsCommands.CONFIG.value: self._manage_config,
86
86
  SkillsCommands.CONFIGURE.value: self._configure_skills,
87
+ SkillsCommands.SELECT.value: self._select_skills_interactive,
87
88
  # GitHub deployment commands
88
89
  SkillsCommands.DEPLOY_FROM_GITHUB.value: self._deploy_from_github,
89
90
  SkillsCommands.LIST_AVAILABLE.value: self._list_available_github_skills,
@@ -504,7 +505,7 @@ class SkillsManagementCommand(BaseCommand):
504
505
  # Open in editor
505
506
  editor = os.environ.get("EDITOR", "nano")
506
507
  try:
507
- subprocess.run([editor, str(config_path)], check=True)
508
+ subprocess.run([editor, str(config_path)], check=True) # nosec B603
508
509
  console.print(
509
510
  f"\n[green]Configuration saved to {config_path}[/green]\n"
510
511
  )
@@ -537,6 +538,7 @@ class SkillsManagementCommand(BaseCommand):
537
538
  toolchain = getattr(args, "toolchain", None)
538
539
  categories = getattr(args, "categories", None)
539
540
  force = getattr(args, "force", False)
541
+ deploy_all = getattr(args, "all", False)
540
542
 
541
543
  if collection:
542
544
  console.print(
@@ -547,14 +549,15 @@ class SkillsManagementCommand(BaseCommand):
547
549
  "\n[bold cyan]Deploying skills from default collection...[/bold cyan]\n"
548
550
  )
549
551
 
550
- # Selective deployment is ALWAYS enabled (deploy only agent-referenced skills)
551
- # This ensures only skills linked to deployed agents are deployed
552
+ # Use selective deployment unless --all flag is provided
553
+ # Selective mode deploys only agent-referenced skills
554
+ # --all mode deploys all available skills from the collection
552
555
  result = self.skills_deployer.deploy_skills(
553
556
  collection=collection,
554
557
  toolchain=toolchain,
555
558
  categories=categories,
556
559
  force=force,
557
- selective=True, # Always use selective deployment
560
+ selective=not deploy_all,
558
561
  )
559
562
 
560
563
  # Display results
@@ -673,33 +676,91 @@ class SkillsManagementCommand(BaseCommand):
673
676
  def _check_deployed_skills(self, args) -> CommandResult:
674
677
  """Check currently deployed skills in ~/.claude/skills/."""
675
678
  try:
676
- result = self.skills_deployer.check_deployed_skills()
679
+ # Get deployed skills
680
+ deployed_result = self.skills_deployer.check_deployed_skills()
681
+ deployed_names = {skill["name"] for skill in deployed_result["skills"]}
677
682
 
678
683
  console.print("\n[bold cyan]Claude Code Skills Status:[/bold cyan]\n")
679
- console.print(f"[dim]Directory: {result['claude_skills_dir']}[/dim]\n")
684
+ console.print(
685
+ f"[dim]Directory: {deployed_result['claude_skills_dir']}[/dim]\n"
686
+ )
687
+
688
+ # Fetch available skills from GitHub to get full list
689
+ try:
690
+ available_result = self.skills_deployer.list_available_skills()
691
+ all_skills = available_result.get("skills", [])
692
+ except Exception as e:
693
+ console.print(
694
+ f"[yellow]Warning: Could not fetch available skills: {e}[/yellow]\n"
695
+ )
696
+ all_skills = []
680
697
 
681
- if result["deployed_count"] == 0:
682
- console.print("[yellow]No skills currently deployed.[/yellow]")
698
+ # Combine deployed and available skills
699
+ skill_map = {}
700
+
701
+ # Add available skills
702
+ for skill in all_skills:
703
+ skill_name = skill.get("name", "")
704
+ if skill_name:
705
+ skill_map[skill_name] = {
706
+ "name": skill.get("display_name")
707
+ or skill_name.replace("-", " ").title(),
708
+ "skill_id": skill_name,
709
+ "source": "MPM Skills",
710
+ "is_deployed": skill_name in deployed_names,
711
+ }
712
+
713
+ # Add any deployed skills not in available list (local/custom skills)
714
+ for skill in deployed_result["skills"]:
715
+ skill_name = skill["name"]
716
+ if skill_name not in skill_map:
717
+ skill_map[skill_name] = {
718
+ "name": skill_name.replace("-", " ").title(),
719
+ "skill_id": skill_name,
720
+ "source": "Local",
721
+ "is_deployed": True,
722
+ }
723
+
724
+ if not skill_map:
725
+ console.print("[yellow]No skills available.[/yellow]")
683
726
  console.print(
684
727
  "[dim]Use 'claude-mpm skills deploy-github' to deploy skills.[/dim]\n"
685
728
  )
686
729
  return CommandResult(success=True, exit_code=0)
687
730
 
688
- console.print(
689
- f"[green]{result['deployed_count']} skill(s) deployed:[/green]\n"
731
+ # Create table matching agent management format
732
+ table = Table(show_header=True, header_style="bold cyan")
733
+ table.add_column("#", style="bright_black", width=6, no_wrap=True)
734
+ table.add_column(
735
+ "Skill ID", style="bright_black", no_wrap=True, overflow="ellipsis"
690
736
  )
737
+ table.add_column(
738
+ "Name", style="bright_cyan", no_wrap=True, overflow="ellipsis"
739
+ )
740
+ table.add_column("Source", style="bright_yellow", no_wrap=True)
741
+ table.add_column("Status", style="bright_black", no_wrap=True)
691
742
 
692
- # Create table for deployed skills
693
- table = Table(show_header=True, header_style="bold cyan")
694
- table.add_column("Skill Name", style="green")
695
- table.add_column("Path", style="dim")
743
+ # Sort skills by name for consistent display
744
+ sorted_skills = sorted(skill_map.values(), key=lambda s: s["skill_id"])
696
745
 
697
- for skill in sorted(result["skills"], key=lambda s: s["name"]):
698
- table.add_row(skill["name"], skill["path"])
746
+ for idx, skill in enumerate(sorted_skills, 1):
747
+ status = (
748
+ "[green]Installed[/green]" if skill["is_deployed"] else "Available"
749
+ )
750
+ table.add_row(
751
+ str(idx), skill["skill_id"], skill["name"], skill["source"], status
752
+ )
699
753
 
700
754
  console.print(table)
701
755
  console.print()
702
756
 
757
+ # Show summary
758
+ deployed_count = sum(1 for s in skill_map.values() if s["is_deployed"])
759
+ console.print(
760
+ f"[dim]Showing {len(skill_map)} skills ({deployed_count} installed, "
761
+ f"{len(skill_map) - deployed_count} available)[/dim]\n"
762
+ )
763
+
703
764
  return CommandResult(success=True, exit_code=0)
704
765
 
705
766
  except Exception as e:
@@ -1222,6 +1283,99 @@ class SkillsManagementCommand(BaseCommand):
1222
1283
  console.print(f"[dim]{traceback.format_exc()}[/dim]")
1223
1284
  return CommandResult(success=False, message=str(e), exit_code=1)
1224
1285
 
1286
+ def _select_skills_interactive(self, args) -> CommandResult:
1287
+ """Interactive skill selection with topic grouping.
1288
+
1289
+ This command provides a two-tier selection interface:
1290
+ 1. Select topic groups (toolchains) to explore
1291
+ 2. Multi-select skills within each topic group
1292
+
1293
+ Features:
1294
+ - Groups skills by toolchain (universal, python, typescript, etc.)
1295
+ - Shows skills auto-included by agent dependencies
1296
+ - Displays token counts for each skill
1297
+ - Updates config and runs reconciliation
1298
+
1299
+ Returns:
1300
+ CommandResult with success/failure status
1301
+ """
1302
+ try:
1303
+ from ...cli.interactive.skill_selector import run_skill_selector
1304
+ from ...core.unified_config import UnifiedConfig
1305
+ from ...services.agents.deployment.deployment_reconciler import (
1306
+ DeploymentReconciler,
1307
+ )
1308
+
1309
+ console.print("\n[bold cyan]Interactive Skill Selector[/bold cyan]\n")
1310
+
1311
+ # Run skill selector
1312
+ selected_skills = run_skill_selector()
1313
+
1314
+ if selected_skills is None:
1315
+ console.print("\n[yellow]Skill selection cancelled[/yellow]")
1316
+ return CommandResult(success=True, exit_code=0)
1317
+
1318
+ # Update config with selected skills
1319
+ config = UnifiedConfig()
1320
+ config.skills.enabled = selected_skills
1321
+
1322
+ # Save config
1323
+ try:
1324
+ config.save()
1325
+ console.print(
1326
+ f"\n[green]✓ Saved {len(selected_skills)} skills to configuration[/green]"
1327
+ )
1328
+ except Exception as e:
1329
+ console.print(f"\n[red]Failed to save configuration: {e}[/red]")
1330
+ return CommandResult(success=False, message=str(e), exit_code=1)
1331
+
1332
+ # Run reconciliation to deploy skills
1333
+ console.print("\n[cyan]Running skill reconciliation...[/cyan]")
1334
+ reconciler = DeploymentReconciler(config)
1335
+
1336
+ try:
1337
+ from pathlib import Path
1338
+
1339
+ project_path = Path.cwd()
1340
+ result = reconciler.reconcile_skills(project_path)
1341
+
1342
+ if result.deployed:
1343
+ console.print(
1344
+ f" [green]✓ Deployed: {', '.join(result.deployed)}[/green]"
1345
+ )
1346
+ if result.removed:
1347
+ console.print(
1348
+ f" [yellow]✓ Removed: {', '.join(result.removed)}[/yellow]"
1349
+ )
1350
+ if result.errors:
1351
+ for error in result.errors:
1352
+ console.print(f" [red]✗ {error}[/red]")
1353
+
1354
+ if result.success:
1355
+ console.print(
1356
+ "\n[bold green]✓ Skill deployment complete![/bold green]"
1357
+ )
1358
+ return CommandResult(success=True, exit_code=0)
1359
+ console.print(
1360
+ f"\n[yellow]⚠ Deployment had {len(result.errors)} errors[/yellow]"
1361
+ )
1362
+ return CommandResult(
1363
+ success=False,
1364
+ message=f"{len(result.errors)} deployment errors",
1365
+ exit_code=1,
1366
+ )
1367
+
1368
+ except Exception as e:
1369
+ console.print(f"\n[red]Reconciliation failed: {e}[/red]")
1370
+ return CommandResult(success=False, message=str(e), exit_code=1)
1371
+
1372
+ except Exception as e:
1373
+ console.print(f"[red]Skill selection error: {e}[/red]")
1374
+ import traceback
1375
+
1376
+ console.print(f"[dim]{traceback.format_exc()}[/dim]")
1377
+ return CommandResult(success=False, message=str(e), exit_code=1)
1378
+
1225
1379
 
1226
1380
  def manage_skills(args) -> int:
1227
1381
  """