claude-mpm 5.4.41__py3-none-any.whl → 5.6.72__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (490) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
  3. claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
  4. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +109 -1925
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +161 -298
  6. claude_mpm/agents/WORKFLOW.md +2 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  8. claude_mpm/auth/__init__.py +35 -0
  9. claude_mpm/auth/callback_server.py +328 -0
  10. claude_mpm/auth/models.py +104 -0
  11. claude_mpm/auth/oauth_manager.py +266 -0
  12. claude_mpm/auth/providers/__init__.py +12 -0
  13. claude_mpm/auth/providers/base.py +165 -0
  14. claude_mpm/auth/providers/google.py +261 -0
  15. claude_mpm/auth/token_storage.py +252 -0
  16. claude_mpm/cli/__init__.py +5 -1
  17. claude_mpm/cli/commands/agents.py +2 -4
  18. claude_mpm/cli/commands/agents_reconcile.py +197 -0
  19. claude_mpm/cli/commands/autotodos.py +566 -0
  20. claude_mpm/cli/commands/commander.py +216 -0
  21. claude_mpm/cli/commands/configure.py +620 -21
  22. claude_mpm/cli/commands/configure_agent_display.py +3 -1
  23. claude_mpm/cli/commands/hook_errors.py +60 -60
  24. claude_mpm/cli/commands/mcp.py +29 -17
  25. claude_mpm/cli/commands/mcp_command_router.py +39 -0
  26. claude_mpm/cli/commands/mcp_service_commands.py +304 -0
  27. claude_mpm/cli/commands/monitor.py +2 -2
  28. claude_mpm/cli/commands/mpm_init/core.py +15 -8
  29. claude_mpm/cli/commands/oauth.py +481 -0
  30. claude_mpm/cli/commands/profile.py +9 -10
  31. claude_mpm/cli/commands/run.py +35 -3
  32. claude_mpm/cli/commands/skill_source.py +51 -2
  33. claude_mpm/cli/commands/skills.py +182 -32
  34. claude_mpm/cli/executor.py +129 -16
  35. claude_mpm/cli/helpers.py +1 -1
  36. claude_mpm/cli/interactive/__init__.py +10 -0
  37. claude_mpm/cli/interactive/agent_wizard.py +30 -50
  38. claude_mpm/cli/interactive/questionary_styles.py +65 -0
  39. claude_mpm/cli/interactive/skill_selector.py +481 -0
  40. claude_mpm/cli/parsers/base_parser.py +89 -1
  41. claude_mpm/cli/parsers/commander_parser.py +116 -0
  42. claude_mpm/cli/parsers/mcp_parser.py +79 -0
  43. claude_mpm/cli/parsers/oauth_parser.py +165 -0
  44. claude_mpm/cli/parsers/profile_parser.py +0 -1
  45. claude_mpm/cli/parsers/run_parser.py +10 -0
  46. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  47. claude_mpm/cli/parsers/skills_parser.py +2 -3
  48. claude_mpm/cli/startup.py +662 -524
  49. claude_mpm/cli/startup_display.py +76 -7
  50. claude_mpm/cli/startup_logging.py +2 -2
  51. claude_mpm/cli/utils.py +7 -3
  52. claude_mpm/commander/__init__.py +78 -0
  53. claude_mpm/commander/adapters/__init__.py +60 -0
  54. claude_mpm/commander/adapters/auggie.py +260 -0
  55. claude_mpm/commander/adapters/base.py +288 -0
  56. claude_mpm/commander/adapters/claude_code.py +392 -0
  57. claude_mpm/commander/adapters/codex.py +237 -0
  58. claude_mpm/commander/adapters/communication.py +366 -0
  59. claude_mpm/commander/adapters/example_usage.py +310 -0
  60. claude_mpm/commander/adapters/mpm.py +389 -0
  61. claude_mpm/commander/adapters/registry.py +204 -0
  62. claude_mpm/commander/api/__init__.py +16 -0
  63. claude_mpm/commander/api/app.py +121 -0
  64. claude_mpm/commander/api/errors.py +133 -0
  65. claude_mpm/commander/api/routes/__init__.py +8 -0
  66. claude_mpm/commander/api/routes/events.py +184 -0
  67. claude_mpm/commander/api/routes/inbox.py +171 -0
  68. claude_mpm/commander/api/routes/messages.py +148 -0
  69. claude_mpm/commander/api/routes/projects.py +271 -0
  70. claude_mpm/commander/api/routes/sessions.py +226 -0
  71. claude_mpm/commander/api/routes/work.py +296 -0
  72. claude_mpm/commander/api/schemas.py +186 -0
  73. claude_mpm/commander/chat/__init__.py +7 -0
  74. claude_mpm/commander/chat/cli.py +149 -0
  75. claude_mpm/commander/chat/commands.py +122 -0
  76. claude_mpm/commander/chat/repl.py +1821 -0
  77. claude_mpm/commander/config.py +51 -0
  78. claude_mpm/commander/config_loader.py +115 -0
  79. claude_mpm/commander/core/__init__.py +10 -0
  80. claude_mpm/commander/core/block_manager.py +325 -0
  81. claude_mpm/commander/core/response_manager.py +323 -0
  82. claude_mpm/commander/daemon.py +603 -0
  83. claude_mpm/commander/env_loader.py +59 -0
  84. claude_mpm/commander/events/__init__.py +26 -0
  85. claude_mpm/commander/events/manager.py +392 -0
  86. claude_mpm/commander/frameworks/__init__.py +12 -0
  87. claude_mpm/commander/frameworks/base.py +233 -0
  88. claude_mpm/commander/frameworks/claude_code.py +58 -0
  89. claude_mpm/commander/frameworks/mpm.py +57 -0
  90. claude_mpm/commander/git/__init__.py +5 -0
  91. claude_mpm/commander/git/worktree_manager.py +212 -0
  92. claude_mpm/commander/inbox/__init__.py +16 -0
  93. claude_mpm/commander/inbox/dedup.py +128 -0
  94. claude_mpm/commander/inbox/inbox.py +224 -0
  95. claude_mpm/commander/inbox/models.py +70 -0
  96. claude_mpm/commander/instance_manager.py +865 -0
  97. claude_mpm/commander/llm/__init__.py +6 -0
  98. claude_mpm/commander/llm/openrouter_client.py +167 -0
  99. claude_mpm/commander/llm/summarizer.py +70 -0
  100. claude_mpm/commander/memory/__init__.py +45 -0
  101. claude_mpm/commander/memory/compression.py +347 -0
  102. claude_mpm/commander/memory/embeddings.py +230 -0
  103. claude_mpm/commander/memory/entities.py +310 -0
  104. claude_mpm/commander/memory/example_usage.py +290 -0
  105. claude_mpm/commander/memory/integration.py +325 -0
  106. claude_mpm/commander/memory/search.py +381 -0
  107. claude_mpm/commander/memory/store.py +657 -0
  108. claude_mpm/commander/models/__init__.py +18 -0
  109. claude_mpm/commander/models/events.py +127 -0
  110. claude_mpm/commander/models/project.py +162 -0
  111. claude_mpm/commander/models/work.py +214 -0
  112. claude_mpm/commander/parsing/__init__.py +20 -0
  113. claude_mpm/commander/parsing/extractor.py +132 -0
  114. claude_mpm/commander/parsing/output_parser.py +270 -0
  115. claude_mpm/commander/parsing/patterns.py +100 -0
  116. claude_mpm/commander/persistence/__init__.py +11 -0
  117. claude_mpm/commander/persistence/event_store.py +274 -0
  118. claude_mpm/commander/persistence/state_store.py +403 -0
  119. claude_mpm/commander/persistence/work_store.py +164 -0
  120. claude_mpm/commander/polling/__init__.py +13 -0
  121. claude_mpm/commander/polling/event_detector.py +104 -0
  122. claude_mpm/commander/polling/output_buffer.py +49 -0
  123. claude_mpm/commander/polling/output_poller.py +153 -0
  124. claude_mpm/commander/project_session.py +268 -0
  125. claude_mpm/commander/proxy/__init__.py +12 -0
  126. claude_mpm/commander/proxy/formatter.py +89 -0
  127. claude_mpm/commander/proxy/output_handler.py +191 -0
  128. claude_mpm/commander/proxy/relay.py +155 -0
  129. claude_mpm/commander/registry.py +410 -0
  130. claude_mpm/commander/runtime/__init__.py +10 -0
  131. claude_mpm/commander/runtime/executor.py +191 -0
  132. claude_mpm/commander/runtime/monitor.py +346 -0
  133. claude_mpm/commander/session/__init__.py +6 -0
  134. claude_mpm/commander/session/context.py +81 -0
  135. claude_mpm/commander/session/manager.py +59 -0
  136. claude_mpm/commander/tmux_orchestrator.py +362 -0
  137. claude_mpm/commander/web/__init__.py +1 -0
  138. claude_mpm/commander/work/__init__.py +30 -0
  139. claude_mpm/commander/work/executor.py +207 -0
  140. claude_mpm/commander/work/queue.py +405 -0
  141. claude_mpm/commander/workflow/__init__.py +27 -0
  142. claude_mpm/commander/workflow/event_handler.py +241 -0
  143. claude_mpm/commander/workflow/notifier.py +146 -0
  144. claude_mpm/commands/mpm-config.md +8 -0
  145. claude_mpm/commands/mpm-doctor.md +8 -0
  146. claude_mpm/commands/mpm-help.md +8 -0
  147. claude_mpm/commands/mpm-init.md +8 -0
  148. claude_mpm/commands/mpm-monitor.md +8 -0
  149. claude_mpm/commands/mpm-organize.md +8 -0
  150. claude_mpm/commands/mpm-postmortem.md +8 -0
  151. claude_mpm/commands/mpm-session-resume.md +9 -1
  152. claude_mpm/commands/mpm-status.md +8 -0
  153. claude_mpm/commands/mpm-ticket-view.md +8 -0
  154. claude_mpm/commands/mpm-version.md +8 -0
  155. claude_mpm/commands/mpm.md +8 -0
  156. claude_mpm/config/agent_presets.py +8 -7
  157. claude_mpm/config/skill_sources.py +16 -0
  158. claude_mpm/constants.py +6 -0
  159. claude_mpm/core/claude_runner.py +154 -2
  160. claude_mpm/core/config.py +35 -22
  161. claude_mpm/core/config_constants.py +74 -9
  162. claude_mpm/core/constants.py +56 -12
  163. claude_mpm/core/hook_manager.py +53 -4
  164. claude_mpm/core/interactive_session.py +12 -11
  165. claude_mpm/core/logger.py +26 -9
  166. claude_mpm/core/logging_utils.py +39 -13
  167. claude_mpm/core/network_config.py +148 -0
  168. claude_mpm/core/oneshot_session.py +7 -6
  169. claude_mpm/core/optimized_startup.py +3 -1
  170. claude_mpm/core/output_style_manager.py +66 -18
  171. claude_mpm/core/shared/config_loader.py +3 -1
  172. claude_mpm/core/socketio_pool.py +47 -15
  173. claude_mpm/core/unified_config.py +54 -8
  174. claude_mpm/core/unified_paths.py +95 -90
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/1WZnGYqX.js +24 -0
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/67pF3qNn.js +1 -0
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/6RxdMKe4.js +1 -0
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/8cZrfX0h.js +60 -0
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/9a6T2nm-.js +7 -0
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B443AUzu.js +1 -0
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B8AwtY2H.js +1 -0
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BF15LAsF.js +1 -0
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BRcwIQNr.js +4 -0
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uj46x2Wr.js → BSNlmTZj.js} +1 -1
  188. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BV6nKitt.js +43 -0
  189. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BViJ8lZt.js +128 -0
  190. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BcQ-Q0FE.js +1 -0
  191. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bpyvgze_.js +30 -0
  192. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  193. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  194. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C3rbW_a-.js +1 -0
  195. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C8WYN38h.js +1 -0
  196. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C9I8FlXH.js +61 -0
  197. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIQcWgO2.js +36 -0
  198. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIctN7YN.js +7 -0
  199. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CKrS_JZW.js +145 -0
  200. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CR6P9C4A.js +89 -0
  201. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRRR9MD_.js +2 -0
  202. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  203. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CSXtMOf0.js +1 -0
  204. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CT-sbxSk.js +1 -0
  205. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWm6DJsp.js +1 -0
  206. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
  207. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CpqQ1Kzn.js +1 -0
  208. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
  209. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D2nGpDRe.js +1 -0
  210. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9iCMida.js +267 -0
  211. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9ykgMoY.js +10 -0
  212. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DL2Ldur1.js +1 -0
  213. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DPfltzjH.js +165 -0
  214. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{N4qtv3Hx.js → DR8nis88.js} +2 -2
  215. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUliQN2b.js +1 -0
  216. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
  217. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DXlhR01x.js +122 -0
  218. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D_lyTybS.js +1 -0
  219. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DngoTTgh.js +1 -0
  220. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DqkmHtDC.js +220 -0
  221. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DsDh8EYs.js +1 -0
  222. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DypDmXgd.js +139 -0
  223. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
  224. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/IPYC-LnN.js +162 -0
  225. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  226. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JpevfAFt.js +68 -0
  227. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DjhvlsAc.js → NqQ1dWOy.js} +1 -1
  228. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/R8CEIRAd.js +2 -0
  229. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Zxy7qc-l.js +64 -0
  230. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  231. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/qtd3IeO4.js +15 -0
  232. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ulBFON_C.js +65 -0
  233. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/wQVh1CoA.js +10 -0
  234. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.Dr7t0z2J.js +2 -0
  235. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  236. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.CAGBuiOw.js → 0.RgBboRvH.js} +1 -1
  237. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DG-KkbDf.js +1 -0
  238. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  239. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  240. claude_mpm/dashboard/static/svelte-build/index.html +11 -11
  241. claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
  242. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
  243. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
  244. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
  245. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
  246. claude_mpm/experimental/cli_enhancements.py +2 -1
  247. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  248. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  249. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  250. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  251. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  252. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  253. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  254. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  255. claude_mpm/hooks/claude_hooks/event_handlers.py +466 -136
  256. claude_mpm/hooks/claude_hooks/hook_handler.py +204 -104
  257. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  258. claude_mpm/hooks/claude_hooks/installer.py +291 -59
  259. claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
  260. claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
  261. claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
  262. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  263. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  264. claude_mpm/hooks/claude_hooks/services/__pycache__/container.cpython-311.pyc +0 -0
  265. claude_mpm/hooks/claude_hooks/services/__pycache__/protocols.cpython-311.pyc +0 -0
  266. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  267. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  268. claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
  269. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
  270. claude_mpm/hooks/claude_hooks/services/container.py +326 -0
  271. claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
  272. claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
  273. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
  274. claude_mpm/hooks/kuzu_memory_hook.py +5 -5
  275. claude_mpm/hooks/session_resume_hook.py +89 -1
  276. claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
  277. claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
  278. claude_mpm/init.py +224 -4
  279. claude_mpm/mcp/__init__.py +9 -0
  280. claude_mpm/mcp/google_workspace_server.py +610 -0
  281. claude_mpm/scripts/claude-hook-handler.sh +46 -19
  282. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  283. claude_mpm/services/agents/agent_selection_service.py +2 -2
  284. claude_mpm/services/agents/cache_git_manager.py +1 -1
  285. claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -1
  286. claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
  287. claude_mpm/services/agents/deployment/agent_template_builder.py +37 -17
  288. claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
  289. claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
  290. claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
  291. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +36 -8
  292. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +50 -26
  293. claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
  294. claude_mpm/services/agents/git_source_manager.py +21 -2
  295. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  296. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  297. claude_mpm/services/agents/sources/git_source_sync_service.py +116 -5
  298. claude_mpm/services/agents/startup_sync.py +5 -2
  299. claude_mpm/services/cli/__init__.py +3 -0
  300. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  301. claude_mpm/services/cli/session_resume_helper.py +10 -2
  302. claude_mpm/services/command_deployment_service.py +44 -26
  303. claude_mpm/services/delegation_detector.py +175 -0
  304. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  305. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  306. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  307. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  308. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  309. claude_mpm/services/diagnostics/models.py +14 -1
  310. claude_mpm/services/event_log.py +325 -0
  311. claude_mpm/services/hook_installer_service.py +77 -8
  312. claude_mpm/services/infrastructure/__init__.py +4 -0
  313. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  314. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  315. claude_mpm/services/mcp_config_manager.py +99 -19
  316. claude_mpm/services/mcp_service_registry.py +294 -0
  317. claude_mpm/services/monitor/daemon_manager.py +15 -4
  318. claude_mpm/services/monitor/management/lifecycle.py +8 -3
  319. claude_mpm/services/monitor/server.py +111 -16
  320. claude_mpm/services/pm_skills_deployer.py +302 -94
  321. claude_mpm/services/profile_manager.py +10 -4
  322. claude_mpm/services/skills/git_skill_source_manager.py +192 -29
  323. claude_mpm/services/skills/selective_skill_deployer.py +211 -46
  324. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  325. claude_mpm/services/skills_deployer.py +192 -70
  326. claude_mpm/services/socketio/handlers/hook.py +14 -7
  327. claude_mpm/services/socketio/server/main.py +12 -4
  328. claude_mpm/skills/__init__.py +2 -1
  329. claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
  330. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
  331. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
  332. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
  333. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
  334. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
  335. claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
  336. claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
  337. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
  338. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
  339. claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
  340. claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
  341. claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
  342. claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
  343. claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
  344. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
  345. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
  346. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
  347. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
  348. claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
  349. claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
  350. claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
  351. claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
  352. claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
  353. claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
  354. claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
  355. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
  356. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
  357. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
  358. claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
  359. claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
  360. claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
  361. claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
  362. claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
  363. claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
  364. claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
  365. claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
  366. claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
  367. claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
  368. claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
  369. claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
  370. claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
  371. claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
  372. claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
  373. claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
  374. claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
  375. claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
  376. claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
  377. claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
  378. claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
  379. claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
  380. claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
  381. claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
  382. claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
  383. claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
  384. claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
  385. claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
  386. claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
  387. claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
  388. claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
  389. claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
  390. claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
  391. claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
  392. claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
  393. claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
  394. claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
  395. claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
  396. claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
  397. claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
  398. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  399. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  400. claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
  401. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  402. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  403. claude_mpm/skills/bundled/pm/mpm-delegation-patterns/SKILL.md +167 -0
  404. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  405. claude_mpm/skills/bundled/pm/mpm-git-file-tracking/SKILL.md +113 -0
  406. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  407. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  408. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  409. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  410. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  411. claude_mpm/skills/bundled/pm/mpm-pr-workflow/SKILL.md +124 -0
  412. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  413. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  414. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  415. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  416. claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
  417. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  418. claude_mpm/skills/bundled/pm/mpm-ticketing-integration/SKILL.md +154 -0
  419. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  420. claude_mpm/skills/bundled/pm/mpm-verification-protocols/SKILL.md +198 -0
  421. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  422. claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
  423. claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
  424. claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
  425. claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
  426. claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
  427. claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
  428. claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
  429. claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
  430. claude_mpm/skills/bundled/security-scanning.md +112 -0
  431. claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
  432. claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
  433. claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
  434. claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
  435. claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
  436. claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
  437. claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
  438. claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
  439. claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
  440. claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
  441. claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
  442. claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
  443. claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
  444. claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
  445. claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
  446. claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
  447. claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
  448. claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
  449. claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
  450. claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
  451. claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
  452. claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
  453. claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
  454. claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
  455. claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
  456. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
  457. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
  458. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
  459. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
  460. claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
  461. claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
  462. claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
  463. claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
  464. claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
  465. claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
  466. claude_mpm/skills/registry.py +295 -90
  467. claude_mpm/skills/skill_manager.py +29 -23
  468. claude_mpm/templates/.pre-commit-config.yaml +112 -0
  469. claude_mpm/utils/agent_dependency_loader.py +103 -4
  470. claude_mpm/utils/robust_installer.py +45 -24
  471. claude_mpm-5.6.72.dist-info/METADATA +416 -0
  472. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/RECORD +477 -159
  473. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/WHEEL +1 -1
  474. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/entry_points.txt +2 -0
  475. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +0 -1
  476. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +0 -1
  477. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +0 -1
  478. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +0 -1
  479. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +0 -1
  480. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +0 -2
  481. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +0 -2
  482. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +0 -1
  483. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +0 -1
  484. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +0 -10
  485. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  486. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  487. claude_mpm-5.4.41.dist-info/METADATA +0 -998
  488. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE +0 -0
  489. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  490. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,410 @@
1
+ """Project registry for MPM Commander.
2
+
3
+ This module provides thread-safe registration and management of projects,
4
+ including state tracking, session management, and path indexing.
5
+ """
6
+
7
+ import logging
8
+ import threading
9
+ import uuid
10
+ from datetime import datetime, timezone
11
+ from pathlib import Path
12
+ from typing import Dict, List, Optional
13
+
14
+ from .models import Project, ProjectState, ToolSession
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class ProjectRegistry:
20
+ """Thread-safe registry for managing projects.
21
+
22
+ Maintains an in-memory registry of all active projects with:
23
+ - Unique project IDs (UUIDs)
24
+ - Path-based indexing for fast lookup
25
+ - Thread-safe access with RLock
26
+ - State management and session tracking
27
+
28
+ Example:
29
+ >>> registry = ProjectRegistry()
30
+ >>> project = registry.register("/Users/masa/Projects/my-app")
31
+ >>> project.id
32
+ 'a3f2c1d4-...'
33
+ >>> registry.update_state(project.id, ProjectState.WORKING)
34
+ >>> registry.get(project.id).state
35
+ <ProjectState.WORKING: 'working'>
36
+ """
37
+
38
+ def __init__(self):
39
+ """Initialize empty registry with thread-safe lock."""
40
+ self._projects: Dict[str, Project] = {}
41
+ self._path_index: Dict[str, str] = {} # path -> project_id
42
+ self._lock = threading.RLock()
43
+ logger.info("Initialized ProjectRegistry")
44
+
45
+ def register(
46
+ self, path: str, name: Optional[str] = None, project_id: Optional[str] = None
47
+ ) -> Project:
48
+ """Register a new project.
49
+
50
+ Creates a new project with unique UUID (or user-provided ID) and adds it to registry.
51
+ Path must be a valid directory and cannot already be registered.
52
+
53
+ Args:
54
+ path: Absolute filesystem path to project directory
55
+ name: Optional human-readable name (defaults to directory name)
56
+ project_id: Optional project identifier (UUID generated if omitted)
57
+
58
+ Returns:
59
+ Newly created Project instance
60
+
61
+ Raises:
62
+ ValueError: If path is invalid, not a directory, or already registered
63
+
64
+ Example:
65
+ >>> registry = ProjectRegistry()
66
+ >>> project = registry.register("/Users/masa/Projects/my-app")
67
+ >>> project.name
68
+ 'my-app'
69
+ >>> project.state
70
+ <ProjectState.IDLE: 'idle'>
71
+ """
72
+ with self._lock:
73
+ # Validate path exists and is directory
74
+ path_obj = Path(path)
75
+ try:
76
+ if not path_obj.exists():
77
+ raise ValueError(f"Path does not exist: {path}")
78
+ if not path_obj.is_dir():
79
+ raise ValueError(f"Path is not a directory: {path}")
80
+ except (OSError, PermissionError) as e:
81
+ raise ValueError(f"Cannot access path: {path}") from e
82
+
83
+ # Resolve to absolute path for consistency
84
+ abs_path = str(path_obj.resolve())
85
+
86
+ # Check for duplicate registration
87
+ if abs_path in self._path_index:
88
+ existing_id = self._path_index[abs_path]
89
+ raise ValueError(
90
+ f"Project already registered at path: {abs_path} "
91
+ f"(project_id: {existing_id})"
92
+ )
93
+
94
+ # Derive name from directory if not provided
95
+ if name is None:
96
+ name = path_obj.name
97
+
98
+ # Generate unique project ID if not provided
99
+ if project_id is None:
100
+ project_id = str(uuid.uuid4())
101
+ elif project_id in self._projects:
102
+ raise ValueError(f"Project ID already exists: {project_id}")
103
+
104
+ # Create project instance
105
+ project = Project(
106
+ id=project_id,
107
+ path=abs_path,
108
+ name=name,
109
+ state=ProjectState.IDLE,
110
+ )
111
+
112
+ # Register in both indexes
113
+ self._projects[project_id] = project
114
+ self._path_index[abs_path] = project_id
115
+
116
+ logger.info(
117
+ "Registered project: id=%s, path=%s, name=%s",
118
+ project_id,
119
+ abs_path,
120
+ name,
121
+ )
122
+
123
+ return project
124
+
125
+ def unregister(self, project_id: str) -> None:
126
+ """Remove project from registry.
127
+
128
+ Removes project from both ID and path indexes.
129
+
130
+ Args:
131
+ project_id: Unique project identifier
132
+
133
+ Raises:
134
+ KeyError: If project_id not found
135
+
136
+ Example:
137
+ >>> registry = ProjectRegistry()
138
+ >>> project = registry.register("/tmp/test-project")
139
+ >>> registry.unregister(project.id)
140
+ >>> registry.get(project.id) is None
141
+ True
142
+ """
143
+ with self._lock:
144
+ if project_id not in self._projects:
145
+ raise KeyError(f"Project not found: {project_id}")
146
+
147
+ project = self._projects[project_id]
148
+
149
+ # Remove from both indexes
150
+ del self._projects[project_id]
151
+ del self._path_index[project.path]
152
+
153
+ logger.info(
154
+ "Unregistered project: id=%s, path=%s",
155
+ project_id,
156
+ project.path,
157
+ )
158
+
159
+ def get(self, project_id: str) -> Optional[Project]:
160
+ """Get project by ID.
161
+
162
+ Args:
163
+ project_id: Unique project identifier
164
+
165
+ Returns:
166
+ Project instance or None if not found
167
+
168
+ Example:
169
+ >>> registry = ProjectRegistry()
170
+ >>> project = registry.register("/tmp/test")
171
+ >>> registry.get(project.id).name
172
+ 'test'
173
+ >>> registry.get("invalid-id") is None
174
+ True
175
+ """
176
+ with self._lock:
177
+ return self._projects.get(project_id)
178
+
179
+ def get_by_path(self, path: str) -> Optional[Project]:
180
+ """Get project by filesystem path.
181
+
182
+ Resolves path to absolute before lookup.
183
+
184
+ Args:
185
+ path: Filesystem path to project
186
+
187
+ Returns:
188
+ Project instance or None if not found
189
+
190
+ Example:
191
+ >>> registry = ProjectRegistry()
192
+ >>> project = registry.register("/tmp/test")
193
+ >>> found = registry.get_by_path("/tmp/test")
194
+ >>> found.id == project.id
195
+ True
196
+ """
197
+ with self._lock:
198
+ # Resolve to absolute path for consistent lookup
199
+ try:
200
+ abs_path = str(Path(path).resolve())
201
+ except (OSError, ValueError):
202
+ # Invalid path
203
+ return None
204
+
205
+ project_id = self._path_index.get(abs_path)
206
+ if project_id is None:
207
+ return None
208
+
209
+ return self._projects.get(project_id)
210
+
211
+ def list_all(self) -> List[Project]:
212
+ """List all registered projects.
213
+
214
+ Returns:
215
+ List of all Project instances (may be empty)
216
+
217
+ Example:
218
+ >>> registry = ProjectRegistry()
219
+ >>> registry.register("/tmp/proj1")
220
+ >>> registry.register("/tmp/proj2")
221
+ >>> len(registry.list_all())
222
+ 2
223
+ """
224
+ with self._lock:
225
+ return list(self._projects.values())
226
+
227
+ def list_by_state(self, state: ProjectState) -> List[Project]:
228
+ """List projects in specific state.
229
+
230
+ Args:
231
+ state: ProjectState to filter by
232
+
233
+ Returns:
234
+ List of projects in given state (may be empty)
235
+
236
+ Example:
237
+ >>> registry = ProjectRegistry()
238
+ >>> p1 = registry.register("/tmp/proj1")
239
+ >>> p2 = registry.register("/tmp/proj2")
240
+ >>> registry.update_state(p1.id, ProjectState.WORKING)
241
+ >>> working = registry.list_by_state(ProjectState.WORKING)
242
+ >>> len(working)
243
+ 1
244
+ >>> working[0].id == p1.id
245
+ True
246
+ """
247
+ with self._lock:
248
+ return [p for p in self._projects.values() if p.state == state]
249
+
250
+ def update_state(
251
+ self,
252
+ project_id: str,
253
+ state: ProjectState,
254
+ reason: Optional[str] = None,
255
+ ) -> None:
256
+ """Update project state.
257
+
258
+ Updates both state and optional reason, and touches last_activity.
259
+
260
+ Args:
261
+ project_id: Unique project identifier
262
+ state: New ProjectState
263
+ reason: Optional state reason (e.g., error message)
264
+
265
+ Raises:
266
+ KeyError: If project_id not found
267
+
268
+ Example:
269
+ >>> registry = ProjectRegistry()
270
+ >>> project = registry.register("/tmp/test")
271
+ >>> registry.update_state(
272
+ ... project.id,
273
+ ... ProjectState.ERROR,
274
+ ... reason="Connection timeout"
275
+ ... )
276
+ >>> project.state
277
+ <ProjectState.ERROR: 'error'>
278
+ >>> project.state_reason
279
+ 'Connection timeout'
280
+ """
281
+ with self._lock:
282
+ if project_id not in self._projects:
283
+ raise KeyError(f"Project not found: {project_id}")
284
+
285
+ project = self._projects[project_id]
286
+ old_state = project.state
287
+
288
+ project.state = state
289
+ project.state_reason = reason
290
+ project.last_activity = datetime.now(timezone.utc)
291
+
292
+ logger.info(
293
+ "State change: project=%s, %s -> %s, reason=%s",
294
+ project_id,
295
+ old_state.value,
296
+ state.value,
297
+ reason,
298
+ )
299
+
300
+ def add_session(self, project_id: str, session: ToolSession) -> None:
301
+ """Add session to project.
302
+
303
+ Adds tool session to project's session dict and updates last_activity.
304
+
305
+ Args:
306
+ project_id: Unique project identifier
307
+ session: ToolSession to add
308
+
309
+ Raises:
310
+ KeyError: If project_id not found
311
+
312
+ Example:
313
+ >>> registry = ProjectRegistry()
314
+ >>> project = registry.register("/tmp/test")
315
+ >>> session = ToolSession(
316
+ ... id="sess-123",
317
+ ... project_id=project.id,
318
+ ... runtime="claude-code",
319
+ ... tmux_target="commander:test-cc"
320
+ ... )
321
+ >>> registry.add_session(project.id, session)
322
+ >>> len(project.sessions)
323
+ 1
324
+ """
325
+ with self._lock:
326
+ if project_id not in self._projects:
327
+ raise KeyError(f"Project not found: {project_id}")
328
+
329
+ project = self._projects[project_id]
330
+ project.sessions[session.id] = session
331
+ project.last_activity = datetime.now(timezone.utc)
332
+
333
+ logger.info(
334
+ "Added session: project=%s, session=%s, runtime=%s",
335
+ project_id,
336
+ session.id,
337
+ session.runtime,
338
+ )
339
+
340
+ def remove_session(self, project_id: str, session_id: str) -> None:
341
+ """Remove session from project.
342
+
343
+ Removes tool session from project's session dict and updates last_activity.
344
+
345
+ Args:
346
+ project_id: Unique project identifier
347
+ session_id: Session ID to remove
348
+
349
+ Raises:
350
+ KeyError: If project_id not found or session_id not in project
351
+
352
+ Example:
353
+ >>> registry = ProjectRegistry()
354
+ >>> project = registry.register("/tmp/test")
355
+ >>> session = ToolSession(
356
+ ... id="sess-123",
357
+ ... project_id=project.id,
358
+ ... runtime="claude-code",
359
+ ... tmux_target="commander:test-cc"
360
+ ... )
361
+ >>> registry.add_session(project.id, session)
362
+ >>> registry.remove_session(project.id, session.id)
363
+ >>> len(project.sessions)
364
+ 0
365
+ """
366
+ with self._lock:
367
+ if project_id not in self._projects:
368
+ raise KeyError(f"Project not found: {project_id}")
369
+
370
+ project = self._projects[project_id]
371
+
372
+ if session_id not in project.sessions:
373
+ raise KeyError(f"Session not found in project: {session_id}")
374
+
375
+ del project.sessions[session_id]
376
+ project.last_activity = datetime.now(timezone.utc)
377
+
378
+ logger.info(
379
+ "Removed session: project=%s, session=%s",
380
+ project_id,
381
+ session_id,
382
+ )
383
+
384
+ def touch(self, project_id: str) -> None:
385
+ """Update last_activity timestamp.
386
+
387
+ Args:
388
+ project_id: Unique project identifier
389
+
390
+ Raises:
391
+ KeyError: If project_id not found
392
+
393
+ Example:
394
+ >>> import time
395
+ >>> registry = ProjectRegistry()
396
+ >>> project = registry.register("/tmp/test")
397
+ >>> old_time = project.last_activity
398
+ >>> time.sleep(0.01)
399
+ >>> registry.touch(project.id)
400
+ >>> project.last_activity > old_time
401
+ True
402
+ """
403
+ with self._lock:
404
+ if project_id not in self._projects:
405
+ raise KeyError(f"Project not found: {project_id}")
406
+
407
+ project = self._projects[project_id]
408
+ project.last_activity = datetime.now(timezone.utc)
409
+
410
+ logger.debug("Touched project: %s", project_id)
@@ -0,0 +1,10 @@
1
+ """Runtime integration for MPM Commander.
2
+
3
+ This module provides components for spawning and monitoring Claude Code instances
4
+ in tmux panes, enabling autonomous task execution with event detection.
5
+ """
6
+
7
+ from .executor import RuntimeExecutor
8
+ from .monitor import RuntimeMonitor
9
+
10
+ __all__ = ["RuntimeExecutor", "RuntimeMonitor"]
@@ -0,0 +1,191 @@
1
+ """Runtime executor for spawning and managing Claude Code instances in tmux.
2
+
3
+ This module provides RuntimeExecutor which spawns Claude Code processes in tmux
4
+ panes and manages their lifecycle, including sending messages and terminating.
5
+ """
6
+
7
+ import logging
8
+
9
+ from ..models.project import Project
10
+ from ..tmux_orchestrator import TmuxOrchestrator
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class RuntimeExecutor:
16
+ """Spawns and manages Claude Code processes in tmux panes.
17
+
18
+ This class handles the lifecycle of Claude Code instances running in tmux panes,
19
+ providing capabilities to spawn new instances, send messages, check status,
20
+ and terminate processes.
21
+
22
+ Attributes:
23
+ orchestrator: TmuxOrchestrator instance for tmux operations
24
+
25
+ Example:
26
+ >>> orchestrator = TmuxOrchestrator()
27
+ >>> executor = RuntimeExecutor(orchestrator)
28
+ >>> pane_target = await executor.spawn(project, "claude")
29
+ >>> await executor.send_message(pane_target, "Implement user authentication")
30
+ >>> if executor.is_running(pane_target):
31
+ ... await executor.terminate(pane_target)
32
+ """
33
+
34
+ def __init__(self, orchestrator: TmuxOrchestrator):
35
+ """Initialize runtime executor.
36
+
37
+ Args:
38
+ orchestrator: TmuxOrchestrator for tmux operations
39
+
40
+ Raises:
41
+ ValueError: If orchestrator is None
42
+ """
43
+ if orchestrator is None:
44
+ raise ValueError("Orchestrator cannot be None")
45
+
46
+ self.orchestrator = orchestrator
47
+ logger.debug("RuntimeExecutor initialized")
48
+
49
+ async def spawn(self, project: Project, command: str = "claude") -> str:
50
+ """Spawn Claude Code in a new tmux pane for the project.
51
+
52
+ Creates a new tmux pane in the project's working directory and spawns
53
+ the specified command (typically "claude" for Claude Code).
54
+
55
+ Args:
56
+ project: Project instance with working directory
57
+ command: Command to run (default: "claude")
58
+
59
+ Returns:
60
+ The pane target (e.g., '%5') that can be used for subsequent operations
61
+
62
+ Raises:
63
+ RuntimeError: If pane creation fails
64
+ ValueError: If project or project.path is None
65
+
66
+ Example:
67
+ >>> project = Project(id="proj1", path="/path/to/project")
68
+ >>> pane_target = await executor.spawn(project)
69
+ >>> print(f"Spawned in pane: {pane_target}")
70
+ Spawned in pane: %5
71
+ """
72
+ if project is None:
73
+ raise ValueError("Project cannot be None")
74
+ if not project.path:
75
+ raise ValueError("Project path cannot be None or empty")
76
+
77
+ logger.info(
78
+ "Spawning %s for project %s in %s", command, project.id, project.path
79
+ )
80
+
81
+ try:
82
+ # Create tmux session if it doesn't exist
83
+ if not self.orchestrator.session_exists():
84
+ self.orchestrator.create_session()
85
+ logger.debug("Created tmux session")
86
+
87
+ # Create pane with project working directory
88
+ pane_target = self.orchestrator.create_pane(project.id, project.path)
89
+ logger.debug("Created pane: %s", pane_target)
90
+
91
+ # Send command to pane
92
+ self.orchestrator.send_keys(pane_target, command, enter=True)
93
+ logger.info("Spawned %s in pane %s", command, pane_target)
94
+
95
+ return pane_target
96
+
97
+ except Exception as e:
98
+ logger.error(
99
+ "Failed to spawn %s for project %s: %s", command, project.id, e
100
+ )
101
+ raise RuntimeError(f"Failed to spawn {command}: {e}") from e
102
+
103
+ async def send_message(self, pane_target: str, message: str) -> None:
104
+ """Send a message/command to a running Claude instance.
105
+
106
+ Sends the message to the specified tmux pane, followed by Enter.
107
+
108
+ Args:
109
+ pane_target: Pane target from spawn()
110
+ message: Message to send to Claude Code
111
+
112
+ Raises:
113
+ ValueError: If pane_target or message is None/empty
114
+ RuntimeError: If sending message fails
115
+
116
+ Example:
117
+ >>> await executor.send_message("%5", "Fix the authentication bug")
118
+ """
119
+ if not pane_target:
120
+ raise ValueError("Pane target cannot be None or empty")
121
+ if not message:
122
+ raise ValueError("Message cannot be None or empty")
123
+
124
+ logger.debug("Sending message to pane %s: %s", pane_target, message[:50])
125
+
126
+ try:
127
+ self.orchestrator.send_keys(pane_target, message, enter=True)
128
+ logger.info("Sent message to pane %s", pane_target)
129
+
130
+ except Exception as e:
131
+ logger.error("Failed to send message to pane %s: %s", pane_target, e)
132
+ raise RuntimeError(
133
+ f"Failed to send message to pane {pane_target}: {e}"
134
+ ) from e
135
+
136
+ async def terminate(self, pane_target: str) -> None:
137
+ """Terminate a Claude Code instance.
138
+
139
+ Kills the specified tmux pane, terminating the Claude Code process.
140
+
141
+ Args:
142
+ pane_target: Pane target from spawn()
143
+
144
+ Raises:
145
+ ValueError: If pane_target is None/empty
146
+ RuntimeError: If termination fails
147
+
148
+ Example:
149
+ >>> await executor.terminate("%5")
150
+ """
151
+ if not pane_target:
152
+ raise ValueError("Pane target cannot be None or empty")
153
+
154
+ logger.info("Terminating pane %s", pane_target)
155
+
156
+ try:
157
+ self.orchestrator.kill_pane(pane_target)
158
+ logger.info("Terminated pane %s", pane_target)
159
+
160
+ except Exception as e:
161
+ logger.error("Failed to terminate pane %s: %s", pane_target, e)
162
+ raise RuntimeError(f"Failed to terminate pane {pane_target}: {e}") from e
163
+
164
+ def is_running(self, pane_target: str) -> bool:
165
+ """Check if a pane is still active.
166
+
167
+ Args:
168
+ pane_target: Pane target from spawn()
169
+
170
+ Returns:
171
+ True if pane exists and is running, False otherwise
172
+
173
+ Example:
174
+ >>> if executor.is_running("%5"):
175
+ ... print("Pane is still running")
176
+ """
177
+ if not pane_target:
178
+ return False
179
+
180
+ try:
181
+ # List all panes and check if target exists
182
+ panes = self.orchestrator.list_panes()
183
+ pane_ids = [pane["id"] for pane in panes]
184
+ is_active = pane_target in pane_ids
185
+
186
+ logger.debug("Pane %s running: %s", pane_target, is_active)
187
+ return is_active
188
+
189
+ except Exception as e:
190
+ logger.warning("Error checking if pane %s is running: %s", pane_target, e)
191
+ return False