claude-mpm 5.4.65__py3-none-any.whl → 5.6.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (313) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
  3. claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
  4. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +107 -1928
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +119 -689
  6. claude_mpm/agents/WORKFLOW.md +2 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  8. claude_mpm/cli/__init__.py +5 -1
  9. claude_mpm/cli/commands/agents.py +2 -4
  10. claude_mpm/cli/commands/agents_reconcile.py +197 -0
  11. claude_mpm/cli/commands/autotodos.py +566 -0
  12. claude_mpm/cli/commands/commander.py +46 -0
  13. claude_mpm/cli/commands/configure.py +620 -21
  14. claude_mpm/cli/commands/hook_errors.py +60 -60
  15. claude_mpm/cli/commands/monitor.py +2 -2
  16. claude_mpm/cli/commands/mpm_init/core.py +2 -2
  17. claude_mpm/cli/commands/run.py +35 -3
  18. claude_mpm/cli/commands/skill_source.py +51 -2
  19. claude_mpm/cli/commands/skills.py +171 -17
  20. claude_mpm/cli/executor.py +120 -16
  21. claude_mpm/cli/interactive/__init__.py +10 -0
  22. claude_mpm/cli/interactive/agent_wizard.py +30 -50
  23. claude_mpm/cli/interactive/questionary_styles.py +65 -0
  24. claude_mpm/cli/interactive/skill_selector.py +481 -0
  25. claude_mpm/cli/parsers/base_parser.py +76 -1
  26. claude_mpm/cli/parsers/commander_parser.py +83 -0
  27. claude_mpm/cli/parsers/run_parser.py +10 -0
  28. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  29. claude_mpm/cli/parsers/skills_parser.py +5 -0
  30. claude_mpm/cli/startup.py +203 -359
  31. claude_mpm/cli/startup_display.py +72 -5
  32. claude_mpm/cli/startup_logging.py +2 -2
  33. claude_mpm/cli/utils.py +7 -3
  34. claude_mpm/commander/__init__.py +72 -0
  35. claude_mpm/commander/adapters/__init__.py +31 -0
  36. claude_mpm/commander/adapters/base.py +191 -0
  37. claude_mpm/commander/adapters/claude_code.py +361 -0
  38. claude_mpm/commander/adapters/communication.py +366 -0
  39. claude_mpm/commander/api/__init__.py +16 -0
  40. claude_mpm/commander/api/app.py +105 -0
  41. claude_mpm/commander/api/errors.py +133 -0
  42. claude_mpm/commander/api/routes/__init__.py +8 -0
  43. claude_mpm/commander/api/routes/events.py +184 -0
  44. claude_mpm/commander/api/routes/inbox.py +171 -0
  45. claude_mpm/commander/api/routes/messages.py +148 -0
  46. claude_mpm/commander/api/routes/projects.py +271 -0
  47. claude_mpm/commander/api/routes/sessions.py +228 -0
  48. claude_mpm/commander/api/routes/work.py +260 -0
  49. claude_mpm/commander/api/schemas.py +182 -0
  50. claude_mpm/commander/chat/__init__.py +7 -0
  51. claude_mpm/commander/chat/cli.py +107 -0
  52. claude_mpm/commander/chat/commands.py +96 -0
  53. claude_mpm/commander/chat/repl.py +310 -0
  54. claude_mpm/commander/config.py +49 -0
  55. claude_mpm/commander/config_loader.py +115 -0
  56. claude_mpm/commander/daemon.py +398 -0
  57. claude_mpm/commander/events/__init__.py +26 -0
  58. claude_mpm/commander/events/manager.py +332 -0
  59. claude_mpm/commander/frameworks/__init__.py +12 -0
  60. claude_mpm/commander/frameworks/base.py +143 -0
  61. claude_mpm/commander/frameworks/claude_code.py +58 -0
  62. claude_mpm/commander/frameworks/mpm.py +62 -0
  63. claude_mpm/commander/inbox/__init__.py +16 -0
  64. claude_mpm/commander/inbox/dedup.py +128 -0
  65. claude_mpm/commander/inbox/inbox.py +224 -0
  66. claude_mpm/commander/inbox/models.py +70 -0
  67. claude_mpm/commander/instance_manager.py +337 -0
  68. claude_mpm/commander/llm/__init__.py +6 -0
  69. claude_mpm/commander/llm/openrouter_client.py +167 -0
  70. claude_mpm/commander/llm/summarizer.py +70 -0
  71. claude_mpm/commander/models/__init__.py +18 -0
  72. claude_mpm/commander/models/events.py +121 -0
  73. claude_mpm/commander/models/project.py +162 -0
  74. claude_mpm/commander/models/work.py +214 -0
  75. claude_mpm/commander/parsing/__init__.py +20 -0
  76. claude_mpm/commander/parsing/extractor.py +132 -0
  77. claude_mpm/commander/parsing/output_parser.py +270 -0
  78. claude_mpm/commander/parsing/patterns.py +100 -0
  79. claude_mpm/commander/persistence/__init__.py +11 -0
  80. claude_mpm/commander/persistence/event_store.py +274 -0
  81. claude_mpm/commander/persistence/state_store.py +309 -0
  82. claude_mpm/commander/persistence/work_store.py +164 -0
  83. claude_mpm/commander/polling/__init__.py +13 -0
  84. claude_mpm/commander/polling/event_detector.py +104 -0
  85. claude_mpm/commander/polling/output_buffer.py +49 -0
  86. claude_mpm/commander/polling/output_poller.py +153 -0
  87. claude_mpm/commander/project_session.py +268 -0
  88. claude_mpm/commander/proxy/__init__.py +12 -0
  89. claude_mpm/commander/proxy/formatter.py +89 -0
  90. claude_mpm/commander/proxy/output_handler.py +191 -0
  91. claude_mpm/commander/proxy/relay.py +155 -0
  92. claude_mpm/commander/registry.py +404 -0
  93. claude_mpm/commander/runtime/__init__.py +10 -0
  94. claude_mpm/commander/runtime/executor.py +191 -0
  95. claude_mpm/commander/runtime/monitor.py +316 -0
  96. claude_mpm/commander/session/__init__.py +6 -0
  97. claude_mpm/commander/session/context.py +81 -0
  98. claude_mpm/commander/session/manager.py +59 -0
  99. claude_mpm/commander/tmux_orchestrator.py +361 -0
  100. claude_mpm/commander/web/__init__.py +1 -0
  101. claude_mpm/commander/work/__init__.py +30 -0
  102. claude_mpm/commander/work/executor.py +189 -0
  103. claude_mpm/commander/work/queue.py +405 -0
  104. claude_mpm/commander/workflow/__init__.py +27 -0
  105. claude_mpm/commander/workflow/event_handler.py +219 -0
  106. claude_mpm/commander/workflow/notifier.py +146 -0
  107. claude_mpm/commands/mpm-config.md +8 -0
  108. claude_mpm/commands/mpm-doctor.md +8 -0
  109. claude_mpm/commands/mpm-help.md +8 -0
  110. claude_mpm/commands/mpm-init.md +8 -0
  111. claude_mpm/commands/mpm-monitor.md +8 -0
  112. claude_mpm/commands/mpm-organize.md +8 -0
  113. claude_mpm/commands/mpm-postmortem.md +8 -0
  114. claude_mpm/commands/mpm-session-resume.md +9 -1
  115. claude_mpm/commands/mpm-status.md +8 -0
  116. claude_mpm/commands/mpm-ticket-view.md +8 -0
  117. claude_mpm/commands/mpm-version.md +8 -0
  118. claude_mpm/commands/mpm.md +8 -0
  119. claude_mpm/config/agent_presets.py +8 -7
  120. claude_mpm/config/skill_sources.py +16 -0
  121. claude_mpm/constants.py +1 -0
  122. claude_mpm/core/claude_runner.py +2 -2
  123. claude_mpm/core/config.py +32 -19
  124. claude_mpm/core/hook_manager.py +51 -3
  125. claude_mpm/core/interactive_session.py +7 -7
  126. claude_mpm/core/logger.py +26 -9
  127. claude_mpm/core/logging_utils.py +35 -11
  128. claude_mpm/core/output_style_manager.py +31 -13
  129. claude_mpm/core/unified_config.py +54 -8
  130. claude_mpm/core/unified_paths.py +95 -90
  131. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  132. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  133. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
  134. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
  135. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
  136. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
  137. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
  138. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
  139. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
  140. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
  141. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
  142. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
  143. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
  144. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
  145. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
  146. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  147. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  148. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
  149. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
  150. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
  151. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
  152. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
  153. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
  154. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
  155. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
  156. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  157. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
  158. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
  159. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
  160. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
  161. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
  162. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
  163. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
  164. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
  165. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
  166. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
  167. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
  168. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
  169. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
  170. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
  171. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
  172. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
  173. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
  174. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  188. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  189. claude_mpm/dashboard/static/svelte-build/index.html +9 -9
  190. claude_mpm/experimental/cli_enhancements.py +2 -1
  191. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  192. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  193. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  194. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  195. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
  196. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
  197. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  198. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
  199. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
  200. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  201. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
  202. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
  203. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  204. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
  205. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  206. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
  207. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
  208. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  209. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
  210. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
  211. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
  212. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  213. claude_mpm/hooks/claude_hooks/event_handlers.py +283 -87
  214. claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
  215. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  216. claude_mpm/hooks/claude_hooks/installer.py +116 -8
  217. claude_mpm/hooks/claude_hooks/memory_integration.py +51 -31
  218. claude_mpm/hooks/claude_hooks/response_tracking.py +42 -59
  219. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
  220. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  221. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
  222. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
  223. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
  224. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  225. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
  226. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
  227. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  228. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
  229. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
  230. claude_mpm/hooks/claude_hooks/services/connection_manager.py +39 -24
  231. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +36 -103
  232. claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
  233. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +73 -75
  234. claude_mpm/hooks/session_resume_hook.py +89 -1
  235. claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
  236. claude_mpm/init.py +1 -1
  237. claude_mpm/scripts/claude-hook-handler.sh +43 -16
  238. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  239. claude_mpm/services/agents/agent_selection_service.py +2 -2
  240. claude_mpm/services/agents/cache_git_manager.py +1 -1
  241. claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
  242. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
  243. claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
  244. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  245. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  246. claude_mpm/services/agents/startup_sync.py +5 -2
  247. claude_mpm/services/cli/__init__.py +3 -0
  248. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  249. claude_mpm/services/cli/session_resume_helper.py +10 -2
  250. claude_mpm/services/delegation_detector.py +175 -0
  251. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  252. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  253. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  254. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  255. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  256. claude_mpm/services/diagnostics/models.py +14 -1
  257. claude_mpm/services/event_log.py +325 -0
  258. claude_mpm/services/infrastructure/__init__.py +4 -0
  259. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  260. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  261. claude_mpm/services/monitor/daemon_manager.py +15 -4
  262. claude_mpm/services/monitor/management/lifecycle.py +8 -2
  263. claude_mpm/services/monitor/server.py +106 -16
  264. claude_mpm/services/pm_skills_deployer.py +259 -87
  265. claude_mpm/services/skills/git_skill_source_manager.py +135 -11
  266. claude_mpm/services/skills/selective_skill_deployer.py +142 -26
  267. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  268. claude_mpm/services/skills_deployer.py +31 -5
  269. claude_mpm/services/socketio/handlers/hook.py +14 -7
  270. claude_mpm/services/socketio/server/main.py +12 -4
  271. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  272. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  273. claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
  274. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  275. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  276. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  277. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  278. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  279. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  280. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  281. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  282. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  283. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  284. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  285. claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
  286. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  287. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  288. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  289. claude_mpm/skills/skill_manager.py +4 -4
  290. claude_mpm/utils/agent_dependency_loader.py +4 -2
  291. claude_mpm/utils/robust_installer.py +10 -6
  292. claude_mpm-5.6.10.dist-info/METADATA +391 -0
  293. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/RECORD +303 -181
  294. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
  295. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
  296. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
  297. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
  298. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
  299. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
  300. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
  301. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
  302. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
  303. claude_mpm-5.4.65.dist-info/METADATA +0 -999
  304. /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
  305. /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
  306. /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
  307. /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
  308. /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
  309. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/WHEEL +0 -0
  310. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/entry_points.txt +0 -0
  311. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE +0 -0
  312. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  313. {claude_mpm-5.4.65.dist-info → claude_mpm-5.6.10.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,366 @@
1
+ """Communication adapters for managing async I/O with AI coding assistants.
2
+
3
+ This module provides the async communication layer that sits between
4
+ InstanceManager and TmuxOrchestrator, using RuntimeAdapter for parsing.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ from abc import ABC, abstractmethod
10
+ from dataclasses import dataclass
11
+ from enum import Enum
12
+ from typing import TYPE_CHECKING, AsyncIterator, Optional
13
+
14
+ from claude_mpm.commander.tmux_orchestrator import TmuxOrchestrator
15
+
16
+ from .base import RuntimeAdapter
17
+
18
+ if TYPE_CHECKING:
19
+ from .base import ParsedResponse
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class AdapterState(Enum):
25
+ """States that a communication adapter can be in."""
26
+
27
+ IDLE = "idle" # Ready for input
28
+ PROCESSING = "processing" # Working on request
29
+ WAITING = "waiting" # Waiting for user input (e.g., permission)
30
+ ERROR = "error" # Error state
31
+
32
+
33
+ @dataclass
34
+ class AdapterResponse:
35
+ """Response from a communication adapter.
36
+
37
+ Attributes:
38
+ content: The response content
39
+ state: Current adapter state
40
+ tool_uses: List of tools used in response
41
+ files_modified: List of files edited
42
+ is_complete: True if response is complete (prompt returned)
43
+
44
+ Example:
45
+ >>> response = AdapterResponse(
46
+ ... content="File created: test.py",
47
+ ... state=AdapterState.IDLE,
48
+ ... tool_uses=["Write"],
49
+ ... files_modified=["test.py"],
50
+ ... is_complete=True
51
+ ... )
52
+ """
53
+
54
+ content: str
55
+ state: AdapterState
56
+ tool_uses: Optional[list[str]] = None
57
+ files_modified: Optional[list[str]] = None
58
+ is_complete: bool = False
59
+
60
+
61
+ class BaseCommunicationAdapter(ABC):
62
+ """Abstract base class for communication adapters.
63
+
64
+ A communication adapter manages the async I/O with an AI coding assistant
65
+ via TmuxOrchestrator, maintaining state and handling streaming responses.
66
+
67
+ Example:
68
+ >>> orchestrator = TmuxOrchestrator()
69
+ >>> adapter = ClaudeCodeCommunicationAdapter(orchestrator, "%0")
70
+ >>> await adapter.send("Fix the bug in main.py")
71
+ >>> response = await adapter.receive()
72
+ >>> print(response.content)
73
+ """
74
+
75
+ @abstractmethod
76
+ async def send(self, message: str) -> None:
77
+ """Send a message to the assistant.
78
+
79
+ Args:
80
+ message: The message to send
81
+
82
+ Example:
83
+ >>> await adapter.send("Fix the bug in main.py")
84
+ """
85
+
86
+ @abstractmethod
87
+ async def receive(self, timeout: float = 30.0) -> AdapterResponse:
88
+ """Wait for and return response.
89
+
90
+ Args:
91
+ timeout: Maximum time to wait for response
92
+
93
+ Returns:
94
+ AdapterResponse with parsed content and state
95
+
96
+ Example:
97
+ >>> response = await adapter.receive(timeout=60.0)
98
+ >>> print(response.content)
99
+ """
100
+
101
+ @abstractmethod
102
+ async def interrupt(self) -> bool:
103
+ """Send interrupt signal (Ctrl+C).
104
+
105
+ Returns:
106
+ True if interrupt was successful
107
+
108
+ Example:
109
+ >>> success = await adapter.interrupt()
110
+ """
111
+
112
+ @abstractmethod
113
+ def is_ready(self) -> bool:
114
+ """Check if adapter is ready for input.
115
+
116
+ Returns:
117
+ True if adapter is in IDLE state
118
+
119
+ Example:
120
+ >>> if adapter.is_ready():
121
+ ... await adapter.send("Next task")
122
+ """
123
+
124
+ @abstractmethod
125
+ async def stream_response(self) -> AsyncIterator[str]:
126
+ """Stream response chunks as they arrive.
127
+
128
+ Yields:
129
+ Response chunks as they become available
130
+
131
+ Example:
132
+ >>> async for chunk in adapter.stream_response():
133
+ ... print(chunk, end='')
134
+ """
135
+
136
+
137
+ class ClaudeCodeCommunicationAdapter(BaseCommunicationAdapter):
138
+ """Communication adapter for Claude Code CLI.
139
+
140
+ This adapter manages async I/O with Claude Code via TmuxOrchestrator,
141
+ using ClaudeCodeAdapter (RuntimeAdapter) for output parsing.
142
+
143
+ Attributes:
144
+ orchestrator: TmuxOrchestrator for tmux operations
145
+ pane_target: Tmux pane target (e.g., "%0")
146
+ poll_interval: Polling interval for output capture (seconds)
147
+
148
+ Example:
149
+ >>> orchestrator = TmuxOrchestrator()
150
+ >>> adapter = ClaudeCodeCommunicationAdapter(orchestrator, "%0")
151
+ >>> await adapter.send("Create a new Python file")
152
+ >>> response = await adapter.receive()
153
+ >>> print(response.files_modified)
154
+ ['new_file.py']
155
+ """
156
+
157
+ def __init__(
158
+ self,
159
+ orchestrator: TmuxOrchestrator,
160
+ pane_target: str,
161
+ runtime_adapter: RuntimeAdapter,
162
+ poll_interval: float = 0.2,
163
+ ):
164
+ """Initialize the communication adapter.
165
+
166
+ Args:
167
+ orchestrator: TmuxOrchestrator for tmux operations
168
+ pane_target: Tmux pane target (e.g., "%0")
169
+ runtime_adapter: RuntimeAdapter for parsing output
170
+ poll_interval: Polling interval for output capture (seconds)
171
+ """
172
+ self.orchestrator = orchestrator
173
+ self.pane_target = pane_target
174
+ self.runtime_adapter = runtime_adapter
175
+ self.poll_interval = poll_interval
176
+ self._state = AdapterState.IDLE
177
+ self._last_output = ""
178
+ self._output_buffer = ""
179
+
180
+ async def send(self, message: str) -> None:
181
+ """Send message to Claude Code.
182
+
183
+ Args:
184
+ message: The message to send
185
+
186
+ Example:
187
+ >>> await adapter.send("Fix the bug in main.py")
188
+ """
189
+ logger.debug(f"Sending message to {self.pane_target}: {message[:50]}...")
190
+ self._state = AdapterState.PROCESSING
191
+ self._output_buffer = ""
192
+
193
+ # Format message using RuntimeAdapter
194
+ formatted = self.runtime_adapter.format_input(message)
195
+
196
+ # Send via tmux
197
+ self.orchestrator.send_keys(self.pane_target, formatted, enter=True)
198
+
199
+ async def receive(self, timeout: float = 30.0) -> AdapterResponse:
200
+ """Wait for complete response from Claude Code.
201
+
202
+ Args:
203
+ timeout: Maximum time to wait for response
204
+
205
+ Returns:
206
+ AdapterResponse with parsed content and state
207
+
208
+ Example:
209
+ >>> response = await adapter.receive(timeout=60.0)
210
+ >>> if response.is_complete:
211
+ ... print("Task complete!")
212
+ """
213
+ start = asyncio.get_event_loop().time()
214
+
215
+ while asyncio.get_event_loop().time() - start < timeout:
216
+ # Capture output from tmux pane
217
+ output = self.orchestrator.capture_output(self.pane_target, lines=100)
218
+
219
+ # Get only new output
220
+ new_output = self._get_new_output(output)
221
+ if new_output:
222
+ self._output_buffer += new_output
223
+
224
+ # Parse response using RuntimeAdapter
225
+ parsed = self.runtime_adapter.parse_response(output)
226
+
227
+ # Check if error occurred (prioritize error state)
228
+ if parsed.is_error:
229
+ self._state = AdapterState.ERROR
230
+ logger.warning(f"Error detected: {parsed.error_message}")
231
+ return self._build_response(parsed, is_complete=True)
232
+
233
+ # Check if waiting for user input (question)
234
+ if parsed.is_question:
235
+ self._state = AdapterState.WAITING
236
+ logger.debug(f"Question detected: {parsed.question_text}")
237
+ return self._build_response(parsed, is_complete=False)
238
+
239
+ # Check if response is complete (idle state)
240
+ if parsed.is_complete:
241
+ self._state = AdapterState.IDLE
242
+ logger.debug("Response complete (idle detected)")
243
+ return self._build_response(parsed, is_complete=True)
244
+
245
+ # Continue polling
246
+ await asyncio.sleep(self.poll_interval)
247
+
248
+ # Timeout - return partial response
249
+ logger.warning(f"Timeout after {timeout}s")
250
+ parsed = self.runtime_adapter.parse_response(self._output_buffer)
251
+ return self._build_response(parsed, is_complete=False)
252
+
253
+ async def interrupt(self) -> bool:
254
+ """Send Ctrl+C to interrupt Claude Code.
255
+
256
+ Returns:
257
+ True if interrupt was successful
258
+
259
+ Example:
260
+ >>> success = await adapter.interrupt()
261
+ >>> if success:
262
+ ... print("Interrupted successfully")
263
+ """
264
+ try:
265
+ logger.info(f"Sending interrupt to {self.pane_target}")
266
+ self.orchestrator.send_keys(self.pane_target, "C-c", enter=False)
267
+ self._state = AdapterState.IDLE
268
+ return True
269
+ except Exception as e:
270
+ logger.error(f"Failed to interrupt: {e}")
271
+ return False
272
+
273
+ def is_ready(self) -> bool:
274
+ """Check if adapter is ready for input.
275
+
276
+ Returns:
277
+ True if adapter is in IDLE state
278
+
279
+ Example:
280
+ >>> if adapter.is_ready():
281
+ ... await adapter.send("Next task")
282
+ """
283
+ return self._state == AdapterState.IDLE
284
+
285
+ async def stream_response(self) -> AsyncIterator[str]:
286
+ """Stream response chunks from Claude Code.
287
+
288
+ Yields:
289
+ Response chunks as they become available
290
+
291
+ Example:
292
+ >>> async for chunk in adapter.stream_response():
293
+ ... print(chunk, end='', flush=True)
294
+ """
295
+ last_len = 0
296
+
297
+ while self._state == AdapterState.PROCESSING:
298
+ # Capture current output
299
+ output = self.orchestrator.capture_output(self.pane_target, lines=100)
300
+
301
+ # Get new output since last check and add to buffer
302
+ new_output = self._get_new_output(output)
303
+ if new_output:
304
+ self._output_buffer += new_output
305
+
306
+ # Yield new chunk if buffer grew
307
+ if len(self._output_buffer) > last_len:
308
+ chunk = self._output_buffer[last_len:]
309
+ last_len = len(self._output_buffer)
310
+ yield chunk
311
+
312
+ # Check if complete using RuntimeAdapter
313
+ parsed = self.runtime_adapter.parse_response(output)
314
+ if parsed.is_complete:
315
+ self._state = AdapterState.IDLE
316
+ logger.debug("Streaming complete (idle detected)")
317
+ break
318
+
319
+ await asyncio.sleep(self.poll_interval)
320
+
321
+ def _get_new_output(self, current: str) -> str:
322
+ """Get only new output since last capture.
323
+
324
+ Args:
325
+ current: Current output from tmux pane
326
+
327
+ Returns:
328
+ New output that hasn't been seen before
329
+ """
330
+ if current == self._last_output:
331
+ return ""
332
+
333
+ # Find where new content starts
334
+ if self._last_output and current.startswith(self._last_output):
335
+ new = current[len(self._last_output) :]
336
+ else:
337
+ new = current
338
+
339
+ self._last_output = current
340
+ return new
341
+
342
+ def _build_response(
343
+ self,
344
+ parsed: "ParsedResponse",
345
+ is_complete: bool,
346
+ ) -> AdapterResponse:
347
+ """Build AdapterResponse from ParsedResponse.
348
+
349
+ Args:
350
+ parsed: ParsedResponse from RuntimeAdapter
351
+ is_complete: Whether response is complete
352
+
353
+ Returns:
354
+ AdapterResponse with metadata
355
+ """
356
+ # TODO: Extract tool uses and files modified from content
357
+ # This would require additional parsing patterns in RuntimeAdapter
358
+ # For now, return basic response
359
+
360
+ return AdapterResponse(
361
+ content=parsed.content,
362
+ state=self._state,
363
+ tool_uses=None, # Future: extract from content
364
+ files_modified=None, # Future: extract from content
365
+ is_complete=is_complete,
366
+ )
@@ -0,0 +1,16 @@
1
+ """MPM Commander REST API.
2
+
3
+ This package provides a FastAPI-based REST API for managing projects,
4
+ sessions, and messages in the MPM Commander.
5
+
6
+ Example:
7
+ Run the API server with uvicorn:
8
+
9
+ $ uvicorn claude_mpm.commander.api.app:app --host 127.0.0.1 --port 8000
10
+
11
+ Access the API documentation at http://127.0.0.1:8000/docs
12
+ """
13
+
14
+ from .app import app
15
+
16
+ __all__ = ["app"]
@@ -0,0 +1,105 @@
1
+ """FastAPI application for MPM Commander REST API.
2
+
3
+ This module defines the main FastAPI application instance with CORS,
4
+ lifecycle management, and route registration.
5
+ """
6
+
7
+ from contextlib import asynccontextmanager
8
+ from pathlib import Path
9
+ from typing import AsyncGenerator, Optional
10
+
11
+ from fastapi import FastAPI
12
+ from fastapi.middleware.cors import CORSMiddleware
13
+ from fastapi.responses import FileResponse
14
+ from fastapi.staticfiles import StaticFiles
15
+
16
+ from ..events.manager import EventManager
17
+ from ..inbox import Inbox
18
+ from ..registry import ProjectRegistry
19
+ from ..tmux_orchestrator import TmuxOrchestrator
20
+ from ..workflow import EventHandler
21
+ from .routes import events, inbox as inbox_routes, messages, projects, sessions, work
22
+
23
+ # Global instances (injected at startup via lifespan)
24
+ registry: Optional[ProjectRegistry] = None
25
+ tmux: Optional[TmuxOrchestrator] = None
26
+ event_manager: Optional[EventManager] = None
27
+ inbox: Optional[Inbox] = None
28
+ event_handler: Optional[EventHandler] = None
29
+ session_manager: dict = {} # project_id -> ProjectSession
30
+
31
+
32
+ @asynccontextmanager
33
+ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
34
+ """Manage application lifecycle.
35
+
36
+ Initializes shared resources on startup and cleans up on shutdown.
37
+
38
+ Args:
39
+ app: FastAPI application instance
40
+
41
+ Yields:
42
+ None during application runtime
43
+ """
44
+ # Startup
45
+ global registry, tmux, event_manager, inbox, event_handler, session_manager
46
+ registry = ProjectRegistry()
47
+ tmux = TmuxOrchestrator()
48
+ event_manager = EventManager()
49
+ inbox = Inbox(event_manager, registry)
50
+ session_manager = {} # Populated by daemon when sessions are created
51
+ event_handler = EventHandler(inbox, session_manager)
52
+
53
+ yield
54
+
55
+ # Shutdown
56
+ # No cleanup needed for Phase 1
57
+
58
+
59
+ app = FastAPI(
60
+ title="MPM Commander API",
61
+ description="REST API for MPM Commander - Autonomous AI Orchestration",
62
+ version="1.0.0",
63
+ lifespan=lifespan,
64
+ )
65
+
66
+ # CORS for local development
67
+ app.add_middleware(
68
+ CORSMiddleware,
69
+ allow_origins=["http://localhost:*"],
70
+ allow_credentials=True,
71
+ allow_methods=["*"],
72
+ allow_headers=["*"],
73
+ )
74
+
75
+ # Include routers
76
+ app.include_router(projects.router, prefix="/api", tags=["projects"])
77
+ app.include_router(sessions.router, prefix="/api", tags=["sessions"])
78
+ app.include_router(messages.router, prefix="/api", tags=["messages"])
79
+ app.include_router(inbox_routes.router, prefix="/api", tags=["inbox"])
80
+ app.include_router(events.router, prefix="/api", tags=["events"])
81
+ app.include_router(work.router, prefix="/api", tags=["work"])
82
+
83
+ # Mount static files
84
+ static_path = Path(__file__).parent.parent / "web" / "static"
85
+ app.mount("/static", StaticFiles(directory=str(static_path)), name="static")
86
+
87
+
88
+ @app.get("/api/health")
89
+ async def health_check() -> dict:
90
+ """Health check endpoint.
91
+
92
+ Returns:
93
+ Status and version information
94
+ """
95
+ return {"status": "ok", "version": "1.0.0"}
96
+
97
+
98
+ @app.get("/")
99
+ async def root() -> FileResponse:
100
+ """Serve the web UI index page.
101
+
102
+ Returns:
103
+ HTML page for the web UI
104
+ """
105
+ return FileResponse(static_path / "index.html")
@@ -0,0 +1,133 @@
1
+ """Error handling for MPM Commander API.
2
+
3
+ This module defines custom exception classes that map to HTTP error responses
4
+ with structured error codes and messages.
5
+ """
6
+
7
+ from fastapi import HTTPException
8
+
9
+
10
+ class CommanderAPIError(HTTPException):
11
+ """Base exception for all Commander API errors.
12
+
13
+ Provides consistent error response format with code and message.
14
+
15
+ Attributes:
16
+ code: Machine-readable error code
17
+ message: Human-readable error message
18
+ status_code: HTTP status code
19
+ """
20
+
21
+ def __init__(self, code: str, message: str, status_code: int = 400):
22
+ """Initialize API error.
23
+
24
+ Args:
25
+ code: Error code (e.g., "PROJECT_NOT_FOUND")
26
+ message: Descriptive error message
27
+ status_code: HTTP status code (default: 400)
28
+ """
29
+ super().__init__(
30
+ status_code=status_code,
31
+ detail={"error": {"code": code, "message": message}},
32
+ )
33
+
34
+
35
+ class ProjectNotFoundError(CommanderAPIError):
36
+ """Project with given ID does not exist."""
37
+
38
+ def __init__(self, project_id: str):
39
+ """Initialize project not found error.
40
+
41
+ Args:
42
+ project_id: The project ID that was not found
43
+ """
44
+ super().__init__(
45
+ "PROJECT_NOT_FOUND",
46
+ f"Project not found: {project_id}",
47
+ 404,
48
+ )
49
+
50
+
51
+ class ProjectAlreadyExistsError(CommanderAPIError):
52
+ """Project already registered at given path."""
53
+
54
+ def __init__(self, path: str):
55
+ """Initialize project already exists error.
56
+
57
+ Args:
58
+ path: The path that is already registered
59
+ """
60
+ super().__init__(
61
+ "PROJECT_ALREADY_EXISTS",
62
+ f"Project already registered: {path}",
63
+ 409,
64
+ )
65
+
66
+
67
+ class InvalidPathError(CommanderAPIError):
68
+ """Path does not exist or is not a directory."""
69
+
70
+ def __init__(self, path: str):
71
+ """Initialize invalid path error.
72
+
73
+ Args:
74
+ path: The invalid path
75
+ """
76
+ super().__init__(
77
+ "INVALID_PATH",
78
+ f"Path does not exist or is not a directory: {path}",
79
+ 400,
80
+ )
81
+
82
+
83
+ class SessionNotFoundError(CommanderAPIError):
84
+ """Session with given ID does not exist."""
85
+
86
+ def __init__(self, session_id: str):
87
+ """Initialize session not found error.
88
+
89
+ Args:
90
+ session_id: The session ID that was not found
91
+ """
92
+ super().__init__(
93
+ "SESSION_NOT_FOUND",
94
+ f"Session not found: {session_id}",
95
+ 404,
96
+ )
97
+
98
+
99
+ class InvalidRuntimeError(CommanderAPIError):
100
+ """Invalid runtime adapter specified."""
101
+
102
+ def __init__(self, runtime: str):
103
+ """Initialize invalid runtime error.
104
+
105
+ Args:
106
+ runtime: The invalid runtime name
107
+ """
108
+ super().__init__(
109
+ "INVALID_RUNTIME",
110
+ f"Invalid runtime: {runtime}",
111
+ 400,
112
+ )
113
+
114
+
115
+ class TmuxNoSpaceError(CommanderAPIError):
116
+ """Raised when tmux has no space for a new pane."""
117
+
118
+ def __init__(self, message: str | None = None):
119
+ """Initialize tmux no space error.
120
+
121
+ Args:
122
+ message: Custom error message (optional)
123
+ """
124
+ default_msg = (
125
+ "Unable to create session: tmux has no space for new pane. "
126
+ "Try closing some sessions or resize your terminal window. "
127
+ "You can also create a new tmux window with `tmux new-window`."
128
+ )
129
+ super().__init__(
130
+ "TMUX_NO_SPACE",
131
+ message or default_msg,
132
+ 409,
133
+ )
@@ -0,0 +1,8 @@
1
+ """API route modules for MPM Commander.
2
+
3
+ This package exports all route modules for registration with the FastAPI app.
4
+ """
5
+
6
+ from . import messages, projects, sessions, work
7
+
8
+ __all__ = ["messages", "projects", "sessions", "work"]