claude-mpm 5.4.3__py3-none-any.whl → 5.4.14__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 (76) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/PM_INSTRUCTIONS.md +39 -0
  4. claude_mpm/agents/agent_loader.py +3 -27
  5. claude_mpm/cli/__main__.py +4 -0
  6. claude_mpm/cli/commands/auto_configure.py +210 -25
  7. claude_mpm/cli/commands/config.py +88 -2
  8. claude_mpm/cli/commands/configure.py +85 -43
  9. claude_mpm/cli/commands/configure_agent_display.py +3 -1
  10. claude_mpm/cli/commands/mpm_init/core.py +2 -45
  11. claude_mpm/cli/commands/skills.py +21 -2
  12. claude_mpm/cli/executor.py +3 -3
  13. claude_mpm/cli/parsers/config_parser.py +153 -83
  14. claude_mpm/cli/parsers/skills_parser.py +3 -2
  15. claude_mpm/cli/startup.py +273 -36
  16. claude_mpm/commands/mpm-config.md +266 -0
  17. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  18. claude_mpm/core/framework_loader.py +4 -2
  19. claude_mpm/core/logger.py +13 -0
  20. claude_mpm/hooks/claude_hooks/event_handlers.py +171 -76
  21. claude_mpm/hooks/claude_hooks/hook_handler.py +2 -0
  22. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  23. claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
  24. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  25. claude_mpm/hooks/memory_integration_hook.py +46 -1
  26. claude_mpm/init.py +0 -19
  27. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  28. claude_mpm/services/agents/agent_recommendation_service.py +6 -7
  29. claude_mpm/services/agents/agent_review_service.py +280 -0
  30. claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -3
  31. claude_mpm/services/agents/deployment/agent_template_builder.py +1 -0
  32. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +78 -9
  33. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +13 -0
  34. claude_mpm/services/agents/git_source_manager.py +14 -0
  35. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  36. claude_mpm/services/agents/toolchain_detector.py +6 -3
  37. claude_mpm/services/command_deployment_service.py +71 -8
  38. claude_mpm/services/git/git_operations_service.py +93 -8
  39. claude_mpm/services/self_upgrade_service.py +120 -12
  40. claude_mpm/services/skills/__init__.py +3 -0
  41. claude_mpm/services/skills/git_skill_source_manager.py +32 -2
  42. claude_mpm/services/skills/selective_skill_deployer.py +230 -0
  43. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  44. claude_mpm/services/skills_deployer.py +64 -3
  45. {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.14.dist-info}/METADATA +47 -8
  46. {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.14.dist-info}/RECORD +51 -70
  47. {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.14.dist-info}/entry_points.txt +0 -3
  48. claude_mpm-5.4.14.dist-info/licenses/LICENSE +94 -0
  49. claude_mpm-5.4.14.dist-info/licenses/LICENSE-FAQ.md +153 -0
  50. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  51. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  52. claude_mpm/agents/BASE_ENGINEER.md +0 -658
  53. claude_mpm/agents/BASE_OPS.md +0 -219
  54. claude_mpm/agents/BASE_PM.md +0 -480
  55. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  56. claude_mpm/agents/BASE_QA.md +0 -167
  57. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  58. claude_mpm/agents/base_agent.json +0 -31
  59. claude_mpm/agents/base_agent_loader.py +0 -601
  60. claude_mpm/cli/ticket_cli.py +0 -35
  61. claude_mpm/commands/mpm-config-view.md +0 -150
  62. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  63. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-313.pyc +0 -0
  64. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  65. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  66. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  67. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  68. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  69. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  70. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  71. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  72. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  73. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  74. claude_mpm-5.4.3.dist-info/licenses/LICENSE +0 -21
  75. {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.14.dist-info}/WHEEL +0 -0
  76. {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.14.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION CHANGED
@@ -1 +1 @@
1
- 5.4.3
1
+ 5.4.14
claude_mpm/__init__.py CHANGED
@@ -1,3 +1,7 @@
1
+ # Copyright (c) 2024-2025 Bob Matsuoka
2
+ # Licensed under the Elastic License 2.0
3
+ # See LICENSE file in the project root for full license information.
4
+
1
5
  """Claude MPM - Multi-Agent Project Manager."""
2
6
 
3
7
  from pathlib import Path
@@ -1343,6 +1343,45 @@ PM: "I notice this is a FastAPI project. Would you like me to run auto-configura
1343
1343
  - User choice: Always respect if user prefers manual configuration
1344
1344
  - Preview first: Recommend --preview flag for first-time users
1345
1345
 
1346
+ ## Proactive Architecture Improvement Suggestions
1347
+
1348
+ **When agents report opportunities, PM suggests improvements to user.**
1349
+
1350
+ ### Trigger Conditions
1351
+ - Research/Code Analyzer reports code smells, anti-patterns, or structural issues
1352
+ - Engineer reports implementation difficulty due to architecture
1353
+ - Repeated similar issues suggest systemic problems
1354
+
1355
+ ### Suggestion Format
1356
+ ```
1357
+ 💡 Architecture Suggestion
1358
+
1359
+ [Agent] identified [specific issue].
1360
+
1361
+ Consider: [improvement] — [one-line benefit]
1362
+ Effort: [small/medium/large]
1363
+
1364
+ Want me to implement this?
1365
+ ```
1366
+
1367
+ ### Example
1368
+ ```
1369
+ 💡 Architecture Suggestion
1370
+
1371
+ Research found database queries scattered across 12 files.
1372
+
1373
+ Consider: Repository pattern — centralized queries, easier testing
1374
+ Effort: Medium
1375
+
1376
+ Want me to implement this?
1377
+ ```
1378
+
1379
+ ### Rules
1380
+ - Max 1-2 suggestions per session
1381
+ - Don't repeat declined suggestions
1382
+ - If accepted: delegate to Research → Code Analyzer → Engineer (standard workflow)
1383
+ - Be specific, not vague ("Repository pattern" not "better architecture")
1384
+
1346
1385
  ## PM Examples: Correct Delegation Patterns
1347
1386
 
1348
1387
  ### Example 1: Bug Fixing Workflow
@@ -49,25 +49,9 @@ from claude_mpm.services.memory.cache.shared_prompt_cache import SharedPromptCac
49
49
 
50
50
  from ..core.agent_name_normalizer import AgentNameNormalizer
51
51
 
52
- # Lazy import for base_agent_loader to reduce initialization overhead
53
- # base_agent_loader adds ~500ms to import time and is only needed when
54
- # actually loading agent prompts, not during module import
55
- # from .base_agent_loader import prepend_base_instructions
56
-
57
52
  logger = get_logger(__name__)
58
53
 
59
54
 
60
- def _get_prepend_base_instructions():
61
- """Lazy loader for prepend_base_instructions function.
62
-
63
- This defers the import of base_agent_loader until it's actually needed,
64
- reducing hook handler initialization time by ~500ms.
65
- """
66
- from .base_agent_loader import prepend_base_instructions
67
-
68
- return prepend_base_instructions
69
-
70
-
71
55
  class ModelType(str, Enum):
72
56
  """Claude model types for agent configuration."""
73
57
 
@@ -248,9 +232,7 @@ class AgentLoader:
248
232
  logger.warning(f"Agent '{agent_id}' has no instructions")
249
233
  return None
250
234
 
251
- # Prepend base instructions (lazy load to avoid import overhead)
252
- prepend_base_instructions = _get_prepend_base_instructions()
253
- return prepend_base_instructions(instructions)
235
+ return instructions
254
236
 
255
237
  def get_agent_metadata(self, agent_id: str) -> Optional[Dict[str, Any]]:
256
238
  """
@@ -796,16 +778,10 @@ def get_agent_prompt(
796
778
  model_metadata = f"\n<!-- Model Selection: {selected_model} (Complexity: {model_config.get('complexity_level', 'UNKNOWN')}) -->\n"
797
779
  prompt = model_metadata + prompt
798
780
 
799
- # Prepend base instructions with dynamic template based on complexity
800
- # The base instructions provide common guidelines all agents should follow
801
- complexity_score = model_config.get("complexity_score", 50) if model_config else 50
802
- prepend_base_instructions = _get_prepend_base_instructions()
803
- final_prompt = prepend_base_instructions(prompt, complexity_score=complexity_score)
804
-
805
781
  # Return format based on caller's needs
806
782
  if return_model_info:
807
- return final_prompt, selected_model, model_config
808
- return final_prompt
783
+ return prompt, selected_model, model_config
784
+ return prompt
809
785
 
810
786
 
811
787
  # Legacy hardcoded agent functions removed - use get_agent_prompt(agent_id) instead
@@ -1,3 +1,7 @@
1
+ # Copyright (c) 2024-2025 Bob Matsuoka
2
+ # Licensed under the Elastic License 2.0
3
+ # See LICENSE file in the project root for full license information.
4
+
1
5
  """
2
6
  Entry point for executing the CLI module with python -m claude_mpm.cli.
3
7
 
@@ -21,7 +21,6 @@ from typing import Optional
21
21
 
22
22
  try:
23
23
  from rich.console import Console
24
- from rich.panel import Panel
25
24
  from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
26
25
  from rich.table import Table
27
26
 
@@ -267,6 +266,15 @@ class AutoConfigureCommand(BaseCommand):
267
266
  project_path, min_confidence
268
267
  )
269
268
 
269
+ # Review existing project agents
270
+ agent_review_results = None
271
+ if configure_agents:
272
+ if self.console and not json_output:
273
+ with self.console.status("[bold green]Reviewing existing agents..."):
274
+ agent_review_results = self._review_project_agents(agent_preview)
275
+ else:
276
+ agent_review_results = self._review_project_agents(agent_preview)
277
+
270
278
  # Get skills recommendations
271
279
  skills_recommendations = None
272
280
  if configure_skills:
@@ -283,9 +291,14 @@ class AutoConfigureCommand(BaseCommand):
283
291
  skills_recommendations,
284
292
  configure_agents,
285
293
  configure_skills,
294
+ agent_review_results,
286
295
  )
287
296
  return self._display_preview(
288
- agent_preview, skills_recommendations, configure_agents, configure_skills
297
+ agent_preview,
298
+ skills_recommendations,
299
+ configure_agents,
300
+ configure_skills,
301
+ agent_review_results,
289
302
  )
290
303
 
291
304
  def _run_full_configuration(
@@ -311,6 +324,15 @@ class AutoConfigureCommand(BaseCommand):
311
324
  project_path, min_confidence
312
325
  )
313
326
 
327
+ # Review existing project agents
328
+ agent_review_results = None
329
+ if configure_agents:
330
+ if self.console and not json_output:
331
+ with self.console.status("[bold green]Reviewing existing agents..."):
332
+ agent_review_results = self._review_project_agents(agent_preview)
333
+ else:
334
+ agent_review_results = self._review_project_agents(agent_preview)
335
+
314
336
  # Get skills recommendations
315
337
  skills_recommendations = None
316
338
  if configure_skills:
@@ -327,6 +349,7 @@ class AutoConfigureCommand(BaseCommand):
327
349
  skills_recommendations,
328
350
  configure_agents,
329
351
  configure_skills,
352
+ agent_review_results,
330
353
  )
331
354
 
332
355
  # Ask for confirmation (unless skipped)
@@ -336,6 +359,7 @@ class AutoConfigureCommand(BaseCommand):
336
359
  skills_recommendations,
337
360
  configure_agents,
338
361
  configure_skills,
362
+ agent_review_results,
339
363
  ):
340
364
  if self.console:
341
365
  self.console.print("\n❌ Operation cancelled by user")
@@ -343,6 +367,17 @@ class AutoConfigureCommand(BaseCommand):
343
367
  print("\nOperation cancelled by user")
344
368
  return CommandResult.error_result("Operation cancelled", exit_code=0)
345
369
 
370
+ # Archive unused agents (before deploying new ones)
371
+ archive_result = None
372
+ if configure_agents and agent_review_results:
373
+ agents_to_archive = agent_review_results.get("unused", [])
374
+ if agents_to_archive:
375
+ if self.console and not json_output:
376
+ self.console.print(
377
+ "\n[bold yellow]Archiving unused agents...[/bold yellow]\n"
378
+ )
379
+ archive_result = self._archive_agents(agents_to_archive)
380
+
346
381
  # Execute agent configuration
347
382
  agent_result = None
348
383
  if configure_agents and agent_preview:
@@ -368,8 +403,8 @@ class AutoConfigureCommand(BaseCommand):
368
403
 
369
404
  # Output results
370
405
  if json_output:
371
- return self._output_result_json(agent_result, skills_result)
372
- return self._display_result(agent_result, skills_result)
406
+ return self._output_result_json(agent_result, skills_result, archive_result)
407
+ return self._display_result(agent_result, skills_result, archive_result)
373
408
 
374
409
  def _display_preview(
375
410
  self,
@@ -377,6 +412,7 @@ class AutoConfigureCommand(BaseCommand):
377
412
  skills_recommendations=None,
378
413
  configure_agents=True,
379
414
  configure_skills=True,
415
+ agent_review_results=None,
380
416
  ) -> CommandResult:
381
417
  """Display configuration preview with Rich formatting."""
382
418
  if not self.console:
@@ -386,6 +422,7 @@ class AutoConfigureCommand(BaseCommand):
386
422
  skills_recommendations,
387
423
  configure_agents,
388
424
  configure_skills,
425
+ agent_review_results,
389
426
  )
390
427
 
391
428
  # Only show toolchain and agents if configuring agents
@@ -457,6 +494,10 @@ class AutoConfigureCommand(BaseCommand):
457
494
  f" {severity_icon} {issue.message}", style="yellow"
458
495
  )
459
496
 
497
+ # Display agent review results
498
+ if configure_agents and agent_review_results:
499
+ self._display_agent_review(agent_review_results)
500
+
460
501
  # Display recommended skills
461
502
  if configure_skills and skills_recommendations:
462
503
  self.console.print("\n🎯 Recommended Skills:", style="bold blue")
@@ -471,6 +512,7 @@ class AutoConfigureCommand(BaseCommand):
471
512
  skills_recommendations=None,
472
513
  configure_agents=True,
473
514
  configure_skills=True,
515
+ agent_review_results=None,
474
516
  ) -> CommandResult:
475
517
  """Display preview in plain text (fallback when Rich not available)."""
476
518
  if configure_agents and agent_preview:
@@ -517,6 +559,7 @@ class AutoConfigureCommand(BaseCommand):
517
559
  skills_recommendations=None,
518
560
  configure_agents=True,
519
561
  configure_skills=True,
562
+ agent_review_results=None,
520
563
  ) -> bool:
521
564
  """Ask user to confirm deployment."""
522
565
  has_agents = (
@@ -566,11 +609,16 @@ class AutoConfigureCommand(BaseCommand):
566
609
  return False
567
610
 
568
611
  def _display_result(
569
- self, agent_result: Optional = None, skills_result: Optional[dict] = None
612
+ self,
613
+ agent_result: Optional = None,
614
+ skills_result: Optional[dict] = None,
615
+ archive_result: Optional[dict] = None,
570
616
  ) -> CommandResult:
571
617
  """Display configuration result."""
572
618
  if not self.console:
573
- return self._display_result_plain(agent_result, skills_result)
619
+ return self._display_result_plain(
620
+ agent_result, skills_result, archive_result
621
+ )
574
622
 
575
623
  # Determine overall success
576
624
  agent_success = (
@@ -581,26 +629,11 @@ class AutoConfigureCommand(BaseCommand):
581
629
  skills_success = not skills_result or (
582
630
  skills_result and not skills_result.get("errors")
583
631
  )
584
- overall_success = agent_success and skills_success
632
+ archive_success = not archive_result or not archive_result.get("errors")
633
+ overall_success = agent_success and skills_success and archive_success
585
634
 
586
635
  # Display summary
587
636
  if overall_success:
588
- # Build summary message
589
- deployed_items = []
590
- if agent_result and agent_result.deployed_agents:
591
- deployed_items.append(f"{len(agent_result.deployed_agents)} agent(s)")
592
- if skills_result and skills_result.get("deployed"):
593
- deployed_items.append(f"{len(skills_result['deployed'])} skill(s)")
594
-
595
- panel_text = "✅ Auto-configuration completed successfully!\n\n"
596
- if deployed_items:
597
- panel_text += f"Deployed {' and '.join(deployed_items)}"
598
- else:
599
- panel_text += "No deployments needed"
600
-
601
- panel = Panel(panel_text, title="Success", border_style="green")
602
- self.console.print(panel)
603
-
604
637
  # Show deployed agents
605
638
  if agent_result and agent_result.deployed_agents:
606
639
  self.console.print("\n📦 Deployed Agents:", style="bold green")
@@ -613,6 +646,15 @@ class AutoConfigureCommand(BaseCommand):
613
646
  for skill in skills_result["deployed"]:
614
647
  self.console.print(f" ✓ {skill}")
615
648
 
649
+ # Show archived agents
650
+ if archive_result and archive_result.get("archived"):
651
+ self.console.print("\n📁 Archived Agents:", style="bold yellow")
652
+ for archived in archive_result["archived"]:
653
+ self.console.print(f" → {archived['name']}")
654
+
655
+ # Show restart notification
656
+ self._show_restart_notification(agent_result, skills_result, archive_result)
657
+
616
658
  return CommandResult.success_result()
617
659
 
618
660
  # Partial or complete failure
@@ -654,7 +696,10 @@ class AutoConfigureCommand(BaseCommand):
654
696
  )
655
697
 
656
698
  def _display_result_plain(
657
- self, agent_result: Optional = None, skills_result: Optional[dict] = None
699
+ self,
700
+ agent_result: Optional = None,
701
+ skills_result: Optional[dict] = None,
702
+ archive_result: Optional[dict] = None,
658
703
  ) -> CommandResult:
659
704
  """Display result in plain text (fallback)."""
660
705
  # Determine overall success
@@ -725,6 +770,7 @@ class AutoConfigureCommand(BaseCommand):
725
770
  skills_recommendations=None,
726
771
  configure_agents=True,
727
772
  configure_skills=True,
773
+ agent_review_results=None,
728
774
  ) -> CommandResult:
729
775
  """Output preview as JSON."""
730
776
  output = {}
@@ -790,7 +836,10 @@ class AutoConfigureCommand(BaseCommand):
790
836
  return CommandResult.success_result(data=output)
791
837
 
792
838
  def _output_result_json(
793
- self, agent_result: Optional = None, skills_result: Optional[dict] = None
839
+ self,
840
+ agent_result: Optional = None,
841
+ skills_result: Optional[dict] = None,
842
+ archive_result: Optional[dict] = None,
794
843
  ) -> CommandResult:
795
844
  """Output result as JSON."""
796
845
  output = {}
@@ -866,3 +915,139 @@ class AutoConfigureCommand(BaseCommand):
866
915
  except Exception as e:
867
916
  self.logger.error(f"Failed to deploy skills: {e}")
868
917
  return {"deployed": [], "errors": [str(e)]}
918
+
919
+ def _review_project_agents(self, agent_preview) -> Optional[dict]:
920
+ """Review existing project agents and categorize them.
921
+
922
+ Args:
923
+ agent_preview: Agent preview result with recommendations
924
+
925
+ Returns:
926
+ Dictionary with categorized agents or None if no preview
927
+ """
928
+ if not agent_preview:
929
+ return None
930
+
931
+ from ...services.agents.agent_review_service import AgentReviewService
932
+ from ...services.agents.deployment.remote_agent_discovery_service import (
933
+ RemoteAgentDiscoveryService,
934
+ )
935
+
936
+ # Get managed agents from cache
937
+ remote_agents_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
938
+ if not remote_agents_dir.exists():
939
+ self.logger.debug("No remote agents cache found")
940
+ return None
941
+
942
+ # Discover managed agents
943
+ discovery_service = RemoteAgentDiscoveryService(remote_agents_dir)
944
+ managed_agents = discovery_service.discover_remote_agents()
945
+
946
+ if not managed_agents:
947
+ self.logger.debug("No managed agents found in cache")
948
+ return None
949
+
950
+ # Get recommended agent IDs
951
+ recommended_ids = set()
952
+ if agent_preview.recommendations:
953
+ recommended_ids = {rec.agent_id for rec in agent_preview.recommendations}
954
+
955
+ # Review project agents
956
+ project_agents_dir = Path.cwd() / ".claude" / "agents"
957
+ review_service = AgentReviewService()
958
+ return review_service.review_project_agents(
959
+ project_agents_dir, managed_agents, recommended_ids
960
+ )
961
+
962
+ def _archive_agents(self, agents_to_archive: list[dict]) -> dict:
963
+ """Archive unused agents by moving them to .claude/agents/unused/.
964
+
965
+ Args:
966
+ agents_to_archive: List of agent dicts to archive
967
+
968
+ Returns:
969
+ Dictionary with archival results
970
+ """
971
+ from ...services.agents.agent_review_service import AgentReviewService
972
+
973
+ project_agents_dir = Path.cwd() / ".claude" / "agents"
974
+ review_service = AgentReviewService()
975
+ return review_service.archive_agents(agents_to_archive, project_agents_dir)
976
+
977
+ def _display_agent_review(self, review_results: dict) -> None:
978
+ """Display agent review results in the preview.
979
+
980
+ Args:
981
+ review_results: Dictionary with categorized agents
982
+ """
983
+ if not self.console:
984
+ return
985
+
986
+ # Count agents to archive
987
+ unused_count = len(review_results.get("unused", []))
988
+ outdated_count = len(review_results.get("outdated", []))
989
+ custom_count = len(review_results.get("custom", []))
990
+
991
+ if unused_count > 0 or outdated_count > 0 or custom_count > 0:
992
+ self.console.print("\n📋 Existing Agents Review:", style="bold blue")
993
+
994
+ # Show custom agents (will be preserved)
995
+ if custom_count > 0:
996
+ self.console.print(
997
+ "\n [green]Custom agents (will be preserved):[/green]"
998
+ )
999
+ for agent in review_results["custom"]:
1000
+ self.console.print(f" ✓ {agent['name']} (v{agent['version']})")
1001
+
1002
+ # Show agents to be archived
1003
+ if unused_count > 0:
1004
+ self.console.print(
1005
+ "\n [yellow]Agents to archive (not needed for this toolchain):[/yellow]"
1006
+ )
1007
+ for agent in review_results["unused"]:
1008
+ reason = (
1009
+ f"outdated (v{agent['current_version']} → v{agent['available_version']})"
1010
+ if "current_version" in agent
1011
+ else "not recommended"
1012
+ )
1013
+ self.console.print(f" → {agent['name']} ({reason})")
1014
+ self.console.print(
1015
+ " [dim]Will be moved to .claude/agents/unused/[/dim]"
1016
+ )
1017
+
1018
+ def _show_restart_notification(
1019
+ self, agent_result=None, skills_result=None, archive_result=None
1020
+ ) -> None:
1021
+ """Show restart notification after configuration is complete.
1022
+
1023
+ Args:
1024
+ agent_result: Agent deployment results
1025
+ skills_result: Skills deployment results
1026
+ archive_result: Agent archival results
1027
+ """
1028
+ if not self.console:
1029
+ return
1030
+
1031
+ # Build summary of changes
1032
+ changes = []
1033
+ if agent_result and agent_result.deployed_agents:
1034
+ changes.append(f"Deployed {len(agent_result.deployed_agents)} agent(s)")
1035
+ if skills_result and skills_result.get("deployed"):
1036
+ changes.append(f"Deployed {len(skills_result['deployed'])} skill(s)")
1037
+ if archive_result and archive_result.get("archived"):
1038
+ changes.append(
1039
+ f"Archived {len(archive_result['archived'])} unused agent(s) to .claude/agents/unused/"
1040
+ )
1041
+
1042
+ if changes:
1043
+ self.console.print("\n" + "=" * 70)
1044
+ self.console.print("✅ [bold green]Configuration complete![/bold green]")
1045
+ self.console.print(
1046
+ "\n🔄 [bold yellow]Please restart Claude Code to apply changes:[/bold yellow]"
1047
+ )
1048
+ self.console.print(" - Quit Claude Code completely")
1049
+ self.console.print(" - Relaunch Claude Code")
1050
+ self.console.print("\n[bold]Changes applied:[/bold]")
1051
+ for change in changes:
1052
+ self.console.print(f" • {change}")
1053
+ self.console.print("=" * 70 + "\n")
@@ -68,10 +68,12 @@ class ConfigCommand(BaseCommand):
68
68
 
69
69
  def validate_args(self, args) -> str:
70
70
  """Validate command arguments."""
71
+ # If no config_command specified, default to 'auto' (preview mode)
71
72
  if not hasattr(args, "config_command") or not args.config_command:
72
- return "No config command specified"
73
+ args.config_command = "auto"
74
+ args.preview = True # Default to preview when no args
73
75
 
74
- valid_commands = ["validate", "view", "status"]
76
+ valid_commands = ["validate", "view", "status", "auto", "gitignore"]
75
77
  if args.config_command not in valid_commands:
76
78
  return f"Unknown config command: {args.config_command}. Valid commands: {', '.join(valid_commands)}"
77
79
 
@@ -85,6 +87,11 @@ class ConfigCommand(BaseCommand):
85
87
  return self._view_config(args)
86
88
  if args.config_command == "status":
87
89
  return self._show_config_status(args)
90
+ if args.config_command == "gitignore":
91
+ self._show_gitignore_recommendations()
92
+ return CommandResult.success_result("Gitignore recommendations displayed")
93
+ if args.config_command == "auto":
94
+ return self._auto_configure(args)
88
95
  return CommandResult.error_result(
89
96
  f"Unknown config command: {args.config_command}"
90
97
  )
@@ -450,6 +457,85 @@ class ConfigCommand(BaseCommand):
450
457
 
451
458
  return flattened
452
459
 
460
+ def _show_gitignore_recommendations(self) -> None:
461
+ """Show recommended .gitignore patterns for Claude MPM projects.
462
+
463
+ This displays recommended gitignore patterns without making any changes
464
+ to the user's .gitignore file. Users can choose to apply these manually.
465
+ """
466
+ console.print(
467
+ "\n[bold cyan]Recommended .gitignore Patterns for Claude MPM[/bold cyan]\n"
468
+ )
469
+
470
+ console.print("[bold]Track agent memories, ignore runtime data:[/bold]")
471
+ console.print("[dim]# Add this to your .gitignore:[/dim]\n")
472
+
473
+ # The recommended gitignore block
474
+ recommended_patterns = """# Claude MPM - Track memories, ignore runtime data
475
+ .claude-mpm/*
476
+ !.claude-mpm/memories/
477
+ .claude-mpm/memories/*
478
+ !.claude-mpm/memories/*.md"""
479
+
480
+ # Display in a panel for clarity
481
+ from rich.panel import Panel
482
+
483
+ panel = Panel(
484
+ recommended_patterns,
485
+ title="Recommended .gitignore Patterns",
486
+ border_style="green",
487
+ padding=(1, 2),
488
+ )
489
+ console.print(panel)
490
+
491
+ # Explanation
492
+ console.print("\n[bold]What this does:[/bold]")
493
+ console.print(
494
+ " • Track [cyan].claude-mpm/memories/*.md[/cyan] (agent memories are valuable)"
495
+ )
496
+ console.print(
497
+ " • Ignore everything else in [dim].claude-mpm/[/dim] (cache, logs, sessions, tmp)"
498
+ )
499
+ console.print(
500
+ " • Services (mcp-vector-search, kuzu-memory, etc.) handle their own .gitignore"
501
+ )
502
+
503
+ console.print(
504
+ "\n[yellow]Note:[/yellow] These patterns are recommendations only."
505
+ )
506
+ console.print(" Add them manually to your .gitignore if desired.\n")
507
+
508
+ def _auto_configure(self, args) -> CommandResult:
509
+ """
510
+ Run auto-configuration to detect toolchain and recommend agents/skills.
511
+
512
+ This delegates to the AutoConfigureCommand for the actual implementation.
513
+ """
514
+ # Check if user wants gitignore recommendations
515
+ if hasattr(args, "gitignore") and args.gitignore:
516
+ self._show_gitignore_recommendations()
517
+ return CommandResult.success_result("Gitignore recommendations displayed")
518
+
519
+ try:
520
+ # Import AutoConfigureCommand
521
+ from .auto_configure import AutoConfigureCommand
522
+
523
+ # Create auto-configure command instance
524
+ auto_cmd = AutoConfigureCommand()
525
+
526
+ # Run auto-configuration
527
+ return auto_cmd.run(args)
528
+
529
+ except ImportError as e:
530
+ self.logger.error(f"AutoConfigureCommand not available: {e}")
531
+ return CommandResult.error_result(
532
+ "Auto-configuration feature not available. "
533
+ "Please ensure all dependencies are installed."
534
+ )
535
+ except Exception as e:
536
+ self.logger.error(f"Auto-configuration failed: {e}", exc_info=True)
537
+ return CommandResult.error_result(f"Auto-configuration failed: {e}")
538
+
453
539
 
454
540
  def manage_config(args) -> int:
455
541
  """Main entry point for configuration management commands.