claude-mpm 4.21.3__py3-none-any.whl → 5.1.9__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 (517) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_PM.md +12 -0
  3. claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +3 -48
  4. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +1239 -674
  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 +69 -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 +1128 -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 +935 -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/core.py +50 -2
  41. claude_mpm/cli/commands/mpm_init/git_activity.py +10 -10
  42. claude_mpm/cli/commands/mpm_init/prompts.py +6 -6
  43. claude_mpm/cli/commands/postmortem.py +401 -0
  44. claude_mpm/cli/commands/run.py +125 -167
  45. claude_mpm/cli/commands/skill_source.py +694 -0
  46. claude_mpm/cli/commands/skills.py +757 -20
  47. claude_mpm/cli/executor.py +78 -3
  48. claude_mpm/cli/interactive/agent_wizard.py +1032 -47
  49. claude_mpm/cli/parsers/agent_source_parser.py +171 -0
  50. claude_mpm/cli/parsers/agents_parser.py +310 -4
  51. claude_mpm/cli/parsers/auto_configure_parser.py +13 -0
  52. claude_mpm/cli/parsers/base_parser.py +53 -0
  53. claude_mpm/cli/parsers/config_parser.py +96 -43
  54. claude_mpm/cli/parsers/skill_source_parser.py +169 -0
  55. claude_mpm/cli/parsers/skills_parser.py +145 -0
  56. claude_mpm/cli/parsers/source_parser.py +138 -0
  57. claude_mpm/cli/startup.py +564 -108
  58. claude_mpm/cli/startup_display.py +480 -0
  59. claude_mpm/cli/utils.py +1 -1
  60. claude_mpm/cli_module/commands.py +1 -1
  61. claude_mpm/commands/{mpm-auto-configure.md → mpm-agents-auto-configure.md} +9 -0
  62. claude_mpm/commands/mpm-agents-detect.md +9 -0
  63. claude_mpm/commands/{mpm-agents.md → mpm-agents-list.md} +9 -0
  64. claude_mpm/commands/mpm-agents-recommend.md +9 -0
  65. claude_mpm/commands/{mpm-config.md → mpm-config-view.md} +9 -0
  66. claude_mpm/commands/mpm-doctor.md +9 -0
  67. claude_mpm/commands/mpm-help.md +14 -2
  68. claude_mpm/commands/mpm-init.md +27 -2
  69. claude_mpm/commands/mpm-monitor.md +9 -0
  70. claude_mpm/commands/mpm-postmortem.md +123 -0
  71. claude_mpm/commands/{mpm-resume.md → mpm-session-resume.md} +9 -0
  72. claude_mpm/commands/mpm-status.md +9 -0
  73. claude_mpm/commands/{mpm-organize.md → mpm-ticket-organize.md} +9 -0
  74. claude_mpm/commands/mpm-ticket-view.md +552 -0
  75. claude_mpm/commands/mpm-version.md +9 -0
  76. claude_mpm/commands/mpm.md +10 -0
  77. claude_mpm/config/agent_presets.py +488 -0
  78. claude_mpm/config/agent_sources.py +325 -0
  79. claude_mpm/config/skill_presets.py +392 -0
  80. claude_mpm/config/skill_sources.py +590 -0
  81. claude_mpm/constants.py +13 -0
  82. claude_mpm/core/api_validator.py +1 -1
  83. claude_mpm/core/claude_runner.py +19 -35
  84. claude_mpm/core/config.py +24 -0
  85. claude_mpm/core/constants.py +1 -1
  86. claude_mpm/core/framework/__init__.py +3 -16
  87. claude_mpm/core/framework/loaders/file_loader.py +54 -101
  88. claude_mpm/core/framework/loaders/instruction_loader.py +25 -5
  89. claude_mpm/core/framework/processors/metadata_processor.py +1 -1
  90. claude_mpm/core/hook_error_memory.py +381 -0
  91. claude_mpm/core/hook_manager.py +41 -2
  92. claude_mpm/core/interactive_session.py +131 -10
  93. claude_mpm/core/logger.py +3 -1
  94. claude_mpm/core/oneshot_session.py +110 -8
  95. claude_mpm/core/output_style_manager.py +173 -43
  96. claude_mpm/core/protocols/__init__.py +23 -0
  97. claude_mpm/core/protocols/runner_protocol.py +103 -0
  98. claude_mpm/core/protocols/session_protocol.py +131 -0
  99. claude_mpm/core/shared/singleton_manager.py +11 -4
  100. claude_mpm/core/system_context.py +38 -0
  101. claude_mpm/core/unified_agent_registry.py +129 -1
  102. claude_mpm/core/unified_config.py +22 -0
  103. claude_mpm/dashboard/static/css/activity.css +69 -69
  104. claude_mpm/dashboard/static/css/connection-status.css +10 -10
  105. claude_mpm/dashboard/static/css/dashboard.css +15 -15
  106. claude_mpm/dashboard/static/js/components/activity-tree.js +178 -178
  107. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +101 -101
  108. claude_mpm/dashboard/static/js/components/agent-inference.js +31 -31
  109. claude_mpm/dashboard/static/js/components/build-tracker.js +59 -59
  110. claude_mpm/dashboard/static/js/components/code-simple.js +107 -107
  111. claude_mpm/dashboard/static/js/components/connection-debug.js +101 -101
  112. claude_mpm/dashboard/static/js/components/diff-viewer.js +113 -113
  113. claude_mpm/dashboard/static/js/components/event-viewer.js +12 -12
  114. claude_mpm/dashboard/static/js/components/file-change-tracker.js +57 -57
  115. claude_mpm/dashboard/static/js/components/file-change-viewer.js +74 -74
  116. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +6 -6
  117. claude_mpm/dashboard/static/js/components/file-viewer.js +42 -42
  118. claude_mpm/dashboard/static/js/components/module-viewer.js +27 -27
  119. claude_mpm/dashboard/static/js/components/session-manager.js +14 -14
  120. claude_mpm/dashboard/static/js/components/socket-manager.js +1 -1
  121. claude_mpm/dashboard/static/js/components/ui-state-manager.js +14 -14
  122. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +110 -110
  123. claude_mpm/dashboard/static/js/components/working-directory.js +8 -8
  124. claude_mpm/dashboard/static/js/connection-manager.js +76 -76
  125. claude_mpm/dashboard/static/js/dashboard.js +76 -58
  126. claude_mpm/dashboard/static/js/extension-error-handler.js +22 -22
  127. claude_mpm/dashboard/static/js/socket-client.js +138 -121
  128. claude_mpm/dashboard/templates/code_simple.html +23 -23
  129. claude_mpm/dashboard/templates/index.html +18 -18
  130. claude_mpm/experimental/cli_enhancements.py +1 -5
  131. claude_mpm/hooks/claude_hooks/event_handlers.py +3 -1
  132. claude_mpm/hooks/claude_hooks/hook_handler.py +24 -7
  133. claude_mpm/hooks/claude_hooks/installer.py +45 -0
  134. claude_mpm/hooks/claude_hooks/memory_integration.py +12 -1
  135. claude_mpm/hooks/failure_learning/__init__.py +2 -8
  136. claude_mpm/hooks/failure_learning/failure_detection_hook.py +1 -6
  137. claude_mpm/hooks/failure_learning/fix_detection_hook.py +1 -6
  138. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +1 -6
  139. claude_mpm/hooks/kuzu_response_hook.py +1 -5
  140. claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
  141. claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
  142. claude_mpm/models/agent_definition.py +7 -0
  143. claude_mpm/models/git_repository.py +198 -0
  144. claude_mpm/scripts/claude-hook-handler.sh +3 -3
  145. claude_mpm/scripts/start_activity_logging.py +3 -1
  146. claude_mpm/services/agents/agent_builder.py +45 -9
  147. claude_mpm/services/agents/agent_preset_service.py +238 -0
  148. claude_mpm/services/agents/agent_selection_service.py +484 -0
  149. claude_mpm/services/agents/auto_deploy_index_parser.py +569 -0
  150. claude_mpm/services/agents/cache_git_manager.py +621 -0
  151. claude_mpm/services/agents/deployment/agent_deployment.py +126 -2
  152. claude_mpm/services/agents/deployment/agent_discovery_service.py +105 -73
  153. claude_mpm/services/agents/deployment/agent_format_converter.py +1 -1
  154. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -5
  155. claude_mpm/services/agents/deployment/agent_metrics_collector.py +3 -3
  156. claude_mpm/services/agents/deployment/agent_restore_handler.py +1 -4
  157. claude_mpm/services/agents/deployment/agent_template_builder.py +236 -15
  158. claude_mpm/services/agents/deployment/agents_directory_resolver.py +101 -15
  159. claude_mpm/services/agents/deployment/async_agent_deployment.py +2 -1
  160. claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -3
  161. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +225 -18
  162. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +2 -2
  163. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +1 -4
  164. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +557 -0
  165. claude_mpm/services/agents/deployment/single_agent_deployer.py +2 -2
  166. claude_mpm/services/agents/deployment/system_instructions_deployer.py +168 -46
  167. claude_mpm/services/agents/deployment/validation/deployment_validator.py +2 -2
  168. claude_mpm/services/agents/git_source_manager.py +629 -0
  169. claude_mpm/services/agents/loading/framework_agent_loader.py +9 -12
  170. claude_mpm/services/agents/local_template_manager.py +50 -10
  171. claude_mpm/services/agents/single_tier_deployment_service.py +696 -0
  172. claude_mpm/services/agents/sources/__init__.py +13 -0
  173. claude_mpm/services/agents/sources/agent_sync_state.py +516 -0
  174. claude_mpm/services/agents/sources/git_source_sync_service.py +1087 -0
  175. claude_mpm/services/agents/startup_sync.py +239 -0
  176. claude_mpm/services/agents/toolchain_detector.py +474 -0
  177. claude_mpm/services/analysis/__init__.py +25 -0
  178. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  179. claude_mpm/services/analysis/postmortem_service.py +765 -0
  180. claude_mpm/services/cli/session_pause_manager.py +1 -1
  181. claude_mpm/services/cli/unified_dashboard_manager.py +1 -1
  182. claude_mpm/services/command_deployment_service.py +200 -6
  183. claude_mpm/services/core/base.py +7 -2
  184. claude_mpm/services/core/interfaces/__init__.py +1 -3
  185. claude_mpm/services/core/interfaces/health.py +1 -4
  186. claude_mpm/services/core/models/__init__.py +2 -11
  187. claude_mpm/services/diagnostics/checks/__init__.py +4 -0
  188. claude_mpm/services/diagnostics/checks/agent_check.py +0 -2
  189. claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
  190. claude_mpm/services/diagnostics/checks/instructions_check.py +1 -2
  191. claude_mpm/services/diagnostics/checks/mcp_check.py +0 -1
  192. claude_mpm/services/diagnostics/checks/mcp_services_check.py +7 -15
  193. claude_mpm/services/diagnostics/checks/monitor_check.py +0 -1
  194. claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
  195. claude_mpm/services/diagnostics/diagnostic_runner.py +9 -0
  196. claude_mpm/services/diagnostics/doctor_reporter.py +40 -10
  197. claude_mpm/services/event_bus/direct_relay.py +3 -3
  198. claude_mpm/services/event_bus/event_bus.py +36 -3
  199. claude_mpm/services/events/consumers/logging.py +1 -2
  200. claude_mpm/services/git/__init__.py +21 -0
  201. claude_mpm/services/git/git_operations_service.py +494 -0
  202. claude_mpm/services/github/__init__.py +21 -0
  203. claude_mpm/services/github/github_cli_service.py +397 -0
  204. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -5
  205. claude_mpm/services/infrastructure/monitoring/aggregator.py +1 -6
  206. claude_mpm/services/infrastructure/monitoring/resources.py +1 -1
  207. claude_mpm/services/instructions/__init__.py +9 -0
  208. claude_mpm/services/instructions/instruction_cache_service.py +374 -0
  209. claude_mpm/services/local_ops/__init__.py +3 -13
  210. claude_mpm/services/local_ops/health_checks/__init__.py +1 -3
  211. claude_mpm/services/local_ops/health_manager.py +1 -4
  212. claude_mpm/services/local_ops/process_manager.py +1 -1
  213. claude_mpm/services/local_ops/resource_monitor.py +2 -2
  214. claude_mpm/services/mcp_config_manager.py +75 -145
  215. claude_mpm/services/mcp_gateway/config/configuration.py +1 -1
  216. claude_mpm/services/mcp_gateway/core/process_pool.py +22 -16
  217. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +1 -6
  218. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -2
  219. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
  220. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +6 -2
  221. claude_mpm/services/mcp_service_verifier.py +6 -3
  222. claude_mpm/services/memory/optimizer.py +1 -1
  223. claude_mpm/services/model/model_router.py +8 -9
  224. claude_mpm/services/monitor/daemon.py +29 -9
  225. claude_mpm/services/monitor/daemon_manager.py +96 -19
  226. claude_mpm/services/monitor/server.py +2 -2
  227. claude_mpm/services/native_agent_converter.py +356 -0
  228. claude_mpm/services/port_manager.py +1 -1
  229. claude_mpm/services/pr/__init__.py +14 -0
  230. claude_mpm/services/pr/pr_template_service.py +329 -0
  231. claude_mpm/services/project/documentation_manager.py +2 -1
  232. claude_mpm/services/project/project_organizer.py +4 -0
  233. claude_mpm/services/project/toolchain_analyzer.py +3 -1
  234. claude_mpm/services/runner_configuration_service.py +17 -3
  235. claude_mpm/services/self_upgrade_service.py +165 -7
  236. claude_mpm/services/session_management_service.py +16 -4
  237. claude_mpm/services/skills/__init__.py +18 -0
  238. claude_mpm/services/skills/git_skill_source_manager.py +1169 -0
  239. claude_mpm/services/skills/skill_discovery_service.py +568 -0
  240. claude_mpm/services/skills_config.py +547 -0
  241. claude_mpm/services/skills_deployer.py +955 -0
  242. claude_mpm/services/socketio/handlers/connection.py +1 -1
  243. claude_mpm/services/socketio/handlers/git.py +2 -2
  244. claude_mpm/services/socketio/server/core.py +1 -4
  245. claude_mpm/services/socketio/server/main.py +1 -3
  246. claude_mpm/services/system_instructions_service.py +1 -3
  247. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +0 -3
  248. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +0 -1
  249. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +1 -1
  250. claude_mpm/services/unified/deployment_strategies/vercel.py +1 -5
  251. claude_mpm/services/unified/unified_deployment.py +1 -5
  252. claude_mpm/services/version_control/conflict_resolution.py +6 -4
  253. claude_mpm/services/visualization/__init__.py +1 -5
  254. claude_mpm/services/visualization/mermaid_generator.py +2 -3
  255. claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
  256. claude_mpm/skills/bundled/performance-profiling.md +6 -0
  257. claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +2 -2
  258. claude_mpm/skills/skills_registry.py +0 -1
  259. claude_mpm/templates/questions/__init__.py +38 -0
  260. claude_mpm/templates/questions/base.py +193 -0
  261. claude_mpm/templates/questions/pr_strategy.py +311 -0
  262. claude_mpm/templates/questions/project_init.py +385 -0
  263. claude_mpm/templates/questions/ticket_mgmt.py +394 -0
  264. claude_mpm/tools/__main__.py +8 -8
  265. claude_mpm/tools/code_tree_analyzer/analysis.py +1 -1
  266. claude_mpm/utils/agent_dependency_loader.py +80 -13
  267. claude_mpm/utils/agent_filters.py +288 -0
  268. claude_mpm/utils/dependency_cache.py +3 -1
  269. claude_mpm/utils/gitignore.py +244 -0
  270. claude_mpm/utils/log_cleanup.py +3 -3
  271. claude_mpm/utils/migration.py +372 -0
  272. claude_mpm/utils/progress.py +387 -0
  273. claude_mpm/utils/robust_installer.py +3 -5
  274. claude_mpm/utils/structured_questions.py +619 -0
  275. {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.dist-info}/METADATA +496 -65
  276. {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.dist-info}/RECORD +284 -443
  277. claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -17
  278. claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +0 -3
  279. claude_mpm/agents/templates/agent-manager.json +0 -273
  280. claude_mpm/agents/templates/agentic-coder-optimizer.json +0 -248
  281. claude_mpm/agents/templates/api_qa.json +0 -180
  282. claude_mpm/agents/templates/circuit_breakers.md +0 -638
  283. claude_mpm/agents/templates/clerk-ops.json +0 -235
  284. claude_mpm/agents/templates/code_analyzer.json +0 -101
  285. claude_mpm/agents/templates/content-agent.json +0 -358
  286. claude_mpm/agents/templates/dart_engineer.json +0 -307
  287. claude_mpm/agents/templates/data_engineer.json +0 -225
  288. claude_mpm/agents/templates/documentation.json +0 -211
  289. claude_mpm/agents/templates/engineer.json +0 -210
  290. claude_mpm/agents/templates/gcp_ops_agent.json +0 -253
  291. claude_mpm/agents/templates/golang_engineer.json +0 -270
  292. claude_mpm/agents/templates/imagemagick.json +0 -264
  293. claude_mpm/agents/templates/java_engineer.json +0 -346
  294. claude_mpm/agents/templates/local_ops_agent.json +0 -1840
  295. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +0 -39
  296. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250901_010124_142.md +0 -400
  297. claude_mpm/agents/templates/memory_manager.json +0 -158
  298. claude_mpm/agents/templates/nextjs_engineer.json +0 -285
  299. claude_mpm/agents/templates/ops.json +0 -185
  300. claude_mpm/agents/templates/php-engineer.json +0 -287
  301. claude_mpm/agents/templates/product_owner.json +0 -338
  302. claude_mpm/agents/templates/project_organizer.json +0 -140
  303. claude_mpm/agents/templates/prompt-engineer.json +0 -737
  304. claude_mpm/agents/templates/python_engineer.json +0 -387
  305. claude_mpm/agents/templates/qa.json +0 -242
  306. claude_mpm/agents/templates/react_engineer.json +0 -238
  307. claude_mpm/agents/templates/refactoring_engineer.json +0 -276
  308. claude_mpm/agents/templates/research.json +0 -188
  309. claude_mpm/agents/templates/ruby-engineer.json +0 -280
  310. claude_mpm/agents/templates/rust_engineer.json +0 -275
  311. claude_mpm/agents/templates/security.json +0 -202
  312. claude_mpm/agents/templates/svelte-engineer.json +0 -225
  313. claude_mpm/agents/templates/ticketing.json +0 -177
  314. claude_mpm/agents/templates/typescript_engineer.json +0 -285
  315. claude_mpm/agents/templates/vercel_ops_agent.json +0 -412
  316. claude_mpm/agents/templates/version_control.json +0 -157
  317. claude_mpm/agents/templates/web_qa.json +0 -399
  318. claude_mpm/agents/templates/web_ui.json +0 -189
  319. claude_mpm/commands/mpm-tickets.md +0 -102
  320. claude_mpm/dashboard/.claude-mpm/socketio-instances.json +0 -1
  321. claude_mpm/dashboard/react/components/DataInspector/DataInspector.module.css +0 -188
  322. claude_mpm/dashboard/react/components/EventViewer/EventViewer.module.css +0 -156
  323. claude_mpm/dashboard/react/components/shared/ConnectionStatus.module.css +0 -38
  324. claude_mpm/dashboard/react/components/shared/FilterBar.module.css +0 -92
  325. claude_mpm/dashboard/static/archive/activity_dashboard_fixed.html +0 -248
  326. claude_mpm/dashboard/static/archive/activity_dashboard_test.html +0 -61
  327. claude_mpm/dashboard/static/archive/test_activity_connection.html +0 -179
  328. claude_mpm/dashboard/static/archive/test_claude_tree_tab.html +0 -68
  329. claude_mpm/dashboard/static/archive/test_dashboard.html +0 -409
  330. claude_mpm/dashboard/static/archive/test_dashboard_fixed.html +0 -519
  331. claude_mpm/dashboard/static/archive/test_dashboard_verification.html +0 -181
  332. claude_mpm/dashboard/static/archive/test_file_data.html +0 -315
  333. claude_mpm/dashboard/static/archive/test_file_tree_empty_state.html +0 -243
  334. claude_mpm/dashboard/static/archive/test_file_tree_fix.html +0 -234
  335. claude_mpm/dashboard/static/archive/test_file_tree_rename.html +0 -117
  336. claude_mpm/dashboard/static/archive/test_file_tree_tab.html +0 -115
  337. claude_mpm/dashboard/static/archive/test_file_viewer.html +0 -224
  338. claude_mpm/dashboard/static/archive/test_final_activity.html +0 -220
  339. claude_mpm/dashboard/static/archive/test_tab_fix.html +0 -139
  340. claude_mpm/dashboard/static/built/assets/events.DjpNxWNo.css +0 -1
  341. claude_mpm/dashboard/static/built/components/activity-tree.js +0 -2
  342. claude_mpm/dashboard/static/built/components/agent-hierarchy.js +0 -777
  343. claude_mpm/dashboard/static/built/components/agent-inference.js +0 -2
  344. claude_mpm/dashboard/static/built/components/build-tracker.js +0 -333
  345. claude_mpm/dashboard/static/built/components/code-simple.js +0 -857
  346. claude_mpm/dashboard/static/built/components/code-tree/tree-breadcrumb.js +0 -353
  347. claude_mpm/dashboard/static/built/components/code-tree/tree-constants.js +0 -235
  348. claude_mpm/dashboard/static/built/components/code-tree/tree-search.js +0 -409
  349. claude_mpm/dashboard/static/built/components/code-tree/tree-utils.js +0 -435
  350. claude_mpm/dashboard/static/built/components/code-tree.js +0 -2
  351. claude_mpm/dashboard/static/built/components/code-viewer.js +0 -2
  352. claude_mpm/dashboard/static/built/components/connection-debug.js +0 -654
  353. claude_mpm/dashboard/static/built/components/diff-viewer.js +0 -891
  354. claude_mpm/dashboard/static/built/components/event-processor.js +0 -2
  355. claude_mpm/dashboard/static/built/components/event-viewer.js +0 -2
  356. claude_mpm/dashboard/static/built/components/export-manager.js +0 -2
  357. claude_mpm/dashboard/static/built/components/file-change-tracker.js +0 -443
  358. claude_mpm/dashboard/static/built/components/file-change-viewer.js +0 -690
  359. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +0 -2
  360. claude_mpm/dashboard/static/built/components/file-viewer.js +0 -2
  361. claude_mpm/dashboard/static/built/components/hud-library-loader.js +0 -2
  362. claude_mpm/dashboard/static/built/components/hud-manager.js +0 -2
  363. claude_mpm/dashboard/static/built/components/hud-visualizer.js +0 -2
  364. claude_mpm/dashboard/static/built/components/module-viewer.js +0 -2
  365. claude_mpm/dashboard/static/built/components/nav-bar.js +0 -145
  366. claude_mpm/dashboard/static/built/components/page-structure.js +0 -429
  367. claude_mpm/dashboard/static/built/components/session-manager.js +0 -2
  368. claude_mpm/dashboard/static/built/components/socket-manager.js +0 -2
  369. claude_mpm/dashboard/static/built/components/ui-state-manager.js +0 -2
  370. claude_mpm/dashboard/static/built/components/unified-data-viewer.js +0 -2
  371. claude_mpm/dashboard/static/built/components/working-directory.js +0 -2
  372. claude_mpm/dashboard/static/built/connection-manager.js +0 -536
  373. claude_mpm/dashboard/static/built/dashboard.js +0 -2
  374. claude_mpm/dashboard/static/built/extension-error-handler.js +0 -164
  375. claude_mpm/dashboard/static/built/react/events.js +0 -30
  376. claude_mpm/dashboard/static/built/shared/dom-helpers.js +0 -396
  377. claude_mpm/dashboard/static/built/shared/event-bus.js +0 -330
  378. claude_mpm/dashboard/static/built/shared/event-filter-service.js +0 -540
  379. claude_mpm/dashboard/static/built/shared/logger.js +0 -385
  380. claude_mpm/dashboard/static/built/shared/page-structure.js +0 -249
  381. claude_mpm/dashboard/static/built/shared/tooltip-service.js +0 -253
  382. claude_mpm/dashboard/static/built/socket-client.js +0 -2
  383. claude_mpm/dashboard/static/built/tab-isolation-fix.js +0 -185
  384. claude_mpm/dashboard/static/dist/assets/events.DjpNxWNo.css +0 -1
  385. claude_mpm/dashboard/static/dist/components/activity-tree.js +0 -2
  386. claude_mpm/dashboard/static/dist/components/agent-inference.js +0 -2
  387. claude_mpm/dashboard/static/dist/components/code-tree.js +0 -2
  388. claude_mpm/dashboard/static/dist/components/code-viewer.js +0 -2
  389. claude_mpm/dashboard/static/dist/components/event-processor.js +0 -2
  390. claude_mpm/dashboard/static/dist/components/event-viewer.js +0 -2
  391. claude_mpm/dashboard/static/dist/components/export-manager.js +0 -2
  392. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +0 -2
  393. claude_mpm/dashboard/static/dist/components/file-viewer.js +0 -2
  394. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +0 -2
  395. claude_mpm/dashboard/static/dist/components/hud-manager.js +0 -2
  396. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +0 -2
  397. claude_mpm/dashboard/static/dist/components/module-viewer.js +0 -2
  398. claude_mpm/dashboard/static/dist/components/session-manager.js +0 -2
  399. claude_mpm/dashboard/static/dist/components/socket-manager.js +0 -2
  400. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +0 -2
  401. claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +0 -2
  402. claude_mpm/dashboard/static/dist/components/working-directory.js +0 -2
  403. claude_mpm/dashboard/static/dist/dashboard.js +0 -2
  404. claude_mpm/dashboard/static/dist/react/events.js +0 -30
  405. claude_mpm/dashboard/static/dist/socket-client.js +0 -2
  406. claude_mpm/dashboard/static/events.html +0 -607
  407. claude_mpm/dashboard/static/index.html +0 -635
  408. claude_mpm/dashboard/static/js/shared/dom-helpers.js +0 -396
  409. claude_mpm/dashboard/static/js/shared/event-bus.js +0 -330
  410. claude_mpm/dashboard/static/js/shared/logger.js +0 -385
  411. claude_mpm/dashboard/static/js/shared/tooltip-service.js +0 -253
  412. claude_mpm/dashboard/static/js/stores/dashboard-store.js +0 -562
  413. claude_mpm/dashboard/static/legacy/activity.html +0 -736
  414. claude_mpm/dashboard/static/legacy/agents.html +0 -786
  415. claude_mpm/dashboard/static/legacy/files.html +0 -747
  416. claude_mpm/dashboard/static/legacy/tools.html +0 -831
  417. claude_mpm/dashboard/static/monitors.html +0 -431
  418. claude_mpm/dashboard/static/production/events.html +0 -659
  419. claude_mpm/dashboard/static/production/main.html +0 -698
  420. claude_mpm/dashboard/static/production/monitors.html +0 -483
  421. claude_mpm/dashboard/static/test-archive/dashboard.html +0 -635
  422. claude_mpm/dashboard/static/test-archive/debug-events.html +0 -147
  423. claude_mpm/dashboard/static/test-archive/test-navigation.html +0 -256
  424. claude_mpm/dashboard/static/test-archive/test-react-exports.html +0 -180
  425. claude_mpm/dashboard/static/test-archive/test_debug.html +0 -25
  426. claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +0 -79
  427. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +0 -178
  428. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +0 -577
  429. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +0 -467
  430. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +0 -537
  431. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +0 -730
  432. claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +0 -112
  433. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +0 -146
  434. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +0 -412
  435. claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +0 -81
  436. claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +0 -362
  437. claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +0 -312
  438. claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +0 -152
  439. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +0 -668
  440. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +0 -587
  441. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +0 -438
  442. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +0 -391
  443. claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +0 -119
  444. claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +0 -148
  445. claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +0 -483
  446. claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +0 -452
  447. claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +0 -449
  448. claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +0 -411
  449. claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +0 -14
  450. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +0 -58
  451. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +0 -68
  452. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +0 -69
  453. claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +0 -131
  454. claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +0 -325
  455. claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +0 -490
  456. claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +0 -425
  457. claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +0 -499
  458. claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +0 -86
  459. claude_mpm/skills/bundled/main/internal-comms/SKILL.md +0 -43
  460. claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +0 -47
  461. claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +0 -65
  462. claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +0 -30
  463. claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +0 -16
  464. claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +0 -160
  465. claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +0 -412
  466. claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +0 -602
  467. claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +0 -915
  468. claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +0 -916
  469. claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +0 -752
  470. claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +0 -1237
  471. claude_mpm/skills/bundled/main/skill-creator/SKILL.md +0 -189
  472. claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +0 -500
  473. claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +0 -464
  474. claude_mpm/skills/bundled/main/skill-creator/references/examples.md +0 -619
  475. claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +0 -437
  476. claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +0 -231
  477. claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +0 -170
  478. claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +0 -602
  479. claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +0 -821
  480. claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +0 -742
  481. claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +0 -726
  482. claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +0 -764
  483. claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +0 -831
  484. claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +0 -226
  485. claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +0 -901
  486. claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +0 -901
  487. claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +0 -775
  488. claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +0 -937
  489. claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +0 -770
  490. claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +0 -961
  491. claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +0 -119
  492. claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +0 -253
  493. claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +0 -145
  494. claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +0 -543
  495. claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +0 -741
  496. claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +0 -470
  497. claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +0 -458
  498. claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +0 -639
  499. claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +0 -140
  500. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +0 -572
  501. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +0 -411
  502. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +0 -569
  503. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +0 -695
  504. claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +0 -184
  505. claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +0 -459
  506. claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +0 -479
  507. claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +0 -687
  508. claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +0 -758
  509. claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +0 -868
  510. /claude_mpm/agents/templates/{git_file_tracking.md → git-file-tracking.md} +0 -0
  511. /claude_mpm/agents/templates/{pm_examples.md → pm-examples.md} +0 -0
  512. /claude_mpm/agents/templates/{response_format.md → response-format.md} +0 -0
  513. /claude_mpm/agents/templates/{validation_templates.md → validation-templates.md} +0 -0
  514. {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.dist-info}/WHEEL +0 -0
  515. {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.dist-info}/entry_points.txt +0 -0
  516. {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.dist-info}/licenses/LICENSE +0 -0
  517. {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.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)