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,481 @@
1
+ """
2
+ OAuth management commands for claude-mpm CLI.
3
+
4
+ WHY: Users need a way to manage OAuth authentication for MCP services
5
+ that require OAuth2 flows (e.g., Google Workspace) directly from the terminal.
6
+
7
+ DESIGN DECISIONS:
8
+ - Use BaseCommand for consistent CLI patterns
9
+ - Reuse OAuth logic from commander/chat/repl.py
10
+ - Support multiple credential sources: .env.local, .env, environment variables
11
+ - Provide clear feedback during OAuth flow
12
+ """
13
+
14
+ import asyncio
15
+ import json
16
+ import os
17
+ from pathlib import Path
18
+ from typing import Any
19
+
20
+ from rich.console import Console
21
+ from rich.panel import Panel
22
+ from rich.table import Table
23
+
24
+ from ..shared import BaseCommand, CommandResult
25
+
26
+ console = Console()
27
+
28
+
29
+ def _load_oauth_credentials_from_env_files() -> tuple[str | None, str | None]:
30
+ """Load OAuth credentials from .env files.
31
+
32
+ Checks .env.local first (user overrides), then .env.
33
+ Returns tuple of (client_id, client_secret), either may be None.
34
+ """
35
+ client_id = None
36
+ client_secret = None
37
+
38
+ # Priority order: .env.local first (user overrides), then .env
39
+ env_files = [".env.local", ".env"]
40
+
41
+ for env_file in env_files:
42
+ env_path = Path.cwd() / env_file
43
+ if env_path.exists():
44
+ try:
45
+ with open(env_path) as f:
46
+ for line in f:
47
+ line = line.strip()
48
+ # Skip empty lines and comments
49
+ if not line or line.startswith("#"):
50
+ continue
51
+ if "=" in line:
52
+ key, _, value = line.partition("=")
53
+ key = key.strip()
54
+ value = value.strip().strip('"').strip("'")
55
+
56
+ if key == "GOOGLE_OAUTH_CLIENT_ID" and not client_id:
57
+ client_id = value
58
+ elif (
59
+ key == "GOOGLE_OAUTH_CLIENT_SECRET"
60
+ and not client_secret
61
+ ):
62
+ client_secret = value
63
+
64
+ # If we found both, no need to check more files
65
+ if client_id and client_secret:
66
+ break
67
+ except Exception: # nosec B110 - intentionally ignore .env file read errors
68
+ pass
69
+
70
+ return client_id, client_secret
71
+
72
+
73
+ class OAuthCommand(BaseCommand):
74
+ """OAuth management command for MCP services."""
75
+
76
+ def __init__(self):
77
+ super().__init__("oauth")
78
+
79
+ def validate_args(self, args) -> str | None:
80
+ """Validate command arguments."""
81
+ # If no oauth_command specified, default to 'list'
82
+ if not hasattr(args, "oauth_command") or not args.oauth_command:
83
+ args.oauth_command = None # Will show help
84
+ return None
85
+
86
+ valid_commands = ["list", "setup", "status", "revoke", "refresh"]
87
+ if args.oauth_command not in valid_commands:
88
+ return f"Unknown oauth command: {args.oauth_command}. Valid commands: {', '.join(valid_commands)}"
89
+
90
+ # Validate service_name for commands that require it
91
+ if args.oauth_command in ["setup", "status", "revoke", "refresh"]:
92
+ if not hasattr(args, "service_name") or not args.service_name:
93
+ return f"oauth {args.oauth_command} requires a service name"
94
+
95
+ return None
96
+
97
+ def run(self, args) -> CommandResult:
98
+ """Execute the OAuth command."""
99
+ # If no subcommand, show help
100
+ if not hasattr(args, "oauth_command") or not args.oauth_command:
101
+ self._show_help()
102
+ return CommandResult.success_result("Help displayed")
103
+
104
+ if args.oauth_command == "list":
105
+ return self._list_services(args)
106
+ if args.oauth_command == "setup":
107
+ return self._setup_oauth(args)
108
+ if args.oauth_command == "status":
109
+ return self._show_status(args)
110
+ if args.oauth_command == "revoke":
111
+ return self._revoke_tokens(args)
112
+ if args.oauth_command == "refresh":
113
+ return self._refresh_tokens(args)
114
+
115
+ return CommandResult.error_result(
116
+ f"Unknown oauth command: {args.oauth_command}"
117
+ )
118
+
119
+ def _show_help(self) -> None:
120
+ """Display OAuth command help."""
121
+ help_text = """
122
+ [bold]OAuth Commands:[/bold]
123
+ oauth list List OAuth-capable MCP services
124
+ oauth setup <service> Set up OAuth authentication for a service
125
+ oauth status <service> Show OAuth token status for a service
126
+ oauth revoke <service> Revoke OAuth tokens for a service
127
+ oauth refresh <service> Refresh OAuth tokens for a service
128
+
129
+ [bold]Examples:[/bold]
130
+ claude-mpm oauth list
131
+ claude-mpm oauth setup workspace-mcp
132
+ claude-mpm oauth status workspace-mcp
133
+ """
134
+ console.print(help_text)
135
+
136
+ def _list_services(self, args) -> CommandResult:
137
+ """List OAuth-capable MCP services."""
138
+ try:
139
+ from claude_mpm.services.mcp_service_registry import MCPServiceRegistry
140
+
141
+ services = MCPServiceRegistry.list_all()
142
+ oauth_services = [s for s in services if s.oauth_provider]
143
+
144
+ if not oauth_services:
145
+ console.print("[yellow]No OAuth-capable services found.[/yellow]")
146
+ return CommandResult.success_result("No OAuth services found")
147
+
148
+ # Check output format
149
+ output_format = getattr(args, "format", "table")
150
+
151
+ if output_format == "json":
152
+ data = [
153
+ {
154
+ "name": s.name,
155
+ "description": s.description,
156
+ "oauth_provider": s.oauth_provider,
157
+ "oauth_scopes": s.oauth_scopes,
158
+ "required_env": s.required_env,
159
+ }
160
+ for s in oauth_services
161
+ ]
162
+ console.print(json.dumps(data, indent=2))
163
+ return CommandResult.success_result(
164
+ "Services listed", data={"services": data}
165
+ )
166
+
167
+ # Table format
168
+ table = Table(title="OAuth-Capable MCP Services")
169
+ table.add_column("Service", style="cyan")
170
+ table.add_column("Provider", style="green")
171
+ table.add_column("Description", style="white")
172
+
173
+ for service in oauth_services:
174
+ table.add_row(
175
+ service.name,
176
+ service.oauth_provider or "",
177
+ service.description,
178
+ )
179
+
180
+ console.print(table)
181
+ return CommandResult.success_result(
182
+ f"Found {len(oauth_services)} OAuth-capable service(s)"
183
+ )
184
+
185
+ except ImportError:
186
+ return CommandResult.error_result("MCP Service Registry not available")
187
+ except Exception as e:
188
+ return CommandResult.error_result(f"Error listing services: {e}")
189
+
190
+ def _setup_oauth(self, args) -> CommandResult:
191
+ """Set up OAuth for a service."""
192
+ service_name = args.service_name
193
+
194
+ # Get service info from registry to get provider and scopes
195
+ try:
196
+ from claude_mpm.services.mcp_service_registry import MCPServiceRegistry
197
+
198
+ service = MCPServiceRegistry.get(service_name)
199
+ if not service:
200
+ return CommandResult.error_result(f"Service '{service_name}' not found")
201
+
202
+ provider_name = service.oauth_provider
203
+ if not provider_name:
204
+ return CommandResult.error_result(
205
+ f"Service '{service_name}' does not use OAuth"
206
+ )
207
+
208
+ scopes = service.oauth_scopes or None
209
+ except ImportError:
210
+ return CommandResult.error_result("MCP Service Registry not available")
211
+
212
+ # Priority: 1) .env files, 2) environment variables, 3) interactive prompt
213
+ client_id, client_secret = _load_oauth_credentials_from_env_files()
214
+
215
+ # Fall back to environment variables if not found in .env files
216
+ if not client_id:
217
+ client_id = os.environ.get("GOOGLE_OAUTH_CLIENT_ID")
218
+ if not client_secret:
219
+ client_secret = os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET")
220
+
221
+ # Set credentials in environment so OAuth provider can access them
222
+ if client_id and client_secret:
223
+ os.environ["GOOGLE_OAUTH_CLIENT_ID"] = client_id
224
+ os.environ["GOOGLE_OAUTH_CLIENT_SECRET"] = client_secret
225
+
226
+ # If credentials missing, prompt for them interactively
227
+ if not client_id or not client_secret:
228
+ console.print("\n[yellow]Google OAuth credentials not found.[/yellow]")
229
+ console.print("Checked: .env.local, .env, and environment variables.\n")
230
+ console.print(
231
+ "Get credentials from: https://console.cloud.google.com/apis/credentials\n"
232
+ )
233
+ console.print("[dim]Tip: Add to .env.local for automatic loading:[/dim]")
234
+ console.print('[dim] GOOGLE_OAUTH_CLIENT_ID="your-client-id"[/dim]')
235
+ console.print(
236
+ '[dim] GOOGLE_OAUTH_CLIENT_SECRET="your-client-secret"[/dim]\n' # pragma: allowlist secret
237
+ )
238
+
239
+ try:
240
+ from prompt_toolkit import prompt as pt_prompt
241
+
242
+ client_id = pt_prompt("Enter GOOGLE_OAUTH_CLIENT_ID: ")
243
+ if not client_id.strip():
244
+ return CommandResult.error_result("Client ID is required")
245
+
246
+ client_secret = pt_prompt(
247
+ "Enter GOOGLE_OAUTH_CLIENT_SECRET: ", is_password=True
248
+ )
249
+ if not client_secret.strip():
250
+ return CommandResult.error_result("Client Secret is required")
251
+
252
+ # Set in environment for this session
253
+ os.environ["GOOGLE_OAUTH_CLIENT_ID"] = client_id.strip()
254
+ os.environ["GOOGLE_OAUTH_CLIENT_SECRET"] = client_secret.strip()
255
+ console.print("\n[green]Credentials set for this session.[/green]")
256
+
257
+ except (EOFError, KeyboardInterrupt):
258
+ return CommandResult.error_result("Credential entry cancelled")
259
+ except ImportError:
260
+ return CommandResult.error_result(
261
+ "prompt_toolkit not available for interactive input. "
262
+ "Please set GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET environment variables."
263
+ )
264
+
265
+ # Run OAuth flow
266
+ try:
267
+ from claude_mpm.auth import OAuthManager
268
+ from claude_mpm.auth.callback_server import DEFAULT_PORT
269
+ from claude_mpm.auth.providers.google import OAuthError
270
+
271
+ manager = OAuthManager()
272
+
273
+ # Get the actual callback port from the server
274
+ callback_port = DEFAULT_PORT
275
+ no_browser = getattr(args, "no_browser", False)
276
+
277
+ console.print(f"\n[cyan]Setting up OAuth for '{service_name}'...[/cyan]")
278
+ console.print(
279
+ f"Callback server listening on http://localhost:{callback_port}/callback"
280
+ )
281
+
282
+ if not no_browser:
283
+ console.print("Opening browser for authentication...")
284
+ else:
285
+ console.print(
286
+ "[yellow]Browser auto-open disabled. Please open the URL manually.[/yellow]"
287
+ )
288
+
289
+ # Run async OAuth flow - authenticate returns OAuthToken directly
290
+ # and raises OAuthError on failure
291
+ token = asyncio.run(
292
+ manager.authenticate(
293
+ service_name=service_name,
294
+ provider_name=provider_name,
295
+ scopes=scopes,
296
+ open_browser=not no_browser,
297
+ )
298
+ )
299
+
300
+ # Success - token was returned
301
+ console.print(f"\n[green]OAuth setup complete for '{service_name}'[/green]")
302
+ if token.expires_at:
303
+ console.print(f" Token expires: {token.expires_at}")
304
+ return CommandResult.success_result(
305
+ f"OAuth setup complete for '{service_name}'"
306
+ )
307
+
308
+ except OAuthError as e:
309
+ return CommandResult.error_result(f"OAuth setup failed: {e}")
310
+ except ImportError as e:
311
+ return CommandResult.error_result(f"OAuth module not available: {e}")
312
+ except Exception as e:
313
+ return CommandResult.error_result(f"Error during OAuth setup: {e}")
314
+
315
+ def _show_status(self, args) -> CommandResult:
316
+ """Show OAuth token status for a service."""
317
+ service_name = args.service_name
318
+
319
+ try:
320
+ from claude_mpm.auth import OAuthManager
321
+ from claude_mpm.auth.models import TokenStatus
322
+
323
+ manager = OAuthManager()
324
+ # get_status is synchronous and returns (TokenStatus, StoredToken | None)
325
+ token_status, stored_token = manager.get_status(service_name)
326
+
327
+ if token_status == TokenStatus.MISSING or stored_token is None:
328
+ console.print(
329
+ f"[yellow]No OAuth tokens found for '{service_name}'[/yellow]"
330
+ )
331
+ return CommandResult.success_result(
332
+ f"No tokens found for '{service_name}'"
333
+ )
334
+
335
+ # Build status dict for display
336
+ is_valid = token_status == TokenStatus.VALID
337
+ status_data = {
338
+ "valid": is_valid,
339
+ "status": token_status.name,
340
+ "expires_at": stored_token.token.expires_at,
341
+ "scopes": stored_token.token.scopes,
342
+ }
343
+
344
+ # Check output format
345
+ output_format = getattr(args, "format", "table")
346
+
347
+ if output_format == "json":
348
+ console.print(json.dumps(status_data, indent=2, default=str))
349
+ return CommandResult.success_result(
350
+ "Status displayed", data=status_data
351
+ )
352
+
353
+ # Table format
354
+ self._print_token_status(service_name, status_data)
355
+ return CommandResult.success_result("Status displayed")
356
+
357
+ except ImportError:
358
+ return CommandResult.error_result("OAuth module not available")
359
+ except Exception as e:
360
+ return CommandResult.error_result(f"Error checking status: {e}")
361
+
362
+ def _print_token_status(self, name: str, status: dict[str, Any]) -> None:
363
+ """Print token status information."""
364
+ panel_content = []
365
+ panel_content.append(f"[bold]Service:[/bold] {name}")
366
+ panel_content.append("[bold]Stored:[/bold] Yes")
367
+
368
+ if status.get("valid"):
369
+ panel_content.append("[bold]Status:[/bold] [green]Valid[/green]")
370
+ else:
371
+ panel_content.append("[bold]Status:[/bold] [red]Invalid/Expired[/red]")
372
+
373
+ if status.get("expires_at"):
374
+ panel_content.append(f"[bold]Expires:[/bold] {status['expires_at']}")
375
+
376
+ if status.get("scopes"):
377
+ scopes = ", ".join(status["scopes"])
378
+ panel_content.append(f"[bold]Scopes:[/bold] {scopes}")
379
+
380
+ panel = Panel(
381
+ "\n".join(panel_content),
382
+ title="OAuth Token Status",
383
+ border_style="green" if status.get("valid") else "red",
384
+ )
385
+ console.print(panel)
386
+
387
+ def _revoke_tokens(self, args) -> CommandResult:
388
+ """Revoke OAuth tokens for a service."""
389
+ service_name = args.service_name
390
+
391
+ # Confirm unless -y flag
392
+ if not getattr(args, "yes", False):
393
+ console.print(
394
+ f"[yellow]This will revoke OAuth tokens for '{service_name}'.[/yellow]"
395
+ )
396
+ try:
397
+ from prompt_toolkit import prompt as pt_prompt
398
+
399
+ confirm = pt_prompt("Are you sure? (y/N): ")
400
+ if confirm.lower() not in ("y", "yes"):
401
+ return CommandResult.success_result("Revocation cancelled")
402
+ except (EOFError, KeyboardInterrupt):
403
+ return CommandResult.success_result("Revocation cancelled")
404
+ except ImportError:
405
+ # No prompt_toolkit, proceed without confirmation
406
+ pass
407
+
408
+ try:
409
+ from claude_mpm.auth import OAuthManager
410
+
411
+ manager = OAuthManager()
412
+
413
+ console.print(f"[cyan]Revoking OAuth tokens for '{service_name}'...[/cyan]")
414
+ # revoke() returns bool directly
415
+ revoked = asyncio.run(manager.revoke(service_name))
416
+
417
+ if revoked:
418
+ console.print(
419
+ f"[green]OAuth tokens revoked for '{service_name}'[/green]"
420
+ )
421
+ return CommandResult.success_result(
422
+ f"Tokens revoked for '{service_name}'"
423
+ )
424
+ return CommandResult.error_result(
425
+ f"Failed to revoke tokens for '{service_name}'"
426
+ )
427
+
428
+ except ImportError:
429
+ return CommandResult.error_result("OAuth module not available")
430
+ except Exception as e:
431
+ return CommandResult.error_result(f"Error revoking tokens: {e}")
432
+
433
+ def _refresh_tokens(self, args) -> CommandResult:
434
+ """Refresh OAuth tokens for a service."""
435
+ service_name = args.service_name
436
+
437
+ try:
438
+ from claude_mpm.auth import OAuthManager
439
+ from claude_mpm.auth.providers.google import OAuthError
440
+
441
+ manager = OAuthManager()
442
+
443
+ console.print(
444
+ f"[cyan]Refreshing OAuth tokens for '{service_name}'...[/cyan]"
445
+ )
446
+ # refresh_if_needed() returns Optional[OAuthToken]
447
+ token = asyncio.run(manager.refresh_if_needed(service_name))
448
+
449
+ if token is not None:
450
+ console.print(
451
+ f"[green]OAuth tokens refreshed for '{service_name}'[/green]"
452
+ )
453
+ if token.expires_at:
454
+ console.print(f" New expiry: {token.expires_at}")
455
+ return CommandResult.success_result(
456
+ f"Tokens refreshed for '{service_name}'"
457
+ )
458
+ return CommandResult.error_result(
459
+ f"Failed to refresh tokens for '{service_name}' - no token found or no refresh token available"
460
+ )
461
+
462
+ except OAuthError as e:
463
+ return CommandResult.error_result(f"Failed to refresh: {e}")
464
+ except ImportError:
465
+ return CommandResult.error_result("OAuth module not available")
466
+ except Exception as e:
467
+ return CommandResult.error_result(f"Error refreshing tokens: {e}")
468
+
469
+
470
+ def manage_oauth(args) -> int:
471
+ """Main entry point for OAuth management commands.
472
+
473
+ Args:
474
+ args: Parsed command line arguments
475
+
476
+ Returns:
477
+ Exit code (0 for success, non-zero for errors)
478
+ """
479
+ command = OAuthCommand()
480
+ result = command.execute(args)
481
+ return result.exit_code
@@ -13,7 +13,7 @@ DESIGN DECISIONS:
13
13
  - Support multiple output formats (json, yaml, table, text)
14
14
  """
15
15
 
16
- import subprocess
16
+ import subprocess # nosec B404 - required for process management
17
17
  import sys
18
18
  from datetime import datetime, timezone
19
19
  from typing import Optional
@@ -489,6 +489,18 @@ class RunCommand(BaseCommand):
489
489
  if hasattr(args, "claude_args") and args.claude_args:
490
490
  claude_args.extend(args.claude_args)
491
491
 
492
+ # Add --resume if flag is set
493
+ if getattr(args, "resume", False) and "--resume" not in claude_args:
494
+ claude_args.insert(0, "--resume")
495
+
496
+ # Add --chrome if flag is set
497
+ if getattr(args, "chrome", False) and "--chrome" not in claude_args:
498
+ claude_args.insert(0, "--chrome")
499
+
500
+ # Add --no-chrome if flag is set
501
+ if getattr(args, "no_chrome", False) and "--no-chrome" not in claude_args:
502
+ claude_args.insert(0, "--no-chrome")
503
+
492
504
  # Create runner
493
505
  runner = ClaudeRunner(
494
506
  enable_tickets=enable_tickets,
@@ -553,7 +565,7 @@ class RunCommand(BaseCommand):
553
565
  wrapper_path = get_scripts_dir() / "interactive_wrapper.py"
554
566
  if wrapper_path.exists():
555
567
  print("Starting interactive session with command interception...")
556
- subprocess.run([sys.executable, str(wrapper_path)], check=False)
568
+ subprocess.run([sys.executable, str(wrapper_path)], check=False) # nosec B603 - trusted internal paths
557
569
  else:
558
570
  self.logger.warning(
559
571
  "Interactive wrapper not found, falling back to normal mode"
@@ -907,6 +919,26 @@ def run_session_legacy(args):
907
919
  else:
908
920
  logger.info("[INFO]ī¸ --resume already in claude_args")
909
921
 
922
+ # Add --chrome to claude_args if the flag is set
923
+ chrome_flag_present = getattr(args, "chrome", False)
924
+ if chrome_flag_present:
925
+ logger.info("📌 --chrome flag detected in args")
926
+ if "--chrome" not in raw_claude_args:
927
+ raw_claude_args = ["--chrome", *raw_claude_args]
928
+ logger.info("✅ Added --chrome to claude_args")
929
+ else:
930
+ logger.info("â„šī¸ --chrome already in claude_args")
931
+
932
+ # Add --no-chrome to claude_args if the flag is set
933
+ no_chrome_flag_present = getattr(args, "no_chrome", False)
934
+ if no_chrome_flag_present:
935
+ logger.info("📌 --no-chrome flag detected in args")
936
+ if "--no-chrome" not in raw_claude_args:
937
+ raw_claude_args = ["--no-chrome", *raw_claude_args]
938
+ logger.info("✅ Added --no-chrome to claude_args")
939
+ else:
940
+ logger.info("â„šī¸ --no-chrome already in claude_args")
941
+
910
942
  # Filter out claude-mpm specific flags before passing to Claude CLI
911
943
  logger.debug(f"Pre-filter claude_args: {raw_claude_args}")
912
944
  claude_args = filter_claude_mpm_args(raw_claude_args)
@@ -1044,7 +1076,7 @@ def run_session_legacy(args):
1044
1076
  wrapper_path = get_scripts_dir() / "interactive_wrapper.py"
1045
1077
  if wrapper_path.exists():
1046
1078
  print("Starting interactive session with command interception...")
1047
- subprocess.run([sys.executable, str(wrapper_path)], check=False)
1079
+ subprocess.run([sys.executable, str(wrapper_path)], check=False) # nosec B603 - trusted internal paths
1048
1080
  else:
1049
1081
  logger.warning("Interactive wrapper not found, falling back to normal mode")
1050
1082
  runner.run_interactive(context)
@@ -11,6 +11,7 @@ for better UX. Handles errors gracefully with actionable messages.
11
11
 
12
12
  import json
13
13
  import logging
14
+ import os
14
15
  import re
15
16
 
16
17
  from ...config.skill_sources import SkillSource, SkillSourceConfiguration
@@ -20,6 +21,33 @@ from ...services.skills.skill_discovery_service import SkillDiscoveryService
20
21
  logger = logging.getLogger(__name__)
21
22
 
22
23
 
24
+ def _get_github_token(source: SkillSource | None = None) -> str | None:
25
+ """Get GitHub token with source-specific override support.
26
+
27
+ Priority: source.token > GITHUB_TOKEN > GH_TOKEN
28
+
29
+ Args:
30
+ source: Optional SkillSource to check for per-source token
31
+
32
+ Returns:
33
+ GitHub token if found, None otherwise
34
+
35
+ Security Note:
36
+ Token is never logged or printed to avoid exposure.
37
+ """
38
+ # Priority 1: Per-source token (env var reference or direct)
39
+ if source and source.token:
40
+ if source.token.startswith("$"):
41
+ # Env var reference: $VAR_NAME -> os.environ.get("VAR_NAME")
42
+ env_var_name = source.token[1:]
43
+ return os.environ.get(env_var_name)
44
+ # Direct token (not recommended but supported)
45
+ return source.token
46
+
47
+ # Priority 2-3: Global environment variables
48
+ return os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
49
+
50
+
23
51
  def _test_skill_repository_access(source: SkillSource) -> dict:
24
52
  """Test if skill repository is accessible via GitHub API.
25
53
 
@@ -58,7 +86,13 @@ def _test_skill_repository_access(source: SkillSource) -> dict:
58
86
  # Test GitHub API access
59
87
  api_url = f"https://api.github.com/repos/{owner_repo}"
60
88
 
61
- response = requests.get(api_url, timeout=10)
89
+ # Build headers with authentication if token available
90
+ headers = {"Accept": "application/vnd.github+json"}
91
+ token = _get_github_token(source)
92
+ if token:
93
+ headers["Authorization"] = f"token {token}"
94
+
95
+ response = requests.get(api_url, headers=headers, timeout=10)
62
96
 
63
97
  if response.status_code == 200:
64
98
  return {"accessible": True, "error": None}
@@ -68,9 +102,14 @@ def _test_skill_repository_access(source: SkillSource) -> dict:
68
102
  "error": f"Repository not found: {owner_repo}",
69
103
  }
70
104
  if response.status_code == 403:
105
+ error_msg = "Access denied (private repository or rate limit)"
106
+ if not token:
107
+ error_msg += (
108
+ ". Try setting GITHUB_TOKEN environment variable for private repos"
109
+ )
71
110
  return {
72
111
  "accessible": False,
73
- "error": "Access denied (private repository or rate limit)",
112
+ "error": error_msg,
74
113
  }
75
114
  return {
76
115
  "accessible": False,
@@ -263,6 +302,15 @@ def handle_add_skill_source(args) -> int:
263
302
 
264
303
  # Create new source
265
304
  enabled = not args.disabled
305
+ token = getattr(args, "token", None)
306
+
307
+ # Security warning for direct tokens
308
+ if token and not token.startswith("$"):
309
+ print("âš ī¸ Warning: Direct token values in config are not recommended")
310
+ print(" Consider using environment variable reference instead:")
311
+ print(" --token $MY_PRIVATE_TOKEN")
312
+ print()
313
+
266
314
  source = SkillSource(
267
315
  id=source_id,
268
316
  type="git",
@@ -270,6 +318,7 @@ def handle_add_skill_source(args) -> int:
270
318
  branch=args.branch,
271
319
  priority=args.priority,
272
320
  enabled=enabled,
321
+ token=token,
273
322
  )
274
323
 
275
324
  # Determine if we should test
@@ -538,6 +538,7 @@ class SkillsManagementCommand(BaseCommand):
538
538
  toolchain = getattr(args, "toolchain", None)
539
539
  categories = getattr(args, "categories", None)
540
540
  force = getattr(args, "force", False)
541
+ deploy_all = getattr(args, "all", False)
541
542
 
542
543
  if collection:
543
544
  console.print(
@@ -548,14 +549,15 @@ class SkillsManagementCommand(BaseCommand):
548
549
  "\n[bold cyan]Deploying skills from default collection...[/bold cyan]\n"
549
550
  )
550
551
 
551
- # Selective deployment is ALWAYS enabled (deploy only agent-referenced skills)
552
- # This ensures only skills linked to deployed agents are deployed
552
+ # Use selective deployment unless --all flag is provided
553
+ # Selective mode deploys only agent-referenced skills
554
+ # --all mode deploys all available skills from the collection
553
555
  result = self.skills_deployer.deploy_skills(
554
556
  collection=collection,
555
557
  toolchain=toolchain,
556
558
  categories=categories,
557
559
  force=force,
558
- selective=True, # Always use selective deployment
560
+ selective=not deploy_all,
559
561
  )
560
562
 
561
563
  # Display results