claude-mpm 5.4.41__py3-none-any.whl → 5.6.23__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 (460) 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/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 +216 -0
  13. claude_mpm/cli/commands/configure.py +620 -21
  14. claude_mpm/cli/commands/configure_agent_display.py +3 -1
  15. claude_mpm/cli/commands/hook_errors.py +60 -60
  16. claude_mpm/cli/commands/monitor.py +2 -2
  17. claude_mpm/cli/commands/mpm_init/core.py +15 -8
  18. claude_mpm/cli/commands/profile.py +9 -10
  19. claude_mpm/cli/commands/run.py +35 -3
  20. claude_mpm/cli/commands/skill_source.py +51 -2
  21. claude_mpm/cli/commands/skills.py +182 -32
  22. claude_mpm/cli/executor.py +120 -16
  23. claude_mpm/cli/interactive/__init__.py +10 -0
  24. claude_mpm/cli/interactive/agent_wizard.py +30 -50
  25. claude_mpm/cli/interactive/questionary_styles.py +65 -0
  26. claude_mpm/cli/interactive/skill_selector.py +481 -0
  27. claude_mpm/cli/parsers/base_parser.py +76 -1
  28. claude_mpm/cli/parsers/commander_parser.py +116 -0
  29. claude_mpm/cli/parsers/profile_parser.py +0 -1
  30. claude_mpm/cli/parsers/run_parser.py +10 -0
  31. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  32. claude_mpm/cli/parsers/skills_parser.py +2 -3
  33. claude_mpm/cli/startup.py +527 -506
  34. claude_mpm/cli/startup_display.py +74 -6
  35. claude_mpm/cli/startup_logging.py +2 -2
  36. claude_mpm/cli/utils.py +7 -3
  37. claude_mpm/commander/__init__.py +78 -0
  38. claude_mpm/commander/adapters/__init__.py +60 -0
  39. claude_mpm/commander/adapters/auggie.py +260 -0
  40. claude_mpm/commander/adapters/base.py +288 -0
  41. claude_mpm/commander/adapters/claude_code.py +392 -0
  42. claude_mpm/commander/adapters/codex.py +237 -0
  43. claude_mpm/commander/adapters/communication.py +366 -0
  44. claude_mpm/commander/adapters/example_usage.py +310 -0
  45. claude_mpm/commander/adapters/mpm.py +389 -0
  46. claude_mpm/commander/adapters/registry.py +204 -0
  47. claude_mpm/commander/api/__init__.py +16 -0
  48. claude_mpm/commander/api/app.py +121 -0
  49. claude_mpm/commander/api/errors.py +133 -0
  50. claude_mpm/commander/api/routes/__init__.py +8 -0
  51. claude_mpm/commander/api/routes/events.py +184 -0
  52. claude_mpm/commander/api/routes/inbox.py +171 -0
  53. claude_mpm/commander/api/routes/messages.py +148 -0
  54. claude_mpm/commander/api/routes/projects.py +271 -0
  55. claude_mpm/commander/api/routes/sessions.py +226 -0
  56. claude_mpm/commander/api/routes/work.py +296 -0
  57. claude_mpm/commander/api/schemas.py +186 -0
  58. claude_mpm/commander/chat/__init__.py +7 -0
  59. claude_mpm/commander/chat/cli.py +146 -0
  60. claude_mpm/commander/chat/commands.py +96 -0
  61. claude_mpm/commander/chat/repl.py +310 -0
  62. claude_mpm/commander/config.py +51 -0
  63. claude_mpm/commander/config_loader.py +115 -0
  64. claude_mpm/commander/core/__init__.py +10 -0
  65. claude_mpm/commander/core/block_manager.py +325 -0
  66. claude_mpm/commander/core/response_manager.py +323 -0
  67. claude_mpm/commander/daemon.py +603 -0
  68. claude_mpm/commander/env_loader.py +59 -0
  69. claude_mpm/commander/events/__init__.py +26 -0
  70. claude_mpm/commander/events/manager.py +332 -0
  71. claude_mpm/commander/frameworks/__init__.py +12 -0
  72. claude_mpm/commander/frameworks/base.py +146 -0
  73. claude_mpm/commander/frameworks/claude_code.py +58 -0
  74. claude_mpm/commander/frameworks/mpm.py +62 -0
  75. claude_mpm/commander/inbox/__init__.py +16 -0
  76. claude_mpm/commander/inbox/dedup.py +128 -0
  77. claude_mpm/commander/inbox/inbox.py +224 -0
  78. claude_mpm/commander/inbox/models.py +70 -0
  79. claude_mpm/commander/instance_manager.py +450 -0
  80. claude_mpm/commander/llm/__init__.py +6 -0
  81. claude_mpm/commander/llm/openrouter_client.py +167 -0
  82. claude_mpm/commander/llm/summarizer.py +70 -0
  83. claude_mpm/commander/memory/__init__.py +45 -0
  84. claude_mpm/commander/memory/compression.py +347 -0
  85. claude_mpm/commander/memory/embeddings.py +230 -0
  86. claude_mpm/commander/memory/entities.py +310 -0
  87. claude_mpm/commander/memory/example_usage.py +290 -0
  88. claude_mpm/commander/memory/integration.py +325 -0
  89. claude_mpm/commander/memory/search.py +381 -0
  90. claude_mpm/commander/memory/store.py +657 -0
  91. claude_mpm/commander/models/__init__.py +18 -0
  92. claude_mpm/commander/models/events.py +121 -0
  93. claude_mpm/commander/models/project.py +162 -0
  94. claude_mpm/commander/models/work.py +214 -0
  95. claude_mpm/commander/parsing/__init__.py +20 -0
  96. claude_mpm/commander/parsing/extractor.py +132 -0
  97. claude_mpm/commander/parsing/output_parser.py +270 -0
  98. claude_mpm/commander/parsing/patterns.py +100 -0
  99. claude_mpm/commander/persistence/__init__.py +11 -0
  100. claude_mpm/commander/persistence/event_store.py +274 -0
  101. claude_mpm/commander/persistence/state_store.py +309 -0
  102. claude_mpm/commander/persistence/work_store.py +164 -0
  103. claude_mpm/commander/polling/__init__.py +13 -0
  104. claude_mpm/commander/polling/event_detector.py +104 -0
  105. claude_mpm/commander/polling/output_buffer.py +49 -0
  106. claude_mpm/commander/polling/output_poller.py +153 -0
  107. claude_mpm/commander/project_session.py +268 -0
  108. claude_mpm/commander/proxy/__init__.py +12 -0
  109. claude_mpm/commander/proxy/formatter.py +89 -0
  110. claude_mpm/commander/proxy/output_handler.py +191 -0
  111. claude_mpm/commander/proxy/relay.py +155 -0
  112. claude_mpm/commander/registry.py +410 -0
  113. claude_mpm/commander/runtime/__init__.py +10 -0
  114. claude_mpm/commander/runtime/executor.py +191 -0
  115. claude_mpm/commander/runtime/monitor.py +346 -0
  116. claude_mpm/commander/session/__init__.py +6 -0
  117. claude_mpm/commander/session/context.py +81 -0
  118. claude_mpm/commander/session/manager.py +59 -0
  119. claude_mpm/commander/tmux_orchestrator.py +361 -0
  120. claude_mpm/commander/web/__init__.py +1 -0
  121. claude_mpm/commander/work/__init__.py +30 -0
  122. claude_mpm/commander/work/executor.py +207 -0
  123. claude_mpm/commander/work/queue.py +405 -0
  124. claude_mpm/commander/workflow/__init__.py +27 -0
  125. claude_mpm/commander/workflow/event_handler.py +241 -0
  126. claude_mpm/commander/workflow/notifier.py +146 -0
  127. claude_mpm/commands/mpm-config.md +8 -0
  128. claude_mpm/commands/mpm-doctor.md +8 -0
  129. claude_mpm/commands/mpm-help.md +8 -0
  130. claude_mpm/commands/mpm-init.md +8 -0
  131. claude_mpm/commands/mpm-monitor.md +8 -0
  132. claude_mpm/commands/mpm-organize.md +8 -0
  133. claude_mpm/commands/mpm-postmortem.md +8 -0
  134. claude_mpm/commands/mpm-session-resume.md +9 -1
  135. claude_mpm/commands/mpm-status.md +8 -0
  136. claude_mpm/commands/mpm-ticket-view.md +8 -0
  137. claude_mpm/commands/mpm-version.md +8 -0
  138. claude_mpm/commands/mpm.md +8 -0
  139. claude_mpm/config/agent_presets.py +8 -7
  140. claude_mpm/config/skill_sources.py +16 -0
  141. claude_mpm/constants.py +1 -0
  142. claude_mpm/core/claude_runner.py +154 -2
  143. claude_mpm/core/config.py +35 -22
  144. claude_mpm/core/config_constants.py +74 -9
  145. claude_mpm/core/constants.py +56 -12
  146. claude_mpm/core/hook_manager.py +51 -3
  147. claude_mpm/core/interactive_session.py +12 -11
  148. claude_mpm/core/logger.py +26 -9
  149. claude_mpm/core/logging_utils.py +35 -11
  150. claude_mpm/core/network_config.py +148 -0
  151. claude_mpm/core/oneshot_session.py +7 -6
  152. claude_mpm/core/optimized_startup.py +3 -1
  153. claude_mpm/core/output_style_manager.py +63 -18
  154. claude_mpm/core/shared/config_loader.py +3 -1
  155. claude_mpm/core/socketio_pool.py +13 -5
  156. claude_mpm/core/unified_config.py +54 -8
  157. claude_mpm/core/unified_paths.py +95 -90
  158. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  159. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  160. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/1WZnGYqX.js +24 -0
  161. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/67pF3qNn.js +1 -0
  162. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/6RxdMKe4.js +1 -0
  163. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/8cZrfX0h.js +60 -0
  164. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/9a6T2nm-.js +7 -0
  165. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B443AUzu.js +1 -0
  166. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B8AwtY2H.js +1 -0
  167. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BF15LAsF.js +1 -0
  168. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
  169. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BRcwIQNr.js +4 -0
  170. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uj46x2Wr.js → BSNlmTZj.js} +1 -1
  171. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BV6nKitt.js +43 -0
  172. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BViJ8lZt.js +128 -0
  173. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BcQ-Q0FE.js +1 -0
  174. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bpyvgze_.js +30 -0
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C3rbW_a-.js +1 -0
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C8WYN38h.js +1 -0
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C9I8FlXH.js +61 -0
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIQcWgO2.js +36 -0
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIctN7YN.js +7 -0
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CKrS_JZW.js +145 -0
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CR6P9C4A.js +89 -0
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRRR9MD_.js +2 -0
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CSXtMOf0.js +1 -0
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CT-sbxSk.js +1 -0
  188. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWm6DJsp.js +1 -0
  189. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
  190. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CpqQ1Kzn.js +1 -0
  191. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
  192. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D2nGpDRe.js +1 -0
  193. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9iCMida.js +267 -0
  194. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9ykgMoY.js +10 -0
  195. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DL2Ldur1.js +1 -0
  196. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DPfltzjH.js +165 -0
  197. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{N4qtv3Hx.js → DR8nis88.js} +2 -2
  198. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUliQN2b.js +1 -0
  199. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
  200. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DXlhR01x.js +122 -0
  201. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D_lyTybS.js +1 -0
  202. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DngoTTgh.js +1 -0
  203. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DqkmHtDC.js +220 -0
  204. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DsDh8EYs.js +1 -0
  205. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DypDmXgd.js +139 -0
  206. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
  207. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/IPYC-LnN.js +162 -0
  208. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  209. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JpevfAFt.js +68 -0
  210. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DjhvlsAc.js → NqQ1dWOy.js} +1 -1
  211. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/R8CEIRAd.js +2 -0
  212. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Zxy7qc-l.js +64 -0
  213. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  214. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/qtd3IeO4.js +15 -0
  215. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ulBFON_C.js +65 -0
  216. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/wQVh1CoA.js +10 -0
  217. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.Dr7t0z2J.js +2 -0
  218. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  219. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.CAGBuiOw.js → 0.RgBboRvH.js} +1 -1
  220. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DG-KkbDf.js +1 -0
  221. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  222. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  223. claude_mpm/dashboard/static/svelte-build/index.html +11 -11
  224. claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
  225. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
  226. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
  227. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
  228. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
  229. claude_mpm/experimental/cli_enhancements.py +2 -1
  230. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  231. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  232. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  233. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  234. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  235. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  236. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  237. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  238. claude_mpm/hooks/claude_hooks/event_handlers.py +305 -87
  239. claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
  240. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  241. claude_mpm/hooks/claude_hooks/installer.py +116 -8
  242. claude_mpm/hooks/claude_hooks/memory_integration.py +51 -31
  243. claude_mpm/hooks/claude_hooks/response_tracking.py +42 -59
  244. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  245. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  246. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  247. claude_mpm/hooks/claude_hooks/services/connection_manager.py +39 -24
  248. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +36 -103
  249. claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
  250. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +73 -75
  251. claude_mpm/hooks/kuzu_memory_hook.py +5 -5
  252. claude_mpm/hooks/session_resume_hook.py +89 -1
  253. claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
  254. claude_mpm/init.py +215 -2
  255. claude_mpm/scripts/claude-hook-handler.sh +43 -16
  256. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  257. claude_mpm/services/agents/agent_selection_service.py +2 -2
  258. claude_mpm/services/agents/cache_git_manager.py +1 -1
  259. claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -1
  260. claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
  261. claude_mpm/services/agents/deployment/agent_template_builder.py +37 -17
  262. claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
  263. claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
  264. claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
  265. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +36 -8
  266. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +50 -26
  267. claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
  268. claude_mpm/services/agents/git_source_manager.py +21 -2
  269. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  270. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  271. claude_mpm/services/agents/sources/git_source_sync_service.py +116 -5
  272. claude_mpm/services/agents/startup_sync.py +5 -2
  273. claude_mpm/services/cli/__init__.py +3 -0
  274. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  275. claude_mpm/services/cli/session_resume_helper.py +10 -2
  276. claude_mpm/services/delegation_detector.py +175 -0
  277. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  278. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  279. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  280. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  281. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  282. claude_mpm/services/diagnostics/models.py +14 -1
  283. claude_mpm/services/event_log.py +325 -0
  284. claude_mpm/services/infrastructure/__init__.py +4 -0
  285. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  286. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  287. claude_mpm/services/monitor/daemon_manager.py +15 -4
  288. claude_mpm/services/monitor/management/lifecycle.py +8 -3
  289. claude_mpm/services/monitor/server.py +106 -16
  290. claude_mpm/services/pm_skills_deployer.py +302 -94
  291. claude_mpm/services/profile_manager.py +10 -4
  292. claude_mpm/services/skills/git_skill_source_manager.py +192 -29
  293. claude_mpm/services/skills/selective_skill_deployer.py +211 -46
  294. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  295. claude_mpm/services/skills_deployer.py +192 -70
  296. claude_mpm/services/socketio/handlers/hook.py +14 -7
  297. claude_mpm/services/socketio/server/main.py +12 -4
  298. claude_mpm/skills/__init__.py +2 -1
  299. claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
  300. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
  301. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
  302. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
  303. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
  304. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
  305. claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
  306. claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
  307. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
  308. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
  309. claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
  310. claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
  311. claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
  312. claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
  313. claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
  314. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
  315. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
  316. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
  317. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
  318. claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
  319. claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
  320. claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
  321. claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
  322. claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
  323. claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
  324. claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
  325. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
  326. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
  327. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
  328. claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
  329. claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
  330. claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
  331. claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
  332. claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
  333. claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
  334. claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
  335. claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
  336. claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
  337. claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
  338. claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
  339. claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
  340. claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
  341. claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
  342. claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
  343. claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
  344. claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
  345. claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
  346. claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
  347. claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
  348. claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
  349. claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
  350. claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
  351. claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
  352. claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
  353. claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
  354. claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
  355. claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
  356. claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
  357. claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
  358. claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
  359. claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
  360. claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
  361. claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
  362. claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
  363. claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
  364. claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
  365. claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
  366. claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
  367. claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
  368. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  369. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  370. claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
  371. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  372. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  373. claude_mpm/skills/bundled/pm/mpm-delegation-patterns/SKILL.md +167 -0
  374. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  375. claude_mpm/skills/bundled/pm/mpm-git-file-tracking/SKILL.md +113 -0
  376. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  377. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  378. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  379. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  380. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  381. claude_mpm/skills/bundled/pm/mpm-pr-workflow/SKILL.md +124 -0
  382. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  383. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  384. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  385. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  386. claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
  387. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  388. claude_mpm/skills/bundled/pm/mpm-ticketing-integration/SKILL.md +154 -0
  389. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  390. claude_mpm/skills/bundled/pm/mpm-verification-protocols/SKILL.md +198 -0
  391. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  392. claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
  393. claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
  394. claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
  395. claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
  396. claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
  397. claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
  398. claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
  399. claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
  400. claude_mpm/skills/bundled/security-scanning.md +112 -0
  401. claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
  402. claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
  403. claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
  404. claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
  405. claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
  406. claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
  407. claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
  408. claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
  409. claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
  410. claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
  411. claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
  412. claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
  413. claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
  414. claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
  415. claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
  416. claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
  417. claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
  418. claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
  419. claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
  420. claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
  421. claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
  422. claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
  423. claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
  424. claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
  425. claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
  426. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
  427. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
  428. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
  429. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
  430. claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
  431. claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
  432. claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
  433. claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
  434. claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
  435. claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
  436. claude_mpm/skills/registry.py +295 -90
  437. claude_mpm/skills/skill_manager.py +29 -23
  438. claude_mpm/templates/.pre-commit-config.yaml +112 -0
  439. claude_mpm/utils/agent_dependency_loader.py +103 -4
  440. claude_mpm/utils/robust_installer.py +45 -24
  441. claude_mpm-5.6.23.dist-info/METADATA +393 -0
  442. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/RECORD +447 -149
  443. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +0 -1
  444. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +0 -1
  445. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +0 -1
  446. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +0 -1
  447. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +0 -1
  448. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +0 -2
  449. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +0 -2
  450. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +0 -1
  451. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +0 -1
  452. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +0 -10
  453. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  454. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  455. claude_mpm-5.4.41.dist-info/METADATA +0 -998
  456. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/WHEEL +0 -0
  457. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/entry_points.txt +0 -0
  458. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/licenses/LICENSE +0 -0
  459. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  460. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.23.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,603 @@
1
+ """Commander daemon for autonomous multi-project orchestration.
2
+
3
+ This module implements the main daemon process that coordinates multiple
4
+ projects, manages their lifecycles, and handles graceful shutdown.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ import signal
10
+ from typing import Dict, Optional
11
+
12
+ import uvicorn
13
+
14
+ from .api.app import (
15
+ app,
16
+ )
17
+ from .config import DaemonConfig
18
+ from .core.block_manager import BlockManager
19
+ from .env_loader import load_env
20
+ from .events.manager import EventManager
21
+ from .inbox import Inbox
22
+ from .models.events import EventStatus
23
+ from .parsing.output_parser import OutputParser
24
+ from .persistence import EventStore, StateStore
25
+ from .project_session import ProjectSession, SessionState
26
+ from .registry import ProjectRegistry
27
+ from .runtime.monitor import RuntimeMonitor
28
+ from .tmux_orchestrator import TmuxOrchestrator
29
+ from .work.executor import WorkExecutor
30
+ from .work.queue import WorkQueue
31
+ from .workflow.event_handler import EventHandler
32
+
33
+ # Load environment variables at module import
34
+ load_env()
35
+
36
+ logger = logging.getLogger(__name__)
37
+
38
+
39
+ class CommanderDaemon:
40
+ """Main daemon process for MPM Commander.
41
+
42
+ Orchestrates multiple projects, manages their sessions, handles events,
43
+ and provides REST API for external control.
44
+
45
+ Attributes:
46
+ config: Daemon configuration
47
+ registry: Project registry
48
+ orchestrator: Tmux orchestrator
49
+ event_manager: Event manager
50
+ inbox: Event inbox
51
+ sessions: Active project sessions by project_id
52
+ work_queues: Work queues by project_id
53
+ work_executors: Work executors by project_id
54
+ block_manager: Block manager for automatic work blocking
55
+ runtime_monitor: Runtime monitor for output monitoring
56
+ event_handler: Event handler for blocking event workflow
57
+ state_store: StateStore for project/session persistence
58
+ event_store: EventStore for event queue persistence
59
+ running: Whether daemon is currently running
60
+
61
+ Example:
62
+ >>> config = DaemonConfig(port=8765)
63
+ >>> daemon = CommanderDaemon(config)
64
+ >>> await daemon.start()
65
+ >>> # Daemon runs until stopped
66
+ >>> await daemon.stop()
67
+ """
68
+
69
+ def __init__(self, config: DaemonConfig):
70
+ """Initialize Commander daemon.
71
+
72
+ Args:
73
+ config: Daemon configuration
74
+
75
+ Raises:
76
+ ValueError: If config is invalid
77
+ """
78
+ if config is None:
79
+ raise ValueError("Config cannot be None")
80
+
81
+ self.config = config
82
+ self.registry = ProjectRegistry()
83
+ self.orchestrator = TmuxOrchestrator()
84
+ self.event_manager = EventManager()
85
+ self.inbox = Inbox(self.event_manager, self.registry)
86
+ self.sessions: Dict[str, ProjectSession] = {}
87
+ self.work_queues: Dict[str, WorkQueue] = {}
88
+ self.work_executors: Dict[str, WorkExecutor] = {}
89
+ self._running = False
90
+ self._server_task: Optional[asyncio.Task] = None
91
+ self._main_loop_task: Optional[asyncio.Task] = None
92
+
93
+ # Initialize persistence stores
94
+ self.state_store = StateStore(config.state_dir)
95
+ self.event_store = EventStore(config.state_dir)
96
+
97
+ # Initialize BlockManager with work queues and executors
98
+ self.block_manager = BlockManager(
99
+ event_manager=self.event_manager,
100
+ work_queues=self.work_queues,
101
+ work_executors=self.work_executors,
102
+ )
103
+
104
+ # Initialize RuntimeMonitor with BlockManager
105
+ parser = OutputParser(self.event_manager)
106
+ self.runtime_monitor = RuntimeMonitor(
107
+ orchestrator=self.orchestrator,
108
+ parser=parser,
109
+ event_manager=self.event_manager,
110
+ poll_interval=config.poll_interval,
111
+ block_manager=self.block_manager,
112
+ )
113
+
114
+ # Initialize EventHandler with BlockManager
115
+ self.event_handler = EventHandler(
116
+ inbox=self.inbox,
117
+ session_manager=self.sessions,
118
+ block_manager=self.block_manager,
119
+ )
120
+
121
+ # Configure logging
122
+ logging.basicConfig(
123
+ level=getattr(logging, config.log_level.upper()),
124
+ format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
125
+ datefmt="%Y-%m-%d %H:%M:%S",
126
+ )
127
+
128
+ logger.info(
129
+ f"Initialized CommanderDaemon (host={config.host}, "
130
+ f"port={config.port}, state_dir={config.state_dir})"
131
+ )
132
+
133
+ @property
134
+ def is_running(self) -> bool:
135
+ """Check if daemon is running.
136
+
137
+ Returns:
138
+ True if daemon main loop is active
139
+ """
140
+ return self._running
141
+
142
+ async def start(self) -> None:
143
+ """Start daemon and all subsystems.
144
+
145
+ Initializes:
146
+ - Load state from disk (projects, sessions, events)
147
+ - Signal handlers for graceful shutdown
148
+ - REST API server
149
+ - Main daemon loop
150
+ - Tmux session for project management
151
+
152
+ Raises:
153
+ RuntimeError: If daemon already running
154
+ """
155
+ if self._running:
156
+ raise RuntimeError("Daemon already running")
157
+
158
+ logger.info("Starting Commander daemon...")
159
+ self._running = True
160
+
161
+ # Load state from disk
162
+ await self._load_state()
163
+
164
+ # Set up signal handlers
165
+ self._setup_signal_handlers()
166
+
167
+ # Inject daemon instances into API app.state (BEFORE lifespan runs)
168
+ app.state.registry = self.registry
169
+ app.state.tmux = self.orchestrator
170
+ app.state.event_manager = self.event_manager
171
+ app.state.inbox = self.inbox
172
+ app.state.work_queues = self.work_queues
173
+ app.state.daemon_instance = self
174
+ app.state.session_manager = self.sessions
175
+ app.state.event_handler = self.event_handler
176
+ logger.info(f"Injected work_queues dict id: {id(self.work_queues)}")
177
+
178
+ # Start API server in background
179
+ logger.info(f"Starting API server on {self.config.host}:{self.config.port}")
180
+ config_uvicorn = uvicorn.Config(
181
+ app,
182
+ host=self.config.host,
183
+ port=self.config.port,
184
+ log_level=self.config.log_level.lower(),
185
+ )
186
+ server = uvicorn.Server(config_uvicorn)
187
+ self._server_task = asyncio.create_task(server.serve())
188
+
189
+ # Create tmux session for projects
190
+ if not self.orchestrator.session_exists():
191
+ self.orchestrator.create_session()
192
+ logger.info("Created tmux session for project management")
193
+
194
+ # Start main daemon loop
195
+ logger.info("Starting main daemon loop")
196
+ self._main_loop_task = asyncio.create_task(self.run())
197
+
198
+ logger.info("Commander daemon started successfully")
199
+
200
+ async def stop(self) -> None:
201
+ """Graceful shutdown with cleanup.
202
+
203
+ Stops all active sessions, persists state, and shuts down API server.
204
+ """
205
+ if not self._running:
206
+ logger.warning("Daemon not running, nothing to stop")
207
+ return
208
+
209
+ logger.info("Stopping Commander daemon...")
210
+ self._running = False
211
+
212
+ # Stop all project sessions
213
+ for project_id, session in list(self.sessions.items()):
214
+ try:
215
+ logger.info(f"Stopping session for project {project_id}")
216
+ await session.stop()
217
+ except Exception as e:
218
+ logger.error(f"Error stopping session {project_id}: {e}")
219
+
220
+ # Clear BlockManager project mappings
221
+ for project_id in list(self.work_queues.keys()):
222
+ try:
223
+ removed = self.block_manager.clear_project_mappings(project_id)
224
+ logger.debug(
225
+ f"Cleared {removed} work mappings for project {project_id}"
226
+ )
227
+ except Exception as e:
228
+ logger.error(f"Error clearing mappings for {project_id}: {e}")
229
+
230
+ # Cancel main loop task
231
+ if self._main_loop_task and not self._main_loop_task.done():
232
+ self._main_loop_task.cancel()
233
+ try:
234
+ await self._main_loop_task
235
+ except asyncio.CancelledError:
236
+ pass
237
+
238
+ # Persist state to disk
239
+ await self._save_state()
240
+
241
+ # Stop API server
242
+ if self._server_task and not self._server_task.done():
243
+ self._server_task.cancel()
244
+ try:
245
+ await self._server_task
246
+ except asyncio.CancelledError:
247
+ pass
248
+
249
+ logger.info("Commander daemon stopped")
250
+
251
+ async def run(self) -> None:
252
+ """Main daemon loop.
253
+
254
+ Continuously polls for:
255
+ - Resolved events to resume paused sessions
256
+ - New work items to execute
257
+ - Project state changes
258
+ - Periodic state persistence
259
+
260
+ Runs until _running flag is set to False.
261
+ """
262
+ logger.info("Main daemon loop starting")
263
+
264
+ # Track last save time for periodic persistence
265
+ last_save_time = asyncio.get_event_loop().time()
266
+
267
+ while self._running:
268
+ try:
269
+ logger.info(f"🔄 Main loop iteration (running={self._running})")
270
+ logger.info(
271
+ f"work_queues dict id: {id(self.work_queues)}, keys: {list(self.work_queues.keys())}"
272
+ )
273
+
274
+ # Check for resolved events and resume sessions
275
+ await self._check_and_resume_sessions()
276
+
277
+ # Check each ProjectSession for runnable work
278
+ logger.info(
279
+ f"Checking for pending work across {len(self.work_queues)} queues"
280
+ )
281
+ await self._execute_pending_work()
282
+
283
+ # Periodic state persistence
284
+ current_time = asyncio.get_event_loop().time()
285
+ if current_time - last_save_time >= self.config.save_interval:
286
+ try:
287
+ await self._save_state()
288
+ last_save_time = current_time
289
+ except Exception as e:
290
+ logger.error(f"Error during periodic save: {e}", exc_info=True)
291
+
292
+ # Sleep to prevent tight loop
293
+ await asyncio.sleep(self.config.poll_interval)
294
+
295
+ except asyncio.CancelledError:
296
+ logger.info("Main loop cancelled")
297
+ break
298
+ except Exception as e:
299
+ logger.error(f"Error in main loop: {e}", exc_info=True)
300
+ # Continue running despite errors
301
+ await asyncio.sleep(self.config.poll_interval)
302
+
303
+ logger.info("Main daemon loop stopped")
304
+
305
+ def _setup_signal_handlers(self) -> None:
306
+ """Set up signal handlers for graceful shutdown.
307
+
308
+ Registers handlers for SIGINT and SIGTERM that trigger
309
+ daemon shutdown via asyncio event loop.
310
+
311
+ Note: Signal handlers can only be registered from the main thread.
312
+ If called from a background thread, registration is skipped.
313
+ """
314
+ import threading
315
+
316
+ # Signal handlers can only be registered from the main thread
317
+ if threading.current_thread() is not threading.main_thread():
318
+ logger.info("Running in background thread - signal handlers skipped")
319
+ return
320
+
321
+ def handle_signal(signum: int, frame) -> None:
322
+ """Handle shutdown signal.
323
+
324
+ Args:
325
+ signum: Signal number
326
+ frame: Current stack frame
327
+ """
328
+ sig_name = signal.Signals(signum).name
329
+ logger.info(f"Received {sig_name}, initiating graceful shutdown...")
330
+
331
+ # Schedule shutdown in event loop
332
+ if self._running:
333
+ asyncio.create_task(self.stop())
334
+
335
+ # Register signal handlers
336
+ signal.signal(signal.SIGINT, handle_signal)
337
+ signal.signal(signal.SIGTERM, handle_signal)
338
+
339
+ logger.debug("Signal handlers configured (SIGINT, SIGTERM)")
340
+
341
+ def get_or_create_session(self, project_id: str) -> ProjectSession:
342
+ """Get existing session or create new one for project.
343
+
344
+ Args:
345
+ project_id: Project identifier
346
+
347
+ Returns:
348
+ ProjectSession for the project
349
+
350
+ Raises:
351
+ ValueError: If project not found in registry
352
+ """
353
+ if project_id in self.sessions:
354
+ return self.sessions[project_id]
355
+
356
+ project = self.registry.get(project_id)
357
+ if project is None:
358
+ raise ValueError(f"Project not found: {project_id}")
359
+
360
+ # Create work queue for project if not exists
361
+ if project_id not in self.work_queues:
362
+ self.work_queues[project_id] = WorkQueue(project_id)
363
+ logger.debug(f"Created work queue for project {project_id}")
364
+
365
+ # Create work executor for project if not exists
366
+ if project_id not in self.work_executors:
367
+ from .runtime.executor import RuntimeExecutor
368
+
369
+ runtime_executor = RuntimeExecutor(self.orchestrator)
370
+ self.work_executors[project_id] = WorkExecutor(
371
+ runtime=runtime_executor, queue=self.work_queues[project_id]
372
+ )
373
+ logger.debug(f"Created work executor for project {project_id}")
374
+
375
+ session = ProjectSession(
376
+ project=project,
377
+ orchestrator=self.orchestrator,
378
+ monitor=self.runtime_monitor,
379
+ )
380
+ self.sessions[project_id] = session
381
+
382
+ logger.info(f"Created new session for project {project_id}")
383
+ return session
384
+
385
+ async def _load_state(self) -> None:
386
+ """Load state from disk (projects, sessions, events).
387
+
388
+ Called on daemon startup to restore previous state.
389
+ Handles missing or corrupt files gracefully.
390
+ """
391
+ logger.info("Loading state from disk...")
392
+
393
+ # Load projects
394
+ try:
395
+ projects = await self.state_store.load_projects()
396
+ for project in projects:
397
+ # Re-register projects (bypassing validation for already-registered paths)
398
+ self.registry._projects[project.id] = project
399
+ self.registry._path_index[project.path] = project.id
400
+ logger.info(f"Restored {len(projects)} projects")
401
+ except Exception as e:
402
+ logger.error(f"Failed to load projects: {e}", exc_info=True)
403
+
404
+ # Load sessions
405
+ try:
406
+ session_states = await self.state_store.load_sessions()
407
+ for project_id, state_dict in session_states.items():
408
+ # Only restore sessions for projects we have
409
+ if project_id in self.registry._projects:
410
+ project = self.registry.get(project_id)
411
+ session = ProjectSession(project, self.orchestrator)
412
+
413
+ # Restore session state (but don't restart runtime - manual resume)
414
+ try:
415
+ session._state = SessionState(state_dict.get("state", "idle"))
416
+ session.active_pane = state_dict.get("pane_target")
417
+ session.pause_reason = state_dict.get("paused_event_id")
418
+ self.sessions[project_id] = session
419
+ except Exception as e:
420
+ logger.warning(
421
+ f"Failed to restore session for {project_id}: {e}"
422
+ )
423
+ logger.info(f"Restored {len(self.sessions)} sessions")
424
+ except Exception as e:
425
+ logger.error(f"Failed to load sessions: {e}", exc_info=True)
426
+
427
+ # Load events
428
+ try:
429
+ events = await self.event_store.load_events()
430
+ for event in events:
431
+ self.event_manager.add_event(event)
432
+ logger.info(f"Restored {len(events)} events")
433
+ except Exception as e:
434
+ logger.error(f"Failed to load events: {e}", exc_info=True)
435
+
436
+ logger.info("State loading complete")
437
+
438
+ async def _save_state(self) -> None:
439
+ """Save state to disk (projects, sessions, events).
440
+
441
+ Called on daemon shutdown and periodically during runtime.
442
+ Uses atomic writes to prevent corruption.
443
+ """
444
+ logger.debug("Saving state to disk...")
445
+
446
+ try:
447
+ # Save projects
448
+ await self.state_store.save_projects(self.registry)
449
+
450
+ # Save sessions
451
+ await self.state_store.save_sessions(self.sessions)
452
+
453
+ # Save events
454
+ await self.event_store.save_events(self.inbox)
455
+
456
+ logger.debug("State saved successfully")
457
+ except Exception as e:
458
+ logger.error(f"Failed to save state: {e}", exc_info=True)
459
+
460
+ async def _check_and_resume_sessions(self) -> None:
461
+ """Check for resolved events and resume paused sessions.
462
+
463
+ Iterates through all paused sessions, checks if their blocking events
464
+ have been resolved, and resumes execution if ready.
465
+ """
466
+ for project_id, session in list(self.sessions.items()):
467
+ # Skip non-paused sessions
468
+ if session.state != SessionState.PAUSED:
469
+ continue
470
+
471
+ # Check if pause reason (event ID) is resolved
472
+ if not session.pause_reason:
473
+ logger.warning(f"Session {project_id} paused with no reason, resuming")
474
+ await session.resume()
475
+ continue
476
+
477
+ # Check if event is resolved
478
+ event = self.event_manager.get(session.pause_reason)
479
+ if event and event.status == EventStatus.RESOLVED:
480
+ logger.info(
481
+ f"Event {event.id} resolved, resuming session for {project_id}"
482
+ )
483
+ await session.resume()
484
+
485
+ # Unblock any work items that were blocked by this event
486
+ if project_id in self.work_executors:
487
+ executor = self.work_executors[project_id]
488
+ queue = self.work_queues[project_id]
489
+
490
+ # Find work items blocked by this event
491
+ blocked_items = [
492
+ item
493
+ for item in queue.list()
494
+ if item.state.value == "blocked"
495
+ and item.metadata.get("block_reason") == event.id
496
+ ]
497
+
498
+ for item in blocked_items:
499
+ await executor.handle_unblock(item.id)
500
+ logger.info(f"Unblocked work item {item.id}")
501
+
502
+ async def _execute_pending_work(self) -> None:
503
+ """Execute pending work for all ready sessions.
504
+
505
+ Scans all work queues for pending work. For projects with work but no session,
506
+ auto-creates a session. Then executes the next available work item via WorkExecutor.
507
+ """
508
+ # First pass: Auto-create and start sessions for projects with pending work
509
+ for project_id, queue in list(self.work_queues.items()):
510
+ logger.info(
511
+ f"Checking queue for {project_id}: pending={queue.pending_count}"
512
+ )
513
+ # Skip if no pending work
514
+ if queue.pending_count == 0:
515
+ continue
516
+
517
+ # Auto-create session if needed
518
+ if project_id not in self.sessions:
519
+ try:
520
+ logger.info(
521
+ f"Auto-creating session for project {project_id} with pending work"
522
+ )
523
+ session = self.get_or_create_session(project_id)
524
+
525
+ # Start the session so it's ready for work
526
+ if session.state.value == "idle":
527
+ logger.info(f"Auto-starting session for {project_id}")
528
+ await session.start()
529
+ except Exception as e:
530
+ logger.error(
531
+ f"Failed to auto-create/start session for {project_id}: {e}",
532
+ exc_info=True,
533
+ )
534
+ continue
535
+
536
+ # Second pass: Execute work for ready sessions
537
+ for project_id, session in list(self.sessions.items()):
538
+ # Skip sessions that aren't ready for work
539
+ if not session.is_ready():
540
+ continue
541
+
542
+ # Skip if no work queue exists
543
+ if project_id not in self.work_queues:
544
+ continue
545
+
546
+ # Get work executor for project
547
+ executor = self.work_executors.get(project_id)
548
+ if not executor:
549
+ logger.warning(
550
+ f"No work executor found for project {project_id}, skipping"
551
+ )
552
+ continue
553
+
554
+ # Check if there's work available
555
+ queue = self.work_queues[project_id]
556
+ if queue.pending_count == 0:
557
+ continue
558
+
559
+ # Try to execute next work item
560
+ try:
561
+ # Pass the session's active pane for execution
562
+ executed = await executor.execute_next(pane_target=session.active_pane)
563
+ if executed:
564
+ logger.info(f"Started work execution for project {project_id}")
565
+ except Exception as e:
566
+ logger.error(
567
+ f"Error executing work for project {project_id}: {e}",
568
+ exc_info=True,
569
+ )
570
+
571
+
572
+ async def main(config: Optional[DaemonConfig] = None) -> None:
573
+ """Main entry point for running the daemon.
574
+
575
+ Args:
576
+ config: Optional daemon configuration (uses defaults if None)
577
+
578
+ Example:
579
+ >>> import asyncio
580
+ >>> asyncio.run(main())
581
+ """
582
+ if config is None:
583
+ config = DaemonConfig()
584
+
585
+ daemon = CommanderDaemon(config)
586
+
587
+ try:
588
+ await daemon.start()
589
+
590
+ # Keep daemon running until stopped
591
+ while daemon.is_running:
592
+ await asyncio.sleep(1)
593
+
594
+ except KeyboardInterrupt:
595
+ logger.info("Received KeyboardInterrupt")
596
+ except Exception as e:
597
+ logger.error(f"Daemon error: {e}", exc_info=True)
598
+ finally:
599
+ await daemon.stop()
600
+
601
+
602
+ if __name__ == "__main__":
603
+ asyncio.run(main())
@@ -0,0 +1,59 @@
1
+ """Environment variable loader for Commander.
2
+
3
+ This module handles automatic loading of .env and .env.local files
4
+ at Commander startup. Environment files are loaded with the following precedence:
5
+ 1. Existing environment variables (not overridden)
6
+ 2. .env.local (local overrides)
7
+ 3. .env (defaults)
8
+
9
+ Example:
10
+ >>> from claude_mpm.commander.env_loader import load_env
11
+ >>> load_env()
12
+ # Automatically loads .env.local and .env from project root
13
+ """
14
+
15
+ import logging
16
+ from pathlib import Path
17
+
18
+ from dotenv import load_dotenv
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ def load_env() -> None:
24
+ """Load environment variables from .env and .env.local files.
25
+
26
+ Searches for .env and .env.local in the project root directory
27
+ (parent of src/claude_mpm). Files are loaded with override=False,
28
+ meaning existing environment variables take precedence.
29
+
30
+ Precedence (highest to lowest):
31
+ 1. Existing environment variables
32
+ 2. .env.local
33
+ 3. .env
34
+
35
+ Example:
36
+ >>> load_env()
37
+ # Loads .env.local and .env if they exist
38
+ """
39
+ # Find project root (parent of src/claude_mpm)
40
+ # Current file: src/claude_mpm/commander/env_loader.py
41
+ # Project root: ../../../ (3 levels up)
42
+ current_file = Path(__file__)
43
+ project_root = current_file.parent.parent.parent.parent
44
+
45
+ # Try loading .env.local first (higher priority)
46
+ env_local = project_root / ".env.local"
47
+ if env_local.exists():
48
+ load_dotenv(env_local, override=False)
49
+ logger.debug(f"Loaded environment from {env_local}")
50
+
51
+ # Then load .env (lower priority)
52
+ env_file = project_root / ".env"
53
+ if env_file.exists():
54
+ load_dotenv(env_file, override=False)
55
+ logger.debug(f"Loaded environment from {env_file}")
56
+
57
+ # Log if neither file exists
58
+ if not env_local.exists() and not env_file.exists():
59
+ logger.debug("No .env or .env.local files found in project root")
@@ -0,0 +1,26 @@
1
+ """Event management for MPM Commander.
2
+
3
+ Exports:
4
+ - Event model and enums from models.events
5
+ - EventManager for event lifecycle management
6
+ """
7
+
8
+ from ..models.events import (
9
+ BLOCKING_EVENTS,
10
+ DEFAULT_PRIORITIES,
11
+ Event,
12
+ EventPriority,
13
+ EventStatus,
14
+ EventType,
15
+ )
16
+ from .manager import EventManager
17
+
18
+ __all__ = [
19
+ "BLOCKING_EVENTS",
20
+ "DEFAULT_PRIORITIES",
21
+ "Event",
22
+ "EventManager",
23
+ "EventPriority",
24
+ "EventStatus",
25
+ "EventType",
26
+ ]