claude-mpm 0.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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (159) hide show
  1. claude_mpm/__init__.py +17 -0
  2. claude_mpm/__main__.py +14 -0
  3. claude_mpm/_version.py +32 -0
  4. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
  5. claude_mpm/agents/INSTRUCTIONS.md +375 -0
  6. claude_mpm/agents/__init__.py +118 -0
  7. claude_mpm/agents/agent_loader.py +621 -0
  8. claude_mpm/agents/agent_loader_integration.py +229 -0
  9. claude_mpm/agents/agents_metadata.py +204 -0
  10. claude_mpm/agents/base_agent.json +27 -0
  11. claude_mpm/agents/base_agent_loader.py +519 -0
  12. claude_mpm/agents/schema/agent_schema.json +160 -0
  13. claude_mpm/agents/system_agent_config.py +587 -0
  14. claude_mpm/agents/templates/__init__.py +101 -0
  15. claude_mpm/agents/templates/data_engineer_agent.json +46 -0
  16. claude_mpm/agents/templates/documentation_agent.json +45 -0
  17. claude_mpm/agents/templates/engineer_agent.json +49 -0
  18. claude_mpm/agents/templates/ops_agent.json +46 -0
  19. claude_mpm/agents/templates/qa_agent.json +45 -0
  20. claude_mpm/agents/templates/research_agent.json +49 -0
  21. claude_mpm/agents/templates/security_agent.json +46 -0
  22. claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
  23. claude_mpm/agents/templates/version_control_agent.json +46 -0
  24. claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
  25. claude_mpm/cli.py +655 -0
  26. claude_mpm/cli_main.py +13 -0
  27. claude_mpm/cli_module/__init__.py +15 -0
  28. claude_mpm/cli_module/args.py +222 -0
  29. claude_mpm/cli_module/commands.py +203 -0
  30. claude_mpm/cli_module/migration_example.py +183 -0
  31. claude_mpm/cli_module/refactoring_guide.md +253 -0
  32. claude_mpm/cli_old/__init__.py +1 -0
  33. claude_mpm/cli_old/ticket_cli.py +102 -0
  34. claude_mpm/config/__init__.py +5 -0
  35. claude_mpm/config/hook_config.py +42 -0
  36. claude_mpm/constants.py +150 -0
  37. claude_mpm/core/__init__.py +45 -0
  38. claude_mpm/core/agent_name_normalizer.py +248 -0
  39. claude_mpm/core/agent_registry.py +627 -0
  40. claude_mpm/core/agent_registry.py.bak +312 -0
  41. claude_mpm/core/agent_session_manager.py +273 -0
  42. claude_mpm/core/base_service.py +747 -0
  43. claude_mpm/core/base_service.py.bak +406 -0
  44. claude_mpm/core/config.py +334 -0
  45. claude_mpm/core/config_aliases.py +292 -0
  46. claude_mpm/core/container.py +347 -0
  47. claude_mpm/core/factories.py +281 -0
  48. claude_mpm/core/framework_loader.py +472 -0
  49. claude_mpm/core/injectable_service.py +206 -0
  50. claude_mpm/core/interfaces.py +539 -0
  51. claude_mpm/core/logger.py +468 -0
  52. claude_mpm/core/minimal_framework_loader.py +107 -0
  53. claude_mpm/core/mixins.py +150 -0
  54. claude_mpm/core/service_registry.py +299 -0
  55. claude_mpm/core/session_manager.py +190 -0
  56. claude_mpm/core/simple_runner.py +511 -0
  57. claude_mpm/core/tool_access_control.py +173 -0
  58. claude_mpm/hooks/README.md +243 -0
  59. claude_mpm/hooks/__init__.py +5 -0
  60. claude_mpm/hooks/base_hook.py +154 -0
  61. claude_mpm/hooks/builtin/__init__.py +1 -0
  62. claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
  63. claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
  64. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
  65. claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
  66. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
  67. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
  68. claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
  69. claude_mpm/hooks/hook_client.py +264 -0
  70. claude_mpm/hooks/hook_runner.py +370 -0
  71. claude_mpm/hooks/json_rpc_executor.py +259 -0
  72. claude_mpm/hooks/json_rpc_hook_client.py +319 -0
  73. claude_mpm/hooks/tool_call_interceptor.py +204 -0
  74. claude_mpm/init.py +246 -0
  75. claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
  76. claude_mpm/orchestration/__init__.py +6 -0
  77. claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
  78. claude_mpm/orchestration/archive/factory.py +215 -0
  79. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
  80. claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
  81. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
  82. claude_mpm/orchestration/archive/orchestrator.py +501 -0
  83. claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
  84. claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
  85. claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
  86. claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
  87. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
  88. claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
  89. claude_mpm/scripts/__init__.py +1 -0
  90. claude_mpm/scripts/ticket.py +269 -0
  91. claude_mpm/services/__init__.py +10 -0
  92. claude_mpm/services/agent_deployment.py +955 -0
  93. claude_mpm/services/agent_lifecycle_manager.py +948 -0
  94. claude_mpm/services/agent_management_service.py +596 -0
  95. claude_mpm/services/agent_modification_tracker.py +841 -0
  96. claude_mpm/services/agent_profile_loader.py +606 -0
  97. claude_mpm/services/agent_registry.py +677 -0
  98. claude_mpm/services/base_agent_manager.py +380 -0
  99. claude_mpm/services/framework_agent_loader.py +337 -0
  100. claude_mpm/services/framework_claude_md_generator/README.md +92 -0
  101. claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
  102. claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
  103. claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
  104. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
  105. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
  106. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
  107. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
  108. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
  109. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
  110. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
  111. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
  112. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
  113. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
  114. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
  115. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
  116. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
  117. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
  118. claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
  119. claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
  120. claude_mpm/services/framework_claude_md_generator.py +621 -0
  121. claude_mpm/services/hook_service.py +388 -0
  122. claude_mpm/services/hook_service_manager.py +223 -0
  123. claude_mpm/services/json_rpc_hook_manager.py +92 -0
  124. claude_mpm/services/parent_directory_manager/README.md +83 -0
  125. claude_mpm/services/parent_directory_manager/__init__.py +577 -0
  126. claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
  127. claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
  128. claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
  129. claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
  130. claude_mpm/services/parent_directory_manager/operations.py +186 -0
  131. claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
  132. claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
  133. claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
  134. claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
  135. claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
  136. claude_mpm/services/shared_prompt_cache.py +819 -0
  137. claude_mpm/services/ticket_manager.py +213 -0
  138. claude_mpm/services/ticket_manager_di.py +318 -0
  139. claude_mpm/services/ticketing_service_original.py +508 -0
  140. claude_mpm/services/version_control/VERSION +1 -0
  141. claude_mpm/services/version_control/__init__.py +70 -0
  142. claude_mpm/services/version_control/branch_strategy.py +670 -0
  143. claude_mpm/services/version_control/conflict_resolution.py +744 -0
  144. claude_mpm/services/version_control/git_operations.py +784 -0
  145. claude_mpm/services/version_control/semantic_versioning.py +703 -0
  146. claude_mpm/ui/__init__.py +1 -0
  147. claude_mpm/ui/rich_terminal_ui.py +295 -0
  148. claude_mpm/ui/terminal_ui.py +328 -0
  149. claude_mpm/utils/__init__.py +16 -0
  150. claude_mpm/utils/config_manager.py +468 -0
  151. claude_mpm/utils/import_migration_example.py +80 -0
  152. claude_mpm/utils/imports.py +182 -0
  153. claude_mpm/utils/path_operations.py +357 -0
  154. claude_mpm/utils/paths.py +289 -0
  155. claude_mpm-0.3.0.dist-info/METADATA +290 -0
  156. claude_mpm-0.3.0.dist-info/RECORD +159 -0
  157. claude_mpm-0.3.0.dist-info/WHEEL +5 -0
  158. claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
  159. claude_mpm-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,948 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Agent Lifecycle Manager - ISS-0118 Integration Service
4
+ ======================================================
5
+
6
+ Comprehensive agent lifecycle management integrating modification tracking,
7
+ persistence, and registry services for complete agent management across
8
+ the three-tier hierarchy.
9
+
10
+ Key Features:
11
+ - Unified agent lifecycle management (create, modify, delete, restore)
12
+ - Integrated modification tracking and persistence
13
+ - Automatic cache invalidation and registry updates
14
+ - Comprehensive backup and versioning system
15
+ - Conflict detection and resolution workflows
16
+ - Performance monitoring and optimization
17
+
18
+ Performance Impact:
19
+ - <100ms end-to-end agent operations
20
+ - Automatic cache coherency maintenance
21
+ - Intelligent persistence routing
22
+ - Real-time modification detection
23
+
24
+ Created for ISS-0118: Agent Registry and Hierarchical Discovery System
25
+ """
26
+
27
+ import asyncio
28
+ import logging
29
+ import time
30
+ from dataclasses import dataclass, field
31
+ from datetime import datetime, timedelta
32
+ from enum import Enum
33
+ from pathlib import Path
34
+ from typing import Dict, List, Optional, Set, Any, Tuple, Union
35
+
36
+ from claude_mpm.services.shared_prompt_cache import SharedPromptCache
37
+ from claude_mpm.services.agent_registry import AgentRegistry, AgentMetadata
38
+ from claude_mpm.services.agent_modification_tracker import (
39
+ AgentModificationTracker,
40
+ AgentModification,
41
+ ModificationType,
42
+ ModificationTier
43
+ )
44
+ from claude_mpm.services.agent_persistence_service import (
45
+ AgentPersistenceService,
46
+ PersistenceRecord,
47
+ PersistenceStrategy,
48
+ PersistenceOperation
49
+ )
50
+ from claude_mpm.core.base_service import BaseService
51
+ from claude_mpm.utils.path_operations import path_ops
52
+ from claude_mpm.utils.config_manager import ConfigurationManager
53
+
54
+
55
+ class LifecycleOperation(Enum):
56
+ """Agent lifecycle operations."""
57
+ CREATE = "create"
58
+ UPDATE = "update"
59
+ DELETE = "delete"
60
+ RESTORE = "restore"
61
+ MIGRATE = "migrate"
62
+ REPLICATE = "replicate"
63
+ VALIDATE = "validate"
64
+
65
+
66
+ class LifecycleState(Enum):
67
+ """Agent lifecycle states."""
68
+ ACTIVE = "active"
69
+ MODIFIED = "modified"
70
+ DELETED = "deleted"
71
+ CONFLICTED = "conflicted"
72
+ MIGRATING = "migrating"
73
+ VALIDATING = "validating"
74
+
75
+
76
+ @dataclass
77
+ class AgentLifecycleRecord:
78
+ """Complete lifecycle record for an agent."""
79
+
80
+ agent_name: str
81
+ current_state: LifecycleState
82
+ tier: ModificationTier
83
+ file_path: str
84
+ created_at: float
85
+ last_modified: float
86
+ version: str
87
+ modifications: List[str] = field(default_factory=list) # Modification IDs
88
+ persistence_operations: List[str] = field(default_factory=list) # Operation IDs
89
+ backup_paths: List[str] = field(default_factory=list)
90
+ validation_status: str = "valid"
91
+ validation_errors: List[str] = field(default_factory=list)
92
+ metadata: Dict[str, Any] = field(default_factory=dict)
93
+
94
+ @property
95
+ def age_days(self) -> float:
96
+ """Get age in days."""
97
+ return (time.time() - self.created_at) / (24 * 3600)
98
+
99
+ @property
100
+ def last_modified_datetime(self) -> datetime:
101
+ """Get last modified as datetime."""
102
+ return datetime.fromtimestamp(self.last_modified)
103
+
104
+
105
+ @dataclass
106
+ class LifecycleOperationResult:
107
+ """Result of a lifecycle operation."""
108
+
109
+ operation: LifecycleOperation
110
+ agent_name: str
111
+ success: bool
112
+ duration_ms: float
113
+ error_message: Optional[str] = None
114
+ modification_id: Optional[str] = None
115
+ persistence_id: Optional[str] = None
116
+ cache_invalidated: bool = False
117
+ registry_updated: bool = False
118
+ metadata: Dict[str, Any] = field(default_factory=dict)
119
+
120
+
121
+ class AgentLifecycleManager(BaseService):
122
+ """
123
+ Agent Lifecycle Manager - Unified agent management across hierarchy tiers.
124
+
125
+ Features:
126
+ - Complete agent lifecycle management (CRUD operations)
127
+ - Integrated modification tracking and persistence
128
+ - Automatic cache invalidation and registry synchronization
129
+ - Comprehensive backup and versioning system
130
+ - Real-time conflict detection and resolution
131
+ - Performance monitoring and optimization
132
+ """
133
+
134
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
135
+ """Initialize the agent lifecycle manager."""
136
+ super().__init__("agent_lifecycle_manager", config)
137
+
138
+ # Configuration
139
+ self.enable_auto_backup = self.get_config("enable_auto_backup", True)
140
+ self.enable_auto_validation = self.get_config("enable_auto_validation", True)
141
+ self.enable_cache_invalidation = self.get_config("enable_cache_invalidation", True)
142
+ self.enable_registry_sync = self.get_config("enable_registry_sync", True)
143
+ self.default_persistence_strategy = PersistenceStrategy(
144
+ self.get_config("default_persistence_strategy", PersistenceStrategy.USER_OVERRIDE.value)
145
+ )
146
+
147
+ # Core services
148
+ self.shared_cache: Optional[SharedPromptCache] = None
149
+ self.agent_registry: Optional[AgentRegistry] = None
150
+ self.modification_tracker: Optional[AgentModificationTracker] = None
151
+ self.persistence_service: Optional[AgentPersistenceService] = None
152
+
153
+ # Lifecycle tracking
154
+ self.agent_records: Dict[str, AgentLifecycleRecord] = {}
155
+ self.operation_history: List[LifecycleOperationResult] = []
156
+ self.active_operations: Dict[str, LifecycleOperation] = {}
157
+
158
+ # Performance metrics
159
+ self.performance_metrics = {
160
+ 'total_operations': 0,
161
+ 'successful_operations': 0,
162
+ 'failed_operations': 0,
163
+ 'average_duration_ms': 0.0,
164
+ 'cache_hit_rate': 0.0
165
+ }
166
+
167
+ # Operation lock for thread safety
168
+ self._operation_lock = asyncio.Lock()
169
+
170
+ # Configuration manager
171
+ self.config_mgr = ConfigurationManager(cache_enabled=True)
172
+
173
+ self.logger.info("AgentLifecycleManager initialized")
174
+
175
+ async def _initialize(self) -> None:
176
+ """Initialize the lifecycle manager."""
177
+ self.logger.info("Initializing AgentLifecycleManager...")
178
+
179
+ # Initialize core services
180
+ await self._initialize_core_services()
181
+
182
+ # Load existing agent records
183
+ await self._load_agent_records()
184
+
185
+ # Start service integrations
186
+ await self._setup_service_integrations()
187
+
188
+ # Perform initial registry sync
189
+ if self.enable_registry_sync:
190
+ await self._sync_with_registry()
191
+
192
+ self.logger.info("AgentLifecycleManager initialized successfully")
193
+
194
+ async def _cleanup(self) -> None:
195
+ """Cleanup lifecycle manager resources."""
196
+ self.logger.info("Cleaning up AgentLifecycleManager...")
197
+
198
+ # Save agent records
199
+ await self._save_agent_records()
200
+
201
+ # Stop core services if we own them
202
+ await self._cleanup_core_services()
203
+
204
+ self.logger.info("AgentLifecycleManager cleaned up")
205
+
206
+ async def _health_check(self) -> Dict[str, bool]:
207
+ """Perform lifecycle manager health checks."""
208
+ checks = {}
209
+
210
+ try:
211
+ # Check core service availability
212
+ checks["cache_service"] = self.shared_cache is not None
213
+ checks["registry_service"] = self.agent_registry is not None
214
+ checks["tracker_service"] = self.modification_tracker is not None
215
+ checks["persistence_service"] = self.persistence_service is not None
216
+
217
+ # Check operation capabilities
218
+ checks["can_create_agents"] = await self._test_create_capability()
219
+ checks["can_modify_agents"] = await self._test_modify_capability()
220
+ checks["can_delete_agents"] = await self._test_delete_capability()
221
+
222
+ # Check data integrity
223
+ checks["agent_records_valid"] = len(self.agent_records) >= 0
224
+ checks["operation_history_valid"] = len(self.operation_history) >= 0
225
+
226
+ except Exception as e:
227
+ self.logger.error(f"Lifecycle manager health check failed: {e}")
228
+ checks["health_check_error"] = False
229
+
230
+ return checks
231
+
232
+ async def _initialize_core_services(self) -> None:
233
+ """Initialize core service dependencies."""
234
+ try:
235
+ # Initialize SharedPromptCache
236
+ self.shared_cache = SharedPromptCache.get_instance()
237
+
238
+ # Initialize AgentRegistry
239
+ self.agent_registry = AgentRegistry(cache_service=self.shared_cache)
240
+
241
+ # Initialize AgentModificationTracker
242
+ self.modification_tracker = AgentModificationTracker()
243
+ await self.modification_tracker.start()
244
+
245
+ # Initialize AgentPersistenceService
246
+ self.persistence_service = AgentPersistenceService()
247
+ await self.persistence_service.start()
248
+
249
+ self.logger.info("Core services initialized successfully")
250
+
251
+ except Exception as e:
252
+ self.logger.error(f"Failed to initialize core services: {e}")
253
+ raise
254
+
255
+ async def _setup_service_integrations(self) -> None:
256
+ """Set up integrations between services."""
257
+ try:
258
+ # Register modification callback
259
+ if self.modification_tracker:
260
+ self.modification_tracker.register_modification_callback(
261
+ self._handle_modification_event
262
+ )
263
+
264
+ self.logger.debug("Service integrations set up successfully")
265
+
266
+ except Exception as e:
267
+ self.logger.warning(f"Failed to setup some service integrations: {e}")
268
+
269
+ async def _load_agent_records(self) -> None:
270
+ """Load existing agent lifecycle records."""
271
+ try:
272
+ records_file = Path.home() / '.claude-pm' / 'agent_tracking' / 'lifecycle_records.json'
273
+ if path_ops.validate_exists(records_file):
274
+ data = self.config_mgr.load_json(records_file)
275
+
276
+ for agent_name, record_data in data.items():
277
+ record = AgentLifecycleRecord(**record_data)
278
+ # Convert string enum back to enum
279
+ record.current_state = LifecycleState(record_data['current_state'])
280
+ record.tier = ModificationTier(record_data['tier'])
281
+ self.agent_records[agent_name] = record
282
+
283
+ self.logger.info(f"Loaded {len(self.agent_records)} agent records")
284
+
285
+ except Exception as e:
286
+ self.logger.warning(f"Failed to load agent records: {e}")
287
+
288
+ async def _save_agent_records(self) -> None:
289
+ """Save agent lifecycle records to disk."""
290
+ try:
291
+ records_file = Path.home() / '.claude-pm' / 'agent_tracking' / 'lifecycle_records.json'
292
+ path_ops.ensure_dir(records_file.parent)
293
+
294
+ data = {}
295
+ for agent_name, record in self.agent_records.items():
296
+ record_dict = record.__dict__.copy()
297
+ # Convert enums to strings for JSON serialization
298
+ record_dict['current_state'] = record.current_state.value
299
+ record_dict['tier'] = record.tier.value
300
+ data[agent_name] = record_dict
301
+
302
+ # Use save_json with custom encoder for datetime serialization
303
+ import json
304
+
305
+ # First convert to JSON string with custom encoder, then save
306
+ json_str = json.dumps(data, indent=2, default=str)
307
+ records_file.parent.mkdir(parents=True, exist_ok=True)
308
+ with open(records_file, 'w', encoding='utf-8') as f:
309
+ f.write(json_str)
310
+
311
+ self.logger.debug(f"Saved {len(self.agent_records)} agent records")
312
+
313
+ except Exception as e:
314
+ self.logger.error(f"Failed to save agent records: {e}")
315
+
316
+ async def _sync_with_registry(self) -> None:
317
+ """Synchronize with agent registry."""
318
+ try:
319
+ if not self.agent_registry:
320
+ return
321
+
322
+ # Discover all agents via registry
323
+ await self.agent_registry.discover_agents()
324
+ all_agents = await self.agent_registry.list_agents()
325
+
326
+ # Update lifecycle records with registry data
327
+ for agent_metadata in all_agents:
328
+ if agent_metadata.name not in self.agent_records:
329
+ # Create new lifecycle record
330
+ tier_map = {
331
+ 'project': ModificationTier.PROJECT,
332
+ 'user': ModificationTier.USER,
333
+ 'system': ModificationTier.SYSTEM
334
+ }
335
+
336
+ record = AgentLifecycleRecord(
337
+ agent_name=agent_metadata.name,
338
+ current_state=LifecycleState.ACTIVE,
339
+ tier=tier_map.get(agent_metadata.tier, ModificationTier.USER),
340
+ file_path=agent_metadata.path,
341
+ created_at=agent_metadata.last_modified or time.time(),
342
+ last_modified=agent_metadata.last_modified or time.time(),
343
+ version="1.0.0",
344
+ metadata={
345
+ 'type': agent_metadata.type,
346
+ 'description': agent_metadata.description,
347
+ 'capabilities': agent_metadata.capabilities,
348
+ 'validated': agent_metadata.validated
349
+ }
350
+ )
351
+
352
+ self.agent_records[agent_metadata.name] = record
353
+
354
+ self.logger.info(f"Synchronized with registry: {len(all_agents)} agents")
355
+
356
+ except Exception as e:
357
+ self.logger.error(f"Failed to sync with registry: {e}")
358
+
359
+ async def create_agent(self,
360
+ agent_name: str,
361
+ agent_content: str,
362
+ tier: ModificationTier = ModificationTier.USER,
363
+ agent_type: str = "custom",
364
+ **kwargs) -> LifecycleOperationResult:
365
+ """
366
+ Create a new agent with complete lifecycle tracking.
367
+
368
+ Args:
369
+ agent_name: Name of the agent to create
370
+ agent_content: Content of the agent file
371
+ tier: Target tier for creation
372
+ agent_type: Type of agent (for classification)
373
+ **kwargs: Additional metadata
374
+
375
+ Returns:
376
+ LifecycleOperationResult with operation details
377
+ """
378
+ start_time = time.time()
379
+
380
+ async with self._operation_lock:
381
+ self.active_operations[agent_name] = LifecycleOperation.CREATE
382
+
383
+ try:
384
+ # Check if agent already exists
385
+ if agent_name in self.agent_records:
386
+ return LifecycleOperationResult(
387
+ operation=LifecycleOperation.CREATE,
388
+ agent_name=agent_name,
389
+ success=False,
390
+ duration_ms=(time.time() - start_time) * 1000,
391
+ error_message="Agent already exists"
392
+ )
393
+
394
+ # Determine file path
395
+ file_path = await self._determine_agent_file_path(agent_name, tier)
396
+
397
+ # Track modification
398
+ modification = await self.modification_tracker.track_modification(
399
+ agent_name=agent_name,
400
+ modification_type=ModificationType.CREATE,
401
+ file_path=str(file_path),
402
+ tier=tier,
403
+ agent_type=agent_type,
404
+ **kwargs
405
+ )
406
+
407
+ # Persist agent
408
+ persistence_record = await self.persistence_service.persist_agent(
409
+ agent_name=agent_name,
410
+ agent_content=agent_content,
411
+ source_tier=tier,
412
+ target_tier=tier,
413
+ strategy=self.default_persistence_strategy
414
+ )
415
+
416
+ # Create lifecycle record
417
+ lifecycle_record = AgentLifecycleRecord(
418
+ agent_name=agent_name,
419
+ current_state=LifecycleState.ACTIVE,
420
+ tier=tier,
421
+ file_path=str(file_path),
422
+ created_at=time.time(),
423
+ last_modified=time.time(),
424
+ version="1.0.0",
425
+ modifications=[modification.modification_id],
426
+ persistence_operations=[persistence_record.operation_id],
427
+ metadata={
428
+ 'agent_type': agent_type,
429
+ **kwargs
430
+ }
431
+ )
432
+
433
+ self.agent_records[agent_name] = lifecycle_record
434
+
435
+ # Invalidate cache and update registry
436
+ cache_invalidated = await self._invalidate_agent_cache(agent_name)
437
+ registry_updated = await self._update_registry(agent_name)
438
+
439
+ # Create result
440
+ result = LifecycleOperationResult(
441
+ operation=LifecycleOperation.CREATE,
442
+ agent_name=agent_name,
443
+ success=persistence_record.success,
444
+ duration_ms=(time.time() - start_time) * 1000,
445
+ modification_id=modification.modification_id,
446
+ persistence_id=persistence_record.operation_id,
447
+ cache_invalidated=cache_invalidated,
448
+ registry_updated=registry_updated,
449
+ metadata={'file_path': str(file_path)}
450
+ )
451
+
452
+ if not persistence_record.success:
453
+ result.error_message = persistence_record.error_message
454
+ lifecycle_record.current_state = LifecycleState.CONFLICTED
455
+
456
+ # Update performance metrics
457
+ await self._update_performance_metrics(result)
458
+
459
+ self.operation_history.append(result)
460
+ self.logger.info(f"Created agent '{agent_name}' in {result.duration_ms:.1f}ms")
461
+
462
+ return result
463
+
464
+ except Exception as e:
465
+ result = LifecycleOperationResult(
466
+ operation=LifecycleOperation.CREATE,
467
+ agent_name=agent_name,
468
+ success=False,
469
+ duration_ms=(time.time() - start_time) * 1000,
470
+ error_message=str(e)
471
+ )
472
+
473
+ self.operation_history.append(result)
474
+ await self._update_performance_metrics(result)
475
+
476
+ self.logger.error(f"Failed to create agent '{agent_name}': {e}")
477
+ return result
478
+
479
+ finally:
480
+ self.active_operations.pop(agent_name, None)
481
+
482
+ async def update_agent(self,
483
+ agent_name: str,
484
+ agent_content: str,
485
+ **kwargs) -> LifecycleOperationResult:
486
+ """
487
+ Update an existing agent with lifecycle tracking.
488
+
489
+ Args:
490
+ agent_name: Name of the agent to update
491
+ agent_content: New content for the agent
492
+ **kwargs: Additional metadata
493
+
494
+ Returns:
495
+ LifecycleOperationResult with operation details
496
+ """
497
+ start_time = time.time()
498
+
499
+ async with self._operation_lock:
500
+ self.active_operations[agent_name] = LifecycleOperation.UPDATE
501
+
502
+ try:
503
+ # Check if agent exists
504
+ if agent_name not in self.agent_records:
505
+ return LifecycleOperationResult(
506
+ operation=LifecycleOperation.UPDATE,
507
+ agent_name=agent_name,
508
+ success=False,
509
+ duration_ms=(time.time() - start_time) * 1000,
510
+ error_message="Agent not found"
511
+ )
512
+
513
+ record = self.agent_records[agent_name]
514
+
515
+ # Track modification
516
+ modification = await self.modification_tracker.track_modification(
517
+ agent_name=agent_name,
518
+ modification_type=ModificationType.MODIFY,
519
+ file_path=record.file_path,
520
+ tier=record.tier,
521
+ **kwargs
522
+ )
523
+
524
+ # Persist agent
525
+ persistence_record = await self.persistence_service.persist_agent(
526
+ agent_name=agent_name,
527
+ agent_content=agent_content,
528
+ source_tier=record.tier,
529
+ strategy=self.default_persistence_strategy
530
+ )
531
+
532
+ # Update lifecycle record
533
+ record.current_state = LifecycleState.MODIFIED
534
+ record.last_modified = time.time()
535
+ record.modifications.append(modification.modification_id)
536
+ record.persistence_operations.append(persistence_record.operation_id)
537
+
538
+ # Increment version
539
+ current_version = record.version.split('.')
540
+ current_version[-1] = str(int(current_version[-1]) + 1)
541
+ record.version = '.'.join(current_version)
542
+
543
+ # Invalidate cache and update registry
544
+ cache_invalidated = await self._invalidate_agent_cache(agent_name)
545
+ registry_updated = await self._update_registry(agent_name)
546
+
547
+ # Create result
548
+ result = LifecycleOperationResult(
549
+ operation=LifecycleOperation.UPDATE,
550
+ agent_name=agent_name,
551
+ success=persistence_record.success,
552
+ duration_ms=(time.time() - start_time) * 1000,
553
+ modification_id=modification.modification_id,
554
+ persistence_id=persistence_record.operation_id,
555
+ cache_invalidated=cache_invalidated,
556
+ registry_updated=registry_updated,
557
+ metadata={'new_version': record.version}
558
+ )
559
+
560
+ if not persistence_record.success:
561
+ result.error_message = persistence_record.error_message
562
+ record.current_state = LifecycleState.CONFLICTED
563
+
564
+ # Update performance metrics
565
+ await self._update_performance_metrics(result)
566
+
567
+ self.operation_history.append(result)
568
+ self.logger.info(f"Updated agent '{agent_name}' to version {record.version} in {result.duration_ms:.1f}ms")
569
+
570
+ return result
571
+
572
+ except Exception as e:
573
+ result = LifecycleOperationResult(
574
+ operation=LifecycleOperation.UPDATE,
575
+ agent_name=agent_name,
576
+ success=False,
577
+ duration_ms=(time.time() - start_time) * 1000,
578
+ error_message=str(e)
579
+ )
580
+
581
+ self.operation_history.append(result)
582
+ await self._update_performance_metrics(result)
583
+
584
+ self.logger.error(f"Failed to update agent '{agent_name}': {e}")
585
+ return result
586
+
587
+ finally:
588
+ self.active_operations.pop(agent_name, None)
589
+
590
+ async def delete_agent(self, agent_name: str, **kwargs) -> LifecycleOperationResult:
591
+ """
592
+ Delete an agent with lifecycle tracking.
593
+
594
+ Args:
595
+ agent_name: Name of the agent to delete
596
+ **kwargs: Additional metadata
597
+
598
+ Returns:
599
+ LifecycleOperationResult with operation details
600
+ """
601
+ start_time = time.time()
602
+
603
+ async with self._operation_lock:
604
+ self.active_operations[agent_name] = LifecycleOperation.DELETE
605
+
606
+ try:
607
+ # Check if agent exists
608
+ if agent_name not in self.agent_records:
609
+ return LifecycleOperationResult(
610
+ operation=LifecycleOperation.DELETE,
611
+ agent_name=agent_name,
612
+ success=False,
613
+ duration_ms=(time.time() - start_time) * 1000,
614
+ error_message="Agent not found"
615
+ )
616
+
617
+ record = self.agent_records[agent_name]
618
+
619
+ # Create backup before deletion
620
+ backup_path = None
621
+ if self.enable_auto_backup:
622
+ backup_path = await self._create_deletion_backup(agent_name, record)
623
+
624
+ # Track modification
625
+ modification = await self.modification_tracker.track_modification(
626
+ agent_name=agent_name,
627
+ modification_type=ModificationType.DELETE,
628
+ file_path=record.file_path,
629
+ tier=record.tier,
630
+ backup_path=backup_path,
631
+ **kwargs
632
+ )
633
+
634
+ # Delete file
635
+ file_path = Path(record.file_path)
636
+ if path_ops.validate_exists(file_path):
637
+ path_ops.safe_delete(file_path)
638
+
639
+ # Update lifecycle record
640
+ record.current_state = LifecycleState.DELETED
641
+ record.last_modified = time.time()
642
+ record.modifications.append(modification.modification_id)
643
+ if backup_path:
644
+ record.backup_paths.append(backup_path)
645
+
646
+ # Invalidate cache and update registry
647
+ cache_invalidated = await self._invalidate_agent_cache(agent_name)
648
+ registry_updated = await self._update_registry(agent_name)
649
+
650
+ # Create result
651
+ result = LifecycleOperationResult(
652
+ operation=LifecycleOperation.DELETE,
653
+ agent_name=agent_name,
654
+ success=True,
655
+ duration_ms=(time.time() - start_time) * 1000,
656
+ modification_id=modification.modification_id,
657
+ cache_invalidated=cache_invalidated,
658
+ registry_updated=registry_updated,
659
+ metadata={'backup_path': backup_path}
660
+ )
661
+
662
+ # Update performance metrics
663
+ await self._update_performance_metrics(result)
664
+
665
+ self.operation_history.append(result)
666
+ self.logger.info(f"Deleted agent '{agent_name}' in {result.duration_ms:.1f}ms")
667
+
668
+ return result
669
+
670
+ except Exception as e:
671
+ result = LifecycleOperationResult(
672
+ operation=LifecycleOperation.DELETE,
673
+ agent_name=agent_name,
674
+ success=False,
675
+ duration_ms=(time.time() - start_time) * 1000,
676
+ error_message=str(e)
677
+ )
678
+
679
+ self.operation_history.append(result)
680
+ await self._update_performance_metrics(result)
681
+
682
+ self.logger.error(f"Failed to delete agent '{agent_name}': {e}")
683
+ return result
684
+
685
+ finally:
686
+ self.active_operations.pop(agent_name, None)
687
+
688
+ async def _determine_agent_file_path(self, agent_name: str, tier: ModificationTier) -> Path:
689
+ """Determine appropriate file path for agent."""
690
+ if tier == ModificationTier.USER:
691
+ base_path = Path.home() / '.claude-pm' / 'agents'
692
+ elif tier == ModificationTier.PROJECT:
693
+ base_path = Path.cwd() / '.claude-pm' / 'agents'
694
+ else: # SYSTEM
695
+ base_path = Path.cwd() / 'claude_pm' / 'agents'
696
+
697
+ path_ops.ensure_dir(base_path)
698
+ return base_path / f"{agent_name}_agent.py"
699
+
700
+ async def _create_deletion_backup(self, agent_name: str, record: AgentLifecycleRecord) -> Optional[str]:
701
+ """Create backup before agent deletion."""
702
+ try:
703
+ source_path = Path(record.file_path)
704
+ if not path_ops.validate_exists(source_path):
705
+ return None
706
+
707
+ backup_dir = Path.home() / '.claude-pm' / 'agent_tracking' / 'backups'
708
+ path_ops.ensure_dir(backup_dir)
709
+
710
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
711
+ backup_filename = f"{agent_name}_deleted_{timestamp}{source_path.suffix}"
712
+ backup_path = backup_dir / backup_filename
713
+
714
+ path_ops.safe_copy(source_path, backup_path)
715
+ return str(backup_path)
716
+
717
+ except Exception as e:
718
+ self.logger.warning(f"Failed to create deletion backup for {agent_name}: {e}")
719
+ return None
720
+
721
+ async def _invalidate_agent_cache(self, agent_name: str) -> bool:
722
+ """Invalidate cache entries for agent."""
723
+ if not self.enable_cache_invalidation or not self.shared_cache:
724
+ return False
725
+
726
+ try:
727
+ patterns = [
728
+ f"agent_profile:{agent_name}:*",
729
+ f"task_prompt:{agent_name}:*",
730
+ f"agent_registry_discovery",
731
+ f"agent_profile_enhanced:{agent_name}:*"
732
+ ]
733
+
734
+ for pattern in patterns:
735
+ await asyncio.get_event_loop().run_in_executor(
736
+ None,
737
+ lambda p=pattern: self.shared_cache.invalidate(p)
738
+ )
739
+
740
+ return True
741
+
742
+ except Exception as e:
743
+ self.logger.warning(f"Failed to invalidate cache for {agent_name}: {e}")
744
+ return False
745
+
746
+ async def _update_registry(self, agent_name: str) -> bool:
747
+ """Update agent registry after modification."""
748
+ if not self.enable_registry_sync or not self.agent_registry:
749
+ return False
750
+
751
+ try:
752
+ # Refresh specific agent in registry
753
+ await self.agent_registry.refresh_agent(agent_name)
754
+ return True
755
+
756
+ except Exception as e:
757
+ self.logger.warning(f"Failed to update registry for {agent_name}: {e}")
758
+ return False
759
+
760
+ async def _update_performance_metrics(self, result: LifecycleOperationResult) -> None:
761
+ """Update performance metrics with operation result."""
762
+ self.performance_metrics['total_operations'] += 1
763
+
764
+ if result.success:
765
+ self.performance_metrics['successful_operations'] += 1
766
+ else:
767
+ self.performance_metrics['failed_operations'] += 1
768
+
769
+ # Update average duration
770
+ total_ops = self.performance_metrics['total_operations']
771
+ current_avg = self.performance_metrics['average_duration_ms']
772
+ new_avg = ((current_avg * (total_ops - 1)) + result.duration_ms) / total_ops
773
+ self.performance_metrics['average_duration_ms'] = new_avg
774
+
775
+ # Update cache hit rate if cache was involved
776
+ if result.cache_invalidated:
777
+ # This would be more sophisticated in practice
778
+ pass
779
+
780
+ async def _handle_modification_event(self, modification: AgentModification) -> None:
781
+ """Handle modification events from tracker."""
782
+ try:
783
+ agent_name = modification.agent_name
784
+
785
+ # Update lifecycle record if exists
786
+ if agent_name in self.agent_records:
787
+ record = self.agent_records[agent_name]
788
+ record.last_modified = modification.timestamp
789
+ record.modifications.append(modification.modification_id)
790
+
791
+ # Update state based on modification type
792
+ if modification.modification_type == ModificationType.DELETE:
793
+ record.current_state = LifecycleState.DELETED
794
+ elif modification.modification_type in [ModificationType.CREATE, ModificationType.MODIFY]:
795
+ record.current_state = LifecycleState.MODIFIED
796
+
797
+ self.logger.debug(f"Updated lifecycle record for {agent_name} due to {modification.modification_type.value}")
798
+
799
+ except Exception as e:
800
+ self.logger.error(f"Error handling modification event: {e}")
801
+
802
+ async def _test_create_capability(self) -> bool:
803
+ """Test agent creation capability."""
804
+ try:
805
+ # This would test if we can create agents in the configured tiers
806
+ return (self.modification_tracker is not None and
807
+ self.persistence_service is not None)
808
+ except Exception:
809
+ return False
810
+
811
+ async def _test_modify_capability(self) -> bool:
812
+ """Test agent modification capability."""
813
+ try:
814
+ # This would test if we can modify existing agents
815
+ return (self.modification_tracker is not None and
816
+ self.persistence_service is not None)
817
+ except Exception:
818
+ return False
819
+
820
+ async def _test_delete_capability(self) -> bool:
821
+ """Test agent deletion capability."""
822
+ try:
823
+ # This would test if we can delete agents
824
+ return self.modification_tracker is not None
825
+ except Exception:
826
+ return False
827
+
828
+ async def _cleanup_core_services(self) -> None:
829
+ """Cleanup core services if we manage their lifecycle."""
830
+ try:
831
+ if self.modification_tracker:
832
+ await self.modification_tracker.stop()
833
+
834
+ if self.persistence_service:
835
+ await self.persistence_service.stop()
836
+
837
+ except Exception as e:
838
+ self.logger.error(f"Error cleaning up core services: {e}")
839
+
840
+ # Public API Methods
841
+
842
+ async def get_agent_status(self, agent_name: str) -> Optional[AgentLifecycleRecord]:
843
+ """Get current status of an agent."""
844
+ return self.agent_records.get(agent_name)
845
+
846
+ async def list_agents(self, state_filter: Optional[LifecycleState] = None) -> List[AgentLifecycleRecord]:
847
+ """List agents with optional state filtering."""
848
+ agents = list(self.agent_records.values())
849
+
850
+ if state_filter:
851
+ agents = [agent for agent in agents if agent.current_state == state_filter]
852
+
853
+ return sorted(agents, key=lambda x: x.last_modified, reverse=True)
854
+
855
+ async def get_operation_history(self, agent_name: Optional[str] = None, limit: int = 100) -> List[LifecycleOperationResult]:
856
+ """Get operation history with optional filtering."""
857
+ history = self.operation_history
858
+
859
+ if agent_name:
860
+ history = [op for op in history if op.agent_name == agent_name]
861
+
862
+ return sorted(history, key=lambda x: x.duration_ms, reverse=True)[:limit]
863
+
864
+ async def get_lifecycle_stats(self) -> Dict[str, Any]:
865
+ """Get comprehensive lifecycle statistics."""
866
+ stats = {
867
+ 'total_agents': len(self.agent_records),
868
+ 'active_operations': len(self.active_operations),
869
+ 'performance_metrics': self.performance_metrics.copy()
870
+ }
871
+
872
+ # State distribution
873
+ state_counts = {}
874
+ for record in self.agent_records.values():
875
+ state_counts[record.current_state.value] = state_counts.get(record.current_state.value, 0) + 1
876
+
877
+ stats['agents_by_state'] = state_counts
878
+
879
+ # Tier distribution
880
+ tier_counts = {}
881
+ for record in self.agent_records.values():
882
+ tier_counts[record.tier.value] = tier_counts.get(record.tier.value, 0) + 1
883
+
884
+ stats['agents_by_tier'] = tier_counts
885
+
886
+ # Recent activity
887
+ recent_ops = [
888
+ op for op in self.operation_history
889
+ if (time.time() - (op.duration_ms / 1000)) < 3600 # Last hour
890
+ ]
891
+ stats['recent_operations'] = len(recent_ops)
892
+
893
+ return stats
894
+
895
+ async def restore_agent(self, agent_name: str, backup_path: Optional[str] = None) -> LifecycleOperationResult:
896
+ """Restore agent from backup."""
897
+ start_time = time.time()
898
+
899
+ try:
900
+ record = self.agent_records.get(agent_name)
901
+ if not record:
902
+ return LifecycleOperationResult(
903
+ operation=LifecycleOperation.RESTORE,
904
+ agent_name=agent_name,
905
+ success=False,
906
+ duration_ms=(time.time() - start_time) * 1000,
907
+ error_message="Agent record not found"
908
+ )
909
+
910
+ # Use latest backup if not specified
911
+ if not backup_path and record.backup_paths:
912
+ backup_path = record.backup_paths[-1]
913
+
914
+ if not backup_path or not path_ops.validate_exists(backup_path):
915
+ return LifecycleOperationResult(
916
+ operation=LifecycleOperation.RESTORE,
917
+ agent_name=agent_name,
918
+ success=False,
919
+ duration_ms=(time.time() - start_time) * 1000,
920
+ error_message="No valid backup found"
921
+ )
922
+
923
+ # Read backup content
924
+ backup_content = path_ops.safe_read(backup_path)
925
+ if not backup_content:
926
+ return LifecycleOperationResult(
927
+ operation=LifecycleOperation.RESTORE,
928
+ agent_name=agent_name,
929
+ success=False,
930
+ duration_ms=(time.time() - start_time) * 1000,
931
+ error_message="Failed to read backup content"
932
+ )
933
+
934
+ # Restore via update operation
935
+ return await self.update_agent(
936
+ agent_name=agent_name,
937
+ agent_content=backup_content,
938
+ restored_from=backup_path
939
+ )
940
+
941
+ except Exception as e:
942
+ return LifecycleOperationResult(
943
+ operation=LifecycleOperation.RESTORE,
944
+ agent_name=agent_name,
945
+ success=False,
946
+ duration_ms=(time.time() - start_time) * 1000,
947
+ error_message=str(e)
948
+ )