claude-mpm 3.4.26__py3-none-any.whl → 3.5.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 (123) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/INSTRUCTIONS.md +182 -299
  3. claude_mpm/agents/agent_loader.py +283 -57
  4. claude_mpm/agents/agent_loader_integration.py +6 -9
  5. claude_mpm/agents/base_agent.json +2 -1
  6. claude_mpm/agents/base_agent_loader.py +1 -1
  7. claude_mpm/cli/__init__.py +6 -10
  8. claude_mpm/cli/commands/__init__.py +0 -2
  9. claude_mpm/cli/commands/agents.py +1 -1
  10. claude_mpm/cli/commands/memory.py +1 -1
  11. claude_mpm/cli/commands/run.py +12 -0
  12. claude_mpm/cli/parser.py +0 -13
  13. claude_mpm/cli/utils.py +1 -1
  14. claude_mpm/config/__init__.py +44 -2
  15. claude_mpm/config/agent_config.py +348 -0
  16. claude_mpm/config/paths.py +322 -0
  17. claude_mpm/constants.py +0 -1
  18. claude_mpm/core/__init__.py +2 -5
  19. claude_mpm/core/agent_registry.py +63 -17
  20. claude_mpm/core/claude_runner.py +354 -43
  21. claude_mpm/core/config.py +7 -1
  22. claude_mpm/core/config_aliases.py +4 -3
  23. claude_mpm/core/config_paths.py +151 -0
  24. claude_mpm/core/factories.py +4 -50
  25. claude_mpm/core/logger.py +11 -13
  26. claude_mpm/core/service_registry.py +2 -2
  27. claude_mpm/dashboard/static/js/components/agent-inference.js +101 -25
  28. claude_mpm/dashboard/static/js/components/event-processor.js +3 -2
  29. claude_mpm/hooks/claude_hooks/hook_handler.py +343 -83
  30. claude_mpm/hooks/memory_integration_hook.py +1 -1
  31. claude_mpm/init.py +37 -6
  32. claude_mpm/scripts/socketio_daemon.py +6 -2
  33. claude_mpm/services/__init__.py +71 -3
  34. claude_mpm/services/agents/__init__.py +85 -0
  35. claude_mpm/services/agents/deployment/__init__.py +21 -0
  36. claude_mpm/services/{agent_deployment.py → agents/deployment/agent_deployment.py} +192 -41
  37. claude_mpm/services/{agent_lifecycle_manager.py → agents/deployment/agent_lifecycle_manager.py} +11 -10
  38. claude_mpm/services/agents/loading/__init__.py +11 -0
  39. claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +9 -8
  40. claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +2 -2
  41. claude_mpm/services/{framework_agent_loader.py → agents/loading/framework_agent_loader.py} +116 -40
  42. claude_mpm/services/agents/management/__init__.py +9 -0
  43. claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +6 -5
  44. claude_mpm/services/agents/memory/__init__.py +21 -0
  45. claude_mpm/services/{agent_memory_manager.py → agents/memory/agent_memory_manager.py} +3 -3
  46. claude_mpm/services/agents/registry/__init__.py +29 -0
  47. claude_mpm/services/{agent_registry.py → agents/registry/agent_registry.py} +101 -16
  48. claude_mpm/services/{deployed_agent_discovery.py → agents/registry/deployed_agent_discovery.py} +12 -2
  49. claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +6 -5
  50. claude_mpm/services/async_session_logger.py +584 -0
  51. claude_mpm/services/claude_session_logger.py +299 -0
  52. claude_mpm/services/framework_claude_md_generator/content_assembler.py +2 -2
  53. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +17 -17
  54. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +3 -3
  55. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +1 -1
  56. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +1 -1
  57. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +19 -24
  58. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +1 -1
  59. claude_mpm/services/framework_claude_md_generator.py +4 -2
  60. claude_mpm/services/memory/__init__.py +17 -0
  61. claude_mpm/services/{memory_builder.py → memory/builder.py} +3 -3
  62. claude_mpm/services/memory/cache/__init__.py +14 -0
  63. claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +1 -1
  64. claude_mpm/services/memory/cache/simple_cache.py +317 -0
  65. claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +1 -1
  66. claude_mpm/services/{memory_router.py → memory/router.py} +1 -1
  67. claude_mpm/services/optimized_hook_service.py +542 -0
  68. claude_mpm/services/project_registry.py +14 -8
  69. claude_mpm/services/response_tracker.py +237 -0
  70. claude_mpm/services/ticketing_service_original.py +4 -2
  71. claude_mpm/services/version_control/branch_strategy.py +3 -1
  72. claude_mpm/utils/paths.py +12 -10
  73. claude_mpm/utils/session_logging.py +114 -0
  74. claude_mpm/validation/agent_validator.py +2 -1
  75. {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/METADATA +26 -20
  76. {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/RECORD +83 -106
  77. claude_mpm/cli/commands/ui.py +0 -57
  78. claude_mpm/core/simple_runner.py +0 -1046
  79. claude_mpm/hooks/builtin/__init__.py +0 -1
  80. claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
  81. claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
  82. claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
  83. claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
  84. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
  85. claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
  86. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
  87. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
  88. claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
  89. claude_mpm/orchestration/__init__.py +0 -6
  90. claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
  91. claude_mpm/orchestration/archive/factory.py +0 -215
  92. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
  93. claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
  94. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
  95. claude_mpm/orchestration/archive/orchestrator.py +0 -501
  96. claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
  97. claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
  98. claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
  99. claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
  100. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
  101. claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
  102. claude_mpm/schemas/workflow_validator.py +0 -411
  103. claude_mpm/services/parent_directory_manager/__init__.py +0 -577
  104. claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
  105. claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
  106. claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
  107. claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
  108. claude_mpm/services/parent_directory_manager/operations.py +0 -186
  109. claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
  110. claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
  111. claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
  112. claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
  113. claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
  114. claude_mpm/ui/__init__.py +0 -1
  115. claude_mpm/ui/rich_terminal_ui.py +0 -295
  116. claude_mpm/ui/terminal_ui.py +0 -328
  117. /claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +0 -0
  118. /claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +0 -0
  119. /claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +0 -0
  120. {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/WHEEL +0 -0
  121. {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/entry_points.txt +0 -0
  122. {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/licenses/LICENSE +0 -0
  123. {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/top_level.txt +0 -0
@@ -29,6 +29,9 @@ from dataclasses import dataclass, field, asdict
29
29
  from datetime import datetime
30
30
  from enum import Enum
31
31
 
32
+ from claude_mpm.core.config_paths import ConfigPaths
33
+ from claude_mpm.services.memory.cache.simple_cache import SimpleCacheService
34
+
32
35
  logger = logging.getLogger(__name__)
33
36
 
34
37
 
@@ -51,6 +54,7 @@ ALL_AGENT_TYPES = CORE_AGENT_TYPES | SPECIALIZED_AGENT_TYPES
51
54
 
52
55
  class AgentTier(Enum):
53
56
  """Agent hierarchy tiers."""
57
+ PROJECT = "project" # Highest precedence - project-specific agents
54
58
  USER = "user"
55
59
  SYSTEM = "system"
56
60
 
@@ -115,7 +119,15 @@ class AgentRegistry:
115
119
 
116
120
  def __init__(self, cache_service=None, model_selector=None):
117
121
  """Initialize AgentRegistry with optional cache service and model selector."""
118
- self.cache_service = cache_service
122
+ # Use provided cache service or create a default one
123
+ if cache_service is None:
124
+ # Create a simple in-memory cache with 1 hour TTL by default
125
+ self.cache_service = SimpleCacheService(default_ttl=3600, max_size=500)
126
+ self.cache_enabled = True
127
+ else:
128
+ self.cache_service = cache_service
129
+ self.cache_enabled = True
130
+
119
131
  self.model_selector = model_selector
120
132
 
121
133
  # Registry storage
@@ -123,10 +135,12 @@ class AgentRegistry:
123
135
  self.discovery_paths: List[Path] = []
124
136
 
125
137
  # Cache configuration
126
- self.cache_enabled = cache_service is not None
127
138
  self.cache_ttl = 3600 # 1 hour
128
139
  self.cache_prefix = "agent_registry"
129
140
 
141
+ # Track discovered files for cache invalidation
142
+ self.discovered_files: Set[Path] = set()
143
+
130
144
  # Discovery configuration
131
145
  self.file_extensions = {'.md', '.json', '.yaml', '.yml'}
132
146
  self.ignore_patterns = {'__pycache__', '.git', 'node_modules', '.pytest_cache'}
@@ -147,8 +161,13 @@ class AgentRegistry:
147
161
 
148
162
  def _setup_discovery_paths(self) -> None:
149
163
  """Setup standard discovery paths for agent files."""
164
+ # Project-level agents (highest priority)
165
+ project_path = ConfigPaths.get_project_agents_dir()
166
+ if project_path.exists():
167
+ self.discovery_paths.append(project_path)
168
+
150
169
  # User-level agents
151
- user_path = Path.home() / '.claude-pm' / 'agents'
170
+ user_path = ConfigPaths.get_user_agents_dir()
152
171
  if user_path.exists():
153
172
  self.discovery_paths.append(user_path)
154
173
 
@@ -192,8 +211,9 @@ class AgentRegistry:
192
211
 
193
212
  self.discovery_stats['cache_misses'] += 1
194
213
 
195
- # Clear existing registry
214
+ # Clear existing registry and discovered files
196
215
  self.registry.clear()
216
+ self.discovered_files.clear()
197
217
 
198
218
  # Discover agents from all paths
199
219
  for discovery_path in self.discovery_paths:
@@ -203,7 +223,7 @@ class AgentRegistry:
203
223
  # Handle tier precedence
204
224
  self._apply_tier_precedence()
205
225
 
206
- # Cache the results
226
+ # Cache the results with file tracking
207
227
  if self.cache_enabled:
208
228
  self._cache_registry()
209
229
 
@@ -238,6 +258,9 @@ class AgentRegistry:
238
258
  if not agent_name:
239
259
  continue
240
260
 
261
+ # Track discovered file for cache invalidation
262
+ self.discovered_files.add(file_path)
263
+
241
264
  # Create metadata
242
265
  metadata = self._create_agent_metadata(file_path, agent_name, tier)
243
266
 
@@ -392,16 +415,26 @@ class AgentRegistry:
392
415
 
393
416
  def _determine_tier(self, path: Path) -> AgentTier:
394
417
  """Determine tier based on path location."""
395
- path_str = str(path).lower()
418
+ path_str = str(path)
396
419
 
397
- if '.claude-pm' in path_str or str(Path.home()) in str(path):
420
+ # Check if it's a project-level path (in current working directory)
421
+ # Project agents are in <project_root>/.claude-mpm/agents
422
+ project_agents_dir = ConfigPaths.get_project_agents_dir()
423
+ if project_agents_dir.exists() and (path == project_agents_dir or project_agents_dir in path.parents):
424
+ return AgentTier.PROJECT
425
+
426
+ # Check if it's a user-level path (in home directory)
427
+ user_agents_dir = ConfigPaths.get_user_agents_dir()
428
+ if user_agents_dir.exists() and (path == user_agents_dir or user_agents_dir in path.parents):
398
429
  return AgentTier.USER
399
- else:
400
- return AgentTier.SYSTEM
430
+
431
+ # Everything else is system-level
432
+ return AgentTier.SYSTEM
401
433
 
402
434
  def _has_tier_precedence(self, tier1: AgentTier, tier2: AgentTier) -> bool:
403
435
  """Check if tier1 has precedence over tier2."""
404
436
  precedence = {
437
+ AgentTier.PROJECT: 3, # Highest precedence
405
438
  AgentTier.USER: 2,
406
439
  AgentTier.SYSTEM: 1
407
440
  }
@@ -424,7 +457,7 @@ class AgentRegistry:
424
457
  self.registry[agent_name] = agents[0]
425
458
  else:
426
459
  # Sort by tier precedence
427
- agents.sort(key=lambda a: {AgentTier.USER: 2, AgentTier.SYSTEM: 1}.get(a.tier, 0), reverse=True)
460
+ agents.sort(key=lambda a: {AgentTier.PROJECT: 3, AgentTier.USER: 2, AgentTier.SYSTEM: 1}.get(a.tier, 0), reverse=True)
428
461
  self.registry[agent_name] = agents[0]
429
462
 
430
463
  if len(agents) > 1:
@@ -482,6 +515,13 @@ class AgentRegistry:
482
515
  registry = {}
483
516
  for name, data in cached_data.items():
484
517
  registry[name] = AgentMetadata.from_dict(data)
518
+
519
+ # Also restore discovered files set
520
+ files_key = f"{self.cache_prefix}_discovered_files"
521
+ discovered_files = self.cache_service.get(files_key)
522
+ if discovered_files:
523
+ self.discovered_files = {Path(f) for f in discovered_files}
524
+
485
525
  return registry
486
526
 
487
527
  except Exception as e:
@@ -490,20 +530,47 @@ class AgentRegistry:
490
530
  return None
491
531
 
492
532
  def _cache_registry(self) -> None:
493
- """Cache the current registry."""
533
+ """Cache the current registry with file tracking."""
494
534
  if not self.cache_service:
495
535
  return
496
536
 
497
537
  try:
498
538
  cache_key = f"{self.cache_prefix}_registry"
539
+
499
540
  # Serialize metadata
500
541
  cache_data = {
501
542
  name: metadata.to_dict()
502
543
  for name, metadata in self.registry.items()
503
544
  }
504
545
 
505
- self.cache_service.set(cache_key, cache_data, ttl=self.cache_ttl)
506
- logger.debug("Cached agent registry")
546
+ # If the cache service supports file tracking, use it
547
+ if hasattr(self.cache_service, 'set'):
548
+ import inspect
549
+ sig = inspect.signature(self.cache_service.set)
550
+ if 'tracked_files' in sig.parameters:
551
+ # Cache with file tracking for automatic invalidation
552
+ self.cache_service.set(
553
+ cache_key,
554
+ cache_data,
555
+ ttl=self.cache_ttl,
556
+ tracked_files=list(self.discovered_files)
557
+ )
558
+ else:
559
+ # Fall back to regular caching
560
+ self.cache_service.set(cache_key, cache_data, ttl=self.cache_ttl)
561
+ else:
562
+ # Fall back to regular caching
563
+ self.cache_service.set(cache_key, cache_data, ttl=self.cache_ttl)
564
+
565
+ # Also cache the discovered files list
566
+ files_key = f"{self.cache_prefix}_discovered_files"
567
+ self.cache_service.set(
568
+ files_key,
569
+ [str(f) for f in self.discovered_files],
570
+ ttl=self.cache_ttl
571
+ )
572
+
573
+ logger.debug(f"Cached agent registry with {len(self.discovered_files)} tracked files")
507
574
 
508
575
  except Exception as e:
509
576
  logger.warning(f"Failed to cache registry: {e}")
@@ -512,8 +579,17 @@ class AgentRegistry:
512
579
  """Invalidate the registry cache."""
513
580
  if self.cache_service:
514
581
  try:
515
- cache_key = f"{self.cache_prefix}_registry"
516
- self.cache_service.delete(cache_key)
582
+ # Invalidate both registry and files cache
583
+ registry_key = f"{self.cache_prefix}_registry"
584
+ files_key = f"{self.cache_prefix}_discovered_files"
585
+
586
+ self.cache_service.delete(registry_key)
587
+ self.cache_service.delete(files_key)
588
+
589
+ # Also clear in-memory registry to force re-discovery
590
+ self.registry.clear()
591
+ self.discovered_files.clear()
592
+
517
593
  logger.debug("Invalidated registry cache")
518
594
  except Exception as e:
519
595
  logger.warning(f"Failed to invalidate cache: {e}")
@@ -600,9 +676,14 @@ class AgentRegistry:
600
676
  'valid': 0,
601
677
  'invalid': 0,
602
678
  'errors': []
603
- }
679
+ },
680
+ 'cache_metrics': {}
604
681
  }
605
682
 
683
+ # Add cache metrics if available
684
+ if self.cache_enabled and hasattr(self.cache_service, 'get_cache_metrics'):
685
+ stats['cache_metrics'] = self.cache_service.get_cache_metrics()
686
+
606
687
  # Count by tier
607
688
  for agent in self.registry.values():
608
689
  tier = agent.tier.value
@@ -651,6 +732,8 @@ class AgentRegistry:
651
732
  logger.info(f"Added discovery path: {path}")
652
733
  # Invalidate cache since paths changed
653
734
  self.invalidate_cache()
735
+ # Force re-discovery with new path
736
+ self.discover_agents(force_refresh=True)
654
737
 
655
738
  def remove_discovery_path(self, path: Union[str, Path]) -> None:
656
739
  """Remove a path from agent discovery."""
@@ -660,6 +743,8 @@ class AgentRegistry:
660
743
  logger.info(f"Removed discovery path: {path}")
661
744
  # Invalidate cache since paths changed
662
745
  self.invalidate_cache()
746
+ # Force re-discovery without the removed path
747
+ self.discover_agents(force_refresh=True)
663
748
 
664
749
  def export_registry(self, output_path: Union[str, Path]) -> None:
665
750
  """Export registry to JSON file."""
@@ -213,7 +213,8 @@ class DeployedAgentDiscovery:
213
213
  True if agent is valid, False if it's a template or invalid
214
214
  """
215
215
  # Filter out known templates and non-agent files
216
- invalid_names = ['BASE_AGENT_TEMPLATE', 'INSTRUCTIONS', 'base_agent', 'template']
216
+ invalid_names = ['BASE_AGENT_TEMPLATE', 'INSTRUCTIONS', 'base_agent', 'template',
217
+ 'MEMORIES', 'TODOWRITE']
217
218
 
218
219
  agent_id = agent_info.get('id', '').upper()
219
220
  agent_name = agent_info.get('name', '').upper()
@@ -223,4 +224,13 @@ class DeployedAgentDiscovery:
223
224
  logger.debug(f"Filtering out template/invalid agent: {agent_info['id']}")
224
225
  return False
225
226
 
226
- return True
227
+ return True
228
+
229
+ def get_precedence_order(self) -> List[str]:
230
+ """
231
+ Get the precedence order for agent discovery.
232
+
233
+ Returns:
234
+ List of tiers in precedence order (highest to lowest)
235
+ """
236
+ return ['project', 'user', 'system']
@@ -38,8 +38,9 @@ from watchdog.observers import Observer
38
38
  from watchdog.events import FileSystemEventHandler, FileSystemEvent
39
39
 
40
40
  from claude_mpm.core.base_service import BaseService
41
- from claude_mpm.services.shared_prompt_cache import SharedPromptCache
42
- from claude_mpm.services.agent_registry import AgentRegistry
41
+ from claude_mpm.core.config_paths import ConfigPaths
42
+ from claude_mpm.services.memory.cache.shared_prompt_cache import SharedPromptCache
43
+ from .agent_registry import AgentRegistry
43
44
  from claude_mpm.utils.path_operations import path_ops
44
45
  from claude_mpm.utils.config_manager import ConfigurationManager
45
46
 
@@ -217,7 +218,7 @@ class AgentModificationTracker(BaseService):
217
218
  self.watched_paths: Set[Path] = set()
218
219
 
219
220
  # Persistence paths
220
- self.persistence_root = Path.home() / '.claude-pm' / 'agent_tracking'
221
+ self.persistence_root = ConfigPaths.get_tracking_dir()
221
222
  self.backup_root = self.persistence_root / 'backups'
222
223
  self.history_root = self.persistence_root / 'history'
223
224
 
@@ -410,7 +411,7 @@ class AgentModificationTracker(BaseService):
410
411
  agent_dirs = [
411
412
  Path("agents"),
412
413
  Path("src/claude_mpm/agents"),
413
- Path.home() / ".claude-pm" / "agents"
414
+ ConfigPaths.get_user_agents_dir()
414
415
  ]
415
416
 
416
417
  for agent_dir in agent_dirs:
@@ -576,7 +577,7 @@ class AgentModificationTracker(BaseService):
576
577
  path_str = str(path).lower()
577
578
  if 'system' in path_str or '/claude_mpm/agents/' in path_str:
578
579
  tier = ModificationTier.SYSTEM
579
- elif '.claude-pm' in path_str or str(Path.home()) in path_str:
580
+ elif ConfigPaths.CONFIG_DIR.lower() in path_str or str(Path.home()) in path_str:
580
581
  tier = ModificationTier.USER
581
582
  else:
582
583
  tier = ModificationTier.PROJECT