claude-mpm 5.0.2__py3-none-any.whl → 5.4.3__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 (184) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
  3. claude_mpm/agents/PM_INSTRUCTIONS.md +1218 -905
  4. claude_mpm/agents/agent_loader.py +10 -17
  5. claude_mpm/agents/base_agent_loader.py +10 -35
  6. claude_mpm/agents/frontmatter_validator.py +68 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +431 -45
  8. claude_mpm/cli/__init__.py +0 -1
  9. claude_mpm/cli/commands/__init__.py +2 -0
  10. claude_mpm/cli/commands/agent_state_manager.py +67 -23
  11. claude_mpm/cli/commands/agents.py +446 -25
  12. claude_mpm/cli/commands/auto_configure.py +535 -233
  13. claude_mpm/cli/commands/configure.py +1500 -147
  14. claude_mpm/cli/commands/configure_agent_display.py +13 -6
  15. claude_mpm/cli/commands/mpm_init/core.py +158 -1
  16. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  17. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  18. claude_mpm/cli/commands/postmortem.py +401 -0
  19. claude_mpm/cli/commands/run.py +1 -39
  20. claude_mpm/cli/commands/skills.py +322 -19
  21. claude_mpm/cli/commands/summarize.py +413 -0
  22. claude_mpm/cli/executor.py +8 -0
  23. claude_mpm/cli/interactive/agent_wizard.py +302 -195
  24. claude_mpm/cli/parsers/agents_parser.py +137 -0
  25. claude_mpm/cli/parsers/auto_configure_parser.py +13 -0
  26. claude_mpm/cli/parsers/base_parser.py +9 -0
  27. claude_mpm/cli/parsers/skills_parser.py +7 -0
  28. claude_mpm/cli/startup.py +133 -85
  29. claude_mpm/commands/mpm-agents-auto-configure.md +2 -2
  30. claude_mpm/commands/mpm-agents-list.md +2 -2
  31. claude_mpm/commands/mpm-config-view.md +2 -2
  32. claude_mpm/commands/mpm-help.md +3 -0
  33. claude_mpm/commands/{mpm-ticket-organize.md → mpm-organize.md} +4 -5
  34. claude_mpm/commands/mpm-postmortem.md +123 -0
  35. claude_mpm/commands/mpm-session-resume.md +2 -2
  36. claude_mpm/commands/mpm-ticket-view.md +2 -2
  37. claude_mpm/config/agent_presets.py +312 -82
  38. claude_mpm/config/agent_sources.py +27 -0
  39. claude_mpm/config/skill_presets.py +392 -0
  40. claude_mpm/constants.py +1 -0
  41. claude_mpm/core/claude_runner.py +2 -25
  42. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  43. claude_mpm/core/framework/loaders/file_loader.py +54 -101
  44. claude_mpm/core/interactive_session.py +19 -5
  45. claude_mpm/core/oneshot_session.py +16 -4
  46. claude_mpm/core/output_style_manager.py +173 -43
  47. claude_mpm/core/protocols/__init__.py +23 -0
  48. claude_mpm/core/protocols/runner_protocol.py +103 -0
  49. claude_mpm/core/protocols/session_protocol.py +131 -0
  50. claude_mpm/core/shared/singleton_manager.py +11 -4
  51. claude_mpm/core/socketio_pool.py +3 -3
  52. claude_mpm/core/system_context.py +38 -0
  53. claude_mpm/core/unified_agent_registry.py +134 -16
  54. claude_mpm/core/unified_config.py +22 -0
  55. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  56. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-313.pyc +0 -0
  57. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  58. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  59. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  60. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  61. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  62. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  63. claude_mpm/hooks/claude_hooks/event_handlers.py +35 -2
  64. claude_mpm/hooks/claude_hooks/hook_handler.py +4 -0
  65. claude_mpm/hooks/claude_hooks/memory_integration.py +12 -1
  66. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  67. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  68. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  69. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  70. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  71. claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
  72. claude_mpm/models/agent_definition.py +7 -0
  73. claude_mpm/scripts/launch_monitor.py +93 -13
  74. claude_mpm/services/agents/agent_recommendation_service.py +279 -0
  75. claude_mpm/services/agents/cache_git_manager.py +621 -0
  76. claude_mpm/services/agents/deployment/agent_template_builder.py +3 -2
  77. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +110 -3
  78. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +518 -55
  79. claude_mpm/services/agents/git_source_manager.py +20 -0
  80. claude_mpm/services/agents/sources/git_source_sync_service.py +45 -6
  81. claude_mpm/services/agents/toolchain_detector.py +6 -5
  82. claude_mpm/services/analysis/__init__.py +35 -0
  83. claude_mpm/services/analysis/clone_detector.py +1030 -0
  84. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  85. claude_mpm/services/analysis/postmortem_service.py +765 -0
  86. claude_mpm/services/command_deployment_service.py +106 -5
  87. claude_mpm/services/core/base.py +7 -2
  88. claude_mpm/services/diagnostics/checks/mcp_services_check.py +7 -15
  89. claude_mpm/services/event_bus/config.py +3 -1
  90. claude_mpm/services/git/git_operations_service.py +8 -8
  91. claude_mpm/services/mcp_config_manager.py +75 -145
  92. claude_mpm/services/mcp_service_verifier.py +6 -3
  93. claude_mpm/services/monitor/daemon.py +37 -10
  94. claude_mpm/services/monitor/daemon_manager.py +134 -21
  95. claude_mpm/services/monitor/server.py +225 -19
  96. claude_mpm/services/project/project_organizer.py +4 -0
  97. claude_mpm/services/runner_configuration_service.py +16 -3
  98. claude_mpm/services/session_management_service.py +16 -4
  99. claude_mpm/services/socketio/event_normalizer.py +15 -1
  100. claude_mpm/services/socketio/server/core.py +160 -21
  101. claude_mpm/services/version_control/git_operations.py +103 -0
  102. claude_mpm/utils/agent_filters.py +261 -0
  103. claude_mpm/utils/gitignore.py +3 -0
  104. claude_mpm/utils/migration.py +372 -0
  105. claude_mpm/utils/progress.py +5 -1
  106. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/METADATA +69 -84
  107. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/RECORD +112 -153
  108. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/entry_points.txt +0 -2
  109. claude_mpm/dashboard/analysis_runner.py +0 -455
  110. claude_mpm/dashboard/index.html +0 -13
  111. claude_mpm/dashboard/open_dashboard.py +0 -66
  112. claude_mpm/dashboard/static/css/activity.css +0 -1958
  113. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  114. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  115. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  116. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  117. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  118. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  119. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  120. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  121. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  122. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  123. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  124. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  125. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  126. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  127. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  128. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  129. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  130. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  131. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  132. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  133. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  134. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  135. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  136. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  137. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  138. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  139. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  140. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  141. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  142. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  143. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  144. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  145. claude_mpm/dashboard/templates/code_simple.html +0 -153
  146. claude_mpm/dashboard/templates/index.html +0 -606
  147. claude_mpm/dashboard/test_dashboard.html +0 -372
  148. claude_mpm/scripts/mcp_server.py +0 -75
  149. claude_mpm/scripts/mcp_wrapper.py +0 -39
  150. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  151. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  152. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  153. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  154. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  155. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  156. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  157. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  158. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  159. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  160. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -971
  161. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  162. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  163. claude_mpm/services/mcp_gateway/main.py +0 -589
  164. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  165. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  166. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  167. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  168. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  169. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  170. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  171. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  172. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  173. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  174. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  175. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  176. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  177. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  178. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  179. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  180. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  181. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  182. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/WHEEL +0 -0
  183. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/licenses/LICENSE +0 -0
  184. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/top_level.txt +0 -0
@@ -569,41 +569,6 @@ class RunCommand(BaseCommand):
569
569
  return False
570
570
 
571
571
 
572
- def _ensure_mcp_services_configured(logger):
573
- """
574
- Ensure MCP services are configured in .mcp.json on startup.
575
-
576
- This function automatically configures the core MCP services
577
- (mcp-vector-search, mcp-browser, mcp-ticketer) if they're not
578
- already configured in the project's .mcp.json file.
579
-
580
- Args:
581
- logger: Logger instance for output
582
- """
583
- try:
584
- from ...services.mcp_config_manager import MCPConfigManager
585
-
586
- logger.debug("Checking MCP service configuration...")
587
- manager = MCPConfigManager()
588
-
589
- # Check and auto-configure missing MCP services
590
- success, message = manager.ensure_mcp_services_configured()
591
-
592
- if success:
593
- if "already configured" not in message.lower():
594
- logger.info(message)
595
- print(f"✅ {message}")
596
- else:
597
- logger.debug(message)
598
- else:
599
- logger.warning(f"MCP auto-configuration issue: {message}")
600
- # Don't fail the session, just warn
601
-
602
- except Exception as e:
603
- logger.debug(f"MCP auto-configuration skipped: {e}")
604
- # Don't fail the session if auto-configuration fails
605
-
606
-
607
572
  def _handle_reload_agents(logger):
608
573
  """
609
574
  Handle the --reload-agents flag by deleting all local claude-mpm system agents.
@@ -738,10 +703,7 @@ def run_session_legacy(args):
738
703
  if getattr(args, "reload_agents", False):
739
704
  _handle_reload_agents(logger)
740
705
 
741
- # Auto-configure MCP services on startup
742
- _ensure_mcp_services_configured(logger)
743
-
744
- # Trigger vector search indexing after MCP is configured
706
+ # Trigger vector search indexing
745
707
  try:
746
708
  from ...cli.startup_logging import start_vector_search_indexing
747
709
 
@@ -83,6 +83,7 @@ class SkillsManagementCommand(BaseCommand):
83
83
  SkillsCommands.UPDATE.value: self._update_skills,
84
84
  SkillsCommands.INFO.value: self._show_skill_info,
85
85
  SkillsCommands.CONFIG.value: self._manage_config,
86
+ SkillsCommands.CONFIGURE.value: self._configure_skills,
86
87
  # GitHub deployment commands
87
88
  SkillsCommands.DEPLOY_FROM_GITHUB.value: self._deploy_from_github,
88
89
  SkillsCommands.LIST_AVAILABLE.value: self._list_available_github_skills,
@@ -195,55 +196,109 @@ class SkillsManagementCommand(BaseCommand):
195
196
  return CommandResult(success=False, message=str(e), exit_code=1)
196
197
 
197
198
  def _deploy_skills(self, args) -> CommandResult:
198
- """Deploy bundled skills to project."""
199
+ """Deploy skills using two-phase sync: cache → deploy.
200
+
201
+ Phase 3 Integration (1M-486): Uses Git skill source manager for deployment.
202
+ - Phase 1: Sync skills to ~/.claude-mpm/cache/skills/ (if needed)
203
+ - Phase 2: Deploy from cache to project .claude-mpm/skills/
204
+
205
+ This replaces bundled skill deployment with a multi-project
206
+ architecture where one cache serves multiple project deployments.
207
+ """
199
208
  try:
209
+ from pathlib import Path
210
+
211
+ from ...config.skill_sources import SkillSourceConfiguration
212
+ from ...services.skills.git_skill_source_manager import (
213
+ GitSkillSourceManager,
214
+ )
215
+
200
216
  force = getattr(args, "force", False)
201
217
  specific_skills = getattr(args, "skills", None)
202
218
 
203
219
  console.print("\n[bold cyan]Deploying skills...[/bold cyan]\n")
204
220
 
205
- result = self.skills_service.deploy_bundled_skills(
206
- force=force, skill_names=specific_skills
221
+ # Initialize git skill source manager
222
+ config = SkillSourceConfiguration.load()
223
+ git_skill_manager = GitSkillSourceManager(config)
224
+ project_dir = Path.cwd()
225
+
226
+ # Phase 1: Sync skills to cache
227
+ console.print("[dim]Phase 1: Syncing skills to cache...[/dim]")
228
+ sync_results = git_skill_manager.sync_all_sources(force=force)
229
+
230
+ synced_count = sum(
231
+ 1 for result in sync_results.values() if result.get("synced")
232
+ )
233
+ console.print(f"[dim]Synced {synced_count} skill source(s)[/dim]\n")
234
+
235
+ # Phase 2: Deploy from cache to project
236
+ console.print("[dim]Phase 2: Deploying from cache to project...[/dim]\n")
237
+ deploy_result = git_skill_manager.deploy_skills_to_project(
238
+ project_dir=project_dir,
239
+ skill_list=specific_skills,
240
+ force=force,
207
241
  )
208
242
 
209
243
  # Display results
210
- if result["deployed"]:
244
+ if deploy_result["deployed"]:
211
245
  console.print(
212
- f"[green]✓ Deployed {len(result['deployed'])} skill(s):[/green]"
246
+ f"[green]✓ Deployed {len(deploy_result['deployed'])} skill(s):[/green]"
213
247
  )
214
- for skill in result["deployed"]:
248
+ for skill in deploy_result["deployed"]:
215
249
  console.print(f" • {skill}")
216
250
  console.print()
217
251
 
218
- if result["skipped"]:
252
+ if deploy_result["updated"]:
219
253
  console.print(
220
- f"[yellow] Skipped {len(result['skipped'])} skill(s) (already deployed):[/yellow]"
254
+ f"[green] Updated {len(deploy_result['updated'])} skill(s):[/green]"
221
255
  )
222
- for skill in result["skipped"]:
256
+ for skill in deploy_result["updated"]:
257
+ console.print(f" • {skill}")
258
+ console.print()
259
+
260
+ if deploy_result["skipped"]:
261
+ console.print(
262
+ f"[yellow]⊘ Skipped {len(deploy_result['skipped'])} skill(s) (already up-to-date):[/yellow]"
263
+ )
264
+ for skill in deploy_result["skipped"]:
223
265
  console.print(f" • {skill}")
224
266
  console.print("[dim]Use --force to redeploy[/dim]\n")
225
267
 
226
- if result["errors"]:
268
+ if deploy_result["failed"]:
227
269
  console.print(
228
- f"[red]✗ Failed to deploy {len(result['errors'])} skill(s):[/red]"
270
+ f"[red]✗ Failed to deploy {len(deploy_result['failed'])} skill(s):[/red]"
229
271
  )
230
- for skill, error in result["errors"].items():
231
- console.print(f" • {skill}: {error}")
272
+ for skill in deploy_result["failed"]:
273
+ console.print(f" • {skill}")
232
274
  console.print()
233
275
 
234
276
  # Summary
277
+ success_count = len(deploy_result["deployed"]) + len(
278
+ deploy_result["updated"]
279
+ )
235
280
  total = (
236
- len(result["deployed"]) + len(result["skipped"]) + len(result["errors"])
281
+ success_count
282
+ + len(deploy_result["skipped"])
283
+ + len(deploy_result["failed"])
237
284
  )
238
285
  console.print(
239
- f"[bold]Summary:[/bold] {len(result['deployed'])} deployed, "
240
- f"{len(result['skipped'])} skipped, {len(result['errors'])} errors "
241
- f"(Total: {total})\n"
286
+ f"[bold]Summary:[/bold] {success_count} deployed/updated, "
287
+ f"{len(deploy_result['skipped'])} skipped, "
288
+ f"{len(deploy_result['failed'])} errors (Total: {total})\n"
289
+ )
290
+
291
+ console.print(
292
+ f"[dim]Deployment directory: {deploy_result['deployment_dir']}[/dim]\n"
242
293
  )
243
294
 
244
295
  # Exit with error if any deployments failed
245
- exit_code = 1 if result["errors"] else 0
246
- return CommandResult(success=not result["errors"], exit_code=exit_code)
296
+ exit_code = 1 if deploy_result["failed"] else 0
297
+ return CommandResult(
298
+ success=not deploy_result["failed"],
299
+ message=f"Deployed {success_count} skills from cache",
300
+ exit_code=exit_code,
301
+ )
247
302
 
248
303
  except Exception as e:
249
304
  console.print(f"[red]Error deploying skills: {e}[/red]")
@@ -896,6 +951,254 @@ class SkillsManagementCommand(BaseCommand):
896
951
  return CommandResult(success=False, message=str(e), exit_code=1)
897
952
  except Exception as e:
898
953
  console.print(f"[red]Unexpected error: {e}[/red]")
954
+
955
+ def _configure_skills(self, args) -> CommandResult:
956
+ """Interactive skills configuration with checkbox selection.
957
+
958
+ Provides checkbox-based selection interface matching agents configure UX:
959
+ - Status column showing Installed/Available
960
+ - Pre-selection for installed skills
961
+ - Apply/Adjust/Cancel menu
962
+ - While loop for adjustment
963
+ - Simplified labels (checkbox state only)
964
+
965
+ This is Option 3 (Hybrid approach): Separate command for interactive mode
966
+ while keeping deploy-github for CLI automation.
967
+ """
968
+ try:
969
+ import questionary
970
+ from questionary import Choice, Style
971
+ from rich.prompt import Prompt
972
+
973
+ # Questionary style (matching agents configure)
974
+ QUESTIONARY_STYLE = Style(
975
+ [
976
+ (
977
+ "selected",
978
+ "fg:#e0e0e0 bold",
979
+ ), # Light gray - excellent readability
980
+ (
981
+ "pointer",
982
+ "fg:#ffd700 bold",
983
+ ), # Gold/yellow - highly visible pointer
984
+ ("highlighted", "fg:#e0e0e0"), # Light gray - clear hover state
985
+ (
986
+ "question",
987
+ "fg:#e0e0e0 bold",
988
+ ), # Light gray bold - prominent questions
989
+ ("checkbox", "fg:#00ff00"), # Green - for checked boxes
990
+ (
991
+ "checkbox-selected",
992
+ "fg:#00ff00 bold",
993
+ ), # Green bold - for checked selected boxes
994
+ ]
995
+ )
996
+
997
+ console.print("\n[bold cyan]Interactive Skills Configuration[/bold cyan]\n")
998
+ console.print(
999
+ "[dim]Select skills to install/uninstall using checkboxes[/dim]"
1000
+ )
1001
+ console.print("[dim]● = Installed, ○ = Available[/dim]\n")
1002
+
1003
+ # Get deployed skills for status detection
1004
+ deployed_result = self.skills_deployer.check_deployed_skills()
1005
+ deployed_skills = {
1006
+ skill["name"] for skill in deployed_result.get("skills", [])
1007
+ }
1008
+
1009
+ # Get available skills from GitHub
1010
+ console.print("[dim]Fetching available skills from GitHub...[/dim]\n")
1011
+ available_result = self.skills_deployer.list_available_skills()
1012
+
1013
+ if available_result.get("error"):
1014
+ console.print(f"[red]Error: {available_result['error']}[/red]")
1015
+ return CommandResult(
1016
+ success=False, message=available_result["error"], exit_code=1
1017
+ )
1018
+
1019
+ # Flatten skills by category
1020
+ all_skills = []
1021
+ for category, skills in available_result.get("by_category", {}).items():
1022
+ for skill in skills:
1023
+ skill_info = {
1024
+ "name": skill.get("name", "unknown"),
1025
+ "category": category,
1026
+ "is_deployed": skill.get("name", "unknown") in deployed_skills,
1027
+ }
1028
+ all_skills.append(skill_info)
1029
+
1030
+ # Sort by deployed status (deployed first), then by name
1031
+ all_skills.sort(key=lambda s: (not s["is_deployed"], s["name"]))
1032
+
1033
+ # Build checkbox choices with pre-selection
1034
+ # Loop to allow adjusting selection
1035
+ while True:
1036
+ skill_choices = []
1037
+ skill_map = {} # For lookup after selection
1038
+
1039
+ for skill in all_skills:
1040
+ skill_name = skill["name"]
1041
+ category = skill["category"]
1042
+ is_deployed = skill["is_deployed"]
1043
+
1044
+ # Simple format: "skill-name (category)"
1045
+ # Checkbox state (checked/unchecked) indicates installed status
1046
+ choice_text = f"{skill_name} ({category})"
1047
+
1048
+ # Pre-select if deployed
1049
+ choice = Choice(
1050
+ title=choice_text, value=skill_name, checked=is_deployed
1051
+ )
1052
+
1053
+ skill_choices.append(choice)
1054
+ skill_map[skill_name] = skill
1055
+
1056
+ # Display checkbox selection
1057
+ selected_skills = questionary.checkbox(
1058
+ "Select skills (Space to toggle, Enter to confirm):",
1059
+ choices=skill_choices,
1060
+ style=QUESTIONARY_STYLE,
1061
+ ).ask()
1062
+
1063
+ if selected_skills is None:
1064
+ # User cancelled (Ctrl+C)
1065
+ console.print("[yellow]Skills configuration cancelled[/yellow]")
1066
+ return CommandResult(success=True, exit_code=0)
1067
+
1068
+ # Determine changes
1069
+ to_install = []
1070
+ to_remove = []
1071
+
1072
+ for skill in all_skills:
1073
+ skill_name = skill["name"]
1074
+ is_deployed = skill["is_deployed"]
1075
+ is_selected = skill_name in selected_skills
1076
+
1077
+ if is_selected and not is_deployed:
1078
+ to_install.append(skill_name)
1079
+ elif not is_selected and is_deployed:
1080
+ to_remove.append(skill_name)
1081
+
1082
+ # Show summary of changes
1083
+ console.print("\n[bold]Changes to apply:[/bold]")
1084
+ if to_install:
1085
+ console.print(
1086
+ f"\n[green]✓ Install ({len(to_install)} skills):[/green]"
1087
+ )
1088
+ for skill in to_install:
1089
+ console.print(f" • {skill}")
1090
+
1091
+ if to_remove:
1092
+ console.print(
1093
+ f"\n[yellow]✗ Remove ({len(to_remove)} skills):[/yellow]"
1094
+ )
1095
+ for skill in to_remove:
1096
+ console.print(f" • {skill}")
1097
+
1098
+ if not to_install and not to_remove:
1099
+ console.print(
1100
+ "\n[dim]No changes (selection matches current deployment)[/dim]"
1101
+ )
1102
+
1103
+ console.print()
1104
+
1105
+ # Ask user to confirm, adjust, or cancel
1106
+ action = questionary.select(
1107
+ "\nWhat would you like to do?",
1108
+ choices=[
1109
+ Choice("Apply these changes", value="apply"),
1110
+ Choice("Adjust selection", value="adjust"),
1111
+ Choice("Cancel", value="cancel"),
1112
+ ],
1113
+ default="apply",
1114
+ style=QUESTIONARY_STYLE,
1115
+ ).ask()
1116
+
1117
+ if action == "cancel":
1118
+ console.print("[yellow]Changes cancelled[/yellow]")
1119
+ Prompt.ask("\nPress Enter to continue")
1120
+ return CommandResult(success=True, exit_code=0)
1121
+ if action == "adjust":
1122
+ # Loop back to skill selection
1123
+ console.print("\n[dim]Adjusting selection...[/dim]\n")
1124
+ continue
1125
+
1126
+ # Apply changes
1127
+ success = True
1128
+ errors = []
1129
+
1130
+ # Install skills
1131
+ if to_install:
1132
+ console.print("\n[bold cyan]Installing skills...[/bold cyan]\n")
1133
+ for skill_name in to_install:
1134
+ try:
1135
+ # Deploy single skill
1136
+ result = self.skills_deployer.deploy_skills(
1137
+ skill_names=[skill_name], force=False
1138
+ )
1139
+
1140
+ if result.get("errors"):
1141
+ errors.extend(result["errors"])
1142
+ success = False
1143
+ else:
1144
+ console.print(
1145
+ f"[green]✓ Installed: {skill_name}[/green]"
1146
+ )
1147
+ except Exception as e:
1148
+ errors.append(f"Failed to install {skill_name}: {e}")
1149
+ success = False
1150
+
1151
+ # Remove skills
1152
+ if to_remove:
1153
+ console.print("\n[bold yellow]Removing skills...[/bold yellow]\n")
1154
+ for skill_name in to_remove:
1155
+ try:
1156
+ # Remove single skill
1157
+ result = self.skills_deployer.remove_skills(
1158
+ skill_names=[skill_name]
1159
+ )
1160
+
1161
+ if result.get("errors"):
1162
+ errors.extend(result["errors"])
1163
+ success = False
1164
+ else:
1165
+ console.print(
1166
+ f"[yellow]✗ Removed: {skill_name}[/yellow]"
1167
+ )
1168
+ except Exception as e:
1169
+ errors.append(f"Failed to remove {skill_name}: {e}")
1170
+ success = False
1171
+
1172
+ # Show errors if any
1173
+ if errors:
1174
+ console.print(f"\n[red]✗ {len(errors)} error(s):[/red]")
1175
+ for error in errors:
1176
+ console.print(f" • {error}")
1177
+
1178
+ # Show restart instructions
1179
+ if success and (to_install or to_remove):
1180
+ console.print(
1181
+ "\n[bold green]✓ Changes applied successfully![/bold green]"
1182
+ )
1183
+ console.print("\n[yellow]⚠️ Important:[/yellow]")
1184
+ console.print(" Restart Claude Code for changes to take effect")
1185
+
1186
+ console.print()
1187
+ Prompt.ask("\nPress Enter to continue")
1188
+
1189
+ # Exit the loop after successful execution
1190
+ break
1191
+
1192
+ exit_code = 0 if success else 1
1193
+ return CommandResult(success=success, exit_code=exit_code)
1194
+
1195
+ except Exception as e:
1196
+ console.print(f"[red]Error in skills configuration: {e}[/red]")
1197
+ import traceback
1198
+
1199
+ console.print(f"[dim]{traceback.format_exc()}[/dim]")
1200
+ return CommandResult(success=False, message=str(e), exit_code=1)
1201
+
899
1202
  return CommandResult(success=False, message=str(e), exit_code=1)
900
1203
 
901
1204