claude-mpm 5.4.41__py3-none-any.whl → 5.6.72__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 (490) 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 +109 -1925
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +161 -298
  6. claude_mpm/agents/WORKFLOW.md +2 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  8. claude_mpm/auth/__init__.py +35 -0
  9. claude_mpm/auth/callback_server.py +328 -0
  10. claude_mpm/auth/models.py +104 -0
  11. claude_mpm/auth/oauth_manager.py +266 -0
  12. claude_mpm/auth/providers/__init__.py +12 -0
  13. claude_mpm/auth/providers/base.py +165 -0
  14. claude_mpm/auth/providers/google.py +261 -0
  15. claude_mpm/auth/token_storage.py +252 -0
  16. claude_mpm/cli/__init__.py +5 -1
  17. claude_mpm/cli/commands/agents.py +2 -4
  18. claude_mpm/cli/commands/agents_reconcile.py +197 -0
  19. claude_mpm/cli/commands/autotodos.py +566 -0
  20. claude_mpm/cli/commands/commander.py +216 -0
  21. claude_mpm/cli/commands/configure.py +620 -21
  22. claude_mpm/cli/commands/configure_agent_display.py +3 -1
  23. claude_mpm/cli/commands/hook_errors.py +60 -60
  24. claude_mpm/cli/commands/mcp.py +29 -17
  25. claude_mpm/cli/commands/mcp_command_router.py +39 -0
  26. claude_mpm/cli/commands/mcp_service_commands.py +304 -0
  27. claude_mpm/cli/commands/monitor.py +2 -2
  28. claude_mpm/cli/commands/mpm_init/core.py +15 -8
  29. claude_mpm/cli/commands/oauth.py +481 -0
  30. claude_mpm/cli/commands/profile.py +9 -10
  31. claude_mpm/cli/commands/run.py +35 -3
  32. claude_mpm/cli/commands/skill_source.py +51 -2
  33. claude_mpm/cli/commands/skills.py +182 -32
  34. claude_mpm/cli/executor.py +129 -16
  35. claude_mpm/cli/helpers.py +1 -1
  36. claude_mpm/cli/interactive/__init__.py +10 -0
  37. claude_mpm/cli/interactive/agent_wizard.py +30 -50
  38. claude_mpm/cli/interactive/questionary_styles.py +65 -0
  39. claude_mpm/cli/interactive/skill_selector.py +481 -0
  40. claude_mpm/cli/parsers/base_parser.py +89 -1
  41. claude_mpm/cli/parsers/commander_parser.py +116 -0
  42. claude_mpm/cli/parsers/mcp_parser.py +79 -0
  43. claude_mpm/cli/parsers/oauth_parser.py +165 -0
  44. claude_mpm/cli/parsers/profile_parser.py +0 -1
  45. claude_mpm/cli/parsers/run_parser.py +10 -0
  46. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  47. claude_mpm/cli/parsers/skills_parser.py +2 -3
  48. claude_mpm/cli/startup.py +662 -524
  49. claude_mpm/cli/startup_display.py +76 -7
  50. claude_mpm/cli/startup_logging.py +2 -2
  51. claude_mpm/cli/utils.py +7 -3
  52. claude_mpm/commander/__init__.py +78 -0
  53. claude_mpm/commander/adapters/__init__.py +60 -0
  54. claude_mpm/commander/adapters/auggie.py +260 -0
  55. claude_mpm/commander/adapters/base.py +288 -0
  56. claude_mpm/commander/adapters/claude_code.py +392 -0
  57. claude_mpm/commander/adapters/codex.py +237 -0
  58. claude_mpm/commander/adapters/communication.py +366 -0
  59. claude_mpm/commander/adapters/example_usage.py +310 -0
  60. claude_mpm/commander/adapters/mpm.py +389 -0
  61. claude_mpm/commander/adapters/registry.py +204 -0
  62. claude_mpm/commander/api/__init__.py +16 -0
  63. claude_mpm/commander/api/app.py +121 -0
  64. claude_mpm/commander/api/errors.py +133 -0
  65. claude_mpm/commander/api/routes/__init__.py +8 -0
  66. claude_mpm/commander/api/routes/events.py +184 -0
  67. claude_mpm/commander/api/routes/inbox.py +171 -0
  68. claude_mpm/commander/api/routes/messages.py +148 -0
  69. claude_mpm/commander/api/routes/projects.py +271 -0
  70. claude_mpm/commander/api/routes/sessions.py +226 -0
  71. claude_mpm/commander/api/routes/work.py +296 -0
  72. claude_mpm/commander/api/schemas.py +186 -0
  73. claude_mpm/commander/chat/__init__.py +7 -0
  74. claude_mpm/commander/chat/cli.py +149 -0
  75. claude_mpm/commander/chat/commands.py +122 -0
  76. claude_mpm/commander/chat/repl.py +1821 -0
  77. claude_mpm/commander/config.py +51 -0
  78. claude_mpm/commander/config_loader.py +115 -0
  79. claude_mpm/commander/core/__init__.py +10 -0
  80. claude_mpm/commander/core/block_manager.py +325 -0
  81. claude_mpm/commander/core/response_manager.py +323 -0
  82. claude_mpm/commander/daemon.py +603 -0
  83. claude_mpm/commander/env_loader.py +59 -0
  84. claude_mpm/commander/events/__init__.py +26 -0
  85. claude_mpm/commander/events/manager.py +392 -0
  86. claude_mpm/commander/frameworks/__init__.py +12 -0
  87. claude_mpm/commander/frameworks/base.py +233 -0
  88. claude_mpm/commander/frameworks/claude_code.py +58 -0
  89. claude_mpm/commander/frameworks/mpm.py +57 -0
  90. claude_mpm/commander/git/__init__.py +5 -0
  91. claude_mpm/commander/git/worktree_manager.py +212 -0
  92. claude_mpm/commander/inbox/__init__.py +16 -0
  93. claude_mpm/commander/inbox/dedup.py +128 -0
  94. claude_mpm/commander/inbox/inbox.py +224 -0
  95. claude_mpm/commander/inbox/models.py +70 -0
  96. claude_mpm/commander/instance_manager.py +865 -0
  97. claude_mpm/commander/llm/__init__.py +6 -0
  98. claude_mpm/commander/llm/openrouter_client.py +167 -0
  99. claude_mpm/commander/llm/summarizer.py +70 -0
  100. claude_mpm/commander/memory/__init__.py +45 -0
  101. claude_mpm/commander/memory/compression.py +347 -0
  102. claude_mpm/commander/memory/embeddings.py +230 -0
  103. claude_mpm/commander/memory/entities.py +310 -0
  104. claude_mpm/commander/memory/example_usage.py +290 -0
  105. claude_mpm/commander/memory/integration.py +325 -0
  106. claude_mpm/commander/memory/search.py +381 -0
  107. claude_mpm/commander/memory/store.py +657 -0
  108. claude_mpm/commander/models/__init__.py +18 -0
  109. claude_mpm/commander/models/events.py +127 -0
  110. claude_mpm/commander/models/project.py +162 -0
  111. claude_mpm/commander/models/work.py +214 -0
  112. claude_mpm/commander/parsing/__init__.py +20 -0
  113. claude_mpm/commander/parsing/extractor.py +132 -0
  114. claude_mpm/commander/parsing/output_parser.py +270 -0
  115. claude_mpm/commander/parsing/patterns.py +100 -0
  116. claude_mpm/commander/persistence/__init__.py +11 -0
  117. claude_mpm/commander/persistence/event_store.py +274 -0
  118. claude_mpm/commander/persistence/state_store.py +403 -0
  119. claude_mpm/commander/persistence/work_store.py +164 -0
  120. claude_mpm/commander/polling/__init__.py +13 -0
  121. claude_mpm/commander/polling/event_detector.py +104 -0
  122. claude_mpm/commander/polling/output_buffer.py +49 -0
  123. claude_mpm/commander/polling/output_poller.py +153 -0
  124. claude_mpm/commander/project_session.py +268 -0
  125. claude_mpm/commander/proxy/__init__.py +12 -0
  126. claude_mpm/commander/proxy/formatter.py +89 -0
  127. claude_mpm/commander/proxy/output_handler.py +191 -0
  128. claude_mpm/commander/proxy/relay.py +155 -0
  129. claude_mpm/commander/registry.py +410 -0
  130. claude_mpm/commander/runtime/__init__.py +10 -0
  131. claude_mpm/commander/runtime/executor.py +191 -0
  132. claude_mpm/commander/runtime/monitor.py +346 -0
  133. claude_mpm/commander/session/__init__.py +6 -0
  134. claude_mpm/commander/session/context.py +81 -0
  135. claude_mpm/commander/session/manager.py +59 -0
  136. claude_mpm/commander/tmux_orchestrator.py +362 -0
  137. claude_mpm/commander/web/__init__.py +1 -0
  138. claude_mpm/commander/work/__init__.py +30 -0
  139. claude_mpm/commander/work/executor.py +207 -0
  140. claude_mpm/commander/work/queue.py +405 -0
  141. claude_mpm/commander/workflow/__init__.py +27 -0
  142. claude_mpm/commander/workflow/event_handler.py +241 -0
  143. claude_mpm/commander/workflow/notifier.py +146 -0
  144. claude_mpm/commands/mpm-config.md +8 -0
  145. claude_mpm/commands/mpm-doctor.md +8 -0
  146. claude_mpm/commands/mpm-help.md +8 -0
  147. claude_mpm/commands/mpm-init.md +8 -0
  148. claude_mpm/commands/mpm-monitor.md +8 -0
  149. claude_mpm/commands/mpm-organize.md +8 -0
  150. claude_mpm/commands/mpm-postmortem.md +8 -0
  151. claude_mpm/commands/mpm-session-resume.md +9 -1
  152. claude_mpm/commands/mpm-status.md +8 -0
  153. claude_mpm/commands/mpm-ticket-view.md +8 -0
  154. claude_mpm/commands/mpm-version.md +8 -0
  155. claude_mpm/commands/mpm.md +8 -0
  156. claude_mpm/config/agent_presets.py +8 -7
  157. claude_mpm/config/skill_sources.py +16 -0
  158. claude_mpm/constants.py +6 -0
  159. claude_mpm/core/claude_runner.py +154 -2
  160. claude_mpm/core/config.py +35 -22
  161. claude_mpm/core/config_constants.py +74 -9
  162. claude_mpm/core/constants.py +56 -12
  163. claude_mpm/core/hook_manager.py +53 -4
  164. claude_mpm/core/interactive_session.py +12 -11
  165. claude_mpm/core/logger.py +26 -9
  166. claude_mpm/core/logging_utils.py +39 -13
  167. claude_mpm/core/network_config.py +148 -0
  168. claude_mpm/core/oneshot_session.py +7 -6
  169. claude_mpm/core/optimized_startup.py +3 -1
  170. claude_mpm/core/output_style_manager.py +66 -18
  171. claude_mpm/core/shared/config_loader.py +3 -1
  172. claude_mpm/core/socketio_pool.py +47 -15
  173. claude_mpm/core/unified_config.py +54 -8
  174. claude_mpm/core/unified_paths.py +95 -90
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/1WZnGYqX.js +24 -0
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/67pF3qNn.js +1 -0
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/6RxdMKe4.js +1 -0
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/8cZrfX0h.js +60 -0
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/9a6T2nm-.js +7 -0
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B443AUzu.js +1 -0
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B8AwtY2H.js +1 -0
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BF15LAsF.js +1 -0
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BRcwIQNr.js +4 -0
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uj46x2Wr.js → BSNlmTZj.js} +1 -1
  188. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BV6nKitt.js +43 -0
  189. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BViJ8lZt.js +128 -0
  190. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BcQ-Q0FE.js +1 -0
  191. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bpyvgze_.js +30 -0
  192. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  193. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  194. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C3rbW_a-.js +1 -0
  195. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C8WYN38h.js +1 -0
  196. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C9I8FlXH.js +61 -0
  197. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIQcWgO2.js +36 -0
  198. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIctN7YN.js +7 -0
  199. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CKrS_JZW.js +145 -0
  200. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CR6P9C4A.js +89 -0
  201. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRRR9MD_.js +2 -0
  202. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  203. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CSXtMOf0.js +1 -0
  204. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CT-sbxSk.js +1 -0
  205. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWm6DJsp.js +1 -0
  206. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
  207. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CpqQ1Kzn.js +1 -0
  208. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
  209. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D2nGpDRe.js +1 -0
  210. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9iCMida.js +267 -0
  211. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9ykgMoY.js +10 -0
  212. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DL2Ldur1.js +1 -0
  213. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DPfltzjH.js +165 -0
  214. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{N4qtv3Hx.js → DR8nis88.js} +2 -2
  215. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUliQN2b.js +1 -0
  216. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
  217. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DXlhR01x.js +122 -0
  218. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D_lyTybS.js +1 -0
  219. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DngoTTgh.js +1 -0
  220. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DqkmHtDC.js +220 -0
  221. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DsDh8EYs.js +1 -0
  222. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DypDmXgd.js +139 -0
  223. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
  224. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/IPYC-LnN.js +162 -0
  225. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  226. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JpevfAFt.js +68 -0
  227. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DjhvlsAc.js → NqQ1dWOy.js} +1 -1
  228. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/R8CEIRAd.js +2 -0
  229. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Zxy7qc-l.js +64 -0
  230. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  231. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/qtd3IeO4.js +15 -0
  232. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ulBFON_C.js +65 -0
  233. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/wQVh1CoA.js +10 -0
  234. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.Dr7t0z2J.js +2 -0
  235. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  236. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.CAGBuiOw.js → 0.RgBboRvH.js} +1 -1
  237. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DG-KkbDf.js +1 -0
  238. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  239. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  240. claude_mpm/dashboard/static/svelte-build/index.html +11 -11
  241. claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
  242. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
  243. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
  244. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
  245. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
  246. claude_mpm/experimental/cli_enhancements.py +2 -1
  247. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  248. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  249. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  250. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  251. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  252. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  253. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  254. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  255. claude_mpm/hooks/claude_hooks/event_handlers.py +466 -136
  256. claude_mpm/hooks/claude_hooks/hook_handler.py +204 -104
  257. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  258. claude_mpm/hooks/claude_hooks/installer.py +291 -59
  259. claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
  260. claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
  261. claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
  262. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  263. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  264. claude_mpm/hooks/claude_hooks/services/__pycache__/container.cpython-311.pyc +0 -0
  265. claude_mpm/hooks/claude_hooks/services/__pycache__/protocols.cpython-311.pyc +0 -0
  266. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  267. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  268. claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
  269. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
  270. claude_mpm/hooks/claude_hooks/services/container.py +326 -0
  271. claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
  272. claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
  273. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
  274. claude_mpm/hooks/kuzu_memory_hook.py +5 -5
  275. claude_mpm/hooks/session_resume_hook.py +89 -1
  276. claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
  277. claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
  278. claude_mpm/init.py +224 -4
  279. claude_mpm/mcp/__init__.py +9 -0
  280. claude_mpm/mcp/google_workspace_server.py +610 -0
  281. claude_mpm/scripts/claude-hook-handler.sh +46 -19
  282. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  283. claude_mpm/services/agents/agent_selection_service.py +2 -2
  284. claude_mpm/services/agents/cache_git_manager.py +1 -1
  285. claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -1
  286. claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
  287. claude_mpm/services/agents/deployment/agent_template_builder.py +37 -17
  288. claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
  289. claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
  290. claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
  291. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +36 -8
  292. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +50 -26
  293. claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
  294. claude_mpm/services/agents/git_source_manager.py +21 -2
  295. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  296. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  297. claude_mpm/services/agents/sources/git_source_sync_service.py +116 -5
  298. claude_mpm/services/agents/startup_sync.py +5 -2
  299. claude_mpm/services/cli/__init__.py +3 -0
  300. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  301. claude_mpm/services/cli/session_resume_helper.py +10 -2
  302. claude_mpm/services/command_deployment_service.py +44 -26
  303. claude_mpm/services/delegation_detector.py +175 -0
  304. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  305. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  306. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  307. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  308. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  309. claude_mpm/services/diagnostics/models.py +14 -1
  310. claude_mpm/services/event_log.py +325 -0
  311. claude_mpm/services/hook_installer_service.py +77 -8
  312. claude_mpm/services/infrastructure/__init__.py +4 -0
  313. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  314. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  315. claude_mpm/services/mcp_config_manager.py +99 -19
  316. claude_mpm/services/mcp_service_registry.py +294 -0
  317. claude_mpm/services/monitor/daemon_manager.py +15 -4
  318. claude_mpm/services/monitor/management/lifecycle.py +8 -3
  319. claude_mpm/services/monitor/server.py +111 -16
  320. claude_mpm/services/pm_skills_deployer.py +302 -94
  321. claude_mpm/services/profile_manager.py +10 -4
  322. claude_mpm/services/skills/git_skill_source_manager.py +192 -29
  323. claude_mpm/services/skills/selective_skill_deployer.py +211 -46
  324. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  325. claude_mpm/services/skills_deployer.py +192 -70
  326. claude_mpm/services/socketio/handlers/hook.py +14 -7
  327. claude_mpm/services/socketio/server/main.py +12 -4
  328. claude_mpm/skills/__init__.py +2 -1
  329. claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
  330. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
  331. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
  332. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
  333. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
  334. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
  335. claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
  336. claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
  337. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
  338. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
  339. claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
  340. claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
  341. claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
  342. claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
  343. claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
  344. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
  345. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
  346. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
  347. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
  348. claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
  349. claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
  350. claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
  351. claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
  352. claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
  353. claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
  354. claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
  355. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
  356. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
  357. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
  358. claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
  359. claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
  360. claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
  361. claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
  362. claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
  363. claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
  364. claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
  365. claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
  366. claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
  367. claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
  368. claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
  369. claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
  370. claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
  371. claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
  372. claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
  373. claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
  374. claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
  375. claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
  376. claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
  377. claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
  378. claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
  379. claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
  380. claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
  381. claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
  382. claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
  383. claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
  384. claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
  385. claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
  386. claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
  387. claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
  388. claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
  389. claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
  390. claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
  391. claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
  392. claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
  393. claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
  394. claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
  395. claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
  396. claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
  397. claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
  398. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  399. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  400. claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
  401. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  402. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  403. claude_mpm/skills/bundled/pm/mpm-delegation-patterns/SKILL.md +167 -0
  404. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  405. claude_mpm/skills/bundled/pm/mpm-git-file-tracking/SKILL.md +113 -0
  406. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  407. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  408. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  409. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  410. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  411. claude_mpm/skills/bundled/pm/mpm-pr-workflow/SKILL.md +124 -0
  412. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  413. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  414. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  415. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  416. claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
  417. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  418. claude_mpm/skills/bundled/pm/mpm-ticketing-integration/SKILL.md +154 -0
  419. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  420. claude_mpm/skills/bundled/pm/mpm-verification-protocols/SKILL.md +198 -0
  421. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  422. claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
  423. claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
  424. claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
  425. claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
  426. claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
  427. claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
  428. claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
  429. claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
  430. claude_mpm/skills/bundled/security-scanning.md +112 -0
  431. claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
  432. claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
  433. claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
  434. claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
  435. claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
  436. claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
  437. claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
  438. claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
  439. claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
  440. claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
  441. claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
  442. claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
  443. claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
  444. claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
  445. claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
  446. claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
  447. claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
  448. claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
  449. claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
  450. claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
  451. claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
  452. claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
  453. claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
  454. claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
  455. claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
  456. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
  457. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
  458. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
  459. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
  460. claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
  461. claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
  462. claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
  463. claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
  464. claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
  465. claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
  466. claude_mpm/skills/registry.py +295 -90
  467. claude_mpm/skills/skill_manager.py +29 -23
  468. claude_mpm/templates/.pre-commit-config.yaml +112 -0
  469. claude_mpm/utils/agent_dependency_loader.py +103 -4
  470. claude_mpm/utils/robust_installer.py +45 -24
  471. claude_mpm-5.6.72.dist-info/METADATA +416 -0
  472. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/RECORD +477 -159
  473. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/WHEEL +1 -1
  474. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/entry_points.txt +2 -0
  475. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +0 -1
  476. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +0 -1
  477. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +0 -1
  478. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +0 -1
  479. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +0 -1
  480. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +0 -2
  481. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +0 -2
  482. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +0 -1
  483. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +0 -1
  484. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +0 -10
  485. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  486. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  487. claude_mpm-5.4.41.dist-info/METADATA +0 -998
  488. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE +0 -0
  489. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  490. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,49 @@
1
+ """Output buffer for tracking session output changes."""
2
+
3
+ import hashlib
4
+ from dataclasses import dataclass
5
+ from datetime import datetime, timezone
6
+ from typing import Optional
7
+
8
+
9
+ @dataclass
10
+ class OutputBuffer:
11
+ """Tracks output from a session and detects changes."""
12
+
13
+ session_id: str
14
+ content: str = ""
15
+ last_hash: str = ""
16
+ last_update: Optional[datetime] = None
17
+ lines_captured: int = 0
18
+
19
+ def update(self, new_content: str) -> tuple[bool, str]:
20
+ """Update buffer with new content.
21
+
22
+ Args:
23
+ new_content: The new output content to check
24
+
25
+ Returns:
26
+ Tuple of (has_changed, new_lines_only)
27
+ - has_changed: True if content changed since last update
28
+ - new_lines_only: Only the new lines added (diff)
29
+ """
30
+ new_hash = hashlib.md5(new_content.encode(), usedforsecurity=False).hexdigest()
31
+ if new_hash == self.last_hash:
32
+ return False, ""
33
+
34
+ # Find new lines (diff)
35
+ old_lines = self.content.split("\n") if self.content else []
36
+ new_lines = new_content.split("\n")
37
+
38
+ # Simple diff: new lines at the end
39
+ if len(new_lines) > len(old_lines):
40
+ diff = "\n".join(new_lines[len(old_lines) :])
41
+ else:
42
+ diff = new_content # Content replaced entirely
43
+
44
+ self.content = new_content
45
+ self.last_hash = new_hash
46
+ self.last_update = datetime.now(timezone.utc)
47
+ self.lines_captured = len(new_lines)
48
+
49
+ return True, diff
@@ -0,0 +1,153 @@
1
+ """Output polling loop for MPM Commander."""
2
+
3
+ import asyncio
4
+ import logging
5
+ from datetime import datetime, timezone
6
+ from typing import TYPE_CHECKING, Callable, Dict, Optional
7
+
8
+ from ..models.project import ProjectState, ToolSession
9
+ from ..registry import ProjectRegistry
10
+ from ..tmux_orchestrator import TmuxOrchestrator
11
+ from .event_detector import BasicEventDetector
12
+ from .output_buffer import OutputBuffer
13
+
14
+ if TYPE_CHECKING:
15
+ from .event_detector import DetectedEvent
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class OutputPoller:
21
+ """Polls tmux sessions for new output and detects events."""
22
+
23
+ def __init__(
24
+ self,
25
+ tmux: TmuxOrchestrator,
26
+ registry: ProjectRegistry,
27
+ poll_interval: float = 0.5,
28
+ capture_lines: int = 1000,
29
+ ):
30
+ """Initialize output poller.
31
+
32
+ Args:
33
+ tmux: TmuxOrchestrator instance for capturing output
34
+ registry: ProjectRegistry for state management
35
+ poll_interval: Seconds between polls (default: 0.5)
36
+ capture_lines: Number of lines to capture from tmux (default: 1000)
37
+ """
38
+ self.tmux = tmux
39
+ self.registry = registry
40
+ self.poll_interval = poll_interval
41
+ self.capture_lines = capture_lines
42
+ self.detector = BasicEventDetector()
43
+ self.buffers: Dict[str, OutputBuffer] = {}
44
+ self._running = False
45
+ self._task: Optional[asyncio.Task[None]] = None
46
+ self.on_error: Optional[Callable[[str, DetectedEvent], None]] = None
47
+ self.on_idle: Optional[Callable[[str], None]] = None
48
+
49
+ async def start(self) -> None:
50
+ """Start the polling loop."""
51
+ if self._running:
52
+ return
53
+ self._running = True
54
+ self._task = asyncio.create_task(self._poll_loop())
55
+ logger.info("Output poller started (interval: %.2fs)", self.poll_interval)
56
+
57
+ async def stop(self) -> None:
58
+ """Stop the polling loop."""
59
+ self._running = False
60
+ if self._task:
61
+ self._task.cancel()
62
+ try:
63
+ await self._task
64
+ except asyncio.CancelledError:
65
+ pass
66
+ logger.info("Output poller stopped")
67
+
68
+ async def _poll_loop(self) -> None:
69
+ """Main polling loop - runs until stopped."""
70
+ while self._running:
71
+ try:
72
+ await self._poll_all_sessions()
73
+ except Exception as e:
74
+ logger.error("Polling error: %s", e, exc_info=True)
75
+ await asyncio.sleep(self.poll_interval)
76
+
77
+ async def _poll_all_sessions(self) -> None:
78
+ """Poll all active sessions for new output."""
79
+ for project in self.registry.list_all():
80
+ for session_id, session in project.sessions.items():
81
+ if session.status in ("stopped", "error"):
82
+ continue
83
+
84
+ await self._poll_session(project.id, session)
85
+
86
+ async def _poll_session(self, project_id: str, session: ToolSession) -> None:
87
+ """Poll a single session for output changes.
88
+
89
+ Args:
90
+ project_id: ID of the project owning the session
91
+ session: Session object to poll
92
+ """
93
+ # Skip stopped or errored sessions
94
+ if session.status in ("stopped", "error"):
95
+ return
96
+
97
+ # Get or create buffer
98
+ if session.id not in self.buffers:
99
+ self.buffers[session.id] = OutputBuffer(session_id=session.id)
100
+
101
+ buffer = self.buffers[session.id]
102
+
103
+ # Capture output from tmux
104
+ try:
105
+ output = self.tmux.capture_output(
106
+ session.tmux_target, lines=self.capture_lines
107
+ )
108
+ except Exception as e:
109
+ logger.warning("Failed to capture output for %s: %s", session.id, e)
110
+ return
111
+
112
+ # Check for changes
113
+ has_changed, new_content = buffer.update(output)
114
+ if not has_changed:
115
+ return
116
+
117
+ # Update session output buffer
118
+ session.output_buffer = output
119
+ session.last_output_at = datetime.now(timezone.utc)
120
+
121
+ # Detect events - errors take priority
122
+ error_event = self.detector.detect_error(new_content)
123
+ if error_event:
124
+ logger.warning("Error detected in %s: %s", session.id, error_event.content)
125
+ session.status = "error"
126
+ self.registry.update_state(
127
+ project_id, ProjectState.ERROR, error_event.content
128
+ )
129
+ if self.on_error:
130
+ self.on_error(session.id, error_event)
131
+ return
132
+
133
+ # Check for idle state
134
+ is_idle = self.detector.detect_idle(output)
135
+ if is_idle:
136
+ if session.status != "idle":
137
+ logger.debug("Session %s now idle", session.id)
138
+ session.status = "idle"
139
+ if self.on_idle:
140
+ self.on_idle(session.id)
141
+ elif session.status == "idle":
142
+ logger.debug("Session %s now running", session.id)
143
+ session.status = "running"
144
+ self.registry.update_state(project_id, ProjectState.WORKING)
145
+
146
+ def clear_buffer(self, session_id: str) -> None:
147
+ """Clear the buffer for a session.
148
+
149
+ Args:
150
+ session_id: ID of session to clear buffer for
151
+ """
152
+ if session_id in self.buffers:
153
+ del self.buffers[session_id]
@@ -0,0 +1,268 @@
1
+ """Project session lifecycle coordinator for MPM Commander.
2
+
3
+ This module manages per-project execution state, coordinating between
4
+ runtime processes, events, and work queue.
5
+ """
6
+
7
+ import logging
8
+ from enum import Enum
9
+ from typing import Optional
10
+
11
+ from .models import Project, ProjectState
12
+ from .runtime.executor import RuntimeExecutor
13
+ from .runtime.monitor import RuntimeMonitor
14
+ from .tmux_orchestrator import TmuxOrchestrator
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class SessionState(Enum):
20
+ """Project session execution state.
21
+
22
+ Attributes:
23
+ IDLE: No active work, ready to start
24
+ STARTING: Initializing session resources
25
+ RUNNING: Actively executing work
26
+ PAUSED: Execution paused (e.g., waiting for event resolution)
27
+ STOPPING: Shutting down gracefully
28
+ STOPPED: Session terminated
29
+ """
30
+
31
+ IDLE = "idle"
32
+ STARTING = "starting"
33
+ RUNNING = "running"
34
+ PAUSED = "paused"
35
+ STOPPING = "stopping"
36
+ STOPPED = "stopped"
37
+
38
+
39
+ class ProjectSession:
40
+ """Manages lifecycle of a single project's work execution.
41
+
42
+ Coordinates between runtime executor, event system, and work queue
43
+ for autonomous task execution within a project.
44
+
45
+ Attributes:
46
+ project: Associated project instance
47
+ orchestrator: Tmux orchestrator for process management
48
+ executor: RuntimeExecutor for spawning/managing processes
49
+ monitor: RuntimeMonitor for output monitoring and event detection
50
+ state: Current session execution state
51
+ pause_reason: Reason for pause (e.g., event ID)
52
+ active_pane: Tmux pane target for active runtime, if any
53
+
54
+ Example:
55
+ >>> session = ProjectSession(project, tmux_orchestrator, executor, monitor)
56
+ >>> await session.start()
57
+ >>> session.state
58
+ <SessionState.RUNNING: 'running'>
59
+ >>> await session.pause("Event requires user input")
60
+ >>> await session.resume()
61
+ >>> await session.stop()
62
+ """
63
+
64
+ def __init__(
65
+ self,
66
+ project: Project,
67
+ orchestrator: TmuxOrchestrator,
68
+ executor: Optional[RuntimeExecutor] = None,
69
+ monitor: Optional[RuntimeMonitor] = None,
70
+ ):
71
+ """Initialize project session.
72
+
73
+ Args:
74
+ project: Project instance to manage
75
+ orchestrator: TmuxOrchestrator for process control
76
+ executor: Optional RuntimeExecutor (created if not provided)
77
+ monitor: Optional RuntimeMonitor (not used if not provided)
78
+
79
+ Raises:
80
+ ValueError: If project or orchestrator is None
81
+ """
82
+ if project is None:
83
+ raise ValueError("Project cannot be None")
84
+ if orchestrator is None:
85
+ raise ValueError("Orchestrator cannot be None")
86
+
87
+ self.project = project
88
+ self.orchestrator = orchestrator
89
+ self.executor = executor or RuntimeExecutor(orchestrator)
90
+ self.monitor = monitor
91
+ self._state = SessionState.IDLE
92
+ self.pause_reason: Optional[str] = None
93
+ self.active_pane: Optional[str] = None
94
+
95
+ logger.info(f"Created ProjectSession for project {project.id}")
96
+
97
+ @property
98
+ def state(self) -> SessionState:
99
+ """Get current session state.
100
+
101
+ Returns:
102
+ Current SessionState
103
+ """
104
+ return self._state
105
+
106
+ async def start(self) -> None:
107
+ """Initialize and start project session.
108
+
109
+ Transitions from IDLE → STARTING → RUNNING.
110
+ Spawns Claude Code via RuntimeExecutor and starts output monitoring
111
+ via RuntimeMonitor if available.
112
+
113
+ Raises:
114
+ RuntimeError: If session not in IDLE state
115
+ """
116
+ if self._state != SessionState.IDLE:
117
+ raise RuntimeError(
118
+ f"Cannot start session in state {self._state}. Must be IDLE."
119
+ )
120
+
121
+ logger.info(f"Starting session for project {self.project.id}")
122
+ self._state = SessionState.STARTING
123
+
124
+ try:
125
+ # Create tmux session if needed
126
+ if not self.orchestrator.session_exists():
127
+ self.orchestrator.create_session()
128
+ logger.debug("Created tmux session")
129
+
130
+ # Spawn Claude Code process
131
+ self.active_pane = await self.executor.spawn(self.project, command="claude")
132
+ logger.info(f"Spawned Claude Code in pane {self.active_pane}")
133
+
134
+ # Start monitoring if monitor available
135
+ if self.monitor:
136
+ await self.monitor.start_monitoring(self.active_pane, self.project.id)
137
+ logger.debug(f"Started monitoring pane {self.active_pane}")
138
+
139
+ # Update project state
140
+ self.project.state = ProjectState.IDLE
141
+ self.project.state_reason = None
142
+
143
+ # Transition to RUNNING
144
+ self._state = SessionState.RUNNING
145
+ logger.info(f"Session started for project {self.project.id}")
146
+
147
+ except Exception as e:
148
+ logger.error(f"Failed to start session: {e}")
149
+ self._state = SessionState.IDLE
150
+ # Clean up pane if created
151
+ if self.active_pane:
152
+ try:
153
+ await self.executor.terminate(self.active_pane)
154
+ except Exception as cleanup_error:
155
+ logger.debug(f"Error cleaning up pane: {cleanup_error}")
156
+ self.active_pane = None
157
+ raise
158
+
159
+ async def pause(self, reason: str) -> None:
160
+ """Pause execution (e.g., waiting for event resolution).
161
+
162
+ Transitions from RUNNING → PAUSED.
163
+ Execution will not resume until resume() is called.
164
+
165
+ Args:
166
+ reason: Reason for pause (typically event ID or description)
167
+
168
+ Raises:
169
+ RuntimeError: If session not in RUNNING state
170
+ """
171
+ if self._state != SessionState.RUNNING:
172
+ raise RuntimeError(
173
+ f"Cannot pause session in state {self._state}. Must be RUNNING."
174
+ )
175
+
176
+ logger.info(f"Pausing session for project {self.project.id}: {reason}")
177
+ self._state = SessionState.PAUSED
178
+ self.pause_reason = reason
179
+
180
+ # Update project state
181
+ self.project.state = ProjectState.BLOCKED
182
+ self.project.state_reason = reason
183
+
184
+ async def resume(self) -> None:
185
+ """Resume execution after pause.
186
+
187
+ Transitions from PAUSED → RUNNING.
188
+ Clears pause reason and continues work execution.
189
+
190
+ Raises:
191
+ RuntimeError: If session not in PAUSED state
192
+ """
193
+ if self._state != SessionState.PAUSED:
194
+ raise RuntimeError(
195
+ f"Cannot resume session in state {self._state}. Must be PAUSED."
196
+ )
197
+
198
+ logger.info(f"Resuming session for project {self.project.id}")
199
+ self._state = SessionState.RUNNING
200
+ self.pause_reason = None
201
+
202
+ # Update project state
203
+ self.project.state = ProjectState.WORKING
204
+ self.project.state_reason = None
205
+
206
+ async def stop(self) -> None:
207
+ """Gracefully stop project session.
208
+
209
+ Transitions from any state → STOPPING → STOPPED.
210
+ Stops monitoring, terminates runtime, and cleans up resources.
211
+ """
212
+ logger.info(f"Stopping session for project {self.project.id}")
213
+ self._state = SessionState.STOPPING
214
+
215
+ try:
216
+ # Stop monitoring if active
217
+ if self.monitor and self.active_pane:
218
+ await self.monitor.stop_monitoring(self.active_pane)
219
+ logger.debug(f"Stopped monitoring pane {self.active_pane}")
220
+
221
+ # Terminate active pane
222
+ if self.active_pane:
223
+ await self.executor.terminate(self.active_pane)
224
+ logger.debug(f"Terminated pane {self.active_pane}")
225
+ self.active_pane = None
226
+
227
+ # Clean up any remaining sessions (backward compatibility)
228
+ for session_id in list(self.project.sessions.keys()):
229
+ try:
230
+ session = self.project.sessions[session_id]
231
+ if session.tmux_target:
232
+ # Kill tmux pane
233
+ self.orchestrator.kill_pane(session.tmux_target)
234
+ logger.debug(f"Killed pane {session.tmux_target}")
235
+ except Exception as e:
236
+ logger.warning(f"Error killing session {session_id}: {e}")
237
+
238
+ # Update project state
239
+ self.project.state = ProjectState.IDLE
240
+ self.project.state_reason = None
241
+
242
+ # Transition to STOPPED
243
+ self._state = SessionState.STOPPED
244
+ logger.info(f"Session stopped for project {self.project.id}")
245
+
246
+ except Exception as e:
247
+ logger.error(f"Error during session stop: {e}")
248
+ self._state = SessionState.STOPPED
249
+ raise
250
+
251
+ def is_ready(self) -> bool:
252
+ """Check if session can start new work.
253
+
254
+ Returns:
255
+ True if session is in RUNNING state (not blocked or paused)
256
+ """
257
+ return self._state == SessionState.RUNNING
258
+
259
+ def can_accept_work(self) -> bool:
260
+ """Check if session can accept new work items.
261
+
262
+ Returns:
263
+ True if session is IDLE or RUNNING with no active work
264
+ """
265
+ return (
266
+ self._state in (SessionState.IDLE, SessionState.RUNNING)
267
+ and self.project.active_work is None
268
+ )
@@ -0,0 +1,12 @@
1
+ """Output proxy and summarization for MPM Commander."""
2
+
3
+ from .formatter import OutputFormatter
4
+ from .output_handler import OutputChunk, OutputHandler
5
+ from .relay import OutputRelay
6
+
7
+ __all__ = [
8
+ "OutputChunk",
9
+ "OutputFormatter",
10
+ "OutputHandler",
11
+ "OutputRelay",
12
+ ]
@@ -0,0 +1,89 @@
1
+ """Formats output for Commander chat display."""
2
+
3
+ from .output_handler import OutputChunk
4
+
5
+
6
+ class OutputFormatter:
7
+ """Formats Claude Code output for user display."""
8
+
9
+ def __init__(self, max_raw_display: int = 500):
10
+ """Initialize OutputFormatter.
11
+
12
+ Args:
13
+ max_raw_display: Maximum characters of raw output to display.
14
+ """
15
+ self.max_raw_display = max_raw_display
16
+
17
+ def format_summary(self, chunk: OutputChunk) -> str:
18
+ """Format a summarized output chunk for display.
19
+
20
+ Args:
21
+ chunk: OutputChunk with summary.
22
+
23
+ Returns:
24
+ Formatted output with summary and optionally truncated raw output.
25
+ """
26
+ lines = []
27
+
28
+ # Instance header
29
+ status = "✓" if chunk.is_complete else "⋯"
30
+ lines.append(f"[{chunk.instance_name}] {status}")
31
+
32
+ # Summary if available
33
+ if chunk.summary:
34
+ lines.append(f"\n📝 Summary: {chunk.summary}")
35
+
36
+ # Show truncated raw output if it's short enough
37
+ if len(chunk.raw_output) <= self.max_raw_display:
38
+ lines.append(f"\n```\n{chunk.raw_output}\n```")
39
+ else:
40
+ # Show truncated preview
41
+ preview = chunk.raw_output[: self.max_raw_display]
42
+ lines.append(
43
+ f"\n```\n{preview}...\n(truncated, {len(chunk.raw_output)} chars total)\n```"
44
+ )
45
+
46
+ return "".join(lines)
47
+
48
+ def format_raw(self, chunk: OutputChunk, truncate: bool = True) -> str:
49
+ """Format raw output, optionally truncated.
50
+
51
+ Args:
52
+ chunk: OutputChunk to format.
53
+ truncate: Whether to truncate long output.
54
+
55
+ Returns:
56
+ Formatted raw output.
57
+ """
58
+ status = "✓" if chunk.is_complete else "⋯"
59
+ header = f"[{chunk.instance_name}] {status}"
60
+
61
+ raw = chunk.raw_output
62
+ if truncate and len(raw) > self.max_raw_display:
63
+ raw = raw[: self.max_raw_display] + "...\n(truncated)"
64
+
65
+ return f"{header}\n```\n{raw}\n```"
66
+
67
+ def format_status(self, instance_name: str, status: str) -> str:
68
+ """Format a status message.
69
+
70
+ Args:
71
+ instance_name: Name of the instance.
72
+ status: Status message.
73
+
74
+ Returns:
75
+ Formatted status message.
76
+ """
77
+ return f"[{instance_name}] ℹ️ {status}"
78
+
79
+ def format_error(self, instance_name: str, error: str) -> str:
80
+ """Format an error message.
81
+
82
+ Args:
83
+ instance_name: Name of the instance.
84
+ error: Error message.
85
+
86
+ Returns:
87
+ Formatted error message.
88
+ """
89
+ return f"[{instance_name}] ❌ Error: {error}"