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