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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +8 -5
  3. claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +109 -706
  5. claude_mpm/agents/WORKFLOW.md +2 -0
  6. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  7. claude_mpm/auth/__init__.py +35 -0
  8. claude_mpm/auth/callback_server.py +328 -0
  9. claude_mpm/auth/models.py +104 -0
  10. claude_mpm/auth/oauth_manager.py +266 -0
  11. claude_mpm/auth/providers/__init__.py +12 -0
  12. claude_mpm/auth/providers/base.py +165 -0
  13. claude_mpm/auth/providers/google.py +261 -0
  14. claude_mpm/auth/token_storage.py +252 -0
  15. claude_mpm/cli/commands/autotodos.py +566 -0
  16. claude_mpm/cli/commands/commander.py +216 -0
  17. claude_mpm/cli/commands/hook_errors.py +60 -60
  18. claude_mpm/cli/commands/mcp.py +29 -17
  19. claude_mpm/cli/commands/mcp_command_router.py +39 -0
  20. claude_mpm/cli/commands/mcp_service_commands.py +304 -0
  21. claude_mpm/cli/commands/monitor.py +2 -2
  22. claude_mpm/cli/commands/mpm_init/core.py +2 -2
  23. claude_mpm/cli/commands/oauth.py +481 -0
  24. claude_mpm/cli/commands/run.py +35 -3
  25. claude_mpm/cli/commands/skill_source.py +51 -2
  26. claude_mpm/cli/commands/skills.py +5 -3
  27. claude_mpm/cli/executor.py +128 -16
  28. claude_mpm/cli/helpers.py +1 -1
  29. claude_mpm/cli/parsers/base_parser.py +84 -1
  30. claude_mpm/cli/parsers/commander_parser.py +116 -0
  31. claude_mpm/cli/parsers/mcp_parser.py +79 -0
  32. claude_mpm/cli/parsers/oauth_parser.py +165 -0
  33. claude_mpm/cli/parsers/run_parser.py +10 -0
  34. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  35. claude_mpm/cli/parsers/skills_parser.py +5 -0
  36. claude_mpm/cli/startup.py +345 -40
  37. claude_mpm/cli/startup_display.py +76 -7
  38. claude_mpm/cli/startup_logging.py +2 -2
  39. claude_mpm/cli/startup_migrations.py +236 -0
  40. claude_mpm/cli/utils.py +7 -3
  41. claude_mpm/commander/__init__.py +78 -0
  42. claude_mpm/commander/adapters/__init__.py +60 -0
  43. claude_mpm/commander/adapters/auggie.py +260 -0
  44. claude_mpm/commander/adapters/base.py +288 -0
  45. claude_mpm/commander/adapters/claude_code.py +392 -0
  46. claude_mpm/commander/adapters/codex.py +237 -0
  47. claude_mpm/commander/adapters/communication.py +366 -0
  48. claude_mpm/commander/adapters/example_usage.py +310 -0
  49. claude_mpm/commander/adapters/mpm.py +389 -0
  50. claude_mpm/commander/adapters/registry.py +204 -0
  51. claude_mpm/commander/api/__init__.py +16 -0
  52. claude_mpm/commander/api/app.py +121 -0
  53. claude_mpm/commander/api/errors.py +133 -0
  54. claude_mpm/commander/api/routes/__init__.py +8 -0
  55. claude_mpm/commander/api/routes/events.py +184 -0
  56. claude_mpm/commander/api/routes/inbox.py +171 -0
  57. claude_mpm/commander/api/routes/messages.py +148 -0
  58. claude_mpm/commander/api/routes/projects.py +271 -0
  59. claude_mpm/commander/api/routes/sessions.py +226 -0
  60. claude_mpm/commander/api/routes/work.py +296 -0
  61. claude_mpm/commander/api/schemas.py +186 -0
  62. claude_mpm/commander/chat/__init__.py +7 -0
  63. claude_mpm/commander/chat/cli.py +149 -0
  64. claude_mpm/commander/chat/commands.py +124 -0
  65. claude_mpm/commander/chat/repl.py +1957 -0
  66. claude_mpm/commander/config.py +51 -0
  67. claude_mpm/commander/config_loader.py +115 -0
  68. claude_mpm/commander/core/__init__.py +10 -0
  69. claude_mpm/commander/core/block_manager.py +325 -0
  70. claude_mpm/commander/core/response_manager.py +323 -0
  71. claude_mpm/commander/daemon.py +603 -0
  72. claude_mpm/commander/env_loader.py +59 -0
  73. claude_mpm/commander/events/__init__.py +26 -0
  74. claude_mpm/commander/events/manager.py +392 -0
  75. claude_mpm/commander/frameworks/__init__.py +12 -0
  76. claude_mpm/commander/frameworks/base.py +233 -0
  77. claude_mpm/commander/frameworks/claude_code.py +58 -0
  78. claude_mpm/commander/frameworks/mpm.py +57 -0
  79. claude_mpm/commander/git/__init__.py +5 -0
  80. claude_mpm/commander/git/worktree_manager.py +212 -0
  81. claude_mpm/commander/inbox/__init__.py +16 -0
  82. claude_mpm/commander/inbox/dedup.py +128 -0
  83. claude_mpm/commander/inbox/inbox.py +224 -0
  84. claude_mpm/commander/inbox/models.py +70 -0
  85. claude_mpm/commander/instance_manager.py +868 -0
  86. claude_mpm/commander/llm/__init__.py +6 -0
  87. claude_mpm/commander/llm/openrouter_client.py +167 -0
  88. claude_mpm/commander/llm/summarizer.py +70 -0
  89. claude_mpm/commander/memory/__init__.py +45 -0
  90. claude_mpm/commander/memory/compression.py +347 -0
  91. claude_mpm/commander/memory/embeddings.py +230 -0
  92. claude_mpm/commander/memory/entities.py +310 -0
  93. claude_mpm/commander/memory/example_usage.py +290 -0
  94. claude_mpm/commander/memory/integration.py +325 -0
  95. claude_mpm/commander/memory/search.py +381 -0
  96. claude_mpm/commander/memory/store.py +657 -0
  97. claude_mpm/commander/models/__init__.py +18 -0
  98. claude_mpm/commander/models/events.py +127 -0
  99. claude_mpm/commander/models/project.py +162 -0
  100. claude_mpm/commander/models/work.py +214 -0
  101. claude_mpm/commander/parsing/__init__.py +20 -0
  102. claude_mpm/commander/parsing/extractor.py +132 -0
  103. claude_mpm/commander/parsing/output_parser.py +270 -0
  104. claude_mpm/commander/parsing/patterns.py +100 -0
  105. claude_mpm/commander/persistence/__init__.py +11 -0
  106. claude_mpm/commander/persistence/event_store.py +274 -0
  107. claude_mpm/commander/persistence/state_store.py +403 -0
  108. claude_mpm/commander/persistence/work_store.py +164 -0
  109. claude_mpm/commander/polling/__init__.py +13 -0
  110. claude_mpm/commander/polling/event_detector.py +104 -0
  111. claude_mpm/commander/polling/output_buffer.py +49 -0
  112. claude_mpm/commander/polling/output_poller.py +153 -0
  113. claude_mpm/commander/project_session.py +268 -0
  114. claude_mpm/commander/proxy/__init__.py +12 -0
  115. claude_mpm/commander/proxy/formatter.py +89 -0
  116. claude_mpm/commander/proxy/output_handler.py +191 -0
  117. claude_mpm/commander/proxy/relay.py +155 -0
  118. claude_mpm/commander/registry.py +410 -0
  119. claude_mpm/commander/runtime/__init__.py +10 -0
  120. claude_mpm/commander/runtime/executor.py +191 -0
  121. claude_mpm/commander/runtime/monitor.py +346 -0
  122. claude_mpm/commander/session/__init__.py +6 -0
  123. claude_mpm/commander/session/context.py +81 -0
  124. claude_mpm/commander/session/manager.py +59 -0
  125. claude_mpm/commander/tmux_orchestrator.py +362 -0
  126. claude_mpm/commander/web/__init__.py +1 -0
  127. claude_mpm/commander/work/__init__.py +30 -0
  128. claude_mpm/commander/work/executor.py +207 -0
  129. claude_mpm/commander/work/queue.py +405 -0
  130. claude_mpm/commander/workflow/__init__.py +27 -0
  131. claude_mpm/commander/workflow/event_handler.py +241 -0
  132. claude_mpm/commander/workflow/notifier.py +146 -0
  133. claude_mpm/commands/mpm-config.md +8 -0
  134. claude_mpm/commands/mpm-doctor.md +8 -0
  135. claude_mpm/commands/mpm-help.md +8 -0
  136. claude_mpm/commands/mpm-init.md +8 -0
  137. claude_mpm/commands/mpm-monitor.md +8 -0
  138. claude_mpm/commands/mpm-organize.md +8 -0
  139. claude_mpm/commands/mpm-postmortem.md +8 -0
  140. claude_mpm/commands/mpm-session-resume.md +9 -1
  141. claude_mpm/commands/mpm-status.md +8 -0
  142. claude_mpm/commands/mpm-ticket-view.md +8 -0
  143. claude_mpm/commands/mpm-version.md +8 -0
  144. claude_mpm/commands/mpm.md +8 -0
  145. claude_mpm/config/agent_presets.py +8 -7
  146. claude_mpm/config/skill_sources.py +16 -0
  147. claude_mpm/constants.py +5 -0
  148. claude_mpm/core/claude_runner.py +152 -0
  149. claude_mpm/core/config.py +35 -22
  150. claude_mpm/core/config_constants.py +74 -9
  151. claude_mpm/core/constants.py +56 -12
  152. claude_mpm/core/hook_manager.py +53 -4
  153. claude_mpm/core/interactive_session.py +5 -4
  154. claude_mpm/core/logger.py +26 -9
  155. claude_mpm/core/logging_utils.py +39 -13
  156. claude_mpm/core/network_config.py +148 -0
  157. claude_mpm/core/oneshot_session.py +7 -6
  158. claude_mpm/core/output_style_manager.py +52 -12
  159. claude_mpm/core/socketio_pool.py +47 -15
  160. claude_mpm/core/unified_config.py +10 -6
  161. claude_mpm/core/unified_paths.py +68 -80
  162. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  163. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  164. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
  165. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
  166. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
  167. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
  168. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
  169. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
  170. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
  171. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
  172. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
  173. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
  174. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  188. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
  189. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
  190. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
  191. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
  192. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
  193. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
  194. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
  195. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
  196. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
  197. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
  198. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
  199. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
  200. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
  201. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
  202. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
  203. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
  204. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
  205. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
  206. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  207. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
  208. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
  209. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
  210. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  211. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
  212. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
  213. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
  214. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
  215. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  216. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
  217. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
  218. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  219. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  220. claude_mpm/dashboard/static/svelte-build/index.html +9 -9
  221. claude_mpm/experimental/cli_enhancements.py +2 -1
  222. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  223. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  224. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  225. claude_mpm/hooks/claude_hooks/event_handlers.py +466 -136
  226. claude_mpm/hooks/claude_hooks/hook_handler.py +204 -104
  227. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  228. claude_mpm/hooks/claude_hooks/installer.py +291 -59
  229. claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
  230. claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
  231. claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
  232. claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
  233. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
  234. claude_mpm/hooks/claude_hooks/services/container.py +326 -0
  235. claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
  236. claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
  237. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
  238. claude_mpm/hooks/session_resume_hook.py +89 -1
  239. claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
  240. claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
  241. claude_mpm/init.py +22 -15
  242. claude_mpm/mcp/__init__.py +9 -0
  243. claude_mpm/mcp/google_workspace_server.py +610 -0
  244. claude_mpm/scripts/claude-hook-handler.sh +46 -19
  245. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  246. claude_mpm/services/agents/agent_selection_service.py +2 -2
  247. claude_mpm/services/agents/cache_git_manager.py +1 -1
  248. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
  249. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  250. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  251. claude_mpm/services/cli/__init__.py +3 -0
  252. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  253. claude_mpm/services/cli/session_resume_helper.py +10 -2
  254. claude_mpm/services/command_deployment_service.py +44 -26
  255. claude_mpm/services/delegation_detector.py +175 -0
  256. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  257. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  258. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  259. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  260. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  261. claude_mpm/services/diagnostics/models.py +14 -1
  262. claude_mpm/services/event_log.py +325 -0
  263. claude_mpm/services/hook_installer_service.py +77 -8
  264. claude_mpm/services/infrastructure/__init__.py +4 -0
  265. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  266. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  267. claude_mpm/services/mcp_config_manager.py +99 -19
  268. claude_mpm/services/mcp_service_registry.py +294 -0
  269. claude_mpm/services/monitor/daemon_manager.py +15 -4
  270. claude_mpm/services/monitor/management/lifecycle.py +8 -2
  271. claude_mpm/services/monitor/server.py +111 -16
  272. claude_mpm/services/pm_skills_deployer.py +261 -87
  273. claude_mpm/services/skills/git_skill_source_manager.py +130 -10
  274. claude_mpm/services/skills/selective_skill_deployer.py +142 -16
  275. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  276. claude_mpm/services/skills_deployer.py +31 -5
  277. claude_mpm/services/socketio/handlers/hook.py +14 -7
  278. claude_mpm/services/socketio/server/main.py +12 -4
  279. claude_mpm/skills/__init__.py +2 -1
  280. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  281. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  282. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  283. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  284. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  285. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  286. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  287. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  288. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  289. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  290. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  291. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  292. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  293. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  294. claude_mpm/skills/bundled/pm/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
  295. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  296. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  297. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  298. claude_mpm/skills/registry.py +295 -90
  299. claude_mpm/skills/skill_manager.py +4 -4
  300. claude_mpm-5.6.76.dist-info/METADATA +416 -0
  301. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/RECORD +312 -175
  302. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/WHEEL +1 -1
  303. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/entry_points.txt +2 -0
  304. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
  305. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
  306. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
  307. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
  308. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
  309. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
  310. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
  311. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
  312. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
  313. claude_mpm-5.4.85.dist-info/METADATA +0 -1023
  314. /claude_mpm/skills/bundled/pm/{pm-bug-reporting/pm-bug-reporting.md → mpm-bug-reporting/SKILL.md} +0 -0
  315. /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
  316. /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
  317. /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
  318. /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
  319. /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
  320. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE +0 -0
  321. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  322. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,610 @@
1
+ """Google Workspace MCP server integrated with claude-mpm OAuth storage.
2
+
3
+ This MCP server provides tools for interacting with Google Workspace APIs
4
+ (Calendar, Gmail, Drive) using OAuth tokens managed by claude-mpm's
5
+ TokenStorage system.
6
+
7
+ The server automatically handles token refresh when tokens expire,
8
+ using the OAuthManager for seamless re-authentication.
9
+ """
10
+
11
+ import asyncio
12
+ import json
13
+ import logging
14
+ from typing import Any, Optional
15
+
16
+ import httpx
17
+ from mcp.server import Server
18
+ from mcp.server.stdio import stdio_server
19
+ from mcp.types import TextContent, Tool
20
+
21
+ from claude_mpm.auth import OAuthManager, TokenStatus, TokenStorage
22
+
23
+ # Configure logging
24
+ logging.basicConfig(level=logging.INFO)
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # Service name for token storage - matches workspace-mcp convention
28
+ SERVICE_NAME = "workspace-mcp"
29
+
30
+ # Google API base URLs
31
+ CALENDAR_API_BASE = "https://www.googleapis.com/calendar/v3"
32
+ GMAIL_API_BASE = "https://gmail.googleapis.com/gmail/v1"
33
+ DRIVE_API_BASE = "https://www.googleapis.com/drive/v3"
34
+
35
+
36
+ class GoogleWorkspaceServer:
37
+ """MCP server for Google Workspace APIs.
38
+
39
+ Integrates with claude-mpm's TokenStorage for credential management
40
+ and provides tools for Calendar, Gmail, and Drive operations.
41
+
42
+ Attributes:
43
+ server: MCP Server instance.
44
+ storage: TokenStorage for retrieving OAuth tokens.
45
+ manager: OAuthManager for token refresh operations.
46
+ """
47
+
48
+ def __init__(self) -> None:
49
+ """Initialize the Google Workspace MCP server."""
50
+ self.server = Server("google-workspace-mpm")
51
+ self.storage = TokenStorage()
52
+ self.manager = OAuthManager(storage=self.storage)
53
+ self._setup_handlers()
54
+
55
+ def _setup_handlers(self) -> None:
56
+ """Register MCP tool handlers."""
57
+
58
+ @self.server.list_tools()
59
+ async def list_tools() -> list[Tool]:
60
+ """Return list of available tools."""
61
+ return [
62
+ Tool(
63
+ name="list_calendars",
64
+ description="List all calendars accessible by the authenticated user",
65
+ inputSchema={
66
+ "type": "object",
67
+ "properties": {},
68
+ "required": [],
69
+ },
70
+ ),
71
+ Tool(
72
+ name="get_events",
73
+ description="Get events from a calendar within a time range",
74
+ inputSchema={
75
+ "type": "object",
76
+ "properties": {
77
+ "calendar_id": {
78
+ "type": "string",
79
+ "description": "Calendar ID (default: 'primary')",
80
+ "default": "primary",
81
+ },
82
+ "time_min": {
83
+ "type": "string",
84
+ "description": "Start time in RFC3339 format (e.g., '2024-01-01T00:00:00Z')",
85
+ },
86
+ "time_max": {
87
+ "type": "string",
88
+ "description": "End time in RFC3339 format",
89
+ },
90
+ "max_results": {
91
+ "type": "integer",
92
+ "description": "Maximum number of events to return (default: 10)",
93
+ "default": 10,
94
+ },
95
+ },
96
+ "required": [],
97
+ },
98
+ ),
99
+ Tool(
100
+ name="search_gmail_messages",
101
+ description="Search Gmail messages using a query string",
102
+ inputSchema={
103
+ "type": "object",
104
+ "properties": {
105
+ "query": {
106
+ "type": "string",
107
+ "description": "Gmail search query (e.g., 'from:user@example.com subject:meeting')",
108
+ },
109
+ "max_results": {
110
+ "type": "integer",
111
+ "description": "Maximum number of messages to return (default: 10)",
112
+ "default": 10,
113
+ },
114
+ },
115
+ "required": ["query"],
116
+ },
117
+ ),
118
+ Tool(
119
+ name="get_gmail_message_content",
120
+ description="Get the full content of a Gmail message by ID",
121
+ inputSchema={
122
+ "type": "object",
123
+ "properties": {
124
+ "message_id": {
125
+ "type": "string",
126
+ "description": "Gmail message ID",
127
+ },
128
+ },
129
+ "required": ["message_id"],
130
+ },
131
+ ),
132
+ Tool(
133
+ name="search_drive_files",
134
+ description="Search Google Drive files using a query string",
135
+ inputSchema={
136
+ "type": "object",
137
+ "properties": {
138
+ "query": {
139
+ "type": "string",
140
+ "description": "Drive search query (e.g., 'name contains \"report\"')",
141
+ },
142
+ "max_results": {
143
+ "type": "integer",
144
+ "description": "Maximum number of files to return (default: 10)",
145
+ "default": 10,
146
+ },
147
+ },
148
+ "required": ["query"],
149
+ },
150
+ ),
151
+ Tool(
152
+ name="get_drive_file_content",
153
+ description="Get the content of a Google Drive file by ID (text files only)",
154
+ inputSchema={
155
+ "type": "object",
156
+ "properties": {
157
+ "file_id": {
158
+ "type": "string",
159
+ "description": "Google Drive file ID",
160
+ },
161
+ },
162
+ "required": ["file_id"],
163
+ },
164
+ ),
165
+ ]
166
+
167
+ @self.server.call_tool()
168
+ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
169
+ """Handle tool calls."""
170
+ try:
171
+ result = await self._dispatch_tool(name, arguments)
172
+ return [TextContent(type="text", text=json.dumps(result, indent=2))]
173
+ except Exception as e:
174
+ logger.exception(f"Error calling tool {name}")
175
+ return [
176
+ TextContent(
177
+ type="text",
178
+ text=json.dumps({"error": str(e)}, indent=2),
179
+ )
180
+ ]
181
+
182
+ async def _get_access_token(self) -> str:
183
+ """Get a valid access token, refreshing if necessary.
184
+
185
+ Returns:
186
+ Valid access token string.
187
+
188
+ Raises:
189
+ RuntimeError: If no token is available or refresh fails.
190
+ """
191
+ status = self.storage.get_status(SERVICE_NAME)
192
+
193
+ if status == TokenStatus.MISSING:
194
+ raise RuntimeError(
195
+ f"No OAuth token found for service '{SERVICE_NAME}'. "
196
+ "Please authenticate first using: claude-mpm auth login google"
197
+ )
198
+
199
+ if status == TokenStatus.INVALID:
200
+ raise RuntimeError(
201
+ f"OAuth token for service '{SERVICE_NAME}' is invalid or corrupted. "
202
+ "Please re-authenticate using: claude-mpm auth login google"
203
+ )
204
+
205
+ # Try to refresh if expired
206
+ if status == TokenStatus.EXPIRED:
207
+ logger.info("Token expired, attempting refresh...")
208
+ token = await self.manager.refresh_if_needed(SERVICE_NAME)
209
+ if token is None:
210
+ raise RuntimeError(
211
+ "Token refresh failed. Please re-authenticate using: "
212
+ "claude-mpm auth login google"
213
+ )
214
+ return token.access_token
215
+
216
+ # Token is valid
217
+ stored = self.storage.retrieve(SERVICE_NAME)
218
+ if stored is None:
219
+ raise RuntimeError("Unexpected error: token retrieval failed")
220
+
221
+ return stored.token.access_token
222
+
223
+ async def _make_request(
224
+ self,
225
+ method: str,
226
+ url: str,
227
+ params: Optional[dict[str, Any]] = None,
228
+ json_data: Optional[dict[str, Any]] = None,
229
+ ) -> dict[str, Any]:
230
+ """Make an authenticated HTTP request to Google APIs.
231
+
232
+ Args:
233
+ method: HTTP method (GET, POST, etc.).
234
+ url: Full URL to request.
235
+ params: Optional query parameters.
236
+ json_data: Optional JSON body data.
237
+
238
+ Returns:
239
+ JSON response as a dictionary.
240
+
241
+ Raises:
242
+ httpx.HTTPStatusError: If the request fails.
243
+ """
244
+ access_token = await self._get_access_token()
245
+
246
+ async with httpx.AsyncClient() as client:
247
+ response = await client.request(
248
+ method=method,
249
+ url=url,
250
+ params=params,
251
+ json=json_data,
252
+ headers={
253
+ "Authorization": f"Bearer {access_token}",
254
+ "Accept": "application/json",
255
+ },
256
+ timeout=30.0,
257
+ )
258
+ response.raise_for_status()
259
+ result: dict[str, Any] = response.json()
260
+ return result
261
+
262
+ async def _dispatch_tool(
263
+ self, name: str, arguments: dict[str, Any]
264
+ ) -> dict[str, Any]:
265
+ """Dispatch tool call to appropriate handler.
266
+
267
+ Args:
268
+ name: Tool name.
269
+ arguments: Tool arguments.
270
+
271
+ Returns:
272
+ Tool result as dictionary.
273
+
274
+ Raises:
275
+ ValueError: If tool name is not recognized.
276
+ """
277
+ handlers = {
278
+ "list_calendars": self._list_calendars,
279
+ "get_events": self._get_events,
280
+ "search_gmail_messages": self._search_gmail_messages,
281
+ "get_gmail_message_content": self._get_gmail_message_content,
282
+ "search_drive_files": self._search_drive_files,
283
+ "get_drive_file_content": self._get_drive_file_content,
284
+ }
285
+
286
+ handler = handlers.get(name)
287
+ if handler is None:
288
+ raise ValueError(f"Unknown tool: {name}")
289
+
290
+ return await handler(arguments)
291
+
292
+ async def _list_calendars(self, arguments: dict[str, Any]) -> dict[str, Any]:
293
+ """List all calendars accessible by the user.
294
+
295
+ Args:
296
+ arguments: Tool arguments (not used).
297
+
298
+ Returns:
299
+ List of calendars with id, summary, and access role.
300
+ """
301
+ url = f"{CALENDAR_API_BASE}/users/me/calendarList"
302
+ response = await self._make_request("GET", url)
303
+
304
+ calendars = []
305
+ for item in response.get("items", []):
306
+ calendars.append(
307
+ {
308
+ "id": item.get("id"),
309
+ "summary": item.get("summary"),
310
+ "description": item.get("description"),
311
+ "access_role": item.get("accessRole"),
312
+ "primary": item.get("primary", False),
313
+ }
314
+ )
315
+
316
+ return {"calendars": calendars, "count": len(calendars)}
317
+
318
+ async def _get_events(self, arguments: dict[str, Any]) -> dict[str, Any]:
319
+ """Get events from a calendar.
320
+
321
+ Args:
322
+ arguments: Tool arguments with calendar_id, time_min, time_max, max_results.
323
+
324
+ Returns:
325
+ List of events with summary, start, end times.
326
+ """
327
+ calendar_id = arguments.get("calendar_id", "primary")
328
+ time_min = arguments.get("time_min")
329
+ time_max = arguments.get("time_max")
330
+ max_results = arguments.get("max_results", 10)
331
+
332
+ url = f"{CALENDAR_API_BASE}/calendars/{calendar_id}/events"
333
+ params: dict[str, Any] = {
334
+ "maxResults": max_results,
335
+ "singleEvents": True,
336
+ "orderBy": "startTime",
337
+ }
338
+
339
+ if time_min:
340
+ params["timeMin"] = time_min
341
+ if time_max:
342
+ params["timeMax"] = time_max
343
+
344
+ response = await self._make_request("GET", url, params=params)
345
+
346
+ events = []
347
+ for item in response.get("items", []):
348
+ start = item.get("start", {})
349
+ end = item.get("end", {})
350
+ events.append(
351
+ {
352
+ "id": item.get("id"),
353
+ "summary": item.get("summary"),
354
+ "description": item.get("description"),
355
+ "start": start.get("dateTime") or start.get("date"),
356
+ "end": end.get("dateTime") or end.get("date"),
357
+ "location": item.get("location"),
358
+ "attendees": [a.get("email") for a in item.get("attendees", [])],
359
+ }
360
+ )
361
+
362
+ return {"events": events, "count": len(events)}
363
+
364
+ async def _search_gmail_messages(self, arguments: dict[str, Any]) -> dict[str, Any]:
365
+ """Search Gmail messages.
366
+
367
+ Args:
368
+ arguments: Tool arguments with query and max_results.
369
+
370
+ Returns:
371
+ List of message snippets with id, thread_id, subject, from, date.
372
+ """
373
+ query = arguments.get("query", "")
374
+ max_results = arguments.get("max_results", 10)
375
+
376
+ url = f"{GMAIL_API_BASE}/users/me/messages"
377
+ params = {"q": query, "maxResults": max_results}
378
+
379
+ response = await self._make_request("GET", url, params=params)
380
+
381
+ messages = []
382
+ for msg in response.get("messages", []):
383
+ # Get message metadata
384
+ msg_url = f"{GMAIL_API_BASE}/users/me/messages/{msg['id']}"
385
+ msg_detail = await self._make_request(
386
+ "GET", msg_url, params={"format": "metadata"}
387
+ )
388
+
389
+ headers = {
390
+ h["name"]: h["value"]
391
+ for h in msg_detail.get("payload", {}).get("headers", [])
392
+ }
393
+
394
+ messages.append(
395
+ {
396
+ "id": msg["id"],
397
+ "thread_id": msg.get("threadId"),
398
+ "subject": headers.get("Subject"),
399
+ "from": headers.get("From"),
400
+ "to": headers.get("To"),
401
+ "date": headers.get("Date"),
402
+ "snippet": msg_detail.get("snippet"),
403
+ }
404
+ )
405
+
406
+ return {"messages": messages, "count": len(messages)}
407
+
408
+ async def _get_gmail_message_content(
409
+ self, arguments: dict[str, Any]
410
+ ) -> dict[str, Any]:
411
+ """Get full content of a Gmail message.
412
+
413
+ Args:
414
+ arguments: Tool arguments with message_id.
415
+
416
+ Returns:
417
+ Message content including headers and body.
418
+ """
419
+ message_id = arguments["message_id"]
420
+
421
+ url = f"{GMAIL_API_BASE}/users/me/messages/{message_id}"
422
+ response = await self._make_request("GET", url, params={"format": "full"})
423
+
424
+ headers = {
425
+ h["name"]: h["value"]
426
+ for h in response.get("payload", {}).get("headers", [])
427
+ }
428
+
429
+ # Extract body content
430
+ body = self._extract_message_body(response.get("payload", {}))
431
+
432
+ return {
433
+ "id": response.get("id"),
434
+ "thread_id": response.get("threadId"),
435
+ "subject": headers.get("Subject"),
436
+ "from": headers.get("From"),
437
+ "to": headers.get("To"),
438
+ "cc": headers.get("Cc"),
439
+ "date": headers.get("Date"),
440
+ "body": body,
441
+ "labels": response.get("labelIds", []),
442
+ }
443
+
444
+ def _extract_message_body(self, payload: dict[str, Any]) -> str:
445
+ """Extract message body from Gmail payload.
446
+
447
+ Handles both simple and multipart messages.
448
+
449
+ Args:
450
+ payload: Gmail message payload.
451
+
452
+ Returns:
453
+ Decoded message body text.
454
+ """
455
+ import base64
456
+
457
+ # Simple message with body data
458
+ if "body" in payload and payload["body"].get("data"):
459
+ data = payload["body"]["data"]
460
+ return base64.urlsafe_b64decode(data).decode("utf-8", errors="replace")
461
+
462
+ # Multipart message
463
+ parts = payload.get("parts", [])
464
+ for part in parts:
465
+ mime_type = part.get("mimeType", "")
466
+ if mime_type == "text/plain":
467
+ data = part.get("body", {}).get("data", "")
468
+ if data:
469
+ return base64.urlsafe_b64decode(data).decode(
470
+ "utf-8", errors="replace"
471
+ )
472
+ elif mime_type.startswith("multipart/"):
473
+ # Recursively extract from nested parts
474
+ result = self._extract_message_body(part)
475
+ if result:
476
+ return result
477
+
478
+ # Fallback to HTML if no plain text
479
+ for part in parts:
480
+ if part.get("mimeType") == "text/html":
481
+ data = part.get("body", {}).get("data", "")
482
+ if data:
483
+ return base64.urlsafe_b64decode(data).decode(
484
+ "utf-8", errors="replace"
485
+ )
486
+
487
+ return ""
488
+
489
+ async def _search_drive_files(self, arguments: dict[str, Any]) -> dict[str, Any]:
490
+ """Search Google Drive files.
491
+
492
+ Args:
493
+ arguments: Tool arguments with query and max_results.
494
+
495
+ Returns:
496
+ List of files with id, name, mimeType, modifiedTime.
497
+ """
498
+ query = arguments.get("query", "")
499
+ max_results = arguments.get("max_results", 10)
500
+
501
+ url = f"{DRIVE_API_BASE}/files"
502
+ params = {
503
+ "q": query,
504
+ "pageSize": max_results,
505
+ "fields": "files(id,name,mimeType,modifiedTime,size,webViewLink,owners)",
506
+ }
507
+
508
+ response = await self._make_request("GET", url, params=params)
509
+
510
+ files = []
511
+ for item in response.get("files", []):
512
+ files.append(
513
+ {
514
+ "id": item.get("id"),
515
+ "name": item.get("name"),
516
+ "mimeType": item.get("mimeType"),
517
+ "modifiedTime": item.get("modifiedTime"),
518
+ "size": item.get("size"),
519
+ "webViewLink": item.get("webViewLink"),
520
+ "owners": [o.get("emailAddress") for o in item.get("owners", [])],
521
+ }
522
+ )
523
+
524
+ return {"files": files, "count": len(files)}
525
+
526
+ async def _get_drive_file_content(
527
+ self, arguments: dict[str, Any]
528
+ ) -> dict[str, Any]:
529
+ """Get content of a Google Drive file.
530
+
531
+ Args:
532
+ arguments: Tool arguments with file_id.
533
+
534
+ Returns:
535
+ File metadata and content (for exportable types).
536
+ """
537
+ file_id = arguments["file_id"]
538
+
539
+ # First get file metadata
540
+ meta_url = f"{DRIVE_API_BASE}/files/{file_id}"
541
+ metadata = await self._make_request(
542
+ "GET", meta_url, params={"fields": "id,name,mimeType,size"}
543
+ )
544
+
545
+ mime_type = metadata.get("mimeType", "")
546
+
547
+ # Google Docs types need export
548
+ export_map = {
549
+ "application/vnd.google-apps.document": "text/plain",
550
+ "application/vnd.google-apps.spreadsheet": "text/csv",
551
+ "application/vnd.google-apps.presentation": "text/plain",
552
+ }
553
+
554
+ access_token = await self._get_access_token()
555
+
556
+ if mime_type in export_map:
557
+ # Export Google Workspace files
558
+ export_url = f"{DRIVE_API_BASE}/files/{file_id}/export"
559
+ async with httpx.AsyncClient() as client:
560
+ response = await client.get(
561
+ export_url,
562
+ params={"mimeType": export_map[mime_type]},
563
+ headers={"Authorization": f"Bearer {access_token}"},
564
+ timeout=30.0,
565
+ )
566
+ response.raise_for_status()
567
+ content = response.text
568
+ else:
569
+ # Download regular files
570
+ download_url = f"{DRIVE_API_BASE}/files/{file_id}"
571
+ async with httpx.AsyncClient() as client:
572
+ response = await client.get(
573
+ download_url,
574
+ params={"alt": "media"},
575
+ headers={"Authorization": f"Bearer {access_token}"},
576
+ timeout=30.0,
577
+ )
578
+ response.raise_for_status()
579
+
580
+ # Try to decode as text, otherwise indicate binary
581
+ try:
582
+ content = response.text
583
+ except UnicodeDecodeError:
584
+ content = f"[Binary file: {metadata.get('size', 'unknown')} bytes]"
585
+
586
+ return {
587
+ "id": metadata.get("id"),
588
+ "name": metadata.get("name"),
589
+ "mimeType": mime_type,
590
+ "content": content,
591
+ }
592
+
593
+ async def run(self) -> None:
594
+ """Run the MCP server using stdio transport."""
595
+ async with stdio_server() as (read_stream, write_stream):
596
+ await self.server.run(
597
+ read_stream,
598
+ write_stream,
599
+ self.server.create_initialization_options(),
600
+ )
601
+
602
+
603
+ def main() -> None:
604
+ """Entry point for the Google Workspace MCP server."""
605
+ server = GoogleWorkspaceServer()
606
+ asyncio.run(server.run())
607
+
608
+
609
+ if __name__ == "__main__":
610
+ main()