claude-mpm 3.1.3__py3-none-any.whl → 3.3.0__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.
Files changed (80) hide show
  1. claude_mpm/__init__.py +3 -3
  2. claude_mpm/__main__.py +0 -17
  3. claude_mpm/agents/INSTRUCTIONS.md +149 -17
  4. claude_mpm/agents/backups/INSTRUCTIONS.md +238 -0
  5. claude_mpm/agents/base_agent.json +1 -1
  6. claude_mpm/agents/templates/pm.json +25 -0
  7. claude_mpm/agents/templates/research.json +2 -1
  8. claude_mpm/cli/__init__.py +19 -23
  9. claude_mpm/cli/commands/__init__.py +3 -1
  10. claude_mpm/cli/commands/agents.py +7 -18
  11. claude_mpm/cli/commands/info.py +5 -10
  12. claude_mpm/cli/commands/memory.py +232 -0
  13. claude_mpm/cli/commands/run.py +501 -28
  14. claude_mpm/cli/commands/tickets.py +10 -17
  15. claude_mpm/cli/commands/ui.py +15 -37
  16. claude_mpm/cli/parser.py +91 -1
  17. claude_mpm/cli/utils.py +9 -28
  18. claude_mpm/config/socketio_config.py +256 -0
  19. claude_mpm/constants.py +9 -0
  20. claude_mpm/core/__init__.py +2 -2
  21. claude_mpm/core/agent_registry.py +4 -4
  22. claude_mpm/core/claude_runner.py +919 -0
  23. claude_mpm/core/config.py +21 -1
  24. claude_mpm/core/factories.py +1 -1
  25. claude_mpm/core/hook_manager.py +196 -0
  26. claude_mpm/core/pm_hook_interceptor.py +205 -0
  27. claude_mpm/core/service_registry.py +1 -1
  28. claude_mpm/core/simple_runner.py +323 -33
  29. claude_mpm/core/socketio_pool.py +582 -0
  30. claude_mpm/core/websocket_handler.py +233 -0
  31. claude_mpm/deployment_paths.py +261 -0
  32. claude_mpm/hooks/builtin/memory_hooks_example.py +67 -0
  33. claude_mpm/hooks/claude_hooks/hook_handler.py +667 -679
  34. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +9 -4
  35. claude_mpm/hooks/memory_integration_hook.py +312 -0
  36. claude_mpm/models/__init__.py +9 -91
  37. claude_mpm/orchestration/__init__.py +1 -1
  38. claude_mpm/scripts/claude-mpm-socketio +32 -0
  39. claude_mpm/scripts/claude_mpm_monitor.html +567 -0
  40. claude_mpm/scripts/install_socketio_server.py +407 -0
  41. claude_mpm/scripts/launch_monitor.py +132 -0
  42. claude_mpm/scripts/launch_socketio_dashboard.py +261 -0
  43. claude_mpm/scripts/manage_version.py +479 -0
  44. claude_mpm/scripts/socketio_daemon.py +181 -0
  45. claude_mpm/scripts/socketio_server_manager.py +428 -0
  46. claude_mpm/services/__init__.py +5 -0
  47. claude_mpm/services/agent_lifecycle_manager.py +76 -25
  48. claude_mpm/services/agent_memory_manager.py +684 -0
  49. claude_mpm/services/agent_modification_tracker.py +98 -17
  50. claude_mpm/services/agent_persistence_service.py +33 -13
  51. claude_mpm/services/agent_registry.py +82 -43
  52. claude_mpm/services/hook_service.py +362 -0
  53. claude_mpm/services/socketio_client_manager.py +474 -0
  54. claude_mpm/services/socketio_server.py +922 -0
  55. claude_mpm/services/standalone_socketio_server.py +631 -0
  56. claude_mpm/services/ticket_manager.py +4 -5
  57. claude_mpm/services/{ticket_manager_dependency_injection.py → ticket_manager_di.py} +12 -39
  58. claude_mpm/services/{legacy_ticketing_service.py → ticketing_service_original.py} +9 -16
  59. claude_mpm/services/version_control/semantic_versioning.py +9 -10
  60. claude_mpm/services/websocket_server.py +376 -0
  61. claude_mpm/utils/dependency_manager.py +211 -0
  62. claude_mpm/utils/import_migration_example.py +80 -0
  63. claude_mpm/utils/path_operations.py +0 -20
  64. claude_mpm/web/open_dashboard.py +34 -0
  65. {claude_mpm-3.1.3.dist-info → claude_mpm-3.3.0.dist-info}/METADATA +20 -9
  66. {claude_mpm-3.1.3.dist-info → claude_mpm-3.3.0.dist-info}/RECORD +71 -50
  67. claude_mpm-3.3.0.dist-info/entry_points.txt +7 -0
  68. claude_mpm/cli_old.py +0 -728
  69. claude_mpm/models/common.py +0 -41
  70. claude_mpm/models/lifecycle.py +0 -97
  71. claude_mpm/models/modification.py +0 -126
  72. claude_mpm/models/persistence.py +0 -57
  73. claude_mpm/models/registry.py +0 -91
  74. claude_mpm/security/__init__.py +0 -8
  75. claude_mpm/security/bash_validator.py +0 -393
  76. claude_mpm-3.1.3.dist-info/entry_points.txt +0 -4
  77. /claude_mpm/{cli_enhancements.py → experimental/cli_enhancements.py} +0 -0
  78. {claude_mpm-3.1.3.dist-info → claude_mpm-3.3.0.dist-info}/WHEEL +0 -0
  79. {claude_mpm-3.1.3.dist-info → claude_mpm-3.3.0.dist-info}/licenses/LICENSE +0 -0
  80. {claude_mpm-3.1.3.dist-info → claude_mpm-3.3.0.dist-info}/top_level.txt +0 -0
@@ -28,8 +28,9 @@ import os
28
28
  import shutil
29
29
  import time
30
30
  import uuid
31
- from dataclasses import asdict
31
+ from dataclasses import dataclass, field, asdict
32
32
  from datetime import datetime
33
+ from enum import Enum
33
34
  from pathlib import Path
34
35
  from typing import Dict, List, Optional, Any, Callable, Tuple, Set
35
36
 
@@ -39,25 +40,105 @@ from watchdog.events import FileSystemEventHandler, FileSystemEvent
39
40
  from claude_mpm.core.base_service import BaseService
40
41
  from claude_mpm.services.shared_prompt_cache import SharedPromptCache
41
42
  from claude_mpm.services.agent_registry import AgentRegistry
42
- from claude_mpm.models.modification import (
43
- ModificationType,
44
- ModificationTier,
45
- AgentModification,
46
- ModificationHistory
47
- )
48
43
  from claude_mpm.utils.path_operations import path_ops
49
44
  from claude_mpm.utils.config_manager import ConfigurationManager
50
45
 
51
- # Backward compatibility exports
52
- # These allow existing code to import models from this module
53
- __all__ = [
54
- 'ModificationType',
55
- 'ModificationTier',
56
- 'AgentModification',
57
- 'ModificationHistory',
58
- 'AgentModificationTracker',
59
- 'AgentFileSystemHandler'
60
- ]
46
+
47
+ # ============================================================================
48
+ # Data Models
49
+ # ============================================================================
50
+
51
+ class ModificationType(Enum):
52
+ """Types of agent modifications."""
53
+ CREATE = "create"
54
+ MODIFY = "modify"
55
+ DELETE = "delete"
56
+ MOVE = "move"
57
+ RESTORE = "restore"
58
+
59
+
60
+ class ModificationTier(Enum):
61
+ """Agent hierarchy tiers for modification tracking."""
62
+ PROJECT = "project"
63
+ USER = "user"
64
+ SYSTEM = "system"
65
+
66
+
67
+ @dataclass
68
+ class AgentModification:
69
+ """Agent modification record with comprehensive metadata."""
70
+
71
+ modification_id: str
72
+ agent_name: str
73
+ modification_type: ModificationType
74
+ tier: ModificationTier
75
+ file_path: str
76
+ timestamp: float
77
+ user_id: Optional[str] = None
78
+ modification_details: Dict[str, Any] = field(default_factory=dict)
79
+ file_hash_before: Optional[str] = None
80
+ file_hash_after: Optional[str] = None
81
+ file_size_before: Optional[int] = None
82
+ file_size_after: Optional[int] = None
83
+ backup_path: Optional[str] = None
84
+ validation_status: str = "pending"
85
+ validation_errors: List[str] = field(default_factory=list)
86
+ related_modifications: List[str] = field(default_factory=list)
87
+ metadata: Dict[str, Any] = field(default_factory=dict)
88
+
89
+ @property
90
+ def modification_datetime(self) -> datetime:
91
+ """Get modification timestamp as datetime."""
92
+ return datetime.fromtimestamp(self.timestamp)
93
+
94
+ @property
95
+ def age_seconds(self) -> float:
96
+ """Get age of modification in seconds."""
97
+ return time.time() - self.timestamp
98
+
99
+ def to_dict(self) -> Dict[str, Any]:
100
+ """Convert to dictionary for serialization."""
101
+ data = asdict(self)
102
+ data['modification_type'] = self.modification_type.value
103
+ data['tier'] = self.tier.value
104
+ return data
105
+
106
+ @classmethod
107
+ def from_dict(cls, data: Dict[str, Any]) -> 'AgentModification':
108
+ """Create from dictionary."""
109
+ data['modification_type'] = ModificationType(data['modification_type'])
110
+ data['tier'] = ModificationTier(data['tier'])
111
+ return cls(**data)
112
+
113
+
114
+ @dataclass
115
+ class ModificationHistory:
116
+ """Complete modification history for an agent."""
117
+
118
+ agent_name: str
119
+ modifications: List[AgentModification] = field(default_factory=list)
120
+ current_version: Optional[str] = None
121
+ total_modifications: int = 0
122
+ first_seen: Optional[float] = None
123
+ last_modified: Optional[float] = None
124
+
125
+ def add_modification(self, modification: AgentModification) -> None:
126
+ """Add a modification to history."""
127
+ self.modifications.append(modification)
128
+ self.total_modifications += 1
129
+ self.last_modified = modification.timestamp
130
+
131
+ if self.first_seen is None:
132
+ self.first_seen = modification.timestamp
133
+
134
+ def get_recent_modifications(self, hours: int = 24) -> List[AgentModification]:
135
+ """Get modifications within specified hours."""
136
+ cutoff = time.time() - (hours * 3600)
137
+ return [mod for mod in self.modifications if mod.timestamp >= cutoff]
138
+
139
+ def get_modifications_by_type(self, mod_type: ModificationType) -> List[AgentModification]:
140
+ """Get modifications by type."""
141
+ return [mod for mod in self.modifications if mod.modification_type == mod_type]
61
142
 
62
143
 
63
144
  # ============================================================================
@@ -13,22 +13,42 @@ DESIGN DECISION: Creating a minimal stub because:
13
13
  - Allows for future extension if needed
14
14
  """
15
15
 
16
- from typing import Optional, Any
16
+ from dataclasses import dataclass
17
+ from enum import Enum
18
+ from typing import Optional, Any, Dict
17
19
  import time
18
20
 
19
- from claude_mpm.models.persistence import (
20
- PersistenceStrategy,
21
- PersistenceOperation,
22
- PersistenceRecord
23
- )
24
21
 
25
- # Backward compatibility exports
26
- __all__ = [
27
- 'PersistenceStrategy',
28
- 'PersistenceOperation',
29
- 'PersistenceRecord',
30
- 'AgentPersistenceService'
31
- ]
22
+ class PersistenceStrategy(Enum):
23
+ """Agent persistence strategies."""
24
+ USER_OVERRIDE = "user_override"
25
+ PROJECT_SPECIFIC = "project_specific"
26
+ SYSTEM_DEFAULT = "system_default"
27
+
28
+
29
+ class PersistenceOperation(Enum):
30
+ """Persistence operation types."""
31
+ CREATE = "create"
32
+ UPDATE = "update"
33
+ DELETE = "delete"
34
+ BACKUP = "backup"
35
+ RESTORE = "restore"
36
+
37
+
38
+ @dataclass
39
+ class PersistenceRecord:
40
+ """Record of a persistence operation."""
41
+ operation_id: str
42
+ operation_type: PersistenceOperation
43
+ agent_name: str
44
+ source_tier: Any
45
+ target_tier: Optional[Any] = None
46
+ strategy: Optional[PersistenceStrategy] = None
47
+ success: bool = True
48
+ timestamp: float = 0.0
49
+ file_path: Optional[str] = None
50
+ error_message: Optional[str] = None
51
+ metadata: Dict[str, Any] = None
32
52
 
33
53
 
34
54
  class AgentPersistenceService:
@@ -25,35 +25,36 @@ import hashlib
25
25
  import logging
26
26
  from pathlib import Path
27
27
  from typing import Dict, List, Optional, Set, Tuple, Any, Union
28
- from dataclasses import asdict
28
+ from dataclasses import dataclass, field, asdict
29
29
  from datetime import datetime
30
30
  from enum import Enum
31
31
 
32
- from claude_mpm.models.registry import AgentTier, AgentRegistryMetadata
33
- from claude_mpm.models.common import (
34
- CORE_AGENT_TYPES,
35
- SPECIALIZED_AGENT_TYPES,
36
- ALL_AGENT_TYPES,
37
- AGENT_FILE_EXTENSIONS,
38
- AGENT_IGNORE_PATTERNS
39
- )
40
-
41
32
  logger = logging.getLogger(__name__)
42
33
 
43
- # Backward compatibility exports
44
- # Export AgentRegistryMetadata as AgentMetadata for existing code
45
- AgentMetadata = AgentRegistryMetadata
46
34
 
47
- __all__ = [
48
- 'AgentTier',
49
- 'AgentType',
50
- 'AgentMetadata', # Alias for AgentRegistryMetadata
51
- 'AgentRegistryMetadata',
52
- 'AgentRegistry'
53
- ]
35
+ # ============================================================================
36
+ # Constants and Types
37
+ # ============================================================================
38
+
39
+ CORE_AGENT_TYPES = {
40
+ 'engineer', 'architect', 'qa', 'security', 'documentation',
41
+ 'ops', 'data', 'research', 'version_control'
42
+ }
43
+
44
+ SPECIALIZED_AGENT_TYPES = {
45
+ 'pm_orchestrator', 'frontend', 'backend', 'devops', 'ml',
46
+ 'database', 'api', 'mobile', 'cloud', 'testing'
47
+ }
48
+
49
+ ALL_AGENT_TYPES = CORE_AGENT_TYPES | SPECIALIZED_AGENT_TYPES
50
+
51
+
52
+ class AgentTier(Enum):
53
+ """Agent hierarchy tiers."""
54
+ USER = "user"
55
+ SYSTEM = "system"
54
56
 
55
57
 
56
- # Keep local AgentType enum for internal classification logic
57
58
  class AgentType(Enum):
58
59
  """Agent classification types."""
59
60
  CORE = "core"
@@ -62,6 +63,44 @@ class AgentType(Enum):
62
63
  UNKNOWN = "unknown"
63
64
 
64
65
 
66
+ # ============================================================================
67
+ # Data Models
68
+ # ============================================================================
69
+
70
+ @dataclass
71
+ class AgentMetadata:
72
+ """Complete metadata for discovered agent."""
73
+ name: str
74
+ path: str
75
+ tier: AgentTier
76
+ agent_type: AgentType
77
+ description: str = ""
78
+ version: str = "0.0.0"
79
+ dependencies: List[str] = field(default_factory=list)
80
+ capabilities: List[str] = field(default_factory=list)
81
+ created_at: float = field(default_factory=time.time)
82
+ last_modified: float = field(default_factory=time.time)
83
+ file_size: int = 0
84
+ checksum: str = ""
85
+ is_valid: bool = True
86
+ validation_errors: List[str] = field(default_factory=list)
87
+ metadata: Dict[str, Any] = field(default_factory=dict)
88
+
89
+ def to_dict(self) -> Dict[str, Any]:
90
+ """Convert to dictionary for serialization."""
91
+ data = asdict(self)
92
+ data['tier'] = self.tier.value
93
+ data['agent_type'] = self.agent_type.value
94
+ return data
95
+
96
+ @classmethod
97
+ def from_dict(cls, data: Dict[str, Any]) -> 'AgentMetadata':
98
+ """Create from dictionary."""
99
+ data['tier'] = AgentTier(data['tier'])
100
+ data['agent_type'] = AgentType(data['agent_type'])
101
+ return cls(**data)
102
+
103
+
65
104
  # ============================================================================
66
105
  # Main Registry Class
67
106
  # ============================================================================
@@ -80,7 +119,7 @@ class AgentRegistry:
80
119
  self.model_selector = model_selector
81
120
 
82
121
  # Registry storage
83
- self.registry: Dict[str, AgentRegistryMetadata] = {}
122
+ self.registry: Dict[str, AgentMetadata] = {}
84
123
  self.discovery_paths: List[Path] = []
85
124
 
86
125
  # Cache configuration
@@ -89,8 +128,8 @@ class AgentRegistry:
89
128
  self.cache_prefix = "agent_registry"
90
129
 
91
130
  # Discovery configuration
92
- self.file_extensions = AGENT_FILE_EXTENSIONS
93
- self.ignore_patterns = AGENT_IGNORE_PATTERNS
131
+ self.file_extensions = {'.md', '.json', '.yaml', '.yml'}
132
+ self.ignore_patterns = {'__pycache__', '.git', 'node_modules', '.pytest_cache'}
94
133
 
95
134
  # Statistics
96
135
  self.discovery_stats = {
@@ -130,7 +169,7 @@ class AgentRegistry:
130
169
  # Discovery Methods
131
170
  # ========================================================================
132
171
 
133
- def discover_agents(self, force_refresh: bool = False) -> Dict[str, AgentRegistryMetadata]:
172
+ def discover_agents(self, force_refresh: bool = False) -> Dict[str, AgentMetadata]:
134
173
  """
135
174
  Discover all available agents across configured paths.
136
175
 
@@ -230,7 +269,7 @@ class AgentRegistry:
230
269
 
231
270
  return name
232
271
 
233
- def _create_agent_metadata(self, file_path: Path, agent_name: str, tier: AgentTier) -> AgentRegistryMetadata:
272
+ def _create_agent_metadata(self, file_path: Path, agent_name: str, tier: AgentTier) -> AgentMetadata:
234
273
  """Create agent metadata from file."""
235
274
  # Get file stats
236
275
  stat = file_path.stat()
@@ -285,11 +324,11 @@ class AgentRegistry:
285
324
  except Exception as e:
286
325
  logger.warning(f"Failed to parse {file_path}: {e}")
287
326
 
288
- return AgentRegistryMetadata(
327
+ return AgentMetadata(
289
328
  name=agent_name,
290
329
  path=str(file_path),
291
330
  tier=tier,
292
- agent_type=agent_type.value, # Convert enum to string
331
+ agent_type=agent_type,
293
332
  description=description,
294
333
  version=version,
295
334
  capabilities=capabilities,
@@ -340,7 +379,7 @@ class AgentRegistry:
340
379
  def _apply_tier_precedence(self) -> None:
341
380
  """Apply tier precedence rules to discovered agents."""
342
381
  # Group agents by name
343
- agents_by_name: Dict[str, List[AgentRegistryMetadata]] = {}
382
+ agents_by_name: Dict[str, List[AgentMetadata]] = {}
344
383
 
345
384
  for agent in self.registry.values():
346
385
  if agent.name not in agents_by_name:
@@ -364,7 +403,7 @@ class AgentRegistry:
364
403
  # Validation Methods
365
404
  # ========================================================================
366
405
 
367
- def _validate_agent(self, metadata: AgentRegistryMetadata) -> bool:
406
+ def _validate_agent(self, metadata: AgentMetadata) -> bool:
368
407
  """Validate agent metadata and file."""
369
408
  errors = []
370
409
 
@@ -398,7 +437,7 @@ class AgentRegistry:
398
437
  # Cache Methods
399
438
  # ========================================================================
400
439
 
401
- def _get_cached_registry(self) -> Optional[Dict[str, AgentRegistryMetadata]]:
440
+ def _get_cached_registry(self) -> Optional[Dict[str, AgentMetadata]]:
402
441
  """Get registry from cache if available."""
403
442
  if not self.cache_service:
404
443
  return None
@@ -411,7 +450,7 @@ class AgentRegistry:
411
450
  # Deserialize metadata
412
451
  registry = {}
413
452
  for name, data in cached_data.items():
414
- registry[name] = AgentRegistryMetadata.from_dict(data)
453
+ registry[name] = AgentMetadata.from_dict(data)
415
454
  return registry
416
455
 
417
456
  except Exception as e:
@@ -452,7 +491,7 @@ class AgentRegistry:
452
491
  # Query Methods
453
492
  # ========================================================================
454
493
 
455
- def get_agent(self, name: str) -> Optional[AgentRegistryMetadata]:
494
+ def get_agent(self, name: str) -> Optional[AgentMetadata]:
456
495
  """Get metadata for a specific agent."""
457
496
  # Ensure registry is populated
458
497
  if not self.registry:
@@ -461,7 +500,7 @@ class AgentRegistry:
461
500
  return self.registry.get(name)
462
501
 
463
502
  def list_agents(self, tier: Optional[AgentTier] = None,
464
- agent_type: Optional[str] = None) -> List[AgentRegistryMetadata]:
503
+ agent_type: Optional[AgentType] = None) -> List[AgentMetadata]:
465
504
  """List agents with optional filtering."""
466
505
  # Ensure registry is populated
467
506
  if not self.registry:
@@ -485,19 +524,19 @@ class AgentRegistry:
485
524
 
486
525
  return sorted(self.registry.keys())
487
526
 
488
- def get_core_agents(self) -> List[AgentRegistryMetadata]:
527
+ def get_core_agents(self) -> List[AgentMetadata]:
489
528
  """Get all core framework agents."""
490
- return self.list_agents(agent_type=AgentType.CORE.value)
529
+ return self.list_agents(agent_type=AgentType.CORE)
491
530
 
492
- def get_specialized_agents(self) -> List[AgentRegistryMetadata]:
531
+ def get_specialized_agents(self) -> List[AgentMetadata]:
493
532
  """Get all specialized agents."""
494
- return self.list_agents(agent_type=AgentType.SPECIALIZED.value)
533
+ return self.list_agents(agent_type=AgentType.SPECIALIZED)
495
534
 
496
- def get_custom_agents(self) -> List[AgentRegistryMetadata]:
535
+ def get_custom_agents(self) -> List[AgentMetadata]:
497
536
  """Get all custom user-defined agents."""
498
- return self.list_agents(agent_type=AgentType.CUSTOM.value)
537
+ return self.list_agents(agent_type=AgentType.CUSTOM)
499
538
 
500
- def search_agents(self, query: str) -> List[AgentRegistryMetadata]:
539
+ def search_agents(self, query: str) -> List[AgentMetadata]:
501
540
  """Search agents by name or description."""
502
541
  if not self.registry:
503
542
  self.discover_agents()
@@ -540,7 +579,7 @@ class AgentRegistry:
540
579
 
541
580
  # Count by type
542
581
  for agent in self.registry.values():
543
- agent_type = agent.agent_type # Already a string
582
+ agent_type = agent.agent_type.value
544
583
  stats['agents_by_type'][agent_type] = stats['agents_by_type'].get(agent_type, 0) + 1
545
584
 
546
585
  # Validation stats
@@ -628,7 +667,7 @@ class AgentRegistry:
628
667
 
629
668
  # Import agents
630
669
  for name, agent_data in data.get('agents', {}).items():
631
- self.registry[name] = AgentRegistryMetadata.from_dict(agent_data)
670
+ self.registry[name] = AgentMetadata.from_dict(agent_data)
632
671
 
633
672
  # Cache imported registry
634
673
  if self.cache_enabled: