claude-mpm 5.4.3__py3-none-any.whl → 5.4.21__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 (90) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +166 -21
  5. claude_mpm/agents/agent_loader.py +3 -27
  6. claude_mpm/cli/__main__.py +4 -0
  7. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  8. claude_mpm/cli/commands/agents.py +0 -31
  9. claude_mpm/cli/commands/auto_configure.py +210 -25
  10. claude_mpm/cli/commands/config.py +88 -2
  11. claude_mpm/cli/commands/configure.py +85 -43
  12. claude_mpm/cli/commands/configure_agent_display.py +3 -1
  13. claude_mpm/cli/commands/mpm_init/core.py +2 -45
  14. claude_mpm/cli/commands/skills.py +214 -189
  15. claude_mpm/cli/executor.py +3 -3
  16. claude_mpm/cli/parsers/agents_parser.py +0 -9
  17. claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
  18. claude_mpm/cli/parsers/config_parser.py +153 -83
  19. claude_mpm/cli/parsers/skills_parser.py +3 -2
  20. claude_mpm/cli/startup.py +490 -41
  21. claude_mpm/commands/mpm-config.md +265 -0
  22. claude_mpm/commands/mpm-help.md +14 -95
  23. claude_mpm/commands/mpm-organize.md +350 -153
  24. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  25. claude_mpm/core/framework_loader.py +4 -2
  26. claude_mpm/core/logger.py +13 -0
  27. claude_mpm/hooks/claude_hooks/event_handlers.py +176 -76
  28. claude_mpm/hooks/claude_hooks/hook_handler.py +2 -0
  29. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  30. claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
  31. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  32. claude_mpm/hooks/memory_integration_hook.py +46 -1
  33. claude_mpm/init.py +0 -19
  34. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  35. claude_mpm/scripts/start_activity_logging.py +0 -0
  36. claude_mpm/services/agents/agent_recommendation_service.py +6 -7
  37. claude_mpm/services/agents/agent_review_service.py +280 -0
  38. claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -3
  39. claude_mpm/services/agents/deployment/agent_template_builder.py +1 -0
  40. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +78 -9
  41. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +13 -0
  42. claude_mpm/services/agents/git_source_manager.py +14 -0
  43. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  44. claude_mpm/services/agents/toolchain_detector.py +6 -3
  45. claude_mpm/services/command_deployment_service.py +81 -8
  46. claude_mpm/services/git/git_operations_service.py +93 -8
  47. claude_mpm/services/self_upgrade_service.py +120 -12
  48. claude_mpm/services/skills/__init__.py +3 -0
  49. claude_mpm/services/skills/git_skill_source_manager.py +32 -2
  50. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  51. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  52. claude_mpm/services/skills_deployer.py +126 -9
  53. {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.21.dist-info}/METADATA +47 -8
  54. {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.21.dist-info}/RECORD +58 -82
  55. {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.21.dist-info}/entry_points.txt +0 -3
  56. claude_mpm-5.4.21.dist-info/licenses/LICENSE +94 -0
  57. claude_mpm-5.4.21.dist-info/licenses/LICENSE-FAQ.md +153 -0
  58. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  59. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  60. claude_mpm/agents/BASE_ENGINEER.md +0 -658
  61. claude_mpm/agents/BASE_OPS.md +0 -219
  62. claude_mpm/agents/BASE_PM.md +0 -480
  63. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  64. claude_mpm/agents/BASE_QA.md +0 -167
  65. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  66. claude_mpm/agents/base_agent.json +0 -31
  67. claude_mpm/agents/base_agent_loader.py +0 -601
  68. claude_mpm/cli/commands/agents_detect.py +0 -380
  69. claude_mpm/cli/commands/agents_recommend.py +0 -309
  70. claude_mpm/cli/ticket_cli.py +0 -35
  71. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  72. claude_mpm/commands/mpm-agents-detect.md +0 -177
  73. claude_mpm/commands/mpm-agents-list.md +0 -131
  74. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  75. claude_mpm/commands/mpm-config-view.md +0 -150
  76. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  77. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-313.pyc +0 -0
  78. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  79. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  85. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  86. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  87. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  88. claude_mpm-5.4.3.dist-info/licenses/LICENSE +0 -21
  89. {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.21.dist-info}/WHEEL +0 -0
  90. {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.21.dist-info}/top_level.txt +0 -0
@@ -400,8 +400,9 @@ class ConfigureCommand(BaseCommand):
400
400
  # Set deployment status on each agent for display
401
401
  deployed_ids = get_deployed_agent_ids()
402
402
  for agent in agents:
403
- # Extract leaf name for comparison
404
- agent_leaf_name = agent.name.split("/")[-1]
403
+ # Use agent_id (technical ID) for comparison, not display name
404
+ agent_id = getattr(agent, "agent_id", agent.name)
405
+ agent_leaf_name = agent_id.split("/")[-1]
405
406
  agent.is_deployed = agent_leaf_name in deployed_ids
406
407
 
407
408
  # Filter BASE_AGENT from display (1M-502 Phase 1)
@@ -1218,7 +1219,9 @@ class ConfigureCommand(BaseCommand):
1218
1219
  source_label = "Local"
1219
1220
 
1220
1221
  # FIX 2: Check actual deployment status from .claude/agents/ directory
1221
- is_installed = agent.name in deployed_ids
1222
+ # Use agent_id (technical ID like "python-engineer") not display name
1223
+ agent_id = getattr(agent, "agent_id", agent.name)
1224
+ is_installed = agent_id in deployed_ids
1222
1225
  if is_installed:
1223
1226
  status = "[green]Installed[/green]"
1224
1227
  else:
@@ -1248,8 +1251,8 @@ class ConfigureCommand(BaseCommand):
1248
1251
  recommended_count += 1
1249
1252
  break
1250
1253
 
1251
- # FIX 1: Removed asterisk - using Status column instead
1252
- agent_id_display = agent.name
1254
+ # FIX 1: Show agent_id (technical ID) in first column, not display name
1255
+ agent_id_display = getattr(agent, "agent_id", agent.name)
1253
1256
 
1254
1257
  # Get display name and format it properly
1255
1258
  # Raw display_name from YAML may contain underscores (e.g., "agentic_coder_optimizer")
@@ -1281,7 +1284,10 @@ class ConfigureCommand(BaseCommand):
1281
1284
  self.console.print("\n[dim]* = recommended for this project[/dim]")
1282
1285
 
1283
1286
  # Show installed vs available count (use deployed_ids for accuracy)
1284
- installed_count = sum(1 for a in agents if a.name in deployed_ids)
1287
+ # Use agent_id (technical ID) for comparison, not display name
1288
+ installed_count = sum(
1289
+ 1 for a in agents if getattr(a, "agent_id", a.name) in deployed_ids
1290
+ )
1285
1291
  available_count = len(agents) - installed_count
1286
1292
  self.console.print(
1287
1293
  f"\n[green]✓ {installed_count} installed[/green] | "
@@ -1326,7 +1332,7 @@ class ConfigureCommand(BaseCommand):
1326
1332
  all_agents = filter_base_agents(
1327
1333
  [
1328
1334
  {
1329
- "agent_id": a.name,
1335
+ "agent_id": getattr(a, "agent_id", a.name),
1330
1336
  "name": a.name,
1331
1337
  "description": a.description,
1332
1338
  "deployed": getattr(a, "is_deployed", False),
@@ -1352,11 +1358,14 @@ class ConfigureCommand(BaseCommand):
1352
1358
  recommended_agent_ids = set()
1353
1359
 
1354
1360
  # Build mapping: leaf name -> full path for deployed agents
1361
+ # Use agent_id (technical ID) for comparison, not display name
1355
1362
  deployed_full_paths = set()
1356
1363
  for agent in agents:
1357
- agent_leaf_name = agent.name.split("/")[-1]
1364
+ agent_id = getattr(agent, "agent_id", agent.name)
1365
+ agent_leaf_name = agent_id.split("/")[-1]
1358
1366
  if agent_leaf_name in deployed_ids:
1359
- deployed_full_paths.add(agent.name)
1367
+ # Store agent_id for selection tracking (not display name)
1368
+ deployed_full_paths.add(agent_id)
1360
1369
 
1361
1370
  # Track current selection state (starts with deployed, updated in loop)
1362
1371
  current_selection = deployed_full_paths.copy()
@@ -1366,7 +1375,9 @@ class ConfigureCommand(BaseCommand):
1366
1375
  collections = defaultdict(list)
1367
1376
 
1368
1377
  for agent in agents:
1369
- if agent.name in {a["agent_id"] for a in all_agents}:
1378
+ # Use agent_id (technical ID) for comparison, not display name
1379
+ agent_id = getattr(agent, "agent_id", agent.name)
1380
+ if agent_id in {a["agent_id"] for a in all_agents}:
1370
1381
  # Determine collection ID
1371
1382
  source_type = getattr(agent, "source_type", "local")
1372
1383
  if source_type == "remote":
@@ -1391,7 +1402,7 @@ class ConfigureCommand(BaseCommand):
1391
1402
  collection_id = "Local Agents"
1392
1403
 
1393
1404
  collections[collection_id].append(agent)
1394
- agent_map[agent.name] = agent
1405
+ agent_map[agent_id] = agent
1395
1406
 
1396
1407
  # Monkey-patch questionary symbols for better visibility
1397
1408
  questionary.prompts.common.INDICATOR_SELECTED = "[✓]"
@@ -1406,10 +1417,11 @@ class ConfigureCommand(BaseCommand):
1406
1417
  agents_in_collection = collections[collection_id]
1407
1418
 
1408
1419
  # Count selected/total agents in collection
1420
+ # Use agent_id for selection tracking, not display name
1409
1421
  selected_count = sum(
1410
1422
  1
1411
1423
  for agent in agents_in_collection
1412
- if agent.name in current_selection
1424
+ if getattr(agent, "agent_id", agent.name) in current_selection
1413
1425
  )
1414
1426
  total_count = len(agents_in_collection)
1415
1427
 
@@ -1498,20 +1510,25 @@ class ConfigureCommand(BaseCommand):
1498
1510
 
1499
1511
  # Add individual agents
1500
1512
  for agent in agents_in_category:
1501
- agent_leaf_name = agent.name.split("/")[-1]
1502
- display_name = getattr(agent, "display_name", agent_leaf_name)
1513
+ # Use agent_id (technical ID) for all tracking/selection
1514
+ agent_id = getattr(agent, "agent_id", agent.name)
1515
+ agent_leaf_name = agent_id.split("/")[-1]
1516
+ raw_display_name = getattr(
1517
+ agent, "display_name", agent_leaf_name
1518
+ )
1519
+ display_name = self._format_display_name(raw_display_name)
1503
1520
 
1504
1521
  # Check if agent is deployed (exists in .claude/agents/)
1505
1522
 
1506
1523
  # Format choice text (no asterisk needed)
1507
1524
  choice_text = f" {display_name}"
1508
1525
 
1509
- is_selected = agent.name in current_selection
1526
+ is_selected = agent_id in current_selection
1510
1527
 
1511
1528
  choices.append(
1512
1529
  Choice(
1513
1530
  title=choice_text,
1514
- value=agent.name,
1531
+ value=agent_id, # Use agent_id for value
1515
1532
  checked=is_selected,
1516
1533
  )
1517
1534
  )
@@ -1565,38 +1582,42 @@ class ConfigureCommand(BaseCommand):
1565
1582
  )
1566
1583
  # Add all agents from this collection to current_selection
1567
1584
  for agent in collections[collection_id]:
1568
- current_selection.add(agent.name)
1585
+ agent_id = getattr(agent, "agent_id", agent.name)
1586
+ current_selection.add(agent_id)
1569
1587
  elif control.startswith("__DESELECT_ALL_"):
1570
1588
  collection_id = control.replace("__DESELECT_ALL_", "").replace(
1571
1589
  "__", ""
1572
1590
  )
1573
1591
  # Remove all agents from this collection
1574
1592
  for agent in collections[collection_id]:
1575
- current_selection.discard(agent.name)
1593
+ agent_id = getattr(agent, "agent_id", agent.name)
1594
+ current_selection.discard(agent_id)
1576
1595
  elif control.startswith("__SELECT_REC_"):
1577
1596
  collection_id = control.replace("__SELECT_REC_", "").replace(
1578
1597
  "__", ""
1579
1598
  )
1580
1599
  # Add all recommended agents from this collection
1581
1600
  for agent in collections[collection_id]:
1601
+ agent_id = getattr(agent, "agent_id", agent.name)
1582
1602
  if any(
1583
- agent.name == rec_id
1584
- or agent.name.split("/")[-1] == rec_id.split("/")[-1]
1603
+ agent_id == rec_id
1604
+ or agent_id.split("/")[-1] == rec_id.split("/")[-1]
1585
1605
  for rec_id in recommended_agent_ids
1586
1606
  ):
1587
- current_selection.add(agent.name)
1607
+ current_selection.add(agent_id)
1588
1608
  elif control.startswith("__DESELECT_REC_"):
1589
1609
  collection_id = control.replace("__DESELECT_REC_", "").replace(
1590
1610
  "__", ""
1591
1611
  )
1592
1612
  # Remove all recommended agents from this collection
1593
1613
  for agent in collections[collection_id]:
1614
+ agent_id = getattr(agent, "agent_id", agent.name)
1594
1615
  if any(
1595
- agent.name == rec_id
1596
- or agent.name.split("/")[-1] == rec_id.split("/")[-1]
1616
+ agent_id == rec_id
1617
+ or agent_id.split("/")[-1] == rec_id.split("/")[-1]
1597
1618
  for rec_id in recommended_agent_ids
1598
1619
  ):
1599
- current_selection.discard(agent.name)
1620
+ current_selection.discard(agent_id)
1600
1621
 
1601
1622
  # Loop back to re-display with updated selections
1602
1623
  continue
@@ -1734,7 +1755,7 @@ class ConfigureCommand(BaseCommand):
1734
1755
  all_agents = filter_base_agents(
1735
1756
  [
1736
1757
  {
1737
- "agent_id": a.name,
1758
+ "agent_id": getattr(a, "agent_id", a.name),
1738
1759
  "name": a.name,
1739
1760
  "description": a.description,
1740
1761
  "deployed": getattr(a, "is_deployed", False),
@@ -1753,12 +1774,14 @@ class ConfigureCommand(BaseCommand):
1753
1774
  return
1754
1775
 
1755
1776
  # Build mapping: leaf name -> full path for deployed agents
1756
- # This allows comparing deployed_ids (leaf names) with agent.name (full paths)
1777
+ # This allows comparing deployed_ids (leaf names) with agent.agent_id (full paths)
1757
1778
  deployed_full_paths = set()
1758
1779
  for agent in agents:
1759
- agent_leaf_name = agent.name.split("/")[-1]
1780
+ # FIX: Use agent_id (technical ID) instead of display name
1781
+ agent_id = getattr(agent, "agent_id", agent.name)
1782
+ agent_leaf_name = agent_id.split("/")[-1]
1760
1783
  if agent_leaf_name in deployed_ids:
1761
- deployed_full_paths.add(agent.name)
1784
+ deployed_full_paths.add(agent_id)
1762
1785
 
1763
1786
  # Track current selection state (starts with deployed full paths, updated after each iteration)
1764
1787
  current_selection = deployed_full_paths.copy()
@@ -1770,7 +1793,9 @@ class ConfigureCommand(BaseCommand):
1770
1793
  collections = defaultdict(list)
1771
1794
 
1772
1795
  for agent in agents:
1773
- if agent.name in {a["agent_id"] for a in all_agents}:
1796
+ # FIX: Use agent_id (technical ID) for comparison
1797
+ agent_id = getattr(agent, "agent_id", agent.name)
1798
+ if agent_id in {a["agent_id"] for a in all_agents}:
1774
1799
  # Determine collection ID
1775
1800
  source_type = getattr(agent, "source_type", "local")
1776
1801
  if source_type == "remote":
@@ -1789,7 +1814,7 @@ class ConfigureCommand(BaseCommand):
1789
1814
  collection_id = "local"
1790
1815
 
1791
1816
  collections[collection_id].append(agent)
1792
- agent_map[agent.name] = agent
1817
+ agent_map[agent_id] = agent # FIX: Use agent_id as key
1793
1818
 
1794
1819
  # STEP 1: Collection-level selection
1795
1820
  self.console.print("\n[bold cyan]Select Agent Collections[/bold cyan]")
@@ -1809,15 +1834,18 @@ class ConfigureCommand(BaseCommand):
1809
1834
 
1810
1835
  # Check if ANY agent in this collection is currently deployed
1811
1836
  # This reflects actual deployment state, not just selection
1837
+ # FIX: Use agent_id for comparison with current_selection
1812
1838
  any_deployed = any(
1813
- agent.name in current_selection for agent in agents_in_collection
1839
+ getattr(agent, "agent_id", agent.name) in current_selection
1840
+ for agent in agents_in_collection
1814
1841
  )
1815
1842
 
1816
1843
  # Count deployed agents for display
1844
+ # FIX: Use agent_id for comparison with current_selection
1817
1845
  deployed_count = sum(
1818
1846
  1
1819
1847
  for agent in agents_in_collection
1820
- if agent.name in current_selection
1848
+ if getattr(agent, "agent_id", agent.name) in current_selection
1821
1849
  )
1822
1850
 
1823
1851
  collection_choices.append(
@@ -1911,17 +1939,23 @@ class ConfigureCommand(BaseCommand):
1911
1939
  )
1912
1940
 
1913
1941
  # Add individual agents from this collection
1914
- for agent in sorted(agents_in_collection, key=lambda a: a.name):
1915
- display_name = getattr(agent, "display_name", agent.name)
1916
- is_selected = agent.name in deployed_full_paths
1917
-
1918
- choice_text = f"{agent.name}"
1919
- if display_name and display_name != agent.name:
1942
+ # FIX: Use agent_id for sorting, comparison, and values
1943
+ for agent in sorted(
1944
+ agents_in_collection,
1945
+ key=lambda a: getattr(a, "agent_id", a.name),
1946
+ ):
1947
+ agent_id = getattr(agent, "agent_id", agent.name)
1948
+ raw_display_name = getattr(agent, "display_name", agent.name)
1949
+ display_name = self._format_display_name(raw_display_name)
1950
+ is_selected = agent_id in deployed_full_paths
1951
+
1952
+ choice_text = f"{agent_id}"
1953
+ if display_name and display_name != agent_id:
1920
1954
  choice_text += f" - {display_name}"
1921
1955
 
1922
1956
  agent_choices.append(
1923
1957
  Choice(
1924
- title=choice_text, value=agent.name, checked=is_selected
1958
+ title=choice_text, value=agent_id, checked=is_selected
1925
1959
  )
1926
1960
  )
1927
1961
 
@@ -1971,7 +2005,8 @@ class ConfigureCommand(BaseCommand):
1971
2005
  final_selections = set()
1972
2006
  for collection_id in selected_collections:
1973
2007
  for agent in collections[collection_id]:
1974
- final_selections.add(agent.name)
2008
+ # FIX: Use agent_id for selection tracking
2009
+ final_selections.add(getattr(agent, "agent_id", agent.name))
1975
2010
 
1976
2011
  # Update current_selection
1977
2012
  # This replaces the previous selection entirely with the new collection selections
@@ -2484,7 +2519,8 @@ class ConfigureCommand(BaseCommand):
2484
2519
 
2485
2520
  self.console.print(f"\n[bold]Installed agents ({len(installed)}):[/bold]")
2486
2521
  for idx, agent in enumerate(installed, 1):
2487
- display_name = getattr(agent, "display_name", agent.name)
2522
+ raw_display_name = getattr(agent, "display_name", agent.name)
2523
+ display_name = self._format_display_name(raw_display_name)
2488
2524
  self.console.print(f" {idx}. {agent.name} - {display_name}")
2489
2525
 
2490
2526
  selection = Prompt.ask("\nEnter agent number to remove (or 'c' to cancel)")
@@ -2547,7 +2583,8 @@ class ConfigureCommand(BaseCommand):
2547
2583
 
2548
2584
  self.console.print(f"\n[bold]Available agents ({len(agents)}):[/bold]")
2549
2585
  for idx, agent in enumerate(agents, 1):
2550
- display_name = getattr(agent, "display_name", agent.name)
2586
+ raw_display_name = getattr(agent, "display_name", agent.name)
2587
+ display_name = self._format_display_name(raw_display_name)
2551
2588
  self.console.print(f" {idx}. {agent.name} - {display_name}")
2552
2589
 
2553
2590
  selection = Prompt.ask("\nEnter agent number to view (or 'c' to cancel)")
@@ -2564,7 +2601,12 @@ class ConfigureCommand(BaseCommand):
2564
2601
 
2565
2602
  # Basic info
2566
2603
  self.console.print(f"[bold]ID:[/bold] {agent.name}")
2567
- display_name = getattr(agent, "display_name", "N/A")
2604
+ raw_display_name = getattr(agent, "display_name", "N/A")
2605
+ display_name = (
2606
+ self._format_display_name(raw_display_name)
2607
+ if raw_display_name != "N/A"
2608
+ else "N/A"
2609
+ )
2568
2610
  self.console.print(f"[bold]Name:[/bold] {display_name}")
2569
2611
  self.console.print(f"[bold]Description:[/bold] {agent.description}")
2570
2612
 
@@ -85,7 +85,9 @@ class AgentDisplay:
85
85
 
86
86
  for idx, agent in enumerate(agents, 1):
87
87
  # Check if agent is deployed to .claude/agents/
88
- agent_leaf_name = agent.name.split("/")[-1]
88
+ # Use agent_id (technical ID) for comparison, not display name
89
+ agent_id = getattr(agent, "agent_id", agent.name)
90
+ agent_leaf_name = agent_id.split("/")[-1]
89
91
  is_deployed = agent_leaf_name in deployed_ids
90
92
 
91
93
  # Show "Installed" for deployed agents, "Available" otherwise
@@ -157,9 +157,8 @@ class MPMInitCommand:
157
157
  if pre_check_result.get("status") == OperationResult.ERROR:
158
158
  return pre_check_result
159
159
 
160
- # Update .gitignore to exclude claude-mpm configuration directories
161
- if not review_only and not dry_run:
162
- self._update_gitignore()
160
+ # Note: .gitignore recommendations are now shown in 'mpm config' command
161
+ # instead of automatic updates during initialization
163
162
 
164
163
  # Build the delegation prompt
165
164
  if update_mode:
@@ -401,48 +400,6 @@ class MPMInitCommand:
401
400
  "warnings": warnings,
402
401
  }
403
402
 
404
- def _update_gitignore(self) -> None:
405
- """Update .gitignore to exclude claude-mpm configuration directories.
406
-
407
- Ensures that claude-mpm configuration directories are added to .gitignore
408
- to prevent them from being committed to version control.
409
-
410
- This is a non-destructive operation that:
411
- - Creates .gitignore if it doesn't exist
412
- - Appends missing entries only (never duplicates)
413
- - Preserves all existing content
414
-
415
- Standard entries added:
416
- - .claude-mpm/: Main configuration directory
417
- - .claude/agents/: Agent runtime files
418
- """
419
- from claude_mpm.utils.gitignore import ensure_claude_mpm_gitignore
420
-
421
- try:
422
- result = ensure_claude_mpm_gitignore(str(self.project_path))
423
-
424
- if result.get("status") == "success":
425
- if result.get("added"):
426
- self.console.print(
427
- f"[green]✓[/green] Updated .gitignore: {', '.join(result['added'])}"
428
- )
429
- logger.info(f"Added to .gitignore: {result['added']}")
430
- elif result.get("existing"):
431
- logger.debug(f".gitignore already contains: {result['existing']}")
432
- else:
433
- # Non-critical error - log but don't fail initialization
434
- logger.warning(
435
- f"Could not update .gitignore: {result.get('error', 'Unknown error')}"
436
- )
437
- self.console.print(
438
- f"[yellow]⚠️ Could not update .gitignore: {result.get('error')}[/yellow]"
439
- )
440
-
441
- except Exception as e:
442
- # Non-critical error - log but don't fail initialization
443
- logger.warning(f"Error updating .gitignore: {e}")
444
- self.console.print(f"[yellow]⚠️ Could not update .gitignore: {e}[/yellow]")
445
-
446
403
  def _build_initialization_prompt(
447
404
  self,
448
405
  project_type: Optional[str] = None,