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
claude_mpm/cli/startup.py CHANGED
@@ -161,7 +161,25 @@ def setup_early_environment(argv):
161
161
  # CRITICAL: Suppress ALL logging by default
162
162
  # This catches all loggers (claude_mpm.*, service.*, framework_loader, etc.)
163
163
  # This will be overridden by setup_mcp_server_logging() based on user preference
164
- logging.getLogger().setLevel(logging.CRITICAL + 1) # Root logger catches everything
164
+ root_logger = logging.getLogger()
165
+ root_logger.setLevel(logging.CRITICAL + 1) # Root logger catches everything
166
+ root_logger.handlers = [] # Remove any handlers
167
+
168
+ # Also suppress common module loggers explicitly to prevent handler leakage
169
+ for logger_name in [
170
+ "claude_mpm",
171
+ "path_resolver",
172
+ "file_loader",
173
+ "framework_loader",
174
+ "service",
175
+ "instruction_loader",
176
+ "agent_loader",
177
+ "startup",
178
+ ]:
179
+ module_logger = logging.getLogger(logger_name)
180
+ module_logger.setLevel(logging.CRITICAL + 1)
181
+ module_logger.handlers = []
182
+ module_logger.propagate = False
165
183
 
166
184
  # Process argv
167
185
  if argv is None:
@@ -191,7 +209,8 @@ def should_skip_background_services(args, processed_argv):
191
209
  skip_commands = ["--version", "-v", "--help", "-h"]
192
210
  return any(cmd in (processed_argv or sys.argv[1:]) for cmd in skip_commands) or (
193
211
  hasattr(args, "command")
194
- and args.command in ["info", "doctor", "config", "mcp", "configure"]
212
+ and args.command
213
+ in ["info", "doctor", "config", "mcp", "configure", "hook-errors", "autotodos"]
195
214
  )
196
215
 
197
216
 
@@ -234,7 +253,7 @@ def deploy_bundled_skills():
234
253
  if not skills_config.get("auto_deploy", True):
235
254
  # Auto-deploy disabled, skip silently
236
255
  return
237
- except Exception:
256
+ except Exception: # nosec B110
238
257
  # If config loading fails, assume auto-deploy is enabled (default)
239
258
  pass
240
259
 
@@ -308,68 +327,60 @@ def deploy_output_style_on_startup():
308
327
  communication without emojis and exclamation points. Styles are project-specific
309
328
  to allow different projects to have different communication styles.
310
329
 
311
- DESIGN DECISION: This is non-blocking and idempotent. Deploys to project-level
312
- directory (.claude/settings/output-styles/) instead of user-level to maintain
313
- project isolation.
330
+ DESIGN DECISION: This is non-blocking and idempotent. Deploys to user-level
331
+ directory (~/.claude/output-styles/) which is the official Claude Code location
332
+ for custom output styles.
314
333
 
315
- Deploys two styles:
316
- - claude-mpm-style.md (professional mode)
317
- - claude-mpm-teach.md (teaching mode)
334
+ Deploys all styles:
335
+ - claude-mpm.md (professional mode)
336
+ - claude-mpm-teacher.md (teaching mode)
337
+ - claude-mpm-research.md (research mode - for codebase analysis)
318
338
  """
319
339
  try:
320
- import shutil
321
- from pathlib import Path
340
+ from ..core.output_style_manager import OutputStyleManager
322
341
 
323
- # Source files (in framework package)
324
- package_dir = Path(__file__).parent.parent / "agents"
325
- professional_source = package_dir / "CLAUDE_MPM_OUTPUT_STYLE.md"
326
- teacher_source = package_dir / "CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md"
327
-
328
- # Target directory (USER-LEVEL for global availability)
329
- # Claude Code reads output styles from ~/.claude/settings/output-styles/
330
- user_home = Path.home()
331
- output_styles_dir = user_home / ".claude" / "settings" / "output-styles"
332
- professional_target = output_styles_dir / "claude-mpm-style.md"
333
- teacher_target = output_styles_dir / "claude-mpm-teach.md"
334
-
335
- # Create directory if it doesn't exist
336
- output_styles_dir.mkdir(parents=True, exist_ok=True)
337
-
338
- # Check if already deployed AND up-to-date (compare sizes to detect changes)
339
- professional_up_to_date = (
340
- professional_target.exists()
341
- and professional_source.exists()
342
- and professional_target.stat().st_size == professional_source.stat().st_size
343
- )
344
- teacher_up_to_date = (
345
- teacher_target.exists()
346
- and teacher_source.exists()
347
- and teacher_target.stat().st_size == teacher_source.stat().st_size
348
- )
342
+ # Initialize the output style manager
343
+ manager = OutputStyleManager()
349
344
 
350
- if professional_up_to_date and teacher_up_to_date:
345
+ # Check if Claude Code version supports output styles (>= 1.0.83)
346
+ if not manager.supports_output_styles():
347
+ # Skip deployment for older versions
348
+ # The manager will fall back to injecting content directly
349
+ return
350
+
351
+ # Check if all styles are already deployed and up-to-date
352
+ all_up_to_date = True
353
+ for style_config in manager.styles.values():
354
+ source_path = style_config["source"]
355
+ target_path = style_config["target"]
356
+
357
+ if not (
358
+ target_path.exists()
359
+ and source_path.exists()
360
+ and target_path.stat().st_size == source_path.stat().st_size
361
+ ):
362
+ all_up_to_date = False
363
+ break
364
+
365
+ if all_up_to_date:
351
366
  # Show feedback that output styles are ready
352
367
  print("✓ Output styles ready", flush=True)
353
368
  return
354
369
 
355
- # Deploy both styles
356
- deployed_count = 0
357
- if professional_source.exists():
358
- shutil.copy2(professional_source, professional_target)
359
- deployed_count += 1
370
+ # Deploy all styles using the manager
371
+ results = manager.deploy_all_styles(activate_default=True)
360
372
 
361
- if teacher_source.exists():
362
- shutil.copy2(teacher_source, teacher_target)
363
- deployed_count += 1
373
+ # Count successful deployments
374
+ deployed_count = sum(1 for success in results.values() if success)
364
375
 
365
376
  if deployed_count > 0:
366
377
  print(f"✓ Output styles deployed ({deployed_count} styles)", flush=True)
367
378
  else:
368
- # Source files missing - log but don't fail
379
+ # Deployment failed - log but don't fail startup
369
380
  from ..core.logger import get_logger
370
381
 
371
382
  logger = get_logger("cli")
372
- logger.debug("Output style source files not found")
383
+ logger.debug("Failed to deploy any output styles")
373
384
 
374
385
  except Exception as e:
375
386
  # Non-critical - log but don't fail startup
@@ -458,7 +469,7 @@ def _cleanup_orphaned_agents(deploy_target: Path, deployed_agents: list[str]) ->
458
469
  return removed_count
459
470
 
460
471
 
461
- def sync_remote_agents_on_startup():
472
+ def sync_remote_agents_on_startup(force_sync: bool = False):
462
473
  """
463
474
  Synchronize agent templates from remote sources on startup.
464
475
 
@@ -476,6 +487,9 @@ def sync_remote_agents_on_startup():
476
487
  3. Cleanup orphaned agents (ours but no longer deployed) - Phase 3
477
488
  4. Cleanup legacy agent cache directories (after sync/deployment) - Phase 4
478
489
  5. Log deployment results
490
+
491
+ Args:
492
+ force_sync: Force download even if cache is fresh (bypasses ETag).
479
493
  """
480
494
  # DEPRECATED: Legacy warning - no-op function, kept for compatibility
481
495
  check_legacy_cache()
@@ -486,7 +500,6 @@ def sync_remote_agents_on_startup():
486
500
  from pathlib import Path
487
501
 
488
502
  from ..core.shared.config_loader import ConfigLoader
489
- from ..services.agents.deployment.agent_deployment import AgentDeploymentService
490
503
  from ..services.agents.startup_sync import sync_agents_on_startup
491
504
  from ..services.profile_manager import ProfileManager
492
505
  from ..utils.progress import ProgressBar
@@ -511,7 +524,7 @@ def sync_remote_agents_on_startup():
511
524
  )
512
525
 
513
526
  # Phase 1: Sync files from Git sources
514
- result = sync_agents_on_startup()
527
+ result = sync_agents_on_startup(force_refresh=force_sync)
515
528
 
516
529
  # Only proceed with deployment if sync was enabled and ran
517
530
  if result.get("enabled") and result.get("sources_synced", 0) > 0:
@@ -534,305 +547,89 @@ def sync_remote_agents_on_startup():
534
547
  logger.warning(f"Agent sync completed with {len(errors)} errors")
535
548
 
536
549
  # Phase 2: Deploy agents from cache to ~/.claude/agents/
537
- # This mirrors the skills deployment pattern (lines 371-407)
550
+ # Use reconciliation service to respect configuration.yaml settings
538
551
  try:
539
- # Initialize deployment service with profile-filtered configuration
540
- from ..core.config import Config
541
-
542
- deploy_config = None
543
- if active_profile and profile_manager.active_profile:
544
- # Create config with excluded agents based on profile
545
- # Get all agents that should be excluded (not in enabled list)
546
- from pathlib import Path
547
-
548
- cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
549
- if cache_dir.exists():
550
- # Find all agent files
551
- # Supports both flat cache and {owner}/{repo}/agents/ structure
552
- all_agent_files = [
553
- f
554
- for f in cache_dir.rglob("*.md")
555
- if "/agents/" in str(f)
556
- and f.stem.lower() != "base-agent"
557
- and f.name.lower()
558
- not in {"readme.md", "changelog.md", "contributing.md"}
559
- ]
560
-
561
- # Build exclusion list for agents not in profile
562
- excluded_agents = []
563
- for agent_file in all_agent_files:
564
- agent_name = agent_file.stem
565
- if not profile_manager.is_agent_enabled(agent_name):
566
- excluded_agents.append(agent_name)
567
-
568
- if excluded_agents:
569
- # Get singleton config and update with profile settings
570
- # BUGFIX: Config is a singleton that ignores dict parameter if already initialized.
571
- # Creating Config({...}) doesn't store excluded_agents - use set() instead.
572
- deploy_config = Config()
573
- deploy_config.set(
574
- "agent_deployment.excluded_agents", excluded_agents
575
- )
576
- deploy_config.set(
577
- "agent_deployment.filter_non_mpm_agents", False
578
- )
579
- deploy_config.set("agent_deployment.case_sensitive", False)
580
- deploy_config.set(
581
- "agent_deployment.exclude_dependencies", False
582
- )
583
- logger.info(
584
- f"Profile '{active_profile}': Excluding {len(excluded_agents)} agents from deployment"
585
- )
586
-
587
- deployment_service = AgentDeploymentService(config=deploy_config)
588
-
589
- # Count agents in cache to show accurate progress
590
552
  from pathlib import Path
591
553
 
592
- cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
593
- agent_count = 0
594
-
595
- if cache_dir.exists():
596
- # BUGFIX (cache-count-inflation): Clean up stale cache files
597
- # from old repositories before counting to prevent inflated counts.
598
- # Issue: Old caches like bobmatnyc/claude-mpm-agents/agents/
599
- # were counted alongside current agents, inflating count
600
- # from 44 to 85.
601
- #
602
- # Solution: Remove files with nested /agents/ paths
603
- # (e.g., cache/agents/user/repo/agents/...)
604
- # Keep only current agents (e.g., cache/agents/engineer/...)
605
- removed_count = 0
606
- stale_dirs = set()
607
-
608
- for md_file in cache_dir.rglob("*.md"):
609
- # Stale cache files have multiple /agents/ in their path RELATIVE to cache_dir
610
- # Current: cache/agents/bobmatnyc/claude-mpm-agents/agents/engineer/...
611
- # (1 occurrence in relative path: /agents/)
612
- # Old flat: cache/agents/engineer/...
613
- # (0 occurrences in relative path - no repo structure)
614
- # The issue: str(md_file).count("/agents/") counts BOTH cache/agents/ AND repo/agents/
615
- # Fix: Count /agents/ in path RELATIVE to cache_dir (after cache/agents/)
616
- relative_path = str(md_file.relative_to(cache_dir))
617
- if relative_path.count("/agents/") > 1:
618
- # Track parent directory for cleanup
619
- # Extract subdirectory under cache/agents/
620
- # (e.g., "bobmatnyc")
621
- parts = md_file.parts
622
- cache_agents_idx = parts.index("agents")
623
- if cache_agents_idx + 1 < len(parts):
624
- stale_subdir = parts[cache_agents_idx + 1]
625
- # Only remove if it's not a known category directory
626
- if stale_subdir not in [
627
- "engineer",
628
- "ops",
629
- "qa",
630
- "universal",
631
- "documentation",
632
- "claude-mpm",
633
- "security",
634
- ]:
635
- stale_dirs.add(cache_dir / stale_subdir)
636
-
637
- md_file.unlink()
638
- removed_count += 1
639
-
640
- # Remove empty stale directories
641
- for stale_dir in stale_dirs:
642
- if stale_dir.exists() and stale_dir.is_dir():
643
- try:
644
- # Remove directory and all contents
645
- import shutil
646
-
647
- shutil.rmtree(stale_dir)
648
- except Exception:
649
- pass # Ignore cleanup errors
650
-
651
- if removed_count > 0:
652
- from loguru import logger
653
-
654
- logger.info(
655
- f"Cleaned up {removed_count} stale cache files "
656
- f"from old repositories"
657
- )
554
+ from ..core.unified_config import UnifiedConfig
555
+ from ..services.agents.deployment.startup_reconciliation import (
556
+ perform_startup_reconciliation,
557
+ )
658
558
 
659
- # Count MD files in cache (agent markdown files from
660
- # current repos)
661
- # BUGFIX: Only count files in agent directories,
662
- # not docs/templates/READMEs
663
- # Valid agent paths must contain "/agents/" exactly ONCE
664
- # (current structure)
665
- # Exclude PM templates, BASE-AGENT, and documentation files
666
- pm_templates = {
667
- "base-agent.md",
668
- "circuit_breakers.md",
669
- "pm_examples.md",
670
- "pm_red_flags.md",
671
- "research_gate_examples.md",
672
- "response_format.md",
673
- "ticket_completeness_examples.md",
674
- "validation_templates.md",
675
- "git_file_tracking.md",
676
- }
677
- # Documentation files to exclude (by filename)
678
- doc_files = {
679
- "readme.md",
680
- "changelog.md",
681
- "contributing.md",
682
- "implementation-summary.md",
683
- "reorganization-plan.md",
684
- "auto-deploy-index.md",
685
- }
686
-
687
- # Find all markdown files (after cleanup)
688
- all_md_files = list(cache_dir.rglob("*.md"))
689
-
690
- # Filter to only agent files:
691
- # 1. Must have "/agents/" in path (current structure supports
692
- # both flat and {owner}/{repo}/agents/ patterns)
693
- # 2. Must not be in PM templates or doc files
694
- # 3. Exclude BASE-AGENT.md which is not a deployable agent
695
- # 4. Exclude build artifacts (dist/, build/, .cache/)
696
- # to prevent double-counting
697
- agent_files = [
698
- f
699
- for f in all_md_files
700
- if
701
- (
702
- # Must be in an agent directory
703
- # Supports: cache/agents/{category}/... (flat)
704
- # Supports: cache/agents/{owner}/{repo}/agents/{category}/... (GitHub sync)
705
- "/agents/" in str(f)
706
- # Exclude PM templates, doc files, and BASE-AGENT
707
- and f.name.lower() not in pm_templates
708
- and f.name.lower() not in doc_files
709
- and f.name.lower() != "base-agent.md"
710
- # Exclude build artifacts (prevents double-counting
711
- # source + built files)
712
- and not any(
713
- part in str(f).split("/")
714
- for part in ["dist", "build", ".cache"]
715
- )
716
- )
717
- ]
718
- agent_count = len(agent_files)
719
-
720
- if agent_count > 0:
721
- # Deploy agents to project-level directory where Claude Code expects them
722
- deploy_target = Path.cwd() / ".claude" / "agents"
723
- deployment_result = deployment_service.deploy_agents(
724
- target_dir=deploy_target,
725
- force_rebuild=False, # Only deploy if versions differ
726
- deployment_mode="update", # Version-aware updates
727
- config=deploy_config, # Pass config to respect profile filtering
559
+ # Load configuration
560
+ unified_config = UnifiedConfig()
561
+
562
+ # Override with profile settings if active
563
+ if active_profile and profile_manager.active_profile:
564
+ # Get enabled agents from profile (returns Set[str])
565
+ profile_enabled_agents = (
566
+ profile_manager.active_profile.get_enabled_agents()
567
+ )
568
+ # Update config with profile's enabled list (convert Set to List)
569
+ unified_config.agents.enabled = list(profile_enabled_agents)
570
+ logger.info(
571
+ f"Profile '{active_profile}': Using {len(profile_enabled_agents)} enabled agents"
728
572
  )
729
573
 
730
- # Get actual counts from deployment result (reflects configured agents)
731
- deployed = len(deployment_result.get("deployed", []))
732
- updated = len(deployment_result.get("updated", []))
733
- skipped = len(deployment_result.get("skipped", []))
734
- total_configured = deployed + updated + skipped
735
-
736
- # FALLBACK: If deployment result doesn't track skipped agents (async path),
737
- # count existing agents in target directory as "already deployed"
738
- # This ensures accurate reporting when agents are already up-to-date
739
- if total_configured == 0 and deploy_target.exists():
740
- existing_agents = list(deploy_target.glob("*.md"))
741
- # Filter out non-agent files (e.g., README.md, INSTRUCTIONS.md)
742
- agent_count_in_target = len(
743
- [
744
- f
745
- for f in existing_agents
746
- if not f.name.startswith(("README", "INSTRUCTIONS"))
747
- ]
748
- )
749
- if agent_count_in_target > 0:
750
- # All agents already deployed - count them as skipped
751
- skipped = agent_count_in_target
752
- total_configured = agent_count_in_target
574
+ # Perform reconciliation to deploy configured agents
575
+ project_path = Path.cwd()
576
+ agent_result, _skill_result = perform_startup_reconciliation(
577
+ project_path=project_path, config=unified_config, silent=False
578
+ )
579
+
580
+ # Display results with progress bar
581
+ total_operations = (
582
+ len(agent_result.deployed)
583
+ + len(agent_result.removed)
584
+ + len(agent_result.unchanged)
585
+ )
753
586
 
754
- # Create progress bar with actual configured agent count (not raw file count)
587
+ if total_operations > 0:
755
588
  deploy_progress = ProgressBar(
756
- total=total_configured if total_configured > 0 else 1,
589
+ total=total_operations,
757
590
  prefix="Deploying agents",
758
591
  show_percentage=True,
759
592
  show_counter=True,
760
593
  )
761
-
762
- # Update progress bar to completion
763
- deploy_progress.update(
764
- total_configured if total_configured > 0 else 1
765
- )
766
-
767
- # Cleanup orphaned agents (ours but no longer deployed)
768
- # Get list of deployed agent filenames (what should remain)
769
- deployed_filenames = []
770
- for agent_name in deployment_result.get("deployed", []):
771
- deployed_filenames.append(f"{agent_name}.md")
772
- for agent_name in deployment_result.get("updated", []):
773
- deployed_filenames.append(f"{agent_name}.md")
774
- for agent_name in deployment_result.get("skipped", []):
775
- deployed_filenames.append(f"{agent_name}.md")
776
-
777
- # Run cleanup and get count of removed agents
778
- removed = _cleanup_orphaned_agents(
779
- deploy_target, deployed_filenames
594
+ deploy_progress.update(total_operations)
595
+
596
+ # Build summary message
597
+ deployed = len(agent_result.deployed)
598
+ removed = len(agent_result.removed)
599
+ unchanged = len(agent_result.unchanged)
600
+
601
+ summary_parts = []
602
+ if deployed > 0:
603
+ summary_parts.append(f"{deployed} new")
604
+ if removed > 0:
605
+ summary_parts.append(f"{removed} removed")
606
+ if unchanged > 0:
607
+ summary_parts.append(f"{unchanged} unchanged")
608
+
609
+ summary = f"Complete: {', '.join(summary_parts)}"
610
+ deploy_progress.finish(summary)
611
+
612
+ # Display errors if any
613
+ if agent_result.errors:
614
+ logger.warning(
615
+ f"Agent deployment completed with {len(agent_result.errors)} errors"
780
616
  )
617
+ print("\n⚠️ Agent Deployment Errors:")
618
+ max_errors_to_show = 10
619
+ errors_to_display = agent_result.errors[:max_errors_to_show]
781
620
 
782
- # Show total configured agents (deployed + updated + already existing)
783
- # Include cache count for context and removed count if any
784
- if deployed > 0 or updated > 0:
785
- if removed > 0:
786
- deploy_progress.finish(
787
- f"Complete: {deployed} new, {updated} updated, {skipped} unchanged, "
788
- f"{removed} removed ({total_configured} configured from {agent_count} files in cache)"
789
- )
790
- else:
791
- deploy_progress.finish(
792
- f"Complete: {deployed} new, {updated} updated, {skipped} unchanged "
793
- f"({total_configured} configured from {agent_count} files in cache)"
794
- )
795
- elif removed > 0:
796
- deploy_progress.finish(
797
- f"Complete: {total_configured} agents deployed, "
798
- f"{removed} removed ({agent_count} files in cache)"
799
- )
800
- else:
801
- deploy_progress.finish(
802
- f"Complete: {total_configured} agents deployed "
803
- f"({agent_count} files in cache)"
804
- )
805
-
806
- # Display deployment errors to user (not just logs)
807
- deploy_errors = deployment_result.get("errors", [])
808
- if deploy_errors:
809
- # Log for debugging
810
- logger.warning(
811
- f"Agent deployment completed with {len(deploy_errors)} errors: {deploy_errors}"
812
- )
621
+ for error in errors_to_display:
622
+ print(f" - {error}")
813
623
 
814
- # Display errors to user with clear formatting
815
- print("\n⚠️ Agent Deployment Errors:")
624
+ if len(agent_result.errors) > max_errors_to_show:
625
+ remaining = len(agent_result.errors) - max_errors_to_show
626
+ print(f" ... and {remaining} more error(s)")
816
627
 
817
- # Show first 10 errors to avoid overwhelming output
818
- max_errors_to_show = 10
819
- errors_to_display = deploy_errors[:max_errors_to_show]
820
-
821
- for error in errors_to_display:
822
- # Format error message for readability
823
- # Errors typically come as strings like "agent.md: Error message"
824
- print(f" - {error}")
825
-
826
- # If more errors exist, show count
827
- if len(deploy_errors) > max_errors_to_show:
828
- remaining = len(deploy_errors) - max_errors_to_show
829
- print(f" ... and {remaining} more error(s)")
830
-
831
- # Show summary message
832
- print(
833
- f"\n❌ Failed to deploy {len(deploy_errors)} agent(s). Please check the error messages above."
834
- )
835
- print(" Run with --verbose for detailed error information.\n")
628
+ print(
629
+ f"\n❌ Failed to deploy {len(agent_result.errors)} agent(s). "
630
+ "Please check the error messages above."
631
+ )
632
+ print(" Run with --verbose for detailed error information.\n")
836
633
 
837
634
  except Exception as e:
838
635
  # Deployment failure shouldn't block startup
@@ -857,11 +654,11 @@ def sync_remote_agents_on_startup():
857
654
  # Cleanup legacy cache even if sync failed
858
655
  try:
859
656
  cleanup_legacy_agent_cache()
860
- except Exception:
657
+ except Exception: # nosec B110
861
658
  pass # Ignore cleanup errors
862
659
 
863
660
 
864
- def sync_remote_skills_on_startup():
661
+ def sync_remote_skills_on_startup(force_sync: bool = False):
865
662
  """
866
663
  Synchronize skill templates from remote sources on startup.
867
664
 
@@ -879,6 +676,9 @@ def sync_remote_skills_on_startup():
879
676
  4. Apply profile filtering if active
880
677
  5. Deploy resolved skills to ~/.claude/skills/ - Phase 2 progress bar
881
678
  6. Log deployment results with source indication
679
+
680
+ Args:
681
+ force_sync: Force download even if cache is fresh (bypasses ETag).
882
682
  """
883
683
  try:
884
684
  from pathlib import Path
@@ -984,7 +784,7 @@ def sync_remote_skills_on_startup():
984
784
 
985
785
  # Sync all sources with progress callback
986
786
  results = manager.sync_all_sources(
987
- force=False, progress_callback=sync_progress.update
787
+ force=force_sync, progress_callback=sync_progress.update
988
788
  )
989
789
 
990
790
  # Finish sync progress bar with clear breakdown
@@ -1093,7 +893,7 @@ def sync_remote_skills_on_startup():
1093
893
  # Deploy to project-local directory with cleanup
1094
894
  deployment_result = manager.deploy_skills(
1095
895
  target_dir=Path.cwd() / ".claude" / "skills",
1096
- force=False,
896
+ force=force_sync,
1097
897
  # CRITICAL FIX: Empty list should mean "deploy no skills", not "deploy all"
1098
898
  # When skills_to_deploy is [], we want skill_filter=set() NOT skill_filter=None
1099
899
  # None means "no filtering" (deploy all), empty set means "filter to nothing"
@@ -1358,33 +1158,70 @@ def show_skill_summary():
1358
1158
 
1359
1159
 
1360
1160
  def verify_and_show_pm_skills():
1361
- """Verify PM skills and display status.
1161
+ """Verify PM skills and display status with enhanced validation.
1162
+
1163
+ WHY: PM skills are CRITICAL for PM agent operation. PM must KNOW if
1164
+ framework knowledge is unavailable at startup. Enhanced validation
1165
+ checks all required skills exist, are not corrupted, and auto-repairs
1166
+ if needed.
1362
1167
 
1363
- WHY: PM skills are essential for PM agent operation.
1364
- Shows deployment status and auto-deploys if missing.
1168
+ Shows deployment status:
1169
+ - "✓ PM skills: 8/8 verified" if all required skills are valid
1170
+ - "⚠ PM skills: 2 missing, auto-repairing..." if issues detected
1171
+ - Non-blocking but visible warning if auto-repair fails
1365
1172
  """
1366
1173
  try:
1367
1174
  from pathlib import Path
1368
1175
 
1369
- from ..services.pm_skills_deployer import PMSkillsDeployerService
1176
+ from ..services.pm_skills_deployer import (
1177
+ REQUIRED_PM_SKILLS,
1178
+ PMSkillsDeployerService,
1179
+ )
1370
1180
 
1371
1181
  deployer = PMSkillsDeployerService()
1372
1182
  project_dir = Path.cwd()
1373
1183
 
1374
- result = deployer.verify_pm_skills(project_dir)
1184
+ # Verify with auto-repair enabled
1185
+ result = deployer.verify_pm_skills(project_dir, auto_repair=True)
1375
1186
 
1376
1187
  if result.verified:
1377
- # Show verified status
1378
- print(f"✓ PM skills: {result.skill_count} verified", flush=True)
1188
+ # Show verified status with count
1189
+ total_required = len(REQUIRED_PM_SKILLS)
1190
+ print(
1191
+ f"✓ PM skills: {total_required}/{total_required} verified", flush=True
1192
+ )
1379
1193
  else:
1380
- # Auto-deploy if missing
1381
- print("Deploying PM skills...", end="", flush=True)
1382
- deploy_result = deployer.deploy_pm_skills(project_dir)
1383
- if deploy_result.success:
1384
- total = len(deploy_result.deployed) + len(deploy_result.skipped)
1385
- print(f"\r✓ PM skills: {total} deployed" + " " * 20, flush=True)
1194
+ # Show warning with details
1195
+ missing_count = len(result.missing_skills)
1196
+ corrupted_count = len(result.corrupted_skills)
1197
+
1198
+ # Build status message
1199
+ issues = []
1200
+ if missing_count > 0:
1201
+ issues.append(f"{missing_count} missing")
1202
+ if corrupted_count > 0:
1203
+ issues.append(f"{corrupted_count} corrupted")
1204
+
1205
+ status = ", ".join(issues)
1206
+
1207
+ # Check if auto-repair was attempted
1208
+ if "Auto-repaired" in result.message:
1209
+ # Auto-repair succeeded
1210
+ total_required = len(REQUIRED_PM_SKILLS)
1211
+ print(
1212
+ f"✓ PM skills: {total_required}/{total_required} verified (auto-repaired)",
1213
+ flush=True,
1214
+ )
1386
1215
  else:
1387
- print("\r⚠ PM skills: deployment failed" + " " * 20, flush=True)
1216
+ # Auto-repair failed or not attempted
1217
+ print(f"⚠ PM skills: {status}", flush=True)
1218
+
1219
+ # Log warnings for debugging
1220
+ from ..core.logger import get_logger
1221
+
1222
+ logger = get_logger("cli")
1223
+ for warning in result.warnings:
1224
+ logger.warning(f"PM skills: {warning}")
1388
1225
 
1389
1226
  except ImportError:
1390
1227
  # PM skills deployer not available - skip silently
@@ -1419,7 +1256,7 @@ def auto_install_chrome_devtools_on_startup():
1419
1256
  if not chrome_devtools_config.get("auto_install", True):
1420
1257
  # Auto-install disabled, skip silently
1421
1258
  return
1422
- except Exception:
1259
+ except Exception: # nosec B110
1423
1260
  # If config loading fails, assume auto-install is enabled (default)
1424
1261
  pass
1425
1262
 
@@ -1437,7 +1274,7 @@ def auto_install_chrome_devtools_on_startup():
1437
1274
  # Continue execution - chrome-devtools installation failure shouldn't block startup
1438
1275
 
1439
1276
 
1440
- def run_background_services():
1277
+ def run_background_services(force_sync: bool = False):
1441
1278
  """
1442
1279
  Initialize all background services on startup.
1443
1280
 
@@ -1448,6 +1285,9 @@ def run_background_services():
1448
1285
  explicitly requests them via agent-manager commands. This prevents unwanted
1449
1286
  file creation in project .claude/ directories.
1450
1287
  See: SystemInstructionsDeployer and agent_deployment.py line 504-509
1288
+
1289
+ Args:
1290
+ force_sync: Force download even if cache is fresh (bypasses ETag).
1451
1291
  """
1452
1292
  # Sync hooks early to ensure up-to-date configuration
1453
1293
  # RATIONALE: Hooks should be synced before other services to fix stale configs
@@ -1458,7 +1298,9 @@ def run_background_services():
1458
1298
  check_mcp_auto_configuration()
1459
1299
  verify_mcp_gateway_startup()
1460
1300
  check_for_updates_async()
1461
- sync_remote_agents_on_startup() # Sync agents from remote sources
1301
+ sync_remote_agents_on_startup(
1302
+ force_sync=force_sync
1303
+ ) # Sync agents from remote sources
1462
1304
  show_agent_summary() # Display agent counts after deployment
1463
1305
 
1464
1306
  # Skills deployment order (precedence: remote > bundled)
@@ -1467,7 +1309,9 @@ def run_background_services():
1467
1309
  # 3. Discover and link runtime skills (user-added skills)
1468
1310
  # This ensures remote skills take precedence over bundled skills when names conflict
1469
1311
  deploy_bundled_skills() # Base layer: package-bundled skills
1470
- sync_remote_skills_on_startup() # Override layer: Git-based skills (takes precedence)
1312
+ sync_remote_skills_on_startup(
1313
+ force_sync=force_sync
1314
+ ) # Override layer: Git-based skills (takes precedence)
1471
1315
  discover_and_link_runtime_skills() # Discovery: user-added skills
1472
1316
  show_skill_summary() # Display skill counts after deployment
1473
1317
  verify_and_show_pm_skills() # PM skills verification and status
@@ -1667,7 +1511,7 @@ def verify_mcp_gateway_startup():
1667
1511
  loop.run_until_complete(
1668
1512
  asyncio.gather(*pending, return_exceptions=True)
1669
1513
  )
1670
- except Exception:
1514
+ except Exception: # nosec B110
1671
1515
  pass # Ignore cleanup errors
1672
1516
  finally:
1673
1517
  loop.close()
@@ -1761,7 +1605,7 @@ def check_for_updates_async():
1761
1605
 
1762
1606
  logger = get_logger("upgrade_check")
1763
1607
  logger.debug(f"Update check failed (non-critical): {e}")
1764
- except Exception:
1608
+ except Exception: # nosec B110
1765
1609
  pass # Avoid any errors in error handling
1766
1610
  finally:
1767
1611
  # Properly clean up event loop
@@ -1776,7 +1620,7 @@ def check_for_updates_async():
1776
1620
  loop.run_until_complete(
1777
1621
  asyncio.gather(*pending, return_exceptions=True)
1778
1622
  )
1779
- except Exception:
1623
+ except Exception: # nosec B110
1780
1624
  pass # Ignore cleanup errors
1781
1625
  finally:
1782
1626
  loop.close()