claude-mpm 4.7.8__py3-none-any.whl → 4.7.11__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.8
1
+ 4.7.11
@@ -36,6 +36,7 @@ from .commands import ( # run_guarded_session is imported lazily to avoid loadi
36
36
  )
37
37
  from .commands.analyze_code import manage_analyze_code
38
38
  from .commands.dashboard import manage_dashboard
39
+ from .commands.upgrade import upgrade
39
40
  from .parser import create_parser, preprocess_args
40
41
  from .utils import ensure_directories, setup_logging
41
42
 
@@ -232,6 +233,9 @@ def main(argv: Optional[list] = None):
232
233
  # Re-enabled: MCP pre-warming is safe with subprocess daemon (v4.2.40)
233
234
  # The subprocess approach avoids fork() issues entirely
234
235
  _verify_mcp_gateway_startup()
236
+
237
+ # Check for updates (non-blocking, async)
238
+ _check_for_updates_async()
235
239
  else:
236
240
  # Still need directories for configure command to work
237
241
  ensure_directories()
@@ -544,6 +548,83 @@ def _verify_mcp_gateway_startup():
544
548
  # Continue execution - MCP gateway issues shouldn't block startup
545
549
 
546
550
 
551
+ def _check_for_updates_async():
552
+ """
553
+ Check for updates in background thread (non-blocking).
554
+
555
+ WHY: Users should be notified of new versions and have an easy way to upgrade
556
+ without manually checking PyPI/npm. This runs asynchronously on startup to avoid
557
+ blocking the CLI.
558
+
559
+ DESIGN DECISION: This is non-blocking and non-critical - failures are logged
560
+ but don't prevent startup. Only runs for pip/pipx/npm installations, skips
561
+ editable/development installations.
562
+ """
563
+
564
+ def run_update_check():
565
+ """Inner function to run in background thread."""
566
+ loop = None
567
+ try:
568
+ import asyncio
569
+
570
+ from ..core.logger import get_logger
571
+ from ..services.self_upgrade_service import SelfUpgradeService
572
+
573
+ logger = get_logger("upgrade_check")
574
+
575
+ # Create new event loop for this thread
576
+ loop = asyncio.new_event_loop()
577
+ asyncio.set_event_loop(loop)
578
+
579
+ # Create upgrade service and check for updates
580
+ upgrade_service = SelfUpgradeService()
581
+
582
+ # Skip for editable installs (development mode)
583
+ from ..services.self_upgrade_service import InstallationMethod
584
+
585
+ if upgrade_service.installation_method == InstallationMethod.EDITABLE:
586
+ logger.debug("Skipping version check for editable installation")
587
+ return
588
+
589
+ # Check and prompt for upgrade if available (non-blocking)
590
+ loop.run_until_complete(upgrade_service.check_and_prompt_on_startup())
591
+
592
+ except Exception as e:
593
+ # Non-critical - log but don't fail startup
594
+ try:
595
+ from ..core.logger import get_logger
596
+
597
+ logger = get_logger("upgrade_check")
598
+ logger.debug(f"Update check failed (non-critical): {e}")
599
+ except Exception:
600
+ pass # Avoid any errors in error handling
601
+ finally:
602
+ # Properly clean up event loop
603
+ if loop is not None:
604
+ try:
605
+ # Cancel all running tasks
606
+ pending = asyncio.all_tasks(loop)
607
+ for task in pending:
608
+ task.cancel()
609
+ # Wait for tasks to complete cancellation
610
+ if pending:
611
+ loop.run_until_complete(
612
+ asyncio.gather(*pending, return_exceptions=True)
613
+ )
614
+ except Exception:
615
+ pass # Ignore cleanup errors
616
+ finally:
617
+ loop.close()
618
+ # Clear the event loop reference to help with cleanup
619
+ asyncio.set_event_loop(None)
620
+
621
+ # Run update check in background thread to avoid blocking startup
622
+ import threading
623
+
624
+ update_check_thread = threading.Thread(target=run_update_check, daemon=True)
625
+ update_check_thread.start()
626
+
627
+
547
628
  def _ensure_run_attributes(args):
548
629
  """
549
630
  Ensure run command attributes exist when defaulting to run.
@@ -657,6 +738,7 @@ def _execute_command(command: str, args) -> int:
657
738
  CLICommands.CLEANUP.value: cleanup_memory,
658
739
  CLICommands.MCP.value: manage_mcp,
659
740
  CLICommands.DOCTOR.value: run_doctor,
741
+ CLICommands.UPGRADE.value: upgrade,
660
742
  "debug": manage_debug, # Add debug command
661
743
  "mpm-init": None, # Will be handled separately with lazy import
662
744
  }
@@ -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,
@@ -0,0 +1,152 @@
1
+ """
2
+ Upgrade command implementation for claude-mpm.
3
+
4
+ WHY: This module provides a manual upgrade command that allows users to check
5
+ for and install the latest version of claude-mpm without waiting for the
6
+ automatic startup check.
7
+
8
+ DESIGN DECISIONS:
9
+ - Use BaseCommand for consistent CLI patterns
10
+ - Leverage SelfUpgradeService for upgrade functionality
11
+ - Support multiple installation methods (pip, pipx, npm)
12
+ - Skip editable/development installations (must upgrade manually)
13
+ - Provide clear feedback on available updates and upgrade progress
14
+ """
15
+
16
+ import asyncio
17
+
18
+ from ..shared import BaseCommand, CommandResult
19
+
20
+
21
+ class UpgradeCommand(BaseCommand):
22
+ """Upgrade command using shared utilities."""
23
+
24
+ def __init__(self):
25
+ super().__init__("upgrade")
26
+
27
+ def validate_args(self, args) -> str:
28
+ """Validate command arguments."""
29
+ # Upgrade command doesn't require specific validation
30
+ return None
31
+
32
+ def run(self, args) -> CommandResult:
33
+ """Execute the upgrade command."""
34
+ try:
35
+ from ...services.self_upgrade_service import (
36
+ InstallationMethod,
37
+ SelfUpgradeService,
38
+ )
39
+
40
+ # Create upgrade service
41
+ upgrade_service = SelfUpgradeService()
42
+
43
+ # Check installation method
44
+ if upgrade_service.installation_method == InstallationMethod.EDITABLE:
45
+ self.logger.info(
46
+ "Editable installation detected - upgrade must be done manually"
47
+ )
48
+ print("\n⚠️ Editable Installation Detected")
49
+ print(
50
+ "\nYou are running claude-mpm from an editable installation (development mode)."
51
+ )
52
+ print("To upgrade, run:")
53
+ print(" cd /path/to/claude-mpm")
54
+ print(" git pull")
55
+ print(" pip install -e .")
56
+ return CommandResult.success_result(
57
+ "Upgrade information provided for editable installation"
58
+ )
59
+
60
+ # Check for updates
61
+ print("\n🔍 Checking for updates...")
62
+ loop = asyncio.new_event_loop()
63
+ asyncio.set_event_loop(loop)
64
+ try:
65
+ update_info = loop.run_until_complete(
66
+ upgrade_service.check_for_update()
67
+ )
68
+ finally:
69
+ loop.close()
70
+
71
+ if not update_info or not update_info.get("update_available"):
72
+ print(
73
+ f"\n✅ You are already on the latest version (v{upgrade_service.current_version})"
74
+ )
75
+ return CommandResult.success_result("Already on latest version")
76
+
77
+ # Display update information
78
+ current = update_info["current"]
79
+ latest = update_info["latest"]
80
+ method = update_info.get("installation_method", "unknown")
81
+
82
+ print("\n🎉 New version available!")
83
+ print(f" Current: v{current}")
84
+ print(f" Latest: v{latest}")
85
+ print(f" Installation method: {method}")
86
+
87
+ # Check if --yes flag is set for non-interactive upgrade
88
+ force_upgrade = getattr(args, "yes", False) or getattr(args, "force", False)
89
+
90
+ if not force_upgrade:
91
+ # Prompt user for confirmation
92
+ if not upgrade_service.prompt_for_upgrade(update_info):
93
+ print("\n⏸️ Upgrade cancelled by user")
94
+ return CommandResult.success_result("Upgrade cancelled by user")
95
+
96
+ # Perform upgrade
97
+ success, message = upgrade_service.perform_upgrade(update_info)
98
+ print(f"\n{message}")
99
+
100
+ if success:
101
+ # Restart after successful upgrade
102
+ upgrade_service.restart_after_upgrade()
103
+ # Note: This line won't be reached as restart replaces process
104
+ return CommandResult.success_result(f"Upgraded to v{latest}")
105
+ return CommandResult.error_result("Upgrade failed")
106
+
107
+ except Exception as e:
108
+ self.logger.error(f"Error during upgrade: {e}", exc_info=True)
109
+ return CommandResult.error_result(f"Error during upgrade: {e}")
110
+
111
+
112
+ def add_upgrade_parser(subparsers):
113
+ """
114
+ Add upgrade command parser.
115
+
116
+ WHY: This command helps users check for and install the latest version of
117
+ claude-mpm without waiting for the automatic startup check.
118
+
119
+ Args:
120
+ subparsers: The subparser action object to add the upgrade command to
121
+ """
122
+ parser = subparsers.add_parser(
123
+ "upgrade",
124
+ help="Check for and install latest claude-mpm version",
125
+ description="Check for updates and upgrade claude-mpm to the latest version from PyPI/npm",
126
+ )
127
+
128
+ parser.add_argument(
129
+ "-y",
130
+ "--yes",
131
+ action="store_true",
132
+ help="Skip confirmation prompt and upgrade immediately if available",
133
+ )
134
+
135
+ parser.add_argument(
136
+ "--force",
137
+ action="store_true",
138
+ help="Force upgrade even if already on latest version (alias for --yes)",
139
+ )
140
+
141
+ return parser
142
+
143
+
144
+ def upgrade(args):
145
+ """
146
+ Main entry point for upgrade command.
147
+
148
+ This function maintains backward compatibility while using the new BaseCommand pattern.
149
+ """
150
+ command = UpgradeCommand()
151
+ result = command.execute(args)
152
+ return result.exit_code
@@ -423,6 +423,11 @@ def create_parser(
423
423
 
424
424
  add_doctor_parser(subparsers)
425
425
 
426
+ # Add upgrade command
427
+ from ..commands.upgrade import add_upgrade_parser
428
+
429
+ add_upgrade_parser(subparsers)
430
+
426
431
  # Add verify command for MCP service verification
427
432
  from ..commands.verify import add_parser as add_verify_parser
428
433
 
@@ -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
claude_mpm/constants.py CHANGED
@@ -44,6 +44,7 @@ class CLICommands(str, Enum):
44
44
  MCP = "mcp"
45
45
  DOCTOR = "doctor"
46
46
  DASHBOARD = "dashboard"
47
+ UPGRADE = "upgrade"
47
48
 
48
49
  def with_prefix(self, prefix: CLIPrefix = CLIPrefix.MPM) -> str:
49
50
  """Get command with prefix."""
@@ -40,9 +40,11 @@ class MemoryManager(IMemoryManager):
40
40
  4. Legacy format migration (e.g., PM.md -> PM_memories.md)
41
41
  5. Memory caching for performance
42
42
 
43
- Memory Loading Order:
44
- - User-level memories: ~/.claude-mpm/memories/ (global defaults)
45
- - Project-level memories: ./.claude-mpm/memories/ (overrides user)
43
+ Memory Scope:
44
+ - Project-level ONLY: ./.claude-mpm/memories/ (project-scoped isolation)
45
+ - User-level memories are NOT loaded to prevent cross-project contamination
46
+
47
+ Note: As of v4.7.10+, memories are strictly project-scoped for complete isolation.
46
48
  """
47
49
 
48
50
  def __init__(
@@ -279,7 +281,10 @@ class MemoryManager(IMemoryManager):
279
281
 
280
282
  def _load_actual_memories(self, deployed_agents: Set[str]) -> Dict[str, Any]:
281
283
  """
282
- Load actual memories from both user and project directories.
284
+ Load actual memories from project directory only.
285
+
286
+ Memories are project-scoped to ensure complete isolation between projects.
287
+ User-level memories are no longer supported to prevent cross-project contamination.
283
288
 
284
289
  Args:
285
290
  deployed_agents: Set of deployed agent names
@@ -287,32 +292,14 @@ class MemoryManager(IMemoryManager):
287
292
  Returns:
288
293
  Dictionary with actual_memories and agent_memories
289
294
  """
290
- # Define memory directories in priority order
291
- user_memories_dir = Path.home() / ".claude-mpm" / "memories"
295
+ # Define project memory directory (project-scoped only)
292
296
  project_memories_dir = Path.cwd() / ".claude-mpm" / "memories"
293
297
 
294
298
  # Dictionary to store aggregated memories
295
299
  pm_memories = []
296
300
  agent_memories_dict = {}
297
301
 
298
- # Load memories from user directory first
299
- if user_memories_dir.exists():
300
- self.logger.info(
301
- f"Loading user-level memory files from: {user_memories_dir}"
302
- )
303
- self._load_memories_from_directory(
304
- user_memories_dir,
305
- deployed_agents,
306
- pm_memories,
307
- agent_memories_dict,
308
- "user",
309
- )
310
- else:
311
- self.logger.debug(
312
- f"No user memories directory found at: {user_memories_dir}"
313
- )
314
-
315
- # Load memories from project directory (overrides user memories)
302
+ # Load memories from project directory only
316
303
  if project_memories_dir.exists():
317
304
  self.logger.info(
318
305
  f"Loading project-level memory files from: {project_memories_dir}"
@@ -0,0 +1,342 @@
1
+ """
2
+ Self-Upgrade Service
3
+ ====================
4
+
5
+ Handles version checking and self-upgrade functionality for claude-mpm.
6
+ Supports pip, pipx, and npm installations with automatic detection.
7
+
8
+ WHY: Users should be notified of updates and have an easy way to upgrade
9
+ without manually running installation commands.
10
+
11
+ DESIGN DECISIONS:
12
+ - Detects installation method (pip/pipx/npm/editable)
13
+ - Non-blocking version checks with caching
14
+ - Interactive upgrade prompts with confirmation
15
+ - Automatic restart after upgrade
16
+ - Graceful failure handling (never breaks existing installation)
17
+ """
18
+
19
+ import os
20
+ import subprocess
21
+ import sys
22
+ from datetime import datetime, timezone
23
+ from pathlib import Path
24
+ from typing import Dict, Optional, Tuple
25
+
26
+ from packaging import version
27
+
28
+ from ..core.logger import get_logger
29
+ from ..core.unified_paths import PathContext
30
+ from .mcp_gateway.utils.package_version_checker import PackageVersionChecker
31
+
32
+
33
+ class InstallationMethod:
34
+ """Installation method enumeration."""
35
+
36
+ PIP = "pip"
37
+ PIPX = "pipx"
38
+ NPM = "npm"
39
+ EDITABLE = "editable"
40
+ UNKNOWN = "unknown"
41
+
42
+
43
+ class SelfUpgradeService:
44
+ """
45
+ Service for checking and performing self-upgrades.
46
+
47
+ Capabilities:
48
+ - Detect current installation method
49
+ - Check PyPI/npm for latest version
50
+ - Prompt user for upgrade confirmation
51
+ - Execute upgrade command
52
+ - Restart after upgrade
53
+ """
54
+
55
+ def __init__(self):
56
+ """Initialize the self-upgrade service."""
57
+ self.logger = get_logger("SelfUpgradeService")
58
+ self.version_checker = PackageVersionChecker()
59
+ self.current_version = self._get_current_version()
60
+ self.installation_method = self._detect_installation_method()
61
+
62
+ def _get_current_version(self) -> str:
63
+ """
64
+ Get the current installed version.
65
+
66
+ Returns:
67
+ Version string (e.g., "4.7.10")
68
+ """
69
+ try:
70
+ from .. import __version__
71
+
72
+ return __version__
73
+ except ImportError:
74
+ # Fallback to VERSION file
75
+ try:
76
+ version_file = Path(__file__).parent.parent / "VERSION"
77
+ if version_file.exists():
78
+ return version_file.read_text().strip()
79
+ except Exception:
80
+ pass
81
+
82
+ return "unknown"
83
+
84
+ def _detect_installation_method(self) -> str:
85
+ """
86
+ Detect how claude-mpm was installed.
87
+
88
+ Returns:
89
+ Installation method constant
90
+ """
91
+ # Check for editable install
92
+ if PathContext.detect_deployment_context().name in [
93
+ "DEVELOPMENT",
94
+ "EDITABLE_INSTALL",
95
+ ]:
96
+ return InstallationMethod.EDITABLE
97
+
98
+ # Check for pipx by looking at executable path
99
+ executable = sys.executable
100
+ if "pipx" in executable:
101
+ return InstallationMethod.PIPX
102
+
103
+ # Check if npm wrapper is present
104
+ try:
105
+ result = subprocess.run(
106
+ ["npm", "list", "-g", "claude-mpm"],
107
+ check=False,
108
+ capture_output=True,
109
+ text=True,
110
+ timeout=5,
111
+ )
112
+ if result.returncode == 0 and "claude-mpm" in result.stdout:
113
+ return InstallationMethod.NPM
114
+ except Exception:
115
+ pass
116
+
117
+ # Default to pip
118
+ return InstallationMethod.PIP
119
+
120
+ async def check_for_update(
121
+ self, cache_ttl: Optional[int] = None
122
+ ) -> Optional[Dict[str, any]]:
123
+ """
124
+ Check if an update is available.
125
+
126
+ Args:
127
+ cache_ttl: Cache time-to-live in seconds (default: 24 hours)
128
+
129
+ Returns:
130
+ Dict with update info or None:
131
+ {
132
+ "current": "4.7.10",
133
+ "latest": "4.7.11",
134
+ "update_available": True,
135
+ "installation_method": "pipx",
136
+ "upgrade_command": "pipx upgrade claude-mpm"
137
+ }
138
+ """
139
+ if self.current_version == "unknown":
140
+ self.logger.warning("Cannot check for updates: version unknown")
141
+ return None
142
+
143
+ # Check PyPI for Python installations
144
+ if self.installation_method in [
145
+ InstallationMethod.PIP,
146
+ InstallationMethod.PIPX,
147
+ ]:
148
+ result = await self.version_checker.check_for_update(
149
+ "claude-mpm", self.current_version, cache_ttl
150
+ )
151
+ if result and result.get("update_available"):
152
+ result["installation_method"] = self.installation_method
153
+ result["upgrade_command"] = self._get_upgrade_command()
154
+ return result
155
+
156
+ # Check npm for npm installations
157
+ elif self.installation_method == InstallationMethod.NPM:
158
+ npm_version = await self._check_npm_version()
159
+ if npm_version:
160
+ current_ver = version.parse(self.current_version)
161
+ latest_ver = version.parse(npm_version)
162
+ if latest_ver > current_ver:
163
+ return {
164
+ "current": self.current_version,
165
+ "latest": npm_version,
166
+ "update_available": True,
167
+ "installation_method": InstallationMethod.NPM,
168
+ "upgrade_command": self._get_upgrade_command(),
169
+ "checked_at": datetime.now(timezone.utc).isoformat(),
170
+ }
171
+
172
+ return None
173
+
174
+ async def _check_npm_version(self) -> Optional[str]:
175
+ """
176
+ Check npm registry for latest version.
177
+
178
+ Returns:
179
+ Latest version string or None
180
+ """
181
+ try:
182
+ result = subprocess.run(
183
+ ["npm", "view", "claude-mpm", "version"],
184
+ check=False,
185
+ capture_output=True,
186
+ text=True,
187
+ timeout=10,
188
+ )
189
+ if result.returncode == 0:
190
+ return result.stdout.strip()
191
+ except Exception as e:
192
+ self.logger.debug(f"npm version check failed: {e}")
193
+
194
+ return None
195
+
196
+ def _get_upgrade_command(self) -> str:
197
+ """
198
+ Get the appropriate upgrade command for current installation method.
199
+
200
+ Returns:
201
+ Shell command string to upgrade claude-mpm
202
+ """
203
+ if self.installation_method == InstallationMethod.PIPX:
204
+ return "pipx upgrade claude-mpm"
205
+ if self.installation_method == InstallationMethod.NPM:
206
+ return "npm update -g claude-mpm"
207
+ if self.installation_method == InstallationMethod.PIP:
208
+ return f"{sys.executable} -m pip install --upgrade claude-mpm"
209
+ if self.installation_method == InstallationMethod.EDITABLE:
210
+ return "git pull && pip install -e ."
211
+ return "pip install --upgrade claude-mpm"
212
+
213
+ def prompt_for_upgrade(self, update_info: Dict[str, any]) -> bool:
214
+ """
215
+ Prompt user to upgrade.
216
+
217
+ Args:
218
+ update_info: Update information dict
219
+
220
+ Returns:
221
+ True if user confirms upgrade, False otherwise
222
+ """
223
+ current = update_info["current"]
224
+ latest = update_info["latest"]
225
+ method = update_info.get("installation_method", "unknown")
226
+
227
+ print("\n🎉 New version available!")
228
+ print(f" Current: v{current}")
229
+ print(f" Latest: v{latest}")
230
+ print(f" Installation method: {method}")
231
+ print(f"\nTo upgrade, run: {update_info['upgrade_command']}")
232
+
233
+ try:
234
+ response = input("\nWould you like to upgrade now? [y/N]: ").strip().lower()
235
+ return response in ["y", "yes"]
236
+ except (KeyboardInterrupt, EOFError):
237
+ print("\n")
238
+ return False
239
+
240
+ def perform_upgrade(self, update_info: Dict[str, any]) -> Tuple[bool, str]:
241
+ """
242
+ Perform the upgrade.
243
+
244
+ Args:
245
+ update_info: Update information dict
246
+
247
+ Returns:
248
+ Tuple of (success: bool, message: str)
249
+ """
250
+ command = update_info["upgrade_command"]
251
+
252
+ # Don't upgrade editable installs automatically
253
+ if self.installation_method == InstallationMethod.EDITABLE:
254
+ return (
255
+ False,
256
+ "Editable installation detected. Please update manually with: git pull && pip install -e .",
257
+ )
258
+
259
+ print("\n⏳ Upgrading claude-mpm...")
260
+ print(f" Running: {command}")
261
+
262
+ try:
263
+ # Execute upgrade command
264
+ result = subprocess.run(
265
+ command,
266
+ check=False,
267
+ shell=True,
268
+ capture_output=True,
269
+ text=True,
270
+ timeout=300, # 5 minutes
271
+ )
272
+
273
+ if result.returncode == 0:
274
+ return (True, f"✅ Successfully upgraded to v{update_info['latest']}")
275
+ error_msg = result.stderr or result.stdout or "Unknown error"
276
+ return (False, f"❌ Upgrade failed: {error_msg}")
277
+
278
+ except subprocess.TimeoutExpired:
279
+ return (False, "❌ Upgrade timed out")
280
+ except Exception as e:
281
+ return (False, f"❌ Upgrade failed: {e!s}")
282
+
283
+ def restart_after_upgrade(self) -> None:
284
+ """
285
+ Restart claude-mpm after a successful upgrade.
286
+
287
+ Preserves original command line arguments.
288
+ """
289
+ print("\n🔄 Restarting claude-mpm...")
290
+
291
+ try:
292
+ # Get current command line arguments
293
+ args = sys.argv[:]
294
+
295
+ # Replace current process with new one
296
+ if self.installation_method == InstallationMethod.PIPX:
297
+ # Use pipx run
298
+ os.execvp("pipx", ["pipx", "run", "claude-mpm", *args[1:]])
299
+ elif self.installation_method == InstallationMethod.NPM:
300
+ # Use npm executable
301
+ os.execvp("claude-mpm", args)
302
+ else:
303
+ # Use Python executable
304
+ os.execvp(sys.executable, [sys.executable, *args])
305
+
306
+ except Exception as e:
307
+ self.logger.error(f"Failed to restart: {e}")
308
+ print(f"\n⚠️ Restart failed: {e}")
309
+ print("Please restart claude-mpm manually.")
310
+
311
+ async def check_and_prompt_on_startup(
312
+ self, auto_upgrade: bool = False
313
+ ) -> Optional[Dict[str, any]]:
314
+ """
315
+ Check for updates on startup and optionally prompt user.
316
+
317
+ Args:
318
+ auto_upgrade: If True, upgrade without prompting (use with caution)
319
+
320
+ Returns:
321
+ Update info if available, None otherwise
322
+ """
323
+ # Skip for editable installs
324
+ if self.installation_method == InstallationMethod.EDITABLE:
325
+ return None
326
+
327
+ try:
328
+ update_info = await self.check_for_update()
329
+
330
+ if update_info and update_info.get("update_available"):
331
+ if auto_upgrade or self.prompt_for_upgrade(update_info):
332
+ success, message = self.perform_upgrade(update_info)
333
+ print(message)
334
+ if success:
335
+ self.restart_after_upgrade()
336
+
337
+ return update_info
338
+
339
+ except Exception as e:
340
+ self.logger.debug(f"Startup version check failed: {e}")
341
+
342
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 4.7.8
3
+ Version: 4.7.11
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,8 +1,8 @@
1
1
  claude_mpm/BUILD_NUMBER,sha256=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
2
- claude_mpm/VERSION,sha256=XWuLX-eNJrJGfyBd1GUrzseop7w6skHL0X8KHS__eFE,6
2
+ claude_mpm/VERSION,sha256=8NJI62Tp7GTnD5qeEDJUZAnPvuDLKbXAbLj5maMHLnM,7
3
3
  claude_mpm/__init__.py,sha256=UCw6j9e_tZQ3kJtTqmdfNv7MHyw9nD1jkj80WurwM2g,2064
4
4
  claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
5
- claude_mpm/constants.py,sha256=cChN3myrAcF3jC-6DvHnBFTEnwlDk-TAsIXPvUZr_yw,5953
5
+ claude_mpm/constants.py,sha256=sLjJF6Kw7H4V9WWeaEYltM-77TgXqzEMX5vx4ukM5-0,5977
6
6
  claude_mpm/init.py,sha256=HFJR_JmTHa53FAhbGusm3P1iXT6VR6V7S-YJz4pOZ1Q,15967
7
7
  claude_mpm/ticket_wrapper.py,sha256=qe5xY579t7_7fK5nyeAfHN_fr7CXdeOD3jfXEc8-7yo,828
8
8
  claude_mpm/agents/BASE_AGENT_TEMPLATE.md,sha256=aK9qxS1FRpm_8VaB5GI2I6YA9Wr8dGHuea_txMFe44M,5524
@@ -64,7 +64,7 @@ claude_mpm/agents/templates/.claude-mpm/memories/README.md,sha256=vEiG7cPjHRZfwX
64
64
  claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md,sha256=KMZSJrQi-wHOwfl2C0m3A4PpC4QuBtDolAtVybGahKc,77
65
65
  claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md,sha256=UBm4BycXtdaa-_l1VCh0alTGGOUSsnCbpKwbFuI-mUY,2219
66
66
  claude_mpm/agents/templates/logs/prompts/agent_engineer_20250901_010124_142.md,sha256=oPvFSYFnmJ4TkbTe4AZnNHWaJMJ-xqZP2WM6scUKQKo,13089
67
- claude_mpm/cli/__init__.py,sha256=0qyZe1R_9I_GJdyIfF6GatbZUVcaclxtvglPjN7dAIA,26270
67
+ claude_mpm/cli/__init__.py,sha256=Ed8uPUqS5FmIAMxxIPxpo5JJX2ZWXIe5v-vx8K9jBEE,29373
68
68
  claude_mpm/cli/__main__.py,sha256=WnVGBwe10InxuZjJRFdwuMF6Gh16aXox6zFgxr0sRXk,847
69
69
  claude_mpm/cli/parser.py,sha256=Vqx9n-6Xo1uNhXR4rThmgWpZXTr0nOtkgDf3oMS9b0g,5855
70
70
  claude_mpm/cli/startup_logging.py,sha256=RTuyd6CbhiFQz7Z07LDDhK_ZAnZfuJ9B0NghVSntHFI,29390
@@ -96,12 +96,13 @@ 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
103
103
  claude_mpm/cli/commands/tickets.py,sha256=kl2dklTBnG3Y4jUUJ_PcEVsTx4CtVJfkGWboWBx_mQM,21234
104
104
  claude_mpm/cli/commands/uninstall.py,sha256=KGlVG6veEs1efLVjrZ3wSty7e1zVR9wpt-VXQA1RzWw,5945
105
+ claude_mpm/cli/commands/upgrade.py,sha256=NYMVONNlj78WHoQ6eyVInroE95AeQxUY2_TpjYFTdYE,5409
105
106
  claude_mpm/cli/commands/verify.py,sha256=wmu2UYINK15q2e34TdlTyamvtLDE7r3Oj_XT9zpT5Kk,3687
106
107
  claude_mpm/cli/interactive/__init__.py,sha256=vQqUCgPFvLYA1Vkq-5pnY7Ow3A-IgdM0SByfNL1ZLTk,433
107
108
  claude_mpm/cli/interactive/agent_wizard.py,sha256=v3nKcLusU49ewjCqvgUWsG35UGc82pC7_Uv-ZzAHZ-c,36201
@@ -110,7 +111,7 @@ claude_mpm/cli/parsers/agent_manager_parser.py,sha256=TQEIm638ELM4X_AAGcn6WrJxlt
110
111
  claude_mpm/cli/parsers/agents_parser.py,sha256=R-9ESNXdjqr5iCEIcmbb2EPvcswW9UfJzMj5bAkgI4U,9042
111
112
  claude_mpm/cli/parsers/analyze_code_parser.py,sha256=cpJSMFbc3mqB4qrMBIEZiikzPekC2IQX-cjt9U2fHW4,5356
112
113
  claude_mpm/cli/parsers/analyze_parser.py,sha256=E00Ao0zwzbJPchs_AJt-aoQ7LQEtJPXRCNQ6Piivb4o,3908
113
- claude_mpm/cli/parsers/base_parser.py,sha256=Wejn4lAo-rMUQVP13PAehE6NoqmH2AD50HLKDf3QAKM,14169
114
+ claude_mpm/cli/parsers/base_parser.py,sha256=iGYJ_-wIXkL4iVclJgrzQ2kz_of3CIqCwIM-FnFKEQI,14298
114
115
  claude_mpm/cli/parsers/config_parser.py,sha256=wp6NbV8_p9txP28MXFcQrri0JDIfGFM7u4aJbYJXcYQ,2699
115
116
  claude_mpm/cli/parsers/configure_parser.py,sha256=t3cwAQX3BfljDDRJH3i0LplpRprw5jdKcI9Uy3M8xtE,4382
116
117
  claude_mpm/cli/parsers/dashboard_parser.py,sha256=JBCM6v_iZhADr_Fwtk_d3up9AOod1avMab-vkNE61gE,3460
@@ -118,7 +119,7 @@ claude_mpm/cli/parsers/debug_parser.py,sha256=F7MZdmiXiPfiIPMv21ZUqB2cMT8Ho1LDmp
118
119
  claude_mpm/cli/parsers/mcp_parser.py,sha256=2j6ULhdu55Z2k_-Gu2QxIsFoTQFbDCEMSGePXSuPoQQ,6532
119
120
  claude_mpm/cli/parsers/memory_parser.py,sha256=ZwCDxJEgp-w03L-1tZsWTgisiwamP42s424bA5bvDJc,4760
120
121
  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
122
+ claude_mpm/cli/parsers/mpm_init_parser.py,sha256=iTMd3RjnHzz89Q0O5Lr0MYI_vOUuXQOHHI6D-Zy8PUE,7823
122
123
  claude_mpm/cli/parsers/run_parser.py,sha256=cs34qNonFZG8uYxTYEt0rXi2LcPz3pw8D8hxiywih6w,4927
123
124
  claude_mpm/cli/parsers/search_parser.py,sha256=L8-65kndg-zutSKpzj-eCvTNkeySCZ-WlSHdhk7pEak,6916
124
125
  claude_mpm/cli/parsers/tickets_parser.py,sha256=FYl-VNH7PrZzfZUCcjnf6F7g6JXnL8YDxwrmR5svIcg,6966
@@ -136,7 +137,7 @@ claude_mpm/commands/mpm-agents.md,sha256=JnYPJ-eWvIEEtiCB6iPu182P2xDBRvU3ArVXQ7h
136
137
  claude_mpm/commands/mpm-config.md,sha256=79Eb-srRpEVV3HCHDHZc8SKec6_LVP6HbXDEVkZKLgw,2929
137
138
  claude_mpm/commands/mpm-doctor.md,sha256=ut5LhFKVRw-2ecjMSPsnaTiRuFXa6Q9t-Wgl3CCnQvk,590
138
139
  claude_mpm/commands/mpm-help.md,sha256=zfhpE0Fd-wW5zWmYYAMRMT-xYK8saqbw-HXRD7csJHI,2850
139
- claude_mpm/commands/mpm-init.md,sha256=hVVr71X0w67O08B_x9wP2FRijtkLUVIHWDi-0FSBRPA,10291
140
+ claude_mpm/commands/mpm-init.md,sha256=5Jqb99qqJ_hQ_41lGmyTyDUhm7V7wQiLCYvksd3tZEo,10696
140
141
  claude_mpm/commands/mpm-monitor.md,sha256=onTHf9Yac1KkdZdENtY2Q5jyw0A-vZLYgoKkPCtZLUY,12193
141
142
  claude_mpm/commands/mpm-organize.md,sha256=T-ysjhwgfW9irjUj02vuY_1jeMdabO_zxcShyjmqsiM,10153
142
143
  claude_mpm/commands/mpm-status.md,sha256=oaM4ybL4ffp55nkT9F0mp_5H4tF-wX9mbqK-LEKEqUU,1919
@@ -427,6 +428,7 @@ claude_mpm/services/project_port_allocator.py,sha256=L_EPuX_vGdv8vWWRVTpRk7F-v7u
427
428
  claude_mpm/services/recovery_manager.py,sha256=ptUYsguF6oKWnJnzPRSBuzUFVDXIcvsS3svT6MBTqCQ,25686
428
429
  claude_mpm/services/response_tracker.py,sha256=wBPgI5fH40xc7NHcCXQFOTPFTPSVbAZPqXIauXOJA5g,9575
429
430
  claude_mpm/services/runner_configuration_service.py,sha256=Qs84yrZfQv-DC0I2Xah1Qt9eunH4gS7LNMZ0mmymcqA,21311
431
+ claude_mpm/services/self_upgrade_service.py,sha256=PuzHFQ3fQx7I9rmINLWs86o2KVyr-1StolLR-xnkOrQ,11246
430
432
  claude_mpm/services/session_management_service.py,sha256=axGZXpexWcYGnOMt5o8UQnFJUN3aVyjXRpFAcFrNKeg,10060
431
433
  claude_mpm/services/session_manager.py,sha256=ElguSoiy725VWS2iEqtcn0dUPIRrQrrON74aUzrWjRE,5147
432
434
  claude_mpm/services/socketio_client_manager.py,sha256=cZjIsnoi2VPXFA-5pNhoz5Vv3qv0iuSTy510TgAJU4U,18179
@@ -546,7 +548,7 @@ claude_mpm/services/core/__init__.py,sha256=evEayLlBqJvxMZhrhuK6aagXmNrKGSj8Jm9O
546
548
  claude_mpm/services/core/base.py,sha256=iA-F7DgGp-FJIMvQTiHQ68RkG_k-AtUWlArJPMw6ZPk,7297
547
549
  claude_mpm/services/core/cache_manager.py,sha256=dBHvvI6M67kaxFtAubi4IsWfbDZSct1siyKHTmCIrW8,11567
548
550
  claude_mpm/services/core/interfaces.py,sha256=FbLhWiOUMlvBfqjYBCeoWgsmRscQGBKteRMW-BGz4hQ,1261
549
- claude_mpm/services/core/memory_manager.py,sha256=2m-BYrNwh1yFjvz1VGB_Ndl_dtSp1xhpBcATaBiJQu0,26642
551
+ claude_mpm/services/core/memory_manager.py,sha256=aajBuvBzTq0-EZrjnBjGRdUSaa6MpbfqHAtCePnn7aQ,26258
550
552
  claude_mpm/services/core/path_resolver.py,sha256=VtqiOEUlskAr9nRsaS9uXQSSjDD8dIrYfNML0zLY2zo,17764
551
553
  claude_mpm/services/core/service_container.py,sha256=3hDwFUahxFZnokPzwgXqBP9swvZhLztKQqwe9xEHgsw,18497
552
554
  claude_mpm/services/core/service_interfaces.py,sha256=dlNp0K4gaMcLiNSZxXjsL-kto6vipYw87pBuFK7oVNo,10770
@@ -786,9 +788,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
786
788
  claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
787
789
  claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
788
790
  claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
789
- claude_mpm-4.7.8.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
790
- claude_mpm-4.7.8.dist-info/METADATA,sha256=0zNQ_GqGZeqiE8cxEiJK7LQNZ6gfeuJyvsVrunOlrEE,17517
791
- claude_mpm-4.7.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
792
- claude_mpm-4.7.8.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
793
- claude_mpm-4.7.8.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
794
- claude_mpm-4.7.8.dist-info/RECORD,,
791
+ claude_mpm-4.7.11.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
792
+ claude_mpm-4.7.11.dist-info/METADATA,sha256=xrQscwKCC7qPBxPTM3pLS0WWsrH6ale3r3620JQqids,17518
793
+ claude_mpm-4.7.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
794
+ claude_mpm-4.7.11.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
795
+ claude_mpm-4.7.11.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
796
+ claude_mpm-4.7.11.dist-info/RECORD,,