claude-mpm 4.7.7__py3-none-any.whl → 4.7.9__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.
claude_mpm/VERSION CHANGED
@@ -1 +1 @@
1
- 4.7.7
1
+ 4.7.9
@@ -60,6 +60,8 @@ class SimpleAgentManager:
60
60
  import logging
61
61
 
62
62
  self.logger = logging.getLogger(__name__)
63
+ # Track pending changes for batch operations
64
+ self.deferred_changes: Dict[str, bool] = {}
63
65
 
64
66
  def _load_states(self):
65
67
  """Load agent states from file."""
@@ -85,6 +87,33 @@ class SimpleAgentManager:
85
87
  self.states[agent_name]["enabled"] = enabled
86
88
  self._save_states()
87
89
 
90
+ def set_agent_enabled_deferred(self, agent_name: str, enabled: bool) -> None:
91
+ """Queue agent state change without saving."""
92
+ self.deferred_changes[agent_name] = enabled
93
+
94
+ def commit_deferred_changes(self) -> None:
95
+ """Save all deferred changes at once."""
96
+ for agent_name, enabled in self.deferred_changes.items():
97
+ if agent_name not in self.states:
98
+ self.states[agent_name] = {}
99
+ self.states[agent_name]["enabled"] = enabled
100
+ self._save_states()
101
+ self.deferred_changes.clear()
102
+
103
+ def discard_deferred_changes(self) -> None:
104
+ """Discard all pending changes."""
105
+ self.deferred_changes.clear()
106
+
107
+ def get_pending_state(self, agent_name: str) -> bool:
108
+ """Get agent state including pending changes."""
109
+ if agent_name in self.deferred_changes:
110
+ return self.deferred_changes[agent_name]
111
+ return self.states.get(agent_name, {}).get("enabled", True)
112
+
113
+ def has_pending_changes(self) -> bool:
114
+ """Check if there are unsaved changes."""
115
+ return len(self.deferred_changes) > 0
116
+
88
117
  def discover_agents(self) -> List[AgentConfig]:
89
118
  """Discover available agents from template JSON files."""
90
119
  agents = []
@@ -296,6 +325,33 @@ class ConfigureCommand(BaseCommand):
296
325
  self._switch_scope()
297
326
  elif choice == "6":
298
327
  self._show_version_info_interactive()
328
+ elif choice == "l":
329
+ # Check for pending agent changes
330
+ if self.agent_manager and self.agent_manager.has_pending_changes():
331
+ should_save = Confirm.ask(
332
+ "[yellow]You have unsaved agent changes. Save them before launching?[/yellow]",
333
+ default=True,
334
+ )
335
+ if should_save:
336
+ self.agent_manager.commit_deferred_changes()
337
+ self.console.print("[green]✓ Agent changes saved[/green]")
338
+ else:
339
+ self.agent_manager.discard_deferred_changes()
340
+ self.console.print(
341
+ "[yellow]⚠ Agent changes discarded[/yellow]"
342
+ )
343
+
344
+ # Save all configuration
345
+ self.console.print("\n[cyan]Saving configuration...[/cyan]")
346
+ if self._save_all_configuration():
347
+ # Launch Claude MPM (this will replace the process if successful)
348
+ self._launch_claude_mpm()
349
+ # If execvp fails, we'll return here and break
350
+ break
351
+ self.console.print(
352
+ "[red]✗ Failed to save configuration. Not launching.[/red]"
353
+ )
354
+ Prompt.ask("\nPress Enter to continue")
299
355
  elif choice == "q":
300
356
  self.console.print(
301
357
  "\n[green]Configuration complete. Goodbye![/green]"
@@ -356,16 +412,17 @@ class ConfigureCommand(BaseCommand):
356
412
  ),
357
413
  ("5", "Switch Scope", f"Current: {self.current_scope}"),
358
414
  ("6", "Version Info", "Display MPM and Claude versions"),
359
- ("q", "Quit", "Exit configuration interface"),
415
+ ("l", "Save & Launch", "Save all changes and start Claude MPM"),
416
+ ("q", "Quit", "Exit without launching"),
360
417
  ]
361
418
 
362
419
  table = Table(show_header=False, box=None, padding=(0, 2))
363
- table.add_column("Key", style="cyan", width=3)
364
- table.add_column("Option", style="bold white", width=20)
365
- table.add_column("Description", style="dim")
420
+ table.add_column("Key", style="cyan bold", width=4) # Bolder shortcuts
421
+ table.add_column("Option", style="bold white", width=24) # Wider for titles
422
+ table.add_column("Description", style="white") # Better contrast
366
423
 
367
424
  for key, option, desc in menu_items:
368
- table.add_row(f"[{key}]", option, desc)
425
+ table.add_row(f"\\[{key}]", option, desc)
369
426
 
370
427
  menu_panel = Panel(
371
428
  table, title="[bold]Main Menu[/bold]", box=ROUNDED, style="green"
@@ -392,15 +449,10 @@ class ConfigureCommand(BaseCommand):
392
449
  self.console.print("\n[bold]Agent Management Options:[/bold]")
393
450
 
394
451
  # Use Text objects to properly display shortcuts with styling
395
- text_e = Text(" ")
396
- text_e.append("[e]", style="cyan bold")
397
- text_e.append(" Enable an agent")
398
- self.console.print(text_e)
399
-
400
- text_d = Text(" ")
401
- text_d.append("[d]", style="cyan bold")
402
- text_d.append(" Disable an agent")
403
- self.console.print(text_d)
452
+ text_t = Text(" ")
453
+ text_t.append("[t]", style="cyan bold")
454
+ text_t.append(" Toggle agents (enable/disable multiple)")
455
+ self.console.print(text_t)
404
456
 
405
457
  text_c = Text(" ")
406
458
  text_c.append("[c]", style="cyan bold")
@@ -428,10 +480,8 @@ class ConfigureCommand(BaseCommand):
428
480
 
429
481
  if choice == "b":
430
482
  break
431
- if choice == "e":
432
- self._enable_agent_interactive(agents)
433
- elif choice == "d":
434
- self._disable_agent_interactive(agents)
483
+ if choice == "t":
484
+ self._toggle_agents_interactive(agents)
435
485
  elif choice == "c":
436
486
  self._customize_agent_template(agents)
437
487
  elif choice == "v":
@@ -494,55 +544,127 @@ class ConfigureCommand(BaseCommand):
494
544
 
495
545
  self.console.print(table)
496
546
 
497
- def _enable_agent_interactive(self, agents: List[AgentConfig]) -> None:
498
- """Interactive agent enabling."""
499
- agent_id = Prompt.ask("Enter agent ID to enable (or 'all' for all agents)")
547
+ def _display_agents_with_pending_states(self, agents: List[AgentConfig]) -> None:
548
+ """Display agents table with pending state indicators."""
549
+ has_pending = self.agent_manager.has_pending_changes()
550
+ pending_count = len(self.agent_manager.deferred_changes) if has_pending else 0
500
551
 
501
- if agent_id.lower() == "all":
502
- if Confirm.ask("[yellow]Enable ALL agents?[/yellow]"):
503
- for agent in agents:
504
- self.agent_manager.set_agent_enabled(agent.name, True)
505
- self.console.print("[green]All agents enabled successfully![/green]")
506
- else:
507
- try:
508
- idx = int(agent_id) - 1
509
- if 0 <= idx < len(agents):
510
- agent = agents[idx]
511
- self.agent_manager.set_agent_enabled(agent.name, True)
512
- self.console.print(
513
- f"[green]Agent '{agent.name}' enabled successfully![/green]"
514
- )
515
- else:
516
- self.console.print("[red]Invalid agent ID.[/red]")
517
- except ValueError:
518
- self.console.print("[red]Invalid input. Please enter a number.[/red]")
552
+ title = f"Available Agents ({len(agents)} total)"
553
+ if has_pending:
554
+ title += f" [yellow]({pending_count} change{'s' if pending_count != 1 else ''} pending)[/yellow]"
519
555
 
520
- Prompt.ask("Press Enter to continue")
556
+ table = Table(title=title, box=ROUNDED, show_lines=True, expand=True)
557
+ table.add_column("ID", justify="right", style="cyan", width=5)
558
+ table.add_column("Name", style="bold", width=22)
559
+ table.add_column("Status", width=20)
560
+ table.add_column("Description", style="bold cyan", width=45)
521
561
 
522
- def _disable_agent_interactive(self, agents: List[AgentConfig]) -> None:
523
- """Interactive agent disabling."""
524
- agent_id = Prompt.ask("Enter agent ID to disable (or 'all' for all agents)")
562
+ for idx, agent in enumerate(agents, 1):
563
+ current_state = self.agent_manager.is_agent_enabled(agent.name)
564
+ pending_state = self.agent_manager.get_pending_state(agent.name)
525
565
 
526
- if agent_id.lower() == "all":
527
- if Confirm.ask("[yellow]Disable ALL agents?[/yellow]"):
528
- for agent in agents:
529
- self.agent_manager.set_agent_enabled(agent.name, False)
530
- self.console.print("[green]All agents disabled successfully![/green]")
531
- else:
532
- try:
533
- idx = int(agent_id) - 1
534
- if 0 <= idx < len(agents):
535
- agent = agents[idx]
536
- self.agent_manager.set_agent_enabled(agent.name, False)
537
- self.console.print(
538
- f"[green]Agent '{agent.name}' disabled successfully![/green]"
539
- )
566
+ # Show pending status with arrow
567
+ if current_state != pending_state:
568
+ if pending_state:
569
+ status = "[yellow]✗ Disabled → ✓ Enabled[/yellow]"
540
570
  else:
541
- self.console.print("[red]Invalid agent ID.[/red]")
542
- except ValueError:
543
- self.console.print("[red]Invalid input. Please enter a number.[/red]")
571
+ status = "[yellow] Enabled → ✗ Disabled[/yellow]"
572
+ else:
573
+ status = (
574
+ "[green]✓ Enabled[/green]"
575
+ if current_state
576
+ else "[dim]✗ Disabled[/dim]"
577
+ )
544
578
 
545
- Prompt.ask("Press Enter to continue")
579
+ desc_display = Text()
580
+ desc_display.append(
581
+ (
582
+ agent.description[:42] + "..."
583
+ if len(agent.description) > 42
584
+ else agent.description
585
+ ),
586
+ style="cyan",
587
+ )
588
+
589
+ table.add_row(str(idx), agent.name, status, desc_display)
590
+
591
+ self.console.print(table)
592
+
593
+ def _toggle_agents_interactive(self, agents: List[AgentConfig]) -> None:
594
+ """Interactive multi-agent enable/disable with batch save."""
595
+
596
+ # Initialize pending states from current states
597
+ for agent in agents:
598
+ current_state = self.agent_manager.is_agent_enabled(agent.name)
599
+ self.agent_manager.set_agent_enabled_deferred(agent.name, current_state)
600
+
601
+ while True:
602
+ # Display table with pending states
603
+ self._display_agents_with_pending_states(agents)
604
+
605
+ # Show menu
606
+ self.console.print("\n[bold]Toggle Agent Status:[/bold]")
607
+ text_toggle = Text(" ")
608
+ text_toggle.append("[t]", style="cyan bold")
609
+ text_toggle.append(" Enter agent IDs to toggle (e.g., '1,3,5' or '1-4')")
610
+ self.console.print(text_toggle)
611
+
612
+ text_all = Text(" ")
613
+ text_all.append("[a]", style="cyan bold")
614
+ text_all.append(" Enable all agents")
615
+ self.console.print(text_all)
616
+
617
+ text_none = Text(" ")
618
+ text_none.append("[n]", style="cyan bold")
619
+ text_none.append(" Disable all agents")
620
+ self.console.print(text_none)
621
+
622
+ text_save = Text(" ")
623
+ text_save.append("[s]", style="green bold")
624
+ text_save.append(" Save changes and return")
625
+ self.console.print(text_save)
626
+
627
+ text_cancel = Text(" ")
628
+ text_cancel.append("[c]", style="yellow bold")
629
+ text_cancel.append(" Cancel (discard changes)")
630
+ self.console.print(text_cancel)
631
+
632
+ choice = (
633
+ Prompt.ask("[bold cyan]Select an option[/bold cyan]", default="s")
634
+ .strip()
635
+ .lower()
636
+ )
637
+
638
+ if choice == "s":
639
+ if self.agent_manager.has_pending_changes():
640
+ self.agent_manager.commit_deferred_changes()
641
+ self.console.print("[green]✓ Changes saved successfully![/green]")
642
+ else:
643
+ self.console.print("[yellow]No changes to save.[/yellow]")
644
+ Prompt.ask("Press Enter to continue")
645
+ break
646
+ if choice == "c":
647
+ self.agent_manager.discard_deferred_changes()
648
+ self.console.print("[yellow]Changes discarded.[/yellow]")
649
+ Prompt.ask("Press Enter to continue")
650
+ break
651
+ if choice == "a":
652
+ for agent in agents:
653
+ self.agent_manager.set_agent_enabled_deferred(agent.name, True)
654
+ elif choice == "n":
655
+ for agent in agents:
656
+ self.agent_manager.set_agent_enabled_deferred(agent.name, False)
657
+ elif choice == "t" or choice.replace(",", "").replace("-", "").isdigit():
658
+ selected_ids = self._parse_id_selection(
659
+ choice if choice != "t" else Prompt.ask("Enter IDs"), len(agents)
660
+ )
661
+ for idx in selected_ids:
662
+ if 1 <= idx <= len(agents):
663
+ agent = agents[idx - 1]
664
+ current = self.agent_manager.get_pending_state(agent.name)
665
+ self.agent_manager.set_agent_enabled_deferred(
666
+ agent.name, not current
667
+ )
546
668
 
547
669
  def _customize_agent_template(self, agents: List[AgentConfig]) -> None:
548
670
  """Customize agent JSON template."""
@@ -1707,6 +1829,68 @@ class ConfigureCommand(BaseCommand):
1707
1829
  Prompt.ask("Press Enter to continue")
1708
1830
  return False
1709
1831
 
1832
+ def _save_all_configuration(self) -> bool:
1833
+ """Save all configuration changes across all contexts.
1834
+
1835
+ Returns:
1836
+ bool: True if all saves successful, False otherwise
1837
+ """
1838
+ try:
1839
+ # 1. Save any pending agent changes
1840
+ if self.agent_manager and self.agent_manager.has_pending_changes():
1841
+ self.agent_manager.commit_deferred_changes()
1842
+ self.console.print("[green]✓ Agent changes saved[/green]")
1843
+
1844
+ # 2. Save configuration file
1845
+ config = Config()
1846
+
1847
+ # Determine config file path based on scope
1848
+ if self.current_scope == "project":
1849
+ config_file = self.project_dir / ".claude-mpm" / "configuration.yaml"
1850
+ else:
1851
+ config_file = Path.home() / ".claude-mpm" / "configuration.yaml"
1852
+
1853
+ config_file.parent.mkdir(parents=True, exist_ok=True)
1854
+
1855
+ # Save with suppressed logging to avoid duplicate messages
1856
+ import logging
1857
+
1858
+ root_logger = logging.getLogger("claude_mpm")
1859
+ original_level = root_logger.level
1860
+ root_logger.setLevel(logging.WARNING)
1861
+
1862
+ try:
1863
+ config.save(config_file, format="yaml")
1864
+ finally:
1865
+ root_logger.setLevel(original_level)
1866
+
1867
+ self.console.print(f"[green]✓ Configuration saved to {config_file}[/green]")
1868
+ return True
1869
+
1870
+ except Exception as e:
1871
+ self.console.print(f"[red]✗ Error saving configuration: {e}[/red]")
1872
+ import traceback
1873
+
1874
+ traceback.print_exc()
1875
+ return False
1876
+
1877
+ def _launch_claude_mpm(self) -> None:
1878
+ """Launch Claude MPM run command, replacing current process."""
1879
+ self.console.print("\n[bold cyan]═══ Launching Claude MPM ═══[/bold cyan]\n")
1880
+
1881
+ try:
1882
+ # Use execvp to replace the current process with claude-mpm run
1883
+ # This ensures a clean transition from configurator to Claude MPM
1884
+ os.execvp("claude-mpm", ["claude-mpm", "run"])
1885
+ except Exception as e:
1886
+ self.console.print(
1887
+ f"[yellow]⚠ Could not launch Claude MPM automatically: {e}[/yellow]"
1888
+ )
1889
+ self.console.print(
1890
+ "[cyan]→ Please run 'claude-mpm run' manually to start.[/cyan]"
1891
+ )
1892
+ Prompt.ask("\nPress Enter to exit")
1893
+
1710
1894
  def _switch_scope(self) -> None:
1711
1895
  """Switch between project and user scope."""
1712
1896
  self.current_scope = "user" if self.current_scope == "project" else "project"
@@ -12,7 +12,7 @@ import contextlib
12
12
  import subprocess
13
13
  import sys
14
14
  from pathlib import Path
15
- from typing import Dict, List, Optional
15
+ from typing import Any, Dict, List, Optional
16
16
 
17
17
  import click
18
18
  from rich.console import Console
@@ -63,6 +63,7 @@ class MPMInitCommand:
63
63
  skip_archive: bool = False,
64
64
  dry_run: bool = False,
65
65
  quick_update: bool = False,
66
+ catchup: bool = False,
66
67
  non_interactive: bool = False,
67
68
  days: int = 30,
68
69
  export: Optional[str] = None,
@@ -84,6 +85,7 @@ class MPMInitCommand:
84
85
  skip_archive: Skip archiving existing files
85
86
  dry_run: Show what would be done without making changes
86
87
  quick_update: Perform lightweight update based on recent git activity
88
+ catchup: Show recent commit history from all branches for PM context
87
89
  non_interactive: Non-interactive mode - display report only without prompting
88
90
  days: Number of days for git history analysis (7, 14, 30, 60, or 90)
89
91
  export: Export report to file (path or "auto" for default location)
@@ -99,6 +101,11 @@ class MPMInitCommand:
99
101
  if review_only:
100
102
  return self._run_review_mode()
101
103
 
104
+ if catchup:
105
+ data = self._catchup()
106
+ self._display_catchup(data)
107
+ return {"status": "success", "mode": "catchup", "catchup_data": data}
108
+
102
109
  if quick_update:
103
110
  return self._run_quick_update_mode(
104
111
  days=days,
@@ -679,6 +686,142 @@ The final CLAUDE.md should be a comprehensive, well-organized guide that any AI
679
686
  console.print("\n[yellow]Quick update cancelled[/yellow]")
680
687
  return {"status": "cancelled", "message": "Quick update cancelled"}
681
688
 
689
+ def _catchup(self) -> Dict[str, Any]:
690
+ """Get recent commit history for PM context.
691
+
692
+ Returns:
693
+ Dict containing commit history and contributor stats
694
+ """
695
+ from collections import Counter
696
+ from datetime import datetime
697
+ from subprocess import run
698
+
699
+ try:
700
+ # Get last 25 commits from all branches with author info
701
+ result = run(
702
+ ["git", "log", "--all", "--format=%h|%an|%ai|%s", "-25"],
703
+ capture_output=True,
704
+ text=True,
705
+ check=True,
706
+ cwd=str(self.project_path),
707
+ )
708
+
709
+ commits = []
710
+ authors = []
711
+
712
+ for line in result.stdout.strip().split("\n"):
713
+ if not line:
714
+ continue
715
+
716
+ parts = line.split("|", 3)
717
+ if len(parts) == 4:
718
+ hash_val, author, date_str, message = parts
719
+
720
+ # Parse date
721
+ try:
722
+ dt = datetime.fromisoformat(date_str.replace(" ", "T", 1))
723
+ date_display = dt.strftime("%Y-%m-%d %H:%M")
724
+ except Exception:
725
+ date_display = date_str[:16]
726
+
727
+ commits.append(
728
+ {
729
+ "hash": hash_val,
730
+ "author": author,
731
+ "date": date_display,
732
+ "message": message,
733
+ }
734
+ )
735
+ authors.append(author)
736
+
737
+ # Calculate contributor stats
738
+ author_counts = Counter(authors)
739
+
740
+ return {
741
+ "commits": commits,
742
+ "total_commits": len(commits),
743
+ "contributors": dict(author_counts),
744
+ "contributor_count": len(author_counts),
745
+ }
746
+
747
+ except Exception as e:
748
+ console.print(f"[yellow]Could not retrieve commit history: {e}[/yellow]")
749
+ return {
750
+ "commits": [],
751
+ "total_commits": 0,
752
+ "contributors": {},
753
+ "contributor_count": 0,
754
+ "error": str(e),
755
+ }
756
+
757
+ def _display_catchup(self, data: Dict[str, Any]) -> None:
758
+ """Display catchup information to console.
759
+
760
+ Args:
761
+ data: Commit history data from _catchup()
762
+ """
763
+ from rich.panel import Panel
764
+ from rich.table import Table
765
+
766
+ if data.get("error"):
767
+ console.print(
768
+ Panel(
769
+ "[yellow]Not a git repository or no commits found[/yellow]",
770
+ title="⚠️ Catchup Status",
771
+ border_style="yellow",
772
+ )
773
+ )
774
+ return
775
+
776
+ # Display contributor summary
777
+ if data["contributors"]:
778
+ console.print("\n[bold cyan]👥 Active Contributors[/bold cyan]")
779
+ for author, count in sorted(
780
+ data["contributors"].items(), key=lambda x: x[1], reverse=True
781
+ ):
782
+ console.print(
783
+ f" • [green]{author}[/green]: {count} commit{'s' if count != 1 else ''}"
784
+ )
785
+
786
+ # Display commit history table
787
+ if data["commits"]:
788
+ console.print(
789
+ f"\n[bold cyan]📝 Last {data['total_commits']} Commits[/bold cyan]"
790
+ )
791
+
792
+ table = Table(
793
+ show_header=True, header_style="bold magenta", border_style="dim"
794
+ )
795
+ table.add_column("#", style="dim", width=3)
796
+ table.add_column("Hash", style="yellow", width=8)
797
+ table.add_column("Author", style="green", width=20)
798
+ table.add_column("Date", style="cyan", width=16)
799
+ table.add_column("Message", style="white")
800
+
801
+ for idx, commit in enumerate(data["commits"], 1):
802
+ # Truncate message if too long
803
+ msg = commit["message"]
804
+ if len(msg) > 80:
805
+ msg = msg[:77] + "..."
806
+
807
+ # Truncate author if too long
808
+ author = commit["author"]
809
+ if len(author) > 18:
810
+ author = author[:18] + "..."
811
+
812
+ table.add_row(str(idx), commit["hash"], author, commit["date"], msg)
813
+
814
+ console.print(table)
815
+
816
+ # Display PM recommendations
817
+ console.print("\n[bold cyan]💡 PM Recommendations[/bold cyan]")
818
+ console.print(
819
+ f" • Total activity: {data['total_commits']} commits from {data['contributor_count']} contributor{'s' if data['contributor_count'] != 1 else ''}"
820
+ )
821
+ console.print(" • Review commit messages for recent project context")
822
+ console.print(" • Identify development patterns and focus areas")
823
+ console.print(" • Use this context to inform current work priorities\n")
824
+
682
825
  def _generate_activity_report(
683
826
  self, git_analysis: Dict, doc_analysis: Dict, days: int = 30
684
827
  ) -> Dict:
@@ -1463,6 +1606,11 @@ preserving valuable project-specific information while refreshing standard secti
1463
1606
  is_flag=True,
1464
1607
  help="Perform lightweight update based on recent git activity (default: 30 days)",
1465
1608
  )
1609
+ @click.option(
1610
+ "--catchup",
1611
+ is_flag=True,
1612
+ help="Show recent commit history from all branches for PM context",
1613
+ )
1466
1614
  @click.option(
1467
1615
  "--non-interactive",
1468
1616
  is_flag=True,
@@ -1499,6 +1647,7 @@ def mpm_init(
1499
1647
  verbose,
1500
1648
  ast_analysis,
1501
1649
  quick_update,
1650
+ catchup,
1502
1651
  non_interactive,
1503
1652
  days,
1504
1653
  export,
@@ -1544,6 +1693,7 @@ def mpm_init(
1544
1693
  preserve_custom=preserve_custom,
1545
1694
  skip_archive=skip_archive,
1546
1695
  quick_update=quick_update,
1696
+ catchup=catchup,
1547
1697
  non_interactive=non_interactive,
1548
1698
  days=days,
1549
1699
  export=export,
@@ -91,6 +91,11 @@ def add_mpm_init_subparser(subparsers: Any) -> None:
91
91
  action="store_true",
92
92
  help="Perform lightweight update based on recent git activity (default: 30 days)",
93
93
  )
94
+ init_group.add_argument(
95
+ "--catchup",
96
+ action="store_true",
97
+ help="Show recent commit history from all branches for PM context",
98
+ )
94
99
  init_group.add_argument(
95
100
  "--non-interactive",
96
101
  action="store_true",
@@ -146,6 +146,23 @@ Fast update based on recent 30-day git activity. Generates activity report and u
146
146
 
147
147
  **Note**: Typing `/mpm-init update` executes `claude-mpm mpm-init --quick-update` automatically.
148
148
 
149
+ ### Catchup Mode
150
+
151
+ Show recent commit history to provide PM with project context:
152
+
153
+ ```bash
154
+ /mpm-init catchup
155
+ ```
156
+
157
+ This displays:
158
+ - Last 25 commits from all branches
159
+ - Author attribution (WHO did WHAT)
160
+ - Temporal context (WHEN)
161
+ - Contributor activity summary
162
+ - PM recommendations based on commit patterns
163
+
164
+ Useful for understanding recent development activity and getting PM up to speed on project changes.
165
+
149
166
  ### Review Project State
150
167
  ```bash
151
168
  /mpm-init --review
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 4.7.7
3
+ Version: 4.7.9
4
4
  Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
5
5
  Author-email: Bob Matsuoka <bob@matsuoka.com>
6
6
  Maintainer: Claude MPM Team
@@ -1,5 +1,5 @@
1
1
  claude_mpm/BUILD_NUMBER,sha256=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
2
- claude_mpm/VERSION,sha256=HB85LgMagRltrQjQ-DOxsxPXgZAlAkVxXen4zNqgCSE,6
2
+ claude_mpm/VERSION,sha256=7-IAGCtmvIwdixVQJDoq-zKnNOnvOHQb0ZEINbbBPJ4,6
3
3
  claude_mpm/__init__.py,sha256=UCw6j9e_tZQ3kJtTqmdfNv7MHyw9nD1jkj80WurwM2g,2064
4
4
  claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
5
5
  claude_mpm/constants.py,sha256=cChN3myrAcF3jC-6DvHnBFTEnwlDk-TAsIXPvUZr_yw,5953
@@ -79,7 +79,7 @@ claude_mpm/cli/commands/analyze_code.py,sha256=yWZpG0aL4XlhcthtvbUqnFSlnvvseWO3V
79
79
  claude_mpm/cli/commands/cleanup.py,sha256=TukZoPVfAFSF4ICfKCQUibczDE73EJP8nbEbfuT8GhE,19768
80
80
  claude_mpm/cli/commands/cleanup_orphaned_agents.py,sha256=JR8crvgrz7Sa6d-SI-gKywok5S9rwc_DzDVk_h85sVs,4467
81
81
  claude_mpm/cli/commands/config.py,sha256=Yfi8WO-10_MYz2QipFw-yEzVvHKNQ6iSQXeyW5J85Cg,18559
82
- claude_mpm/cli/commands/configure.py,sha256=TgWcPzCzIYKpMgcLMaDz4VbXmobeMoDW0oE5tWqyo0E,84906
82
+ claude_mpm/cli/commands/configure.py,sha256=rj8IEAyI-TgRAnTxtOJ8AL2xGhP33gOte8z8WzKhsG4,92942
83
83
  claude_mpm/cli/commands/dashboard.py,sha256=4jPTmTl97DRNNJlYREWeE1iDdkct1uL-vv24MZn9fj4,11403
84
84
  claude_mpm/cli/commands/debug.py,sha256=YCfJ3aYf6hOCvLW_grdfINdEqI4RXVS28VJ7tkZBFS8,47115
85
85
  claude_mpm/cli/commands/doctor.py,sha256=nNKLZG3Qv_UsHNgrmetrWKgS7Pe2Jn5vq5aXyl60wKQ,7310
@@ -96,7 +96,7 @@ claude_mpm/cli/commands/mcp_setup_external.py,sha256=hfBHkaioNa0JRDhahNEc8agyrUw
96
96
  claude_mpm/cli/commands/mcp_tool_commands.py,sha256=q17GzlFT3JiLTrDqwPO2tz1-fKmPO5QU449syTnKTz4,1283
97
97
  claude_mpm/cli/commands/memory.py,sha256=O4T5HGL-Ob_QPt2dZHQvoOrVohnaDKrBjyngq1Mcv1w,26185
98
98
  claude_mpm/cli/commands/monitor.py,sha256=Fjb68hf3dEwTFek2LV8Nh6iU0qEkY7qYlOn32IwNaNg,9566
99
- claude_mpm/cli/commands/mpm_init.py,sha256=H7op69CaE7VPOViTuEiCTeANe708ZAcJpkpgbfG2baM,60311
99
+ claude_mpm/cli/commands/mpm_init.py,sha256=njopKyRBzBT_FL-3LrqishRSeKtIESehzWinhUqmRpM,65724
100
100
  claude_mpm/cli/commands/mpm_init_handler.py,sha256=b1CSwZYJ89wMorKzPOKS-RVxOKR2kT9yv9KQLvKkd2U,3532
101
101
  claude_mpm/cli/commands/run.py,sha256=PB2H55piOPTy4yo4OBgbUCjMlcz9K79wbwpxQVc9m5Q,48225
102
102
  claude_mpm/cli/commands/search.py,sha256=_0qbUnop8v758MHsB0fAop8FVxwygD59tec_-iN7pLE,9806
@@ -118,7 +118,7 @@ claude_mpm/cli/parsers/debug_parser.py,sha256=F7MZdmiXiPfiIPMv21ZUqB2cMT8Ho1LDmp
118
118
  claude_mpm/cli/parsers/mcp_parser.py,sha256=2j6ULhdu55Z2k_-Gu2QxIsFoTQFbDCEMSGePXSuPoQQ,6532
119
119
  claude_mpm/cli/parsers/memory_parser.py,sha256=ZwCDxJEgp-w03L-1tZsWTgisiwamP42s424bA5bvDJc,4760
120
120
  claude_mpm/cli/parsers/monitor_parser.py,sha256=PeoznSi_5Bw6THK_Espl8M20o6dKvvBSmFzAbovkaFQ,4920
121
- claude_mpm/cli/parsers/mpm_init_parser.py,sha256=jMV67caBeC-LV-oOuAGJXvFbiBJQ4nGcl6ggfX0_7jI,7662
121
+ claude_mpm/cli/parsers/mpm_init_parser.py,sha256=iTMd3RjnHzz89Q0O5Lr0MYI_vOUuXQOHHI6D-Zy8PUE,7823
122
122
  claude_mpm/cli/parsers/run_parser.py,sha256=cs34qNonFZG8uYxTYEt0rXi2LcPz3pw8D8hxiywih6w,4927
123
123
  claude_mpm/cli/parsers/search_parser.py,sha256=L8-65kndg-zutSKpzj-eCvTNkeySCZ-WlSHdhk7pEak,6916
124
124
  claude_mpm/cli/parsers/tickets_parser.py,sha256=FYl-VNH7PrZzfZUCcjnf6F7g6JXnL8YDxwrmR5svIcg,6966
@@ -136,7 +136,7 @@ claude_mpm/commands/mpm-agents.md,sha256=JnYPJ-eWvIEEtiCB6iPu182P2xDBRvU3ArVXQ7h
136
136
  claude_mpm/commands/mpm-config.md,sha256=79Eb-srRpEVV3HCHDHZc8SKec6_LVP6HbXDEVkZKLgw,2929
137
137
  claude_mpm/commands/mpm-doctor.md,sha256=ut5LhFKVRw-2ecjMSPsnaTiRuFXa6Q9t-Wgl3CCnQvk,590
138
138
  claude_mpm/commands/mpm-help.md,sha256=zfhpE0Fd-wW5zWmYYAMRMT-xYK8saqbw-HXRD7csJHI,2850
139
- claude_mpm/commands/mpm-init.md,sha256=hVVr71X0w67O08B_x9wP2FRijtkLUVIHWDi-0FSBRPA,10291
139
+ claude_mpm/commands/mpm-init.md,sha256=5Jqb99qqJ_hQ_41lGmyTyDUhm7V7wQiLCYvksd3tZEo,10696
140
140
  claude_mpm/commands/mpm-monitor.md,sha256=onTHf9Yac1KkdZdENtY2Q5jyw0A-vZLYgoKkPCtZLUY,12193
141
141
  claude_mpm/commands/mpm-organize.md,sha256=T-ysjhwgfW9irjUj02vuY_1jeMdabO_zxcShyjmqsiM,10153
142
142
  claude_mpm/commands/mpm-status.md,sha256=oaM4ybL4ffp55nkT9F0mp_5H4tF-wX9mbqK-LEKEqUU,1919
@@ -786,9 +786,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
786
786
  claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
787
787
  claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
788
788
  claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
789
- claude_mpm-4.7.7.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
790
- claude_mpm-4.7.7.dist-info/METADATA,sha256=xVqJzb3mt7CPS-7pxe3uOhh7FeqkBkwociB-EruqX0s,17517
791
- claude_mpm-4.7.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
792
- claude_mpm-4.7.7.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
793
- claude_mpm-4.7.7.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
794
- claude_mpm-4.7.7.dist-info/RECORD,,
789
+ claude_mpm-4.7.9.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
790
+ claude_mpm-4.7.9.dist-info/METADATA,sha256=CKRPRgDYQjMEhisRDRh4swunx7DxHsVA022PufJ0t7Q,17517
791
+ claude_mpm-4.7.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
792
+ claude_mpm-4.7.9.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
793
+ claude_mpm-4.7.9.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
794
+ claude_mpm-4.7.9.dist-info/RECORD,,