claude-mpm 3.4.10__py3-none-any.whl → 5.4.55__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 (950) hide show
  1. claude_mpm/BUILD_NUMBER +1 -0
  2. claude_mpm/VERSION +1 -0
  3. claude_mpm/__init__.py +50 -12
  4. claude_mpm/__main__.py +7 -2
  5. claude_mpm/agents/BASE_AGENT.md +164 -0
  6. claude_mpm/agents/BASE_ENGINEER.md +658 -0
  7. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +290 -0
  8. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
  9. claude_mpm/agents/MEMORY.md +72 -0
  10. claude_mpm/agents/PM_INSTRUCTIONS.md +1402 -0
  11. claude_mpm/agents/WORKFLOW.md +111 -0
  12. claude_mpm/agents/__init__.py +92 -80
  13. claude_mpm/agents/agent-template.yaml +83 -0
  14. claude_mpm/agents/agent_loader.py +560 -745
  15. claude_mpm/agents/agent_loader_integration.py +53 -55
  16. claude_mpm/agents/agents_metadata.py +186 -27
  17. claude_mpm/agents/async_agent_loader.py +436 -0
  18. claude_mpm/agents/base_agent.json +8 -4
  19. claude_mpm/agents/frontmatter_validator.py +754 -0
  20. claude_mpm/agents/system_agent_config.py +222 -155
  21. claude_mpm/agents/templates/README.md +465 -0
  22. claude_mpm/agents/templates/__init__.py +17 -13
  23. claude_mpm/agents/templates/circuit-breakers.md +1391 -0
  24. claude_mpm/agents/templates/context-management-examples.md +544 -0
  25. claude_mpm/agents/templates/git-file-tracking.md +584 -0
  26. claude_mpm/agents/templates/pm-examples.md +474 -0
  27. claude_mpm/agents/templates/pm-red-flags.md +310 -0
  28. claude_mpm/agents/templates/pr-workflow-examples.md +427 -0
  29. claude_mpm/agents/templates/research-gate-examples.md +669 -0
  30. claude_mpm/agents/templates/response-format.md +583 -0
  31. claude_mpm/agents/templates/structured-questions-examples.md +615 -0
  32. claude_mpm/agents/templates/ticket-completeness-examples.md +139 -0
  33. claude_mpm/agents/templates/ticketing-examples.md +277 -0
  34. claude_mpm/agents/templates/validation-templates.md +312 -0
  35. claude_mpm/cli/__init__.py +90 -128
  36. claude_mpm/cli/__main__.py +33 -0
  37. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  38. claude_mpm/cli/commands/__init__.py +36 -12
  39. claude_mpm/cli/commands/agent_manager.py +1403 -0
  40. claude_mpm/cli/commands/agent_source.py +774 -0
  41. claude_mpm/cli/commands/agent_state_manager.py +335 -0
  42. claude_mpm/cli/commands/agents.py +2503 -168
  43. claude_mpm/cli/commands/agents_cleanup.py +210 -0
  44. claude_mpm/cli/commands/agents_discover.py +338 -0
  45. claude_mpm/cli/commands/aggregate.py +540 -0
  46. claude_mpm/cli/commands/analyze.py +553 -0
  47. claude_mpm/cli/commands/analyze_code.py +528 -0
  48. claude_mpm/cli/commands/auto_configure.py +1053 -0
  49. claude_mpm/cli/commands/cleanup.py +588 -0
  50. claude_mpm/cli/commands/cleanup_orphaned_agents.py +150 -0
  51. claude_mpm/cli/commands/config.py +586 -0
  52. claude_mpm/cli/commands/configure.py +2654 -0
  53. claude_mpm/cli/commands/configure_agent_display.py +282 -0
  54. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  55. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  56. claude_mpm/cli/commands/configure_models.py +18 -0
  57. claude_mpm/cli/commands/configure_navigation.py +184 -0
  58. claude_mpm/cli/commands/configure_paths.py +104 -0
  59. claude_mpm/cli/commands/configure_persistence.py +254 -0
  60. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  61. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  62. claude_mpm/cli/commands/configure_validators.py +73 -0
  63. claude_mpm/cli/commands/dashboard.py +286 -0
  64. claude_mpm/cli/commands/debug.py +1386 -0
  65. claude_mpm/cli/commands/doctor.py +243 -0
  66. claude_mpm/cli/commands/hook_errors.py +277 -0
  67. claude_mpm/cli/commands/info.py +195 -74
  68. claude_mpm/cli/commands/local_deploy.py +534 -0
  69. claude_mpm/cli/commands/mcp.py +205 -0
  70. claude_mpm/cli/commands/mcp_command_router.py +161 -0
  71. claude_mpm/cli/commands/mcp_config.py +154 -0
  72. claude_mpm/cli/commands/mcp_config_commands.py +20 -0
  73. claude_mpm/cli/commands/mcp_external_commands.py +249 -0
  74. claude_mpm/cli/commands/mcp_install_commands.py +346 -0
  75. claude_mpm/cli/commands/mcp_pipx_config.py +208 -0
  76. claude_mpm/cli/commands/mcp_server_commands.py +155 -0
  77. claude_mpm/cli/commands/mcp_setup_external.py +868 -0
  78. claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
  79. claude_mpm/cli/commands/memory.py +585 -846
  80. claude_mpm/cli/commands/monitor.py +228 -310
  81. claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
  82. claude_mpm/cli/commands/mpm_init/core.py +759 -0
  83. claude_mpm/cli/commands/mpm_init/display.py +341 -0
  84. claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
  85. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  86. claude_mpm/cli/commands/mpm_init/modes.py +397 -0
  87. claude_mpm/cli/commands/mpm_init/prompts.py +722 -0
  88. claude_mpm/cli/commands/mpm_init_cli.py +396 -0
  89. claude_mpm/cli/commands/mpm_init_handler.py +195 -0
  90. claude_mpm/cli/commands/postmortem.py +401 -0
  91. claude_mpm/cli/commands/profile.py +276 -0
  92. claude_mpm/cli/commands/run.py +910 -488
  93. claude_mpm/cli/commands/search.py +458 -0
  94. claude_mpm/cli/commands/skill_source.py +694 -0
  95. claude_mpm/cli/commands/skills.py +1246 -0
  96. claude_mpm/cli/commands/summarize.py +413 -0
  97. claude_mpm/cli/commands/tickets.py +536 -53
  98. claude_mpm/cli/commands/uninstall.py +176 -0
  99. claude_mpm/cli/commands/upgrade.py +152 -0
  100. claude_mpm/cli/commands/verify.py +119 -0
  101. claude_mpm/cli/executor.py +297 -0
  102. claude_mpm/cli/helpers.py +105 -0
  103. claude_mpm/cli/interactive/__init__.py +21 -0
  104. claude_mpm/cli/interactive/agent_wizard.py +1947 -0
  105. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  106. claude_mpm/cli/parser.py +87 -563
  107. claude_mpm/cli/parsers/__init__.py +35 -0
  108. claude_mpm/cli/parsers/agent_manager_parser.py +393 -0
  109. claude_mpm/cli/parsers/agent_source_parser.py +171 -0
  110. claude_mpm/cli/parsers/agents_parser.py +575 -0
  111. claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
  112. claude_mpm/cli/parsers/analyze_parser.py +135 -0
  113. claude_mpm/cli/parsers/auto_configure_parser.py +120 -0
  114. claude_mpm/cli/parsers/base_parser.py +644 -0
  115. claude_mpm/cli/parsers/config_parser.py +208 -0
  116. claude_mpm/cli/parsers/configure_parser.py +138 -0
  117. claude_mpm/cli/parsers/dashboard_parser.py +113 -0
  118. claude_mpm/cli/parsers/debug_parser.py +319 -0
  119. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  120. claude_mpm/cli/parsers/mcp_parser.py +195 -0
  121. claude_mpm/cli/parsers/memory_parser.py +138 -0
  122. claude_mpm/cli/parsers/monitor_parser.py +142 -0
  123. claude_mpm/cli/parsers/mpm_init_parser.py +311 -0
  124. claude_mpm/cli/parsers/profile_parser.py +147 -0
  125. claude_mpm/cli/parsers/run_parser.py +157 -0
  126. claude_mpm/cli/parsers/search_parser.py +245 -0
  127. claude_mpm/cli/parsers/skill_source_parser.py +169 -0
  128. claude_mpm/cli/parsers/skills_parser.py +277 -0
  129. claude_mpm/cli/parsers/source_parser.py +138 -0
  130. claude_mpm/cli/parsers/tickets_parser.py +203 -0
  131. claude_mpm/cli/shared/__init__.py +40 -0
  132. claude_mpm/cli/shared/argument_patterns.py +205 -0
  133. claude_mpm/cli/shared/base_command.py +242 -0
  134. claude_mpm/cli/shared/error_handling.py +242 -0
  135. claude_mpm/cli/shared/output_formatters.py +241 -0
  136. claude_mpm/cli/startup.py +1743 -0
  137. claude_mpm/cli/startup_display.py +480 -0
  138. claude_mpm/cli/startup_logging.py +839 -0
  139. claude_mpm/cli/utils.py +136 -47
  140. claude_mpm/cli_module/__init__.py +6 -6
  141. claude_mpm/cli_module/args.py +188 -140
  142. claude_mpm/cli_module/commands.py +79 -70
  143. claude_mpm/cli_module/migration_example.py +42 -64
  144. claude_mpm/commands/__init__.py +14 -0
  145. claude_mpm/commands/mpm-config.md +28 -0
  146. claude_mpm/commands/mpm-doctor.md +20 -0
  147. claude_mpm/commands/mpm-help.md +20 -0
  148. claude_mpm/commands/mpm-init.md +120 -0
  149. claude_mpm/commands/mpm-monitor.md +31 -0
  150. claude_mpm/commands/mpm-organize.md +120 -0
  151. claude_mpm/commands/mpm-postmortem.md +21 -0
  152. claude_mpm/commands/mpm-session-resume.md +30 -0
  153. claude_mpm/commands/mpm-status.md +20 -0
  154. claude_mpm/commands/mpm-ticket-view.md +109 -0
  155. claude_mpm/commands/mpm-version.md +20 -0
  156. claude_mpm/commands/mpm.md +31 -0
  157. claude_mpm/config/__init__.py +42 -2
  158. claude_mpm/config/agent_config.py +402 -0
  159. claude_mpm/config/agent_presets.py +488 -0
  160. claude_mpm/config/agent_sources.py +352 -0
  161. claude_mpm/config/experimental_features.py +217 -0
  162. claude_mpm/config/model_config.py +428 -0
  163. claude_mpm/config/paths.py +258 -0
  164. claude_mpm/config/skill_presets.py +392 -0
  165. claude_mpm/config/skill_sources.py +590 -0
  166. claude_mpm/config/socketio_config.py +125 -83
  167. claude_mpm/constants.py +132 -22
  168. claude_mpm/core/__init__.py +62 -36
  169. claude_mpm/core/agent_name_normalizer.py +71 -73
  170. claude_mpm/core/agent_registry.py +385 -492
  171. claude_mpm/core/agent_session_manager.py +81 -70
  172. claude_mpm/core/api_validator.py +330 -0
  173. claude_mpm/core/base_service.py +159 -122
  174. claude_mpm/core/cache.py +560 -0
  175. claude_mpm/core/claude_runner.py +696 -916
  176. claude_mpm/core/config.py +613 -122
  177. claude_mpm/core/config_aliases.py +74 -73
  178. claude_mpm/core/config_constants.py +314 -0
  179. claude_mpm/core/constants.py +361 -0
  180. claude_mpm/core/container.py +646 -104
  181. claude_mpm/core/enums.py +452 -0
  182. claude_mpm/core/error_handler.py +623 -0
  183. claude_mpm/core/exceptions.py +536 -0
  184. claude_mpm/core/factories.py +105 -109
  185. claude_mpm/core/file_utils.py +764 -0
  186. claude_mpm/core/framework/__init__.py +25 -0
  187. claude_mpm/core/framework/formatters/__init__.py +11 -0
  188. claude_mpm/core/framework/formatters/capability_generator.py +367 -0
  189. claude_mpm/core/framework/formatters/content_formatter.py +278 -0
  190. claude_mpm/core/framework/formatters/context_generator.py +185 -0
  191. claude_mpm/core/framework/loaders/__init__.py +13 -0
  192. claude_mpm/core/framework/loaders/agent_loader.py +213 -0
  193. claude_mpm/core/framework/loaders/file_loader.py +176 -0
  194. claude_mpm/core/framework/loaders/instruction_loader.py +222 -0
  195. claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
  196. claude_mpm/core/framework/processors/__init__.py +11 -0
  197. claude_mpm/core/framework/processors/memory_processor.py +230 -0
  198. claude_mpm/core/framework/processors/metadata_processor.py +146 -0
  199. claude_mpm/core/framework/processors/template_processor.py +244 -0
  200. claude_mpm/core/framework_loader.py +485 -414
  201. claude_mpm/core/hook_error_memory.py +381 -0
  202. claude_mpm/core/hook_manager.py +246 -86
  203. claude_mpm/core/hook_performance_config.py +147 -0
  204. claude_mpm/core/injectable_service.py +72 -63
  205. claude_mpm/core/instruction_reinforcement_hook.py +267 -0
  206. claude_mpm/core/interactive_session.py +670 -0
  207. claude_mpm/core/interfaces.py +570 -164
  208. claude_mpm/core/lazy.py +467 -0
  209. claude_mpm/core/log_manager.py +707 -0
  210. claude_mpm/core/logger.py +295 -134
  211. claude_mpm/core/logging_config.py +474 -0
  212. claude_mpm/core/logging_utils.py +520 -0
  213. claude_mpm/core/minimal_framework_loader.py +24 -22
  214. claude_mpm/core/mixins.py +30 -29
  215. claude_mpm/core/oneshot_session.py +594 -0
  216. claude_mpm/core/optimized_agent_loader.py +479 -0
  217. claude_mpm/core/optimized_startup.py +554 -0
  218. claude_mpm/core/output_style_manager.py +483 -0
  219. claude_mpm/core/pm_hook_interceptor.py +197 -82
  220. claude_mpm/core/protocols/__init__.py +23 -0
  221. claude_mpm/core/protocols/runner_protocol.py +103 -0
  222. claude_mpm/core/protocols/session_protocol.py +131 -0
  223. claude_mpm/core/service_registry.py +153 -116
  224. claude_mpm/core/session_manager.py +179 -64
  225. claude_mpm/core/shared/__init__.py +17 -0
  226. claude_mpm/core/shared/config_loader.py +326 -0
  227. claude_mpm/core/shared/path_resolver.py +281 -0
  228. claude_mpm/core/shared/singleton_manager.py +221 -0
  229. claude_mpm/core/socketio_pool.py +400 -137
  230. claude_mpm/core/system_context.py +38 -0
  231. claude_mpm/core/tool_access_control.py +64 -57
  232. claude_mpm/core/types.py +307 -0
  233. claude_mpm/core/typing_utils.py +553 -0
  234. claude_mpm/core/unified_agent_registry.py +969 -0
  235. claude_mpm/core/unified_config.py +570 -0
  236. claude_mpm/core/unified_paths.py +941 -0
  237. claude_mpm/dashboard/__init__.py +12 -0
  238. claude_mpm/dashboard/api/simple_directory.py +261 -0
  239. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  240. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +1 -0
  241. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +1 -0
  242. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +1 -0
  243. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +24 -0
  244. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B0uc0UOD.js +36 -0
  245. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7RN905-.js +1 -0
  246. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7xVLGWV.js +2 -0
  247. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BIF9m_hv.js +61 -0
  248. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +1 -0
  249. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BPYeabCQ.js +1 -0
  250. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
  251. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BSNlmTZj.js +1 -0
  252. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Be7GpZd6.js +7 -0
  253. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bh0LDWpI.js +145 -0
  254. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BofRWZRR.js +10 -0
  255. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BovzEFCE.js +30 -0
  256. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C30mlcqg.js +165 -0
  257. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4B-KCzX.js +1 -0
  258. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4JcI4KD.js +122 -0
  259. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CBBdVcY8.js +1 -0
  260. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CDuw-vjf.js +1 -0
  261. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C_Usid8X.js +15 -0
  262. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cfqx1Qun.js +10 -0
  263. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CiIAseT4.js +128 -0
  264. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
  265. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CnA0NrzZ.js +1 -0
  266. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cs_tUR18.js +24 -0
  267. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
  268. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CyWMqx4W.js +43 -0
  269. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzZX-COe.js +220 -0
  270. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzeYkLYB.js +65 -0
  271. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D3k0OPJN.js +4 -0
  272. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9lljYKQ.js +1 -0
  273. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DGkLK5U1.js +267 -0
  274. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DI7hHRFL.js +1 -0
  275. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DLVjFsZ3.js +139 -0
  276. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUrLdbGD.js +89 -0
  277. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
  278. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DY1XQ8fi.js +2 -0
  279. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DZX00Y4g.js +1 -0
  280. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +1 -0
  281. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DaimHw_p.js +68 -0
  282. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +323 -0
  283. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dhb8PKl3.js +1 -0
  284. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dle-35c7.js +64 -0
  285. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DmxopI1J.js +1 -0
  286. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DwBR2MJi.js +60 -0
  287. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/GYwsonyD.js +1 -0
  288. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
  289. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/NqQ1dWOy.js +1 -0
  290. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/RJiighC3.js +1 -0
  291. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Vzk33B_K.js +2 -0
  292. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ZGh7QtNv.js +7 -0
  293. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bT1r9zLR.js +1 -0
  294. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bTOqqlTd.js +1 -0
  295. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/eNVUfhuA.js +1 -0
  296. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/iEWssX7S.js +162 -0
  297. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/sQeU3Y1z.js +1 -0
  298. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uuIeMWc-.js +1 -0
  299. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.D6-I5TpK.js +2 -0
  300. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +1 -0
  301. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.m1gL8KXf.js +1 -0
  302. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.CgNOuw-d.js +1 -0
  303. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +1 -0
  304. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  305. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  306. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  307. claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
  308. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
  309. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
  310. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
  311. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
  312. claude_mpm/experimental/__init__.py +10 -0
  313. claude_mpm/experimental/cli_enhancements.py +104 -89
  314. claude_mpm/generators/__init__.py +1 -1
  315. claude_mpm/generators/agent_profile_generator.py +76 -66
  316. claude_mpm/hooks/__init__.py +37 -1
  317. claude_mpm/hooks/base_hook.py +37 -32
  318. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  319. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  320. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  321. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  322. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  323. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  324. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  325. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  326. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  327. claude_mpm/hooks/claude_hooks/connection_pool.py +250 -0
  328. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  329. claude_mpm/hooks/claude_hooks/event_handlers.py +888 -0
  330. claude_mpm/hooks/claude_hooks/hook_handler.py +652 -875
  331. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +10 -7
  332. claude_mpm/hooks/claude_hooks/installer.py +806 -0
  333. claude_mpm/hooks/claude_hooks/memory_integration.py +249 -0
  334. claude_mpm/hooks/claude_hooks/response_tracking.py +412 -0
  335. claude_mpm/hooks/claude_hooks/services/__init__.py +15 -0
  336. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  337. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  338. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  339. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  340. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  341. claude_mpm/hooks/claude_hooks/services/connection_manager.py +229 -0
  342. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +254 -0
  343. claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
  344. claude_mpm/hooks/claude_hooks/services/state_manager.py +284 -0
  345. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
  346. claude_mpm/hooks/claude_hooks/tool_analysis.py +224 -0
  347. claude_mpm/hooks/failure_learning/__init__.py +54 -0
  348. claude_mpm/hooks/failure_learning/failure_detection_hook.py +230 -0
  349. claude_mpm/hooks/failure_learning/fix_detection_hook.py +212 -0
  350. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +281 -0
  351. claude_mpm/hooks/instruction_reinforcement.py +301 -0
  352. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  353. claude_mpm/hooks/kuzu_memory_hook.py +386 -0
  354. claude_mpm/hooks/kuzu_response_hook.py +179 -0
  355. claude_mpm/hooks/memory_integration_hook.py +201 -107
  356. claude_mpm/hooks/session_resume_hook.py +121 -0
  357. claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
  358. claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
  359. claude_mpm/hooks/tool_call_interceptor.py +92 -76
  360. claude_mpm/hooks/validation_hooks.py +62 -54
  361. claude_mpm/init.py +518 -83
  362. claude_mpm/models/__init__.py +9 -9
  363. claude_mpm/models/agent_definition.py +40 -23
  364. claude_mpm/models/agent_session.py +538 -0
  365. claude_mpm/models/git_repository.py +198 -0
  366. claude_mpm/models/resume_log.py +340 -0
  367. claude_mpm/schemas/__init__.py +12 -0
  368. claude_mpm/scripts/__init__.py +15 -0
  369. claude_mpm/scripts/claude-hook-handler.sh +227 -0
  370. claude_mpm/scripts/launch_monitor.py +165 -0
  371. claude_mpm/scripts/mpm_doctor.py +322 -0
  372. claude_mpm/scripts/socketio_daemon.py +189 -200
  373. claude_mpm/scripts/start_activity_logging.py +91 -0
  374. claude_mpm/services/__init__.py +208 -39
  375. claude_mpm/services/agent_capabilities_service.py +266 -0
  376. claude_mpm/services/agents/__init__.py +89 -0
  377. claude_mpm/services/agents/agent_builder.py +514 -0
  378. claude_mpm/services/agents/agent_preset_service.py +238 -0
  379. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  380. claude_mpm/services/agents/agent_review_service.py +280 -0
  381. claude_mpm/services/agents/agent_selection_service.py +484 -0
  382. claude_mpm/services/agents/auto_config_manager.py +796 -0
  383. claude_mpm/services/agents/auto_deploy_index_parser.py +569 -0
  384. claude_mpm/services/agents/cache_git_manager.py +621 -0
  385. claude_mpm/services/agents/deployment/__init__.py +21 -0
  386. claude_mpm/services/agents/deployment/agent_config_provider.py +410 -0
  387. claude_mpm/services/agents/deployment/agent_configuration_manager.py +358 -0
  388. claude_mpm/services/agents/deployment/agent_definition_factory.py +80 -0
  389. claude_mpm/services/agents/deployment/agent_deployment.py +1037 -0
  390. claude_mpm/services/agents/deployment/agent_discovery_service.py +546 -0
  391. claude_mpm/services/agents/deployment/agent_environment_manager.py +288 -0
  392. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +383 -0
  393. claude_mpm/services/agents/deployment/agent_format_converter.py +505 -0
  394. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +160 -0
  395. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +957 -0
  396. claude_mpm/services/agents/deployment/agent_metrics_collector.py +273 -0
  397. claude_mpm/services/agents/deployment/agent_operation_service.py +573 -0
  398. claude_mpm/services/agents/deployment/agent_record_service.py +418 -0
  399. claude_mpm/services/agents/deployment/agent_restore_handler.py +84 -0
  400. claude_mpm/services/agents/deployment/agent_state_service.py +381 -0
  401. claude_mpm/services/agents/deployment/agent_template_builder.py +1369 -0
  402. claude_mpm/services/agents/deployment/agent_validator.py +376 -0
  403. claude_mpm/services/agents/deployment/agent_version_manager.py +322 -0
  404. claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +10 -13
  405. claude_mpm/services/agents/deployment/agents_directory_resolver.py +149 -0
  406. claude_mpm/services/agents/deployment/async_agent_deployment.py +768 -0
  407. claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
  408. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  409. claude_mpm/services/agents/deployment/config/deployment_config.py +181 -0
  410. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  411. claude_mpm/services/agents/deployment/deployment_config_loader.py +178 -0
  412. claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
  413. claude_mpm/services/agents/deployment/deployment_type_detector.py +120 -0
  414. claude_mpm/services/agents/deployment/deployment_wrapper.py +129 -0
  415. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  416. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  417. claude_mpm/services/agents/deployment/facade/deployment_executor.py +70 -0
  418. claude_mpm/services/agents/deployment/facade/deployment_facade.py +269 -0
  419. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  420. claude_mpm/services/agents/deployment/interface_adapter.py +226 -0
  421. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  422. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  423. claude_mpm/services/agents/deployment/local_template_deployment.py +362 -0
  424. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +1478 -0
  425. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  426. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  427. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +162 -0
  428. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  429. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  430. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +240 -0
  431. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +110 -0
  432. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +80 -0
  433. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +92 -0
  434. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +101 -0
  435. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  436. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +102 -0
  437. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  438. claude_mpm/services/agents/deployment/processors/agent_processor.py +269 -0
  439. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +311 -0
  440. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +862 -0
  441. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  442. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  443. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  444. claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
  445. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  446. claude_mpm/services/agents/deployment/strategies/base_strategy.py +113 -0
  447. claude_mpm/services/agents/deployment/strategies/project_strategy.py +148 -0
  448. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  449. claude_mpm/services/agents/deployment/strategies/system_strategy.py +131 -0
  450. claude_mpm/services/agents/deployment/strategies/user_strategy.py +130 -0
  451. claude_mpm/services/agents/deployment/system_instructions_deployer.py +228 -0
  452. claude_mpm/services/agents/deployment/validation/__init__.py +21 -0
  453. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  454. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  455. claude_mpm/services/agents/deployment/validation/template_validator.py +319 -0
  456. claude_mpm/services/agents/deployment/validation/validation_result.py +214 -0
  457. claude_mpm/services/agents/git_source_manager.py +682 -0
  458. claude_mpm/services/agents/loading/__init__.py +11 -0
  459. claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +306 -228
  460. claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +106 -91
  461. claude_mpm/services/agents/loading/framework_agent_loader.py +433 -0
  462. claude_mpm/services/agents/local_template_manager.py +784 -0
  463. claude_mpm/services/agents/management/__init__.py +9 -0
  464. claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +92 -69
  465. claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +219 -168
  466. claude_mpm/services/agents/memory/__init__.py +22 -0
  467. claude_mpm/services/agents/memory/agent_memory_manager.py +784 -0
  468. claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +20 -18
  469. claude_mpm/services/agents/memory/content_manager.py +470 -0
  470. claude_mpm/services/agents/memory/memory_categorization_service.py +167 -0
  471. claude_mpm/services/agents/memory/memory_file_service.py +129 -0
  472. claude_mpm/services/agents/memory/memory_format_service.py +201 -0
  473. claude_mpm/services/agents/memory/memory_limits_service.py +101 -0
  474. claude_mpm/services/agents/memory/template_generator.py +83 -0
  475. claude_mpm/services/agents/observers.py +547 -0
  476. claude_mpm/services/agents/recommender.py +617 -0
  477. claude_mpm/services/agents/registry/__init__.py +30 -0
  478. claude_mpm/services/agents/registry/deployed_agent_discovery.py +273 -0
  479. claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +370 -295
  480. claude_mpm/services/agents/single_tier_deployment_service.py +696 -0
  481. claude_mpm/services/agents/sources/__init__.py +13 -0
  482. claude_mpm/services/agents/sources/agent_sync_state.py +516 -0
  483. claude_mpm/services/agents/sources/git_source_sync_service.py +1202 -0
  484. claude_mpm/services/agents/startup_sync.py +259 -0
  485. claude_mpm/services/agents/toolchain_detector.py +478 -0
  486. claude_mpm/services/analysis/__init__.py +35 -0
  487. claude_mpm/services/analysis/clone_detector.py +1030 -0
  488. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  489. claude_mpm/services/analysis/postmortem_service.py +765 -0
  490. claude_mpm/services/async_session_logger.py +665 -0
  491. claude_mpm/services/claude_session_logger.py +321 -0
  492. claude_mpm/services/cli/__init__.py +18 -0
  493. claude_mpm/services/cli/agent_cleanup_service.py +408 -0
  494. claude_mpm/services/cli/agent_dependency_service.py +395 -0
  495. claude_mpm/services/cli/agent_listing_service.py +463 -0
  496. claude_mpm/services/cli/agent_output_formatter.py +605 -0
  497. claude_mpm/services/cli/agent_validation_service.py +590 -0
  498. claude_mpm/services/cli/memory_crud_service.py +622 -0
  499. claude_mpm/services/cli/memory_output_formatter.py +604 -0
  500. claude_mpm/services/cli/resume_service.py +617 -0
  501. claude_mpm/services/cli/session_manager.py +604 -0
  502. claude_mpm/services/cli/session_pause_manager.py +504 -0
  503. claude_mpm/services/cli/session_resume_helper.py +372 -0
  504. claude_mpm/services/cli/startup_checker.py +362 -0
  505. claude_mpm/services/cli/unified_dashboard_manager.py +439 -0
  506. claude_mpm/services/command_deployment_service.py +446 -0
  507. claude_mpm/services/command_handler_service.py +221 -0
  508. claude_mpm/services/communication/__init__.py +22 -0
  509. claude_mpm/services/core/__init__.py +108 -0
  510. claude_mpm/services/core/base.py +269 -0
  511. claude_mpm/services/core/cache_manager.py +309 -0
  512. claude_mpm/services/core/interfaces/__init__.py +273 -0
  513. claude_mpm/services/core/interfaces/agent.py +514 -0
  514. claude_mpm/services/core/interfaces/communication.py +316 -0
  515. claude_mpm/services/core/interfaces/health.py +169 -0
  516. claude_mpm/services/core/interfaces/infrastructure.py +357 -0
  517. claude_mpm/services/core/interfaces/model.py +281 -0
  518. claude_mpm/services/core/interfaces/process.py +372 -0
  519. claude_mpm/services/core/interfaces/project.py +121 -0
  520. claude_mpm/services/core/interfaces/restart.py +307 -0
  521. claude_mpm/services/core/interfaces/service.py +405 -0
  522. claude_mpm/services/core/interfaces/stability.py +260 -0
  523. claude_mpm/services/core/interfaces.py +81 -0
  524. claude_mpm/services/core/memory_manager.py +682 -0
  525. claude_mpm/services/core/models/__init__.py +70 -0
  526. claude_mpm/services/core/models/agent_config.py +384 -0
  527. claude_mpm/services/core/models/health.py +162 -0
  528. claude_mpm/services/core/models/process.py +239 -0
  529. claude_mpm/services/core/models/restart.py +302 -0
  530. claude_mpm/services/core/models/stability.py +264 -0
  531. claude_mpm/services/core/models/toolchain.py +306 -0
  532. claude_mpm/services/core/path_resolver.py +517 -0
  533. claude_mpm/services/core/service_container.py +520 -0
  534. claude_mpm/services/core/service_interfaces.py +436 -0
  535. claude_mpm/services/diagnostics/__init__.py +18 -0
  536. claude_mpm/services/diagnostics/checks/__init__.py +38 -0
  537. claude_mpm/services/diagnostics/checks/agent_check.py +370 -0
  538. claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
  539. claude_mpm/services/diagnostics/checks/base_check.py +60 -0
  540. claude_mpm/services/diagnostics/checks/claude_code_check.py +270 -0
  541. claude_mpm/services/diagnostics/checks/common_issues_check.py +363 -0
  542. claude_mpm/services/diagnostics/checks/configuration_check.py +306 -0
  543. claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
  544. claude_mpm/services/diagnostics/checks/installation_check.py +520 -0
  545. claude_mpm/services/diagnostics/checks/instructions_check.py +415 -0
  546. claude_mpm/services/diagnostics/checks/mcp_check.py +330 -0
  547. claude_mpm/services/diagnostics/checks/mcp_services_check.py +1058 -0
  548. claude_mpm/services/diagnostics/checks/monitor_check.py +281 -0
  549. claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
  550. claude_mpm/services/diagnostics/checks/startup_log_check.py +319 -0
  551. claude_mpm/services/diagnostics/diagnostic_runner.py +286 -0
  552. claude_mpm/services/diagnostics/doctor_reporter.py +578 -0
  553. claude_mpm/services/diagnostics/models.py +138 -0
  554. claude_mpm/services/event_aggregator.py +582 -0
  555. claude_mpm/services/event_bus/__init__.py +18 -0
  556. claude_mpm/services/event_bus/config.py +186 -0
  557. claude_mpm/services/event_bus/direct_relay.py +312 -0
  558. claude_mpm/services/event_bus/event_bus.py +396 -0
  559. claude_mpm/services/event_bus/relay.py +326 -0
  560. claude_mpm/services/events/__init__.py +44 -0
  561. claude_mpm/services/events/consumers/__init__.py +18 -0
  562. claude_mpm/services/events/consumers/dead_letter.py +306 -0
  563. claude_mpm/services/events/consumers/logging.py +184 -0
  564. claude_mpm/services/events/consumers/metrics.py +241 -0
  565. claude_mpm/services/events/consumers/socketio.py +377 -0
  566. claude_mpm/services/events/core.py +480 -0
  567. claude_mpm/services/events/interfaces.py +214 -0
  568. claude_mpm/services/events/producers/__init__.py +14 -0
  569. claude_mpm/services/events/producers/hook.py +269 -0
  570. claude_mpm/services/events/producers/system.py +329 -0
  571. claude_mpm/services/exceptions.py +433 -353
  572. claude_mpm/services/framework_claude_md_generator/__init__.py +81 -80
  573. claude_mpm/services/framework_claude_md_generator/content_assembler.py +74 -67
  574. claude_mpm/services/framework_claude_md_generator/content_validator.py +66 -62
  575. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +82 -60
  576. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +36 -37
  577. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +41 -40
  578. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +15 -15
  579. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +5 -4
  580. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  581. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  582. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  583. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  584. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +5 -4
  585. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  586. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  587. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +26 -30
  588. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +6 -5
  589. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  590. claude_mpm/services/framework_claude_md_generator/version_manager.py +31 -30
  591. claude_mpm/services/git/__init__.py +21 -0
  592. claude_mpm/services/git/git_operations_service.py +579 -0
  593. claude_mpm/services/github/__init__.py +21 -0
  594. claude_mpm/services/github/github_cli_service.py +397 -0
  595. claude_mpm/services/hook_installer_service.py +506 -0
  596. claude_mpm/services/hook_service.py +159 -111
  597. claude_mpm/services/infrastructure/__init__.py +52 -0
  598. claude_mpm/services/infrastructure/context_preservation.py +569 -0
  599. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  600. claude_mpm/services/infrastructure/logging.py +209 -0
  601. claude_mpm/services/infrastructure/monitoring/__init__.py +39 -0
  602. claude_mpm/services/infrastructure/monitoring/aggregator.py +432 -0
  603. claude_mpm/services/infrastructure/monitoring/base.py +122 -0
  604. claude_mpm/services/infrastructure/monitoring/legacy.py +203 -0
  605. claude_mpm/services/infrastructure/monitoring/network.py +219 -0
  606. claude_mpm/services/infrastructure/monitoring/process.py +343 -0
  607. claude_mpm/services/infrastructure/monitoring/resources.py +244 -0
  608. claude_mpm/services/infrastructure/monitoring/service.py +368 -0
  609. claude_mpm/services/infrastructure/monitoring.py +71 -0
  610. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  611. claude_mpm/services/instructions/__init__.py +9 -0
  612. claude_mpm/services/instructions/instruction_cache_service.py +374 -0
  613. claude_mpm/services/local_ops/__init__.py +155 -0
  614. claude_mpm/services/local_ops/crash_detector.py +257 -0
  615. claude_mpm/services/local_ops/health_checks/__init__.py +26 -0
  616. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  617. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  618. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  619. claude_mpm/services/local_ops/health_manager.py +427 -0
  620. claude_mpm/services/local_ops/log_monitor.py +396 -0
  621. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  622. claude_mpm/services/local_ops/process_manager.py +595 -0
  623. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  624. claude_mpm/services/local_ops/restart_manager.py +401 -0
  625. claude_mpm/services/local_ops/restart_policy.py +387 -0
  626. claude_mpm/services/local_ops/state_manager.py +372 -0
  627. claude_mpm/services/local_ops/unified_manager.py +600 -0
  628. claude_mpm/services/mcp_config_manager.py +1542 -0
  629. claude_mpm/services/mcp_service_verifier.py +732 -0
  630. claude_mpm/services/memory/__init__.py +19 -0
  631. claude_mpm/services/{memory_builder.py → memory/builder.py} +465 -373
  632. claude_mpm/services/memory/cache/__init__.py +14 -0
  633. claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +237 -200
  634. claude_mpm/services/memory/cache/simple_cache.py +331 -0
  635. claude_mpm/services/memory/failure_tracker.py +578 -0
  636. claude_mpm/services/memory/indexed_memory.py +648 -0
  637. claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +272 -243
  638. claude_mpm/services/memory/router.py +951 -0
  639. claude_mpm/services/memory_hook_service.py +470 -0
  640. claude_mpm/services/model/__init__.py +147 -0
  641. claude_mpm/services/model/base_provider.py +365 -0
  642. claude_mpm/services/model/claude_provider.py +412 -0
  643. claude_mpm/services/model/model_router.py +452 -0
  644. claude_mpm/services/model/ollama_provider.py +415 -0
  645. claude_mpm/services/monitor/__init__.py +20 -0
  646. claude_mpm/services/monitor/daemon.py +698 -0
  647. claude_mpm/services/monitor/daemon_manager.py +1076 -0
  648. claude_mpm/services/monitor/event_emitter.py +350 -0
  649. claude_mpm/services/monitor/handlers/__init__.py +21 -0
  650. claude_mpm/services/monitor/handlers/code_analysis.py +332 -0
  651. claude_mpm/services/monitor/handlers/dashboard.py +299 -0
  652. claude_mpm/services/monitor/handlers/file.py +264 -0
  653. claude_mpm/services/monitor/handlers/hooks.py +512 -0
  654. claude_mpm/services/monitor/management/__init__.py +18 -0
  655. claude_mpm/services/monitor/management/health.py +124 -0
  656. claude_mpm/services/monitor/management/lifecycle.py +730 -0
  657. claude_mpm/services/monitor/server.py +1493 -0
  658. claude_mpm/services/monitor_build_service.py +349 -0
  659. claude_mpm/services/native_agent_converter.py +356 -0
  660. claude_mpm/services/orphan_detection.py +786 -0
  661. claude_mpm/services/pm_skills_deployer.py +707 -0
  662. claude_mpm/services/port_manager.py +597 -0
  663. claude_mpm/services/pr/__init__.py +14 -0
  664. claude_mpm/services/pr/pr_template_service.py +329 -0
  665. claude_mpm/services/profile_manager.py +337 -0
  666. claude_mpm/services/project/__init__.py +44 -0
  667. claude_mpm/services/{project_analyzer.py → project/analyzer.py} +541 -291
  668. claude_mpm/services/project/analyzer_v2.py +566 -0
  669. claude_mpm/services/project/architecture_analyzer.py +461 -0
  670. claude_mpm/services/project/archive_manager.py +1045 -0
  671. claude_mpm/services/project/dependency_analyzer.py +462 -0
  672. claude_mpm/services/project/detection_strategies.py +719 -0
  673. claude_mpm/services/project/documentation_manager.py +554 -0
  674. claude_mpm/services/project/enhanced_analyzer.py +572 -0
  675. claude_mpm/services/project/language_analyzer.py +265 -0
  676. claude_mpm/services/project/metrics_collector.py +407 -0
  677. claude_mpm/services/project/project_organizer.py +1009 -0
  678. claude_mpm/services/project/registry.py +636 -0
  679. claude_mpm/services/project/toolchain_analyzer.py +583 -0
  680. claude_mpm/services/project_port_allocator.py +596 -0
  681. claude_mpm/services/recovery_manager.py +293 -240
  682. claude_mpm/services/response_tracker.py +267 -0
  683. claude_mpm/services/runner_configuration_service.py +605 -0
  684. claude_mpm/services/self_upgrade_service.py +608 -0
  685. claude_mpm/services/session_management_service.py +314 -0
  686. claude_mpm/services/session_manager.py +380 -0
  687. claude_mpm/services/shared/__init__.py +21 -0
  688. claude_mpm/services/shared/async_service_base.py +216 -0
  689. claude_mpm/services/shared/config_service_base.py +301 -0
  690. claude_mpm/services/shared/lifecycle_service_base.py +308 -0
  691. claude_mpm/services/shared/manager_base.py +315 -0
  692. claude_mpm/services/shared/service_factory.py +309 -0
  693. claude_mpm/services/skills/__init__.py +21 -0
  694. claude_mpm/services/skills/git_skill_source_manager.py +1324 -0
  695. claude_mpm/services/skills/selective_skill_deployer.py +744 -0
  696. claude_mpm/services/skills/skill_discovery_service.py +568 -0
  697. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  698. claude_mpm/services/skills_config.py +547 -0
  699. claude_mpm/services/skills_deployer.py +1168 -0
  700. claude_mpm/services/socketio/__init__.py +25 -0
  701. claude_mpm/services/socketio/client_proxy.py +229 -0
  702. claude_mpm/services/socketio/dashboard_server.py +362 -0
  703. claude_mpm/services/socketio/event_normalizer.py +798 -0
  704. claude_mpm/services/socketio/handlers/__init__.py +30 -0
  705. claude_mpm/services/socketio/handlers/base.py +136 -0
  706. claude_mpm/services/socketio/handlers/code_analysis.py +682 -0
  707. claude_mpm/services/socketio/handlers/connection.py +643 -0
  708. claude_mpm/services/socketio/handlers/connection_handler.py +333 -0
  709. claude_mpm/services/socketio/handlers/file.py +263 -0
  710. claude_mpm/services/socketio/handlers/git.py +962 -0
  711. claude_mpm/services/socketio/handlers/hook.py +211 -0
  712. claude_mpm/services/socketio/handlers/memory.py +26 -0
  713. claude_mpm/services/socketio/handlers/project.py +24 -0
  714. claude_mpm/services/socketio/handlers/registry.py +214 -0
  715. claude_mpm/services/socketio/migration_utils.py +343 -0
  716. claude_mpm/services/socketio/monitor_client.py +364 -0
  717. claude_mpm/services/socketio/server/__init__.py +18 -0
  718. claude_mpm/services/socketio/server/broadcaster.py +569 -0
  719. claude_mpm/services/socketio/server/connection_manager.py +579 -0
  720. claude_mpm/services/socketio/server/core.py +1079 -0
  721. claude_mpm/services/socketio/server/eventbus_integration.py +245 -0
  722. claude_mpm/services/socketio/server/main.py +501 -0
  723. claude_mpm/services/socketio_client_manager.py +173 -143
  724. claude_mpm/services/socketio_server.py +38 -1657
  725. claude_mpm/services/subprocess_launcher_service.py +322 -0
  726. claude_mpm/services/system_instructions_service.py +270 -0
  727. claude_mpm/services/ticket_manager.py +25 -209
  728. claude_mpm/services/ticket_services/__init__.py +26 -0
  729. claude_mpm/services/ticket_services/crud_service.py +328 -0
  730. claude_mpm/services/ticket_services/formatter_service.py +290 -0
  731. claude_mpm/services/ticket_services/search_service.py +324 -0
  732. claude_mpm/services/ticket_services/validation_service.py +303 -0
  733. claude_mpm/services/ticket_services/workflow_service.py +244 -0
  734. claude_mpm/services/unified/__init__.py +65 -0
  735. claude_mpm/services/unified/analyzer_strategies/__init__.py +44 -0
  736. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +518 -0
  737. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +680 -0
  738. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +900 -0
  739. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +745 -0
  740. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +733 -0
  741. claude_mpm/services/unified/config_strategies/__init__.py +175 -0
  742. claude_mpm/services/unified/config_strategies/config_schema.py +731 -0
  743. claude_mpm/services/unified/config_strategies/context_strategy.py +747 -0
  744. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1005 -0
  745. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +881 -0
  746. claude_mpm/services/unified/config_strategies/unified_config_service.py +823 -0
  747. claude_mpm/services/unified/config_strategies/validation_strategy.py +1148 -0
  748. claude_mpm/services/unified/deployment_strategies/__init__.py +97 -0
  749. claude_mpm/services/unified/deployment_strategies/base.py +553 -0
  750. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +573 -0
  751. claude_mpm/services/unified/deployment_strategies/local.py +607 -0
  752. claude_mpm/services/unified/deployment_strategies/utils.py +667 -0
  753. claude_mpm/services/unified/deployment_strategies/vercel.py +471 -0
  754. claude_mpm/services/unified/interfaces.py +475 -0
  755. claude_mpm/services/unified/migration.py +509 -0
  756. claude_mpm/services/unified/strategies.py +534 -0
  757. claude_mpm/services/unified/unified_analyzer.py +542 -0
  758. claude_mpm/services/unified/unified_config.py +691 -0
  759. claude_mpm/services/unified/unified_deployment.py +466 -0
  760. claude_mpm/services/utility_service.py +280 -0
  761. claude_mpm/services/version_control/__init__.py +34 -37
  762. claude_mpm/services/version_control/branch_strategy.py +26 -17
  763. claude_mpm/services/version_control/conflict_resolution.py +52 -36
  764. claude_mpm/services/version_control/git_operations.py +183 -49
  765. claude_mpm/services/version_control/semantic_versioning.py +172 -61
  766. claude_mpm/services/version_control/version_parser.py +546 -0
  767. claude_mpm/services/version_service.py +379 -0
  768. claude_mpm/services/visualization/__init__.py +15 -0
  769. claude_mpm/services/visualization/mermaid_generator.py +937 -0
  770. claude_mpm/skills/__init__.py +42 -0
  771. claude_mpm/skills/agent_skills_injector.py +324 -0
  772. claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
  773. claude_mpm/skills/bundled/__init__.py +6 -0
  774. claude_mpm/skills/bundled/api-documentation.md +393 -0
  775. claude_mpm/skills/bundled/async-testing.md +571 -0
  776. claude_mpm/skills/bundled/code-review.md +143 -0
  777. claude_mpm/skills/bundled/database-migration.md +199 -0
  778. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  779. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  780. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  781. claude_mpm/skills/bundled/git-workflow.md +414 -0
  782. claude_mpm/skills/bundled/imagemagick.md +204 -0
  783. claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
  784. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  785. claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
  786. claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
  787. claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
  788. claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
  789. claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
  790. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  791. claude_mpm/skills/bundled/pdf.md +141 -0
  792. claude_mpm/skills/bundled/performance-profiling.md +573 -0
  793. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  794. claude_mpm/skills/bundled/security-scanning.md +439 -0
  795. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  796. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  797. claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
  798. claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
  799. claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
  800. claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
  801. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  802. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  803. claude_mpm/skills/bundled/xlsx.md +157 -0
  804. claude_mpm/skills/registry.py +286 -0
  805. claude_mpm/skills/skill_manager.py +405 -0
  806. claude_mpm/skills/skills_registry.py +347 -0
  807. claude_mpm/skills/skills_service.py +739 -0
  808. claude_mpm/storage/__init__.py +9 -0
  809. claude_mpm/storage/state_storage.py +546 -0
  810. claude_mpm/templates/.pre-commit-config.yaml +112 -0
  811. claude_mpm/templates/questions/__init__.py +38 -0
  812. claude_mpm/templates/questions/base.py +193 -0
  813. claude_mpm/templates/questions/pr_strategy.py +311 -0
  814. claude_mpm/templates/questions/project_init.py +385 -0
  815. claude_mpm/templates/questions/ticket_mgmt.py +394 -0
  816. claude_mpm/ticket_wrapper.py +2 -2
  817. claude_mpm/tools/__init__.py +10 -0
  818. claude_mpm/tools/__main__.py +208 -0
  819. claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
  820. claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
  821. claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
  822. claude_mpm/tools/code_tree_analyzer/core.py +380 -0
  823. claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
  824. claude_mpm/tools/code_tree_analyzer/events.py +168 -0
  825. claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
  826. claude_mpm/tools/code_tree_analyzer/models.py +39 -0
  827. claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
  828. claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
  829. claude_mpm/tools/code_tree_builder.py +631 -0
  830. claude_mpm/tools/code_tree_events.py +420 -0
  831. claude_mpm/tools/socketio_debug.py +671 -0
  832. claude_mpm/utils/__init__.py +8 -8
  833. claude_mpm/utils/agent_dependency_loader.py +1090 -0
  834. claude_mpm/utils/agent_filters.py +261 -0
  835. claude_mpm/utils/common.py +544 -0
  836. claude_mpm/utils/config_manager.py +168 -126
  837. claude_mpm/utils/console.py +11 -0
  838. claude_mpm/utils/database_connector.py +298 -0
  839. claude_mpm/utils/dependency_cache.py +373 -0
  840. claude_mpm/utils/dependency_manager.py +60 -59
  841. claude_mpm/utils/dependency_strategies.py +381 -0
  842. claude_mpm/utils/display_helper.py +260 -0
  843. claude_mpm/utils/environment_context.py +313 -0
  844. claude_mpm/utils/error_handler.py +78 -66
  845. claude_mpm/utils/file_utils.py +305 -0
  846. claude_mpm/utils/framework_detection.py +12 -11
  847. claude_mpm/utils/git_analyzer.py +407 -0
  848. claude_mpm/utils/gitignore.py +244 -0
  849. claude_mpm/utils/import_migration_example.py +12 -60
  850. claude_mpm/utils/imports.py +48 -45
  851. claude_mpm/utils/log_cleanup.py +627 -0
  852. claude_mpm/utils/migration.py +372 -0
  853. claude_mpm/utils/path_operations.py +110 -104
  854. claude_mpm/utils/progress.py +387 -0
  855. claude_mpm/utils/robust_installer.py +823 -0
  856. claude_mpm/utils/session_logging.py +121 -0
  857. claude_mpm/utils/structured_questions.py +619 -0
  858. claude_mpm/utils/subprocess_utils.py +343 -0
  859. claude_mpm/validation/__init__.py +1 -1
  860. claude_mpm/validation/agent_validator.py +214 -108
  861. claude_mpm/validation/frontmatter_validator.py +252 -0
  862. claude_mpm-5.4.55.dist-info/METADATA +999 -0
  863. claude_mpm-5.4.55.dist-info/RECORD +868 -0
  864. {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/entry_points.txt +1 -3
  865. claude_mpm-5.4.55.dist-info/licenses/LICENSE +94 -0
  866. claude_mpm-5.4.55.dist-info/licenses/LICENSE-FAQ.md +153 -0
  867. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -88
  868. claude_mpm/agents/INSTRUCTIONS.md +0 -352
  869. claude_mpm/agents/backups/INSTRUCTIONS.md +0 -352
  870. claude_mpm/agents/base_agent_loader.py +0 -529
  871. claude_mpm/agents/schema/agent_schema.json +0 -314
  872. claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
  873. claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +0 -46
  874. claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +0 -45
  875. claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +0 -49
  876. claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +0 -46
  877. claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +0 -45
  878. claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +0 -49
  879. claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +0 -46
  880. claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +0 -46
  881. claude_mpm/agents/templates/data_engineer.json +0 -110
  882. claude_mpm/agents/templates/documentation.json +0 -109
  883. claude_mpm/agents/templates/engineer.json +0 -113
  884. claude_mpm/agents/templates/ops.json +0 -109
  885. claude_mpm/agents/templates/pm.json +0 -25
  886. claude_mpm/agents/templates/qa.json +0 -111
  887. claude_mpm/agents/templates/research.json +0 -65
  888. claude_mpm/agents/templates/security.json +0 -113
  889. claude_mpm/agents/templates/test_integration.json +0 -112
  890. claude_mpm/agents/templates/version_control.json +0 -107
  891. claude_mpm/cli/commands/ui.py +0 -57
  892. claude_mpm/core/simple_runner.py +0 -1046
  893. claude_mpm/dashboard/open_dashboard.py +0 -34
  894. claude_mpm/deployment_paths.py +0 -261
  895. claude_mpm/hooks/builtin/__init__.py +0 -1
  896. claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
  897. claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
  898. claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
  899. claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
  900. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
  901. claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
  902. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
  903. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
  904. claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
  905. claude_mpm/orchestration/__init__.py +0 -6
  906. claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
  907. claude_mpm/orchestration/archive/factory.py +0 -215
  908. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
  909. claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
  910. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
  911. claude_mpm/orchestration/archive/orchestrator.py +0 -501
  912. claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
  913. claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
  914. claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
  915. claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
  916. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
  917. claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
  918. claude_mpm/schemas/workflow_validator.py +0 -411
  919. claude_mpm/services/agent_deployment.py +0 -1534
  920. claude_mpm/services/agent_lifecycle_manager.py +0 -1169
  921. claude_mpm/services/agent_memory_manager.py +0 -1415
  922. claude_mpm/services/agent_registry.py +0 -676
  923. claude_mpm/services/deployed_agent_discovery.py +0 -226
  924. claude_mpm/services/framework_agent_loader.py +0 -337
  925. claude_mpm/services/framework_claude_md_generator.py +0 -621
  926. claude_mpm/services/health_monitor.py +0 -892
  927. claude_mpm/services/memory_router.py +0 -538
  928. claude_mpm/services/parent_directory_manager/__init__.py +0 -577
  929. claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
  930. claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
  931. claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
  932. claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
  933. claude_mpm/services/parent_directory_manager/operations.py +0 -186
  934. claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
  935. claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
  936. claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
  937. claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
  938. claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
  939. claude_mpm/services/standalone_socketio_server.py +0 -1300
  940. claude_mpm/services/ticket_manager_di.py +0 -318
  941. claude_mpm/services/ticketing_service_original.py +0 -508
  942. claude_mpm/ui/__init__.py +0 -1
  943. claude_mpm/ui/rich_terminal_ui.py +0 -295
  944. claude_mpm/ui/terminal_ui.py +0 -328
  945. claude_mpm/utils/paths.py +0 -289
  946. claude_mpm-3.4.10.dist-info/METADATA +0 -183
  947. claude_mpm-3.4.10.dist-info/RECORD +0 -201
  948. claude_mpm-3.4.10.dist-info/licenses/LICENSE +0 -21
  949. {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/WHEEL +0 -0
  950. {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1168 @@
1
+ """Skills Deployer Service - Deploy Claude Code skills from GitHub.
2
+
3
+ WHY: Claude Code loads skills at STARTUP ONLY from ~/.claude/skills/ directory.
4
+ This service manages downloading skills from external GitHub repositories and
5
+ deploying them to Claude Code's skills directory with automatic restart warnings.
6
+
7
+ DESIGN DECISIONS:
8
+ - Downloads from https://github.com/bobmatnyc/claude-mpm-skills by default
9
+ - Deploys to ~/.claude/skills/ (Claude Code's directory), NOT project directory
10
+ - Integrates with ToolchainAnalyzer for automatic language detection
11
+ - Handles Claude Code restart requirement (skills only load at startup)
12
+ - Provides filtering by toolchain and categories
13
+ - Graceful error handling with actionable messages
14
+
15
+ ARCHITECTURE:
16
+ 1. GitHub Download: Fetch ZIP archive from repository
17
+ 2. Manifest Parsing: Read skill metadata from manifest.json
18
+ 3. Filtering: Apply toolchain and category filters
19
+ 4. Deployment: Copy skills to ~/.claude/skills/
20
+ 5. Restart Detection: Warn if Claude Code is running
21
+ 6. Cleanup: Remove temporary files
22
+
23
+ References:
24
+ - Research: docs/research/skills-research.md
25
+ - GitHub Repo: https://github.com/bobmatnyc/claude-mpm-skills
26
+ """
27
+
28
+ import json
29
+ import platform
30
+ import shutil
31
+ import subprocess
32
+ from pathlib import Path
33
+ from typing import Any, Dict, List, Optional
34
+
35
+ from claude_mpm.core.mixins import LoggerMixin
36
+ from claude_mpm.services.skills_config import SkillsConfig
37
+
38
+
39
+ class SkillsDeployerService(LoggerMixin):
40
+ """Deploy Claude Code skills from external GitHub repositories.
41
+
42
+ This service:
43
+ - Downloads skills from GitHub repositories
44
+ - Deploys to ~/.claude/skills/ directory
45
+ - Filters by toolchain (python, javascript, rust, etc.)
46
+ - Filters by categories (testing, debugging, web, etc.)
47
+ - Detects Claude Code process and warns about restart requirement
48
+ - Provides deployment summaries and error handling
49
+
50
+ Example:
51
+ >>> deployer = SkillsDeployerService()
52
+ >>> result = deployer.deploy_skills(toolchain=['python'], categories=['testing'])
53
+ >>> print(f"Deployed {result['deployed_count']} skills")
54
+ >>> print(f"Restart Claude Code: {result['restart_required']}")
55
+ """
56
+
57
+ DEFAULT_REPO_URL = "https://github.com/bobmatnyc/claude-mpm-skills"
58
+ CLAUDE_SKILLS_DIR = Path.home() / ".claude" / "skills"
59
+
60
+ def __init__(
61
+ self,
62
+ repo_url: Optional[str] = None,
63
+ toolchain_analyzer: Optional[any] = None,
64
+ ):
65
+ """Initialize Skills Deployer Service.
66
+
67
+ Args:
68
+ repo_url: GitHub repository URL (default: bobmatnyc/claude-mpm-skills)
69
+ toolchain_analyzer: Optional ToolchainAnalyzer for auto-detection
70
+ """
71
+ super().__init__()
72
+ self.repo_url = repo_url or self.DEFAULT_REPO_URL
73
+ self.toolchain_analyzer = toolchain_analyzer
74
+ self.skills_config = SkillsConfig()
75
+
76
+ # Ensure Claude skills directory exists
77
+ self.CLAUDE_SKILLS_DIR.mkdir(parents=True, exist_ok=True)
78
+
79
+ def deploy_skills(
80
+ self,
81
+ collection: Optional[str] = None,
82
+ toolchain: Optional[List[str]] = None,
83
+ categories: Optional[List[str]] = None,
84
+ force: bool = False,
85
+ selective: bool = True,
86
+ project_root: Optional[Path] = None,
87
+ ) -> Dict:
88
+ """Deploy skills from GitHub repository.
89
+
90
+ This is the main entry point for skill deployment. It:
91
+ 1. Downloads skills from GitHub collection
92
+ 2. Parses manifest for metadata
93
+ 3. Filters by toolchain and categories
94
+ 4. (If selective=True) Filters to only agent-referenced skills
95
+ 5. Deploys to ~/.claude/skills/
96
+ 6. Warns about Claude Code restart
97
+
98
+ Args:
99
+ collection: Collection name to deploy from (default: uses default collection)
100
+ toolchain: Filter by toolchain (e.g., ['python', 'javascript'])
101
+ categories: Filter by categories (e.g., ['testing', 'debugging'])
102
+ force: Overwrite existing skills
103
+ selective: If True, only deploy skills referenced by agents (default)
104
+ project_root: Project root directory (for finding agents, auto-detected if None)
105
+
106
+ Returns:
107
+ Dict containing:
108
+ - deployed_count: Number of skills deployed
109
+ - skipped_count: Number of skills skipped
110
+ - errors: List of error messages
111
+ - deployed_skills: List of deployed skill names
112
+ - restart_required: True if Claude Code needs restart
113
+ - restart_instructions: Message about restarting
114
+ - collection: Collection name used for deployment
115
+ - selective_mode: True if selective deployment was used
116
+ - total_available: Total skills available before filtering
117
+
118
+ Example:
119
+ >>> result = deployer.deploy_skills(collection="obra-superpowers")
120
+ >>> result = deployer.deploy_skills(toolchain=['python']) # Uses default
121
+ >>> # Deploy all skills (not just agent-referenced)
122
+ >>> result = deployer.deploy_skills(selective=False)
123
+ >>> if result['restart_required']:
124
+ >>> print(result['restart_instructions'])
125
+ """
126
+ # Determine which collection to use
127
+ collection_name = collection or self.skills_config.get_default_collection()
128
+
129
+ self.logger.info(f"Deploying skills from collection '{collection_name}'")
130
+
131
+ # Step 1: Download skills from GitHub collection
132
+ try:
133
+ skills_data = self._download_from_github(collection_name)
134
+ except Exception as e:
135
+ self.logger.error(f"Failed to download skills: {e}")
136
+ return {
137
+ "deployed_count": 0,
138
+ "skipped_count": 0,
139
+ "errors": [f"Download failed: {e}"],
140
+ "deployed_skills": [],
141
+ "restart_required": False,
142
+ "restart_instructions": "",
143
+ "collection": collection_name,
144
+ }
145
+
146
+ # Step 2: Parse manifest and flatten skills
147
+ manifest = skills_data.get("manifest", {})
148
+ try:
149
+ skills = self._flatten_manifest_skills(manifest)
150
+ except ValueError as e:
151
+ self.logger.error(f"Invalid manifest structure: {e}")
152
+ return {
153
+ "deployed_count": 0,
154
+ "skipped_count": 0,
155
+ "errors": [f"Invalid manifest: {e}"],
156
+ "deployed_skills": [],
157
+ "restart_required": False,
158
+ "restart_instructions": "",
159
+ "collection": collection_name,
160
+ }
161
+
162
+ self.logger.info(f"Found {len(skills)} skills in repository")
163
+
164
+ # Step 3: Filter skills by toolchain and categories
165
+ filtered_skills = self._filter_skills(skills, toolchain, categories)
166
+
167
+ self.logger.info(
168
+ f"After filtering: {len(filtered_skills)} skills to deploy"
169
+ f" (toolchain={toolchain}, categories={categories})"
170
+ )
171
+
172
+ # Step 3.5: Apply selective filtering (only agent-referenced skills)
173
+ total_available = len(filtered_skills)
174
+ if selective:
175
+ # Auto-detect project root if not provided
176
+ if project_root is None:
177
+ # Try to find project root by looking for .claude-mpm directory
178
+ # Start from current directory and walk up
179
+ current = Path.cwd()
180
+ while current != current.parent:
181
+ if (current / ".claude-mpm").exists():
182
+ project_root = current
183
+ break
184
+ current = current.parent
185
+
186
+ # Read skills from configuration.yaml instead of agent frontmatter
187
+ if project_root:
188
+ config_path = Path(project_root) / ".claude-mpm" / "configuration.yaml"
189
+ else:
190
+ # Fallback to current directory's configuration
191
+ config_path = Path.cwd() / ".claude-mpm" / "configuration.yaml"
192
+
193
+ from claude_mpm.services.skills.selective_skill_deployer import (
194
+ get_required_skills_from_agents,
195
+ get_skills_to_deploy,
196
+ save_agent_skills_to_config,
197
+ )
198
+
199
+ # Check if agent_referenced is empty and needs to be populated
200
+ required_skill_names, source = get_skills_to_deploy(config_path)
201
+
202
+ if not required_skill_names and project_root:
203
+ # agent_referenced is empty, scan deployed agents to populate it
204
+ agents_dir = Path(project_root) / ".claude" / "agents"
205
+ if agents_dir.exists():
206
+ self.logger.info(
207
+ "agent_referenced is empty in configuration.yaml, scanning deployed agents..."
208
+ )
209
+ agent_skills = get_required_skills_from_agents(agents_dir)
210
+ if agent_skills:
211
+ save_agent_skills_to_config(list(agent_skills), config_path)
212
+ self.logger.info(
213
+ f"Populated agent_referenced with {len(agent_skills)} skills from deployed agents"
214
+ )
215
+ # Re-read configuration after update
216
+ required_skill_names, source = get_skills_to_deploy(config_path)
217
+ else:
218
+ self.logger.warning(
219
+ "No skills found in deployed agents - configuration.yaml remains empty"
220
+ )
221
+ else:
222
+ self.logger.warning(
223
+ f"Agents directory not found at {agents_dir} - cannot scan for skills"
224
+ )
225
+
226
+ if required_skill_names:
227
+ # Convert required_skill_names to a set for O(1) lookup
228
+ required_set = set(required_skill_names)
229
+
230
+ # Filter to only required skills
231
+ # Match on: 'name', 'skill_id', or normalized 'source_path'
232
+ # source_path example: "universal/web/api-design-patterns/SKILL.md"
233
+ # normalized: "universal-web-api-design-patterns"
234
+ def skill_matches_requirement(skill):
235
+ # Check basic name and skill_id
236
+ if skill.get("name") in required_set:
237
+ return True
238
+ if skill.get("skill_id") in required_set:
239
+ return True
240
+
241
+ # Check normalized source_path
242
+ source_path = skill.get("source_path", "")
243
+ if source_path:
244
+ # Remove /SKILL.md suffix and replace / with -
245
+ normalized = source_path.replace("/SKILL.md", "").replace(
246
+ "/", "-"
247
+ )
248
+ if normalized in required_set:
249
+ return True
250
+
251
+ return False
252
+
253
+ filtered_skills = [
254
+ s for s in filtered_skills if skill_matches_requirement(s)
255
+ ]
256
+
257
+ self.logger.info(
258
+ f"Selective deployment: {len(filtered_skills)}/{total_available} skills "
259
+ f"(source: {source})"
260
+ )
261
+ else:
262
+ self.logger.warning(
263
+ f"No skills found in configuration at {config_path}. "
264
+ f"Deploying all {total_available} skills."
265
+ )
266
+ else:
267
+ self.logger.info(
268
+ f"Selective mode disabled: deploying all {total_available} skills"
269
+ )
270
+
271
+ # Step 4: Deploy skills
272
+ deployed = []
273
+ skipped = []
274
+ errors = []
275
+
276
+ # Extract normalized skill names for cleanup (needed regardless of deployment outcome)
277
+ # Must match the names used during deployment (normalized from source_path)
278
+ filtered_skills_names = []
279
+ for skill in filtered_skills:
280
+ if isinstance(skill, dict) and "name" in skill:
281
+ source_path = skill.get("source_path", "")
282
+ if source_path:
283
+ # Normalize: "universal/web/api-design-patterns/SKILL.md" -> "universal-web-api-design-patterns"
284
+ normalized = source_path.replace("/SKILL.md", "").replace("/", "-")
285
+ filtered_skills_names.append(normalized)
286
+ else:
287
+ # Fallback to skill name
288
+ filtered_skills_names.append(skill["name"])
289
+
290
+ for skill in filtered_skills:
291
+ try:
292
+ # Validate skill is a dictionary
293
+ if not isinstance(skill, dict):
294
+ self.logger.error(f"Invalid skill format: {skill}")
295
+ errors.append(f"Invalid skill format: {skill}")
296
+ continue
297
+
298
+ result = self._deploy_skill(
299
+ skill, skills_data["temp_dir"], collection_name, force=force
300
+ )
301
+ if result["deployed"]:
302
+ # Use normalized name for reporting
303
+ source_path = skill.get("source_path", "")
304
+ if source_path:
305
+ normalized = source_path.replace("/SKILL.md", "").replace(
306
+ "/", "-"
307
+ )
308
+ deployed.append(normalized)
309
+ else:
310
+ deployed.append(skill["name"])
311
+ elif result["skipped"]:
312
+ # Use normalized name for reporting
313
+ source_path = skill.get("source_path", "")
314
+ if source_path:
315
+ normalized = source_path.replace("/SKILL.md", "").replace(
316
+ "/", "-"
317
+ )
318
+ skipped.append(normalized)
319
+ else:
320
+ skipped.append(skill["name"])
321
+ if result["error"]:
322
+ errors.append(result["error"])
323
+ except Exception as e:
324
+ skill_name = (
325
+ skill.get("name", "unknown")
326
+ if isinstance(skill, dict)
327
+ else "unknown"
328
+ )
329
+ self.logger.error(f"Failed to deploy {skill_name}: {e}")
330
+ errors.append(f"{skill_name}: {e}")
331
+
332
+ # Step 5: Cleanup orphaned skills (always run in selective mode)
333
+ cleanup_result = {"removed_count": 0, "removed_skills": []}
334
+ if selective:
335
+ # Get the set of skills that should remain deployed
336
+ # This is the union of what we just deployed and what was already there
337
+ try:
338
+ from claude_mpm.services.skills.selective_skill_deployer import (
339
+ cleanup_orphan_skills,
340
+ )
341
+
342
+ # Cleanup orphaned skills not referenced by agents
343
+ # This runs even if nothing new was deployed to remove stale skills
344
+ cleanup_result = cleanup_orphan_skills(
345
+ self.CLAUDE_SKILLS_DIR, set(filtered_skills_names)
346
+ )
347
+
348
+ if cleanup_result["removed_count"] > 0:
349
+ self.logger.info(
350
+ f"Removed {cleanup_result['removed_count']} orphaned skills: "
351
+ f"{', '.join(cleanup_result['removed_skills'])}"
352
+ )
353
+ except Exception as e:
354
+ self.logger.warning(f"Failed to cleanup orphaned skills: {e}")
355
+
356
+ # Step 6: Cleanup temp directory
357
+ self._cleanup(skills_data["temp_dir"])
358
+
359
+ # Step 7: Check if Claude Code restart needed
360
+ restart_required = len(deployed) > 0
361
+ restart_instructions = ""
362
+
363
+ if restart_required:
364
+ claude_running = self._is_claude_code_running()
365
+ if claude_running:
366
+ restart_instructions = (
367
+ "⚠️ Claude Code is currently running.\n"
368
+ "Skills are only loaded at STARTUP.\n"
369
+ "Please restart Claude Code for new skills to be available.\n\n"
370
+ "How to restart Claude Code:\n"
371
+ "1. Close all Claude Code windows\n"
372
+ "2. Quit Claude Code completely (Cmd+Q on Mac, Alt+F4 on Windows)\n"
373
+ "3. Re-launch Claude Code\n"
374
+ )
375
+ else:
376
+ restart_instructions = (
377
+ "✓ Claude Code is not currently running.\n"
378
+ "Skills will be available when you launch Claude Code.\n"
379
+ )
380
+
381
+ self.logger.info(
382
+ f"Deployment complete: {len(deployed)} deployed, "
383
+ f"{len(skipped)} skipped, {len(errors)} errors, "
384
+ f"{cleanup_result['removed_count']} orphaned skills removed"
385
+ )
386
+
387
+ return {
388
+ "deployed_count": len(deployed),
389
+ "skipped_count": len(skipped),
390
+ "errors": errors,
391
+ "deployed_skills": deployed,
392
+ "skipped_skills": skipped,
393
+ "restart_required": restart_required,
394
+ "restart_instructions": restart_instructions,
395
+ "collection": collection_name,
396
+ "selective_mode": selective,
397
+ "total_available": total_available,
398
+ "cleanup": cleanup_result,
399
+ }
400
+
401
+ def list_available_skills(self, collection: Optional[str] = None) -> Dict:
402
+ """List all available skills from GitHub repository.
403
+
404
+ Downloads manifest and returns skill metadata grouped by category
405
+ and toolchain.
406
+
407
+ Args:
408
+ collection: Collection name to list from (default: uses default collection)
409
+
410
+ Returns:
411
+ Dict containing:
412
+ - total_skills: Total number of available skills
413
+ - by_category: Skills grouped by category
414
+ - by_toolchain: Skills grouped by toolchain
415
+ - skills: Full list of skill metadata
416
+ - collection: Collection name used
417
+
418
+ Example:
419
+ >>> result = deployer.list_available_skills(collection="obra-superpowers")
420
+ >>> result = deployer.list_available_skills() # Uses default
421
+ >>> print(f"Available: {result['total_skills']} skills")
422
+ >>> for category, skills in result['by_category'].items():
423
+ >>> print(f"{category}: {len(skills)} skills")
424
+ """
425
+ collection_name = collection or self.skills_config.get_default_collection()
426
+
427
+ try:
428
+ skills_data = self._download_from_github(collection_name)
429
+ manifest = skills_data.get("manifest", {})
430
+
431
+ # Flatten skills from manifest (supports both legacy and new structure)
432
+ try:
433
+ skills = self._flatten_manifest_skills(manifest)
434
+ except ValueError as e:
435
+ self.logger.error(f"Failed to parse manifest: {e}")
436
+ return {
437
+ "total_skills": 0,
438
+ "by_category": {},
439
+ "by_toolchain": {},
440
+ "skills": [],
441
+ "error": str(e),
442
+ }
443
+
444
+ # Group by category
445
+ by_category = {}
446
+ for skill in skills:
447
+ if not isinstance(skill, dict):
448
+ continue
449
+ category = skill.get("category", "uncategorized")
450
+ if category not in by_category:
451
+ by_category[category] = []
452
+ by_category[category].append(skill)
453
+
454
+ # Group by toolchain
455
+ by_toolchain = {}
456
+ for skill in skills:
457
+ if not isinstance(skill, dict):
458
+ continue
459
+ toolchains = skill.get("toolchain", [])
460
+ if isinstance(toolchains, str):
461
+ toolchains = [toolchains]
462
+ elif not isinstance(toolchains, list):
463
+ toolchains = []
464
+
465
+ for toolchain in toolchains:
466
+ if toolchain not in by_toolchain:
467
+ by_toolchain[toolchain] = []
468
+ by_toolchain[toolchain].append(skill)
469
+
470
+ # Cleanup
471
+ self._cleanup(skills_data["temp_dir"])
472
+
473
+ return {
474
+ "total_skills": len(skills),
475
+ "by_category": by_category,
476
+ "by_toolchain": by_toolchain,
477
+ "skills": skills,
478
+ "collection": collection_name,
479
+ }
480
+
481
+ except Exception as e:
482
+ self.logger.error(f"Failed to list available skills: {e}")
483
+ return {
484
+ "total_skills": 0,
485
+ "by_category": {},
486
+ "by_toolchain": {},
487
+ "skills": [],
488
+ "error": str(e),
489
+ "collection": collection_name,
490
+ }
491
+
492
+ def check_deployed_skills(self) -> Dict:
493
+ """Check which skills are currently deployed.
494
+
495
+ Scans ~/.claude/skills/ directory for deployed skills.
496
+
497
+ Returns:
498
+ Dict containing:
499
+ - deployed_count: Number of deployed skills
500
+ - skills: List of deployed skill names with paths
501
+ - claude_skills_dir: Path to Claude skills directory
502
+
503
+ Example:
504
+ >>> result = deployer.check_deployed_skills()
505
+ >>> print(f"Currently deployed: {result['deployed_count']} skills")
506
+ """
507
+ deployed_skills = []
508
+
509
+ if self.CLAUDE_SKILLS_DIR.exists():
510
+ for skill_dir in self.CLAUDE_SKILLS_DIR.iterdir():
511
+ if skill_dir.is_dir() and not skill_dir.name.startswith("."):
512
+ # Check for SKILL.md
513
+ skill_md = skill_dir / "SKILL.md"
514
+ if skill_md.exists():
515
+ deployed_skills.append(
516
+ {"name": skill_dir.name, "path": str(skill_dir)}
517
+ )
518
+
519
+ return {
520
+ "deployed_count": len(deployed_skills),
521
+ "skills": deployed_skills,
522
+ "claude_skills_dir": str(self.CLAUDE_SKILLS_DIR),
523
+ }
524
+
525
+ def remove_skills(self, skill_names: Optional[List[str]] = None) -> Dict:
526
+ """Remove deployed skills.
527
+
528
+ Args:
529
+ skill_names: List of skill names to remove, or None to remove all
530
+
531
+ Returns:
532
+ Dict containing:
533
+ - removed_count: Number of skills removed
534
+ - removed_skills: List of removed skill names
535
+ - errors: List of error messages
536
+
537
+ Example:
538
+ >>> # Remove specific skills
539
+ >>> result = deployer.remove_skills(['test-skill', 'debug-skill'])
540
+ >>> # Remove all skills
541
+ >>> result = deployer.remove_skills()
542
+ """
543
+ removed = []
544
+ errors = []
545
+
546
+ if not self.CLAUDE_SKILLS_DIR.exists():
547
+ return {
548
+ "removed_count": 0,
549
+ "removed_skills": [],
550
+ "errors": ["Claude skills directory does not exist"],
551
+ }
552
+
553
+ # Get all skills if no specific names provided
554
+ if skill_names is None:
555
+ skill_names = [
556
+ d.name
557
+ for d in self.CLAUDE_SKILLS_DIR.iterdir()
558
+ if d.is_dir() and not d.name.startswith(".")
559
+ ]
560
+
561
+ for skill_name in skill_names:
562
+ skill_dir = self.CLAUDE_SKILLS_DIR / skill_name
563
+
564
+ if not skill_dir.exists():
565
+ errors.append(f"Skill not found: {skill_name}")
566
+ continue
567
+
568
+ try:
569
+ # Security: Validate path is within CLAUDE_SKILLS_DIR
570
+ if not self._validate_safe_path(self.CLAUDE_SKILLS_DIR, skill_dir):
571
+ raise ValueError(f"Path traversal attempt detected: {skill_dir}")
572
+
573
+ # Remove skill directory
574
+ if skill_dir.is_symlink():
575
+ self.logger.warning(f"Removing symlink: {skill_dir}")
576
+ skill_dir.unlink()
577
+ else:
578
+ shutil.rmtree(skill_dir)
579
+
580
+ removed.append(skill_name)
581
+ self.logger.info(f"Removed skill: {skill_name}")
582
+
583
+ # Untrack skill from deployment index
584
+ from claude_mpm.services.skills.selective_skill_deployer import (
585
+ untrack_skill,
586
+ )
587
+
588
+ untrack_skill(self.CLAUDE_SKILLS_DIR, skill_name)
589
+
590
+ except Exception as e:
591
+ self.logger.error(f"Failed to remove {skill_name}: {e}")
592
+ errors.append(f"{skill_name}: {e}")
593
+
594
+ return {
595
+ "removed_count": len(removed),
596
+ "removed_skills": removed,
597
+ "errors": errors,
598
+ }
599
+
600
+ def _download_from_github(self, collection_name: str) -> Dict:
601
+ """Download skills repository from GitHub using git clone/pull.
602
+
603
+ Logic:
604
+ 1. Get collection config from SkillsConfig
605
+ 2. Determine target directory: ~/.claude/skills/{collection_name}/
606
+ 3. Check if directory exists:
607
+ - Exists + is git repo → git pull (update)
608
+ - Exists + not git repo → error (manual cleanup needed)
609
+ - Not exists → git clone (first install)
610
+ 4. Parse manifest.json from collection
611
+ 5. Update last_update timestamp in config
612
+ 6. Return skills data
613
+
614
+ Args:
615
+ collection_name: Name of collection to download
616
+
617
+ Returns:
618
+ Dict containing:
619
+ - temp_dir: Path to collection directory (not temp, but kept for compatibility)
620
+ - manifest: Parsed manifest.json
621
+ - repo_dir: Path to repository directory
622
+
623
+ Raises:
624
+ ValueError: If collection not found or disabled
625
+ Exception: If git operations fail
626
+ """
627
+ # Get collection configuration
628
+ collection_config = self.skills_config.get_collection(collection_name)
629
+ if not collection_config:
630
+ raise ValueError(
631
+ f"Collection '{collection_name}' not found. "
632
+ f"Use 'claude-mpm skills collection add' to add it."
633
+ )
634
+
635
+ if not collection_config.get("enabled", True):
636
+ raise ValueError(
637
+ f"Collection '{collection_name}' is disabled. "
638
+ f"Use 'claude-mpm skills collection enable {collection_name}' to enable it."
639
+ )
640
+
641
+ repo_url = collection_config["url"]
642
+ target_dir = self.CLAUDE_SKILLS_DIR / collection_name
643
+
644
+ self.logger.info(f"Processing collection '{collection_name}' from {repo_url}")
645
+
646
+ # Check if directory exists and handle accordingly
647
+ if target_dir.exists():
648
+ git_dir = target_dir / ".git"
649
+
650
+ if git_dir.exists():
651
+ # Update existing: git pull
652
+ self.logger.info(
653
+ f"Updating existing collection '{collection_name}' at {target_dir}"
654
+ )
655
+ try:
656
+ result = subprocess.run(
657
+ ["git", "pull"],
658
+ cwd=target_dir,
659
+ capture_output=True,
660
+ text=True,
661
+ check=True,
662
+ timeout=60,
663
+ )
664
+ self.logger.debug(f"Git pull output: {result.stdout}")
665
+
666
+ except subprocess.CalledProcessError as e:
667
+ raise Exception(
668
+ f"Failed to update collection '{collection_name}': {e.stderr}"
669
+ ) from e
670
+ except subprocess.TimeoutExpired as e:
671
+ raise Exception(
672
+ f"Git pull timed out for collection '{collection_name}'"
673
+ ) from e
674
+ else:
675
+ # Directory exists but not a git repo - error
676
+ raise ValueError(
677
+ f"Directory {target_dir} exists but is not a git repository. "
678
+ f"Please remove it manually and try again:\n"
679
+ f" rm -rf {target_dir}"
680
+ )
681
+ else:
682
+ # First install: git clone
683
+ self.logger.info(
684
+ f"Installing new collection '{collection_name}' to {target_dir}"
685
+ )
686
+ try:
687
+ result = subprocess.run(
688
+ ["git", "clone", repo_url, str(target_dir)],
689
+ capture_output=True,
690
+ text=True,
691
+ check=True,
692
+ timeout=120,
693
+ )
694
+ self.logger.debug(f"Git clone output: {result.stdout}")
695
+
696
+ except subprocess.CalledProcessError as e:
697
+ raise Exception(
698
+ f"Failed to clone collection '{collection_name}': {e.stderr}"
699
+ ) from e
700
+ except subprocess.TimeoutExpired as e:
701
+ raise Exception(
702
+ f"Git clone timed out for collection '{collection_name}'"
703
+ ) from e
704
+
705
+ # Update last_update timestamp
706
+ self.skills_config.update_collection_timestamp(collection_name)
707
+
708
+ # Parse manifest.json
709
+ manifest_path = target_dir / "manifest.json"
710
+ if not manifest_path.exists():
711
+ raise Exception(
712
+ f"manifest.json not found in collection '{collection_name}' at {target_dir}"
713
+ )
714
+
715
+ try:
716
+ with open(manifest_path, encoding="utf-8") as f:
717
+ manifest = json.load(f)
718
+ except json.JSONDecodeError as e:
719
+ raise Exception(
720
+ f"Invalid manifest.json in collection '{collection_name}': {e}"
721
+ ) from e
722
+
723
+ self.logger.info(
724
+ f"Successfully loaded collection '{collection_name}' from {target_dir}"
725
+ )
726
+
727
+ # Return data in same format as before for compatibility
728
+ # Note: temp_dir is now the persistent collection directory
729
+ return {"temp_dir": target_dir, "manifest": manifest, "repo_dir": target_dir}
730
+
731
+ def _flatten_manifest_skills(self, manifest: Dict) -> List[Dict]:
732
+ """Flatten skills from manifest, supporting both structures.
733
+
734
+ Supports both legacy flat list and new nested dict structures:
735
+ - Legacy: {"skills": [skill1, skill2, ...]}
736
+ - New: {"skills": {"universal": [...], "toolchains": {...}}}
737
+
738
+ Args:
739
+ manifest: The manifest dictionary
740
+
741
+ Returns:
742
+ List of flattened skill dictionaries
743
+
744
+ Raises:
745
+ ValueError: If manifest structure is invalid
746
+
747
+ Example:
748
+ >>> # Legacy flat structure
749
+ >>> manifest = {"skills": [{"name": "skill1"}, {"name": "skill2"}]}
750
+ >>> skills = deployer._flatten_manifest_skills(manifest)
751
+ >>> len(skills) # 2
752
+
753
+ >>> # New nested structure
754
+ >>> manifest = {
755
+ ... "skills": {
756
+ ... "universal": [{"name": "skill1"}],
757
+ ... "toolchains": {"python": [{"name": "skill2"}]}
758
+ ... }
759
+ ... }
760
+ >>> skills = deployer._flatten_manifest_skills(manifest)
761
+ >>> len(skills) # 2
762
+ """
763
+ skills_data = manifest.get("skills", {})
764
+
765
+ # Handle legacy flat list structure
766
+ if isinstance(skills_data, list):
767
+ self.logger.debug(
768
+ f"Using legacy flat manifest structure ({len(skills_data)} skills)"
769
+ )
770
+ return skills_data
771
+
772
+ # Handle new nested dict structure
773
+ if isinstance(skills_data, dict):
774
+ flat_skills = []
775
+
776
+ # Add universal skills
777
+ universal_skills = skills_data.get("universal", [])
778
+ if isinstance(universal_skills, list):
779
+ flat_skills.extend(universal_skills)
780
+ self.logger.debug(f"Added {len(universal_skills)} universal skills")
781
+
782
+ # Add toolchain-specific skills
783
+ toolchains = skills_data.get("toolchains", {})
784
+ if isinstance(toolchains, dict):
785
+ for toolchain_name, toolchain_skills in toolchains.items():
786
+ if isinstance(toolchain_skills, list):
787
+ flat_skills.extend(toolchain_skills)
788
+ self.logger.debug(
789
+ f"Added {len(toolchain_skills)} {toolchain_name} skills"
790
+ )
791
+
792
+ self.logger.info(
793
+ f"Flattened {len(flat_skills)} total skills from nested structure"
794
+ )
795
+ return flat_skills
796
+
797
+ # Invalid structure
798
+ raise ValueError(
799
+ f"Skills manifest must be a list or dict, got {type(skills_data).__name__}"
800
+ )
801
+
802
+ def _filter_skills(
803
+ self,
804
+ skills: List[Dict],
805
+ toolchain: Optional[List[str]] = None,
806
+ categories: Optional[List[str]] = None,
807
+ ) -> List[Dict]:
808
+ """Filter skills by toolchain and categories.
809
+
810
+ Args:
811
+ skills: List of skill metadata dicts
812
+ toolchain: List of toolchains to include (None = all)
813
+ categories: List of categories to include (None = all)
814
+
815
+ Returns:
816
+ Filtered list of skills
817
+ """
818
+ # Ensure skills is a list and contains dicts
819
+ if not isinstance(skills, list):
820
+ return []
821
+
822
+ # Filter out non-dict items
823
+ filtered = [s for s in skills if isinstance(s, dict)]
824
+
825
+ # Filter by toolchain
826
+ if toolchain:
827
+ toolchain_lower = [t.lower() for t in toolchain]
828
+ filtered = [
829
+ s
830
+ for s in filtered
831
+ if isinstance(s, dict)
832
+ and any(
833
+ t.lower() in toolchain_lower
834
+ for t in (
835
+ s.get("toolchain", [])
836
+ if isinstance(s.get("toolchain"), list)
837
+ else ([s.get("toolchain")] if s.get("toolchain") else [])
838
+ )
839
+ )
840
+ ]
841
+
842
+ # Filter by categories
843
+ if categories:
844
+ categories_lower = [c.lower() for c in categories]
845
+ filtered = [
846
+ s
847
+ for s in filtered
848
+ if isinstance(s, dict)
849
+ and s.get("category", "").lower() in categories_lower
850
+ ]
851
+
852
+ return filtered
853
+
854
+ def _deploy_skill(
855
+ self,
856
+ skill: Dict,
857
+ collection_dir: Path,
858
+ collection_name: str,
859
+ force: bool = False,
860
+ ) -> Dict:
861
+ """Deploy a single skill to ~/.claude/skills/ and track deployment.
862
+
863
+ NOTE: With multi-collection support, skills are now stored in collection
864
+ subdirectories. This method creates symlinks or copies to maintain the
865
+ flat structure that Claude Code expects in ~/.claude/skills/.
866
+
867
+ Additionally tracks deployed skills in .mpm-deployed-skills.json index
868
+ for orphan cleanup functionality.
869
+
870
+ Args:
871
+ skill: Skill metadata dict
872
+ collection_dir: Collection directory containing skills
873
+ collection_name: Name of collection (for tracking)
874
+ force: Overwrite if already exists
875
+
876
+ Returns:
877
+ Dict with deployed, skipped, error flags
878
+ """
879
+ skill_name = skill["name"]
880
+
881
+ # Use normalized source_path for both target directory and deployment tracking
882
+ # This ensures consistency with configuration.yaml skill names
883
+ source_path = skill.get("source_path", "")
884
+ if source_path:
885
+ # Normalize: "universal/web/api-design-patterns/SKILL.md" -> "universal-web-api-design-patterns"
886
+ normalized_name = source_path.replace("/SKILL.md", "").replace("/", "-")
887
+ target_dir = self.CLAUDE_SKILLS_DIR / normalized_name
888
+ else:
889
+ # Fallback to skill name if no source_path
890
+ target_dir = self.CLAUDE_SKILLS_DIR / skill_name
891
+
892
+ # Check if already deployed
893
+ if target_dir.exists() and not force:
894
+ self.logger.debug(f"Skipped {skill_name} (already deployed)")
895
+ return {"deployed": False, "skipped": True, "error": None}
896
+
897
+ # Find skill source using source_path from manifest
898
+ source_dir = None
899
+
900
+ if source_path:
901
+ # Direct lookup using source_path (most reliable)
902
+ # Example: "universal/web/api-design-patterns/SKILL.md" -> "universal/web/api-design-patterns"
903
+ skill_dir_path = source_path.replace("/SKILL.md", "")
904
+ potential_source = collection_dir / skill_dir_path
905
+ if potential_source.exists():
906
+ source_dir = potential_source
907
+ else:
908
+ self.logger.debug(
909
+ f"Source path {skill_dir_path} not found, trying fallback search"
910
+ )
911
+
912
+ # Fallback: search using old logic (for backward compatibility)
913
+ if not source_dir:
914
+ skills_base = collection_dir / "skills"
915
+ category = skill.get("category", "")
916
+
917
+ # Try multiple possible locations
918
+ search_paths = []
919
+
920
+ # Try category-based path
921
+ if category and skills_base.exists():
922
+ search_paths.append(skills_base / category / skill_name)
923
+
924
+ # Try universal/toolchains structure
925
+ if (collection_dir / "universal").exists():
926
+ search_paths.append(collection_dir / "universal" / skill_name)
927
+
928
+ if (collection_dir / "toolchains").exists():
929
+ toolchain_dir = collection_dir / "toolchains"
930
+ for tc in toolchain_dir.iterdir():
931
+ if tc.is_dir():
932
+ search_paths.append(tc / skill_name)
933
+
934
+ # Search in all possible locations
935
+ for path in search_paths:
936
+ if path.exists():
937
+ source_dir = path
938
+ break
939
+
940
+ # Final fallback: search recursively for skill in skills directory
941
+ if not source_dir and skills_base.exists():
942
+ for cat_dir in skills_base.iterdir():
943
+ if not cat_dir.is_dir():
944
+ continue
945
+ potential = cat_dir / skill_name
946
+ if potential.exists():
947
+ source_dir = potential
948
+ break
949
+
950
+ if not source_dir or not source_dir.exists():
951
+ return {
952
+ "deployed": False,
953
+ "skipped": False,
954
+ "error": f"Skill source not found: {skill_name} (searched in {collection_dir})",
955
+ }
956
+
957
+ # Security: Validate paths
958
+ if not self._validate_safe_path(collection_dir, source_dir):
959
+ return {
960
+ "deployed": False,
961
+ "skipped": False,
962
+ "error": f"Invalid source path: {source_dir}",
963
+ }
964
+
965
+ if not self._validate_safe_path(self.CLAUDE_SKILLS_DIR, target_dir):
966
+ return {
967
+ "deployed": False,
968
+ "skipped": False,
969
+ "error": f"Invalid target path: {target_dir}",
970
+ }
971
+
972
+ try:
973
+ # Remove existing if force
974
+ if target_dir.exists():
975
+ if target_dir.is_symlink():
976
+ target_dir.unlink()
977
+ else:
978
+ shutil.rmtree(target_dir)
979
+
980
+ # Copy skill to Claude skills directory
981
+ # NOTE: We use copy instead of symlink to maintain Claude Code compatibility
982
+ shutil.copytree(source_dir, target_dir)
983
+
984
+ # Track deployment in index using normalized name
985
+ from claude_mpm.services.skills.selective_skill_deployer import (
986
+ track_deployed_skill,
987
+ )
988
+
989
+ # Use normalized name for tracking (matches configuration.yaml format)
990
+ track_name = normalized_name if source_path else skill_name
991
+ track_deployed_skill(self.CLAUDE_SKILLS_DIR, track_name, collection_name)
992
+
993
+ self.logger.debug(
994
+ f"Deployed {skill_name} from {source_dir} to {target_dir}"
995
+ )
996
+ return {"deployed": True, "skipped": False, "error": None}
997
+
998
+ except Exception as e:
999
+ return {"deployed": False, "skipped": False, "error": str(e)}
1000
+
1001
+ def _validate_safe_path(self, base: Path, target: Path) -> bool:
1002
+ """Ensure target path is within base directory (security).
1003
+
1004
+ Args:
1005
+ base: Base directory
1006
+ target: Target path to validate
1007
+
1008
+ Returns:
1009
+ True if path is safe, False otherwise
1010
+ """
1011
+ try:
1012
+ target.resolve().relative_to(base.resolve())
1013
+ return True
1014
+ except ValueError:
1015
+ return False
1016
+
1017
+ def _is_claude_code_running(self) -> bool:
1018
+ """Check if Claude Code process is running.
1019
+
1020
+ Returns:
1021
+ True if Claude Code is running, False otherwise
1022
+ """
1023
+ try:
1024
+ if platform.system() == "Windows":
1025
+ result = subprocess.run(
1026
+ ["tasklist"], check=False, capture_output=True, text=True, timeout=5
1027
+ )
1028
+ return "claude" in result.stdout.lower()
1029
+ # macOS and Linux
1030
+ result = subprocess.run(
1031
+ ["ps", "aux"], check=False, capture_output=True, text=True, timeout=5
1032
+ )
1033
+ # Look for "Claude Code" or "claude-code" process
1034
+ return (
1035
+ "claude code" in result.stdout.lower()
1036
+ or "claude-code" in result.stdout.lower()
1037
+ )
1038
+
1039
+ except Exception as e:
1040
+ self.logger.debug(f"Failed to check Claude Code process: {e}")
1041
+ return False
1042
+
1043
+ def _cleanup(self, temp_dir: Path) -> None:
1044
+ """Cleanup temporary directory.
1045
+
1046
+ NOTE: With multi-collection support, temp_dir is now the persistent
1047
+ collection directory, so we DON'T delete it. This method is kept for
1048
+ backward compatibility but is now a no-op.
1049
+
1050
+ Args:
1051
+ temp_dir: Collection directory (not deleted)
1052
+ """
1053
+ # NO-OP: Collection directories are persistent, not temporary
1054
+ # Skills are deployed from collection directories to Claude skills dir
1055
+ self.logger.debug(f"Collection directory preserved at {temp_dir} (not deleted)")
1056
+
1057
+ # === Collection Management Methods ===
1058
+
1059
+ def list_collections(self) -> Dict[str, Any]:
1060
+ """List all configured skill collections.
1061
+
1062
+ Returns:
1063
+ Dict containing:
1064
+ - collections: Dict of collection configurations
1065
+ - default_collection: Name of default collection
1066
+ - enabled_count: Number of enabled collections
1067
+
1068
+ Example:
1069
+ >>> result = deployer.list_collections()
1070
+ >>> for name, config in result['collections'].items():
1071
+ >>> print(f"{name}: {config['url']} (priority: {config['priority']})")
1072
+ """
1073
+ collections = self.skills_config.get_collections()
1074
+ default = self.skills_config.get_default_collection()
1075
+ enabled = self.skills_config.get_enabled_collections()
1076
+
1077
+ return {
1078
+ "collections": collections,
1079
+ "default_collection": default,
1080
+ "enabled_count": len(enabled),
1081
+ "total_count": len(collections),
1082
+ }
1083
+
1084
+ def add_collection(self, name: str, url: str, priority: int = 99) -> Dict[str, Any]:
1085
+ """Add a new skill collection.
1086
+
1087
+ Args:
1088
+ name: Collection name (must be unique)
1089
+ url: GitHub repository URL
1090
+ priority: Collection priority (lower = higher priority, default: 99)
1091
+
1092
+ Returns:
1093
+ Dict with operation result
1094
+
1095
+ Example:
1096
+ >>> deployer.add_collection("obra-superpowers", "https://github.com/obra/superpowers")
1097
+ """
1098
+ return self.skills_config.add_collection(name, url, priority)
1099
+
1100
+ def remove_collection(self, name: str) -> Dict[str, Any]:
1101
+ """Remove a skill collection.
1102
+
1103
+ Args:
1104
+ name: Collection name to remove
1105
+
1106
+ Returns:
1107
+ Dict with operation result
1108
+
1109
+ Example:
1110
+ >>> deployer.remove_collection("obra-superpowers")
1111
+ """
1112
+ result = self.skills_config.remove_collection(name)
1113
+
1114
+ # Also remove the collection directory
1115
+ collection_dir = self.CLAUDE_SKILLS_DIR / name
1116
+ if collection_dir.exists():
1117
+ try:
1118
+ shutil.rmtree(collection_dir)
1119
+ self.logger.info(f"Removed collection directory: {collection_dir}")
1120
+ result["directory_removed"] = True
1121
+ except Exception as e:
1122
+ self.logger.warning(f"Failed to remove directory {collection_dir}: {e}")
1123
+ result["directory_removed"] = False
1124
+ result["directory_error"] = str(e)
1125
+
1126
+ return result
1127
+
1128
+ def enable_collection(self, name: str) -> Dict[str, Any]:
1129
+ """Enable a disabled collection.
1130
+
1131
+ Args:
1132
+ name: Collection name
1133
+
1134
+ Returns:
1135
+ Dict with operation result
1136
+
1137
+ Example:
1138
+ >>> deployer.enable_collection("obra-superpowers")
1139
+ """
1140
+ return self.skills_config.enable_collection(name)
1141
+
1142
+ def disable_collection(self, name: str) -> Dict[str, Any]:
1143
+ """Disable a collection without removing it.
1144
+
1145
+ Args:
1146
+ name: Collection name
1147
+
1148
+ Returns:
1149
+ Dict with operation result
1150
+
1151
+ Example:
1152
+ >>> deployer.disable_collection("obra-superpowers")
1153
+ """
1154
+ return self.skills_config.disable_collection(name)
1155
+
1156
+ def set_default_collection(self, name: str) -> Dict[str, Any]:
1157
+ """Set the default collection for deployments.
1158
+
1159
+ Args:
1160
+ name: Collection name to set as default
1161
+
1162
+ Returns:
1163
+ Dict with operation result
1164
+
1165
+ Example:
1166
+ >>> deployer.set_default_collection("obra-superpowers")
1167
+ """
1168
+ return self.skills_config.set_default_collection(name)