claude-mpm 4.21.0__py3-none-any.whl → 5.0.2__py3-none-any.whl

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