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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +8 -5
  3. claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +109 -706
  5. claude_mpm/agents/WORKFLOW.md +2 -0
  6. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  7. claude_mpm/auth/__init__.py +35 -0
  8. claude_mpm/auth/callback_server.py +328 -0
  9. claude_mpm/auth/models.py +104 -0
  10. claude_mpm/auth/oauth_manager.py +266 -0
  11. claude_mpm/auth/providers/__init__.py +12 -0
  12. claude_mpm/auth/providers/base.py +165 -0
  13. claude_mpm/auth/providers/google.py +261 -0
  14. claude_mpm/auth/token_storage.py +252 -0
  15. claude_mpm/cli/commands/autotodos.py +566 -0
  16. claude_mpm/cli/commands/commander.py +216 -0
  17. claude_mpm/cli/commands/hook_errors.py +60 -60
  18. claude_mpm/cli/commands/mcp.py +29 -17
  19. claude_mpm/cli/commands/mcp_command_router.py +39 -0
  20. claude_mpm/cli/commands/mcp_service_commands.py +304 -0
  21. claude_mpm/cli/commands/monitor.py +2 -2
  22. claude_mpm/cli/commands/mpm_init/core.py +2 -2
  23. claude_mpm/cli/commands/oauth.py +481 -0
  24. claude_mpm/cli/commands/run.py +35 -3
  25. claude_mpm/cli/commands/skill_source.py +51 -2
  26. claude_mpm/cli/commands/skills.py +5 -3
  27. claude_mpm/cli/executor.py +128 -16
  28. claude_mpm/cli/helpers.py +1 -1
  29. claude_mpm/cli/parsers/base_parser.py +84 -1
  30. claude_mpm/cli/parsers/commander_parser.py +116 -0
  31. claude_mpm/cli/parsers/mcp_parser.py +79 -0
  32. claude_mpm/cli/parsers/oauth_parser.py +165 -0
  33. claude_mpm/cli/parsers/run_parser.py +10 -0
  34. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  35. claude_mpm/cli/parsers/skills_parser.py +5 -0
  36. claude_mpm/cli/startup.py +345 -40
  37. claude_mpm/cli/startup_display.py +76 -7
  38. claude_mpm/cli/startup_logging.py +2 -2
  39. claude_mpm/cli/startup_migrations.py +236 -0
  40. claude_mpm/cli/utils.py +7 -3
  41. claude_mpm/commander/__init__.py +78 -0
  42. claude_mpm/commander/adapters/__init__.py +60 -0
  43. claude_mpm/commander/adapters/auggie.py +260 -0
  44. claude_mpm/commander/adapters/base.py +288 -0
  45. claude_mpm/commander/adapters/claude_code.py +392 -0
  46. claude_mpm/commander/adapters/codex.py +237 -0
  47. claude_mpm/commander/adapters/communication.py +366 -0
  48. claude_mpm/commander/adapters/example_usage.py +310 -0
  49. claude_mpm/commander/adapters/mpm.py +389 -0
  50. claude_mpm/commander/adapters/registry.py +204 -0
  51. claude_mpm/commander/api/__init__.py +16 -0
  52. claude_mpm/commander/api/app.py +121 -0
  53. claude_mpm/commander/api/errors.py +133 -0
  54. claude_mpm/commander/api/routes/__init__.py +8 -0
  55. claude_mpm/commander/api/routes/events.py +184 -0
  56. claude_mpm/commander/api/routes/inbox.py +171 -0
  57. claude_mpm/commander/api/routes/messages.py +148 -0
  58. claude_mpm/commander/api/routes/projects.py +271 -0
  59. claude_mpm/commander/api/routes/sessions.py +226 -0
  60. claude_mpm/commander/api/routes/work.py +296 -0
  61. claude_mpm/commander/api/schemas.py +186 -0
  62. claude_mpm/commander/chat/__init__.py +7 -0
  63. claude_mpm/commander/chat/cli.py +149 -0
  64. claude_mpm/commander/chat/commands.py +124 -0
  65. claude_mpm/commander/chat/repl.py +1957 -0
  66. claude_mpm/commander/config.py +51 -0
  67. claude_mpm/commander/config_loader.py +115 -0
  68. claude_mpm/commander/core/__init__.py +10 -0
  69. claude_mpm/commander/core/block_manager.py +325 -0
  70. claude_mpm/commander/core/response_manager.py +323 -0
  71. claude_mpm/commander/daemon.py +603 -0
  72. claude_mpm/commander/env_loader.py +59 -0
  73. claude_mpm/commander/events/__init__.py +26 -0
  74. claude_mpm/commander/events/manager.py +392 -0
  75. claude_mpm/commander/frameworks/__init__.py +12 -0
  76. claude_mpm/commander/frameworks/base.py +233 -0
  77. claude_mpm/commander/frameworks/claude_code.py +58 -0
  78. claude_mpm/commander/frameworks/mpm.py +57 -0
  79. claude_mpm/commander/git/__init__.py +5 -0
  80. claude_mpm/commander/git/worktree_manager.py +212 -0
  81. claude_mpm/commander/inbox/__init__.py +16 -0
  82. claude_mpm/commander/inbox/dedup.py +128 -0
  83. claude_mpm/commander/inbox/inbox.py +224 -0
  84. claude_mpm/commander/inbox/models.py +70 -0
  85. claude_mpm/commander/instance_manager.py +868 -0
  86. claude_mpm/commander/llm/__init__.py +6 -0
  87. claude_mpm/commander/llm/openrouter_client.py +167 -0
  88. claude_mpm/commander/llm/summarizer.py +70 -0
  89. claude_mpm/commander/memory/__init__.py +45 -0
  90. claude_mpm/commander/memory/compression.py +347 -0
  91. claude_mpm/commander/memory/embeddings.py +230 -0
  92. claude_mpm/commander/memory/entities.py +310 -0
  93. claude_mpm/commander/memory/example_usage.py +290 -0
  94. claude_mpm/commander/memory/integration.py +325 -0
  95. claude_mpm/commander/memory/search.py +381 -0
  96. claude_mpm/commander/memory/store.py +657 -0
  97. claude_mpm/commander/models/__init__.py +18 -0
  98. claude_mpm/commander/models/events.py +127 -0
  99. claude_mpm/commander/models/project.py +162 -0
  100. claude_mpm/commander/models/work.py +214 -0
  101. claude_mpm/commander/parsing/__init__.py +20 -0
  102. claude_mpm/commander/parsing/extractor.py +132 -0
  103. claude_mpm/commander/parsing/output_parser.py +270 -0
  104. claude_mpm/commander/parsing/patterns.py +100 -0
  105. claude_mpm/commander/persistence/__init__.py +11 -0
  106. claude_mpm/commander/persistence/event_store.py +274 -0
  107. claude_mpm/commander/persistence/state_store.py +403 -0
  108. claude_mpm/commander/persistence/work_store.py +164 -0
  109. claude_mpm/commander/polling/__init__.py +13 -0
  110. claude_mpm/commander/polling/event_detector.py +104 -0
  111. claude_mpm/commander/polling/output_buffer.py +49 -0
  112. claude_mpm/commander/polling/output_poller.py +153 -0
  113. claude_mpm/commander/project_session.py +268 -0
  114. claude_mpm/commander/proxy/__init__.py +12 -0
  115. claude_mpm/commander/proxy/formatter.py +89 -0
  116. claude_mpm/commander/proxy/output_handler.py +191 -0
  117. claude_mpm/commander/proxy/relay.py +155 -0
  118. claude_mpm/commander/registry.py +410 -0
  119. claude_mpm/commander/runtime/__init__.py +10 -0
  120. claude_mpm/commander/runtime/executor.py +191 -0
  121. claude_mpm/commander/runtime/monitor.py +346 -0
  122. claude_mpm/commander/session/__init__.py +6 -0
  123. claude_mpm/commander/session/context.py +81 -0
  124. claude_mpm/commander/session/manager.py +59 -0
  125. claude_mpm/commander/tmux_orchestrator.py +362 -0
  126. claude_mpm/commander/web/__init__.py +1 -0
  127. claude_mpm/commander/work/__init__.py +30 -0
  128. claude_mpm/commander/work/executor.py +207 -0
  129. claude_mpm/commander/work/queue.py +405 -0
  130. claude_mpm/commander/workflow/__init__.py +27 -0
  131. claude_mpm/commander/workflow/event_handler.py +241 -0
  132. claude_mpm/commander/workflow/notifier.py +146 -0
  133. claude_mpm/commands/mpm-config.md +8 -0
  134. claude_mpm/commands/mpm-doctor.md +8 -0
  135. claude_mpm/commands/mpm-help.md +8 -0
  136. claude_mpm/commands/mpm-init.md +8 -0
  137. claude_mpm/commands/mpm-monitor.md +8 -0
  138. claude_mpm/commands/mpm-organize.md +8 -0
  139. claude_mpm/commands/mpm-postmortem.md +8 -0
  140. claude_mpm/commands/mpm-session-resume.md +9 -1
  141. claude_mpm/commands/mpm-status.md +8 -0
  142. claude_mpm/commands/mpm-ticket-view.md +8 -0
  143. claude_mpm/commands/mpm-version.md +8 -0
  144. claude_mpm/commands/mpm.md +8 -0
  145. claude_mpm/config/agent_presets.py +8 -7
  146. claude_mpm/config/skill_sources.py +16 -0
  147. claude_mpm/constants.py +5 -0
  148. claude_mpm/core/claude_runner.py +152 -0
  149. claude_mpm/core/config.py +35 -22
  150. claude_mpm/core/config_constants.py +74 -9
  151. claude_mpm/core/constants.py +56 -12
  152. claude_mpm/core/hook_manager.py +53 -4
  153. claude_mpm/core/interactive_session.py +5 -4
  154. claude_mpm/core/logger.py +26 -9
  155. claude_mpm/core/logging_utils.py +39 -13
  156. claude_mpm/core/network_config.py +148 -0
  157. claude_mpm/core/oneshot_session.py +7 -6
  158. claude_mpm/core/output_style_manager.py +52 -12
  159. claude_mpm/core/socketio_pool.py +47 -15
  160. claude_mpm/core/unified_config.py +10 -6
  161. claude_mpm/core/unified_paths.py +68 -80
  162. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  163. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  164. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
  165. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
  166. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
  167. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
  168. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
  169. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
  170. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
  171. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
  172. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
  173. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
  174. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  188. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
  189. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
  190. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
  191. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
  192. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
  193. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
  194. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
  195. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
  196. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
  197. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
  198. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
  199. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
  200. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
  201. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
  202. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
  203. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
  204. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
  205. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
  206. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  207. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
  208. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
  209. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
  210. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  211. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
  212. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
  213. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
  214. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
  215. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  216. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
  217. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
  218. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  219. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  220. claude_mpm/dashboard/static/svelte-build/index.html +9 -9
  221. claude_mpm/experimental/cli_enhancements.py +2 -1
  222. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  223. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  224. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  225. claude_mpm/hooks/claude_hooks/event_handlers.py +466 -136
  226. claude_mpm/hooks/claude_hooks/hook_handler.py +204 -104
  227. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  228. claude_mpm/hooks/claude_hooks/installer.py +291 -59
  229. claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
  230. claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
  231. claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
  232. claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
  233. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
  234. claude_mpm/hooks/claude_hooks/services/container.py +326 -0
  235. claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
  236. claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
  237. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
  238. claude_mpm/hooks/session_resume_hook.py +89 -1
  239. claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
  240. claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
  241. claude_mpm/init.py +22 -15
  242. claude_mpm/mcp/__init__.py +9 -0
  243. claude_mpm/mcp/google_workspace_server.py +610 -0
  244. claude_mpm/scripts/claude-hook-handler.sh +46 -19
  245. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  246. claude_mpm/services/agents/agent_selection_service.py +2 -2
  247. claude_mpm/services/agents/cache_git_manager.py +1 -1
  248. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
  249. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  250. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  251. claude_mpm/services/cli/__init__.py +3 -0
  252. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  253. claude_mpm/services/cli/session_resume_helper.py +10 -2
  254. claude_mpm/services/command_deployment_service.py +44 -26
  255. claude_mpm/services/delegation_detector.py +175 -0
  256. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  257. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  258. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  259. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  260. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  261. claude_mpm/services/diagnostics/models.py +14 -1
  262. claude_mpm/services/event_log.py +325 -0
  263. claude_mpm/services/hook_installer_service.py +77 -8
  264. claude_mpm/services/infrastructure/__init__.py +4 -0
  265. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  266. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  267. claude_mpm/services/mcp_config_manager.py +99 -19
  268. claude_mpm/services/mcp_service_registry.py +294 -0
  269. claude_mpm/services/monitor/daemon_manager.py +15 -4
  270. claude_mpm/services/monitor/management/lifecycle.py +8 -2
  271. claude_mpm/services/monitor/server.py +111 -16
  272. claude_mpm/services/pm_skills_deployer.py +261 -87
  273. claude_mpm/services/skills/git_skill_source_manager.py +130 -10
  274. claude_mpm/services/skills/selective_skill_deployer.py +142 -16
  275. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  276. claude_mpm/services/skills_deployer.py +31 -5
  277. claude_mpm/services/socketio/handlers/hook.py +14 -7
  278. claude_mpm/services/socketio/server/main.py +12 -4
  279. claude_mpm/skills/__init__.py +2 -1
  280. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  281. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  282. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  283. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  284. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  285. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  286. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  287. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  288. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  289. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  290. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  291. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  292. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  293. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  294. claude_mpm/skills/bundled/pm/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
  295. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  296. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  297. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  298. claude_mpm/skills/registry.py +295 -90
  299. claude_mpm/skills/skill_manager.py +4 -4
  300. claude_mpm-5.6.76.dist-info/METADATA +416 -0
  301. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/RECORD +312 -175
  302. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/WHEEL +1 -1
  303. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/entry_points.txt +2 -0
  304. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
  305. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
  306. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
  307. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
  308. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
  309. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
  310. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
  311. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
  312. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
  313. claude_mpm-5.4.85.dist-info/METADATA +0 -1023
  314. /claude_mpm/skills/bundled/pm/{pm-bug-reporting/pm-bug-reporting.md → mpm-bug-reporting/SKILL.md} +0 -0
  315. /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
  316. /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
  317. /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
  318. /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
  319. /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
  320. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE +0 -0
  321. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  322. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,296 @@
1
+ """Work queue management endpoints for MPM Commander API.
2
+
3
+ This module implements REST endpoints for managing work items
4
+ in project work queues.
5
+ """
6
+
7
+ from typing import Dict, List, Optional
8
+
9
+ from fastapi import APIRouter, HTTPException, Query, Request
10
+
11
+ from ...models.work import WorkPriority, WorkState
12
+ from ...work import WorkQueue
13
+ from ..schemas import CreateWorkRequest, WorkItemResponse
14
+
15
+ router = APIRouter()
16
+
17
+
18
+ def _get_registry(request: Request):
19
+ """Get registry instance from app.state."""
20
+ if not hasattr(request.app.state, "registry") or request.app.state.registry is None:
21
+ raise RuntimeError("Registry not initialized")
22
+ return request.app.state.registry
23
+
24
+
25
+ def _get_work_queues(request: Request) -> Dict:
26
+ """Get work queues dict from app.state (shared with daemon)."""
27
+ if (
28
+ not hasattr(request.app.state, "work_queues")
29
+ or request.app.state.work_queues is None
30
+ ):
31
+ raise RuntimeError("Work queues not initialized")
32
+ return request.app.state.work_queues
33
+
34
+
35
+ def _get_daemon(request: Request):
36
+ """Get daemon instance from app.state."""
37
+ if not hasattr(request.app.state, "daemon_instance"):
38
+ return None
39
+ return request.app.state.daemon_instance
40
+
41
+
42
+ def _work_item_to_response(work_item) -> WorkItemResponse:
43
+ """Convert WorkItem model to WorkItemResponse schema.
44
+
45
+ Args:
46
+ work_item: WorkItem instance
47
+
48
+ Returns:
49
+ WorkItemResponse with all work item data
50
+ """
51
+ return WorkItemResponse(
52
+ id=work_item.id,
53
+ project_id=work_item.project_id,
54
+ content=work_item.content,
55
+ state=work_item.state.value,
56
+ priority=work_item.priority.value,
57
+ created_at=work_item.created_at,
58
+ started_at=work_item.started_at,
59
+ completed_at=work_item.completed_at,
60
+ result=work_item.result,
61
+ error=work_item.error,
62
+ depends_on=work_item.depends_on,
63
+ metadata=work_item.metadata,
64
+ )
65
+
66
+
67
+ @router.post("/projects/{project_id}/work", response_model=WorkItemResponse)
68
+ async def add_work(
69
+ request: Request, project_id: str, work: CreateWorkRequest
70
+ ) -> WorkItemResponse:
71
+ """Add work item to project queue.
72
+
73
+ Args:
74
+ request: FastAPI request (for accessing app.state)
75
+ project_id: Project identifier
76
+ work: Work item creation request
77
+
78
+ Returns:
79
+ Created work item
80
+
81
+ Raises:
82
+ HTTPException: 404 if project not found
83
+
84
+ Example:
85
+ POST /api/projects/proj-123/work
86
+ Request: {
87
+ "content": "Implement OAuth2 authentication",
88
+ "priority": 3,
89
+ "depends_on": ["work-abc"]
90
+ }
91
+ Response: {
92
+ "id": "work-xyz",
93
+ "project_id": "proj-123",
94
+ "content": "Implement OAuth2 authentication",
95
+ "state": "queued",
96
+ "priority": 3,
97
+ ...
98
+ }
99
+ """
100
+ registry = _get_registry(request)
101
+ work_queues = _get_work_queues(request)
102
+ daemon = _get_daemon(request)
103
+
104
+ # Get project
105
+ project = registry.get(project_id)
106
+ if project is None:
107
+ raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
108
+
109
+ # Get or create work queue (shared with daemon)
110
+ import logging
111
+
112
+ logger = logging.getLogger(__name__)
113
+ logger.info(
114
+ f"work_queues dict id: {id(work_queues)}, keys: {list(work_queues.keys())}"
115
+ )
116
+
117
+ if project_id not in work_queues:
118
+ logger.info(f"Creating new work queue for {project_id}")
119
+ work_queues[project_id] = WorkQueue(project_id)
120
+ logger.info(f"After creation, work_queues keys: {list(work_queues.keys())}")
121
+
122
+ queue = work_queues[project_id]
123
+
124
+ # Convert priority int to enum
125
+ priority = WorkPriority(work.priority)
126
+
127
+ # Add work item
128
+ work_item = queue.add(
129
+ content=work.content, priority=priority, depends_on=work.depends_on
130
+ )
131
+
132
+ # Ensure daemon has a session for this project (creates if needed)
133
+ if daemon and not daemon.sessions.get(project_id):
134
+ # Session creation will be handled by daemon's main loop
135
+ # when it detects work in the queue
136
+ pass
137
+
138
+ return _work_item_to_response(work_item)
139
+
140
+
141
+ @router.get("/projects/{project_id}/work", response_model=List[WorkItemResponse])
142
+ async def list_work(
143
+ request: Request,
144
+ project_id: str,
145
+ state: Optional[str] = Query(None, description="Filter by state"),
146
+ ) -> List[WorkItemResponse]:
147
+ """List work items for project.
148
+
149
+ Args:
150
+ request: FastAPI request (for accessing app.state)
151
+ project_id: Project identifier
152
+ state: Optional state filter (pending, queued, in_progress, etc.)
153
+
154
+ Returns:
155
+ List of work items (may be empty)
156
+
157
+ Raises:
158
+ HTTPException: 404 if project not found, 400 if invalid state
159
+
160
+ Example:
161
+ GET /api/projects/proj-123/work
162
+ Response: [
163
+ {"id": "work-1", "state": "queued", ...},
164
+ {"id": "work-2", "state": "in_progress", ...}
165
+ ]
166
+
167
+ GET /api/projects/proj-123/work?state=queued
168
+ Response: [
169
+ {"id": "work-1", "state": "queued", ...}
170
+ ]
171
+ """
172
+ registry = _get_registry(request)
173
+ work_queues = _get_work_queues(request)
174
+
175
+ # Get project
176
+ project = registry.get(project_id)
177
+ if project is None:
178
+ raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
179
+
180
+ # Get work queue (shared with daemon)
181
+ if project_id not in work_queues:
182
+ # Return empty list if no work queue exists yet
183
+ return []
184
+
185
+ queue = work_queues[project_id]
186
+
187
+ # Parse state filter
188
+ state_filter = None
189
+ if state:
190
+ try:
191
+ state_filter = WorkState(state)
192
+ except ValueError as e:
193
+ raise HTTPException(
194
+ status_code=400,
195
+ detail=f"Invalid state: {state}. "
196
+ f"Valid states: {[s.value for s in WorkState]}",
197
+ ) from e
198
+
199
+ # List work items
200
+ items = queue.list(state=state_filter)
201
+
202
+ return [_work_item_to_response(item) for item in items]
203
+
204
+
205
+ @router.get("/projects/{project_id}/work/{work_id}", response_model=WorkItemResponse)
206
+ async def get_work(request: Request, project_id: str, work_id: str) -> WorkItemResponse:
207
+ """Get work item details.
208
+
209
+ Args:
210
+ request: FastAPI request (for accessing app.state)
211
+ project_id: Project identifier
212
+ work_id: Work item identifier
213
+
214
+ Returns:
215
+ Work item details
216
+
217
+ Raises:
218
+ HTTPException: 404 if project or work item not found
219
+
220
+ Example:
221
+ GET /api/projects/proj-123/work/work-xyz
222
+ Response: {
223
+ "id": "work-xyz",
224
+ "project_id": "proj-123",
225
+ "state": "in_progress",
226
+ ...
227
+ }
228
+ """
229
+ registry = _get_registry(request)
230
+ work_queues = _get_work_queues(request)
231
+
232
+ # Get project
233
+ project = registry.get(project_id)
234
+ if project is None:
235
+ raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
236
+
237
+ # Get work queue (shared with daemon)
238
+ if project_id not in work_queues:
239
+ raise HTTPException(status_code=404, detail="Work queue not found")
240
+
241
+ queue = work_queues[project_id]
242
+
243
+ # Get work item
244
+ work_item = queue.get(work_id)
245
+ if not work_item:
246
+ raise HTTPException(status_code=404, detail=f"Work item {work_id} not found")
247
+
248
+ return _work_item_to_response(work_item)
249
+
250
+
251
+ @router.post("/projects/{project_id}/work/{work_id}/cancel")
252
+ async def cancel_work(request: Request, project_id: str, work_id: str) -> dict:
253
+ """Cancel pending work item.
254
+
255
+ Args:
256
+ request: FastAPI request (for accessing app.state)
257
+ project_id: Project identifier
258
+ work_id: Work item identifier
259
+
260
+ Returns:
261
+ Success message
262
+
263
+ Raises:
264
+ HTTPException: 404 if project/work not found, 400 if invalid state
265
+
266
+ Example:
267
+ POST /api/projects/proj-123/work/work-xyz/cancel
268
+ Response: {"status": "cancelled", "id": "work-xyz"}
269
+ """
270
+ registry = _get_registry(request)
271
+ work_queues = _get_work_queues(request)
272
+
273
+ # Get project
274
+ project = registry.get(project_id)
275
+ if project is None:
276
+ raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
277
+
278
+ # Get work queue (shared with daemon)
279
+ if project_id not in work_queues:
280
+ raise HTTPException(status_code=404, detail="Work queue not found")
281
+
282
+ queue = work_queues[project_id]
283
+
284
+ # Cancel work item
285
+ if not queue.cancel(work_id):
286
+ work_item = queue.get(work_id)
287
+ if not work_item:
288
+ raise HTTPException(
289
+ status_code=404, detail=f"Work item {work_id} not found"
290
+ )
291
+ raise HTTPException(
292
+ status_code=400,
293
+ detail=f"Cannot cancel work item in state {work_item.state.value}",
294
+ )
295
+
296
+ return {"status": "cancelled", "id": work_id}
@@ -0,0 +1,186 @@
1
+ """Pydantic request/response schemas for MPM Commander API.
2
+
3
+ This module defines all request and response models for the REST API,
4
+ providing type safety and automatic validation.
5
+ """
6
+
7
+ from datetime import datetime
8
+ from typing import List, Optional
9
+
10
+ from pydantic import BaseModel, Field
11
+
12
+ # Request Models
13
+
14
+
15
+ class RegisterProjectRequest(BaseModel):
16
+ """Request to register a new project.
17
+
18
+ Attributes:
19
+ path: Filesystem path to project directory
20
+ project_id: Optional project identifier (UUID generated if omitted)
21
+ name: Optional display name (derived from path if omitted)
22
+ """
23
+
24
+ path: str = Field(..., description="Filesystem path to project")
25
+ project_id: Optional[str] = Field(
26
+ None, description="Project identifier (UUID generated if omitted)"
27
+ )
28
+ name: Optional[str] = Field(
29
+ None, description="Display name (derived from path if omitted)"
30
+ )
31
+
32
+
33
+ class CreateSessionRequest(BaseModel):
34
+ """Request to create a new tool session.
35
+
36
+ Attributes:
37
+ runtime: Runtime adapter to use (e.g., "claude-code")
38
+ agent_prompt: Optional custom system prompt for the agent
39
+ """
40
+
41
+ runtime: str = Field("claude-code", description="Runtime adapter to use")
42
+ agent_prompt: Optional[str] = Field(None, description="Custom system prompt")
43
+
44
+
45
+ class SendMessageRequest(BaseModel):
46
+ """Request to send a message to a project.
47
+
48
+ Attributes:
49
+ content: Message content
50
+ session_id: Target session ID (uses active session if omitted)
51
+ """
52
+
53
+ content: str = Field(..., description="Message content")
54
+ session_id: Optional[str] = Field(
55
+ None, description="Target session (uses active if omitted)"
56
+ )
57
+
58
+
59
+ # Response Models
60
+
61
+
62
+ class SessionResponse(BaseModel):
63
+ """Session information response.
64
+
65
+ Attributes:
66
+ id: Unique session identifier
67
+ project_id: Parent project ID
68
+ runtime: Runtime adapter name
69
+ tmux_target: Tmux pane target identifier
70
+ status: Current session status
71
+ created_at: Session creation timestamp
72
+ """
73
+
74
+ id: str
75
+ project_id: str
76
+ runtime: str
77
+ tmux_target: str
78
+ status: str
79
+ created_at: datetime
80
+
81
+
82
+ class ProjectResponse(BaseModel):
83
+ """Project information response.
84
+
85
+ Attributes:
86
+ id: Unique project identifier
87
+ path: Absolute filesystem path
88
+ name: Display name
89
+ state: Current project state
90
+ state_reason: Optional state reason (e.g., error message)
91
+ sessions: List of active sessions
92
+ pending_events_count: Number of pending events
93
+ last_activity: Last activity timestamp
94
+ created_at: Project registration timestamp
95
+ """
96
+
97
+ id: str
98
+ path: str
99
+ name: str
100
+ state: str
101
+ state_reason: Optional[str]
102
+ sessions: List[SessionResponse]
103
+ pending_events_count: int
104
+ last_activity: datetime
105
+ created_at: datetime
106
+
107
+
108
+ class MessageResponse(BaseModel):
109
+ """Conversation message response.
110
+
111
+ Attributes:
112
+ id: Unique message identifier
113
+ role: Message sender role (user, assistant, system, tool)
114
+ content: Message content
115
+ session_id: Associated session ID (if from tool)
116
+ timestamp: Message creation timestamp
117
+ """
118
+
119
+ id: str
120
+ role: str
121
+ content: str
122
+ session_id: Optional[str]
123
+ timestamp: datetime
124
+
125
+
126
+ class CreateWorkRequest(BaseModel):
127
+ """Request to create a work item.
128
+
129
+ Attributes:
130
+ content: Task/message to execute
131
+ priority: Priority level (1-4, where 4 is CRITICAL)
132
+ depends_on: Optional list of work item IDs that must complete first
133
+ """
134
+
135
+ content: str = Field(..., description="Task/message to execute")
136
+ priority: int = Field(
137
+ 2,
138
+ description="Priority level (1=LOW, 2=MEDIUM, 3=HIGH, 4=CRITICAL)",
139
+ ge=1,
140
+ le=4,
141
+ )
142
+ depends_on: Optional[List[str]] = Field(
143
+ None, description="Work item IDs that must complete first"
144
+ )
145
+
146
+
147
+ class WorkItemResponse(BaseModel):
148
+ """Work item information response.
149
+
150
+ Attributes:
151
+ id: Unique work item identifier
152
+ project_id: Parent project ID
153
+ content: Task/message content
154
+ state: Current state (pending, queued, in_progress, blocked, completed, failed, cancelled)
155
+ priority: Priority level (1-4)
156
+ created_at: Creation timestamp
157
+ started_at: Execution start timestamp (if started)
158
+ completed_at: Completion timestamp (if completed/failed)
159
+ result: Result message (if completed)
160
+ error: Error message (if failed)
161
+ depends_on: List of dependency work item IDs
162
+ metadata: Additional structured data
163
+ """
164
+
165
+ id: str
166
+ project_id: str
167
+ content: str
168
+ state: str
169
+ priority: int
170
+ created_at: datetime
171
+ started_at: Optional[datetime]
172
+ completed_at: Optional[datetime]
173
+ result: Optional[str]
174
+ error: Optional[str]
175
+ depends_on: List[str]
176
+ metadata: dict
177
+
178
+
179
+ class ErrorResponse(BaseModel):
180
+ """Error response with structured error information.
181
+
182
+ Attributes:
183
+ error: Error details with code and message
184
+ """
185
+
186
+ error: dict # {code: str, message: str}
@@ -0,0 +1,7 @@
1
+ """Commander chat interface."""
2
+
3
+ from .cli import run_commander
4
+ from .commands import Command, CommandParser, CommandType
5
+ from .repl import CommanderREPL
6
+
7
+ __all__ = ["Command", "CommandParser", "CommandType", "CommanderREPL", "run_commander"]
@@ -0,0 +1,149 @@
1
+ """Commander CLI entry point."""
2
+
3
+ import asyncio
4
+ import logging
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ from claude_mpm.commander.env_loader import load_env
10
+ from claude_mpm.commander.instance_manager import InstanceManager
11
+ from claude_mpm.commander.llm.openrouter_client import (
12
+ OpenRouterClient,
13
+ OpenRouterConfig,
14
+ )
15
+ from claude_mpm.commander.llm.summarizer import OutputSummarizer
16
+ from claude_mpm.commander.proxy.formatter import OutputFormatter
17
+ from claude_mpm.commander.proxy.output_handler import OutputHandler
18
+ from claude_mpm.commander.proxy.relay import OutputRelay
19
+ from claude_mpm.commander.session.manager import SessionManager
20
+ from claude_mpm.commander.tmux_orchestrator import TmuxOrchestrator
21
+
22
+ from .repl import CommanderREPL
23
+
24
+ # Load environment variables at module import
25
+ load_env()
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ @dataclass
31
+ class CommanderCLIConfig:
32
+ """Configuration for Commander CLI mode.
33
+
34
+ Attributes:
35
+ summarize_responses: Whether to use LLM to summarize instance responses
36
+ port: Port for internal services (reserved for future use)
37
+ state_dir: Directory for state persistence (optional)
38
+
39
+ Example:
40
+ >>> config = CommanderCLIConfig(summarize_responses=False)
41
+ """
42
+
43
+ summarize_responses: bool = True
44
+ port: int = 8765
45
+ state_dir: Optional[Path] = None
46
+
47
+
48
+ async def run_commander(
49
+ port: int = 8765,
50
+ state_dir: Optional[Path] = None,
51
+ config: Optional[CommanderCLIConfig] = None,
52
+ ) -> None:
53
+ """Run Commander in interactive mode.
54
+
55
+ Args:
56
+ port: Port for internal services (unused currently).
57
+ state_dir: Directory for state persistence (optional).
58
+ config: Commander CLI configuration (optional, uses defaults if None).
59
+
60
+ Example:
61
+ >>> asyncio.run(run_commander())
62
+ # Starts interactive Commander REPL
63
+ >>> config = CommanderCLIConfig(summarize_responses=False)
64
+ >>> asyncio.run(run_commander(config=config))
65
+ # Starts Commander without response summarization
66
+ """
67
+ # Use default config if not provided
68
+ if config is None:
69
+ config = CommanderCLIConfig(port=port, state_dir=state_dir)
70
+
71
+ # Setup logging - suppress noisy libraries
72
+ logging.basicConfig(
73
+ level=logging.INFO,
74
+ format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
75
+ )
76
+ # Suppress httpx request logging (very verbose)
77
+ logging.getLogger("httpx").setLevel(logging.WARNING)
78
+ logging.getLogger("httpcore").setLevel(logging.WARNING)
79
+
80
+ # Initialize components
81
+ logger.info("Initializing Commander...")
82
+
83
+ # Create tmux orchestrator
84
+ orchestrator = TmuxOrchestrator()
85
+
86
+ # Create instance manager
87
+ instance_manager = InstanceManager(orchestrator)
88
+
89
+ # Create session manager
90
+ session_manager = SessionManager()
91
+
92
+ # Try to initialize LLM client (optional)
93
+ llm_client: Optional[OpenRouterClient] = None
94
+ try:
95
+ llm_config = OpenRouterConfig()
96
+ llm_client = OpenRouterClient(llm_config)
97
+ logger.info("LLM client initialized")
98
+ except ValueError as e:
99
+ logger.warning(f"LLM client not available: {e}")
100
+ logger.warning("Output summarization will be disabled")
101
+
102
+ # Create output relay (optional)
103
+ output_relay: Optional[OutputRelay] = None
104
+ if llm_client:
105
+ try:
106
+ # Only create summarizer if summarize_responses is enabled
107
+ summarizer = None
108
+ if config.summarize_responses:
109
+ summarizer = OutputSummarizer(llm_client)
110
+ logger.info("Response summarization enabled")
111
+ else:
112
+ logger.info("Response summarization disabled")
113
+
114
+ handler = OutputHandler(orchestrator, summarizer)
115
+ formatter = OutputFormatter()
116
+ output_relay = OutputRelay(handler, formatter)
117
+ logger.info("Output relay initialized")
118
+ except Exception as e:
119
+ logger.warning(f"Output relay setup failed: {e}")
120
+
121
+ # Create REPL
122
+ repl = CommanderREPL(
123
+ instance_manager=instance_manager,
124
+ session_manager=session_manager,
125
+ output_relay=output_relay,
126
+ llm_client=llm_client,
127
+ )
128
+
129
+ # Run REPL
130
+ try:
131
+ await repl.run()
132
+ except KeyboardInterrupt:
133
+ logger.info("Commander interrupted by user")
134
+ except Exception as e:
135
+ logger.error(f"Commander error: {e}", exc_info=True)
136
+ finally:
137
+ # Cleanup
138
+ logger.info("Shutting down Commander...")
139
+ if output_relay:
140
+ await output_relay.stop_all()
141
+
142
+
143
+ def main() -> None:
144
+ """Entry point for command-line execution."""
145
+ asyncio.run(run_commander())
146
+
147
+
148
+ if __name__ == "__main__":
149
+ main()