claude-mpm 4.20.3__py3-none-any.whl → 5.1.8__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (520) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_PM.md +35 -6
  3. claude_mpm/agents/OUTPUT_STYLE.md +3 -48
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +1241 -667
  5. claude_mpm/agents/PM_INSTRUCTIONS_TEACH.md +1322 -0
  6. claude_mpm/agents/WORKFLOW.md +75 -2
  7. claude_mpm/agents/__init__.py +6 -0
  8. claude_mpm/agents/agent_loader.py +1 -4
  9. claude_mpm/agents/base_agent.json +6 -3
  10. claude_mpm/agents/base_agent_loader.py +10 -35
  11. claude_mpm/agents/frontmatter_validator.py +1 -1
  12. claude_mpm/agents/templates/circuit-breakers.md +1254 -0
  13. claude_mpm/agents/templates/context-management-examples.md +544 -0
  14. claude_mpm/agents/templates/{pm_red_flags.md → pm-red-flags.md} +89 -19
  15. claude_mpm/agents/templates/pr-workflow-examples.md +427 -0
  16. claude_mpm/agents/templates/research-gate-examples.md +669 -0
  17. claude_mpm/agents/templates/structured-questions-examples.md +615 -0
  18. claude_mpm/agents/templates/ticket-completeness-examples.md +139 -0
  19. claude_mpm/agents/templates/ticketing-examples.md +277 -0
  20. claude_mpm/cli/__init__.py +37 -2
  21. claude_mpm/cli/commands/__init__.py +2 -0
  22. claude_mpm/cli/commands/agent_source.py +774 -0
  23. claude_mpm/cli/commands/agent_state_manager.py +188 -30
  24. claude_mpm/cli/commands/agents.py +959 -36
  25. claude_mpm/cli/commands/agents_cleanup.py +210 -0
  26. claude_mpm/cli/commands/agents_discover.py +338 -0
  27. claude_mpm/cli/commands/aggregate.py +1 -1
  28. claude_mpm/cli/commands/analyze.py +3 -3
  29. claude_mpm/cli/commands/auto_configure.py +537 -239
  30. claude_mpm/cli/commands/cleanup.py +1 -1
  31. claude_mpm/cli/commands/config.py +7 -4
  32. claude_mpm/cli/commands/configure.py +924 -45
  33. claude_mpm/cli/commands/configure_agent_display.py +4 -4
  34. claude_mpm/cli/commands/configure_navigation.py +63 -46
  35. claude_mpm/cli/commands/debug.py +12 -12
  36. claude_mpm/cli/commands/doctor.py +10 -2
  37. claude_mpm/cli/commands/hook_errors.py +277 -0
  38. claude_mpm/cli/commands/local_deploy.py +1 -4
  39. claude_mpm/cli/commands/mcp_install_commands.py +1 -1
  40. claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
  41. claude_mpm/cli/commands/mpm_init/core.py +573 -0
  42. claude_mpm/cli/commands/mpm_init/display.py +341 -0
  43. claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
  44. claude_mpm/cli/commands/mpm_init/modes.py +397 -0
  45. claude_mpm/cli/commands/mpm_init/prompts.py +442 -0
  46. claude_mpm/cli/commands/mpm_init_cli.py +396 -0
  47. claude_mpm/cli/commands/mpm_init_handler.py +67 -1
  48. claude_mpm/cli/commands/postmortem.py +401 -0
  49. claude_mpm/cli/commands/run.py +125 -167
  50. claude_mpm/cli/commands/skill_source.py +694 -0
  51. claude_mpm/cli/commands/skills.py +835 -44
  52. claude_mpm/cli/executor.py +78 -3
  53. claude_mpm/cli/interactive/agent_wizard.py +1032 -47
  54. claude_mpm/cli/parsers/agent_source_parser.py +171 -0
  55. claude_mpm/cli/parsers/agents_parser.py +256 -4
  56. claude_mpm/cli/parsers/auto_configure_parser.py +13 -0
  57. claude_mpm/cli/parsers/base_parser.py +53 -0
  58. claude_mpm/cli/parsers/config_parser.py +96 -43
  59. claude_mpm/cli/parsers/mpm_init_parser.py +42 -0
  60. claude_mpm/cli/parsers/skill_source_parser.py +169 -0
  61. claude_mpm/cli/parsers/skills_parser.py +145 -0
  62. claude_mpm/cli/parsers/source_parser.py +138 -0
  63. claude_mpm/cli/startup.py +564 -108
  64. claude_mpm/cli/startup_display.py +480 -0
  65. claude_mpm/cli/utils.py +1 -1
  66. claude_mpm/cli_module/commands.py +1 -1
  67. claude_mpm/commands/{mpm-auto-configure.md → mpm-agents-auto-configure.md} +9 -0
  68. claude_mpm/commands/mpm-agents-detect.md +9 -0
  69. claude_mpm/commands/{mpm-agents.md → mpm-agents-list.md} +9 -0
  70. claude_mpm/commands/mpm-agents-recommend.md +9 -0
  71. claude_mpm/commands/{mpm-config.md → mpm-config-view.md} +9 -0
  72. claude_mpm/commands/mpm-doctor.md +9 -0
  73. claude_mpm/commands/mpm-help.md +17 -2
  74. claude_mpm/commands/mpm-init.md +28 -3
  75. claude_mpm/commands/mpm-monitor.md +9 -0
  76. claude_mpm/commands/mpm-postmortem.md +123 -0
  77. claude_mpm/commands/mpm-session-resume.md +381 -0
  78. claude_mpm/commands/mpm-status.md +9 -0
  79. claude_mpm/commands/{mpm-organize.md → mpm-ticket-organize.md} +9 -0
  80. claude_mpm/commands/mpm-ticket-view.md +552 -0
  81. claude_mpm/commands/mpm-version.md +9 -0
  82. claude_mpm/commands/mpm.md +11 -0
  83. claude_mpm/config/agent_presets.py +488 -0
  84. claude_mpm/config/agent_sources.py +325 -0
  85. claude_mpm/config/skill_presets.py +392 -0
  86. claude_mpm/config/skill_sources.py +590 -0
  87. claude_mpm/constants.py +13 -0
  88. claude_mpm/core/api_validator.py +1 -1
  89. claude_mpm/core/claude_runner.py +19 -35
  90. claude_mpm/core/config.py +24 -0
  91. claude_mpm/core/constants.py +1 -1
  92. claude_mpm/core/framework/__init__.py +3 -16
  93. claude_mpm/core/framework/loaders/file_loader.py +54 -101
  94. claude_mpm/core/framework/loaders/instruction_loader.py +25 -5
  95. claude_mpm/core/framework/processors/metadata_processor.py +1 -1
  96. claude_mpm/core/hook_error_memory.py +381 -0
  97. claude_mpm/core/hook_manager.py +41 -2
  98. claude_mpm/core/interactive_session.py +131 -10
  99. claude_mpm/core/interfaces.py +56 -1
  100. claude_mpm/core/logger.py +3 -1
  101. claude_mpm/core/oneshot_session.py +110 -8
  102. claude_mpm/core/protocols/__init__.py +23 -0
  103. claude_mpm/core/protocols/runner_protocol.py +103 -0
  104. claude_mpm/core/protocols/session_protocol.py +131 -0
  105. claude_mpm/core/shared/singleton_manager.py +11 -4
  106. claude_mpm/core/system_context.py +38 -0
  107. claude_mpm/core/unified_config.py +22 -0
  108. claude_mpm/dashboard/static/css/activity.css +69 -69
  109. claude_mpm/dashboard/static/css/connection-status.css +10 -10
  110. claude_mpm/dashboard/static/css/dashboard.css +15 -15
  111. claude_mpm/dashboard/static/js/components/activity-tree.js +178 -178
  112. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +101 -101
  113. claude_mpm/dashboard/static/js/components/agent-inference.js +31 -31
  114. claude_mpm/dashboard/static/js/components/build-tracker.js +59 -59
  115. claude_mpm/dashboard/static/js/components/code-simple.js +107 -107
  116. claude_mpm/dashboard/static/js/components/connection-debug.js +101 -101
  117. claude_mpm/dashboard/static/js/components/diff-viewer.js +113 -113
  118. claude_mpm/dashboard/static/js/components/event-viewer.js +12 -12
  119. claude_mpm/dashboard/static/js/components/file-change-tracker.js +57 -57
  120. claude_mpm/dashboard/static/js/components/file-change-viewer.js +74 -74
  121. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +6 -6
  122. claude_mpm/dashboard/static/js/components/file-viewer.js +42 -42
  123. claude_mpm/dashboard/static/js/components/module-viewer.js +27 -27
  124. claude_mpm/dashboard/static/js/components/session-manager.js +14 -14
  125. claude_mpm/dashboard/static/js/components/socket-manager.js +1 -1
  126. claude_mpm/dashboard/static/js/components/ui-state-manager.js +14 -14
  127. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +110 -110
  128. claude_mpm/dashboard/static/js/components/working-directory.js +8 -8
  129. claude_mpm/dashboard/static/js/connection-manager.js +76 -76
  130. claude_mpm/dashboard/static/js/dashboard.js +76 -58
  131. claude_mpm/dashboard/static/js/extension-error-handler.js +22 -22
  132. claude_mpm/dashboard/static/js/socket-client.js +138 -121
  133. claude_mpm/dashboard/templates/code_simple.html +23 -23
  134. claude_mpm/dashboard/templates/index.html +18 -18
  135. claude_mpm/experimental/cli_enhancements.py +1 -5
  136. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  137. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  138. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  139. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  140. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  141. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  142. claude_mpm/hooks/claude_hooks/event_handlers.py +3 -1
  143. claude_mpm/hooks/claude_hooks/hook_handler.py +24 -7
  144. claude_mpm/hooks/claude_hooks/installer.py +45 -0
  145. claude_mpm/hooks/claude_hooks/memory_integration.py +12 -1
  146. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  147. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  148. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  149. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  150. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  151. claude_mpm/hooks/failure_learning/__init__.py +2 -8
  152. claude_mpm/hooks/failure_learning/failure_detection_hook.py +1 -6
  153. claude_mpm/hooks/failure_learning/fix_detection_hook.py +1 -6
  154. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +1 -6
  155. claude_mpm/hooks/kuzu_response_hook.py +1 -5
  156. claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
  157. claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
  158. claude_mpm/models/git_repository.py +198 -0
  159. claude_mpm/scripts/claude-hook-handler.sh +3 -3
  160. claude_mpm/scripts/start_activity_logging.py +3 -1
  161. claude_mpm/services/agents/agent_builder.py +45 -9
  162. claude_mpm/services/agents/agent_preset_service.py +238 -0
  163. claude_mpm/services/agents/agent_selection_service.py +484 -0
  164. claude_mpm/services/agents/auto_deploy_index_parser.py +569 -0
  165. claude_mpm/services/agents/cache_git_manager.py +621 -0
  166. claude_mpm/services/agents/deployment/agent_deployment.py +126 -2
  167. claude_mpm/services/agents/deployment/agent_discovery_service.py +105 -73
  168. claude_mpm/services/agents/deployment/agent_format_converter.py +1 -1
  169. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -5
  170. claude_mpm/services/agents/deployment/agent_metrics_collector.py +3 -3
  171. claude_mpm/services/agents/deployment/agent_restore_handler.py +1 -4
  172. claude_mpm/services/agents/deployment/agent_template_builder.py +236 -15
  173. claude_mpm/services/agents/deployment/agents_directory_resolver.py +101 -15
  174. claude_mpm/services/agents/deployment/async_agent_deployment.py +2 -1
  175. claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -3
  176. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +115 -15
  177. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +2 -2
  178. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +1 -4
  179. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +363 -0
  180. claude_mpm/services/agents/deployment/single_agent_deployer.py +2 -2
  181. claude_mpm/services/agents/deployment/system_instructions_deployer.py +168 -46
  182. claude_mpm/services/agents/deployment/validation/deployment_validator.py +2 -2
  183. claude_mpm/services/agents/git_source_manager.py +629 -0
  184. claude_mpm/services/agents/loading/framework_agent_loader.py +9 -12
  185. claude_mpm/services/agents/local_template_manager.py +50 -10
  186. claude_mpm/services/agents/single_tier_deployment_service.py +696 -0
  187. claude_mpm/services/agents/sources/__init__.py +13 -0
  188. claude_mpm/services/agents/sources/agent_sync_state.py +516 -0
  189. claude_mpm/services/agents/sources/git_source_sync_service.py +1087 -0
  190. claude_mpm/services/agents/startup_sync.py +239 -0
  191. claude_mpm/services/agents/toolchain_detector.py +474 -0
  192. claude_mpm/services/analysis/__init__.py +25 -0
  193. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  194. claude_mpm/services/analysis/postmortem_service.py +765 -0
  195. claude_mpm/services/cli/session_pause_manager.py +504 -0
  196. claude_mpm/services/cli/session_resume_helper.py +36 -16
  197. claude_mpm/services/cli/unified_dashboard_manager.py +1 -1
  198. claude_mpm/services/command_deployment_service.py +200 -6
  199. claude_mpm/services/core/base.py +31 -11
  200. claude_mpm/services/core/interfaces/__init__.py +1 -3
  201. claude_mpm/services/core/interfaces/health.py +1 -4
  202. claude_mpm/services/core/interfaces.py +56 -1
  203. claude_mpm/services/core/models/__init__.py +2 -11
  204. claude_mpm/services/core/models/agent_config.py +3 -0
  205. claude_mpm/services/core/models/process.py +4 -0
  206. claude_mpm/services/diagnostics/checks/__init__.py +4 -0
  207. claude_mpm/services/diagnostics/checks/agent_check.py +0 -2
  208. claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
  209. claude_mpm/services/diagnostics/checks/instructions_check.py +1 -2
  210. claude_mpm/services/diagnostics/checks/mcp_check.py +0 -1
  211. claude_mpm/services/diagnostics/checks/mcp_services_check.py +7 -15
  212. claude_mpm/services/diagnostics/checks/monitor_check.py +0 -1
  213. claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
  214. claude_mpm/services/diagnostics/diagnostic_runner.py +9 -0
  215. claude_mpm/services/diagnostics/doctor_reporter.py +40 -10
  216. claude_mpm/services/diagnostics/models.py +21 -0
  217. claude_mpm/services/event_bus/direct_relay.py +3 -3
  218. claude_mpm/services/event_bus/event_bus.py +36 -3
  219. claude_mpm/services/event_bus/relay.py +23 -7
  220. claude_mpm/services/events/consumers/logging.py +1 -2
  221. claude_mpm/services/git/__init__.py +21 -0
  222. claude_mpm/services/git/git_operations_service.py +494 -0
  223. claude_mpm/services/github/__init__.py +21 -0
  224. claude_mpm/services/github/github_cli_service.py +397 -0
  225. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -5
  226. claude_mpm/services/infrastructure/monitoring/aggregator.py +1 -6
  227. claude_mpm/services/infrastructure/monitoring/resources.py +1 -1
  228. claude_mpm/services/instructions/__init__.py +9 -0
  229. claude_mpm/services/instructions/instruction_cache_service.py +374 -0
  230. claude_mpm/services/local_ops/__init__.py +5 -13
  231. claude_mpm/services/local_ops/health_checks/__init__.py +1 -3
  232. claude_mpm/services/local_ops/health_manager.py +1 -4
  233. claude_mpm/services/local_ops/process_manager.py +1 -1
  234. claude_mpm/services/local_ops/resource_monitor.py +2 -2
  235. claude_mpm/services/mcp_config_manager.py +75 -145
  236. claude_mpm/services/mcp_gateway/auto_configure.py +31 -25
  237. claude_mpm/services/mcp_gateway/config/configuration.py +1 -1
  238. claude_mpm/services/mcp_gateway/core/process_pool.py +41 -26
  239. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +1 -6
  240. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -2
  241. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
  242. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +26 -21
  243. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +6 -2
  244. claude_mpm/services/mcp_service_verifier.py +6 -3
  245. claude_mpm/services/memory/failure_tracker.py +19 -4
  246. claude_mpm/services/memory/optimizer.py +1 -1
  247. claude_mpm/services/model/model_router.py +8 -9
  248. claude_mpm/services/monitor/daemon.py +29 -9
  249. claude_mpm/services/monitor/daemon_manager.py +96 -19
  250. claude_mpm/services/monitor/server.py +2 -2
  251. claude_mpm/services/native_agent_converter.py +356 -0
  252. claude_mpm/services/port_manager.py +1 -1
  253. claude_mpm/services/pr/__init__.py +14 -0
  254. claude_mpm/services/pr/pr_template_service.py +329 -0
  255. claude_mpm/services/project/documentation_manager.py +2 -1
  256. claude_mpm/services/project/project_organizer.py +4 -0
  257. claude_mpm/services/project/toolchain_analyzer.py +3 -1
  258. claude_mpm/services/runner_configuration_service.py +17 -3
  259. claude_mpm/services/self_upgrade_service.py +165 -7
  260. claude_mpm/services/session_management_service.py +16 -4
  261. claude_mpm/services/skills/__init__.py +18 -0
  262. claude_mpm/services/skills/git_skill_source_manager.py +1169 -0
  263. claude_mpm/services/skills/skill_discovery_service.py +568 -0
  264. claude_mpm/services/skills_config.py +547 -0
  265. claude_mpm/services/skills_deployer.py +955 -0
  266. claude_mpm/services/socketio/handlers/connection.py +1 -1
  267. claude_mpm/services/socketio/handlers/git.py +2 -2
  268. claude_mpm/services/socketio/server/core.py +1 -4
  269. claude_mpm/services/socketio/server/main.py +1 -3
  270. claude_mpm/services/system_instructions_service.py +1 -3
  271. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +0 -3
  272. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +0 -1
  273. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +1 -1
  274. claude_mpm/services/unified/deployment_strategies/vercel.py +1 -5
  275. claude_mpm/services/unified/unified_deployment.py +1 -5
  276. claude_mpm/services/version_control/conflict_resolution.py +6 -4
  277. claude_mpm/services/visualization/__init__.py +1 -5
  278. claude_mpm/services/visualization/mermaid_generator.py +2 -3
  279. claude_mpm/skills/__init__.py +3 -3
  280. claude_mpm/skills/agent_skills_injector.py +42 -49
  281. claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
  282. claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +17 -10
  283. claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +92 -39
  284. claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +13 -12
  285. claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +5 -3
  286. claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +19 -12
  287. claude_mpm/skills/bundled/performance-profiling.md +6 -0
  288. claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +6 -6
  289. claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +13 -9
  290. claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +8 -8
  291. claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +37 -15
  292. claude_mpm/skills/skills_registry.py +44 -48
  293. claude_mpm/skills/skills_service.py +117 -108
  294. claude_mpm/templates/questions/__init__.py +38 -0
  295. claude_mpm/templates/questions/base.py +193 -0
  296. claude_mpm/templates/questions/pr_strategy.py +311 -0
  297. claude_mpm/templates/questions/project_init.py +385 -0
  298. claude_mpm/templates/questions/ticket_mgmt.py +394 -0
  299. claude_mpm/tools/__main__.py +8 -8
  300. claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
  301. claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
  302. claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
  303. claude_mpm/tools/code_tree_analyzer/core.py +380 -0
  304. claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
  305. claude_mpm/tools/code_tree_analyzer/events.py +168 -0
  306. claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
  307. claude_mpm/tools/code_tree_analyzer/models.py +39 -0
  308. claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
  309. claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
  310. claude_mpm/utils/agent_dependency_loader.py +80 -13
  311. claude_mpm/utils/agent_filters.py +288 -0
  312. claude_mpm/utils/dependency_cache.py +3 -1
  313. claude_mpm/utils/gitignore.py +244 -0
  314. claude_mpm/utils/log_cleanup.py +3 -3
  315. claude_mpm/utils/migration.py +372 -0
  316. claude_mpm/utils/progress.py +387 -0
  317. claude_mpm/utils/robust_installer.py +3 -5
  318. claude_mpm/utils/structured_questions.py +619 -0
  319. {claude_mpm-4.20.3.dist-info → claude_mpm-5.1.8.dist-info}/METADATA +496 -65
  320. {claude_mpm-4.20.3.dist-info → claude_mpm-5.1.8.dist-info}/RECORD +328 -416
  321. claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -17
  322. claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +0 -3
  323. claude_mpm/agents/templates/agent-manager.json +0 -273
  324. claude_mpm/agents/templates/agentic-coder-optimizer.json +0 -248
  325. claude_mpm/agents/templates/api_qa.json +0 -180
  326. claude_mpm/agents/templates/circuit_breakers.md +0 -638
  327. claude_mpm/agents/templates/clerk-ops.json +0 -235
  328. claude_mpm/agents/templates/code_analyzer.json +0 -101
  329. claude_mpm/agents/templates/content-agent.json +0 -358
  330. claude_mpm/agents/templates/dart_engineer.json +0 -307
  331. claude_mpm/agents/templates/data_engineer.json +0 -225
  332. claude_mpm/agents/templates/documentation.json +0 -211
  333. claude_mpm/agents/templates/engineer.json +0 -210
  334. claude_mpm/agents/templates/gcp_ops_agent.json +0 -253
  335. claude_mpm/agents/templates/golang_engineer.json +0 -270
  336. claude_mpm/agents/templates/imagemagick.json +0 -264
  337. claude_mpm/agents/templates/java_engineer.json +0 -346
  338. claude_mpm/agents/templates/local_ops_agent.json +0 -1840
  339. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +0 -39
  340. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250901_010124_142.md +0 -400
  341. claude_mpm/agents/templates/memory_manager.json +0 -158
  342. claude_mpm/agents/templates/nextjs_engineer.json +0 -285
  343. claude_mpm/agents/templates/ops.json +0 -185
  344. claude_mpm/agents/templates/php-engineer.json +0 -281
  345. claude_mpm/agents/templates/product_owner.json +0 -338
  346. claude_mpm/agents/templates/project_organizer.json +0 -140
  347. claude_mpm/agents/templates/prompt-engineer.json +0 -737
  348. claude_mpm/agents/templates/python_engineer.json +0 -387
  349. claude_mpm/agents/templates/qa.json +0 -242
  350. claude_mpm/agents/templates/react_engineer.json +0 -238
  351. claude_mpm/agents/templates/refactoring_engineer.json +0 -276
  352. claude_mpm/agents/templates/research.json +0 -188
  353. claude_mpm/agents/templates/ruby-engineer.json +0 -280
  354. claude_mpm/agents/templates/rust_engineer.json +0 -275
  355. claude_mpm/agents/templates/security.json +0 -202
  356. claude_mpm/agents/templates/svelte-engineer.json +0 -225
  357. claude_mpm/agents/templates/ticketing.json +0 -177
  358. claude_mpm/agents/templates/typescript_engineer.json +0 -285
  359. claude_mpm/agents/templates/vercel_ops_agent.json +0 -412
  360. claude_mpm/agents/templates/version_control.json +0 -157
  361. claude_mpm/agents/templates/web_qa.json +0 -399
  362. claude_mpm/agents/templates/web_ui.json +0 -189
  363. claude_mpm/cli/commands/mpm_init.py +0 -2093
  364. claude_mpm/commands/mpm-tickets.md +0 -102
  365. claude_mpm/dashboard/.claude-mpm/socketio-instances.json +0 -1
  366. claude_mpm/dashboard/react/components/DataInspector/DataInspector.module.css +0 -188
  367. claude_mpm/dashboard/react/components/EventViewer/EventViewer.module.css +0 -156
  368. claude_mpm/dashboard/react/components/shared/ConnectionStatus.module.css +0 -38
  369. claude_mpm/dashboard/react/components/shared/FilterBar.module.css +0 -92
  370. claude_mpm/dashboard/static/archive/activity_dashboard_fixed.html +0 -248
  371. claude_mpm/dashboard/static/archive/activity_dashboard_test.html +0 -61
  372. claude_mpm/dashboard/static/archive/test_activity_connection.html +0 -179
  373. claude_mpm/dashboard/static/archive/test_claude_tree_tab.html +0 -68
  374. claude_mpm/dashboard/static/archive/test_dashboard.html +0 -409
  375. claude_mpm/dashboard/static/archive/test_dashboard_fixed.html +0 -519
  376. claude_mpm/dashboard/static/archive/test_dashboard_verification.html +0 -181
  377. claude_mpm/dashboard/static/archive/test_file_data.html +0 -315
  378. claude_mpm/dashboard/static/archive/test_file_tree_empty_state.html +0 -243
  379. claude_mpm/dashboard/static/archive/test_file_tree_fix.html +0 -234
  380. claude_mpm/dashboard/static/archive/test_file_tree_rename.html +0 -117
  381. claude_mpm/dashboard/static/archive/test_file_tree_tab.html +0 -115
  382. claude_mpm/dashboard/static/archive/test_file_viewer.html +0 -224
  383. claude_mpm/dashboard/static/archive/test_final_activity.html +0 -220
  384. claude_mpm/dashboard/static/archive/test_tab_fix.html +0 -139
  385. claude_mpm/dashboard/static/built/assets/events.DjpNxWNo.css +0 -1
  386. claude_mpm/dashboard/static/built/components/activity-tree.js +0 -2
  387. claude_mpm/dashboard/static/built/components/agent-hierarchy.js +0 -777
  388. claude_mpm/dashboard/static/built/components/agent-inference.js +0 -2
  389. claude_mpm/dashboard/static/built/components/build-tracker.js +0 -333
  390. claude_mpm/dashboard/static/built/components/code-simple.js +0 -857
  391. claude_mpm/dashboard/static/built/components/code-tree/tree-breadcrumb.js +0 -353
  392. claude_mpm/dashboard/static/built/components/code-tree/tree-constants.js +0 -235
  393. claude_mpm/dashboard/static/built/components/code-tree/tree-search.js +0 -409
  394. claude_mpm/dashboard/static/built/components/code-tree/tree-utils.js +0 -435
  395. claude_mpm/dashboard/static/built/components/code-tree.js +0 -2
  396. claude_mpm/dashboard/static/built/components/code-viewer.js +0 -2
  397. claude_mpm/dashboard/static/built/components/connection-debug.js +0 -654
  398. claude_mpm/dashboard/static/built/components/diff-viewer.js +0 -891
  399. claude_mpm/dashboard/static/built/components/event-processor.js +0 -2
  400. claude_mpm/dashboard/static/built/components/event-viewer.js +0 -2
  401. claude_mpm/dashboard/static/built/components/export-manager.js +0 -2
  402. claude_mpm/dashboard/static/built/components/file-change-tracker.js +0 -443
  403. claude_mpm/dashboard/static/built/components/file-change-viewer.js +0 -690
  404. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +0 -2
  405. claude_mpm/dashboard/static/built/components/file-viewer.js +0 -2
  406. claude_mpm/dashboard/static/built/components/hud-library-loader.js +0 -2
  407. claude_mpm/dashboard/static/built/components/hud-manager.js +0 -2
  408. claude_mpm/dashboard/static/built/components/hud-visualizer.js +0 -2
  409. claude_mpm/dashboard/static/built/components/module-viewer.js +0 -2
  410. claude_mpm/dashboard/static/built/components/nav-bar.js +0 -145
  411. claude_mpm/dashboard/static/built/components/page-structure.js +0 -429
  412. claude_mpm/dashboard/static/built/components/session-manager.js +0 -2
  413. claude_mpm/dashboard/static/built/components/socket-manager.js +0 -2
  414. claude_mpm/dashboard/static/built/components/ui-state-manager.js +0 -2
  415. claude_mpm/dashboard/static/built/components/unified-data-viewer.js +0 -2
  416. claude_mpm/dashboard/static/built/components/working-directory.js +0 -2
  417. claude_mpm/dashboard/static/built/connection-manager.js +0 -536
  418. claude_mpm/dashboard/static/built/dashboard.js +0 -2
  419. claude_mpm/dashboard/static/built/extension-error-handler.js +0 -164
  420. claude_mpm/dashboard/static/built/react/events.js +0 -30
  421. claude_mpm/dashboard/static/built/shared/dom-helpers.js +0 -396
  422. claude_mpm/dashboard/static/built/shared/event-bus.js +0 -330
  423. claude_mpm/dashboard/static/built/shared/event-filter-service.js +0 -540
  424. claude_mpm/dashboard/static/built/shared/logger.js +0 -385
  425. claude_mpm/dashboard/static/built/shared/page-structure.js +0 -249
  426. claude_mpm/dashboard/static/built/shared/tooltip-service.js +0 -253
  427. claude_mpm/dashboard/static/built/socket-client.js +0 -2
  428. claude_mpm/dashboard/static/built/tab-isolation-fix.js +0 -185
  429. claude_mpm/dashboard/static/dist/assets/events.DjpNxWNo.css +0 -1
  430. claude_mpm/dashboard/static/dist/components/activity-tree.js +0 -2
  431. claude_mpm/dashboard/static/dist/components/agent-inference.js +0 -2
  432. claude_mpm/dashboard/static/dist/components/code-tree.js +0 -2
  433. claude_mpm/dashboard/static/dist/components/code-viewer.js +0 -2
  434. claude_mpm/dashboard/static/dist/components/event-processor.js +0 -2
  435. claude_mpm/dashboard/static/dist/components/event-viewer.js +0 -2
  436. claude_mpm/dashboard/static/dist/components/export-manager.js +0 -2
  437. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +0 -2
  438. claude_mpm/dashboard/static/dist/components/file-viewer.js +0 -2
  439. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +0 -2
  440. claude_mpm/dashboard/static/dist/components/hud-manager.js +0 -2
  441. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +0 -2
  442. claude_mpm/dashboard/static/dist/components/module-viewer.js +0 -2
  443. claude_mpm/dashboard/static/dist/components/session-manager.js +0 -2
  444. claude_mpm/dashboard/static/dist/components/socket-manager.js +0 -2
  445. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +0 -2
  446. claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +0 -2
  447. claude_mpm/dashboard/static/dist/components/working-directory.js +0 -2
  448. claude_mpm/dashboard/static/dist/dashboard.js +0 -2
  449. claude_mpm/dashboard/static/dist/react/events.js +0 -30
  450. claude_mpm/dashboard/static/dist/socket-client.js +0 -2
  451. claude_mpm/dashboard/static/events.html +0 -607
  452. claude_mpm/dashboard/static/index.html +0 -635
  453. claude_mpm/dashboard/static/js/shared/dom-helpers.js +0 -396
  454. claude_mpm/dashboard/static/js/shared/event-bus.js +0 -330
  455. claude_mpm/dashboard/static/js/shared/logger.js +0 -385
  456. claude_mpm/dashboard/static/js/shared/tooltip-service.js +0 -253
  457. claude_mpm/dashboard/static/js/stores/dashboard-store.js +0 -562
  458. claude_mpm/dashboard/static/legacy/activity.html +0 -736
  459. claude_mpm/dashboard/static/legacy/agents.html +0 -786
  460. claude_mpm/dashboard/static/legacy/files.html +0 -747
  461. claude_mpm/dashboard/static/legacy/tools.html +0 -831
  462. claude_mpm/dashboard/static/monitors.html +0 -431
  463. claude_mpm/dashboard/static/production/events.html +0 -659
  464. claude_mpm/dashboard/static/production/main.html +0 -698
  465. claude_mpm/dashboard/static/production/monitors.html +0 -483
  466. claude_mpm/dashboard/static/test-archive/dashboard.html +0 -635
  467. claude_mpm/dashboard/static/test-archive/debug-events.html +0 -147
  468. claude_mpm/dashboard/static/test-archive/test-navigation.html +0 -256
  469. claude_mpm/dashboard/static/test-archive/test-react-exports.html +0 -180
  470. claude_mpm/dashboard/static/test-archive/test_debug.html +0 -25
  471. claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +0 -75
  472. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +0 -184
  473. claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +0 -107
  474. claude_mpm/skills/bundled/collaboration/requesting-code-review/code-reviewer.md +0 -146
  475. claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +0 -118
  476. claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +0 -177
  477. claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +0 -119
  478. claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +0 -148
  479. claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +0 -483
  480. claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +0 -452
  481. claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +0 -449
  482. claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +0 -411
  483. claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +0 -14
  484. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +0 -58
  485. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +0 -68
  486. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +0 -69
  487. claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +0 -175
  488. claude_mpm/skills/bundled/debugging/verification-before-completion/references/common-failures.md +0 -213
  489. claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +0 -314
  490. claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +0 -227
  491. claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +0 -74
  492. claude_mpm/skills/bundled/main/internal-comms/SKILL.md +0 -32
  493. claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +0 -47
  494. claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +0 -65
  495. claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +0 -30
  496. claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +0 -16
  497. claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +0 -328
  498. claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +0 -602
  499. claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +0 -915
  500. claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +0 -916
  501. claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +0 -752
  502. claude_mpm/skills/bundled/main/skill-creator/SKILL.md +0 -209
  503. claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +0 -123
  504. claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +0 -145
  505. claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +0 -543
  506. claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +0 -741
  507. claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +0 -470
  508. claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +0 -458
  509. claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +0 -639
  510. claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +0 -304
  511. claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +0 -96
  512. claude_mpm/tools/code_tree_analyzer.py +0 -1825
  513. /claude_mpm/agents/templates/{git_file_tracking.md → git-file-tracking.md} +0 -0
  514. /claude_mpm/agents/templates/{pm_examples.md → pm-examples.md} +0 -0
  515. /claude_mpm/agents/templates/{response_format.md → response-format.md} +0 -0
  516. /claude_mpm/agents/templates/{validation_templates.md → validation-templates.md} +0 -0
  517. {claude_mpm-4.20.3.dist-info → claude_mpm-5.1.8.dist-info}/WHEEL +0 -0
  518. {claude_mpm-4.20.3.dist-info → claude_mpm-5.1.8.dist-info}/entry_points.txt +0 -0
  519. {claude_mpm-4.20.3.dist-info → claude_mpm-5.1.8.dist-info}/licenses/LICENSE +0 -0
  520. {claude_mpm-4.20.3.dist-info → claude_mpm-5.1.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,955 @@
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
+ ) -> Dict:
86
+ """Deploy skills from GitHub repository.
87
+
88
+ This is the main entry point for skill deployment. It:
89
+ 1. Downloads skills from GitHub collection
90
+ 2. Parses manifest for metadata
91
+ 3. Filters by toolchain and categories
92
+ 4. Deploys to ~/.claude/skills/
93
+ 5. Warns about Claude Code restart
94
+
95
+ Args:
96
+ collection: Collection name to deploy from (default: uses default collection)
97
+ toolchain: Filter by toolchain (e.g., ['python', 'javascript'])
98
+ categories: Filter by categories (e.g., ['testing', 'debugging'])
99
+ force: Overwrite existing skills
100
+
101
+ Returns:
102
+ Dict containing:
103
+ - deployed_count: Number of skills deployed
104
+ - skipped_count: Number of skills skipped
105
+ - errors: List of error messages
106
+ - deployed_skills: List of deployed skill names
107
+ - restart_required: True if Claude Code needs restart
108
+ - restart_instructions: Message about restarting
109
+ - collection: Collection name used for deployment
110
+
111
+ Example:
112
+ >>> result = deployer.deploy_skills(collection="obra-superpowers")
113
+ >>> result = deployer.deploy_skills(toolchain=['python']) # Uses default
114
+ >>> if result['restart_required']:
115
+ >>> print(result['restart_instructions'])
116
+ """
117
+ # Determine which collection to use
118
+ collection_name = collection or self.skills_config.get_default_collection()
119
+
120
+ self.logger.info(f"Deploying skills from collection '{collection_name}'")
121
+
122
+ # Step 1: Download skills from GitHub collection
123
+ try:
124
+ skills_data = self._download_from_github(collection_name)
125
+ except Exception as e:
126
+ self.logger.error(f"Failed to download skills: {e}")
127
+ return {
128
+ "deployed_count": 0,
129
+ "skipped_count": 0,
130
+ "errors": [f"Download failed: {e}"],
131
+ "deployed_skills": [],
132
+ "restart_required": False,
133
+ "restart_instructions": "",
134
+ "collection": collection_name,
135
+ }
136
+
137
+ # Step 2: Parse manifest and flatten skills
138
+ manifest = skills_data.get("manifest", {})
139
+ try:
140
+ skills = self._flatten_manifest_skills(manifest)
141
+ except ValueError as e:
142
+ self.logger.error(f"Invalid manifest structure: {e}")
143
+ return {
144
+ "deployed_count": 0,
145
+ "skipped_count": 0,
146
+ "errors": [f"Invalid manifest: {e}"],
147
+ "deployed_skills": [],
148
+ "restart_required": False,
149
+ "restart_instructions": "",
150
+ "collection": collection_name,
151
+ }
152
+
153
+ self.logger.info(f"Found {len(skills)} skills in repository")
154
+
155
+ # Step 3: Filter skills
156
+ filtered_skills = self._filter_skills(skills, toolchain, categories)
157
+
158
+ self.logger.info(
159
+ f"After filtering: {len(filtered_skills)} skills to deploy"
160
+ f" (toolchain={toolchain}, categories={categories})"
161
+ )
162
+
163
+ # Step 4: Deploy skills
164
+ deployed = []
165
+ skipped = []
166
+ errors = []
167
+
168
+ for skill in filtered_skills:
169
+ try:
170
+ # Validate skill is a dictionary
171
+ if not isinstance(skill, dict):
172
+ self.logger.error(f"Invalid skill format: {skill}")
173
+ errors.append(f"Invalid skill format: {skill}")
174
+ continue
175
+
176
+ result = self._deploy_skill(skill, skills_data["temp_dir"], force=force)
177
+ if result["deployed"]:
178
+ deployed.append(skill["name"])
179
+ elif result["skipped"]:
180
+ skipped.append(skill["name"])
181
+ if result["error"]:
182
+ errors.append(result["error"])
183
+ except Exception as e:
184
+ skill_name = (
185
+ skill.get("name", "unknown")
186
+ if isinstance(skill, dict)
187
+ else "unknown"
188
+ )
189
+ self.logger.error(f"Failed to deploy {skill_name}: {e}")
190
+ errors.append(f"{skill_name}: {e}")
191
+
192
+ # Step 5: Cleanup
193
+ self._cleanup(skills_data["temp_dir"])
194
+
195
+ # Step 6: Check if Claude Code restart needed
196
+ restart_required = len(deployed) > 0
197
+ restart_instructions = ""
198
+
199
+ if restart_required:
200
+ claude_running = self._is_claude_code_running()
201
+ if claude_running:
202
+ restart_instructions = (
203
+ "⚠️ Claude Code is currently running.\n"
204
+ "Skills are only loaded at STARTUP.\n"
205
+ "Please restart Claude Code for new skills to be available.\n\n"
206
+ "How to restart Claude Code:\n"
207
+ "1. Close all Claude Code windows\n"
208
+ "2. Quit Claude Code completely (Cmd+Q on Mac, Alt+F4 on Windows)\n"
209
+ "3. Re-launch Claude Code\n"
210
+ )
211
+ else:
212
+ restart_instructions = (
213
+ "✓ Claude Code is not currently running.\n"
214
+ "Skills will be available when you launch Claude Code.\n"
215
+ )
216
+
217
+ self.logger.info(
218
+ f"Deployment complete: {len(deployed)} deployed, "
219
+ f"{len(skipped)} skipped, {len(errors)} errors"
220
+ )
221
+
222
+ return {
223
+ "deployed_count": len(deployed),
224
+ "skipped_count": len(skipped),
225
+ "errors": errors,
226
+ "deployed_skills": deployed,
227
+ "skipped_skills": skipped,
228
+ "restart_required": restart_required,
229
+ "restart_instructions": restart_instructions,
230
+ "collection": collection_name,
231
+ }
232
+
233
+ def list_available_skills(self, collection: Optional[str] = None) -> Dict:
234
+ """List all available skills from GitHub repository.
235
+
236
+ Downloads manifest and returns skill metadata grouped by category
237
+ and toolchain.
238
+
239
+ Args:
240
+ collection: Collection name to list from (default: uses default collection)
241
+
242
+ Returns:
243
+ Dict containing:
244
+ - total_skills: Total number of available skills
245
+ - by_category: Skills grouped by category
246
+ - by_toolchain: Skills grouped by toolchain
247
+ - skills: Full list of skill metadata
248
+ - collection: Collection name used
249
+
250
+ Example:
251
+ >>> result = deployer.list_available_skills(collection="obra-superpowers")
252
+ >>> result = deployer.list_available_skills() # Uses default
253
+ >>> print(f"Available: {result['total_skills']} skills")
254
+ >>> for category, skills in result['by_category'].items():
255
+ >>> print(f"{category}: {len(skills)} skills")
256
+ """
257
+ collection_name = collection or self.skills_config.get_default_collection()
258
+
259
+ try:
260
+ skills_data = self._download_from_github(collection_name)
261
+ manifest = skills_data.get("manifest", {})
262
+
263
+ # Flatten skills from manifest (supports both legacy and new structure)
264
+ try:
265
+ skills = self._flatten_manifest_skills(manifest)
266
+ except ValueError as e:
267
+ self.logger.error(f"Failed to parse manifest: {e}")
268
+ return {
269
+ "total_skills": 0,
270
+ "by_category": {},
271
+ "by_toolchain": {},
272
+ "skills": [],
273
+ "error": str(e),
274
+ }
275
+
276
+ # Group by category
277
+ by_category = {}
278
+ for skill in skills:
279
+ if not isinstance(skill, dict):
280
+ continue
281
+ category = skill.get("category", "uncategorized")
282
+ if category not in by_category:
283
+ by_category[category] = []
284
+ by_category[category].append(skill)
285
+
286
+ # Group by toolchain
287
+ by_toolchain = {}
288
+ for skill in skills:
289
+ if not isinstance(skill, dict):
290
+ continue
291
+ toolchains = skill.get("toolchain", [])
292
+ if isinstance(toolchains, str):
293
+ toolchains = [toolchains]
294
+ elif not isinstance(toolchains, list):
295
+ toolchains = []
296
+
297
+ for toolchain in toolchains:
298
+ if toolchain not in by_toolchain:
299
+ by_toolchain[toolchain] = []
300
+ by_toolchain[toolchain].append(skill)
301
+
302
+ # Cleanup
303
+ self._cleanup(skills_data["temp_dir"])
304
+
305
+ return {
306
+ "total_skills": len(skills),
307
+ "by_category": by_category,
308
+ "by_toolchain": by_toolchain,
309
+ "skills": skills,
310
+ "collection": collection_name,
311
+ }
312
+
313
+ except Exception as e:
314
+ self.logger.error(f"Failed to list available skills: {e}")
315
+ return {
316
+ "total_skills": 0,
317
+ "by_category": {},
318
+ "by_toolchain": {},
319
+ "skills": [],
320
+ "error": str(e),
321
+ "collection": collection_name,
322
+ }
323
+
324
+ def check_deployed_skills(self) -> Dict:
325
+ """Check which skills are currently deployed.
326
+
327
+ Scans ~/.claude/skills/ directory for deployed skills.
328
+
329
+ Returns:
330
+ Dict containing:
331
+ - deployed_count: Number of deployed skills
332
+ - skills: List of deployed skill names with paths
333
+ - claude_skills_dir: Path to Claude skills directory
334
+
335
+ Example:
336
+ >>> result = deployer.check_deployed_skills()
337
+ >>> print(f"Currently deployed: {result['deployed_count']} skills")
338
+ """
339
+ deployed_skills = []
340
+
341
+ if self.CLAUDE_SKILLS_DIR.exists():
342
+ for skill_dir in self.CLAUDE_SKILLS_DIR.iterdir():
343
+ if skill_dir.is_dir() and not skill_dir.name.startswith("."):
344
+ # Check for SKILL.md
345
+ skill_md = skill_dir / "SKILL.md"
346
+ if skill_md.exists():
347
+ deployed_skills.append(
348
+ {"name": skill_dir.name, "path": str(skill_dir)}
349
+ )
350
+
351
+ return {
352
+ "deployed_count": len(deployed_skills),
353
+ "skills": deployed_skills,
354
+ "claude_skills_dir": str(self.CLAUDE_SKILLS_DIR),
355
+ }
356
+
357
+ def remove_skills(self, skill_names: Optional[List[str]] = None) -> Dict:
358
+ """Remove deployed skills.
359
+
360
+ Args:
361
+ skill_names: List of skill names to remove, or None to remove all
362
+
363
+ Returns:
364
+ Dict containing:
365
+ - removed_count: Number of skills removed
366
+ - removed_skills: List of removed skill names
367
+ - errors: List of error messages
368
+
369
+ Example:
370
+ >>> # Remove specific skills
371
+ >>> result = deployer.remove_skills(['test-skill', 'debug-skill'])
372
+ >>> # Remove all skills
373
+ >>> result = deployer.remove_skills()
374
+ """
375
+ removed = []
376
+ errors = []
377
+
378
+ if not self.CLAUDE_SKILLS_DIR.exists():
379
+ return {
380
+ "removed_count": 0,
381
+ "removed_skills": [],
382
+ "errors": ["Claude skills directory does not exist"],
383
+ }
384
+
385
+ # Get all skills if no specific names provided
386
+ if skill_names is None:
387
+ skill_names = [
388
+ d.name
389
+ for d in self.CLAUDE_SKILLS_DIR.iterdir()
390
+ if d.is_dir() and not d.name.startswith(".")
391
+ ]
392
+
393
+ for skill_name in skill_names:
394
+ skill_dir = self.CLAUDE_SKILLS_DIR / skill_name
395
+
396
+ if not skill_dir.exists():
397
+ errors.append(f"Skill not found: {skill_name}")
398
+ continue
399
+
400
+ try:
401
+ # Security: Validate path is within CLAUDE_SKILLS_DIR
402
+ if not self._validate_safe_path(self.CLAUDE_SKILLS_DIR, skill_dir):
403
+ raise ValueError(f"Path traversal attempt detected: {skill_dir}")
404
+
405
+ # Remove skill directory
406
+ if skill_dir.is_symlink():
407
+ self.logger.warning(f"Removing symlink: {skill_dir}")
408
+ skill_dir.unlink()
409
+ else:
410
+ shutil.rmtree(skill_dir)
411
+
412
+ removed.append(skill_name)
413
+ self.logger.info(f"Removed skill: {skill_name}")
414
+
415
+ except Exception as e:
416
+ self.logger.error(f"Failed to remove {skill_name}: {e}")
417
+ errors.append(f"{skill_name}: {e}")
418
+
419
+ return {
420
+ "removed_count": len(removed),
421
+ "removed_skills": removed,
422
+ "errors": errors,
423
+ }
424
+
425
+ def _download_from_github(self, collection_name: str) -> Dict:
426
+ """Download skills repository from GitHub using git clone/pull.
427
+
428
+ Logic:
429
+ 1. Get collection config from SkillsConfig
430
+ 2. Determine target directory: ~/.claude/skills/{collection_name}/
431
+ 3. Check if directory exists:
432
+ - Exists + is git repo → git pull (update)
433
+ - Exists + not git repo → error (manual cleanup needed)
434
+ - Not exists → git clone (first install)
435
+ 4. Parse manifest.json from collection
436
+ 5. Update last_update timestamp in config
437
+ 6. Return skills data
438
+
439
+ Args:
440
+ collection_name: Name of collection to download
441
+
442
+ Returns:
443
+ Dict containing:
444
+ - temp_dir: Path to collection directory (not temp, but kept for compatibility)
445
+ - manifest: Parsed manifest.json
446
+ - repo_dir: Path to repository directory
447
+
448
+ Raises:
449
+ ValueError: If collection not found or disabled
450
+ Exception: If git operations fail
451
+ """
452
+ # Get collection configuration
453
+ collection_config = self.skills_config.get_collection(collection_name)
454
+ if not collection_config:
455
+ raise ValueError(
456
+ f"Collection '{collection_name}' not found. "
457
+ f"Use 'claude-mpm skills collection add' to add it."
458
+ )
459
+
460
+ if not collection_config.get("enabled", True):
461
+ raise ValueError(
462
+ f"Collection '{collection_name}' is disabled. "
463
+ f"Use 'claude-mpm skills collection enable {collection_name}' to enable it."
464
+ )
465
+
466
+ repo_url = collection_config["url"]
467
+ target_dir = self.CLAUDE_SKILLS_DIR / collection_name
468
+
469
+ self.logger.info(f"Processing collection '{collection_name}' from {repo_url}")
470
+
471
+ # Check if directory exists and handle accordingly
472
+ if target_dir.exists():
473
+ git_dir = target_dir / ".git"
474
+
475
+ if git_dir.exists():
476
+ # Update existing: git pull
477
+ self.logger.info(
478
+ f"Updating existing collection '{collection_name}' at {target_dir}"
479
+ )
480
+ try:
481
+ result = subprocess.run(
482
+ ["git", "pull"],
483
+ cwd=target_dir,
484
+ capture_output=True,
485
+ text=True,
486
+ check=True,
487
+ timeout=60,
488
+ )
489
+ self.logger.debug(f"Git pull output: {result.stdout}")
490
+
491
+ except subprocess.CalledProcessError as e:
492
+ raise Exception(
493
+ f"Failed to update collection '{collection_name}': {e.stderr}"
494
+ ) from e
495
+ except subprocess.TimeoutExpired as e:
496
+ raise Exception(
497
+ f"Git pull timed out for collection '{collection_name}'"
498
+ ) from e
499
+ else:
500
+ # Directory exists but not a git repo - error
501
+ raise ValueError(
502
+ f"Directory {target_dir} exists but is not a git repository. "
503
+ f"Please remove it manually and try again:\n"
504
+ f" rm -rf {target_dir}"
505
+ )
506
+ else:
507
+ # First install: git clone
508
+ self.logger.info(
509
+ f"Installing new collection '{collection_name}' to {target_dir}"
510
+ )
511
+ try:
512
+ result = subprocess.run(
513
+ ["git", "clone", repo_url, str(target_dir)],
514
+ capture_output=True,
515
+ text=True,
516
+ check=True,
517
+ timeout=120,
518
+ )
519
+ self.logger.debug(f"Git clone output: {result.stdout}")
520
+
521
+ except subprocess.CalledProcessError as e:
522
+ raise Exception(
523
+ f"Failed to clone collection '{collection_name}': {e.stderr}"
524
+ ) from e
525
+ except subprocess.TimeoutExpired as e:
526
+ raise Exception(
527
+ f"Git clone timed out for collection '{collection_name}'"
528
+ ) from e
529
+
530
+ # Update last_update timestamp
531
+ self.skills_config.update_collection_timestamp(collection_name)
532
+
533
+ # Parse manifest.json
534
+ manifest_path = target_dir / "manifest.json"
535
+ if not manifest_path.exists():
536
+ raise Exception(
537
+ f"manifest.json not found in collection '{collection_name}' at {target_dir}"
538
+ )
539
+
540
+ try:
541
+ with open(manifest_path, encoding="utf-8") as f:
542
+ manifest = json.load(f)
543
+ except json.JSONDecodeError as e:
544
+ raise Exception(
545
+ f"Invalid manifest.json in collection '{collection_name}': {e}"
546
+ ) from e
547
+
548
+ self.logger.info(
549
+ f"Successfully loaded collection '{collection_name}' from {target_dir}"
550
+ )
551
+
552
+ # Return data in same format as before for compatibility
553
+ # Note: temp_dir is now the persistent collection directory
554
+ return {"temp_dir": target_dir, "manifest": manifest, "repo_dir": target_dir}
555
+
556
+ def _flatten_manifest_skills(self, manifest: Dict) -> List[Dict]:
557
+ """Flatten skills from manifest, supporting both structures.
558
+
559
+ Supports both legacy flat list and new nested dict structures:
560
+ - Legacy: {"skills": [skill1, skill2, ...]}
561
+ - New: {"skills": {"universal": [...], "toolchains": {...}}}
562
+
563
+ Args:
564
+ manifest: The manifest dictionary
565
+
566
+ Returns:
567
+ List of flattened skill dictionaries
568
+
569
+ Raises:
570
+ ValueError: If manifest structure is invalid
571
+
572
+ Example:
573
+ >>> # Legacy flat structure
574
+ >>> manifest = {"skills": [{"name": "skill1"}, {"name": "skill2"}]}
575
+ >>> skills = deployer._flatten_manifest_skills(manifest)
576
+ >>> len(skills) # 2
577
+
578
+ >>> # New nested structure
579
+ >>> manifest = {
580
+ ... "skills": {
581
+ ... "universal": [{"name": "skill1"}],
582
+ ... "toolchains": {"python": [{"name": "skill2"}]}
583
+ ... }
584
+ ... }
585
+ >>> skills = deployer._flatten_manifest_skills(manifest)
586
+ >>> len(skills) # 2
587
+ """
588
+ skills_data = manifest.get("skills", {})
589
+
590
+ # Handle legacy flat list structure
591
+ if isinstance(skills_data, list):
592
+ self.logger.debug(
593
+ f"Using legacy flat manifest structure ({len(skills_data)} skills)"
594
+ )
595
+ return skills_data
596
+
597
+ # Handle new nested dict structure
598
+ if isinstance(skills_data, dict):
599
+ flat_skills = []
600
+
601
+ # Add universal skills
602
+ universal_skills = skills_data.get("universal", [])
603
+ if isinstance(universal_skills, list):
604
+ flat_skills.extend(universal_skills)
605
+ self.logger.debug(f"Added {len(universal_skills)} universal skills")
606
+
607
+ # Add toolchain-specific skills
608
+ toolchains = skills_data.get("toolchains", {})
609
+ if isinstance(toolchains, dict):
610
+ for toolchain_name, toolchain_skills in toolchains.items():
611
+ if isinstance(toolchain_skills, list):
612
+ flat_skills.extend(toolchain_skills)
613
+ self.logger.debug(
614
+ f"Added {len(toolchain_skills)} {toolchain_name} skills"
615
+ )
616
+
617
+ self.logger.info(
618
+ f"Flattened {len(flat_skills)} total skills from nested structure"
619
+ )
620
+ return flat_skills
621
+
622
+ # Invalid structure
623
+ raise ValueError(
624
+ f"Skills manifest must be a list or dict, got {type(skills_data).__name__}"
625
+ )
626
+
627
+ def _filter_skills(
628
+ self,
629
+ skills: List[Dict],
630
+ toolchain: Optional[List[str]] = None,
631
+ categories: Optional[List[str]] = None,
632
+ ) -> List[Dict]:
633
+ """Filter skills by toolchain and categories.
634
+
635
+ Args:
636
+ skills: List of skill metadata dicts
637
+ toolchain: List of toolchains to include (None = all)
638
+ categories: List of categories to include (None = all)
639
+
640
+ Returns:
641
+ Filtered list of skills
642
+ """
643
+ # Ensure skills is a list and contains dicts
644
+ if not isinstance(skills, list):
645
+ return []
646
+
647
+ # Filter out non-dict items
648
+ filtered = [s for s in skills if isinstance(s, dict)]
649
+
650
+ # Filter by toolchain
651
+ if toolchain:
652
+ toolchain_lower = [t.lower() for t in toolchain]
653
+ filtered = [
654
+ s
655
+ for s in filtered
656
+ if isinstance(s, dict)
657
+ and any(
658
+ t.lower() in toolchain_lower
659
+ for t in (
660
+ s.get("toolchain", [])
661
+ if isinstance(s.get("toolchain"), list)
662
+ else ([s.get("toolchain")] if s.get("toolchain") else [])
663
+ )
664
+ )
665
+ ]
666
+
667
+ # Filter by categories
668
+ if categories:
669
+ categories_lower = [c.lower() for c in categories]
670
+ filtered = [
671
+ s
672
+ for s in filtered
673
+ if isinstance(s, dict)
674
+ and s.get("category", "").lower() in categories_lower
675
+ ]
676
+
677
+ return filtered
678
+
679
+ def _deploy_skill(
680
+ self, skill: Dict, collection_dir: Path, force: bool = False
681
+ ) -> Dict:
682
+ """Deploy a single skill to ~/.claude/skills/.
683
+
684
+ NOTE: With multi-collection support, skills are now stored in collection
685
+ subdirectories. This method creates symlinks or copies to maintain the
686
+ flat structure that Claude Code expects in ~/.claude/skills/.
687
+
688
+ Args:
689
+ skill: Skill metadata dict
690
+ collection_dir: Collection directory containing skills
691
+ force: Overwrite if already exists
692
+
693
+ Returns:
694
+ Dict with deployed, skipped, error flags
695
+ """
696
+ skill_name = skill["name"]
697
+ target_dir = self.CLAUDE_SKILLS_DIR / skill_name
698
+
699
+ # Check if already deployed
700
+ if target_dir.exists() and not force:
701
+ self.logger.debug(f"Skipped {skill_name} (already deployed)")
702
+ return {"deployed": False, "skipped": True, "error": None}
703
+
704
+ # Find skill source in collection directory
705
+ # Updated structure: collection_dir / skills / category / skill-name
706
+ # OR: collection_dir / universal / skill-name
707
+ # OR: collection_dir / toolchains / toolchain-name / skill-name
708
+
709
+ skills_base = collection_dir / "skills"
710
+ category = skill.get("category", "")
711
+
712
+ # Try multiple possible locations
713
+ source_dir = None
714
+ search_paths = []
715
+
716
+ # Try category-based path
717
+ if category and skills_base.exists():
718
+ search_paths.append(skills_base / category / skill_name)
719
+
720
+ # Try universal/toolchains structure
721
+ if (collection_dir / "universal").exists():
722
+ search_paths.append(collection_dir / "universal" / skill_name)
723
+
724
+ if (collection_dir / "toolchains").exists():
725
+ toolchain_dir = collection_dir / "toolchains"
726
+ for tc in toolchain_dir.iterdir():
727
+ if tc.is_dir():
728
+ search_paths.append(tc / skill_name)
729
+
730
+ # Search in all possible locations
731
+ for path in search_paths:
732
+ if path.exists():
733
+ source_dir = path
734
+ break
735
+
736
+ # Fallback: search recursively for skill in skills directory
737
+ if not source_dir and skills_base.exists():
738
+ for cat_dir in skills_base.iterdir():
739
+ if not cat_dir.is_dir():
740
+ continue
741
+ potential = cat_dir / skill_name
742
+ if potential.exists():
743
+ source_dir = potential
744
+ break
745
+
746
+ if not source_dir or not source_dir.exists():
747
+ return {
748
+ "deployed": False,
749
+ "skipped": False,
750
+ "error": f"Skill source not found: {skill_name} (searched in {collection_dir})",
751
+ }
752
+
753
+ # Security: Validate paths
754
+ if not self._validate_safe_path(collection_dir, source_dir):
755
+ return {
756
+ "deployed": False,
757
+ "skipped": False,
758
+ "error": f"Invalid source path: {source_dir}",
759
+ }
760
+
761
+ if not self._validate_safe_path(self.CLAUDE_SKILLS_DIR, target_dir):
762
+ return {
763
+ "deployed": False,
764
+ "skipped": False,
765
+ "error": f"Invalid target path: {target_dir}",
766
+ }
767
+
768
+ try:
769
+ # Remove existing if force
770
+ if target_dir.exists():
771
+ if target_dir.is_symlink():
772
+ target_dir.unlink()
773
+ else:
774
+ shutil.rmtree(target_dir)
775
+
776
+ # Copy skill to Claude skills directory
777
+ # NOTE: We use copy instead of symlink to maintain Claude Code compatibility
778
+ shutil.copytree(source_dir, target_dir)
779
+
780
+ self.logger.debug(
781
+ f"Deployed {skill_name} from {source_dir} to {target_dir}"
782
+ )
783
+ return {"deployed": True, "skipped": False, "error": None}
784
+
785
+ except Exception as e:
786
+ return {"deployed": False, "skipped": False, "error": str(e)}
787
+
788
+ def _validate_safe_path(self, base: Path, target: Path) -> bool:
789
+ """Ensure target path is within base directory (security).
790
+
791
+ Args:
792
+ base: Base directory
793
+ target: Target path to validate
794
+
795
+ Returns:
796
+ True if path is safe, False otherwise
797
+ """
798
+ try:
799
+ target.resolve().relative_to(base.resolve())
800
+ return True
801
+ except ValueError:
802
+ return False
803
+
804
+ def _is_claude_code_running(self) -> bool:
805
+ """Check if Claude Code process is running.
806
+
807
+ Returns:
808
+ True if Claude Code is running, False otherwise
809
+ """
810
+ try:
811
+ if platform.system() == "Windows":
812
+ result = subprocess.run(
813
+ ["tasklist"], check=False, capture_output=True, text=True, timeout=5
814
+ )
815
+ return "claude" in result.stdout.lower()
816
+ # macOS and Linux
817
+ result = subprocess.run(
818
+ ["ps", "aux"], check=False, capture_output=True, text=True, timeout=5
819
+ )
820
+ # Look for "Claude Code" or "claude-code" process
821
+ return (
822
+ "claude code" in result.stdout.lower()
823
+ or "claude-code" in result.stdout.lower()
824
+ )
825
+
826
+ except Exception as e:
827
+ self.logger.debug(f"Failed to check Claude Code process: {e}")
828
+ return False
829
+
830
+ def _cleanup(self, temp_dir: Path) -> None:
831
+ """Cleanup temporary directory.
832
+
833
+ NOTE: With multi-collection support, temp_dir is now the persistent
834
+ collection directory, so we DON'T delete it. This method is kept for
835
+ backward compatibility but is now a no-op.
836
+
837
+ Args:
838
+ temp_dir: Collection directory (not deleted)
839
+ """
840
+ # NO-OP: Collection directories are persistent, not temporary
841
+ # Skills are deployed from collection directories to Claude skills dir
842
+ self.logger.debug(f"Collection directory preserved at {temp_dir} (not deleted)")
843
+
844
+ # === Collection Management Methods ===
845
+
846
+ def list_collections(self) -> Dict[str, Any]:
847
+ """List all configured skill collections.
848
+
849
+ Returns:
850
+ Dict containing:
851
+ - collections: Dict of collection configurations
852
+ - default_collection: Name of default collection
853
+ - enabled_count: Number of enabled collections
854
+
855
+ Example:
856
+ >>> result = deployer.list_collections()
857
+ >>> for name, config in result['collections'].items():
858
+ >>> print(f"{name}: {config['url']} (priority: {config['priority']})")
859
+ """
860
+ collections = self.skills_config.get_collections()
861
+ default = self.skills_config.get_default_collection()
862
+ enabled = self.skills_config.get_enabled_collections()
863
+
864
+ return {
865
+ "collections": collections,
866
+ "default_collection": default,
867
+ "enabled_count": len(enabled),
868
+ "total_count": len(collections),
869
+ }
870
+
871
+ def add_collection(self, name: str, url: str, priority: int = 99) -> Dict[str, Any]:
872
+ """Add a new skill collection.
873
+
874
+ Args:
875
+ name: Collection name (must be unique)
876
+ url: GitHub repository URL
877
+ priority: Collection priority (lower = higher priority, default: 99)
878
+
879
+ Returns:
880
+ Dict with operation result
881
+
882
+ Example:
883
+ >>> deployer.add_collection("obra-superpowers", "https://github.com/obra/superpowers")
884
+ """
885
+ return self.skills_config.add_collection(name, url, priority)
886
+
887
+ def remove_collection(self, name: str) -> Dict[str, Any]:
888
+ """Remove a skill collection.
889
+
890
+ Args:
891
+ name: Collection name to remove
892
+
893
+ Returns:
894
+ Dict with operation result
895
+
896
+ Example:
897
+ >>> deployer.remove_collection("obra-superpowers")
898
+ """
899
+ result = self.skills_config.remove_collection(name)
900
+
901
+ # Also remove the collection directory
902
+ collection_dir = self.CLAUDE_SKILLS_DIR / name
903
+ if collection_dir.exists():
904
+ try:
905
+ shutil.rmtree(collection_dir)
906
+ self.logger.info(f"Removed collection directory: {collection_dir}")
907
+ result["directory_removed"] = True
908
+ except Exception as e:
909
+ self.logger.warning(f"Failed to remove directory {collection_dir}: {e}")
910
+ result["directory_removed"] = False
911
+ result["directory_error"] = str(e)
912
+
913
+ return result
914
+
915
+ def enable_collection(self, name: str) -> Dict[str, Any]:
916
+ """Enable a disabled collection.
917
+
918
+ Args:
919
+ name: Collection name
920
+
921
+ Returns:
922
+ Dict with operation result
923
+
924
+ Example:
925
+ >>> deployer.enable_collection("obra-superpowers")
926
+ """
927
+ return self.skills_config.enable_collection(name)
928
+
929
+ def disable_collection(self, name: str) -> Dict[str, Any]:
930
+ """Disable a collection without removing it.
931
+
932
+ Args:
933
+ name: Collection name
934
+
935
+ Returns:
936
+ Dict with operation result
937
+
938
+ Example:
939
+ >>> deployer.disable_collection("obra-superpowers")
940
+ """
941
+ return self.skills_config.disable_collection(name)
942
+
943
+ def set_default_collection(self, name: str) -> Dict[str, Any]:
944
+ """Set the default collection for deployments.
945
+
946
+ Args:
947
+ name: Collection name to set as default
948
+
949
+ Returns:
950
+ Dict with operation result
951
+
952
+ Example:
953
+ >>> deployer.set_default_collection("obra-superpowers")
954
+ """
955
+ return self.skills_config.set_default_collection(name)