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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +8 -5
  3. claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +101 -703
  5. claude_mpm/agents/WORKFLOW.md +2 -0
  6. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  7. claude_mpm/cli/commands/autotodos.py +566 -0
  8. claude_mpm/cli/commands/commander.py +46 -0
  9. claude_mpm/cli/commands/hook_errors.py +60 -60
  10. claude_mpm/cli/commands/monitor.py +2 -2
  11. claude_mpm/cli/commands/mpm_init/core.py +2 -2
  12. claude_mpm/cli/commands/run.py +35 -3
  13. claude_mpm/cli/executor.py +119 -16
  14. claude_mpm/cli/parsers/base_parser.py +71 -1
  15. claude_mpm/cli/parsers/commander_parser.py +83 -0
  16. claude_mpm/cli/parsers/run_parser.py +10 -0
  17. claude_mpm/cli/startup.py +54 -16
  18. claude_mpm/cli/startup_display.py +72 -5
  19. claude_mpm/cli/startup_logging.py +2 -2
  20. claude_mpm/cli/utils.py +7 -3
  21. claude_mpm/commander/__init__.py +72 -0
  22. claude_mpm/commander/adapters/__init__.py +31 -0
  23. claude_mpm/commander/adapters/base.py +191 -0
  24. claude_mpm/commander/adapters/claude_code.py +361 -0
  25. claude_mpm/commander/adapters/communication.py +366 -0
  26. claude_mpm/commander/api/__init__.py +16 -0
  27. claude_mpm/commander/api/app.py +105 -0
  28. claude_mpm/commander/api/errors.py +112 -0
  29. claude_mpm/commander/api/routes/__init__.py +8 -0
  30. claude_mpm/commander/api/routes/events.py +184 -0
  31. claude_mpm/commander/api/routes/inbox.py +171 -0
  32. claude_mpm/commander/api/routes/messages.py +148 -0
  33. claude_mpm/commander/api/routes/projects.py +271 -0
  34. claude_mpm/commander/api/routes/sessions.py +215 -0
  35. claude_mpm/commander/api/routes/work.py +260 -0
  36. claude_mpm/commander/api/schemas.py +182 -0
  37. claude_mpm/commander/chat/__init__.py +7 -0
  38. claude_mpm/commander/chat/cli.py +107 -0
  39. claude_mpm/commander/chat/commands.py +96 -0
  40. claude_mpm/commander/chat/repl.py +310 -0
  41. claude_mpm/commander/config.py +49 -0
  42. claude_mpm/commander/config_loader.py +115 -0
  43. claude_mpm/commander/daemon.py +398 -0
  44. claude_mpm/commander/events/__init__.py +26 -0
  45. claude_mpm/commander/events/manager.py +332 -0
  46. claude_mpm/commander/frameworks/__init__.py +12 -0
  47. claude_mpm/commander/frameworks/base.py +143 -0
  48. claude_mpm/commander/frameworks/claude_code.py +58 -0
  49. claude_mpm/commander/frameworks/mpm.py +62 -0
  50. claude_mpm/commander/inbox/__init__.py +16 -0
  51. claude_mpm/commander/inbox/dedup.py +128 -0
  52. claude_mpm/commander/inbox/inbox.py +224 -0
  53. claude_mpm/commander/inbox/models.py +70 -0
  54. claude_mpm/commander/instance_manager.py +337 -0
  55. claude_mpm/commander/llm/__init__.py +6 -0
  56. claude_mpm/commander/llm/openrouter_client.py +167 -0
  57. claude_mpm/commander/llm/summarizer.py +70 -0
  58. claude_mpm/commander/models/__init__.py +18 -0
  59. claude_mpm/commander/models/events.py +121 -0
  60. claude_mpm/commander/models/project.py +162 -0
  61. claude_mpm/commander/models/work.py +214 -0
  62. claude_mpm/commander/parsing/__init__.py +20 -0
  63. claude_mpm/commander/parsing/extractor.py +132 -0
  64. claude_mpm/commander/parsing/output_parser.py +270 -0
  65. claude_mpm/commander/parsing/patterns.py +100 -0
  66. claude_mpm/commander/persistence/__init__.py +11 -0
  67. claude_mpm/commander/persistence/event_store.py +274 -0
  68. claude_mpm/commander/persistence/state_store.py +309 -0
  69. claude_mpm/commander/persistence/work_store.py +164 -0
  70. claude_mpm/commander/polling/__init__.py +13 -0
  71. claude_mpm/commander/polling/event_detector.py +104 -0
  72. claude_mpm/commander/polling/output_buffer.py +49 -0
  73. claude_mpm/commander/polling/output_poller.py +153 -0
  74. claude_mpm/commander/project_session.py +268 -0
  75. claude_mpm/commander/proxy/__init__.py +12 -0
  76. claude_mpm/commander/proxy/formatter.py +89 -0
  77. claude_mpm/commander/proxy/output_handler.py +191 -0
  78. claude_mpm/commander/proxy/relay.py +155 -0
  79. claude_mpm/commander/registry.py +404 -0
  80. claude_mpm/commander/runtime/__init__.py +10 -0
  81. claude_mpm/commander/runtime/executor.py +191 -0
  82. claude_mpm/commander/runtime/monitor.py +316 -0
  83. claude_mpm/commander/session/__init__.py +6 -0
  84. claude_mpm/commander/session/context.py +81 -0
  85. claude_mpm/commander/session/manager.py +59 -0
  86. claude_mpm/commander/tmux_orchestrator.py +361 -0
  87. claude_mpm/commander/web/__init__.py +1 -0
  88. claude_mpm/commander/work/__init__.py +30 -0
  89. claude_mpm/commander/work/executor.py +189 -0
  90. claude_mpm/commander/work/queue.py +405 -0
  91. claude_mpm/commander/workflow/__init__.py +27 -0
  92. claude_mpm/commander/workflow/event_handler.py +219 -0
  93. claude_mpm/commander/workflow/notifier.py +146 -0
  94. claude_mpm/commands/mpm-config.md +8 -0
  95. claude_mpm/commands/mpm-doctor.md +8 -0
  96. claude_mpm/commands/mpm-help.md +8 -0
  97. claude_mpm/commands/mpm-init.md +8 -0
  98. claude_mpm/commands/mpm-monitor.md +8 -0
  99. claude_mpm/commands/mpm-organize.md +8 -0
  100. claude_mpm/commands/mpm-postmortem.md +8 -0
  101. claude_mpm/commands/mpm-session-resume.md +9 -1
  102. claude_mpm/commands/mpm-status.md +8 -0
  103. claude_mpm/commands/mpm-ticket-view.md +8 -0
  104. claude_mpm/commands/mpm-version.md +8 -0
  105. claude_mpm/commands/mpm.md +8 -0
  106. claude_mpm/config/agent_presets.py +8 -7
  107. claude_mpm/core/config.py +5 -0
  108. claude_mpm/core/hook_manager.py +51 -3
  109. claude_mpm/core/logger.py +10 -7
  110. claude_mpm/core/logging_utils.py +4 -2
  111. claude_mpm/core/output_style_manager.py +15 -5
  112. claude_mpm/core/unified_config.py +10 -6
  113. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  114. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  115. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
  116. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
  117. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
  118. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
  119. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
  120. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
  121. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
  122. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
  123. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
  124. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
  125. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
  126. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
  127. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
  128. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  129. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  130. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
  131. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
  132. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
  133. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
  134. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
  135. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
  136. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
  137. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
  138. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  139. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
  140. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
  141. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
  142. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
  143. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
  144. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
  145. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
  146. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
  147. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
  148. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
  149. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
  150. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
  151. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
  152. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
  153. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
  154. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
  155. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
  156. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
  157. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  158. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
  159. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
  160. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
  161. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  162. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
  163. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
  164. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
  165. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
  166. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  167. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
  168. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
  169. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  170. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  171. claude_mpm/dashboard/static/svelte-build/index.html +9 -9
  172. claude_mpm/experimental/cli_enhancements.py +2 -1
  173. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  174. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  175. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +486 -0
  176. claude_mpm/hooks/claude_hooks/event_handlers.py +250 -11
  177. claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
  178. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  179. claude_mpm/hooks/claude_hooks/installer.py +69 -5
  180. claude_mpm/hooks/claude_hooks/response_tracking.py +3 -1
  181. claude_mpm/hooks/claude_hooks/services/connection_manager.py +20 -0
  182. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +14 -77
  183. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +30 -6
  184. claude_mpm/hooks/session_resume_hook.py +85 -1
  185. claude_mpm/init.py +1 -1
  186. claude_mpm/scripts/claude-hook-handler.sh +36 -10
  187. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  188. claude_mpm/services/agents/cache_git_manager.py +1 -1
  189. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
  190. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  191. claude_mpm/services/cli/__init__.py +3 -0
  192. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  193. claude_mpm/services/cli/session_resume_helper.py +10 -2
  194. claude_mpm/services/delegation_detector.py +175 -0
  195. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  196. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  197. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  198. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  199. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  200. claude_mpm/services/diagnostics/models.py +14 -1
  201. claude_mpm/services/event_log.py +325 -0
  202. claude_mpm/services/infrastructure/__init__.py +4 -0
  203. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  204. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  205. claude_mpm/services/monitor/daemon_manager.py +15 -4
  206. claude_mpm/services/monitor/management/lifecycle.py +8 -2
  207. claude_mpm/services/monitor/server.py +106 -16
  208. claude_mpm/services/pm_skills_deployer.py +259 -87
  209. claude_mpm/services/skills/git_skill_source_manager.py +51 -2
  210. claude_mpm/services/skills/selective_skill_deployer.py +114 -16
  211. claude_mpm/services/skills/skill_discovery_service.py +57 -3
  212. claude_mpm/services/socketio/handlers/hook.py +14 -7
  213. claude_mpm/services/socketio/server/main.py +12 -4
  214. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  215. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  216. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  217. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  218. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  219. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  220. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  221. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  222. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  223. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  224. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  225. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  226. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  227. claude_mpm/skills/bundled/pm/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
  228. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  229. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  230. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  231. claude_mpm/skills/skill_manager.py +4 -4
  232. claude_mpm-5.6.1.dist-info/METADATA +391 -0
  233. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/RECORD +244 -145
  234. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
  235. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
  236. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
  237. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
  238. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
  239. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
  240. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
  241. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
  242. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
  243. claude_mpm-5.4.85.dist-info/METADATA +0 -1023
  244. /claude_mpm/skills/bundled/pm/{pm-bug-reporting/pm-bug-reporting.md → mpm-bug-reporting/SKILL.md} +0 -0
  245. /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
  246. /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
  247. /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
  248. /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
  249. /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
  250. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/WHEEL +0 -0
  251. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/entry_points.txt +0 -0
  252. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE +0 -0
  253. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  254. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/top_level.txt +0 -0
@@ -64,6 +64,8 @@ Return: Clean or list of blocked items
64
64
 
65
65
  ## Publish and Release Workflow
66
66
 
67
+ **CRITICAL**: PM MUST DELEGATE all version bumps and releases to local-ops. PM never edits version files (pyproject.toml, package.json, VERSION) directly.
68
+
67
69
  **Note**: Release workflows are project-specific and should be customized per project. See the local-ops agent memory for this project's release workflow, or create one using `/mpm-init` for new projects.
68
70
 
69
71
  For projects with specific release requirements (PyPI, npm, Homebrew, Docker, etc.), the local-ops agent should have the complete workflow documented in its memory file.
@@ -523,23 +523,25 @@ PM: Task(agent="qa", task="Verify bug fix with regression test")
523
523
 
524
524
  ### KEY PRINCIPLE
525
525
 
526
- PM delegates implementation work, then MAY verify results.
526
+ PM delegates ALL work - implementation AND verification.
527
527
 
528
528
  **Workflow:**
529
- 1. **DELEGATE** to agent (using Task tool)
529
+ 1. **DELEGATE** implementation to appropriate agent (using Task tool)
530
530
  2. **WAIT** for agent to complete work
531
- 3. **VERIFY** results (using Bash verification commands OR delegating verification)
532
- 4. **REPORT** verified results with evidence
531
+ 3. **DELEGATE** verification to appropriate agent (local-ops, QA, web-qa)
532
+ 4. **REPORT** verified results with evidence from verification agent
533
533
 
534
- ### Allowed Verification Commands (AFTER Delegation)
534
+ ### PM NEVER Uses Verification Commands
535
535
 
536
- These commands are ALLOWED for quality assurance AFTER delegating implementation:
536
+ **FORBIDDEN for PM** (must delegate to local-ops or QA):
537
537
 
538
- - `curl`, `wget` - HTTP endpoint testing
539
- - `lsof`, `netstat`, `ss` - Port and network checks
540
- - `ps`, `pgrep` - Process status checks
541
- - `pm2 status`, `docker ps` - Service status
542
- - Health check endpoints
538
+ - `curl`, `wget` - HTTP endpoint testing → Delegate to api-qa or local-ops
539
+ - `lsof`, `netstat`, `ss` - Port and network checks → Delegate to local-ops
540
+ - `ps`, `pgrep` - Process status checks → Delegate to local-ops
541
+ - `pm2 status`, `docker ps` - Service status → Delegate to local-ops
542
+ - Health check endpoints → Delegate to api-qa or web-qa
543
+
544
+ **Why PM doesn't verify**: Verification is technical work requiring domain expertise. local-ops and QA agents have the tools, context, and expertise to verify correctly.
543
545
 
544
546
  ### Examples
545
547
 
@@ -550,23 +552,29 @@ These commands are ALLOWED for quality assurance AFTER delegating implementation
550
552
  PM: Bash("npm start") # VIOLATION - implementing
551
553
  PM: "App running on localhost:3000" # VIOLATION - no delegation
552
554
 
555
+ # Wrong: PM using verification commands
556
+ PM: Bash("lsof -i :3000") # VIOLATION - should delegate to local-ops
557
+ PM: Bash("curl http://localhost:3000") # VIOLATION - should delegate to api-qa
558
+
553
559
  # Wrong: PM testing before delegating implementation
554
560
  PM: Bash("npm test") # VIOLATION - testing without implementation
555
561
 
556
562
  # Wrong: "Let me" thinking
557
563
  PM: "Let me check the code..." # VIOLATION - should delegate
558
564
  PM: "Let me fix this bug..." # VIOLATION - should delegate
565
+ PM: "Let me verify the deployment..." # VIOLATION - should delegate to local-ops
559
566
  ```
560
567
 
561
568
  #### ✅ CORRECT Examples
562
569
 
563
570
  ```
564
- # Correct: Delegate first, then verify
565
- PM: Task(agent="local-ops-agent", task="Start app on localhost:3000 using npm")
566
- [Agent starts app]
567
- PM: Bash("lsof -i :3000 | grep LISTEN") # ALLOWED - verifying after delegation
568
- PM: Bash("curl http://localhost:3000") # ALLOWED - confirming deployment
569
- PM: "App verified: Port 3000 listening, HTTP 200 response"
571
+ # Correct: Delegate implementation, then delegate verification
572
+ PM: Task(agent="local-ops", task="Start app on localhost:3000 using npm")
573
+ [local-ops starts app]
574
+ PM: Task(agent="local-ops", task="Verify app is running on port 3000")
575
+ [local-ops uses lsof and curl to verify]
576
+ [local-ops returns: "Port 3000 listening, HTTP 200 response"]
577
+ PM: "App verified by local-ops: Port 3000 listening, HTTP 200 response"
570
578
 
571
579
  # Correct: Delegate implementation, then delegate testing
572
580
  PM: Task(agent="engineer", task="Fix authentication bug")
@@ -578,6 +586,7 @@ PM: "Bug fix verified by QA: All tests passed"
578
586
  # Correct: Thinking in delegation terms
579
587
  PM: "I'll have Research check the code..."
580
588
  PM: "I'll delegate this fix to Engineer..."
589
+ PM: "I'll have local-ops verify the deployment..."
581
590
  ```
582
591
 
583
592
  ---
@@ -0,0 +1,566 @@
1
+ """CLI commands for auto-generating todos from hook errors.
2
+
3
+ WHY this is needed:
4
+ - Convert hook errors into actionable todos for the PM
5
+ - Enable PM to delegate error resolution to appropriate agents
6
+ - Reduce manual todo creation overhead
7
+ - Maintain error visibility in the PM's workflow
8
+
9
+ DESIGN DECISION: Event-driven architecture
10
+ - Read from event log instead of hook_errors.json
11
+ - Event log provides clean separation between detection and consumption
12
+ - Supports multiple consumers (CLI, dashboard, notifications)
13
+ - Persistent storage with pending/resolved status tracking
14
+ """
15
+
16
+ import json
17
+ from datetime import datetime, timezone
18
+ from pathlib import Path
19
+ from typing import Any, Dict, List, Optional
20
+
21
+ import click
22
+
23
+ from claude_mpm.services.delegation_detector import get_delegation_detector
24
+ from claude_mpm.services.event_log import get_event_log
25
+
26
+
27
+ def format_error_event_as_todo(event: Dict[str, Any]) -> Dict[str, str]:
28
+ """Convert event log error event to todo format compatible with PM TodoWrite.
29
+
30
+ Args:
31
+ event: Event from event log with payload containing error details
32
+
33
+ Returns:
34
+ Dictionary with todo fields (content, activeForm, status)
35
+ """
36
+ payload = event.get("payload", {})
37
+ error_type = payload.get("error_type", "unknown")
38
+ hook_type = payload.get("hook_type", "unknown")
39
+ details = payload.get("details", "")
40
+ full_message = payload.get("full_message", "")
41
+
42
+ # Create concise todo content
43
+ content = f"Fix {hook_type} hook error: {error_type}"
44
+ if details:
45
+ content += f" ({details[:50]}{'...' if len(details) > 50 else ''})"
46
+
47
+ # Active form for in-progress display
48
+ active_form = f"Fixing {hook_type} hook error"
49
+
50
+ return {
51
+ "content": content,
52
+ "activeForm": active_form,
53
+ "status": "pending",
54
+ "metadata": {
55
+ "event_id": event.get("id", ""),
56
+ "event_type": event.get("event_type", ""),
57
+ "error_type": error_type,
58
+ "hook_type": hook_type,
59
+ "details": details,
60
+ "full_message": full_message,
61
+ "suggested_fix": payload.get("suggested_fix", ""),
62
+ "timestamp": event.get("timestamp", ""),
63
+ },
64
+ }
65
+
66
+
67
+ def format_delegation_event_as_todo(event: Dict[str, Any]) -> Dict[str, str]:
68
+ """Convert event log delegation event to todo format compatible with PM TodoWrite.
69
+
70
+ Args:
71
+ event: Event from event log with payload containing delegation details
72
+
73
+ Returns:
74
+ Dictionary with todo fields (content, activeForm, status)
75
+ """
76
+ payload = event.get("payload", {})
77
+ pattern_type = payload.get("pattern_type", "Task")
78
+ suggested_todo = payload.get("suggested_todo", "")
79
+ action = payload.get("action", "")
80
+ original_text = payload.get("original_text", "")
81
+
82
+ # Create concise todo content
83
+ content = f"[Delegation] {suggested_todo}"
84
+
85
+ # Active form for in-progress display
86
+ active_form = f"Delegating: {action[:30]}..."
87
+
88
+ return {
89
+ "content": content,
90
+ "activeForm": active_form,
91
+ "status": "pending",
92
+ "metadata": {
93
+ "event_id": event.get("id", ""),
94
+ "event_type": event.get("event_type", ""),
95
+ "pattern_type": pattern_type,
96
+ "suggested_todo": suggested_todo,
97
+ "action": action,
98
+ "original_text": original_text,
99
+ "timestamp": event.get("timestamp", ""),
100
+ },
101
+ }
102
+
103
+
104
+ def get_autotodos(max_todos: int = 100) -> List[Dict[str, Any]]:
105
+ """Get all pending hook error events formatted as todos.
106
+
107
+ DESIGN DECISION: Only autotodo.error events are returned
108
+ - autotodo.error = Script/coding failures → PM should delegate fix
109
+ - pm.violation = Delegation anti-patterns → PM behavior error (not todo)
110
+
111
+ Args:
112
+ max_todos: Maximum number of todos to return (default: 100)
113
+
114
+ Returns:
115
+ List of todo dictionaries ready for PM injection
116
+ """
117
+ event_log = get_event_log()
118
+ todos = []
119
+
120
+ # Get all pending autotodo.error events (script failures)
121
+ pending_error_events = event_log.list_events(
122
+ event_type="autotodo.error", status="pending"
123
+ )
124
+
125
+ for event in pending_error_events[:max_todos]:
126
+ todo = format_error_event_as_todo(event)
127
+ todos.append(todo)
128
+
129
+ return todos
130
+
131
+
132
+ def get_pending_todos(
133
+ max_todos: int = 10, working_dir: Optional[Path] = None
134
+ ) -> List[Dict[str, Any]]:
135
+ """Get pending autotodo errors for injection.
136
+
137
+ WHY this function exists:
138
+ - Provides a consistent API for retrieving pending autotodos
139
+ - Used by CLI inject command AND SessionStart hook
140
+ - Supports limiting number of todos to avoid overwhelming PM
141
+
142
+ Args:
143
+ max_todos: Maximum number of todos to return (default: 10)
144
+ working_dir: Working directory to use for event log path (default: Path.cwd())
145
+
146
+ Returns:
147
+ List of todo dicts with content, activeForm, status, metadata
148
+ """
149
+ # Construct log file path from working_dir if provided
150
+ log_file = None
151
+ if working_dir:
152
+ log_file = Path(working_dir) / ".claude-mpm" / "event_log.json"
153
+
154
+ event_log = get_event_log(log_file)
155
+ todos = []
156
+
157
+ # Get all pending autotodo.error events (script failures)
158
+ pending_error_events = event_log.list_events(
159
+ event_type="autotodo.error", status="pending"
160
+ )
161
+
162
+ for event in pending_error_events[:max_todos]:
163
+ todo = format_error_event_as_todo(event)
164
+ todos.append(todo)
165
+
166
+ return todos
167
+
168
+
169
+ @click.group(name="autotodos")
170
+ def autotodos_group():
171
+ """Auto-generate todos from hook errors.
172
+
173
+ This command converts hook errors into actionable todos that can be
174
+ injected into the PM's todo list for delegation and resolution.
175
+
176
+ Uses event-driven architecture - reads from event log instead of
177
+ directly from hook error memory.
178
+ """
179
+
180
+
181
+ @autotodos_group.command(name="status")
182
+ def show_autotodos_status():
183
+ """Show autotodos status and statistics.
184
+
185
+ Quick overview of pending hook errors, PM violations, and autotodos.
186
+
187
+ Example:
188
+ claude-mpm autotodos status
189
+ """
190
+ event_log = get_event_log()
191
+ stats = event_log.get_stats()
192
+ todos = get_autotodos()
193
+ violations = event_log.list_events(event_type="pm.violation", status="pending")
194
+
195
+ click.echo("\n📊 AutoTodos Status")
196
+ click.echo("=" * 80)
197
+
198
+ click.echo(f"Total Events: {stats['total_events']}")
199
+ click.echo(f"Pending Todos (script errors): {len(todos)}")
200
+ click.echo(f"Pending Violations (PM errors): {len(violations)}")
201
+ click.echo(f"Total Pending Events: {stats['by_status']['pending']}")
202
+ click.echo(f"Resolved Events: {stats['by_status']['resolved']}")
203
+
204
+ if stats.get("by_type"):
205
+ click.echo("\n📋 Events by Type:")
206
+ for event_type, count in stats["by_type"].items():
207
+ click.echo(f" {event_type}: {count}")
208
+
209
+ click.echo(f"\n📁 Event Log: {stats['log_file']}")
210
+
211
+ if todos or violations:
212
+ click.echo("\n⚠️ Action Required:")
213
+ if todos:
214
+ click.echo(f" {len(todos)} script error(s) need delegation")
215
+ if violations:
216
+ click.echo(f" {len(violations)} PM violation(s) need correction")
217
+ click.echo("\nCommands:")
218
+ click.echo(
219
+ " claude-mpm autotodos list # View pending todos (script errors)"
220
+ )
221
+ click.echo(" claude-mpm autotodos violations # View PM violations")
222
+ click.echo(" claude-mpm autotodos inject # Inject todos into PM session")
223
+ click.echo(" claude-mpm autotodos clear # Clear after resolution")
224
+ else:
225
+ click.echo("\n✅ No pending todos or violations. All clear!")
226
+
227
+
228
+ @autotodos_group.command(name="list")
229
+ @click.option(
230
+ "--format",
231
+ type=click.Choice(["table", "json"], case_sensitive=False),
232
+ default="table",
233
+ help="Output format (table or json)",
234
+ )
235
+ def list_autotodos(format):
236
+ """List all auto-generated todos from hook errors.
237
+
238
+ Shows pending hook errors formatted as todos that can be acted upon
239
+ by the PM.
240
+
241
+ Examples:
242
+ claude-mpm autotodos list
243
+ claude-mpm autotodos list --format json
244
+ """
245
+ todos = get_autotodos()
246
+
247
+ if not todos:
248
+ click.echo("✅ No pending hook errors. All clear!")
249
+ return
250
+
251
+ if format == "json":
252
+ # JSON output for programmatic use
253
+ click.echo(json.dumps(todos, indent=2))
254
+ else:
255
+ # Table output for human readability
256
+ click.echo("\n" + "=" * 80)
257
+ click.echo("Auto-Generated Todos from Hook Errors")
258
+ click.echo("=" * 80)
259
+
260
+ for i, todo in enumerate(todos, 1):
261
+ metadata = todo.get("metadata", {})
262
+ click.echo(f"\n{i}. {todo['content']}")
263
+ click.echo(f" Status: {todo['status']}")
264
+ click.echo(f" Hook: {metadata.get('hook_type', 'Unknown')}")
265
+ click.echo(f" Error Type: {metadata.get('error_type', 'Unknown')}")
266
+ click.echo(f" Timestamp: {metadata.get('timestamp', 'Unknown')}")
267
+
268
+ # Show suggested fix if available
269
+ suggested_fix = metadata.get("suggested_fix", "")
270
+ if suggested_fix:
271
+ # Show first line of suggestion
272
+ first_line = suggested_fix.split("\n")[0]
273
+ click.echo(f" Suggestion: {first_line}")
274
+
275
+ click.echo("\n" + "=" * 80)
276
+ click.echo(f"Total: {len(todos)} pending todo(s)")
277
+ click.echo("\nTo inject into PM session: claude-mpm autotodos inject")
278
+
279
+
280
+ @autotodos_group.command(name="inject")
281
+ @click.option(
282
+ "--output",
283
+ type=click.Path(),
284
+ help="Output file path (default: stdout)",
285
+ )
286
+ def inject_autotodos(output):
287
+ """Inject auto-generated todos in PM-compatible format.
288
+
289
+ Outputs todos in a format that can be injected into the PM's
290
+ session as system reminders.
291
+
292
+ Examples:
293
+ claude-mpm autotodos inject
294
+ claude-mpm autotodos inject --output todos.json
295
+ """
296
+ todos = get_autotodos()
297
+
298
+ if not todos:
299
+ click.echo("✅ No pending hook errors to inject.", err=True)
300
+ return
301
+
302
+ # Format as system reminder for PM
303
+ pm_message = {
304
+ "type": "autotodos",
305
+ "timestamp": datetime.now(timezone.utc).isoformat(),
306
+ "todos": todos,
307
+ "message": f"Found {len(todos)} hook error(s) requiring attention. "
308
+ "Consider delegating to appropriate agents for resolution.",
309
+ }
310
+
311
+ output_json = json.dumps(pm_message, indent=2)
312
+
313
+ if output:
314
+ # Write to file
315
+ output_path = Path(output)
316
+ output_path.write_text(output_json)
317
+ click.echo(f"✅ Injected {len(todos)} todo(s) to {output_path}", err=True)
318
+ else:
319
+ # Write to stdout for piping
320
+ click.echo(output_json)
321
+
322
+
323
+ @autotodos_group.command(name="clear")
324
+ @click.option(
325
+ "--event-id",
326
+ help="Clear specific event by ID",
327
+ )
328
+ @click.option(
329
+ "--event-type",
330
+ type=click.Choice(["error", "violation", "all"], case_sensitive=False),
331
+ default="all",
332
+ help="Type of events to clear (default: all)",
333
+ )
334
+ @click.option(
335
+ "--yes",
336
+ "-y",
337
+ is_flag=True,
338
+ help="Skip confirmation prompt",
339
+ )
340
+ def clear_autotodos(event_id, event_type, yes):
341
+ """Clear hook errors and PM violations after resolution.
342
+
343
+ This marks resolved events in the event log, removing them from
344
+ the autotodos and violations lists.
345
+
346
+ Examples:
347
+ claude-mpm autotodos clear # Clear all pending
348
+ claude-mpm autotodos clear --event-type error # Clear only errors
349
+ claude-mpm autotodos clear --event-type violation # Clear only violations
350
+ claude-mpm autotodos clear --event-id ID # Clear specific event
351
+ claude-mpm autotodos clear -y # Skip confirmation
352
+ """
353
+ event_log = get_event_log()
354
+
355
+ if event_id:
356
+ # Clear specific event
357
+ if not yes:
358
+ message = f"Clear event: {event_id}?"
359
+ if not click.confirm(message):
360
+ click.echo("Cancelled.")
361
+ return
362
+
363
+ # Mark as resolved
364
+ if event_log.mark_resolved(event_id):
365
+ click.echo(f"✅ Cleared event: {event_id}")
366
+ else:
367
+ click.echo(f"❌ Event not found: {event_id}")
368
+ else:
369
+ # Determine which event types to clear
370
+ if event_type == "error":
371
+ event_types = ["autotodo.error"]
372
+ elif event_type == "violation":
373
+ event_types = ["pm.violation"]
374
+ else: # all
375
+ event_types = ["autotodo.error", "pm.violation"]
376
+
377
+ # Count pending events
378
+ total_count = 0
379
+ for et in event_types:
380
+ pending = event_log.list_events(event_type=et, status="pending")
381
+ total_count += len(pending)
382
+
383
+ if total_count == 0:
384
+ click.echo("No pending events to clear.")
385
+ return
386
+
387
+ if not yes:
388
+ message = f"Clear all {total_count} pending event(s)?"
389
+ if not click.confirm(message):
390
+ click.echo("Cancelled.")
391
+ return
392
+
393
+ # Mark all as resolved
394
+ total_cleared = 0
395
+ for et in event_types:
396
+ cleared = event_log.mark_all_resolved(event_type=et)
397
+ total_cleared += cleared
398
+
399
+ click.echo(f"✅ Cleared {total_cleared} event(s).")
400
+
401
+
402
+ @autotodos_group.command(name="violations")
403
+ @click.option(
404
+ "--format",
405
+ type=click.Choice(["table", "json"], case_sensitive=False),
406
+ default="table",
407
+ help="Output format (table or json)",
408
+ )
409
+ def list_pm_violations(format):
410
+ """List PM delegation violations.
411
+
412
+ Shows instances where PM asked user to do something manually
413
+ instead of delegating to an agent. These are PM behavior errors
414
+ that should be corrected, not todos to delegate.
415
+
416
+ Examples:
417
+ claude-mpm autotodos violations
418
+ claude-mpm autotodos violations --format json
419
+ """
420
+ event_log = get_event_log()
421
+ violations = event_log.list_events(event_type="pm.violation", status="pending")
422
+
423
+ if not violations:
424
+ click.echo("✅ No PM violations detected. All delegation patterns are correct!")
425
+ return
426
+
427
+ if format == "json":
428
+ # JSON output for programmatic use
429
+ click.echo(json.dumps(violations, indent=2))
430
+ else:
431
+ # Table output for human readability
432
+ click.echo("\n" + "=" * 80)
433
+ click.echo("PM Delegation Violations")
434
+ click.echo("=" * 80)
435
+ click.echo("\n⚠️ PM asked user to do these manually instead of delegating:\n")
436
+
437
+ for i, violation in enumerate(violations, 1):
438
+ payload = violation.get("payload", {})
439
+ click.echo(f"{i}. Pattern: {payload.get('pattern_type', 'Unknown')}")
440
+ click.echo(f' Original: "{payload.get("original_text", "")}"')
441
+ click.echo(f" Should delegate: {payload.get('suggested_action', '')}")
442
+ click.echo(f" Severity: {payload.get('severity', 'unknown')}")
443
+ click.echo(f" Timestamp: {violation.get('timestamp', 'Unknown')}")
444
+ click.echo()
445
+
446
+ click.echo("=" * 80)
447
+ click.echo(f"Total: {len(violations)} violation(s) detected")
448
+ click.echo("\n💡 These are PM behavior errors - PM should delegate these tasks")
449
+ click.echo(" to appropriate agents instead of asking user to do them.")
450
+ click.echo("\nTo clear: claude-mpm autotodos clear --event-type violation")
451
+
452
+
453
+ @autotodos_group.command(name="scan")
454
+ @click.argument("text", required=False)
455
+ @click.option(
456
+ "--file",
457
+ "-f",
458
+ type=click.Path(exists=True),
459
+ help="Scan text from file instead of argument",
460
+ )
461
+ @click.option(
462
+ "--format",
463
+ type=click.Choice(["table", "json"], case_sensitive=False),
464
+ default="table",
465
+ help="Output format (table or json)",
466
+ )
467
+ @click.option(
468
+ "--save",
469
+ is_flag=True,
470
+ help="Save detections to event log as PM violations",
471
+ )
472
+ def scan_delegation_patterns(text, file, format, save):
473
+ """Scan text for delegation anti-patterns.
474
+
475
+ Detects when PM asks user to do something manually instead of
476
+ delegating to an agent. Helps enforce the delegation principle.
477
+
478
+ Examples:
479
+ claude-mpm autotodos scan "Make sure .env.local is in .gitignore"
480
+ claude-mpm autotodos scan -f response.txt
481
+ claude-mpm autotodos scan -f response.txt --save
482
+ echo "You'll need to run npm install" | claude-mpm autotodos scan
483
+ """
484
+ detector = get_delegation_detector()
485
+
486
+ # Read text from file, argument, or stdin
487
+ if file:
488
+ text = Path(file).read_text()
489
+ elif not text:
490
+ # Read from stdin
491
+ import sys
492
+
493
+ text = sys.stdin.read()
494
+
495
+ if not text or not text.strip():
496
+ click.echo("Error: No text provided to scan.", err=True)
497
+ click.echo("\nUsage:", err=True)
498
+ click.echo(" claude-mpm autotodos scan 'text to scan'", err=True)
499
+ click.echo(" claude-mpm autotodos scan -f file.txt", err=True)
500
+ click.echo(" echo 'text' | claude-mpm autotodos scan", err=True)
501
+ return
502
+
503
+ # Detect delegation patterns
504
+ detections = detector.detect_user_delegation(text)
505
+
506
+ if not detections:
507
+ click.echo("✅ No delegation anti-patterns detected!")
508
+ return
509
+
510
+ # Save to event log if requested
511
+ if save:
512
+ event_log = get_event_log()
513
+ for detection in detections:
514
+ # Format as PM violation payload
515
+ payload = {
516
+ "violation_type": "delegation_anti_pattern",
517
+ "pattern_type": detection["pattern_type"],
518
+ "original_text": detection["original_text"],
519
+ "suggested_action": detection["suggested_todo"],
520
+ "action": detection["action"],
521
+ "source": "delegation_detector",
522
+ "severity": "warning",
523
+ "message": f"PM asked user to do something manually: {detection['original_text'][:80]}...",
524
+ }
525
+ event_log.append_event(
526
+ event_type="pm.violation", payload=payload, status="pending"
527
+ )
528
+ click.echo(f"\n✅ Saved {len(detections)} violation(s) to event log")
529
+
530
+ # Output results
531
+ if format == "json":
532
+ # JSON output for programmatic use
533
+ click.echo(json.dumps(detections, indent=2))
534
+ else:
535
+ # Table output for human readability
536
+ click.echo("\n" + "=" * 80)
537
+ click.echo("Delegation Anti-Patterns Detected")
538
+ click.echo("=" * 80)
539
+ click.echo(
540
+ "\n⚠️ PM is asking user to do these manually instead of delegating:\n"
541
+ )
542
+
543
+ for i, detection in enumerate(detections, 1):
544
+ click.echo(f"{i}. Pattern: {detection['pattern_type']}")
545
+ click.echo(f' Original: "{detection["original_text"]}"')
546
+ click.echo(f" Suggested Todo: {detection['suggested_todo']}")
547
+ click.echo(f" Action: {detection['action']}")
548
+ click.echo()
549
+
550
+ click.echo("=" * 80)
551
+ click.echo(f"Total: {len(detections)} anti-pattern(s) detected")
552
+ click.echo("\n💡 Tip: PM should delegate these tasks to appropriate agents")
553
+ click.echo(" instead of asking the user to do them manually.")
554
+
555
+ if not save:
556
+ click.echo("\n Use --save to add these as autotodos for PM to see.")
557
+
558
+
559
+ # Register the command group
560
+ def register_commands(cli):
561
+ """Register autotodos commands with CLI.
562
+
563
+ Args:
564
+ cli: Click CLI group to register commands with
565
+ """
566
+ cli.add_command(autotodos_group)