claude-mpm 5.4.71__py3-none-any.whl → 5.6.3__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 (295) 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_RESEARCH_OUTPUT_STYLE.md +413 -0
  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/agents.py +2 -4
  8. claude_mpm/cli/commands/agents_reconcile.py +197 -0
  9. claude_mpm/cli/commands/autotodos.py +566 -0
  10. claude_mpm/cli/commands/commander.py +46 -0
  11. claude_mpm/cli/commands/configure.py +620 -21
  12. claude_mpm/cli/commands/hook_errors.py +60 -60
  13. claude_mpm/cli/commands/monitor.py +2 -2
  14. claude_mpm/cli/commands/mpm_init/core.py +2 -2
  15. claude_mpm/cli/commands/run.py +35 -3
  16. claude_mpm/cli/commands/skills.py +166 -14
  17. claude_mpm/cli/executor.py +119 -16
  18. claude_mpm/cli/interactive/__init__.py +10 -0
  19. claude_mpm/cli/interactive/agent_wizard.py +30 -50
  20. claude_mpm/cli/interactive/questionary_styles.py +65 -0
  21. claude_mpm/cli/interactive/skill_selector.py +481 -0
  22. claude_mpm/cli/parsers/base_parser.py +71 -1
  23. claude_mpm/cli/parsers/commander_parser.py +83 -0
  24. claude_mpm/cli/parsers/run_parser.py +10 -0
  25. claude_mpm/cli/startup.py +183 -348
  26. claude_mpm/cli/startup_display.py +72 -5
  27. claude_mpm/cli/startup_logging.py +2 -2
  28. claude_mpm/cli/utils.py +7 -3
  29. claude_mpm/commander/__init__.py +72 -0
  30. claude_mpm/commander/adapters/__init__.py +31 -0
  31. claude_mpm/commander/adapters/base.py +191 -0
  32. claude_mpm/commander/adapters/claude_code.py +361 -0
  33. claude_mpm/commander/adapters/communication.py +366 -0
  34. claude_mpm/commander/api/__init__.py +16 -0
  35. claude_mpm/commander/api/app.py +105 -0
  36. claude_mpm/commander/api/errors.py +112 -0
  37. claude_mpm/commander/api/routes/__init__.py +8 -0
  38. claude_mpm/commander/api/routes/events.py +184 -0
  39. claude_mpm/commander/api/routes/inbox.py +171 -0
  40. claude_mpm/commander/api/routes/messages.py +148 -0
  41. claude_mpm/commander/api/routes/projects.py +271 -0
  42. claude_mpm/commander/api/routes/sessions.py +215 -0
  43. claude_mpm/commander/api/routes/work.py +260 -0
  44. claude_mpm/commander/api/schemas.py +182 -0
  45. claude_mpm/commander/chat/__init__.py +7 -0
  46. claude_mpm/commander/chat/cli.py +107 -0
  47. claude_mpm/commander/chat/commands.py +96 -0
  48. claude_mpm/commander/chat/repl.py +310 -0
  49. claude_mpm/commander/config.py +49 -0
  50. claude_mpm/commander/config_loader.py +115 -0
  51. claude_mpm/commander/daemon.py +398 -0
  52. claude_mpm/commander/events/__init__.py +26 -0
  53. claude_mpm/commander/events/manager.py +332 -0
  54. claude_mpm/commander/frameworks/__init__.py +12 -0
  55. claude_mpm/commander/frameworks/base.py +143 -0
  56. claude_mpm/commander/frameworks/claude_code.py +58 -0
  57. claude_mpm/commander/frameworks/mpm.py +62 -0
  58. claude_mpm/commander/inbox/__init__.py +16 -0
  59. claude_mpm/commander/inbox/dedup.py +128 -0
  60. claude_mpm/commander/inbox/inbox.py +224 -0
  61. claude_mpm/commander/inbox/models.py +70 -0
  62. claude_mpm/commander/instance_manager.py +337 -0
  63. claude_mpm/commander/llm/__init__.py +6 -0
  64. claude_mpm/commander/llm/openrouter_client.py +167 -0
  65. claude_mpm/commander/llm/summarizer.py +70 -0
  66. claude_mpm/commander/models/__init__.py +18 -0
  67. claude_mpm/commander/models/events.py +121 -0
  68. claude_mpm/commander/models/project.py +162 -0
  69. claude_mpm/commander/models/work.py +214 -0
  70. claude_mpm/commander/parsing/__init__.py +20 -0
  71. claude_mpm/commander/parsing/extractor.py +132 -0
  72. claude_mpm/commander/parsing/output_parser.py +270 -0
  73. claude_mpm/commander/parsing/patterns.py +100 -0
  74. claude_mpm/commander/persistence/__init__.py +11 -0
  75. claude_mpm/commander/persistence/event_store.py +274 -0
  76. claude_mpm/commander/persistence/state_store.py +309 -0
  77. claude_mpm/commander/persistence/work_store.py +164 -0
  78. claude_mpm/commander/polling/__init__.py +13 -0
  79. claude_mpm/commander/polling/event_detector.py +104 -0
  80. claude_mpm/commander/polling/output_buffer.py +49 -0
  81. claude_mpm/commander/polling/output_poller.py +153 -0
  82. claude_mpm/commander/project_session.py +268 -0
  83. claude_mpm/commander/proxy/__init__.py +12 -0
  84. claude_mpm/commander/proxy/formatter.py +89 -0
  85. claude_mpm/commander/proxy/output_handler.py +191 -0
  86. claude_mpm/commander/proxy/relay.py +155 -0
  87. claude_mpm/commander/registry.py +404 -0
  88. claude_mpm/commander/runtime/__init__.py +10 -0
  89. claude_mpm/commander/runtime/executor.py +191 -0
  90. claude_mpm/commander/runtime/monitor.py +316 -0
  91. claude_mpm/commander/session/__init__.py +6 -0
  92. claude_mpm/commander/session/context.py +81 -0
  93. claude_mpm/commander/session/manager.py +59 -0
  94. claude_mpm/commander/tmux_orchestrator.py +361 -0
  95. claude_mpm/commander/web/__init__.py +1 -0
  96. claude_mpm/commander/work/__init__.py +30 -0
  97. claude_mpm/commander/work/executor.py +189 -0
  98. claude_mpm/commander/work/queue.py +405 -0
  99. claude_mpm/commander/workflow/__init__.py +27 -0
  100. claude_mpm/commander/workflow/event_handler.py +219 -0
  101. claude_mpm/commander/workflow/notifier.py +146 -0
  102. claude_mpm/commands/mpm-config.md +8 -0
  103. claude_mpm/commands/mpm-doctor.md +8 -0
  104. claude_mpm/commands/mpm-help.md +8 -0
  105. claude_mpm/commands/mpm-init.md +8 -0
  106. claude_mpm/commands/mpm-monitor.md +8 -0
  107. claude_mpm/commands/mpm-organize.md +8 -0
  108. claude_mpm/commands/mpm-postmortem.md +8 -0
  109. claude_mpm/commands/mpm-session-resume.md +9 -1
  110. claude_mpm/commands/mpm-status.md +8 -0
  111. claude_mpm/commands/mpm-ticket-view.md +8 -0
  112. claude_mpm/commands/mpm-version.md +8 -0
  113. claude_mpm/commands/mpm.md +8 -0
  114. claude_mpm/config/agent_presets.py +8 -7
  115. claude_mpm/constants.py +1 -0
  116. claude_mpm/core/claude_runner.py +2 -2
  117. claude_mpm/core/config.py +5 -0
  118. claude_mpm/core/hook_manager.py +51 -3
  119. claude_mpm/core/interactive_session.py +7 -7
  120. claude_mpm/core/logger.py +26 -9
  121. claude_mpm/core/logging_utils.py +35 -11
  122. claude_mpm/core/output_style_manager.py +27 -9
  123. claude_mpm/core/unified_config.py +54 -8
  124. claude_mpm/core/unified_paths.py +95 -90
  125. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  126. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  127. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
  128. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
  129. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
  130. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
  131. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
  132. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
  133. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
  134. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
  135. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
  136. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
  137. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
  138. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
  139. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
  140. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  141. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  142. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
  143. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
  144. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
  145. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
  146. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
  147. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
  148. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
  149. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
  150. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  151. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
  152. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
  153. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
  154. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
  155. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
  156. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
  157. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
  158. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
  159. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
  160. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
  161. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
  162. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
  163. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
  164. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
  165. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
  166. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
  167. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
  168. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
  169. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  170. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
  171. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
  172. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
  173. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  174. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  182. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  183. claude_mpm/dashboard/static/svelte-build/index.html +9 -9
  184. claude_mpm/experimental/cli_enhancements.py +2 -1
  185. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  186. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  187. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +486 -0
  188. claude_mpm/hooks/claude_hooks/event_handlers.py +254 -11
  189. claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
  190. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  191. claude_mpm/hooks/claude_hooks/installer.py +75 -8
  192. claude_mpm/hooks/claude_hooks/memory_integration.py +22 -11
  193. claude_mpm/hooks/claude_hooks/response_tracking.py +3 -1
  194. claude_mpm/hooks/claude_hooks/services/connection_manager.py +20 -0
  195. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +14 -77
  196. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +30 -6
  197. claude_mpm/hooks/session_resume_hook.py +85 -1
  198. claude_mpm/init.py +1 -1
  199. claude_mpm/scripts/claude-hook-handler.sh +39 -12
  200. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  201. claude_mpm/services/agents/cache_git_manager.py +1 -1
  202. claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
  203. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
  204. claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
  205. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  206. claude_mpm/services/cli/__init__.py +3 -0
  207. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  208. claude_mpm/services/cli/session_resume_helper.py +10 -2
  209. claude_mpm/services/delegation_detector.py +175 -0
  210. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  211. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  212. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  213. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  214. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  215. claude_mpm/services/diagnostics/models.py +14 -1
  216. claude_mpm/services/event_log.py +325 -0
  217. claude_mpm/services/infrastructure/__init__.py +4 -0
  218. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  219. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  220. claude_mpm/services/monitor/daemon_manager.py +15 -4
  221. claude_mpm/services/monitor/management/lifecycle.py +8 -2
  222. claude_mpm/services/monitor/server.py +106 -16
  223. claude_mpm/services/pm_skills_deployer.py +259 -87
  224. claude_mpm/services/skills/git_skill_source_manager.py +56 -3
  225. claude_mpm/services/skills/selective_skill_deployer.py +114 -26
  226. claude_mpm/services/skills/skill_discovery_service.py +57 -3
  227. claude_mpm/services/socketio/handlers/hook.py +14 -7
  228. claude_mpm/services/socketio/server/main.py +12 -4
  229. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  230. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  231. claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
  232. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  233. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  234. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  235. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  236. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  237. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  238. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  239. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  240. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  241. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  242. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  243. claude_mpm/skills/bundled/pm/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
  244. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  245. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  246. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  247. claude_mpm/skills/skill_manager.py +4 -4
  248. claude_mpm/utils/agent_dependency_loader.py +4 -2
  249. claude_mpm/utils/robust_installer.py +10 -6
  250. claude_mpm-5.6.3.dist-info/METADATA +391 -0
  251. {claude_mpm-5.4.71.dist-info → claude_mpm-5.6.3.dist-info}/RECORD +261 -179
  252. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
  253. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
  254. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
  255. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
  256. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
  257. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
  258. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
  259. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
  260. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
  261. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  262. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc +0 -0
  263. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  264. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  265. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
  266. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  267. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
  268. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  269. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  270. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
  271. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  272. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
  273. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  274. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
  275. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  276. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
  277. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  278. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
  279. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  280. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
  281. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  282. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
  283. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  284. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
  285. claude_mpm-5.4.71.dist-info/METADATA +0 -1005
  286. /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
  287. /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
  288. /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
  289. /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
  290. /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
  291. {claude_mpm-5.4.71.dist-info → claude_mpm-5.6.3.dist-info}/WHEEL +0 -0
  292. {claude_mpm-5.4.71.dist-info → claude_mpm-5.6.3.dist-info}/entry_points.txt +0 -0
  293. {claude_mpm-5.4.71.dist-info → claude_mpm-5.6.3.dist-info}/licenses/LICENSE +0 -0
  294. {claude_mpm-5.4.71.dist-info → claude_mpm-5.6.3.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  295. {claude_mpm-5.4.71.dist-info → claude_mpm-5.6.3.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
 
@@ -312,64 +331,56 @@ def deploy_output_style_on_startup():
312
331
  directory (~/.claude/output-styles/) which is the official Claude Code location
313
332
  for custom output styles.
314
333
 
315
- Deploys two styles:
334
+ Deploys all styles:
316
335
  - claude-mpm.md (professional mode)
317
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/output-styles/
330
- user_home = Path.home()
331
- output_styles_dir = user_home / ".claude" / "output-styles"
332
- professional_target = output_styles_dir / "claude-mpm.md"
333
- teacher_target = output_styles_dir / "claude-mpm-teacher.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
@@ -489,7 +500,6 @@ def sync_remote_agents_on_startup(force_sync: bool = False):
489
500
  from pathlib import Path
490
501
 
491
502
  from ..core.shared.config_loader import ConfigLoader
492
- from ..services.agents.deployment.agent_deployment import AgentDeploymentService
493
503
  from ..services.agents.startup_sync import sync_agents_on_startup
494
504
  from ..services.profile_manager import ProfileManager
495
505
  from ..utils.progress import ProgressBar
@@ -537,305 +547,89 @@ def sync_remote_agents_on_startup(force_sync: bool = False):
537
547
  logger.warning(f"Agent sync completed with {len(errors)} errors")
538
548
 
539
549
  # Phase 2: Deploy agents from cache to ~/.claude/agents/
540
- # This mirrors the skills deployment pattern (lines 371-407)
550
+ # Use reconciliation service to respect configuration.yaml settings
541
551
  try:
542
- # Initialize deployment service with profile-filtered configuration
543
- from ..core.config import Config
544
-
545
- deploy_config = None
546
- if active_profile and profile_manager.active_profile:
547
- # Create config with excluded agents based on profile
548
- # Get all agents that should be excluded (not in enabled list)
549
- from pathlib import Path
550
-
551
- cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
552
- if cache_dir.exists():
553
- # Find all agent files
554
- # Supports both flat cache and {owner}/{repo}/agents/ structure
555
- all_agent_files = [
556
- f
557
- for f in cache_dir.rglob("*.md")
558
- if "/agents/" in str(f)
559
- and f.stem.lower() != "base-agent"
560
- and f.name.lower()
561
- not in {"readme.md", "changelog.md", "contributing.md"}
562
- ]
563
-
564
- # Build exclusion list for agents not in profile
565
- excluded_agents = []
566
- for agent_file in all_agent_files:
567
- agent_name = agent_file.stem
568
- if not profile_manager.is_agent_enabled(agent_name):
569
- excluded_agents.append(agent_name)
570
-
571
- if excluded_agents:
572
- # Get singleton config and update with profile settings
573
- # BUGFIX: Config is a singleton that ignores dict parameter if already initialized.
574
- # Creating Config({...}) doesn't store excluded_agents - use set() instead.
575
- deploy_config = Config()
576
- deploy_config.set(
577
- "agent_deployment.excluded_agents", excluded_agents
578
- )
579
- deploy_config.set(
580
- "agent_deployment.filter_non_mpm_agents", False
581
- )
582
- deploy_config.set("agent_deployment.case_sensitive", False)
583
- deploy_config.set(
584
- "agent_deployment.exclude_dependencies", False
585
- )
586
- logger.info(
587
- f"Profile '{active_profile}': Excluding {len(excluded_agents)} agents from deployment"
588
- )
589
-
590
- deployment_service = AgentDeploymentService(config=deploy_config)
591
-
592
- # Count agents in cache to show accurate progress
593
552
  from pathlib import Path
594
553
 
595
- cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
596
- agent_count = 0
597
-
598
- if cache_dir.exists():
599
- # BUGFIX (cache-count-inflation): Clean up stale cache files
600
- # from old repositories before counting to prevent inflated counts.
601
- # Issue: Old caches like bobmatnyc/claude-mpm-agents/agents/
602
- # were counted alongside current agents, inflating count
603
- # from 44 to 85.
604
- #
605
- # Solution: Remove files with nested /agents/ paths
606
- # (e.g., cache/agents/user/repo/agents/...)
607
- # Keep only current agents (e.g., cache/agents/engineer/...)
608
- removed_count = 0
609
- stale_dirs = set()
610
-
611
- for md_file in cache_dir.rglob("*.md"):
612
- # Stale cache files have multiple /agents/ in their path RELATIVE to cache_dir
613
- # Current: cache/agents/bobmatnyc/claude-mpm-agents/agents/engineer/...
614
- # (1 occurrence in relative path: /agents/)
615
- # Old flat: cache/agents/engineer/...
616
- # (0 occurrences in relative path - no repo structure)
617
- # The issue: str(md_file).count("/agents/") counts BOTH cache/agents/ AND repo/agents/
618
- # Fix: Count /agents/ in path RELATIVE to cache_dir (after cache/agents/)
619
- relative_path = str(md_file.relative_to(cache_dir))
620
- if relative_path.count("/agents/") > 1:
621
- # Track parent directory for cleanup
622
- # Extract subdirectory under cache/agents/
623
- # (e.g., "bobmatnyc")
624
- parts = md_file.parts
625
- cache_agents_idx = parts.index("agents")
626
- if cache_agents_idx + 1 < len(parts):
627
- stale_subdir = parts[cache_agents_idx + 1]
628
- # Only remove if it's not a known category directory
629
- if stale_subdir not in [
630
- "engineer",
631
- "ops",
632
- "qa",
633
- "universal",
634
- "documentation",
635
- "claude-mpm",
636
- "security",
637
- ]:
638
- stale_dirs.add(cache_dir / stale_subdir)
639
-
640
- md_file.unlink()
641
- removed_count += 1
642
-
643
- # Remove empty stale directories
644
- for stale_dir in stale_dirs:
645
- if stale_dir.exists() and stale_dir.is_dir():
646
- try:
647
- # Remove directory and all contents
648
- import shutil
649
-
650
- shutil.rmtree(stale_dir)
651
- except Exception:
652
- pass # Ignore cleanup errors
653
-
654
- if removed_count > 0:
655
- from loguru import logger
656
-
657
- logger.info(
658
- f"Cleaned up {removed_count} stale cache files "
659
- f"from old repositories"
660
- )
554
+ from ..core.unified_config import UnifiedConfig
555
+ from ..services.agents.deployment.startup_reconciliation import (
556
+ perform_startup_reconciliation,
557
+ )
661
558
 
662
- # Count MD files in cache (agent markdown files from
663
- # current repos)
664
- # BUGFIX: Only count files in agent directories,
665
- # not docs/templates/READMEs
666
- # Valid agent paths must contain "/agents/" exactly ONCE
667
- # (current structure)
668
- # Exclude PM templates, BASE-AGENT, and documentation files
669
- pm_templates = {
670
- "base-agent.md",
671
- "circuit_breakers.md",
672
- "pm_examples.md",
673
- "pm_red_flags.md",
674
- "research_gate_examples.md",
675
- "response_format.md",
676
- "ticket_completeness_examples.md",
677
- "validation_templates.md",
678
- "git_file_tracking.md",
679
- }
680
- # Documentation files to exclude (by filename)
681
- doc_files = {
682
- "readme.md",
683
- "changelog.md",
684
- "contributing.md",
685
- "implementation-summary.md",
686
- "reorganization-plan.md",
687
- "auto-deploy-index.md",
688
- }
689
-
690
- # Find all markdown files (after cleanup)
691
- all_md_files = list(cache_dir.rglob("*.md"))
692
-
693
- # Filter to only agent files:
694
- # 1. Must have "/agents/" in path (current structure supports
695
- # both flat and {owner}/{repo}/agents/ patterns)
696
- # 2. Must not be in PM templates or doc files
697
- # 3. Exclude BASE-AGENT.md which is not a deployable agent
698
- # 4. Exclude build artifacts (dist/, build/, .cache/)
699
- # to prevent double-counting
700
- agent_files = [
701
- f
702
- for f in all_md_files
703
- if
704
- (
705
- # Must be in an agent directory
706
- # Supports: cache/agents/{category}/... (flat)
707
- # Supports: cache/agents/{owner}/{repo}/agents/{category}/... (GitHub sync)
708
- "/agents/" in str(f)
709
- # Exclude PM templates, doc files, and BASE-AGENT
710
- and f.name.lower() not in pm_templates
711
- and f.name.lower() not in doc_files
712
- and f.name.lower() != "base-agent.md"
713
- # Exclude build artifacts (prevents double-counting
714
- # source + built files)
715
- and not any(
716
- part in str(f).split("/")
717
- for part in ["dist", "build", ".cache"]
718
- )
719
- )
720
- ]
721
- agent_count = len(agent_files)
722
-
723
- if agent_count > 0:
724
- # Deploy agents to project-level directory where Claude Code expects them
725
- deploy_target = Path.cwd() / ".claude" / "agents"
726
- deployment_result = deployment_service.deploy_agents(
727
- target_dir=deploy_target,
728
- force_rebuild=False, # Only deploy if versions differ
729
- deployment_mode="update", # Version-aware updates
730
- 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"
731
572
  )
732
573
 
733
- # Get actual counts from deployment result (reflects configured agents)
734
- deployed = len(deployment_result.get("deployed", []))
735
- updated = len(deployment_result.get("updated", []))
736
- skipped = len(deployment_result.get("skipped", []))
737
- total_configured = deployed + updated + skipped
738
-
739
- # FALLBACK: If deployment result doesn't track skipped agents (async path),
740
- # count existing agents in target directory as "already deployed"
741
- # This ensures accurate reporting when agents are already up-to-date
742
- if total_configured == 0 and deploy_target.exists():
743
- existing_agents = list(deploy_target.glob("*.md"))
744
- # Filter out non-agent files (e.g., README.md, INSTRUCTIONS.md)
745
- agent_count_in_target = len(
746
- [
747
- f
748
- for f in existing_agents
749
- if not f.name.startswith(("README", "INSTRUCTIONS"))
750
- ]
751
- )
752
- if agent_count_in_target > 0:
753
- # All agents already deployed - count them as skipped
754
- skipped = agent_count_in_target
755
- 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
+ )
756
579
 
757
- # Create progress bar with actual configured agent count (not raw file count)
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
+ )
586
+
587
+ if total_operations > 0:
758
588
  deploy_progress = ProgressBar(
759
- total=total_configured if total_configured > 0 else 1,
589
+ total=total_operations,
760
590
  prefix="Deploying agents",
761
591
  show_percentage=True,
762
592
  show_counter=True,
763
593
  )
764
-
765
- # Update progress bar to completion
766
- deploy_progress.update(
767
- total_configured if total_configured > 0 else 1
768
- )
769
-
770
- # Cleanup orphaned agents (ours but no longer deployed)
771
- # Get list of deployed agent filenames (what should remain)
772
- deployed_filenames = []
773
- for agent_name in deployment_result.get("deployed", []):
774
- deployed_filenames.append(f"{agent_name}.md")
775
- for agent_name in deployment_result.get("updated", []):
776
- deployed_filenames.append(f"{agent_name}.md")
777
- for agent_name in deployment_result.get("skipped", []):
778
- deployed_filenames.append(f"{agent_name}.md")
779
-
780
- # Run cleanup and get count of removed agents
781
- removed = _cleanup_orphaned_agents(
782
- 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"
783
616
  )
617
+ print("\n⚠️ Agent Deployment Errors:")
618
+ max_errors_to_show = 10
619
+ errors_to_display = agent_result.errors[:max_errors_to_show]
784
620
 
785
- # Show total configured agents (deployed + updated + already existing)
786
- # Include cache count for context and removed count if any
787
- if deployed > 0 or updated > 0:
788
- if removed > 0:
789
- deploy_progress.finish(
790
- f"Complete: {deployed} new, {updated} updated, {skipped} unchanged, "
791
- f"{removed} removed ({total_configured} configured from {agent_count} files in cache)"
792
- )
793
- else:
794
- deploy_progress.finish(
795
- f"Complete: {deployed} new, {updated} updated, {skipped} unchanged "
796
- f"({total_configured} configured from {agent_count} files in cache)"
797
- )
798
- elif removed > 0:
799
- deploy_progress.finish(
800
- f"Complete: {total_configured} agents deployed, "
801
- f"{removed} removed ({agent_count} files in cache)"
802
- )
803
- else:
804
- deploy_progress.finish(
805
- f"Complete: {total_configured} agents deployed "
806
- f"({agent_count} files in cache)"
807
- )
808
-
809
- # Display deployment errors to user (not just logs)
810
- deploy_errors = deployment_result.get("errors", [])
811
- if deploy_errors:
812
- # Log for debugging
813
- logger.warning(
814
- f"Agent deployment completed with {len(deploy_errors)} errors: {deploy_errors}"
815
- )
816
-
817
- # Display errors to user with clear formatting
818
- print("\n⚠️ Agent Deployment Errors:")
819
-
820
- # Show first 10 errors to avoid overwhelming output
821
- max_errors_to_show = 10
822
- errors_to_display = deploy_errors[:max_errors_to_show]
823
-
824
- for error in errors_to_display:
825
- # Format error message for readability
826
- # Errors typically come as strings like "agent.md: Error message"
827
- print(f" - {error}")
621
+ for error in errors_to_display:
622
+ print(f" - {error}")
828
623
 
829
- # If more errors exist, show count
830
- if len(deploy_errors) > max_errors_to_show:
831
- remaining = len(deploy_errors) - max_errors_to_show
832
- print(f" ... and {remaining} more error(s)")
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)")
833
627
 
834
- # Show summary message
835
- print(
836
- f"\n❌ Failed to deploy {len(deploy_errors)} agent(s). Please check the error messages above."
837
- )
838
- 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")
839
633
 
840
634
  except Exception as e:
841
635
  # Deployment failure shouldn't block startup
@@ -860,7 +654,7 @@ def sync_remote_agents_on_startup(force_sync: bool = False):
860
654
  # Cleanup legacy cache even if sync failed
861
655
  try:
862
656
  cleanup_legacy_agent_cache()
863
- except Exception:
657
+ except Exception: # nosec B110
864
658
  pass # Ignore cleanup errors
865
659
 
866
660
 
@@ -1364,33 +1158,70 @@ def show_skill_summary():
1364
1158
 
1365
1159
 
1366
1160
  def verify_and_show_pm_skills():
1367
- """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.
1368
1167
 
1369
- WHY: PM skills are essential for PM agent operation.
1370
- 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
1371
1172
  """
1372
1173
  try:
1373
1174
  from pathlib import Path
1374
1175
 
1375
- from ..services.pm_skills_deployer import PMSkillsDeployerService
1176
+ from ..services.pm_skills_deployer import (
1177
+ REQUIRED_PM_SKILLS,
1178
+ PMSkillsDeployerService,
1179
+ )
1376
1180
 
1377
1181
  deployer = PMSkillsDeployerService()
1378
1182
  project_dir = Path.cwd()
1379
1183
 
1380
- result = deployer.verify_pm_skills(project_dir)
1184
+ # Verify with auto-repair enabled
1185
+ result = deployer.verify_pm_skills(project_dir, auto_repair=True)
1381
1186
 
1382
1187
  if result.verified:
1383
- # Show verified status
1384
- 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
+ )
1385
1193
  else:
1386
- # Auto-deploy if missing
1387
- print("Deploying PM skills...", end="", flush=True)
1388
- deploy_result = deployer.deploy_pm_skills(project_dir)
1389
- if deploy_result.success:
1390
- total = len(deploy_result.deployed) + len(deploy_result.skipped)
1391
- 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
+ )
1392
1215
  else:
1393
- 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}")
1394
1225
 
1395
1226
  except ImportError:
1396
1227
  # PM skills deployer not available - skip silently
@@ -1425,7 +1256,7 @@ def auto_install_chrome_devtools_on_startup():
1425
1256
  if not chrome_devtools_config.get("auto_install", True):
1426
1257
  # Auto-install disabled, skip silently
1427
1258
  return
1428
- except Exception:
1259
+ except Exception: # nosec B110
1429
1260
  # If config loading fails, assume auto-install is enabled (default)
1430
1261
  pass
1431
1262
 
@@ -1467,7 +1298,9 @@ def run_background_services(force_sync: bool = False):
1467
1298
  check_mcp_auto_configuration()
1468
1299
  verify_mcp_gateway_startup()
1469
1300
  check_for_updates_async()
1470
- sync_remote_agents_on_startup(force_sync=force_sync) # Sync agents from remote sources
1301
+ sync_remote_agents_on_startup(
1302
+ force_sync=force_sync
1303
+ ) # Sync agents from remote sources
1471
1304
  show_agent_summary() # Display agent counts after deployment
1472
1305
 
1473
1306
  # Skills deployment order (precedence: remote > bundled)
@@ -1476,7 +1309,9 @@ def run_background_services(force_sync: bool = False):
1476
1309
  # 3. Discover and link runtime skills (user-added skills)
1477
1310
  # This ensures remote skills take precedence over bundled skills when names conflict
1478
1311
  deploy_bundled_skills() # Base layer: package-bundled skills
1479
- sync_remote_skills_on_startup(force_sync=force_sync) # 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)
1480
1315
  discover_and_link_runtime_skills() # Discovery: user-added skills
1481
1316
  show_skill_summary() # Display skill counts after deployment
1482
1317
  verify_and_show_pm_skills() # PM skills verification and status
@@ -1676,7 +1511,7 @@ def verify_mcp_gateway_startup():
1676
1511
  loop.run_until_complete(
1677
1512
  asyncio.gather(*pending, return_exceptions=True)
1678
1513
  )
1679
- except Exception:
1514
+ except Exception: # nosec B110
1680
1515
  pass # Ignore cleanup errors
1681
1516
  finally:
1682
1517
  loop.close()
@@ -1770,7 +1605,7 @@ def check_for_updates_async():
1770
1605
 
1771
1606
  logger = get_logger("upgrade_check")
1772
1607
  logger.debug(f"Update check failed (non-critical): {e}")
1773
- except Exception:
1608
+ except Exception: # nosec B110
1774
1609
  pass # Avoid any errors in error handling
1775
1610
  finally:
1776
1611
  # Properly clean up event loop
@@ -1785,7 +1620,7 @@ def check_for_updates_async():
1785
1620
  loop.run_until_complete(
1786
1621
  asyncio.gather(*pending, return_exceptions=True)
1787
1622
  )
1788
- except Exception:
1623
+ except Exception: # nosec B110
1789
1624
  pass # Ignore cleanup errors
1790
1625
  finally:
1791
1626
  loop.close()