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,291 @@
1
+ """Context Usage Tracker Service.
2
+
3
+ WHY: Track cumulative token usage across Claude Code hook invocations to prevent
4
+ context window exhaustion and enable intelligent auto-pause behavior.
5
+
6
+ DESIGN DECISIONS:
7
+ - File-based persistence (hooks run in separate processes)
8
+ - Atomic file operations using StateStorage
9
+ - Threshold detection at 70% (caution), 85% (warning), 90% (auto_pause), 95% (critical)
10
+ - Session-scoped tracking with reset capability
11
+ - Compatible with 200k context budget for Claude Sonnet 4.5
12
+
13
+ USAGE:
14
+ tracker = ContextUsageTracker()
15
+ state = tracker.update_usage(input_tokens=15000, output_tokens=2000)
16
+ if tracker.should_auto_pause():
17
+ # Trigger auto-pause workflow
18
+ pass
19
+ """
20
+
21
+ from dataclasses import asdict, dataclass, field
22
+ from datetime import datetime, timezone
23
+ from pathlib import Path
24
+ from typing import Optional
25
+
26
+ from claude_mpm.core.logger import get_logger
27
+ from claude_mpm.storage.state_storage import StateStorage
28
+
29
+ logger = get_logger(__name__)
30
+
31
+
32
+ @dataclass
33
+ class ContextUsageState:
34
+ """State tracking for cumulative context/token usage.
35
+
36
+ Attributes:
37
+ session_id: Unique session identifier
38
+ cumulative_input_tokens: Total input tokens across all hook invocations
39
+ cumulative_output_tokens: Total output tokens across all hook invocations
40
+ cache_creation_tokens: Total tokens spent creating prompt cache
41
+ cache_read_tokens: Total tokens read from prompt cache
42
+ percentage_used: Percentage of 200k context budget used (0.0-100.0)
43
+ threshold_reached: Highest threshold crossed ('caution', 'warning', 'auto_pause', 'critical')
44
+ auto_pause_active: Whether auto-pause has been triggered
45
+ last_updated: ISO timestamp of last update
46
+ """
47
+
48
+ session_id: str
49
+ cumulative_input_tokens: int = 0
50
+ cumulative_output_tokens: int = 0
51
+ cache_creation_tokens: int = 0
52
+ cache_read_tokens: int = 0
53
+ percentage_used: float = 0.0
54
+ threshold_reached: Optional[str] = None
55
+ auto_pause_active: bool = False
56
+ last_updated: str = field(
57
+ default_factory=lambda: datetime.now(timezone.utc).isoformat()
58
+ )
59
+
60
+
61
+ class ContextUsageTracker:
62
+ """Track cumulative context/token usage across hook invocations.
63
+
64
+ Features:
65
+ - Cumulative tracking across multiple API calls
66
+ - File-based persistence for cross-process state sharing
67
+ - Atomic file operations for concurrent safety
68
+ - Threshold detection (70%, 85%, 90%, 95%)
69
+ - Auto-pause triggering at 90%+ usage
70
+ - Session management with reset capability
71
+ """
72
+
73
+ # Claude Sonnet 4.5 context window
74
+ CONTEXT_BUDGET = 200000
75
+
76
+ # Threshold levels for warnings and auto-pause
77
+ THRESHOLDS = {
78
+ "caution": 0.70, # Yellow warning
79
+ "warning": 0.85, # Orange warning
80
+ "auto_pause": 0.90, # Trigger auto-pause
81
+ "critical": 0.95, # Red critical alert
82
+ }
83
+
84
+ def __init__(self, project_path: Optional[Path] = None):
85
+ """Initialize context usage tracker.
86
+
87
+ Args:
88
+ project_path: Project root path (default: current directory)
89
+ """
90
+ self.project_path = (project_path or Path.cwd()).resolve()
91
+ self.state_dir = self.project_path / ".claude-mpm" / "state"
92
+ self.state_dir.mkdir(parents=True, exist_ok=True)
93
+ self.state_file = self.state_dir / "context-usage.json"
94
+
95
+ # Use StateStorage for atomic operations
96
+ self.storage = StateStorage(self.state_dir)
97
+
98
+ logger.debug(f"ContextUsageTracker initialized: {self.state_file}")
99
+
100
+ def update_usage(
101
+ self,
102
+ input_tokens: int,
103
+ output_tokens: int,
104
+ cache_creation: int = 0,
105
+ cache_read: int = 0,
106
+ ) -> ContextUsageState:
107
+ """Update cumulative usage from API response.
108
+
109
+ Args:
110
+ input_tokens: Input tokens from this API call
111
+ output_tokens: Output tokens from this API call
112
+ cache_creation: Cache creation tokens (optional)
113
+ cache_read: Cache read tokens (optional)
114
+
115
+ Returns:
116
+ Updated context usage state
117
+
118
+ Raises:
119
+ ValueError: If token counts are negative
120
+ """
121
+ if any(
122
+ t < 0 for t in [input_tokens, output_tokens, cache_creation, cache_read]
123
+ ):
124
+ raise ValueError("Token counts cannot be negative")
125
+
126
+ # Load current state
127
+ state = self._load_state()
128
+
129
+ # Update cumulative counters
130
+ state.cumulative_input_tokens += input_tokens
131
+ state.cumulative_output_tokens += output_tokens
132
+ state.cache_creation_tokens += cache_creation
133
+ state.cache_read_tokens += cache_read
134
+
135
+ # Calculate total effective tokens (input + output, cache read is "free")
136
+ total_tokens = state.cumulative_input_tokens + state.cumulative_output_tokens
137
+ state.percentage_used = (total_tokens / self.CONTEXT_BUDGET) * 100
138
+
139
+ # Check thresholds
140
+ state.threshold_reached = self.check_thresholds(state)
141
+
142
+ # Activate auto-pause if threshold reached
143
+ if state.threshold_reached in {"auto_pause", "critical"}:
144
+ state.auto_pause_active = True
145
+
146
+ # Update timestamp
147
+ state.last_updated = datetime.now(timezone.utc).isoformat()
148
+
149
+ # Persist state atomically
150
+ self._save_state(state)
151
+
152
+ logger.debug(
153
+ f"Usage updated: {total_tokens}/{self.CONTEXT_BUDGET} tokens "
154
+ f"({state.percentage_used:.1f}%), threshold: {state.threshold_reached}"
155
+ )
156
+
157
+ return state
158
+
159
+ def check_thresholds(
160
+ self, state: Optional[ContextUsageState] = None
161
+ ) -> Optional[str]:
162
+ """Check which threshold (if any) has been exceeded.
163
+
164
+ Args:
165
+ state: Optional state to check (uses current state if None)
166
+
167
+ Returns:
168
+ Highest threshold exceeded ('caution', 'warning', 'auto_pause', 'critical')
169
+ or None if no thresholds exceeded
170
+ """
171
+ if state is None:
172
+ state = self.get_current_state()
173
+
174
+ percentage = state.percentage_used / 100 # Convert to 0.0-1.0
175
+
176
+ # Check thresholds in descending order (highest first)
177
+ for threshold_name in ["critical", "auto_pause", "warning", "caution"]:
178
+ if percentage >= self.THRESHOLDS[threshold_name]:
179
+ return threshold_name
180
+
181
+ return None
182
+
183
+ def should_auto_pause(self) -> bool:
184
+ """Check if auto-pause should be triggered.
185
+
186
+ Returns:
187
+ True if 90%+ context budget used
188
+ """
189
+ state = self.get_current_state()
190
+ return state.percentage_used >= (self.THRESHOLDS["auto_pause"] * 100)
191
+
192
+ def get_current_state(self) -> ContextUsageState:
193
+ """Get current usage state without modifying.
194
+
195
+ Returns:
196
+ Current context usage state
197
+ """
198
+ return self._load_state()
199
+
200
+ def reset_session(self, new_session_id: str) -> None:
201
+ """Reset tracking for a new session.
202
+
203
+ Args:
204
+ new_session_id: New session identifier
205
+ """
206
+ state = ContextUsageState(session_id=new_session_id)
207
+ self._save_state(state)
208
+ logger.info(f"Context usage reset for new session: {new_session_id}")
209
+
210
+ def _load_state(self) -> ContextUsageState:
211
+ """Load state from persistence file.
212
+
213
+ Returns:
214
+ Loaded state or default state if file doesn't exist/is corrupted
215
+ """
216
+ try:
217
+ if not self.state_file.exists():
218
+ # Generate initial session ID
219
+ session_id = (
220
+ f"session-{datetime.now(timezone.utc).strftime('%Y%m%d-%H%M%S')}"
221
+ )
222
+ logger.debug("No state file found, creating default state")
223
+ return ContextUsageState(session_id=session_id)
224
+
225
+ # Load JSON state
226
+ data = self.storage.read_json(self.state_file)
227
+
228
+ if data is None:
229
+ logger.warning("Failed to read state file, using default state")
230
+ session_id = (
231
+ f"session-{datetime.now(timezone.utc).strftime('%Y%m%d-%H%M%S')}"
232
+ )
233
+ return ContextUsageState(session_id=session_id)
234
+
235
+ # Reconstruct ContextUsageState from dict
236
+ return ContextUsageState(**data)
237
+
238
+ except Exception as e:
239
+ logger.error(f"Error loading state, using default: {e}")
240
+ session_id = (
241
+ f"session-{datetime.now(timezone.utc).strftime('%Y%m%d-%H%M%S')}"
242
+ )
243
+ return ContextUsageState(session_id=session_id)
244
+
245
+ def _save_state(self, state: ContextUsageState) -> None:
246
+ """Save state to persistence file atomically.
247
+
248
+ Args:
249
+ state: Context usage state to persist
250
+
251
+ Raises:
252
+ RuntimeError: If atomic write fails
253
+ """
254
+ try:
255
+ # Convert dataclass to dict
256
+ state_dict = asdict(state)
257
+
258
+ # Atomic write using StateStorage
259
+ if not self.storage.write_json(state_dict, self.state_file, atomic=True):
260
+ raise RuntimeError(f"Failed to write state to {self.state_file}")
261
+
262
+ logger.debug(f"State saved: {self.state_file}")
263
+
264
+ except Exception as e:
265
+ logger.error(f"Error saving state: {e}")
266
+ raise RuntimeError(f"Failed to persist context usage state: {e}") from e
267
+
268
+ def get_usage_summary(self) -> dict:
269
+ """Get human-readable usage summary.
270
+
271
+ Returns:
272
+ Dictionary with usage statistics
273
+ """
274
+ state = self.get_current_state()
275
+ total_tokens = state.cumulative_input_tokens + state.cumulative_output_tokens
276
+
277
+ return {
278
+ "session_id": state.session_id,
279
+ "total_tokens": total_tokens,
280
+ "budget": self.CONTEXT_BUDGET,
281
+ "percentage_used": round(state.percentage_used, 2),
282
+ "threshold_reached": state.threshold_reached,
283
+ "auto_pause_active": state.auto_pause_active,
284
+ "breakdown": {
285
+ "input_tokens": state.cumulative_input_tokens,
286
+ "output_tokens": state.cumulative_output_tokens,
287
+ "cache_creation_tokens": state.cache_creation_tokens,
288
+ "cache_read_tokens": state.cache_read_tokens,
289
+ },
290
+ "last_updated": state.last_updated,
291
+ }
@@ -6,7 +6,8 @@ Integrates with session management and response tracking infrastructure.
6
6
  Triggers:
7
7
  - model_context_window_exceeded (stop_reason)
8
8
  - Manual pause command
9
- - 95% token threshold reached
9
+ - 95% token threshold reached (critical)
10
+ - 90% token threshold reached (auto-pause)
10
11
  - Session end with high token usage (>85%)
11
12
 
12
13
  Design Principles:
@@ -71,6 +72,7 @@ class ResumeLogGenerator:
71
72
  thresholds = self.config.get("context_management", {}).get("thresholds", {})
72
73
  self.threshold_caution = thresholds.get("caution", 0.70)
73
74
  self.threshold_warning = thresholds.get("warning", 0.85)
75
+ self.threshold_auto_pause = thresholds.get("auto_pause", 0.90)
74
76
  self.threshold_critical = thresholds.get("critical", 0.95)
75
77
 
76
78
  logger.info(
@@ -96,14 +98,14 @@ class ResumeLogGenerator:
96
98
  if not self.enabled or not self.auto_generate:
97
99
  return manual_trigger # Only generate on manual trigger if auto is disabled
98
100
 
99
- # Trigger conditions
101
+ # Trigger conditions (ordered by severity)
100
102
  triggers = [
101
103
  stop_reason == "max_tokens",
102
104
  stop_reason == "model_context_window_exceeded",
103
105
  manual_trigger,
104
- token_usage_pct and token_usage_pct >= self.threshold_critical,
105
- token_usage_pct
106
- and token_usage_pct >= self.threshold_warning, # Generate at 85% too
106
+ token_usage_pct and token_usage_pct >= self.threshold_critical, # 95%
107
+ token_usage_pct and token_usage_pct >= self.threshold_auto_pause, # 90%
108
+ token_usage_pct and token_usage_pct >= self.threshold_warning, # 85%
107
109
  ]
108
110
 
109
111
  should_gen = any(triggers)
@@ -121,6 +123,22 @@ class ResumeLogGenerator:
121
123
 
122
124
  return should_gen
123
125
 
126
+ def should_auto_pause(self, token_usage_pct: Optional[float]) -> bool:
127
+ """Check if auto-pause threshold (90%) has been reached.
128
+
129
+ This is a convenience method to check specifically for the 90% threshold
130
+ which triggers automatic session pausing.
131
+
132
+ Args:
133
+ token_usage_pct: Current token usage percentage (0.0-1.0)
134
+
135
+ Returns:
136
+ True if auto-pause threshold has been reached
137
+ """
138
+ if token_usage_pct is None:
139
+ return False
140
+ return token_usage_pct >= self.threshold_auto_pause
141
+
124
142
  def generate_from_session_state(
125
143
  self,
126
144
  session_id: str,
@@ -427,6 +445,7 @@ class ResumeLogGenerator:
427
445
  "thresholds": {
428
446
  "caution": f"{self.threshold_caution:.0%}",
429
447
  "warning": f"{self.threshold_warning:.0%}",
448
+ "auto_pause": f"{self.threshold_auto_pause:.0%}",
430
449
  "critical": f"{self.threshold_critical:.0%}",
431
450
  },
432
451
  }
@@ -10,7 +10,7 @@ MCP service installations.
10
10
  """
11
11
 
12
12
  import json
13
- import subprocess
13
+ import subprocess # nosec B404 - Required for MCP service management
14
14
  import sys
15
15
  from enum import Enum
16
16
  from pathlib import Path
@@ -150,6 +150,86 @@ class MCPConfigManager:
150
150
 
151
151
  return is_enabled
152
152
 
153
+ def get_registry_service_config(
154
+ self, service_name: str, env_overrides: Optional[Dict[str, str]] = None
155
+ ) -> Optional[Dict]:
156
+ """
157
+ Get configuration for a service from the MCP Service Registry.
158
+
159
+ Args:
160
+ service_name: Name of the service
161
+ env_overrides: Optional environment variable overrides
162
+
163
+ Returns:
164
+ Service configuration dict or None if service not in registry
165
+ """
166
+ try:
167
+ from .mcp_service_registry import MCPServiceRegistry
168
+
169
+ service = MCPServiceRegistry.get(service_name)
170
+ if not service:
171
+ return None
172
+
173
+ return MCPServiceRegistry.generate_config(service, env_overrides)
174
+ except ImportError:
175
+ self.logger.debug("MCP Service Registry not available")
176
+ return None
177
+
178
+ def filter_services_by_mcp_flag(
179
+ self, mcp_flag: Optional[str], all_services: Dict[str, Dict]
180
+ ) -> Dict[str, Dict]:
181
+ """
182
+ Filter MCP services based on the --mcp command line flag.
183
+
184
+ Args:
185
+ mcp_flag: Comma-separated list of service names, or None for all
186
+ all_services: Dict of all available service configurations
187
+
188
+ Returns:
189
+ Filtered dict of service configurations
190
+ """
191
+ if not mcp_flag:
192
+ return all_services
193
+
194
+ # Parse comma-separated service names
195
+ requested_services = {s.strip() for s in mcp_flag.split(",") if s.strip()}
196
+
197
+ # Filter services
198
+ filtered = {}
199
+ for name, config in all_services.items():
200
+ if name in requested_services:
201
+ filtered[name] = config
202
+ else:
203
+ self.logger.debug(f"MCP service '{name}' excluded by --mcp flag")
204
+
205
+ # Warn about requested services that don't exist
206
+ available = set(all_services.keys())
207
+ missing = requested_services - available
208
+ if missing:
209
+ self.logger.warning(
210
+ f"Requested MCP services not available: {', '.join(missing)}"
211
+ )
212
+
213
+ return filtered
214
+
215
+ def list_available_services(self) -> list[str]:
216
+ """
217
+ List all available MCP services from registry and static configs.
218
+
219
+ Returns:
220
+ List of service names
221
+ """
222
+ services = set(self.STATIC_MCP_CONFIGS.keys())
223
+
224
+ try:
225
+ from .mcp_service_registry import MCPServiceRegistry
226
+
227
+ services.update(MCPServiceRegistry.list_names())
228
+ except ImportError:
229
+ pass
230
+
231
+ return sorted(services)
232
+
153
233
  def detect_service_path(self, service_name: str) -> Optional[str]:
154
234
  """
155
235
  Detect the best path for an MCP service.
@@ -185,7 +265,7 @@ class MCPConfigManager:
185
265
  # Choose the best candidate (prefer v1.1.0+ with MCP support)
186
266
  for path in candidates:
187
267
  try:
188
- result = subprocess.run(
268
+ result = subprocess.run( # nosec B603 B607 - Controlled service help check
189
269
  [path, "--help"],
190
270
  capture_output=True,
191
271
  text=True,
@@ -258,7 +338,7 @@ class MCPConfigManager:
258
338
  def _check_system_path(self, service_name: str) -> Optional[str]:
259
339
  """Check if service is available in system PATH."""
260
340
  try:
261
- result = subprocess.run(
341
+ result = subprocess.run( # nosec B603 B607 - Controlled which command
262
342
  ["which", service_name],
263
343
  capture_output=True,
264
344
  text=True,
@@ -356,7 +436,7 @@ class MCPConfigManager:
356
436
  cmd.append("--help")
357
437
 
358
438
  # Run test command with timeout
359
- result = subprocess.run(
439
+ result = subprocess.run( # nosec B603 - Controlled service test command
360
440
  cmd,
361
441
  capture_output=True,
362
442
  text=True,
@@ -560,7 +640,7 @@ class MCPConfigManager:
560
640
  # Try pipx run test
561
641
  if shutil.which("pipx"):
562
642
  try:
563
- result = subprocess.run(
643
+ result = subprocess.run( # nosec B603 B607 - Controlled pipx run command
564
644
  ["pipx", "run", service_name, "--version"],
565
645
  capture_output=True,
566
646
  text=True,
@@ -576,7 +656,7 @@ class MCPConfigManager:
576
656
  # Try uvx if pipx run not available
577
657
  if not use_pipx_run and shutil.which("uvx"):
578
658
  try:
579
- result = subprocess.run(
659
+ result = subprocess.run( # nosec B603 B607 - Controlled uvx command
580
660
  ["uvx", service_name, "--version"],
581
661
  capture_output=True,
582
662
  text=True,
@@ -876,7 +956,7 @@ class MCPConfigManager:
876
956
  command_path = service_config.get("command", "")
877
957
  results[service_name] = Path(command_path).exists()
878
958
  return results
879
- except Exception:
959
+ except Exception: # nosec B110 - Graceful fallback to empty dict
880
960
  pass
881
961
  return {}
882
962
 
@@ -946,7 +1026,7 @@ class MCPConfigManager:
946
1026
  if shutil.which("pipx"):
947
1027
  try:
948
1028
  self.logger.debug(f"Attempting to install {service_name} via pipx...")
949
- result = subprocess.run(
1029
+ result = subprocess.run( # nosec B603 B607 - Controlled pipx install
950
1030
  ["pipx", "install", service_name],
951
1031
  capture_output=True,
952
1032
  text=True,
@@ -980,7 +1060,7 @@ class MCPConfigManager:
980
1060
  if shutil.which("uvx"):
981
1061
  try:
982
1062
  self.logger.debug(f"Attempting to install {service_name} via uvx...")
983
- result = subprocess.run(
1063
+ result = subprocess.run( # nosec B603 B607 - Controlled uvx install
984
1064
  ["uvx", "install", service_name],
985
1065
  capture_output=True,
986
1066
  text=True,
@@ -997,7 +1077,7 @@ class MCPConfigManager:
997
1077
  # Method 3: Try pip install --user
998
1078
  try:
999
1079
  self.logger.debug(f"Attempting to install {service_name} via pip --user...")
1000
- result = subprocess.run(
1080
+ result = subprocess.run( # nosec B603 B607 - Controlled pip install
1001
1081
  [sys.executable, "-m", "pip", "install", "--user", service_name],
1002
1082
  capture_output=True,
1003
1083
  text=True,
@@ -1093,7 +1173,7 @@ class MCPConfigManager:
1093
1173
  self.logger.debug(
1094
1174
  f" Testing {service_name} from installed pipx venv: {pipx_venv_bin}"
1095
1175
  )
1096
- result = subprocess.run(
1176
+ result = subprocess.run( # nosec B603 - Controlled service help check
1097
1177
  [str(pipx_venv_bin), "--help"],
1098
1178
  capture_output=True,
1099
1179
  text=True,
@@ -1145,7 +1225,7 @@ class MCPConfigManager:
1145
1225
  self.logger.debug(
1146
1226
  f" Testing {service_name} via pipx run (not installed in venv)"
1147
1227
  )
1148
- result = subprocess.run(
1228
+ result = subprocess.run( # nosec B603 B607 - Controlled pipx run command
1149
1229
  ["pipx", "run", service_name, "--help"],
1150
1230
  capture_output=True,
1151
1231
  text=True,
@@ -1216,7 +1296,7 @@ class MCPConfigManager:
1216
1296
  self.logger.debug(f"Uninstalling {service_name}...")
1217
1297
 
1218
1298
  # First uninstall the corrupted version
1219
- uninstall_result = subprocess.run(
1299
+ uninstall_result = subprocess.run( # nosec B603 B607 - Controlled pipx uninstall
1220
1300
  ["pipx", "uninstall", service_name],
1221
1301
  capture_output=True,
1222
1302
  text=True,
@@ -1229,7 +1309,7 @@ class MCPConfigManager:
1229
1309
 
1230
1310
  # Now reinstall
1231
1311
  self.logger.debug(f"Installing fresh {service_name}...")
1232
- install_result = subprocess.run(
1312
+ install_result = subprocess.run( # nosec B603 B607 - Controlled pipx install
1233
1313
  ["pipx", "install", service_name],
1234
1314
  capture_output=True,
1235
1315
  text=True,
@@ -1294,7 +1374,7 @@ class MCPConfigManager:
1294
1374
  for dep in missing_deps:
1295
1375
  try:
1296
1376
  self.logger.debug(f" Injecting {dep} into {service_name}...")
1297
- result = subprocess.run(
1377
+ result = subprocess.run( # nosec B603 B607 - Controlled pipx inject
1298
1378
  ["pipx", "inject", service_name, dep],
1299
1379
  capture_output=True,
1300
1380
  text=True,
@@ -1348,7 +1428,7 @@ class MCPConfigManager:
1348
1428
  return False
1349
1429
 
1350
1430
  self.logger.info(f" → Uninstalling {service_name}...")
1351
- uninstall_result = subprocess.run(
1431
+ uninstall_result = subprocess.run( # nosec B603 B607 - Controlled pipx uninstall
1352
1432
  ["pipx", "uninstall", service_name],
1353
1433
  capture_output=True,
1354
1434
  text=True,
@@ -1363,7 +1443,7 @@ class MCPConfigManager:
1363
1443
  )
1364
1444
 
1365
1445
  self.logger.info(f" → Installing fresh {service_name}...")
1366
- install_result = subprocess.run(
1446
+ install_result = subprocess.run( # nosec B603 B607 - Controlled pipx install
1367
1447
  ["pipx", "install", service_name],
1368
1448
  capture_output=True,
1369
1449
  text=True,
@@ -1439,7 +1519,7 @@ class MCPConfigManager:
1439
1519
  # Try pipx run as fallback for pipx installations
1440
1520
  if method == "pipx":
1441
1521
  try:
1442
- result = subprocess.run(
1522
+ result = subprocess.run( # nosec B603 B607 - Controlled pipx command
1443
1523
  ["pipx", "run", service_name, "--version"],
1444
1524
  capture_output=True,
1445
1525
  text=True,
@@ -1462,7 +1542,7 @@ class MCPConfigManager:
1462
1542
  ]
1463
1543
 
1464
1544
  for cmd in test_commands:
1465
- result = subprocess.run(
1545
+ result = subprocess.run( # nosec B603 - Controlled service verification
1466
1546
  cmd,
1467
1547
  capture_output=True,
1468
1548
  text=True,