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
@@ -152,7 +152,7 @@ class SimpleAgentManager:
152
152
 
153
153
  # Get metadata for display info
154
154
  metadata = template_data.get("metadata", {})
155
- metadata.get("name", agent_id)
155
+ display_name = metadata.get("name", agent_id)
156
156
  description = metadata.get(
157
157
  "description", "No description available"
158
158
  )
@@ -169,18 +169,26 @@ class SimpleAgentManager:
169
169
  # Normalize agent ID (remove -agent suffix if present, replace underscores)
170
170
  normalized_id = agent_id.replace("-agent", "").replace("_", "-")
171
171
 
172
- agents.append(
173
- AgentConfig(
174
- name=normalized_id,
175
- description=(
176
- description[:80] + "..."
177
- if len(description) > 80
178
- else description
179
- ),
180
- dependencies=display_tools,
181
- )
172
+ # Check if this agent is deployed
173
+ is_deployed = self._is_agent_deployed(normalized_id)
174
+
175
+ agent_config = AgentConfig(
176
+ name=normalized_id,
177
+ description=(
178
+ description[:80] + "..."
179
+ if len(description) > 80
180
+ else description
181
+ ),
182
+ dependencies=display_tools,
182
183
  )
183
184
 
185
+ # Set deployment status and display name
186
+ agent_config.is_deployed = is_deployed
187
+ agent_config.source_type = "local"
188
+ agent_config.display_name = display_name
189
+
190
+ agents.append(agent_config)
191
+
184
192
  except (json.JSONDecodeError, KeyError) as e:
185
193
  # Log malformed templates but continue
186
194
  self.logger.debug(
@@ -228,7 +236,7 @@ class SimpleAgentManager:
228
236
  # Create AgentConfig with source information
229
237
  # Store full agent_dict for later use in deployment
230
238
  agent_config = AgentConfig(
231
- name=agent_id, # Use agent_id as name for uniqueness
239
+ name=name, # Use display name for UI
232
240
  description=(
233
241
  f"[{category}] {description[:60]}..."
234
242
  if len(description) > 60
@@ -241,6 +249,7 @@ class SimpleAgentManager:
241
249
  agent_config.source_type = "remote"
242
250
  agent_config.is_deployed = is_deployed
243
251
  agent_config.display_name = name
252
+ agent_config.agent_id = agent_id # Store technical ID for reference
244
253
  agent_config.full_agent_id = agent_id
245
254
  agent_config.source_dict = agent_dict # Store full dict for deployment
246
255
 
@@ -256,7 +265,7 @@ class SimpleAgentManager:
256
265
  return []
257
266
 
258
267
  def _is_agent_deployed(self, agent_id: str) -> bool:
259
- """Check if agent is deployed in current project or user directory.
268
+ """Check if agent is deployed (virtual deployment or physical files).
260
269
 
261
270
  Args:
262
271
  agent_id: Full agent ID (may include hierarchy like engineer/backend/python-engineer)
@@ -264,6 +273,46 @@ class SimpleAgentManager:
264
273
  Returns:
265
274
  True if agent is deployed, False otherwise
266
275
  """
276
+ # Check virtual deployment state (primary method)
277
+ # Only checking project-level deployment in simplified architecture
278
+ deployment_state_paths = [
279
+ Path.cwd() / ".claude" / "agents" / ".mpm_deployment_state",
280
+ ]
281
+
282
+ for state_path in deployment_state_paths:
283
+ if state_path.exists():
284
+ try:
285
+ with state_path.open() as f:
286
+ state = json.load(f)
287
+
288
+ # Check if agent is in deployment state
289
+ agents = state.get("last_check_results", {}).get("agents", {})
290
+
291
+ # Check full agent_id
292
+ if agent_id in agents:
293
+ self.logger.debug(
294
+ f"Agent {agent_id} found in virtual deployment state"
295
+ )
296
+ return True
297
+
298
+ # Check leaf name for hierarchical IDs
299
+ if "/" in agent_id:
300
+ leaf_name = agent_id.split("/")[-1]
301
+ if leaf_name in agents:
302
+ self.logger.debug(
303
+ f"Agent {agent_id} (leaf: {leaf_name}) found in virtual deployment state"
304
+ )
305
+ return True
306
+ except (json.JSONDecodeError, KeyError) as e:
307
+ self.logger.debug(
308
+ f"Failed to read deployment state from {state_path}: {e}"
309
+ )
310
+ continue
311
+ except Exception as e:
312
+ self.logger.debug(f"Unexpected error reading deployment state: {e}")
313
+ continue
314
+
315
+ # Fallback to physical file checks (legacy support)
267
316
  # For hierarchical IDs, check both full ID and leaf name
268
317
  agent_file_names = [f"{agent_id}.md"]
269
318
 
@@ -272,20 +321,15 @@ class SimpleAgentManager:
272
321
  leaf_name = agent_id.split("/")[-1]
273
322
  agent_file_names.append(f"{leaf_name}.md")
274
323
 
275
- # Check .claude-mpm/agents/ directory (project level)
276
- project_agents_dir = Path.cwd() / ".claude-mpm" / "agents"
324
+ # Check .claude/agents/ directory (project deployment)
325
+ project_agents_dir = Path.cwd() / ".claude" / "agents"
277
326
  if project_agents_dir.exists():
278
327
  for agent_file_name in agent_file_names:
279
328
  agent_file = project_agents_dir / agent_file_name
280
329
  if agent_file.exists():
281
- return True
282
-
283
- # Check ~/.claude/agents/ directory (user level)
284
- user_agents_dir = Path.home() / ".claude" / "agents"
285
- if user_agents_dir.exists():
286
- for agent_file_name in agent_file_names:
287
- agent_file = user_agents_dir / agent_file_name
288
- if agent_file.exists():
330
+ self.logger.debug(
331
+ f"Agent {agent_id} found as physical file: {agent_file}"
332
+ )
289
333
  return True
290
334
 
291
335
  return False
@@ -167,6 +167,16 @@ class AgentsCommand(AgentCommand):
167
167
  "available": self._list_available_from_sources,
168
168
  # Agent discovery with rich filtering (Phase 1: Discovery & Browsing)
169
169
  "discover": self._discover_agents,
170
+ # NEW: Collection-based agent management
171
+ "list-collections": self._list_collections,
172
+ "deploy-collection": self._deploy_collection,
173
+ "list-by-collection": self._list_by_collection,
174
+ # Cache git management commands
175
+ "cache-status": self._cache_status,
176
+ "cache-pull": self._cache_pull,
177
+ "cache-push": self._cache_push,
178
+ "cache-sync": self._cache_sync,
179
+ "cache-commit": self._cache_commit,
170
180
  }
171
181
 
172
182
  if args.agents_command in command_map:
@@ -551,34 +561,63 @@ class AgentsCommand(AgentCommand):
551
561
  return CommandResult.error_result(f"Error discovering agents: {e}")
552
562
 
553
563
  def _deploy_agents(self, args, force=False) -> CommandResult:
554
- """Deploy both system and project agents, or deploy by preset."""
564
+ """Deploy agents using two-phase sync: cache deploy.
565
+
566
+ Phase 3 Integration (1M-486): Uses Git sync service for deployment.
567
+ - Phase 1: Sync agents to ~/.claude-mpm/cache/remote-agents/ (if needed)
568
+ - Phase 2: Deploy from cache to project .claude-mpm/agents/
569
+
570
+ This replaces the old single-tier deployment with a multi-project
571
+ architecture where one cache serves multiple project deployments.
572
+ """
555
573
  try:
556
- # Handle preset deployment
574
+ # Handle preset deployment (uses different path)
557
575
  if hasattr(args, "preset") and args.preset:
558
576
  return self._deploy_preset(args)
559
577
 
560
- # Deploy system agents
561
- system_result = self.deployment_service.deploy_system_agents(force=force)
578
+ from ...services.agents.sources.git_source_sync_service import (
579
+ GitSourceSyncService,
580
+ )
581
+
582
+ # Initialize git sync service
583
+ git_sync = GitSourceSyncService()
584
+ project_dir = Path.cwd()
585
+
586
+ self.logger.info("Phase 1: Syncing agents to cache...")
587
+
588
+ # Sync to cache (downloads from GitHub if needed)
589
+ sync_result = git_sync.sync_repository(force=force)
590
+
591
+ if not sync_result.get("synced"):
592
+ error_msg = sync_result.get("error", "Unknown sync error")
593
+ self.logger.error(f"Sync failed: {error_msg}")
594
+ return CommandResult.error_result(f"Sync failed: {error_msg}")
595
+
596
+ self.logger.info(
597
+ f"Phase 1 complete: {sync_result.get('agent_count', 0)} agents in cache"
598
+ )
599
+ self.logger.info(f"Phase 2: Deploying agents to {project_dir}...")
562
600
 
563
- # Deploy project agents if they exist
564
- project_result = self.deployment_service.deploy_project_agents(force=force)
601
+ # Deploy from cache to project directory
602
+ deploy_result = git_sync.deploy_agents_to_project(
603
+ project_dir=project_dir,
604
+ agent_list=None, # Deploy all cached agents
605
+ force=force,
606
+ )
565
607
 
566
- # Combine results
608
+ # Format combined results for output
567
609
  combined_result = {
568
- "deployed_count": system_result.get("deployed_count", 0)
569
- + project_result.get("deployed_count", 0),
570
- "deployed": system_result.get("deployed", [])
571
- + project_result.get("deployed", []),
572
- "updated_count": system_result.get("updated_count", 0)
573
- + project_result.get("updated_count", 0),
574
- "updated": system_result.get("updated", [])
575
- + project_result.get("updated", []),
576
- "skipped": system_result.get("skipped", [])
577
- + project_result.get("skipped", []),
578
- "errors": system_result.get("errors", [])
579
- + project_result.get("errors", []),
580
- "target_dir": system_result.get("target_dir")
581
- or project_result.get("target_dir"),
610
+ "deployed_count": len(deploy_result.get("deployed", []))
611
+ + len(deploy_result.get("updated", [])),
612
+ "deployed": deploy_result.get("deployed", []),
613
+ "updated": deploy_result.get("updated", []),
614
+ "skipped": deploy_result.get("skipped", []),
615
+ "errors": deploy_result.get("failed", []),
616
+ "target_dir": deploy_result.get("deployment_dir", ""),
617
+ "sync_info": {
618
+ "cached_agents": sync_result.get("agent_count", 0),
619
+ "cache_dir": sync_result.get("cache_dir", ""),
620
+ },
582
621
  }
583
622
 
584
623
  output_format = self._get_output_format(args)
@@ -589,12 +628,15 @@ class AgentsCommand(AgentCommand):
589
628
  )
590
629
  print(formatted)
591
630
 
631
+ success_count = len(deploy_result["deployed"]) + len(
632
+ deploy_result["updated"]
633
+ )
592
634
  return CommandResult.success_result(
593
- f"Deployed {combined_result['deployed_count']} agents",
635
+ f"Deployed {success_count} agents from cache",
594
636
  data={
595
- "system_agents": system_result,
596
- "project_agents": project_result,
597
- "total_deployed": combined_result["deployed_count"],
637
+ "sync_result": sync_result,
638
+ "deploy_result": deploy_result,
639
+ "total_deployed": success_count,
598
640
  },
599
641
  )
600
642
 
@@ -2119,6 +2161,385 @@ class AgentsCommand(AgentCommand):
2119
2161
  self.logger.error(f"Error in auto-configure: {e}", exc_info=True)
2120
2162
  return CommandResult.error_result(f"Error in auto-configure: {e}")
2121
2163
 
2164
+ def _list_collections(self, args) -> CommandResult:
2165
+ """List all available agent collections.
2166
+
2167
+ NEW: Shows all collections with agent counts and metadata.
2168
+ Enables discovery of available agent collections before deployment.
2169
+ """
2170
+ try:
2171
+ from pathlib import Path
2172
+
2173
+ from ...services.agents.deployment.remote_agent_discovery_service import (
2174
+ RemoteAgentDiscoveryService,
2175
+ )
2176
+
2177
+ # Get remote agents cache directory
2178
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
2179
+
2180
+ if not cache_dir.exists():
2181
+ return CommandResult.error_result(
2182
+ "No remote agent collections found. Run 'claude-mpm agents deploy' first."
2183
+ )
2184
+
2185
+ # Use RemoteAgentDiscoveryService to list collections
2186
+ remote_service = RemoteAgentDiscoveryService(cache_dir)
2187
+ collections = remote_service.list_collections()
2188
+
2189
+ if not collections:
2190
+ return CommandResult.success_result(
2191
+ "No agent collections found in cache.", data={"collections": []}
2192
+ )
2193
+
2194
+ # Format output
2195
+ output_lines = ["Available Agent Collections:\n"]
2196
+ for collection in collections:
2197
+ output_lines.append(
2198
+ f" • {collection['collection_id']} ({collection['agent_count']} agents)"
2199
+ )
2200
+
2201
+ return CommandResult.success_result(
2202
+ "\n".join(output_lines), data={"collections": collections}
2203
+ )
2204
+
2205
+ except Exception as e:
2206
+ self.logger.error(f"Error listing collections: {e}", exc_info=True)
2207
+ return CommandResult.error_result(f"Error listing collections: {e}")
2208
+
2209
+ def _deploy_collection(self, args) -> CommandResult:
2210
+ """Deploy all agents from a specific collection.
2211
+
2212
+ NEW: Enables bulk deployment of all agents from a named collection.
2213
+ Useful for deploying entire agent sets at once.
2214
+ """
2215
+ try:
2216
+ from pathlib import Path
2217
+
2218
+ from ...services.agents.deployment.multi_source_deployment_service import (
2219
+ MultiSourceAgentDeploymentService,
2220
+ )
2221
+
2222
+ collection_id = args.collection_id
2223
+
2224
+ # Get agents from collection
2225
+ service = MultiSourceAgentDeploymentService()
2226
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
2227
+ agents = service.get_agents_by_collection(collection_id, cache_dir)
2228
+
2229
+ if not agents:
2230
+ return CommandResult.error_result(
2231
+ f"No agents found in collection '{collection_id}'"
2232
+ )
2233
+
2234
+ # Dry run mode
2235
+ if getattr(args, "dry_run", False):
2236
+ agent_names = [
2237
+ agent.get("metadata", {}).get("name", "Unknown") for agent in agents
2238
+ ]
2239
+ output = f"Would deploy {len(agents)} agents from collection '{collection_id}':\n"
2240
+ for name in agent_names:
2241
+ output += f" • {name}\n"
2242
+ return CommandResult.success_result(
2243
+ output,
2244
+ data={"collection_id": collection_id, "agent_count": len(agents)},
2245
+ )
2246
+
2247
+ # Deploy agents
2248
+ # TODO: Implement actual deployment logic using deployment service
2249
+ # For now, show what would be deployed
2250
+ return CommandResult.success_result(
2251
+ f"Deployment of collection '{collection_id}' would deploy {len(agents)} agents.\n"
2252
+ f"(Full deployment implementation pending)",
2253
+ data={
2254
+ "collection_id": collection_id,
2255
+ "agent_count": len(agents),
2256
+ "status": "pending_implementation",
2257
+ },
2258
+ )
2259
+
2260
+ except Exception as e:
2261
+ self.logger.error(f"Error deploying collection: {e}", exc_info=True)
2262
+ return CommandResult.error_result(f"Error deploying collection: {e}")
2263
+
2264
+ def _list_by_collection(self, args) -> CommandResult:
2265
+ """List agents from a specific collection.
2266
+
2267
+ NEW: Shows detailed information about agents in a collection.
2268
+ Supports multiple output formats (table, json, yaml).
2269
+ """
2270
+ try:
2271
+ import json as json_lib
2272
+ from pathlib import Path
2273
+
2274
+ from ...services.agents.deployment.multi_source_deployment_service import (
2275
+ MultiSourceAgentDeploymentService,
2276
+ )
2277
+
2278
+ collection_id = args.collection_id
2279
+ output_format = getattr(args, "format", "table")
2280
+
2281
+ # Get agents from collection
2282
+ service = MultiSourceAgentDeploymentService()
2283
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
2284
+ agents = service.get_agents_by_collection(collection_id, cache_dir)
2285
+
2286
+ if not agents:
2287
+ return CommandResult.error_result(
2288
+ f"No agents found in collection '{collection_id}'"
2289
+ )
2290
+
2291
+ # Format output based on requested format
2292
+ if output_format == "json":
2293
+ return CommandResult.success_result(
2294
+ json_lib.dumps(agents, indent=2),
2295
+ data={"collection_id": collection_id, "agents": agents},
2296
+ )
2297
+ if output_format == "yaml":
2298
+ try:
2299
+ import yaml
2300
+
2301
+ return CommandResult.success_result(
2302
+ yaml.dump(agents, default_flow_style=False),
2303
+ data={"collection_id": collection_id, "agents": agents},
2304
+ )
2305
+ except ImportError:
2306
+ return CommandResult.error_result(
2307
+ "YAML support not available (install PyYAML)"
2308
+ )
2309
+
2310
+ # Table format (default)
2311
+ output_lines = [f"Agents in collection '{collection_id}':\n"]
2312
+ for agent in agents:
2313
+ metadata = agent.get("metadata", {})
2314
+ name = metadata.get("name", "Unknown")
2315
+ description = metadata.get("description", "No description")
2316
+ version = agent.get("version", "unknown")
2317
+ output_lines.append(f" • {name} (v{version})")
2318
+ output_lines.append(f" {description}\n")
2319
+
2320
+ return CommandResult.success_result(
2321
+ "\n".join(output_lines),
2322
+ data={"collection_id": collection_id, "agent_count": len(agents)},
2323
+ )
2324
+
2325
+ except Exception as e:
2326
+ self.logger.error(f"Error listing collection agents: {e}", exc_info=True)
2327
+ return CommandResult.error_result(f"Error listing collection agents: {e}")
2328
+
2329
+ def _cache_status(self, args) -> CommandResult:
2330
+ """Show git status of agent cache.
2331
+
2332
+ Displays current branch, uncommitted changes, unpushed commits, and
2333
+ remote URL for the agent cache repository.
2334
+ """
2335
+ try:
2336
+ from ...services.agents.cache_git_manager import CacheGitManager
2337
+
2338
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
2339
+ manager = CacheGitManager(cache_dir)
2340
+
2341
+ if not manager.is_git_repo():
2342
+ print("❌ Cache is not a git repository")
2343
+ print(f"\nCache location: {cache_dir}")
2344
+ print(
2345
+ "\n💡 This is expected if you haven't cloned the agents repository."
2346
+ )
2347
+ print(" The cache will be managed via HTTP sync instead.")
2348
+ return CommandResult.error_result("Cache is not a git repository")
2349
+
2350
+ status = manager.get_status()
2351
+ output_format = self._get_output_format(args)
2352
+
2353
+ if self._is_structured_format(output_format):
2354
+ formatted = (
2355
+ self._formatter.format_as_json(status)
2356
+ if str(output_format).lower() == OutputFormat.JSON
2357
+ else self._formatter.format_as_yaml(status)
2358
+ )
2359
+ print(formatted)
2360
+ return CommandResult.success_result(
2361
+ "Cache status retrieved", data=status
2362
+ )
2363
+
2364
+ # Text output
2365
+ print(f"\n📁 Cache: {manager.repo_path}")
2366
+ print(f"🌿 Branch: {status.get('branch', 'unknown')}")
2367
+
2368
+ if status.get("remote_url"):
2369
+ print(f"🔗 Remote: {status['remote_url']}")
2370
+
2371
+ # Show sync status
2372
+ ahead = status.get("ahead", 0)
2373
+ behind = status.get("behind", 0)
2374
+
2375
+ if ahead > 0:
2376
+ print(f"📤 Ahead of remote: {ahead} commit(s)")
2377
+ if behind > 0:
2378
+ print(f"📥 Behind remote: {behind} commit(s)")
2379
+
2380
+ if ahead == 0 and behind == 0:
2381
+ print("✅ In sync with remote")
2382
+
2383
+ # Show uncommitted changes
2384
+ uncommitted = status.get("uncommitted", [])
2385
+ if uncommitted:
2386
+ print(f"\n⚠️ Uncommitted changes: {len(uncommitted)}")
2387
+ for file in uncommitted[:10]: # Show max 10 files
2388
+ print(f" - {file}")
2389
+ if len(uncommitted) > 10:
2390
+ print(f" ... and {len(uncommitted) - 10} more")
2391
+ else:
2392
+ print("\n✅ No uncommitted changes")
2393
+
2394
+ # Overall status
2395
+ if status.get("is_clean"):
2396
+ print("\n✨ Cache is clean and up-to-date")
2397
+ else:
2398
+ print("\n💡 Run 'claude-mpm agents cache-sync' to sync with remote")
2399
+
2400
+ return CommandResult.success_result("Cache status displayed", data=status)
2401
+
2402
+ except Exception as e:
2403
+ self.logger.error(f"Error getting cache status: {e}", exc_info=True)
2404
+ return CommandResult.error_result(f"Error getting cache status: {e}")
2405
+
2406
+ def _cache_pull(self, args) -> CommandResult:
2407
+ """Pull latest agents from remote repository."""
2408
+ try:
2409
+ from ...services.agents.cache_git_manager import CacheGitManager
2410
+
2411
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
2412
+ manager = CacheGitManager(cache_dir)
2413
+
2414
+ if not manager.is_git_repo():
2415
+ print("❌ Cache is not a git repository")
2416
+ return CommandResult.error_result("Cache is not a git repository")
2417
+
2418
+ branch = getattr(args, "branch", "main")
2419
+ print(f"🔄 Pulling latest changes from {branch}...")
2420
+
2421
+ success, msg = manager.pull_latest(branch)
2422
+
2423
+ if success:
2424
+ print(f"✅ {msg}")
2425
+ return CommandResult.success_result(msg)
2426
+ print(f"❌ {msg}")
2427
+ return CommandResult.error_result(msg)
2428
+
2429
+ except Exception as e:
2430
+ self.logger.error(f"Error pulling cache: {e}", exc_info=True)
2431
+ return CommandResult.error_result(f"Error pulling cache: {e}")
2432
+
2433
+ def _cache_commit(self, args) -> CommandResult:
2434
+ """Commit changes to cache repository."""
2435
+ try:
2436
+ from ...services.agents.cache_git_manager import CacheGitManager
2437
+
2438
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
2439
+ manager = CacheGitManager(cache_dir)
2440
+
2441
+ if not manager.is_git_repo():
2442
+ print("❌ Cache is not a git repository")
2443
+ return CommandResult.error_result("Cache is not a git repository")
2444
+
2445
+ # Get commit message from args
2446
+ message = getattr(args, "message", None)
2447
+ if not message:
2448
+ # Default message
2449
+ message = "feat: update agents from local development"
2450
+
2451
+ print("💾 Committing changes...")
2452
+ success, msg = manager.commit_changes(message)
2453
+
2454
+ if success:
2455
+ print(f"✅ {msg}")
2456
+ print(f"\n💡 Commit message: {message}")
2457
+ return CommandResult.success_result(msg)
2458
+ print(f"❌ {msg}")
2459
+ return CommandResult.error_result(msg)
2460
+
2461
+ except Exception as e:
2462
+ self.logger.error(f"Error committing cache changes: {e}", exc_info=True)
2463
+ return CommandResult.error_result(f"Error committing cache changes: {e}")
2464
+
2465
+ def _cache_push(self, args) -> CommandResult:
2466
+ """Push local agent changes to remote."""
2467
+ try:
2468
+ from ...services.agents.cache_git_manager import CacheGitManager
2469
+
2470
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
2471
+ manager = CacheGitManager(cache_dir)
2472
+
2473
+ if not manager.is_git_repo():
2474
+ print("❌ Cache is not a git repository")
2475
+ return CommandResult.error_result("Cache is not a git repository")
2476
+
2477
+ # Check for uncommitted changes
2478
+ if manager.has_uncommitted_changes():
2479
+ print("⚠️ You have uncommitted changes.")
2480
+ print("\n💡 Commit changes first with:")
2481
+ print(" claude-mpm agents cache-commit --message 'your message'")
2482
+
2483
+ # Ask if user wants to commit first
2484
+ auto_commit = getattr(args, "auto_commit", False)
2485
+ if auto_commit:
2486
+ print("\n📝 Auto-committing changes...")
2487
+ success, msg = manager.commit_changes("feat: update agents")
2488
+ if not success:
2489
+ print(f"❌ Commit failed: {msg}")
2490
+ return CommandResult.error_result(f"Commit failed: {msg}")
2491
+ print(f"✅ {msg}")
2492
+ else:
2493
+ return CommandResult.error_result(
2494
+ "Uncommitted changes detected. Commit first or use --auto-commit"
2495
+ )
2496
+
2497
+ # Push changes
2498
+ branch = getattr(args, "branch", "main")
2499
+ print(f"📤 Pushing changes to {branch}...")
2500
+
2501
+ success, msg = manager.push_changes(branch)
2502
+
2503
+ if success:
2504
+ print(f"✅ {msg}")
2505
+ return CommandResult.success_result(msg)
2506
+ print(f"❌ {msg}")
2507
+ return CommandResult.error_result(msg)
2508
+
2509
+ except Exception as e:
2510
+ self.logger.error(f"Error pushing cache: {e}", exc_info=True)
2511
+ return CommandResult.error_result(f"Error pushing cache: {e}")
2512
+
2513
+ def _cache_sync(self, args) -> CommandResult:
2514
+ """Full cache sync: pull, commit (if needed), push."""
2515
+ try:
2516
+ from ...services.agents.cache_git_manager import CacheGitManager
2517
+
2518
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
2519
+ manager = CacheGitManager(cache_dir)
2520
+
2521
+ if not manager.is_git_repo():
2522
+ print("❌ Cache is not a git repository")
2523
+ return CommandResult.error_result("Cache is not a git repository")
2524
+
2525
+ print("🔄 Syncing cache with remote...\n")
2526
+
2527
+ success, msg = manager.sync_with_remote()
2528
+
2529
+ # Print detailed sync message
2530
+ print(msg)
2531
+
2532
+ if success:
2533
+ print("\n✨ Cache sync complete!")
2534
+ return CommandResult.success_result("Cache synced successfully")
2535
+
2536
+ print("\n❌ Cache sync failed. See details above.")
2537
+ return CommandResult.error_result("Cache sync failed")
2538
+
2539
+ except Exception as e:
2540
+ self.logger.error(f"Error syncing cache: {e}", exc_info=True)
2541
+ return CommandResult.error_result(f"Error syncing cache: {e}")
2542
+
2122
2543
 
2123
2544
  def manage_agents(args):
2124
2545
  """