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

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

Potentially problematic release.


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

Files changed (490) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
  3. claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
  4. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +109 -1925
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +161 -298
  6. claude_mpm/agents/WORKFLOW.md +2 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  8. claude_mpm/auth/__init__.py +35 -0
  9. claude_mpm/auth/callback_server.py +328 -0
  10. claude_mpm/auth/models.py +104 -0
  11. claude_mpm/auth/oauth_manager.py +266 -0
  12. claude_mpm/auth/providers/__init__.py +12 -0
  13. claude_mpm/auth/providers/base.py +165 -0
  14. claude_mpm/auth/providers/google.py +261 -0
  15. claude_mpm/auth/token_storage.py +252 -0
  16. claude_mpm/cli/__init__.py +5 -1
  17. claude_mpm/cli/commands/agents.py +2 -4
  18. claude_mpm/cli/commands/agents_reconcile.py +197 -0
  19. claude_mpm/cli/commands/autotodos.py +566 -0
  20. claude_mpm/cli/commands/commander.py +216 -0
  21. claude_mpm/cli/commands/configure.py +620 -21
  22. claude_mpm/cli/commands/configure_agent_display.py +3 -1
  23. claude_mpm/cli/commands/hook_errors.py +60 -60
  24. claude_mpm/cli/commands/mcp.py +29 -17
  25. claude_mpm/cli/commands/mcp_command_router.py +39 -0
  26. claude_mpm/cli/commands/mcp_service_commands.py +304 -0
  27. claude_mpm/cli/commands/monitor.py +2 -2
  28. claude_mpm/cli/commands/mpm_init/core.py +15 -8
  29. claude_mpm/cli/commands/oauth.py +481 -0
  30. claude_mpm/cli/commands/profile.py +9 -10
  31. claude_mpm/cli/commands/run.py +35 -3
  32. claude_mpm/cli/commands/skill_source.py +51 -2
  33. claude_mpm/cli/commands/skills.py +182 -32
  34. claude_mpm/cli/executor.py +129 -16
  35. claude_mpm/cli/helpers.py +1 -1
  36. claude_mpm/cli/interactive/__init__.py +10 -0
  37. claude_mpm/cli/interactive/agent_wizard.py +30 -50
  38. claude_mpm/cli/interactive/questionary_styles.py +65 -0
  39. claude_mpm/cli/interactive/skill_selector.py +481 -0
  40. claude_mpm/cli/parsers/base_parser.py +89 -1
  41. claude_mpm/cli/parsers/commander_parser.py +116 -0
  42. claude_mpm/cli/parsers/mcp_parser.py +79 -0
  43. claude_mpm/cli/parsers/oauth_parser.py +165 -0
  44. claude_mpm/cli/parsers/profile_parser.py +0 -1
  45. claude_mpm/cli/parsers/run_parser.py +10 -0
  46. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  47. claude_mpm/cli/parsers/skills_parser.py +2 -3
  48. claude_mpm/cli/startup.py +662 -524
  49. claude_mpm/cli/startup_display.py +76 -7
  50. claude_mpm/cli/startup_logging.py +2 -2
  51. claude_mpm/cli/utils.py +7 -3
  52. claude_mpm/commander/__init__.py +78 -0
  53. claude_mpm/commander/adapters/__init__.py +60 -0
  54. claude_mpm/commander/adapters/auggie.py +260 -0
  55. claude_mpm/commander/adapters/base.py +288 -0
  56. claude_mpm/commander/adapters/claude_code.py +392 -0
  57. claude_mpm/commander/adapters/codex.py +237 -0
  58. claude_mpm/commander/adapters/communication.py +366 -0
  59. claude_mpm/commander/adapters/example_usage.py +310 -0
  60. claude_mpm/commander/adapters/mpm.py +389 -0
  61. claude_mpm/commander/adapters/registry.py +204 -0
  62. claude_mpm/commander/api/__init__.py +16 -0
  63. claude_mpm/commander/api/app.py +121 -0
  64. claude_mpm/commander/api/errors.py +133 -0
  65. claude_mpm/commander/api/routes/__init__.py +8 -0
  66. claude_mpm/commander/api/routes/events.py +184 -0
  67. claude_mpm/commander/api/routes/inbox.py +171 -0
  68. claude_mpm/commander/api/routes/messages.py +148 -0
  69. claude_mpm/commander/api/routes/projects.py +271 -0
  70. claude_mpm/commander/api/routes/sessions.py +226 -0
  71. claude_mpm/commander/api/routes/work.py +296 -0
  72. claude_mpm/commander/api/schemas.py +186 -0
  73. claude_mpm/commander/chat/__init__.py +7 -0
  74. claude_mpm/commander/chat/cli.py +149 -0
  75. claude_mpm/commander/chat/commands.py +122 -0
  76. claude_mpm/commander/chat/repl.py +1821 -0
  77. claude_mpm/commander/config.py +51 -0
  78. claude_mpm/commander/config_loader.py +115 -0
  79. claude_mpm/commander/core/__init__.py +10 -0
  80. claude_mpm/commander/core/block_manager.py +325 -0
  81. claude_mpm/commander/core/response_manager.py +323 -0
  82. claude_mpm/commander/daemon.py +603 -0
  83. claude_mpm/commander/env_loader.py +59 -0
  84. claude_mpm/commander/events/__init__.py +26 -0
  85. claude_mpm/commander/events/manager.py +392 -0
  86. claude_mpm/commander/frameworks/__init__.py +12 -0
  87. claude_mpm/commander/frameworks/base.py +233 -0
  88. claude_mpm/commander/frameworks/claude_code.py +58 -0
  89. claude_mpm/commander/frameworks/mpm.py +57 -0
  90. claude_mpm/commander/git/__init__.py +5 -0
  91. claude_mpm/commander/git/worktree_manager.py +212 -0
  92. claude_mpm/commander/inbox/__init__.py +16 -0
  93. claude_mpm/commander/inbox/dedup.py +128 -0
  94. claude_mpm/commander/inbox/inbox.py +224 -0
  95. claude_mpm/commander/inbox/models.py +70 -0
  96. claude_mpm/commander/instance_manager.py +865 -0
  97. claude_mpm/commander/llm/__init__.py +6 -0
  98. claude_mpm/commander/llm/openrouter_client.py +167 -0
  99. claude_mpm/commander/llm/summarizer.py +70 -0
  100. claude_mpm/commander/memory/__init__.py +45 -0
  101. claude_mpm/commander/memory/compression.py +347 -0
  102. claude_mpm/commander/memory/embeddings.py +230 -0
  103. claude_mpm/commander/memory/entities.py +310 -0
  104. claude_mpm/commander/memory/example_usage.py +290 -0
  105. claude_mpm/commander/memory/integration.py +325 -0
  106. claude_mpm/commander/memory/search.py +381 -0
  107. claude_mpm/commander/memory/store.py +657 -0
  108. claude_mpm/commander/models/__init__.py +18 -0
  109. claude_mpm/commander/models/events.py +127 -0
  110. claude_mpm/commander/models/project.py +162 -0
  111. claude_mpm/commander/models/work.py +214 -0
  112. claude_mpm/commander/parsing/__init__.py +20 -0
  113. claude_mpm/commander/parsing/extractor.py +132 -0
  114. claude_mpm/commander/parsing/output_parser.py +270 -0
  115. claude_mpm/commander/parsing/patterns.py +100 -0
  116. claude_mpm/commander/persistence/__init__.py +11 -0
  117. claude_mpm/commander/persistence/event_store.py +274 -0
  118. claude_mpm/commander/persistence/state_store.py +403 -0
  119. claude_mpm/commander/persistence/work_store.py +164 -0
  120. claude_mpm/commander/polling/__init__.py +13 -0
  121. claude_mpm/commander/polling/event_detector.py +104 -0
  122. claude_mpm/commander/polling/output_buffer.py +49 -0
  123. claude_mpm/commander/polling/output_poller.py +153 -0
  124. claude_mpm/commander/project_session.py +268 -0
  125. claude_mpm/commander/proxy/__init__.py +12 -0
  126. claude_mpm/commander/proxy/formatter.py +89 -0
  127. claude_mpm/commander/proxy/output_handler.py +191 -0
  128. claude_mpm/commander/proxy/relay.py +155 -0
  129. claude_mpm/commander/registry.py +410 -0
  130. claude_mpm/commander/runtime/__init__.py +10 -0
  131. claude_mpm/commander/runtime/executor.py +191 -0
  132. claude_mpm/commander/runtime/monitor.py +346 -0
  133. claude_mpm/commander/session/__init__.py +6 -0
  134. claude_mpm/commander/session/context.py +81 -0
  135. claude_mpm/commander/session/manager.py +59 -0
  136. claude_mpm/commander/tmux_orchestrator.py +362 -0
  137. claude_mpm/commander/web/__init__.py +1 -0
  138. claude_mpm/commander/work/__init__.py +30 -0
  139. claude_mpm/commander/work/executor.py +207 -0
  140. claude_mpm/commander/work/queue.py +405 -0
  141. claude_mpm/commander/workflow/__init__.py +27 -0
  142. claude_mpm/commander/workflow/event_handler.py +241 -0
  143. claude_mpm/commander/workflow/notifier.py +146 -0
  144. claude_mpm/commands/mpm-config.md +8 -0
  145. claude_mpm/commands/mpm-doctor.md +8 -0
  146. claude_mpm/commands/mpm-help.md +8 -0
  147. claude_mpm/commands/mpm-init.md +8 -0
  148. claude_mpm/commands/mpm-monitor.md +8 -0
  149. claude_mpm/commands/mpm-organize.md +8 -0
  150. claude_mpm/commands/mpm-postmortem.md +8 -0
  151. claude_mpm/commands/mpm-session-resume.md +9 -1
  152. claude_mpm/commands/mpm-status.md +8 -0
  153. claude_mpm/commands/mpm-ticket-view.md +8 -0
  154. claude_mpm/commands/mpm-version.md +8 -0
  155. claude_mpm/commands/mpm.md +8 -0
  156. claude_mpm/config/agent_presets.py +8 -7
  157. claude_mpm/config/skill_sources.py +16 -0
  158. claude_mpm/constants.py +6 -0
  159. claude_mpm/core/claude_runner.py +154 -2
  160. claude_mpm/core/config.py +35 -22
  161. claude_mpm/core/config_constants.py +74 -9
  162. claude_mpm/core/constants.py +56 -12
  163. claude_mpm/core/hook_manager.py +53 -4
  164. claude_mpm/core/interactive_session.py +12 -11
  165. claude_mpm/core/logger.py +26 -9
  166. claude_mpm/core/logging_utils.py +39 -13
  167. claude_mpm/core/network_config.py +148 -0
  168. claude_mpm/core/oneshot_session.py +7 -6
  169. claude_mpm/core/optimized_startup.py +3 -1
  170. claude_mpm/core/output_style_manager.py +66 -18
  171. claude_mpm/core/shared/config_loader.py +3 -1
  172. claude_mpm/core/socketio_pool.py +47 -15
  173. claude_mpm/core/unified_config.py +54 -8
  174. claude_mpm/core/unified_paths.py +95 -90
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/1WZnGYqX.js +24 -0
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/67pF3qNn.js +1 -0
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/6RxdMKe4.js +1 -0
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/8cZrfX0h.js +60 -0
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/9a6T2nm-.js +7 -0
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B443AUzu.js +1 -0
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B8AwtY2H.js +1 -0
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BF15LAsF.js +1 -0
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BRcwIQNr.js +4 -0
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uj46x2Wr.js → BSNlmTZj.js} +1 -1
  188. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BV6nKitt.js +43 -0
  189. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BViJ8lZt.js +128 -0
  190. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BcQ-Q0FE.js +1 -0
  191. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bpyvgze_.js +30 -0
  192. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  193. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  194. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C3rbW_a-.js +1 -0
  195. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C8WYN38h.js +1 -0
  196. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C9I8FlXH.js +61 -0
  197. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIQcWgO2.js +36 -0
  198. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIctN7YN.js +7 -0
  199. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CKrS_JZW.js +145 -0
  200. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CR6P9C4A.js +89 -0
  201. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRRR9MD_.js +2 -0
  202. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  203. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CSXtMOf0.js +1 -0
  204. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CT-sbxSk.js +1 -0
  205. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWm6DJsp.js +1 -0
  206. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
  207. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CpqQ1Kzn.js +1 -0
  208. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
  209. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D2nGpDRe.js +1 -0
  210. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9iCMida.js +267 -0
  211. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9ykgMoY.js +10 -0
  212. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DL2Ldur1.js +1 -0
  213. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DPfltzjH.js +165 -0
  214. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{N4qtv3Hx.js → DR8nis88.js} +2 -2
  215. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUliQN2b.js +1 -0
  216. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
  217. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DXlhR01x.js +122 -0
  218. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D_lyTybS.js +1 -0
  219. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DngoTTgh.js +1 -0
  220. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DqkmHtDC.js +220 -0
  221. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DsDh8EYs.js +1 -0
  222. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DypDmXgd.js +139 -0
  223. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
  224. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/IPYC-LnN.js +162 -0
  225. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  226. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JpevfAFt.js +68 -0
  227. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DjhvlsAc.js → NqQ1dWOy.js} +1 -1
  228. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/R8CEIRAd.js +2 -0
  229. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Zxy7qc-l.js +64 -0
  230. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  231. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/qtd3IeO4.js +15 -0
  232. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ulBFON_C.js +65 -0
  233. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/wQVh1CoA.js +10 -0
  234. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.Dr7t0z2J.js +2 -0
  235. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  236. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.CAGBuiOw.js → 0.RgBboRvH.js} +1 -1
  237. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DG-KkbDf.js +1 -0
  238. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  239. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  240. claude_mpm/dashboard/static/svelte-build/index.html +11 -11
  241. claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
  242. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
  243. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
  244. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
  245. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
  246. claude_mpm/experimental/cli_enhancements.py +2 -1
  247. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  248. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  249. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  250. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  251. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  252. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  253. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  254. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  255. claude_mpm/hooks/claude_hooks/event_handlers.py +466 -136
  256. claude_mpm/hooks/claude_hooks/hook_handler.py +204 -104
  257. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  258. claude_mpm/hooks/claude_hooks/installer.py +291 -59
  259. claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
  260. claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
  261. claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
  262. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  263. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  264. claude_mpm/hooks/claude_hooks/services/__pycache__/container.cpython-311.pyc +0 -0
  265. claude_mpm/hooks/claude_hooks/services/__pycache__/protocols.cpython-311.pyc +0 -0
  266. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  267. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  268. claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
  269. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
  270. claude_mpm/hooks/claude_hooks/services/container.py +326 -0
  271. claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
  272. claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
  273. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
  274. claude_mpm/hooks/kuzu_memory_hook.py +5 -5
  275. claude_mpm/hooks/session_resume_hook.py +89 -1
  276. claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
  277. claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
  278. claude_mpm/init.py +224 -4
  279. claude_mpm/mcp/__init__.py +9 -0
  280. claude_mpm/mcp/google_workspace_server.py +610 -0
  281. claude_mpm/scripts/claude-hook-handler.sh +46 -19
  282. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  283. claude_mpm/services/agents/agent_selection_service.py +2 -2
  284. claude_mpm/services/agents/cache_git_manager.py +1 -1
  285. claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -1
  286. claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
  287. claude_mpm/services/agents/deployment/agent_template_builder.py +37 -17
  288. claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
  289. claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
  290. claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
  291. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +36 -8
  292. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +50 -26
  293. claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
  294. claude_mpm/services/agents/git_source_manager.py +21 -2
  295. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  296. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  297. claude_mpm/services/agents/sources/git_source_sync_service.py +116 -5
  298. claude_mpm/services/agents/startup_sync.py +5 -2
  299. claude_mpm/services/cli/__init__.py +3 -0
  300. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  301. claude_mpm/services/cli/session_resume_helper.py +10 -2
  302. claude_mpm/services/command_deployment_service.py +44 -26
  303. claude_mpm/services/delegation_detector.py +175 -0
  304. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  305. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  306. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  307. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  308. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  309. claude_mpm/services/diagnostics/models.py +14 -1
  310. claude_mpm/services/event_log.py +325 -0
  311. claude_mpm/services/hook_installer_service.py +77 -8
  312. claude_mpm/services/infrastructure/__init__.py +4 -0
  313. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  314. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  315. claude_mpm/services/mcp_config_manager.py +99 -19
  316. claude_mpm/services/mcp_service_registry.py +294 -0
  317. claude_mpm/services/monitor/daemon_manager.py +15 -4
  318. claude_mpm/services/monitor/management/lifecycle.py +8 -3
  319. claude_mpm/services/monitor/server.py +111 -16
  320. claude_mpm/services/pm_skills_deployer.py +302 -94
  321. claude_mpm/services/profile_manager.py +10 -4
  322. claude_mpm/services/skills/git_skill_source_manager.py +192 -29
  323. claude_mpm/services/skills/selective_skill_deployer.py +211 -46
  324. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  325. claude_mpm/services/skills_deployer.py +192 -70
  326. claude_mpm/services/socketio/handlers/hook.py +14 -7
  327. claude_mpm/services/socketio/server/main.py +12 -4
  328. claude_mpm/skills/__init__.py +2 -1
  329. claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
  330. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
  331. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
  332. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
  333. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
  334. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
  335. claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
  336. claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
  337. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
  338. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
  339. claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
  340. claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
  341. claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
  342. claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
  343. claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
  344. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
  345. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
  346. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
  347. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
  348. claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
  349. claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
  350. claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
  351. claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
  352. claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
  353. claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
  354. claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
  355. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
  356. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
  357. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
  358. claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
  359. claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
  360. claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
  361. claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
  362. claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
  363. claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
  364. claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
  365. claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
  366. claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
  367. claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
  368. claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
  369. claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
  370. claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
  371. claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
  372. claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
  373. claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
  374. claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
  375. claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
  376. claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
  377. claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
  378. claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
  379. claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
  380. claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
  381. claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
  382. claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
  383. claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
  384. claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
  385. claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
  386. claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
  387. claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
  388. claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
  389. claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
  390. claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
  391. claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
  392. claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
  393. claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
  394. claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
  395. claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
  396. claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
  397. claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
  398. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  399. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  400. claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
  401. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  402. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  403. claude_mpm/skills/bundled/pm/mpm-delegation-patterns/SKILL.md +167 -0
  404. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  405. claude_mpm/skills/bundled/pm/mpm-git-file-tracking/SKILL.md +113 -0
  406. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  407. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  408. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  409. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  410. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  411. claude_mpm/skills/bundled/pm/mpm-pr-workflow/SKILL.md +124 -0
  412. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  413. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  414. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  415. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  416. claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
  417. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  418. claude_mpm/skills/bundled/pm/mpm-ticketing-integration/SKILL.md +154 -0
  419. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  420. claude_mpm/skills/bundled/pm/mpm-verification-protocols/SKILL.md +198 -0
  421. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  422. claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
  423. claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
  424. claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
  425. claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
  426. claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
  427. claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
  428. claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
  429. claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
  430. claude_mpm/skills/bundled/security-scanning.md +112 -0
  431. claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
  432. claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
  433. claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
  434. claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
  435. claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
  436. claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
  437. claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
  438. claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
  439. claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
  440. claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
  441. claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
  442. claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
  443. claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
  444. claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
  445. claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
  446. claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
  447. claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
  448. claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
  449. claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
  450. claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
  451. claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
  452. claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
  453. claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
  454. claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
  455. claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
  456. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
  457. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
  458. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
  459. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
  460. claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
  461. claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
  462. claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
  463. claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
  464. claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
  465. claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
  466. claude_mpm/skills/registry.py +295 -90
  467. claude_mpm/skills/skill_manager.py +29 -23
  468. claude_mpm/templates/.pre-commit-config.yaml +112 -0
  469. claude_mpm/utils/agent_dependency_loader.py +103 -4
  470. claude_mpm/utils/robust_installer.py +45 -24
  471. claude_mpm-5.6.72.dist-info/METADATA +416 -0
  472. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/RECORD +477 -159
  473. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/WHEEL +1 -1
  474. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/entry_points.txt +2 -0
  475. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +0 -1
  476. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +0 -1
  477. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +0 -1
  478. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +0 -1
  479. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +0 -1
  480. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +0 -2
  481. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +0 -2
  482. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +0 -1
  483. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +0 -1
  484. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +0 -10
  485. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  486. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  487. claude_mpm-5.4.41.dist-info/METADATA +0 -998
  488. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE +0 -0
  489. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  490. {claude_mpm-5.4.41.dist-info → claude_mpm-5.6.72.dist-info}/top_level.txt +0 -0
@@ -26,6 +26,7 @@ from rich.prompt import Confirm, Prompt
26
26
  from rich.text import Text
27
27
 
28
28
  from ...core.config import Config
29
+ from ...core.unified_config import UnifiedConfig
29
30
  from ...services.agents.agent_recommendation_service import AgentRecommendationService
30
31
  from ...services.version_service import VersionService
31
32
  from ...utils.agent_filters import apply_all_filters, get_deployed_agent_ids
@@ -79,6 +80,7 @@ class ConfigureCommand(BaseCommand):
79
80
  self._template_editor = None # Lazy-initialized
80
81
  self._startup_manager = None # Lazy-initialized
81
82
  self._recommendation_service = None # Lazy-initialized
83
+ self._unified_config = None # Lazy-initialized
82
84
 
83
85
  def validate_args(self, args) -> Optional[str]:
84
86
  """Validate command arguments."""
@@ -162,6 +164,18 @@ class ConfigureCommand(BaseCommand):
162
164
  self._recommendation_service = AgentRecommendationService()
163
165
  return self._recommendation_service
164
166
 
167
+ @property
168
+ def unified_config(self) -> UnifiedConfig:
169
+ """Lazy-initialize unified config."""
170
+ if self._unified_config is None:
171
+ try:
172
+ self._unified_config = UnifiedConfig()
173
+ except Exception as e:
174
+ self.logger.warning(f"Failed to load unified config: {e}")
175
+ # Fallback to default config
176
+ self._unified_config = UnifiedConfig()
177
+ return self._unified_config
178
+
165
179
  def run(self, args) -> CommandResult:
166
180
  """Execute the configure command."""
167
181
  # Set configuration scope
@@ -656,7 +670,7 @@ class ConfigureCommand(BaseCommand):
656
670
  self.behavior_manager.manage_behaviors()
657
671
 
658
672
  def _manage_skills(self) -> None:
659
- """Skills management interface."""
673
+ """Skills management interface with questionary checkbox selection."""
660
674
  from ...cli.interactive.skills_wizard import SkillsWizard
661
675
  from ...skills.skill_manager import get_manager
662
676
 
@@ -667,22 +681,22 @@ class ConfigureCommand(BaseCommand):
667
681
  self.console.clear()
668
682
  self._display_header()
669
683
 
670
- self.console.print("\n[bold]Skills Management Options:[/bold]\n")
671
- self.console.print(" [1] View Available Skills")
672
- self.console.print(" [2] Configure Skills for Agents")
673
- self.console.print(" [3] View Current Skill Mappings")
674
- self.console.print(" [4] Auto-Link Skills to Agents")
675
- self.console.print(" [b] Back to Main Menu")
684
+ self.console.print("\n[bold]Skills Management[/bold]")
685
+
686
+ # Show action options
687
+ self.console.print("\n[bold]Actions:[/bold]")
688
+ self.console.print(" [1] Install/Uninstall skills")
689
+ self.console.print(" [2] Configure skills for agents")
690
+ self.console.print(" [3] View current skill mappings")
691
+ self.console.print(" [4] Auto-link skills to agents")
692
+ self.console.print(" [b] Back to main menu")
676
693
  self.console.print()
677
694
 
678
695
  choice = Prompt.ask("[bold blue]Select an option[/bold blue]", default="b")
679
696
 
680
697
  if choice == "1":
681
- # View available skills
682
- self.console.clear()
683
- self._display_header()
684
- wizard.list_available_skills()
685
- Prompt.ask("\nPress Enter to continue")
698
+ # Install/Uninstall skills with category-based selection
699
+ self._manage_skill_installation()
686
700
 
687
701
  elif choice == "2":
688
702
  # Configure skills interactively
@@ -805,6 +819,548 @@ class ConfigureCommand(BaseCommand):
805
819
  self.console.print("[red]Invalid choice. Please try again.[/red]")
806
820
  Prompt.ask("\nPress Enter to continue")
807
821
 
822
+ def _detect_skill_patterns(self, skills: list[dict]) -> dict[str, list[dict]]:
823
+ """Group skills by detected common prefixes.
824
+
825
+ Args:
826
+ skills: List of skill dictionaries
827
+
828
+ Returns:
829
+ Dict mapping pattern prefix to list of skills.
830
+ Skills without pattern match go under "" (empty string) key.
831
+ """
832
+ from collections import defaultdict
833
+
834
+ # Count prefix occurrences (try 1-segment and 2-segment prefixes)
835
+ prefix_counts = defaultdict(list)
836
+
837
+ for skill in skills:
838
+ skill_id = skill.get("name", skill.get("skill_id", ""))
839
+
840
+ # Try to extract prefixes (split by hyphen)
841
+ parts = skill_id.split("-")
842
+
843
+ if len(parts) >= 2:
844
+ # Try 2-segment prefix first (e.g., "toolchains-universal")
845
+ two_seg_prefix = f"{parts[0]}-{parts[1]}"
846
+ prefix_counts[two_seg_prefix].append(skill)
847
+
848
+ # Also try 1-segment prefix (e.g., "digitalocean")
849
+ one_seg_prefix = parts[0]
850
+ if one_seg_prefix != two_seg_prefix:
851
+ prefix_counts[one_seg_prefix].append(skill)
852
+
853
+ # Build pattern groups (require at least 2 skills per pattern)
854
+ pattern_groups = defaultdict(list)
855
+ used_skills = set()
856
+
857
+ # Prefer longer (more specific) prefixes
858
+ sorted_prefixes = sorted(prefix_counts.keys(), key=lambda x: (-len(x), x))
859
+
860
+ for prefix in sorted_prefixes:
861
+ matching_skills = prefix_counts[prefix]
862
+
863
+ # Only create a pattern group if we have 2+ skills and they're not already grouped
864
+ available_skills = [s for s in matching_skills if id(s) not in used_skills]
865
+
866
+ if len(available_skills) >= 2:
867
+ pattern_groups[prefix] = available_skills
868
+ used_skills.update(id(s) for s in available_skills)
869
+
870
+ # Add ungrouped skills to "" (Other) group
871
+ for skill in skills:
872
+ if id(skill) not in used_skills:
873
+ pattern_groups[""].append(skill)
874
+
875
+ return dict(pattern_groups)
876
+
877
+ def _get_pattern_icon(self, prefix: str) -> str:
878
+ """Get icon for a pattern prefix.
879
+
880
+ Args:
881
+ prefix: Pattern prefix (e.g., "digitalocean", "vercel")
882
+
883
+ Returns:
884
+ Emoji icon for the pattern
885
+ """
886
+ pattern_icons = {
887
+ "digitalocean": "🌊",
888
+ "aws": "☁️",
889
+ "github": "🐙",
890
+ "google": "🔍",
891
+ "vercel": "▲",
892
+ "netlify": "🦋",
893
+ "universal-testing": "🧪",
894
+ "universal-debugging": "🐛",
895
+ "universal-security": "🔒",
896
+ "toolchains-python": "🐍",
897
+ "toolchains-typescript": "📘",
898
+ "toolchains-javascript": "📒",
899
+ }
900
+ return pattern_icons.get(prefix, "📦")
901
+
902
+ def _manage_skill_installation(self) -> None:
903
+ """Manage skill installation with category-based questionary checkbox selection."""
904
+ import questionary
905
+
906
+ # Get all skills
907
+ all_skills = self._get_all_skills_from_git()
908
+ if not all_skills:
909
+ self.console.print(
910
+ "[yellow]No skills available. Try syncing skills first.[/yellow]"
911
+ )
912
+ Prompt.ask("\nPress Enter to continue")
913
+ return
914
+
915
+ # Get deployed skills
916
+ deployed = self._get_deployed_skill_ids()
917
+
918
+ # Group by category
919
+ grouped = {}
920
+ for skill in all_skills:
921
+ # Try to get category from tags or use toolchain
922
+ category = None
923
+ tags = skill.get("tags", [])
924
+
925
+ # Look for category tag
926
+ for tag in tags:
927
+ if tag in [
928
+ "universal",
929
+ "python",
930
+ "typescript",
931
+ "javascript",
932
+ "go",
933
+ "rust",
934
+ ]:
935
+ category = tag
936
+ break
937
+
938
+ # Fallback to toolchain or universal
939
+ if not category:
940
+ category = skill.get("toolchain", "universal")
941
+
942
+ if category not in grouped:
943
+ grouped[category] = []
944
+ grouped[category].append(skill)
945
+
946
+ # Category icons
947
+ icons = {
948
+ "universal": "🌐",
949
+ "python": "🐍",
950
+ "typescript": "📘",
951
+ "javascript": "📒",
952
+ "go": "🔷",
953
+ "rust": "⚙️",
954
+ }
955
+
956
+ # Sort categories: universal first, then alphabetically
957
+ categories = sorted(grouped.keys(), key=lambda x: (x != "universal", x))
958
+
959
+ while True:
960
+ # Show category selection first
961
+ self.console.clear()
962
+ self._display_header()
963
+ self.console.print("\n[bold cyan]Skills Management[/bold cyan]")
964
+ self.console.print(
965
+ f"[dim]{len(all_skills)} skills available, {len(deployed)} installed[/dim]\n"
966
+ )
967
+
968
+ cat_choices = [
969
+ Choice(
970
+ title=f"{icons.get(cat, '📦')} {cat.title()} ({len(grouped[cat])} skills)",
971
+ value=cat,
972
+ )
973
+ for cat in categories
974
+ ]
975
+ cat_choices.append(Choice(title="← Back to main menu", value="back"))
976
+
977
+ selected_cat = questionary.select(
978
+ "Select a category:", choices=cat_choices, style=self.QUESTIONARY_STYLE
979
+ ).ask()
980
+
981
+ if selected_cat is None or selected_cat == "back":
982
+ return
983
+
984
+ # Show skills in category with checkbox selection
985
+ category_skills = grouped[selected_cat]
986
+
987
+ # Detect pattern groups within category
988
+ pattern_groups = self._detect_skill_patterns(category_skills)
989
+
990
+ # Build choices with pattern grouping and installation status
991
+ skill_choices = []
992
+
993
+ # Track which skills belong to which group for expansion later
994
+ group_to_skills = {}
995
+
996
+ # Sort pattern groups: "" (Other) last, rest alphabetically
997
+ sorted_patterns = sorted(pattern_groups.keys(), key=lambda x: (x == "", x))
998
+
999
+ for pattern in sorted_patterns:
1000
+ pattern_skills = pattern_groups[pattern]
1001
+
1002
+ # Skip empty groups
1003
+ if not pattern_skills:
1004
+ continue
1005
+
1006
+ # Collect skill IDs in this group
1007
+ skill_ids_in_group = []
1008
+ for skill in pattern_skills:
1009
+ skill_id = skill.get("name", skill.get("skill_id", "unknown"))
1010
+ skill_ids_in_group.append(skill_id)
1011
+
1012
+ # Check if all skills in group are installed
1013
+ all_installed = all(
1014
+ skill.get(
1015
+ "deployment_name", skill.get("name", skill.get("skill_id"))
1016
+ )
1017
+ in deployed
1018
+ or skill.get("name", skill.get("skill_id")) in deployed
1019
+ for skill in pattern_skills
1020
+ )
1021
+
1022
+ # Add pattern group header as selectable choice
1023
+ if pattern:
1024
+ # Named pattern group
1025
+ pattern_icon = self._get_pattern_icon(pattern)
1026
+ skill_count = len(pattern_skills)
1027
+ group_key = f"__group__:{pattern}"
1028
+ group_to_skills[group_key] = skill_ids_in_group
1029
+
1030
+ skill_choices.append(
1031
+ Choice(
1032
+ title=f"{pattern_icon} {pattern} ({skill_count} skills) [Select All]",
1033
+ value=group_key,
1034
+ checked=all_installed,
1035
+ )
1036
+ )
1037
+ elif pattern_skills:
1038
+ # "Other" group - only show if there are skills
1039
+ group_key = "__group__:Other"
1040
+ group_to_skills[group_key] = skill_ids_in_group
1041
+
1042
+ skill_choices.append(
1043
+ Choice(
1044
+ title=f"📦 Other ({len(pattern_skills)} skills) [Select All]",
1045
+ value=group_key,
1046
+ checked=all_installed,
1047
+ )
1048
+ )
1049
+
1050
+ # Add skills in this pattern group
1051
+ for skill in sorted(pattern_skills, key=lambda x: x.get("name", "")):
1052
+ skill_id = skill.get("name", skill.get("skill_id", "unknown"))
1053
+ deploy_name = skill.get("deployment_name", skill_id)
1054
+ description = skill.get("description", "")[:50]
1055
+
1056
+ # Check if installed
1057
+ is_installed = deploy_name in deployed or skill_id in deployed
1058
+
1059
+ # Add indentation for pattern-grouped skills (all skills are indented)
1060
+ skill_choices.append(
1061
+ Choice(
1062
+ title=f" {skill_id} - {description}",
1063
+ value=skill_id,
1064
+ checked=is_installed,
1065
+ )
1066
+ )
1067
+
1068
+ # Add spacing between pattern groups (not after last group)
1069
+ if pattern != sorted_patterns[-1]:
1070
+ skill_choices.append(Separator())
1071
+
1072
+ self.console.clear()
1073
+ self._display_header()
1074
+ self.console.print(
1075
+ f"\n{icons.get(selected_cat, '📦')} [bold]{selected_cat.title()}[/bold]"
1076
+ )
1077
+ self.console.print(
1078
+ "[dim]Use spacebar to toggle individual skills or entire groups, enter to confirm[/dim]\n"
1079
+ )
1080
+
1081
+ selected = questionary.checkbox(
1082
+ "Select skills to install:",
1083
+ choices=skill_choices,
1084
+ style=self.QUESTIONARY_STYLE,
1085
+ ).ask()
1086
+
1087
+ if selected is None:
1088
+ continue # User cancelled, go back to category selection
1089
+
1090
+ # Process group selections - expand to individual skills
1091
+ selected_set = set()
1092
+ for item in selected:
1093
+ if item.startswith("__group__:"):
1094
+ # Expand group selection to all skills in that group
1095
+ selected_set.update(group_to_skills[item])
1096
+ else:
1097
+ # Individual skill selection
1098
+ selected_set.add(item)
1099
+
1100
+ current_in_cat = set()
1101
+
1102
+ # Find currently installed skills in this category
1103
+ for skill in category_skills:
1104
+ skill_id = skill.get("name", skill.get("skill_id", "unknown"))
1105
+ deploy_name = skill.get("deployment_name", skill_id)
1106
+ if deploy_name in deployed or skill_id in deployed:
1107
+ current_in_cat.add(skill_id)
1108
+
1109
+ # Install newly selected
1110
+ to_install = selected_set - current_in_cat
1111
+ for skill_id in to_install:
1112
+ skill = next(
1113
+ (
1114
+ s
1115
+ for s in category_skills
1116
+ if s.get("name") == skill_id or s.get("skill_id") == skill_id
1117
+ ),
1118
+ None,
1119
+ )
1120
+ if skill:
1121
+ self._install_skill_from_dict(skill)
1122
+ self.console.print(f"[green]✓ Installed {skill_id}[/green]")
1123
+
1124
+ # Uninstall deselected
1125
+ to_uninstall = current_in_cat - selected_set
1126
+ for skill_id in to_uninstall:
1127
+ # Find the skill to get deployment_name
1128
+ skill = next(
1129
+ (
1130
+ s
1131
+ for s in category_skills
1132
+ if s.get("name") == skill_id or s.get("skill_id") == skill_id
1133
+ ),
1134
+ None,
1135
+ )
1136
+ if skill:
1137
+ deploy_name = skill.get("deployment_name", skill_id)
1138
+ # Use the name that's actually in deployed set
1139
+ name_to_uninstall = (
1140
+ deploy_name if deploy_name in deployed else skill_id
1141
+ )
1142
+ self._uninstall_skill_by_name(name_to_uninstall)
1143
+ self.console.print(f"[yellow]✗ Uninstalled {skill_id}[/yellow]")
1144
+
1145
+ # Update deployed set for next iteration
1146
+ deployed = self._get_deployed_skill_ids()
1147
+
1148
+ # Show completion message
1149
+ if to_install or to_uninstall:
1150
+ Prompt.ask("\nPress Enter to continue")
1151
+
1152
+ def _get_all_skills_from_git(self) -> list:
1153
+ """Get all skills from Git-based skill manager.
1154
+
1155
+ Returns:
1156
+ List of skill dicts with full metadata from GitSkillSourceManager.
1157
+ """
1158
+ from ...config.skill_sources import SkillSourceConfiguration
1159
+ from ...services.skills.git_skill_source_manager import GitSkillSourceManager
1160
+
1161
+ try:
1162
+ config = SkillSourceConfiguration()
1163
+ manager = GitSkillSourceManager(config)
1164
+ return manager.get_all_skills()
1165
+ except Exception as e:
1166
+ self.console.print(
1167
+ f"[yellow]Warning: Could not load Git skills: {e}[/yellow]"
1168
+ )
1169
+ return []
1170
+
1171
+ def _display_skills_table_grouped(self) -> None:
1172
+ """Display skills in a table grouped by category, like agents."""
1173
+ from rich import box
1174
+ from rich.table import Table
1175
+
1176
+ # Get all skills from Git manager
1177
+ all_skills = self._get_all_skills_from_git()
1178
+ deployed_ids = self._get_deployed_skill_ids()
1179
+
1180
+ if not all_skills:
1181
+ self.console.print(
1182
+ "[yellow]No skills available. Try syncing skills first.[/yellow]"
1183
+ )
1184
+ return
1185
+
1186
+ # Group skills by category/toolchain
1187
+ grouped = {}
1188
+ for skill in all_skills:
1189
+ # Try to get category from tags or use toolchain
1190
+ category = None
1191
+ tags = skill.get("tags", [])
1192
+
1193
+ # Look for category tag
1194
+ for tag in tags:
1195
+ if tag in [
1196
+ "universal",
1197
+ "python",
1198
+ "typescript",
1199
+ "javascript",
1200
+ "go",
1201
+ "rust",
1202
+ ]:
1203
+ category = tag
1204
+ break
1205
+
1206
+ # Fallback to toolchain or universal
1207
+ if not category:
1208
+ category = skill.get("toolchain", "universal")
1209
+
1210
+ if category not in grouped:
1211
+ grouped[category] = []
1212
+ grouped[category].append(skill)
1213
+
1214
+ # Sort categories: universal first, then alphabetically
1215
+ categories = sorted(grouped.keys(), key=lambda x: (x != "universal", x))
1216
+
1217
+ # Track global skill number across all categories
1218
+ skill_counter = 0
1219
+
1220
+ for category in categories:
1221
+ category_skills = grouped[category]
1222
+
1223
+ # Category header with icon
1224
+ icons = {
1225
+ "universal": "🌐",
1226
+ "python": "🐍",
1227
+ "typescript": "📘",
1228
+ "javascript": "📒",
1229
+ "go": "🔷",
1230
+ "rust": "⚙️",
1231
+ }
1232
+ icon = icons.get(category, "📦")
1233
+ self.console.print(
1234
+ f"\n{icon} [bold cyan]{category.title()}[/bold cyan] ({len(category_skills)} skills)"
1235
+ )
1236
+
1237
+ # Create table for this category
1238
+ table = Table(show_header=True, header_style="bold", box=box.SIMPLE)
1239
+ table.add_column("#", style="dim", width=4)
1240
+ table.add_column("Skill ID", style="cyan", width=35)
1241
+ table.add_column("Description", style="white", width=45)
1242
+ table.add_column("Status", style="green", width=12)
1243
+
1244
+ for skill in sorted(category_skills, key=lambda x: x.get("name", "")):
1245
+ skill_counter += 1
1246
+ skill_id = skill.get("name", skill.get("skill_id", "unknown"))
1247
+ # Use deployment_name for matching if available
1248
+ deploy_name = skill.get("deployment_name", skill_id)
1249
+ description = skill.get("description", "")[:45]
1250
+
1251
+ # Check if installed - handle both deployment_name and skill_id
1252
+ is_installed = deploy_name in deployed_ids or skill_id in deployed_ids
1253
+ status = "[green]✓ Installed[/green]" if is_installed else "Available"
1254
+
1255
+ table.add_row(str(skill_counter), skill_id, description, status)
1256
+
1257
+ self.console.print(table)
1258
+
1259
+ # Summary
1260
+ total = len(all_skills)
1261
+ installed = sum(
1262
+ 1
1263
+ for s in all_skills
1264
+ if s.get("deployment_name", s.get("name", "")) in deployed_ids
1265
+ or s.get("name", "") in deployed_ids
1266
+ )
1267
+ self.console.print(
1268
+ f"\n[dim]Showing {total} skills ({installed} installed)[/dim]"
1269
+ )
1270
+
1271
+ def _get_deployed_skill_ids(self) -> set:
1272
+ """Get set of deployed skill IDs from .claude/skills/ directory.
1273
+
1274
+ Returns:
1275
+ Set of skill directory names and common variations for matching.
1276
+ """
1277
+ from pathlib import Path
1278
+
1279
+ skills_dir = Path.cwd() / ".claude" / "skills"
1280
+ if not skills_dir.exists():
1281
+ return set()
1282
+
1283
+ # Each deployed skill is a directory in .claude/skills/
1284
+ deployed_ids = set()
1285
+ for skill_dir in skills_dir.iterdir():
1286
+ if skill_dir.is_dir() and not skill_dir.name.startswith("."):
1287
+ # Add both the directory name and common variations
1288
+ deployed_ids.add(skill_dir.name)
1289
+ # Also add without prefix for matching (e.g., universal-testing -> testing)
1290
+ if skill_dir.name.startswith("universal-"):
1291
+ deployed_ids.add(skill_dir.name.replace("universal-", "", 1))
1292
+
1293
+ return deployed_ids
1294
+
1295
+ def _install_skill(self, skill) -> None:
1296
+ """Install a skill to .claude/skills/ directory."""
1297
+ import shutil
1298
+ from pathlib import Path
1299
+
1300
+ # Target directory
1301
+ target_dir = Path.cwd() / ".claude" / "skills" / skill.skill_id
1302
+ target_dir.mkdir(parents=True, exist_ok=True)
1303
+
1304
+ # Copy skill file(s)
1305
+ if skill.path.is_file():
1306
+ # Single file skill - copy to skill.md in target directory
1307
+ shutil.copy2(skill.path, target_dir / "skill.md")
1308
+ elif skill.path.is_dir():
1309
+ # Directory-based skill - copy all contents
1310
+ for item in skill.path.iterdir():
1311
+ if item.is_file():
1312
+ shutil.copy2(item, target_dir / item.name)
1313
+ elif item.is_dir():
1314
+ shutil.copytree(item, target_dir / item.name, dirs_exist_ok=True)
1315
+
1316
+ def _uninstall_skill(self, skill) -> None:
1317
+ """Uninstall a skill from .claude/skills/ directory."""
1318
+ import shutil
1319
+ from pathlib import Path
1320
+
1321
+ target_dir = Path.cwd() / ".claude" / "skills" / skill.skill_id
1322
+ if target_dir.exists():
1323
+ shutil.rmtree(target_dir)
1324
+
1325
+ def _install_skill_from_dict(self, skill_dict: dict) -> None:
1326
+ """Install a skill from Git skill dict to .claude/skills/ directory.
1327
+
1328
+ Args:
1329
+ skill_dict: Skill metadata dict from GitSkillSourceManager.get_all_skills()
1330
+ """
1331
+ from pathlib import Path
1332
+
1333
+ skill_id = skill_dict.get("name", skill_dict.get("skill_id", "unknown"))
1334
+ content = skill_dict.get("content", "")
1335
+
1336
+ if not content:
1337
+ self.console.print(
1338
+ f"[yellow]Warning: Skill '{skill_id}' has no content[/yellow]"
1339
+ )
1340
+ return
1341
+
1342
+ # Target directory using deployment_name if available
1343
+ deploy_name = skill_dict.get("deployment_name", skill_id)
1344
+ target_dir = Path.cwd() / ".claude" / "skills" / deploy_name
1345
+ target_dir.mkdir(parents=True, exist_ok=True)
1346
+
1347
+ # Write skill content to skill.md
1348
+ skill_file = target_dir / "skill.md"
1349
+ skill_file.write_text(content, encoding="utf-8")
1350
+
1351
+ def _uninstall_skill_by_name(self, skill_name: str) -> None:
1352
+ """Uninstall a skill by name from .claude/skills/ directory.
1353
+
1354
+ Args:
1355
+ skill_name: Name of skill directory to remove
1356
+ """
1357
+ import shutil
1358
+ from pathlib import Path
1359
+
1360
+ target_dir = Path.cwd() / ".claude" / "skills" / skill_name
1361
+ if target_dir.exists():
1362
+ shutil.rmtree(target_dir)
1363
+
808
1364
  def _display_behavior_files(self) -> None:
809
1365
  """Display current behavior files."""
810
1366
  self.behavior_manager.display_behavior_files()
@@ -1437,18 +1993,20 @@ class ConfigureCommand(BaseCommand):
1437
1993
 
1438
1994
  # Add inline control: Select/Deselect all from this collection
1439
1995
  if all_selected:
1996
+ deselect_value = f"__DESELECT_ALL_{collection_id}__"
1440
1997
  choices.append(
1441
1998
  Choice(
1442
- f" [Deselect all from {collection_id}]",
1443
- value=f"__DESELECT_ALL_{collection_id}__",
1999
+ f" [Deselect all from {collection_id}]", # nosec B608
2000
+ value=deselect_value,
1444
2001
  checked=False,
1445
2002
  )
1446
2003
  )
1447
2004
  else:
2005
+ select_value = f"__SELECT_ALL_{collection_id}__"
1448
2006
  choices.append(
1449
2007
  Choice(
1450
- f" [Select all from {collection_id}]",
1451
- value=f"__SELECT_ALL_{collection_id}__",
2008
+ f" [Select all from {collection_id}]", # nosec B608
2009
+ value=select_value,
1452
2010
  checked=False,
1453
2011
  )
1454
2012
  )
@@ -1518,18 +2076,32 @@ class ConfigureCommand(BaseCommand):
1518
2076
  )
1519
2077
  display_name = self._format_display_name(raw_display_name)
1520
2078
 
1521
- # Check if agent is deployed (exists in .claude/agents/)
2079
+ # Check if agent is required (cannot be unchecked)
2080
+ required_agents = set(self.unified_config.agents.required)
2081
+ is_required = (
2082
+ agent_leaf_name in required_agents
2083
+ or agent_id in required_agents
2084
+ )
1522
2085
 
1523
- # Format choice text (no asterisk needed)
1524
- choice_text = f" {display_name}"
2086
+ # Format choice text with [Required] indicator
2087
+ if is_required:
2088
+ choice_text = f" {display_name} [Required]"
2089
+ else:
2090
+ choice_text = f" {display_name}"
1525
2091
 
1526
- is_selected = agent_id in current_selection
2092
+ # Required agents are always selected
2093
+ is_selected = is_required or agent_id in current_selection
2094
+
2095
+ # Add to current selection if required
2096
+ if is_required:
2097
+ current_selection.add(agent_id)
1527
2098
 
1528
2099
  choices.append(
1529
2100
  Choice(
1530
2101
  title=choice_text,
1531
2102
  value=agent_id, # Use agent_id for value
1532
2103
  checked=is_selected,
2104
+ disabled=is_required, # Disable checkbox for required agents
1533
2105
  )
1534
2106
  )
1535
2107
 
@@ -1538,6 +2110,7 @@ class ConfigureCommand(BaseCommand):
1538
2110
  self.console.print(
1539
2111
  "[dim][ ] Unchecked = Available (check to install)[/dim]"
1540
2112
  )
2113
+ self.console.print("[dim][Required] = Core agents (always installed)[/dim]")
1541
2114
  self.console.print(
1542
2115
  "[dim]Use arrow keys to navigate, space to toggle, Enter to apply[/dim]\n"
1543
2116
  )
@@ -1624,12 +2197,37 @@ class ConfigureCommand(BaseCommand):
1624
2197
 
1625
2198
  # No controls selected - use the individual selections as final
1626
2199
  final_selection = set(selected_values)
2200
+
2201
+ # Ensure required agents are always in the final selection
2202
+ required_agents = set(self.unified_config.agents.required)
2203
+ for agent in agents:
2204
+ agent_id = getattr(agent, "agent_id", agent.name)
2205
+ agent_leaf_name = agent_id.split("/")[-1]
2206
+ if agent_leaf_name in required_agents or agent_id in required_agents:
2207
+ final_selection.add(agent_id)
2208
+
1627
2209
  break
1628
2210
 
1629
2211
  # Determine changes
1630
2212
  to_deploy = final_selection - deployed_full_paths
1631
2213
  to_remove = deployed_full_paths - final_selection
1632
2214
 
2215
+ # Prevent removal of required agents
2216
+ required_agents = set(self.unified_config.agents.required)
2217
+ to_remove_filtered = set()
2218
+ for agent_id in to_remove:
2219
+ agent_leaf_name = agent_id.split("/")[-1]
2220
+ if (
2221
+ agent_leaf_name not in required_agents
2222
+ and agent_id not in required_agents
2223
+ ):
2224
+ to_remove_filtered.add(agent_id)
2225
+ else:
2226
+ self.console.print(
2227
+ f"[yellow]⚠ Cannot remove required agent: {agent_id}[/yellow]"
2228
+ )
2229
+ to_remove = to_remove_filtered
2230
+
1633
2231
  if not to_deploy and not to_remove:
1634
2232
  self.console.print("[yellow]No changes needed[/yellow]")
1635
2233
  Prompt.ask("\nPress Enter to continue")
@@ -2329,7 +2927,8 @@ class ConfigureCommand(BaseCommand):
2329
2927
  f" Detection Quality: [{'green' if summary.get('detection_quality') == 'high' else 'yellow'}]{summary.get('detection_quality', 'unknown')}[/]"
2330
2928
  )
2331
2929
  self.console.print()
2332
- except Exception:
2930
+ except Exception: # nosec B110 - Suppress broad except for failed safety check
2931
+ # Silent failure on safety check - non-critical feature
2333
2932
  pass
2334
2933
 
2335
2934
  # Build mapping: agent_id -> AgentConfig