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,606 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Agent Profile Loader Service - Consolidated Module
4
+ ================================================
5
+
6
+ Comprehensive agent profile loading service with enhanced prompt integration.
7
+ Implements three-tier hierarchy precedence and improved prompt system integration.
8
+
9
+ Key Features:
10
+ - Three-tier hierarchy precedence (Project → User → System)
11
+ - Improved prompt integration with training system
12
+ - SharedPromptCache integration for performance optimization
13
+ - AgentRegistry integration for enhanced discovery
14
+ - Training system integration for prompt improvement
15
+ - Task Tool subprocess creation enhancement
16
+ - Profile validation and error handling
17
+
18
+ This is a consolidated version combining all functionality from the previous
19
+ multi-file implementation for better maintainability.
20
+ """
21
+
22
+ import os
23
+ import json
24
+ import logging
25
+ import asyncio
26
+ import hashlib
27
+ from dataclasses import dataclass, field
28
+ from datetime import datetime
29
+ from enum import Enum
30
+ from pathlib import Path
31
+ from typing import Dict, List, Optional, Any, Tuple, Union, Set
32
+
33
+ import yaml
34
+
35
+ from claude_mpm.core.base_service import BaseService
36
+ from claude_mpm.core.config import Config
37
+ from claude_mpm.services.shared_prompt_cache import SharedPromptCache
38
+ from claude_mpm.services.agent_registry import AgentRegistry
39
+ from claude_mpm.utils.path_operations import path_ops
40
+
41
+
42
+ logger = logging.getLogger(__name__)
43
+
44
+
45
+ # ============================================================================
46
+ # Data Models
47
+ # ============================================================================
48
+
49
+ class ProfileTier(Enum):
50
+ """Agent profile hierarchy tiers."""
51
+ SYSTEM = "system"
52
+ USER = "user"
53
+ PROJECT = "project"
54
+
55
+
56
+ class ProfileStatus(Enum):
57
+ """Profile loading status."""
58
+ LOADED = "loaded"
59
+ ERROR = "error"
60
+ NOT_FOUND = "not_found"
61
+ CACHED = "cached"
62
+
63
+
64
+ @dataclass
65
+ class ImprovedPrompt:
66
+ """Improved prompt data structure."""
67
+ prompt_id: str
68
+ agent_name: str
69
+ version: str
70
+ content: str
71
+ metrics: Dict[str, float] = field(default_factory=dict)
72
+ metadata: Dict[str, Any] = field(default_factory=dict)
73
+ created_at: datetime = field(default_factory=datetime.now)
74
+ validated: bool = False
75
+
76
+
77
+ @dataclass
78
+ class AgentProfile:
79
+ """Complete agent profile with hierarchy information."""
80
+ name: str
81
+ role: str
82
+ description: str
83
+ tier: ProfileTier
84
+ source_path: str
85
+ instructions: str = ""
86
+ capabilities: List[str] = field(default_factory=list)
87
+ constraints: List[str] = field(default_factory=list)
88
+ metadata: Dict[str, Any] = field(default_factory=dict)
89
+ improved_prompts: List[ImprovedPrompt] = field(default_factory=list)
90
+ status: ProfileStatus = ProfileStatus.LOADED
91
+ error: Optional[str] = None
92
+ loaded_at: datetime = field(default_factory=datetime.now)
93
+ cache_key: Optional[str] = None
94
+
95
+
96
+ # ============================================================================
97
+ # Main Service Class
98
+ # ============================================================================
99
+
100
+ class AgentProfileLoader(BaseService):
101
+ """
102
+ Comprehensive agent profile loading service with enhanced prompt integration.
103
+
104
+ This consolidated version combines all functionality from the previous
105
+ multi-file implementation into a single, maintainable module.
106
+ """
107
+
108
+ def __init__(self, config: Optional[Config] = None):
109
+ """Initialize the agent profile loader."""
110
+ super().__init__(name="agent_profile_loader", config=config)
111
+
112
+ # Core configuration
113
+ self.working_directory = Path(os.getcwd())
114
+ self.framework_path = self._detect_framework_path()
115
+ self.user_home = Path.home()
116
+
117
+ # Tier paths configuration
118
+ self.tier_paths = {
119
+ ProfileTier.PROJECT: self.working_directory / 'agents',
120
+ ProfileTier.USER: self.user_home / '.claude-pm' / 'agents',
121
+ ProfileTier.SYSTEM: self.framework_path / 'agent-roles' if self.framework_path else None
122
+ }
123
+
124
+ # Remove None values
125
+ self.tier_paths = {k: v for k, v in self.tier_paths.items() if v is not None}
126
+
127
+ # Profile cache
128
+ self.profile_cache: Dict[str, AgentProfile] = {}
129
+ self.cache_ttl = 3600 # 1 hour
130
+
131
+ # Service integrations
132
+ self.shared_cache: Optional[SharedPromptCache] = None
133
+ self.agent_registry: Optional[AgentRegistry] = None
134
+
135
+ # Improved prompts storage
136
+ self.improved_prompts_path = self.user_home / '.claude-pm' / 'improved_prompts'
137
+ self.improved_prompts_path.mkdir(parents=True, exist_ok=True)
138
+
139
+ # Performance tracking
140
+ self.load_metrics: Dict[str, float] = {}
141
+
142
+ logger.info(f"AgentProfileLoader initialized successfully")
143
+ logger.info(f" Working directory: {self.working_directory}")
144
+ logger.info(f" Framework path: {self.framework_path}")
145
+ logger.info(f" Tier paths: {list(self.tier_paths.keys())}")
146
+
147
+ async def _initialize(self) -> None:
148
+ """Initialize the service and its integrations."""
149
+ logger.info("Initializing AgentProfileLoader service...")
150
+
151
+ # Initialize service integrations
152
+ try:
153
+ self.shared_cache = SharedPromptCache.get_instance()
154
+ self.agent_registry = AgentRegistry(cache_service=self.shared_cache)
155
+ logger.info("Successfully initialized service integrations")
156
+ except Exception as e:
157
+ logger.warning(f"Failed to initialize some integrations: {e}")
158
+
159
+ # Discover and cache initial profiles
160
+ await self._discover_all_profiles()
161
+
162
+ logger.info("AgentProfileLoader service initialized successfully")
163
+
164
+ async def _cleanup(self) -> None:
165
+ """Cleanup service resources."""
166
+ logger.info("Cleaning up AgentProfileLoader service...")
167
+
168
+ # Clear caches
169
+ self.profile_cache.clear()
170
+
171
+ # Save any pending improved prompts
172
+ await self._save_improved_prompts()
173
+
174
+ logger.info("AgentProfileLoader service cleaned up")
175
+
176
+ async def _health_check(self) -> Dict[str, bool]:
177
+ """Perform service health checks."""
178
+ checks = {}
179
+
180
+ try:
181
+ # Check tier paths
182
+ checks["tier_paths_accessible"] = all(
183
+ path.exists() for path in self.tier_paths.values()
184
+ )
185
+
186
+ # Check service integrations
187
+ checks["shared_cache_integration"] = self.shared_cache is not None
188
+ checks["agent_registry_integration"] = self.agent_registry is not None
189
+
190
+ # Check profile discovery
191
+ profile_count = len(self.profile_cache)
192
+ checks["profiles_loaded"] = profile_count > 0
193
+
194
+ # Check improved prompts storage
195
+ checks["improved_prompts_storage"] = self.improved_prompts_path.exists()
196
+
197
+ except Exception as e:
198
+ logger.error(f"Health check failed: {e}")
199
+ checks["health_check_error"] = False
200
+
201
+ return checks
202
+
203
+ # ========================================================================
204
+ # Core Profile Loading
205
+ # ========================================================================
206
+
207
+ async def load_agent_profile(self, agent_name: str,
208
+ use_cache: bool = True) -> Optional[AgentProfile]:
209
+ """
210
+ Load agent profile with three-tier hierarchy precedence.
211
+
212
+ Args:
213
+ agent_name: Name of the agent to load
214
+ use_cache: Whether to use cached profile if available
215
+
216
+ Returns:
217
+ AgentProfile or None if not found
218
+ """
219
+ start_time = asyncio.get_event_loop().time()
220
+
221
+ # Check cache first
222
+ if use_cache and agent_name in self.profile_cache:
223
+ profile = self.profile_cache[agent_name]
224
+ if (datetime.now() - profile.loaded_at).seconds < self.cache_ttl:
225
+ self.load_metrics[f"{agent_name}_cache_hit"] = asyncio.get_event_loop().time() - start_time
226
+ return profile
227
+
228
+ # Load profile with tier precedence
229
+ profile = await self._load_profile_with_precedence(agent_name)
230
+
231
+ if profile:
232
+ # Load improved prompts
233
+ profile.improved_prompts = await self._load_improved_prompts(agent_name)
234
+
235
+ # Cache the profile
236
+ self.profile_cache[agent_name] = profile
237
+
238
+ # Update metrics
239
+ self.load_metrics[f"{agent_name}_load_time"] = asyncio.get_event_loop().time() - start_time
240
+
241
+ # Integrate with SharedPromptCache if available
242
+ if self.shared_cache and profile.status == ProfileStatus.LOADED:
243
+ cache_key = f"agent_profile_{agent_name}"
244
+ await self.shared_cache.set_cached_result(cache_key, profile)
245
+ profile.cache_key = cache_key
246
+
247
+ return profile
248
+
249
+ async def _load_profile_with_precedence(self, agent_name: str) -> Optional[AgentProfile]:
250
+ """Load profile following tier precedence: Project → User → System."""
251
+ for tier in [ProfileTier.PROJECT, ProfileTier.USER, ProfileTier.SYSTEM]:
252
+ if tier not in self.tier_paths:
253
+ continue
254
+
255
+ profile = await self._load_profile_from_tier(agent_name, tier)
256
+ if profile and profile.status == ProfileStatus.LOADED:
257
+ return profile
258
+
259
+ return None
260
+
261
+ async def _load_profile_from_tier(self, agent_name: str,
262
+ tier: ProfileTier) -> Optional[AgentProfile]:
263
+ """Load profile from specific tier."""
264
+ tier_path = self.tier_paths[tier]
265
+
266
+ # Try different file formats and naming conventions
267
+ possible_files = [
268
+ tier_path / f"{agent_name}.yaml",
269
+ tier_path / f"{agent_name}.yml",
270
+ tier_path / f"{agent_name}.json",
271
+ tier_path / f"{agent_name}_agent.yaml",
272
+ tier_path / f"{agent_name}_agent.yml",
273
+ tier_path / f"{agent_name}_agent.json",
274
+ tier_path / f"{agent_name}-agent.yaml",
275
+ tier_path / f"{agent_name}-agent.yml",
276
+ tier_path / f"{agent_name}-agent.json",
277
+ ]
278
+
279
+ for file_path in possible_files:
280
+ if file_path.exists():
281
+ return await self._parse_profile_file(file_path, tier)
282
+
283
+ return None
284
+
285
+ async def _parse_profile_file(self, file_path: Path,
286
+ tier: ProfileTier) -> Optional[AgentProfile]:
287
+ """Parse agent profile from file."""
288
+ try:
289
+ # Read file content
290
+ content = file_path.read_text()
291
+
292
+ # Parse based on file extension
293
+ if file_path.suffix in ['.yaml', '.yml']:
294
+ data = yaml.safe_load(content)
295
+ elif file_path.suffix == '.json':
296
+ data = json.loads(content)
297
+ else:
298
+ # Try to parse as YAML first, then JSON
299
+ try:
300
+ data = yaml.safe_load(content)
301
+ except:
302
+ data = json.loads(content)
303
+
304
+ # Create profile
305
+ profile = AgentProfile(
306
+ name=data.get('name', file_path.stem),
307
+ role=data.get('role', 'agent'),
308
+ description=data.get('description', ''),
309
+ tier=tier,
310
+ source_path=str(file_path),
311
+ instructions=data.get('instructions', ''),
312
+ capabilities=data.get('capabilities', []),
313
+ constraints=data.get('constraints', []),
314
+ metadata=data.get('metadata', {}),
315
+ status=ProfileStatus.LOADED
316
+ )
317
+
318
+ return profile
319
+
320
+ except Exception as e:
321
+ logger.error(f"Error parsing profile {file_path}: {e}")
322
+ return AgentProfile(
323
+ name=file_path.stem,
324
+ role='error',
325
+ description='Failed to load profile',
326
+ tier=tier,
327
+ source_path=str(file_path),
328
+ status=ProfileStatus.ERROR,
329
+ error=str(e)
330
+ )
331
+
332
+ # ========================================================================
333
+ # Profile Discovery
334
+ # ========================================================================
335
+
336
+ async def _discover_all_profiles(self) -> Dict[ProfileTier, List[str]]:
337
+ """Discover all available agent profiles across tiers."""
338
+ discovered = {}
339
+
340
+ for tier, tier_path in self.tier_paths.items():
341
+ if not tier_path.exists():
342
+ continue
343
+
344
+ agents = []
345
+ for file_path in tier_path.glob('*.{yaml,yml,json}'):
346
+ agent_name = file_path.stem
347
+ # Remove common suffixes
348
+ if agent_name.endswith('_agent'):
349
+ agent_name = agent_name[:-6]
350
+ elif agent_name.endswith('-agent'):
351
+ agent_name = agent_name[:-6]
352
+
353
+ if agent_name not in agents:
354
+ agents.append(agent_name)
355
+
356
+ discovered[tier] = agents
357
+ logger.debug(f"Discovered {len(agents)} agents in {tier.value} tier")
358
+
359
+ return discovered
360
+
361
+ async def get_available_agents(self, tier: Optional[ProfileTier] = None) -> List[str]:
362
+ """Get list of available agents, optionally filtered by tier."""
363
+ discovered = await self._discover_all_profiles()
364
+
365
+ if tier:
366
+ return discovered.get(tier, [])
367
+
368
+ # Combine all tiers, removing duplicates
369
+ all_agents = set()
370
+ for agents in discovered.values():
371
+ all_agents.update(agents)
372
+
373
+ return sorted(list(all_agents))
374
+
375
+ # ========================================================================
376
+ # Improved Prompts Management
377
+ # ========================================================================
378
+
379
+ async def _load_improved_prompts(self, agent_name: str) -> List[ImprovedPrompt]:
380
+ """Load improved prompts for an agent."""
381
+ prompts = []
382
+ prompt_file = self.improved_prompts_path / f"{agent_name}_prompts.json"
383
+
384
+ if prompt_file.exists():
385
+ try:
386
+ with open(prompt_file, 'r') as f:
387
+ data = json.load(f)
388
+ for prompt_data in data:
389
+ prompt = ImprovedPrompt(
390
+ prompt_id=prompt_data['prompt_id'],
391
+ agent_name=prompt_data['agent_name'],
392
+ version=prompt_data['version'],
393
+ content=prompt_data['content'],
394
+ metrics=prompt_data.get('metrics', {}),
395
+ metadata=prompt_data.get('metadata', {}),
396
+ created_at=datetime.fromisoformat(prompt_data['created_at']),
397
+ validated=prompt_data.get('validated', False)
398
+ )
399
+ prompts.append(prompt)
400
+ except Exception as e:
401
+ logger.error(f"Error loading improved prompts for {agent_name}: {e}")
402
+
403
+ return prompts
404
+
405
+ async def save_improved_prompt(self, agent_name: str,
406
+ prompt: ImprovedPrompt) -> bool:
407
+ """Save an improved prompt for an agent."""
408
+ try:
409
+ # Load existing prompts
410
+ prompts = await self._load_improved_prompts(agent_name)
411
+
412
+ # Add or update prompt
413
+ existing_idx = None
414
+ for idx, existing in enumerate(prompts):
415
+ if existing.prompt_id == prompt.prompt_id:
416
+ existing_idx = idx
417
+ break
418
+
419
+ if existing_idx is not None:
420
+ prompts[existing_idx] = prompt
421
+ else:
422
+ prompts.append(prompt)
423
+
424
+ # Save to file
425
+ prompt_file = self.improved_prompts_path / f"{agent_name}_prompts.json"
426
+ with open(prompt_file, 'w') as f:
427
+ json.dump([
428
+ {
429
+ 'prompt_id': p.prompt_id,
430
+ 'agent_name': p.agent_name,
431
+ 'version': p.version,
432
+ 'content': p.content,
433
+ 'metrics': p.metrics,
434
+ 'metadata': p.metadata,
435
+ 'created_at': p.created_at.isoformat(),
436
+ 'validated': p.validated
437
+ }
438
+ for p in prompts
439
+ ], f, indent=2)
440
+
441
+ return True
442
+
443
+ except Exception as e:
444
+ logger.error(f"Error saving improved prompt: {e}")
445
+ return False
446
+
447
+ async def _save_improved_prompts(self) -> None:
448
+ """Save all pending improved prompts."""
449
+ for agent_name, profile in self.profile_cache.items():
450
+ if profile.improved_prompts:
451
+ for prompt in profile.improved_prompts:
452
+ await self.save_improved_prompt(agent_name, prompt)
453
+
454
+ # ========================================================================
455
+ # Task Integration
456
+ # ========================================================================
457
+
458
+ async def enhance_task_creation(self, agent_name: str,
459
+ task_params: Dict[str, Any]) -> Dict[str, Any]:
460
+ """
461
+ Enhance Task Tool subprocess creation with agent profile data.
462
+
463
+ Args:
464
+ agent_name: Name of the agent
465
+ task_params: Original task parameters
466
+
467
+ Returns:
468
+ Enhanced task parameters
469
+ """
470
+ profile = await self.load_agent_profile(agent_name)
471
+
472
+ if not profile:
473
+ logger.warning(f"No profile found for agent {agent_name}")
474
+ return task_params
475
+
476
+ # Enhance with profile data
477
+ enhanced = task_params.copy()
478
+
479
+ # Add agent metadata
480
+ enhanced['agent_metadata'] = {
481
+ 'name': profile.name,
482
+ 'role': profile.role,
483
+ 'tier': profile.tier.value,
484
+ 'capabilities': profile.capabilities,
485
+ 'constraints': profile.constraints
486
+ }
487
+
488
+ # Add improved prompts if available
489
+ if profile.improved_prompts:
490
+ best_prompt = max(
491
+ profile.improved_prompts,
492
+ key=lambda p: p.metrics.get('success_rate', 0)
493
+ )
494
+ enhanced['improved_prompt'] = {
495
+ 'content': best_prompt.content,
496
+ 'version': best_prompt.version,
497
+ 'metrics': best_prompt.metrics
498
+ }
499
+
500
+ # Add custom instructions if available
501
+ if profile.instructions:
502
+ enhanced['additional_instructions'] = profile.instructions
503
+
504
+ return enhanced
505
+
506
+ # ========================================================================
507
+ # Metrics and Validation
508
+ # ========================================================================
509
+
510
+ async def validate_profile(self, agent_name: str) -> Dict[str, Any]:
511
+ """Validate agent profile structure and content."""
512
+ profile = await self.load_agent_profile(agent_name)
513
+
514
+ if not profile:
515
+ return {'valid': False, 'error': 'Profile not found'}
516
+
517
+ validation_results = {
518
+ 'valid': True,
519
+ 'warnings': [],
520
+ 'errors': []
521
+ }
522
+
523
+ # Check required fields
524
+ if not profile.name:
525
+ validation_results['errors'].append('Missing agent name')
526
+ validation_results['valid'] = False
527
+
528
+ if not profile.role:
529
+ validation_results['errors'].append('Missing agent role')
530
+ validation_results['valid'] = False
531
+
532
+ # Check profile completeness
533
+ if not profile.description:
534
+ validation_results['warnings'].append('Missing agent description')
535
+
536
+ if not profile.capabilities:
537
+ validation_results['warnings'].append('No capabilities defined')
538
+
539
+ if not profile.constraints:
540
+ validation_results['warnings'].append('No constraints defined')
541
+
542
+ # Validate improved prompts
543
+ for prompt in profile.improved_prompts:
544
+ if not prompt.validated:
545
+ validation_results['warnings'].append(
546
+ f'Unvalidated prompt: {prompt.prompt_id}'
547
+ )
548
+
549
+ return validation_results
550
+
551
+ async def get_profile_metrics(self) -> Dict[str, Any]:
552
+ """Get comprehensive profile loading metrics."""
553
+ metrics = {
554
+ 'cache_size': len(self.profile_cache),
555
+ 'load_metrics': self.load_metrics,
556
+ 'tier_stats': {}
557
+ }
558
+
559
+ # Count profiles by tier
560
+ for profile in self.profile_cache.values():
561
+ tier = profile.tier.value
562
+ metrics['tier_stats'][tier] = metrics['tier_stats'].get(tier, 0) + 1
563
+
564
+ # Calculate average load times
565
+ if self.load_metrics:
566
+ load_times = [v for k, v in self.load_metrics.items() if k.endswith('_load_time')]
567
+ if load_times:
568
+ metrics['avg_load_time'] = sum(load_times) / len(load_times)
569
+
570
+ # Count improved prompts
571
+ total_prompts = sum(
572
+ len(p.improved_prompts) for p in self.profile_cache.values()
573
+ )
574
+ metrics['total_improved_prompts'] = total_prompts
575
+
576
+ return metrics
577
+
578
+ # ========================================================================
579
+ # Utility Methods
580
+ # ========================================================================
581
+
582
+ def _detect_framework_path(self) -> Optional[Path]:
583
+ """Detect the framework path for system-level agents."""
584
+ possible_paths = [
585
+ self.working_directory / 'framework',
586
+ self.working_directory / 'src' / 'claude_mpm' / 'framework',
587
+ Path(__file__).parent.parent / 'framework'
588
+ ]
589
+
590
+ for path in possible_paths:
591
+ if path.exists() and (path / 'agent-roles').exists():
592
+ return path
593
+
594
+ return None
595
+
596
+ def invalidate_cache(self, agent_name: Optional[str] = None) -> None:
597
+ """Invalidate profile cache."""
598
+ if agent_name:
599
+ self.profile_cache.pop(agent_name, None)
600
+ else:
601
+ self.profile_cache.clear()
602
+
603
+ async def reload_profile(self, agent_name: str) -> Optional[AgentProfile]:
604
+ """Force reload of agent profile, bypassing cache."""
605
+ self.invalidate_cache(agent_name)
606
+ return await self.load_agent_profile(agent_name, use_cache=False)