claude-mpm 5.4.85__py3-none-any.whl → 5.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +8 -5
  3. claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +101 -703
  5. claude_mpm/agents/WORKFLOW.md +2 -0
  6. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  7. claude_mpm/cli/commands/autotodos.py +566 -0
  8. claude_mpm/cli/commands/commander.py +46 -0
  9. claude_mpm/cli/commands/hook_errors.py +60 -60
  10. claude_mpm/cli/commands/monitor.py +2 -2
  11. claude_mpm/cli/commands/mpm_init/core.py +2 -2
  12. claude_mpm/cli/commands/run.py +35 -3
  13. claude_mpm/cli/executor.py +119 -16
  14. claude_mpm/cli/parsers/base_parser.py +71 -1
  15. claude_mpm/cli/parsers/commander_parser.py +83 -0
  16. claude_mpm/cli/parsers/run_parser.py +10 -0
  17. claude_mpm/cli/startup.py +54 -16
  18. claude_mpm/cli/startup_display.py +72 -5
  19. claude_mpm/cli/startup_logging.py +2 -2
  20. claude_mpm/cli/utils.py +7 -3
  21. claude_mpm/commander/__init__.py +72 -0
  22. claude_mpm/commander/adapters/__init__.py +31 -0
  23. claude_mpm/commander/adapters/base.py +191 -0
  24. claude_mpm/commander/adapters/claude_code.py +361 -0
  25. claude_mpm/commander/adapters/communication.py +366 -0
  26. claude_mpm/commander/api/__init__.py +16 -0
  27. claude_mpm/commander/api/app.py +105 -0
  28. claude_mpm/commander/api/errors.py +112 -0
  29. claude_mpm/commander/api/routes/__init__.py +8 -0
  30. claude_mpm/commander/api/routes/events.py +184 -0
  31. claude_mpm/commander/api/routes/inbox.py +171 -0
  32. claude_mpm/commander/api/routes/messages.py +148 -0
  33. claude_mpm/commander/api/routes/projects.py +271 -0
  34. claude_mpm/commander/api/routes/sessions.py +215 -0
  35. claude_mpm/commander/api/routes/work.py +260 -0
  36. claude_mpm/commander/api/schemas.py +182 -0
  37. claude_mpm/commander/chat/__init__.py +7 -0
  38. claude_mpm/commander/chat/cli.py +107 -0
  39. claude_mpm/commander/chat/commands.py +96 -0
  40. claude_mpm/commander/chat/repl.py +310 -0
  41. claude_mpm/commander/config.py +49 -0
  42. claude_mpm/commander/config_loader.py +115 -0
  43. claude_mpm/commander/daemon.py +398 -0
  44. claude_mpm/commander/events/__init__.py +26 -0
  45. claude_mpm/commander/events/manager.py +332 -0
  46. claude_mpm/commander/frameworks/__init__.py +12 -0
  47. claude_mpm/commander/frameworks/base.py +143 -0
  48. claude_mpm/commander/frameworks/claude_code.py +58 -0
  49. claude_mpm/commander/frameworks/mpm.py +62 -0
  50. claude_mpm/commander/inbox/__init__.py +16 -0
  51. claude_mpm/commander/inbox/dedup.py +128 -0
  52. claude_mpm/commander/inbox/inbox.py +224 -0
  53. claude_mpm/commander/inbox/models.py +70 -0
  54. claude_mpm/commander/instance_manager.py +337 -0
  55. claude_mpm/commander/llm/__init__.py +6 -0
  56. claude_mpm/commander/llm/openrouter_client.py +167 -0
  57. claude_mpm/commander/llm/summarizer.py +70 -0
  58. claude_mpm/commander/models/__init__.py +18 -0
  59. claude_mpm/commander/models/events.py +121 -0
  60. claude_mpm/commander/models/project.py +162 -0
  61. claude_mpm/commander/models/work.py +214 -0
  62. claude_mpm/commander/parsing/__init__.py +20 -0
  63. claude_mpm/commander/parsing/extractor.py +132 -0
  64. claude_mpm/commander/parsing/output_parser.py +270 -0
  65. claude_mpm/commander/parsing/patterns.py +100 -0
  66. claude_mpm/commander/persistence/__init__.py +11 -0
  67. claude_mpm/commander/persistence/event_store.py +274 -0
  68. claude_mpm/commander/persistence/state_store.py +309 -0
  69. claude_mpm/commander/persistence/work_store.py +164 -0
  70. claude_mpm/commander/polling/__init__.py +13 -0
  71. claude_mpm/commander/polling/event_detector.py +104 -0
  72. claude_mpm/commander/polling/output_buffer.py +49 -0
  73. claude_mpm/commander/polling/output_poller.py +153 -0
  74. claude_mpm/commander/project_session.py +268 -0
  75. claude_mpm/commander/proxy/__init__.py +12 -0
  76. claude_mpm/commander/proxy/formatter.py +89 -0
  77. claude_mpm/commander/proxy/output_handler.py +191 -0
  78. claude_mpm/commander/proxy/relay.py +155 -0
  79. claude_mpm/commander/registry.py +404 -0
  80. claude_mpm/commander/runtime/__init__.py +10 -0
  81. claude_mpm/commander/runtime/executor.py +191 -0
  82. claude_mpm/commander/runtime/monitor.py +316 -0
  83. claude_mpm/commander/session/__init__.py +6 -0
  84. claude_mpm/commander/session/context.py +81 -0
  85. claude_mpm/commander/session/manager.py +59 -0
  86. claude_mpm/commander/tmux_orchestrator.py +361 -0
  87. claude_mpm/commander/web/__init__.py +1 -0
  88. claude_mpm/commander/work/__init__.py +30 -0
  89. claude_mpm/commander/work/executor.py +189 -0
  90. claude_mpm/commander/work/queue.py +405 -0
  91. claude_mpm/commander/workflow/__init__.py +27 -0
  92. claude_mpm/commander/workflow/event_handler.py +219 -0
  93. claude_mpm/commander/workflow/notifier.py +146 -0
  94. claude_mpm/commands/mpm-config.md +8 -0
  95. claude_mpm/commands/mpm-doctor.md +8 -0
  96. claude_mpm/commands/mpm-help.md +8 -0
  97. claude_mpm/commands/mpm-init.md +8 -0
  98. claude_mpm/commands/mpm-monitor.md +8 -0
  99. claude_mpm/commands/mpm-organize.md +8 -0
  100. claude_mpm/commands/mpm-postmortem.md +8 -0
  101. claude_mpm/commands/mpm-session-resume.md +9 -1
  102. claude_mpm/commands/mpm-status.md +8 -0
  103. claude_mpm/commands/mpm-ticket-view.md +8 -0
  104. claude_mpm/commands/mpm-version.md +8 -0
  105. claude_mpm/commands/mpm.md +8 -0
  106. claude_mpm/config/agent_presets.py +8 -7
  107. claude_mpm/core/config.py +5 -0
  108. claude_mpm/core/hook_manager.py +51 -3
  109. claude_mpm/core/logger.py +10 -7
  110. claude_mpm/core/logging_utils.py +4 -2
  111. claude_mpm/core/output_style_manager.py +15 -5
  112. claude_mpm/core/unified_config.py +10 -6
  113. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  114. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  115. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
  116. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
  117. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
  118. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
  119. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
  120. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
  121. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
  122. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
  123. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
  124. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
  125. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
  126. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
  127. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
  128. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  129. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  130. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
  131. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
  132. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
  133. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
  134. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
  135. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
  136. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
  137. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
  138. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  139. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
  140. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
  141. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
  142. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
  143. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
  144. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
  145. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
  146. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
  147. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
  148. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
  149. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
  150. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
  151. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
  152. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
  153. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
  154. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
  155. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
  156. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
  157. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  158. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
  159. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
  160. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
  161. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  162. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
  163. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
  164. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
  165. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
  166. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  167. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
  168. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
  169. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  170. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  171. claude_mpm/dashboard/static/svelte-build/index.html +9 -9
  172. claude_mpm/experimental/cli_enhancements.py +2 -1
  173. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  174. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  175. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +486 -0
  176. claude_mpm/hooks/claude_hooks/event_handlers.py +250 -11
  177. claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
  178. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  179. claude_mpm/hooks/claude_hooks/installer.py +69 -5
  180. claude_mpm/hooks/claude_hooks/response_tracking.py +3 -1
  181. claude_mpm/hooks/claude_hooks/services/connection_manager.py +20 -0
  182. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +14 -77
  183. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +30 -6
  184. claude_mpm/hooks/session_resume_hook.py +85 -1
  185. claude_mpm/init.py +1 -1
  186. claude_mpm/scripts/claude-hook-handler.sh +36 -10
  187. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  188. claude_mpm/services/agents/cache_git_manager.py +1 -1
  189. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
  190. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  191. claude_mpm/services/cli/__init__.py +3 -0
  192. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  193. claude_mpm/services/cli/session_resume_helper.py +10 -2
  194. claude_mpm/services/delegation_detector.py +175 -0
  195. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  196. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  197. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  198. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  199. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  200. claude_mpm/services/diagnostics/models.py +14 -1
  201. claude_mpm/services/event_log.py +325 -0
  202. claude_mpm/services/infrastructure/__init__.py +4 -0
  203. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  204. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  205. claude_mpm/services/monitor/daemon_manager.py +15 -4
  206. claude_mpm/services/monitor/management/lifecycle.py +8 -2
  207. claude_mpm/services/monitor/server.py +106 -16
  208. claude_mpm/services/pm_skills_deployer.py +259 -87
  209. claude_mpm/services/skills/git_skill_source_manager.py +51 -2
  210. claude_mpm/services/skills/selective_skill_deployer.py +114 -16
  211. claude_mpm/services/skills/skill_discovery_service.py +57 -3
  212. claude_mpm/services/socketio/handlers/hook.py +14 -7
  213. claude_mpm/services/socketio/server/main.py +12 -4
  214. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  215. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  216. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  217. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  218. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  219. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  220. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  221. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  222. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  223. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  224. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  225. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  226. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  227. claude_mpm/skills/bundled/pm/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
  228. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  229. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  230. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  231. claude_mpm/skills/skill_manager.py +4 -4
  232. claude_mpm-5.6.1.dist-info/METADATA +391 -0
  233. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/RECORD +244 -145
  234. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
  235. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
  236. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
  237. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
  238. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
  239. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
  240. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
  241. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
  242. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
  243. claude_mpm-5.4.85.dist-info/METADATA +0 -1023
  244. /claude_mpm/skills/bundled/pm/{pm-bug-reporting/pm-bug-reporting.md → mpm-bug-reporting/SKILL.md} +0 -0
  245. /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
  246. /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
  247. /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
  248. /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
  249. /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
  250. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/WHEEL +0 -0
  251. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/entry_points.txt +0 -0
  252. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE +0 -0
  253. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  254. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/top_level.txt +0 -0
@@ -214,18 +214,90 @@ def get_skills_from_mapping(agent_ids: List[str]) -> Set[str]:
214
214
  return set()
215
215
 
216
216
 
217
+ def extract_skills_from_content(agent_file: Path) -> Set[str]:
218
+ """Extract skill names from [SKILL: skill-name] markers in agent file content.
219
+
220
+ This function complements frontmatter skill extraction by finding inline
221
+ skill references in the agent's markdown content body.
222
+
223
+ Supports multiple formats:
224
+ - Bold marker: **[SKILL: skill-name]**
225
+ - Plain marker: [SKILL: skill-name]
226
+ - Backtick list: - `skill-name` - Description
227
+ - With spaces: [SKILL: skill-name ]
228
+
229
+ Args:
230
+ agent_file: Path to agent markdown file
231
+
232
+ Returns:
233
+ Set of skill names found in content body
234
+
235
+ Example:
236
+ >>> skills = extract_skills_from_content(Path("pm.md"))
237
+ >>> # Finds skills from markers like **[SKILL: mpm-delegation-patterns]**
238
+ >>> # Also finds from lists like - `mpm-teaching-mode` - Description
239
+ >>> print(f"Found {len(skills)} skills in content")
240
+ """
241
+ try:
242
+ content = agent_file.read_text(encoding="utf-8")
243
+ except Exception as e:
244
+ logger.warning(f"Failed to read {agent_file}: {e}")
245
+ return set()
246
+
247
+ skills = set()
248
+
249
+ # Pattern 1: [SKILL: skill-name] markers (with optional markdown bold)
250
+ # Handles: **[SKILL: skill-name]** or [SKILL: skill-name]
251
+ # Pattern breakdown:
252
+ # - \*{0,2}: Optional bold markdown (0-2 asterisks)
253
+ # - \[SKILL:\s*: Opening bracket with optional whitespace
254
+ # - ([a-zA-Z0-9_-]+): Skill name (capture group)
255
+ # - \s*\]: Closing bracket with optional whitespace
256
+ # - \*{0,2}: Optional closing bold markdown
257
+ pattern1 = r"\*{0,2}\[SKILL:\s*([a-zA-Z0-9_-]+)\s*\]\*{0,2}"
258
+ matches1 = re.findall(pattern1, content, re.IGNORECASE)
259
+ skills.update(matches1)
260
+
261
+ # Pattern 2: Backtick list items with mpm-* or toolchains-* skills
262
+ # Handles: - `mpm-skill-name` - Description
263
+ # Pattern breakdown:
264
+ # - ^-\s+: Start with dash and whitespace (list item)
265
+ # - `: Opening backtick
266
+ # - ((?:mpm-|toolchains-|universal-)[a-zA-Z0-9_-]+): Skill name starting with prefix
267
+ # - `: Closing backtick
268
+ # - \s+-: Followed by whitespace and dash (description separator)
269
+ pattern2 = r"^-\s+`((?:mpm-|toolchains-|universal-)[a-zA-Z0-9_-]+)`\s+-"
270
+ matches2 = re.findall(pattern2, content, re.MULTILINE | re.IGNORECASE)
271
+ skills.update(matches2)
272
+
273
+ if skills:
274
+ logger.debug(
275
+ f"Found {len(skills)} skills from content markers in {agent_file.name}"
276
+ )
277
+
278
+ return skills
279
+
280
+
217
281
  def get_required_skills_from_agents(agents_dir: Path) -> Set[str]:
218
282
  """Extract all skills referenced by deployed agents.
219
283
 
220
- MAJOR CHANGE (Phase 3): Now ONLY uses frontmatter-declared skills.
284
+ MAJOR CHANGE (Phase 3): Now uses TWO sources for skill discovery:
285
+ 1. Frontmatter-declared skills (skills: field)
286
+ 2. Content body markers ([SKILL: skill-name])
287
+
221
288
  The static skill_to_agent_mapping.yaml is DEPRECATED. Each agent must
222
- declare its skills in frontmatter or it gets zero skills deployed.
289
+ declare its skills via frontmatter OR inline markers.
223
290
 
224
291
  This change:
225
292
  - Eliminates dual-source complexity (frontmatter + mapping)
226
293
  - Makes skill requirements explicit per agent
227
- - Enables per-agent customization via frontmatter
294
+ - Enables per-agent customization via frontmatter or inline markers
228
295
  - Removes dependency on static YAML mapping
296
+ - Fixes PM skills being removed as orphaned (they use inline markers)
297
+
298
+ Special handling for PM_INSTRUCTIONS.md:
299
+ - Also scans .claude-mpm/PM_INSTRUCTIONS.md for skill markers
300
+ - PM instructions are not in agents_dir but contain [SKILL: ...] references
229
301
 
230
302
  Args:
231
303
  agents_dir: Path to deployed agents directory (e.g., .claude/agents/)
@@ -244,37 +316,63 @@ def get_required_skills_from_agents(agents_dir: Path) -> Set[str]:
244
316
 
245
317
  # Scan all agent markdown files
246
318
  agent_files = list(agents_dir.glob("*.md"))
247
- logger.debug(f"Scanning {len(agent_files)} agent files in {agents_dir}")
248
319
 
249
- # ONLY use frontmatter skills - no more mapping inference
320
+ # Special case: Add PM_INSTRUCTIONS.md if it exists
321
+ # PM instructions live in .claude-mpm/ not .claude/agents/
322
+ pm_instructions = agents_dir.parent.parent / ".claude-mpm" / "PM_INSTRUCTIONS.md"
323
+ if pm_instructions.exists():
324
+ agent_files.append(pm_instructions)
325
+ logger.debug("Added PM_INSTRUCTIONS.md for skill scanning")
326
+
327
+ logger.debug(f"Scanning {len(agent_files)} agent files (including PM instructions)")
328
+
329
+ # Use TWO sources: frontmatter AND content markers
250
330
  frontmatter_skills = set()
331
+ content_skills = set()
251
332
 
252
333
  for agent_file in agent_files:
253
334
  agent_id = agent_file.stem
254
335
 
336
+ # Source 1: Extract from frontmatter
255
337
  frontmatter = parse_agent_frontmatter(agent_file)
256
- agent_skills = get_skills_from_agent(frontmatter)
338
+ agent_fm_skills = get_skills_from_agent(frontmatter)
257
339
 
258
- if agent_skills:
259
- frontmatter_skills.update(agent_skills)
340
+ if agent_fm_skills:
341
+ frontmatter_skills.update(agent_fm_skills)
260
342
  logger.debug(
261
- f"Agent {agent_id}: {len(agent_skills)} skills from frontmatter"
343
+ f"Agent {agent_id}: {len(agent_fm_skills)} skills from frontmatter"
262
344
  )
263
- else:
264
- logger.debug(f"Agent {agent_id}: No skills declared in frontmatter")
345
+
346
+ # Source 2: Extract from content body [SKILL: ...] markers
347
+ agent_content_skills = extract_skills_from_content(agent_file)
348
+
349
+ if agent_content_skills:
350
+ content_skills.update(agent_content_skills)
351
+ logger.debug(
352
+ f"Agent {agent_id}: {len(agent_content_skills)} skills from content markers"
353
+ )
354
+
355
+ if not agent_fm_skills and not agent_content_skills:
356
+ logger.debug(
357
+ f"Agent {agent_id}: No skills declared (checked frontmatter + content)"
358
+ )
359
+
360
+ # Combine both sources
361
+ all_skills = frontmatter_skills | content_skills
265
362
 
266
363
  logger.info(
267
- f"Found {len(frontmatter_skills)} unique skills from agent frontmatter "
268
- f"(static mapping no longer used)"
364
+ f"Found {len(all_skills)} unique skills "
365
+ f"({len(frontmatter_skills)} from frontmatter, "
366
+ f"{len(content_skills)} from content markers)"
269
367
  )
270
368
 
271
369
  # Normalize skill paths: convert slashes to dashes for compatibility with deployment
272
370
  # Some skills may use slash format, normalize to dashes
273
- normalized_skills = {skill.replace("/", "-") for skill in frontmatter_skills}
371
+ normalized_skills = {skill.replace("/", "-") for skill in all_skills}
274
372
 
275
- if normalized_skills != frontmatter_skills:
373
+ if normalized_skills != all_skills:
276
374
  logger.debug(
277
- f"Normalized {len(frontmatter_skills)} skills to {len(normalized_skills)} "
375
+ f"Normalized {len(all_skills)} skills to {len(normalized_skills)} "
278
376
  "(converted slashes to dashes)"
279
377
  )
280
378
 
@@ -163,10 +163,22 @@ class SkillDiscoveryService:
163
163
  skill_md_files = list(self.skills_dir.rglob("SKILL.md"))
164
164
 
165
165
  # Also find legacy *.md files in top-level directory for backward compatibility
166
+ # Exclude common non-skill documentation files
167
+ excluded_filenames = {
168
+ "skill.md", # Case variations of SKILL.md
169
+ "readme.md",
170
+ "claude.md",
171
+ "contributing.md",
172
+ "changelog.md",
173
+ "license.md",
174
+ "authors.md",
175
+ "code_of_conduct.md",
176
+ }
177
+
166
178
  legacy_md_files = [
167
179
  f
168
180
  for f in self.skills_dir.glob("*.md")
169
- if f.name != "SKILL.md" and f.name.lower() != "readme.md"
181
+ if f.name.lower() not in excluded_filenames
170
182
  ]
171
183
 
172
184
  all_skill_files = skill_md_files + legacy_md_files
@@ -255,7 +267,35 @@ class SkillDiscoveryService:
255
267
  try:
256
268
  frontmatter, body = self._extract_frontmatter(content)
257
269
  except Exception as e:
258
- self.logger.warning(f"No valid frontmatter in {skill_file.name}: {e}")
270
+ # Only log as debug for documentation files to reduce noise
271
+ # Common documentation files (CLAUDE.md, README.md) are expected to lack skill frontmatter
272
+ relative_path = (
273
+ skill_file.relative_to(self.skills_dir)
274
+ if skill_file.is_relative_to(self.skills_dir)
275
+ else skill_file
276
+ )
277
+
278
+ # Check if this looks like a documentation file
279
+ is_documentation = any(
280
+ doc_pattern in skill_file.name.lower()
281
+ for doc_pattern in [
282
+ "readme",
283
+ "claude",
284
+ "contributing",
285
+ "changelog",
286
+ "license",
287
+ ]
288
+ )
289
+
290
+ if is_documentation:
291
+ self.logger.debug(
292
+ f"Skipping documentation file {relative_path} (no skill frontmatter): {e}"
293
+ )
294
+ else:
295
+ # For actual skill files with invalid YAML, use warning level
296
+ self.logger.warning(
297
+ f"Failed to parse skill frontmatter in {relative_path}: {e}"
298
+ )
259
299
  return None
260
300
 
261
301
  # Validate required fields
@@ -354,10 +394,24 @@ class SkillDiscoveryService:
354
394
  frontmatter_text = match.group(1)
355
395
  body = match.group(2)
356
396
 
357
- # Parse YAML
397
+ # Parse YAML with improved error handling
358
398
  try:
359
399
  frontmatter = yaml.safe_load(frontmatter_text)
360
400
  except yaml.YAMLError as e:
401
+ # Provide more specific error message with context
402
+ error_line = getattr(e, "problem_mark", None)
403
+ if error_line:
404
+ line_num = error_line.line + 1
405
+ col_num = error_line.column + 1
406
+ # Extract problematic line for context
407
+ lines = frontmatter_text.split("\n")
408
+ problem_line = (
409
+ lines[error_line.line] if error_line.line < len(lines) else ""
410
+ )
411
+ raise ValueError(
412
+ f"Invalid YAML in frontmatter at line {line_num}, column {col_num}: {e.problem}\n"
413
+ f" Problematic line: {problem_line.strip()}"
414
+ ) from e
361
415
  raise ValueError(f"Invalid YAML in frontmatter: {e}") from e
362
416
 
363
417
  if not isinstance(frontmatter, dict):
@@ -118,7 +118,8 @@ class HookEventHandler(BaseEventHandler):
118
118
  self.server.active_sessions[session_id] = {
119
119
  "session_id": session_id,
120
120
  "start_time": datetime.now(timezone.utc).isoformat(),
121
- "agent": agent_type,
121
+ "current_agent": agent_type, # Current active agent
122
+ "agents": [agent_type], # All agents used in this session
122
123
  "status": ServiceState.RUNNING,
123
124
  "prompt": data.get("prompt", "")[:100], # First 100 chars
124
125
  "last_activity": datetime.now(timezone.utc).isoformat(),
@@ -169,7 +170,8 @@ class HookEventHandler(BaseEventHandler):
169
170
  self.server.active_sessions[session_id] = {
170
171
  "session_id": session_id,
171
172
  "start_time": datetime.now(timezone.utc).isoformat(),
172
- "agent": "pm", # Default to PM
173
+ "current_agent": "pm", # Current active agent
174
+ "agents": ["pm"], # All agents used in this session
173
175
  "status": ServiceState.RUNNING,
174
176
  "prompt": data.get("prompt_text", "")[:100],
175
177
  "working_directory": data.get("working_directory", ""),
@@ -200,11 +202,16 @@ class HookEventHandler(BaseEventHandler):
200
202
  # Update session with new agent
201
203
  if hasattr(self.server, "active_sessions"):
202
204
  if session_id in self.server.active_sessions:
203
- self.server.active_sessions[session_id]["agent"] = agent_type
204
- self.server.active_sessions[session_id]["status"] = "delegated"
205
- self.server.active_sessions[session_id]["last_activity"] = datetime.now(
206
- timezone.utc
207
- ).isoformat()
205
+ session = self.server.active_sessions[session_id]
206
+ session["current_agent"] = agent_type
207
+ session["status"] = "delegated"
208
+ session["last_activity"] = datetime.now(timezone.utc).isoformat()
209
+
210
+ # Add to agents list if not already present
211
+ if "agents" not in session:
212
+ session["agents"] = []
213
+ if agent_type not in session["agents"]:
214
+ session["agents"].append(agent_type)
208
215
 
209
216
  self.logger.debug(
210
217
  f"Updated session delegation: session={session_id[:8]}..., agent={agent_type}"
@@ -383,7 +383,8 @@ class SocketIOServer(SocketIOServiceInterface):
383
383
  self.active_sessions[session_id] = {
384
384
  "session_id": session_id,
385
385
  "start_time": datetime.now(timezone.utc).isoformat(),
386
- "agent": "pm", # Default to PM, will be updated if delegated
386
+ "current_agent": "pm", # Current active agent
387
+ "agents": ["pm"], # All agents used in this session
387
388
  "status": ServiceState.RUNNING,
388
389
  "launch_method": launch_method,
389
390
  "working_dir": working_dir,
@@ -419,8 +420,15 @@ class SocketIOServer(SocketIOServiceInterface):
419
420
  """Notify agent delegation."""
420
421
  # Update active session with current agent
421
422
  if self.session_id and self.session_id in self.active_sessions:
422
- self.active_sessions[self.session_id]["agent"] = agent
423
- self.active_sessions[self.session_id]["status"] = status
423
+ session = self.active_sessions[self.session_id]
424
+ session["current_agent"] = agent
425
+ session["status"] = status
426
+
427
+ # Add to agents list if not already present
428
+ if "agents" not in session:
429
+ session["agents"] = []
430
+ if agent not in session["agents"]:
431
+ session["agents"].append(agent)
424
432
 
425
433
  if self.broadcaster:
426
434
  self.broadcaster.agent_delegated(agent, task, status)
@@ -480,7 +488,7 @@ class SocketIOServer(SocketIOServiceInterface):
480
488
  start_time = datetime.fromisoformat(session_data["start_time"])
481
489
  if start_time.timestamp() < cutoff_time:
482
490
  sessions_to_remove.append(session_id)
483
- except Exception:
491
+ except Exception: # nosec B110 - Silently skip malformed timestamps
484
492
  pass
485
493
 
486
494
  for session_id in sessions_to_remove:
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: mpm
3
+ description: Access Claude MPM functionality and manage multi-agent orchestration
4
+ user-invocable: true
5
+ version: "1.0.0"
6
+ category: mpm-command
7
+ tags: [mpm-command, system, pm-required]
8
+ ---
9
+
10
+ # /mpm
11
+
12
+ Access Claude MPM functionality and manage your multi-agent orchestration.
13
+
14
+ ## Available MPM Commands
15
+
16
+ - `/mpm-agents` - Show available agents and versions
17
+ - `/mpm-doctor` - Run diagnostic checks
18
+ - `/mpm-help` - Show command help
19
+ - `/mpm-status` - Show MPM status
20
+ - `/mpm-ticket` - Ticketing workflow management (organize, proceed, status, update, project)
21
+ - `/mpm-config` - Manage configuration
22
+ - `/mpm-resume` - Create session resume files
23
+ - `/mpm-version` - Display version information for project, agents, and skills
24
+
25
+ ## What is Claude MPM?
26
+
27
+ Claude MPM extends Claude Code with:
28
+ - **Multi-agent orchestration** - Delegate work to specialized agents
29
+ - **Project-specific PM instructions** - Tailored guidance for your project
30
+ - **Agent memory management** - Context-aware agent interactions
31
+ - **WebSocket monitoring** - Real-time system monitoring
32
+ - **Hook system for automation** - Automate workflows and tasks
33
+
34
+ ## Quick Start
35
+
36
+ Use `/mpm-help` to explore commands or `/mpm-status` to check system health.
37
+
38
+ For more information, use `/mpm-help [command]` for specific command details.
@@ -0,0 +1,75 @@
1
+ # PM Skill: Agent Update Workflow
2
+
3
+ ## Trigger Patterns
4
+ - "update agent", "fix agent", "improve agent", "modify agent"
5
+ - "change {agent-name} agent", "edit agent instructions"
6
+ - Any request to modify agent behavior
7
+
8
+ ## FUNDAMENTAL RULE: Official vs Custom Agents
9
+
10
+ ### Official MPM Agents (NEVER edit deployed copies)
11
+ **Source**: `~/.claude-mpm/cache/agents/` (from bobmatnyc/claude-mpm-agents repo)
12
+ **Deployed**: `.claude/agents/` - READ-ONLY for official agents
13
+
14
+ **Detection**: Check if agent exists in `~/.claude-mpm/cache/agents/`
15
+ - If YES → Official agent → Follow Official Agent Workflow
16
+ - If NO → Custom agent → Can edit `.claude/agents/` directly
17
+
18
+ ### Custom/Localized Agents
19
+ - Created specifically for project
20
+ - Can be edited directly in `.claude/agents/`
21
+ - Not part of official MPM agent set
22
+
23
+ ## Official Agent Update Workflow
24
+
25
+ ### Step 1: Identify Agent Source
26
+ ```bash
27
+ ls ~/.claude-mpm/cache/agents/ # Find the source file
28
+ ```
29
+
30
+ ### Step 2: Update Source
31
+ Edit the agent source in `~/.claude-mpm/cache/agents/{agent-name}.md`
32
+ (or appropriate path based on agent structure)
33
+
34
+ ### Step 3: Rebuild and Redeploy
35
+ Use MPM deployment tools:
36
+ ```bash
37
+ # Redeploy specific agent
38
+ mpm agents deploy {agent-name}
39
+
40
+ # Or redeploy all agents
41
+ mpm agents deploy --all
42
+ ```
43
+
44
+ ### Step 4: Validate (claude-mpm project only)
45
+ When working in the claude-mpm project itself:
46
+ ```bash
47
+ # Run deepeval against deployed agent instructions
48
+ deepeval test --agent {agent-name}
49
+ ```
50
+
51
+ ## Circuit Breaker
52
+
53
+ **BLOCK** if attempting to edit `.claude/agents/{official-agent}.md` directly:
54
+ - Official agents in deployed location are BUILD OUTPUTS
55
+ - Must update source → rebuild → redeploy
56
+ - Violation = architectural breach
57
+
58
+ ## Examples
59
+
60
+ ### ❌ WRONG (Editing deployed official agent)
61
+ ```
62
+ Edit: .claude/agents/web-qa.md # VIOLATION - this is a built output
63
+ ```
64
+
65
+ ### ✅ CORRECT (Updating source and redeploying)
66
+ ```
67
+ 1. Edit: ~/.claude-mpm/cache/agents/web-qa.md # Update source
68
+ 2. Run: mpm agents deploy web-qa # Rebuild/redeploy
69
+ 3. Validate: deepeval test --agent web-qa # (in claude-mpm project)
70
+ ```
71
+
72
+ ### ✅ CORRECT (Custom agent - can edit directly)
73
+ ```
74
+ Edit: .claude/agents/my-custom-agent.md # OK - not an official agent
75
+ ```