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.
- claude_mpm/__init__.py +17 -0
- claude_mpm/__main__.py +14 -0
- claude_mpm/_version.py +32 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
- claude_mpm/agents/INSTRUCTIONS.md +375 -0
- claude_mpm/agents/__init__.py +118 -0
- claude_mpm/agents/agent_loader.py +621 -0
- claude_mpm/agents/agent_loader_integration.py +229 -0
- claude_mpm/agents/agents_metadata.py +204 -0
- claude_mpm/agents/base_agent.json +27 -0
- claude_mpm/agents/base_agent_loader.py +519 -0
- claude_mpm/agents/schema/agent_schema.json +160 -0
- claude_mpm/agents/system_agent_config.py +587 -0
- claude_mpm/agents/templates/__init__.py +101 -0
- claude_mpm/agents/templates/data_engineer_agent.json +46 -0
- claude_mpm/agents/templates/documentation_agent.json +45 -0
- claude_mpm/agents/templates/engineer_agent.json +49 -0
- claude_mpm/agents/templates/ops_agent.json +46 -0
- claude_mpm/agents/templates/qa_agent.json +45 -0
- claude_mpm/agents/templates/research_agent.json +49 -0
- claude_mpm/agents/templates/security_agent.json +46 -0
- claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
- claude_mpm/agents/templates/version_control_agent.json +46 -0
- claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
- claude_mpm/cli.py +655 -0
- claude_mpm/cli_main.py +13 -0
- claude_mpm/cli_module/__init__.py +15 -0
- claude_mpm/cli_module/args.py +222 -0
- claude_mpm/cli_module/commands.py +203 -0
- claude_mpm/cli_module/migration_example.py +183 -0
- claude_mpm/cli_module/refactoring_guide.md +253 -0
- claude_mpm/cli_old/__init__.py +1 -0
- claude_mpm/cli_old/ticket_cli.py +102 -0
- claude_mpm/config/__init__.py +5 -0
- claude_mpm/config/hook_config.py +42 -0
- claude_mpm/constants.py +150 -0
- claude_mpm/core/__init__.py +45 -0
- claude_mpm/core/agent_name_normalizer.py +248 -0
- claude_mpm/core/agent_registry.py +627 -0
- claude_mpm/core/agent_registry.py.bak +312 -0
- claude_mpm/core/agent_session_manager.py +273 -0
- claude_mpm/core/base_service.py +747 -0
- claude_mpm/core/base_service.py.bak +406 -0
- claude_mpm/core/config.py +334 -0
- claude_mpm/core/config_aliases.py +292 -0
- claude_mpm/core/container.py +347 -0
- claude_mpm/core/factories.py +281 -0
- claude_mpm/core/framework_loader.py +472 -0
- claude_mpm/core/injectable_service.py +206 -0
- claude_mpm/core/interfaces.py +539 -0
- claude_mpm/core/logger.py +468 -0
- claude_mpm/core/minimal_framework_loader.py +107 -0
- claude_mpm/core/mixins.py +150 -0
- claude_mpm/core/service_registry.py +299 -0
- claude_mpm/core/session_manager.py +190 -0
- claude_mpm/core/simple_runner.py +511 -0
- claude_mpm/core/tool_access_control.py +173 -0
- claude_mpm/hooks/README.md +243 -0
- claude_mpm/hooks/__init__.py +5 -0
- claude_mpm/hooks/base_hook.py +154 -0
- claude_mpm/hooks/builtin/__init__.py +1 -0
- claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
- claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
- claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
- claude_mpm/hooks/hook_client.py +264 -0
- claude_mpm/hooks/hook_runner.py +370 -0
- claude_mpm/hooks/json_rpc_executor.py +259 -0
- claude_mpm/hooks/json_rpc_hook_client.py +319 -0
- claude_mpm/hooks/tool_call_interceptor.py +204 -0
- claude_mpm/init.py +246 -0
- claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
- claude_mpm/orchestration/__init__.py +6 -0
- claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
- claude_mpm/orchestration/archive/factory.py +215 -0
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
- claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
- claude_mpm/orchestration/archive/orchestrator.py +501 -0
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
- claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
- claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
- claude_mpm/scripts/__init__.py +1 -0
- claude_mpm/scripts/ticket.py +269 -0
- claude_mpm/services/__init__.py +10 -0
- claude_mpm/services/agent_deployment.py +955 -0
- claude_mpm/services/agent_lifecycle_manager.py +948 -0
- claude_mpm/services/agent_management_service.py +596 -0
- claude_mpm/services/agent_modification_tracker.py +841 -0
- claude_mpm/services/agent_profile_loader.py +606 -0
- claude_mpm/services/agent_registry.py +677 -0
- claude_mpm/services/base_agent_manager.py +380 -0
- claude_mpm/services/framework_agent_loader.py +337 -0
- claude_mpm/services/framework_claude_md_generator/README.md +92 -0
- claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
- claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
- claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
- claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
- claude_mpm/services/framework_claude_md_generator.py +621 -0
- claude_mpm/services/hook_service.py +388 -0
- claude_mpm/services/hook_service_manager.py +223 -0
- claude_mpm/services/json_rpc_hook_manager.py +92 -0
- claude_mpm/services/parent_directory_manager/README.md +83 -0
- claude_mpm/services/parent_directory_manager/__init__.py +577 -0
- claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
- claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
- claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
- claude_mpm/services/parent_directory_manager/operations.py +186 -0
- claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
- claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
- claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
- claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
- claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
- claude_mpm/services/shared_prompt_cache.py +819 -0
- claude_mpm/services/ticket_manager.py +213 -0
- claude_mpm/services/ticket_manager_di.py +318 -0
- claude_mpm/services/ticketing_service_original.py +508 -0
- claude_mpm/services/version_control/VERSION +1 -0
- claude_mpm/services/version_control/__init__.py +70 -0
- claude_mpm/services/version_control/branch_strategy.py +670 -0
- claude_mpm/services/version_control/conflict_resolution.py +744 -0
- claude_mpm/services/version_control/git_operations.py +784 -0
- claude_mpm/services/version_control/semantic_versioning.py +703 -0
- claude_mpm/ui/__init__.py +1 -0
- claude_mpm/ui/rich_terminal_ui.py +295 -0
- claude_mpm/ui/terminal_ui.py +328 -0
- claude_mpm/utils/__init__.py +16 -0
- claude_mpm/utils/config_manager.py +468 -0
- claude_mpm/utils/import_migration_example.py +80 -0
- claude_mpm/utils/imports.py +182 -0
- claude_mpm/utils/path_operations.py +357 -0
- claude_mpm/utils/paths.py +289 -0
- claude_mpm-0.3.0.dist-info/METADATA +290 -0
- claude_mpm-0.3.0.dist-info/RECORD +159 -0
- claude_mpm-0.3.0.dist-info/WHEEL +5 -0
- claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
- 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)
|