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,627 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Consolidated Agent Registry for Claude MPM.
|
|
3
|
+
|
|
4
|
+
This module combines functionality from:
|
|
5
|
+
- agent_registry.py (current working implementation)
|
|
6
|
+
- agent_registry_original.py (legacy convenience functions)
|
|
7
|
+
|
|
8
|
+
Provides:
|
|
9
|
+
- Agent discovery from the framework
|
|
10
|
+
- Agent listing and selection
|
|
11
|
+
- Compatibility with both sync and async interfaces
|
|
12
|
+
- Legacy function names for backwards compatibility
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Optional, Dict, Any, List, Set
|
|
19
|
+
import importlib.util
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from ..core.logger import get_logger
|
|
25
|
+
except ImportError:
|
|
26
|
+
from core.logger import get_logger
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class AgentMetadata:
|
|
31
|
+
"""Metadata for an agent."""
|
|
32
|
+
name: str
|
|
33
|
+
type: str
|
|
34
|
+
path: str
|
|
35
|
+
tier: str = "system"
|
|
36
|
+
last_modified: float = 0.0
|
|
37
|
+
specializations: List[str] = None
|
|
38
|
+
description: str = ""
|
|
39
|
+
|
|
40
|
+
def __post_init__(self):
|
|
41
|
+
if self.specializations is None:
|
|
42
|
+
self.specializations = []
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SimpleAgentRegistry:
|
|
46
|
+
"""Simple agent registry implementation."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, framework_path: Path):
|
|
49
|
+
self.framework_path = framework_path
|
|
50
|
+
self.agents = {}
|
|
51
|
+
self._discover_agents()
|
|
52
|
+
|
|
53
|
+
def _discover_agents(self):
|
|
54
|
+
"""Discover agents from the framework."""
|
|
55
|
+
# Check multiple possible locations
|
|
56
|
+
agent_locations = [
|
|
57
|
+
self.framework_path / "src" / "claude_mpm" / "agents" / "templates",
|
|
58
|
+
self.framework_path / "src" / "claude_mpm" / "agents",
|
|
59
|
+
self.framework_path / "agents",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
for agents_dir in agent_locations:
|
|
63
|
+
if agents_dir.exists():
|
|
64
|
+
# Look for both .md and .json files
|
|
65
|
+
for pattern in ["*.md", "*.json"]:
|
|
66
|
+
for agent_file in agents_dir.glob(pattern):
|
|
67
|
+
agent_id = agent_file.stem
|
|
68
|
+
self.agents[agent_id] = {
|
|
69
|
+
'name': agent_id,
|
|
70
|
+
'type': agent_id,
|
|
71
|
+
'path': str(agent_file),
|
|
72
|
+
'last_modified': agent_file.stat().st_mtime,
|
|
73
|
+
'tier': self._determine_tier(agent_file),
|
|
74
|
+
'specializations': self._extract_specializations(agent_id),
|
|
75
|
+
'description': self._extract_description(agent_id)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
def _determine_tier(self, agent_path: Path) -> str:
|
|
79
|
+
"""Determine agent tier based on path."""
|
|
80
|
+
path_str = str(agent_path)
|
|
81
|
+
if 'project' in path_str or '.claude-mpm' in path_str:
|
|
82
|
+
return 'project'
|
|
83
|
+
elif 'user' in path_str or str(Path.home()) in path_str:
|
|
84
|
+
return 'user'
|
|
85
|
+
else:
|
|
86
|
+
return 'system'
|
|
87
|
+
|
|
88
|
+
def _extract_specializations(self, agent_id: str) -> List[str]:
|
|
89
|
+
"""Extract specializations based on agent type."""
|
|
90
|
+
specialization_map = {
|
|
91
|
+
'engineer': ['coding', 'architecture', 'implementation'],
|
|
92
|
+
'documentation': ['docs', 'api', 'guides'],
|
|
93
|
+
'qa': ['testing', 'quality', 'validation'],
|
|
94
|
+
'research': ['analysis', 'investigation', 'exploration'],
|
|
95
|
+
'ops': ['deployment', 'monitoring', 'infrastructure'],
|
|
96
|
+
'security': ['security', 'audit', 'compliance'],
|
|
97
|
+
'version_control': ['git', 'versioning', 'releases'],
|
|
98
|
+
'data_engineer': ['data', 'etl', 'analytics']
|
|
99
|
+
}
|
|
100
|
+
return specialization_map.get(agent_id, [])
|
|
101
|
+
|
|
102
|
+
def _extract_description(self, agent_id: str) -> str:
|
|
103
|
+
"""Extract description for agent."""
|
|
104
|
+
descriptions = {
|
|
105
|
+
'engineer': 'Software engineering and implementation',
|
|
106
|
+
'documentation': 'Documentation creation and maintenance',
|
|
107
|
+
'qa': 'Quality assurance and testing',
|
|
108
|
+
'research': 'Research and investigation',
|
|
109
|
+
'ops': 'Operations and deployment',
|
|
110
|
+
'security': 'Security analysis and compliance',
|
|
111
|
+
'version_control': 'Version control and release management',
|
|
112
|
+
'data_engineer': 'Data engineering and analytics'
|
|
113
|
+
}
|
|
114
|
+
return descriptions.get(agent_id, f'{agent_id.title()} agent')
|
|
115
|
+
|
|
116
|
+
def listAgents(self, **kwargs) -> Dict[str, Any]:
|
|
117
|
+
"""List all agents (camelCase for compatibility)."""
|
|
118
|
+
return self.agents
|
|
119
|
+
|
|
120
|
+
def list_agents(self, agent_type: Optional[str] = None, tier: Optional[str] = None) -> List[AgentMetadata]:
|
|
121
|
+
"""List agents with optional filtering."""
|
|
122
|
+
results = []
|
|
123
|
+
for agent_id, metadata in self.agents.items():
|
|
124
|
+
if agent_type and metadata.get('type') != agent_type:
|
|
125
|
+
continue
|
|
126
|
+
if tier and metadata.get('tier') != tier:
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
results.append(AgentMetadata(
|
|
130
|
+
name=metadata['name'],
|
|
131
|
+
type=metadata['type'],
|
|
132
|
+
path=metadata['path'],
|
|
133
|
+
tier=metadata.get('tier', 'system'),
|
|
134
|
+
last_modified=metadata.get('last_modified', 0),
|
|
135
|
+
specializations=metadata.get('specializations', []),
|
|
136
|
+
description=metadata.get('description', '')
|
|
137
|
+
))
|
|
138
|
+
return results
|
|
139
|
+
|
|
140
|
+
def get_agent(self, agent_name: str) -> Optional[AgentMetadata]:
|
|
141
|
+
"""Get a specific agent."""
|
|
142
|
+
metadata = self.agents.get(agent_name)
|
|
143
|
+
if metadata:
|
|
144
|
+
return AgentMetadata(
|
|
145
|
+
name=metadata['name'],
|
|
146
|
+
type=metadata['type'],
|
|
147
|
+
path=metadata['path'],
|
|
148
|
+
tier=metadata.get('tier', 'system'),
|
|
149
|
+
last_modified=metadata.get('last_modified', 0),
|
|
150
|
+
specializations=metadata.get('specializations', []),
|
|
151
|
+
description=metadata.get('description', '')
|
|
152
|
+
)
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
def discover_agents(self, force_refresh: bool = False) -> Dict[str, AgentMetadata]:
|
|
156
|
+
"""Discover agents (optionally refresh)."""
|
|
157
|
+
if force_refresh:
|
|
158
|
+
self.agents.clear()
|
|
159
|
+
self._discover_agents()
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
agent_id: AgentMetadata(
|
|
163
|
+
name=metadata['name'],
|
|
164
|
+
type=metadata['type'],
|
|
165
|
+
path=metadata['path'],
|
|
166
|
+
tier=metadata.get('tier', 'system'),
|
|
167
|
+
last_modified=metadata.get('last_modified', 0),
|
|
168
|
+
specializations=metadata.get('specializations', []),
|
|
169
|
+
description=metadata.get('description', '')
|
|
170
|
+
)
|
|
171
|
+
for agent_id, metadata in self.agents.items()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def core_agent_types(self) -> Set[str]:
|
|
176
|
+
"""Get core agent types."""
|
|
177
|
+
return {
|
|
178
|
+
'documentation',
|
|
179
|
+
'engineer',
|
|
180
|
+
'qa',
|
|
181
|
+
'research',
|
|
182
|
+
'ops',
|
|
183
|
+
'security',
|
|
184
|
+
'version_control',
|
|
185
|
+
'data_engineer'
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def specialized_agent_types(self) -> Set[str]:
|
|
190
|
+
"""Get specialized agent types beyond core."""
|
|
191
|
+
all_types = set(metadata['type'] for metadata in self.agents.values())
|
|
192
|
+
return all_types - self.core_agent_types
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class AgentRegistryAdapter:
|
|
196
|
+
"""
|
|
197
|
+
Adapter to integrate agent registry functionality.
|
|
198
|
+
|
|
199
|
+
This adapter:
|
|
200
|
+
1. Locates the claude-mpm installation
|
|
201
|
+
2. Provides a clean interface for agent operations
|
|
202
|
+
3. Maintains backwards compatibility
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
def __init__(self, framework_path: Optional[Path] = None):
|
|
206
|
+
"""
|
|
207
|
+
Initialize the agent registry adapter.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
framework_path: Path to claude-mpm (auto-detected if None)
|
|
211
|
+
"""
|
|
212
|
+
self.logger = get_logger("agent_registry")
|
|
213
|
+
self.framework_path = framework_path or self._find_framework()
|
|
214
|
+
self.registry = None
|
|
215
|
+
self._initialize_registry()
|
|
216
|
+
|
|
217
|
+
def _find_framework(self) -> Optional[Path]:
|
|
218
|
+
"""Find claude-mpm installation.
|
|
219
|
+
|
|
220
|
+
Search order:
|
|
221
|
+
1. CLAUDE_MPM_PATH environment variable
|
|
222
|
+
2. Current working directory (if it's claude-mpm)
|
|
223
|
+
3. Walk up from current file location
|
|
224
|
+
4. Common development locations
|
|
225
|
+
"""
|
|
226
|
+
# Check environment variable first
|
|
227
|
+
env_path = os.environ.get("CLAUDE_MPM_PATH")
|
|
228
|
+
if env_path:
|
|
229
|
+
candidate = Path(env_path)
|
|
230
|
+
if self._is_valid_framework_path(candidate):
|
|
231
|
+
self.logger.info(f"Using claude-mpm from CLAUDE_MPM_PATH: {candidate}")
|
|
232
|
+
return candidate
|
|
233
|
+
else:
|
|
234
|
+
self.logger.warning(f"CLAUDE_MPM_PATH is set but invalid: {env_path}")
|
|
235
|
+
|
|
236
|
+
# Check current working directory
|
|
237
|
+
cwd = Path.cwd()
|
|
238
|
+
if self._is_valid_framework_path(cwd):
|
|
239
|
+
return cwd
|
|
240
|
+
|
|
241
|
+
# Check if we're running from within the installed package
|
|
242
|
+
current_file = Path(__file__).resolve()
|
|
243
|
+
for parent in current_file.parents:
|
|
244
|
+
if self._is_valid_framework_path(parent):
|
|
245
|
+
return parent
|
|
246
|
+
# Stop at site-packages or similar
|
|
247
|
+
if parent.name in ("site-packages", "dist-packages", "lib"):
|
|
248
|
+
break
|
|
249
|
+
|
|
250
|
+
# Check common development locations
|
|
251
|
+
candidates = [
|
|
252
|
+
Path.home() / "Projects" / "claude-mpm",
|
|
253
|
+
Path.home() / "claude-mpm",
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
for candidate in candidates:
|
|
257
|
+
if self._is_valid_framework_path(candidate):
|
|
258
|
+
self.logger.info(f"Found claude-mpm at: {candidate}")
|
|
259
|
+
return candidate
|
|
260
|
+
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
def _is_valid_framework_path(self, path: Path) -> bool:
|
|
264
|
+
"""Check if a path is a valid claude-mpm installation."""
|
|
265
|
+
return (
|
|
266
|
+
path.exists() and
|
|
267
|
+
(path / "src" / "claude_mpm").exists()
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def _initialize_registry(self):
|
|
271
|
+
"""Initialize the agent registry."""
|
|
272
|
+
if not self.framework_path:
|
|
273
|
+
self.logger.warning("No framework path, registry unavailable")
|
|
274
|
+
return
|
|
275
|
+
|
|
276
|
+
try:
|
|
277
|
+
self.registry = SimpleAgentRegistry(self.framework_path)
|
|
278
|
+
self.logger.info("Agent registry initialized successfully")
|
|
279
|
+
|
|
280
|
+
except Exception as e:
|
|
281
|
+
self.logger.error(f"Failed to initialize registry: {e}")
|
|
282
|
+
|
|
283
|
+
def list_agents(self, **kwargs) -> Dict[str, Any]:
|
|
284
|
+
"""
|
|
285
|
+
List available agents.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
**kwargs: Arguments to pass to registry
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
Dictionary of agents with metadata
|
|
292
|
+
"""
|
|
293
|
+
if not self.registry:
|
|
294
|
+
return {}
|
|
295
|
+
|
|
296
|
+
try:
|
|
297
|
+
return self.registry.listAgents(**kwargs)
|
|
298
|
+
except Exception as e:
|
|
299
|
+
self.logger.error(f"Error listing agents: {e}")
|
|
300
|
+
return {}
|
|
301
|
+
|
|
302
|
+
def get_agent_definition(self, agent_name: str) -> Optional[str]:
|
|
303
|
+
"""
|
|
304
|
+
Get agent definition by name.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
agent_name: Name of the agent
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
Agent definition content or None
|
|
311
|
+
"""
|
|
312
|
+
if not self.registry:
|
|
313
|
+
return None
|
|
314
|
+
|
|
315
|
+
try:
|
|
316
|
+
# Try to load agent definition
|
|
317
|
+
agents = self.registry.listAgents()
|
|
318
|
+
for agent_id, metadata in agents.items():
|
|
319
|
+
if agent_name in agent_id or agent_name == metadata.get('type'):
|
|
320
|
+
# Load the agent file
|
|
321
|
+
agent_path = Path(metadata['path'])
|
|
322
|
+
if agent_path.exists():
|
|
323
|
+
return agent_path.read_text()
|
|
324
|
+
|
|
325
|
+
return None
|
|
326
|
+
|
|
327
|
+
except Exception as e:
|
|
328
|
+
self.logger.error(f"Error getting agent definition: {e}")
|
|
329
|
+
return None
|
|
330
|
+
|
|
331
|
+
def select_agent_for_task(self, task_description: str, required_specializations: Optional[List[str]] = None) -> Optional[Dict[str, Any]]:
|
|
332
|
+
"""
|
|
333
|
+
Select optimal agent for a task.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
task_description: Description of the task
|
|
337
|
+
required_specializations: Required agent specializations
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
Agent metadata or None
|
|
341
|
+
"""
|
|
342
|
+
if not self.registry:
|
|
343
|
+
return None
|
|
344
|
+
|
|
345
|
+
try:
|
|
346
|
+
# Get agents with required specializations
|
|
347
|
+
agents = self.registry.listAgents()
|
|
348
|
+
|
|
349
|
+
if required_specializations:
|
|
350
|
+
# Filter by specializations
|
|
351
|
+
filtered = {}
|
|
352
|
+
for agent_id, metadata in agents.items():
|
|
353
|
+
agent_specs = set(metadata.get('specializations', []))
|
|
354
|
+
if any(spec in agent_specs for spec in required_specializations):
|
|
355
|
+
filtered[agent_id] = metadata
|
|
356
|
+
agents = filtered
|
|
357
|
+
|
|
358
|
+
if not agents:
|
|
359
|
+
return None
|
|
360
|
+
|
|
361
|
+
# For now, return the first matching agent
|
|
362
|
+
# In future, could implement more sophisticated selection
|
|
363
|
+
agent_id = next(iter(agents))
|
|
364
|
+
return {
|
|
365
|
+
'id': agent_id,
|
|
366
|
+
'metadata': agents[agent_id]
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
except Exception as e:
|
|
370
|
+
self.logger.error(f"Error selecting agent: {e}")
|
|
371
|
+
return None
|
|
372
|
+
|
|
373
|
+
def get_agent_hierarchy(self) -> Dict[str, List[str]]:
|
|
374
|
+
"""
|
|
375
|
+
Get agent hierarchy (project → user → system).
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
Dictionary with hierarchy levels and agent names
|
|
379
|
+
"""
|
|
380
|
+
if not self.registry:
|
|
381
|
+
return {
|
|
382
|
+
'project': [],
|
|
383
|
+
'user': [],
|
|
384
|
+
'system': []
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
try:
|
|
388
|
+
# Get all agents
|
|
389
|
+
all_agents = self.registry.listAgents()
|
|
390
|
+
|
|
391
|
+
hierarchy = {
|
|
392
|
+
'project': [],
|
|
393
|
+
'user': [],
|
|
394
|
+
'system': []
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
# Categorize by tier
|
|
398
|
+
for agent_id, metadata in all_agents.items():
|
|
399
|
+
tier = metadata.get('tier', 'system')
|
|
400
|
+
hierarchy[tier].append(agent_id)
|
|
401
|
+
|
|
402
|
+
return hierarchy
|
|
403
|
+
|
|
404
|
+
except Exception as e:
|
|
405
|
+
self.logger.error(f"Error getting hierarchy: {e}")
|
|
406
|
+
return {'project': [], 'user': [], 'system': []}
|
|
407
|
+
|
|
408
|
+
def get_core_agents(self) -> List[str]:
|
|
409
|
+
"""
|
|
410
|
+
Get list of core system agents.
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
List of core agent names
|
|
414
|
+
"""
|
|
415
|
+
return [
|
|
416
|
+
'documentation',
|
|
417
|
+
'engineer',
|
|
418
|
+
'qa',
|
|
419
|
+
'research',
|
|
420
|
+
'ops',
|
|
421
|
+
'security',
|
|
422
|
+
'version_control',
|
|
423
|
+
'data_engineer'
|
|
424
|
+
]
|
|
425
|
+
|
|
426
|
+
def format_agent_for_task_tool(self, agent_name: str, task: str, context: str = "") -> str:
|
|
427
|
+
"""
|
|
428
|
+
Format agent delegation for Task Tool.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
agent_name: Name of the agent
|
|
432
|
+
task: Task description
|
|
433
|
+
context: Additional context
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
Formatted Task Tool prompt
|
|
437
|
+
"""
|
|
438
|
+
# Map agent names to nicknames
|
|
439
|
+
nicknames = {
|
|
440
|
+
'documentation': 'Documenter',
|
|
441
|
+
'engineer': 'Engineer',
|
|
442
|
+
'qa': 'QA',
|
|
443
|
+
'research': 'Researcher',
|
|
444
|
+
'ops': 'Ops',
|
|
445
|
+
'security': 'Security',
|
|
446
|
+
'version_control': 'Versioner',
|
|
447
|
+
'data_engineer': 'Data Engineer'
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
nickname = nicknames.get(agent_name, agent_name.title())
|
|
451
|
+
|
|
452
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
453
|
+
|
|
454
|
+
return f"""**{nickname}**: {task}
|
|
455
|
+
|
|
456
|
+
TEMPORAL CONTEXT: Today is {today}. Apply date awareness to task execution.
|
|
457
|
+
|
|
458
|
+
**Task**: {task}
|
|
459
|
+
|
|
460
|
+
**Context**: {context}
|
|
461
|
+
|
|
462
|
+
**Authority**: Agent has full authority for {agent_name} operations
|
|
463
|
+
**Expected Results**: Completed task with operational insights"""
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
# Export main class as AgentRegistry for compatibility
|
|
467
|
+
AgentRegistry = SimpleAgentRegistry
|
|
468
|
+
|
|
469
|
+
# Convenience functions for backwards compatibility
|
|
470
|
+
def create_agent_registry(cache_service: Any = None, framework_path: Optional[Path] = None) -> AgentRegistry:
|
|
471
|
+
"""
|
|
472
|
+
Create a new AgentRegistry instance
|
|
473
|
+
|
|
474
|
+
Args:
|
|
475
|
+
cache_service: Ignored for compatibility
|
|
476
|
+
framework_path: Path to framework (auto-detected if None)
|
|
477
|
+
|
|
478
|
+
Returns:
|
|
479
|
+
AgentRegistry instance
|
|
480
|
+
"""
|
|
481
|
+
if not framework_path:
|
|
482
|
+
adapter = AgentRegistryAdapter()
|
|
483
|
+
framework_path = adapter.framework_path
|
|
484
|
+
|
|
485
|
+
if framework_path:
|
|
486
|
+
return AgentRegistry(framework_path)
|
|
487
|
+
else:
|
|
488
|
+
raise ValueError("Could not find claude-mpm framework path")
|
|
489
|
+
|
|
490
|
+
def discover_agents(force_refresh: bool = False) -> Dict[str, AgentMetadata]:
|
|
491
|
+
"""
|
|
492
|
+
Convenience function for synchronous agent discovery
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
force_refresh: Force cache refresh
|
|
496
|
+
|
|
497
|
+
Returns:
|
|
498
|
+
Dictionary of discovered agents
|
|
499
|
+
"""
|
|
500
|
+
adapter = AgentRegistryAdapter()
|
|
501
|
+
if adapter.registry:
|
|
502
|
+
return adapter.registry.discover_agents(force_refresh=force_refresh)
|
|
503
|
+
return {}
|
|
504
|
+
|
|
505
|
+
def get_core_agent_types() -> Set[str]:
|
|
506
|
+
"""
|
|
507
|
+
Get the set of core agent types
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
Set of core agent type names
|
|
511
|
+
"""
|
|
512
|
+
adapter = AgentRegistryAdapter()
|
|
513
|
+
if adapter.registry:
|
|
514
|
+
return adapter.registry.core_agent_types
|
|
515
|
+
return set()
|
|
516
|
+
|
|
517
|
+
def get_specialized_agent_types() -> Set[str]:
|
|
518
|
+
"""
|
|
519
|
+
Get the set of specialized agent types beyond core 9
|
|
520
|
+
|
|
521
|
+
Returns:
|
|
522
|
+
Set of specialized agent type names
|
|
523
|
+
"""
|
|
524
|
+
adapter = AgentRegistryAdapter()
|
|
525
|
+
if adapter.registry:
|
|
526
|
+
return adapter.registry.specialized_agent_types
|
|
527
|
+
return set()
|
|
528
|
+
|
|
529
|
+
def listAgents() -> Dict[str, Dict[str, Any]]:
|
|
530
|
+
"""
|
|
531
|
+
Synchronous function for listing all agents (camelCase compatibility)
|
|
532
|
+
|
|
533
|
+
Returns:
|
|
534
|
+
Dictionary of agent name -> agent metadata
|
|
535
|
+
"""
|
|
536
|
+
adapter = AgentRegistryAdapter()
|
|
537
|
+
if adapter.registry:
|
|
538
|
+
return adapter.registry.listAgents()
|
|
539
|
+
return {}
|
|
540
|
+
|
|
541
|
+
def list_agents(agent_type: Optional[str] = None, tier: Optional[str] = None) -> List[AgentMetadata]:
|
|
542
|
+
"""
|
|
543
|
+
Synchronous function to list agents with optional filtering
|
|
544
|
+
|
|
545
|
+
Args:
|
|
546
|
+
agent_type: Filter by agent type
|
|
547
|
+
tier: Filter by hierarchy tier
|
|
548
|
+
|
|
549
|
+
Returns:
|
|
550
|
+
List of agent metadata dictionaries
|
|
551
|
+
"""
|
|
552
|
+
adapter = AgentRegistryAdapter()
|
|
553
|
+
if adapter.registry:
|
|
554
|
+
return adapter.registry.list_agents(agent_type=agent_type, tier=tier)
|
|
555
|
+
return []
|
|
556
|
+
|
|
557
|
+
def discover_agents_sync(force_refresh: bool = False) -> Dict[str, AgentMetadata]:
|
|
558
|
+
"""
|
|
559
|
+
Synchronous function for agent discovery
|
|
560
|
+
|
|
561
|
+
Args:
|
|
562
|
+
force_refresh: Force cache refresh
|
|
563
|
+
|
|
564
|
+
Returns:
|
|
565
|
+
Dictionary of discovered agents
|
|
566
|
+
"""
|
|
567
|
+
return discover_agents(force_refresh)
|
|
568
|
+
|
|
569
|
+
def get_agent(agent_name: str) -> Optional[Dict[str, Any]]:
|
|
570
|
+
"""
|
|
571
|
+
Synchronous function to get a specific agent
|
|
572
|
+
|
|
573
|
+
Args:
|
|
574
|
+
agent_name: Name of agent to retrieve
|
|
575
|
+
|
|
576
|
+
Returns:
|
|
577
|
+
Agent metadata or None
|
|
578
|
+
"""
|
|
579
|
+
adapter = AgentRegistryAdapter()
|
|
580
|
+
if adapter.registry:
|
|
581
|
+
agent = adapter.registry.get_agent(agent_name)
|
|
582
|
+
if agent:
|
|
583
|
+
return {
|
|
584
|
+
'name': agent.name,
|
|
585
|
+
'type': agent.type,
|
|
586
|
+
'path': agent.path,
|
|
587
|
+
'tier': agent.tier,
|
|
588
|
+
'last_modified': agent.last_modified,
|
|
589
|
+
'specializations': agent.specializations,
|
|
590
|
+
'description': agent.description
|
|
591
|
+
}
|
|
592
|
+
return None
|
|
593
|
+
|
|
594
|
+
def get_registry_stats() -> Dict[str, Any]:
|
|
595
|
+
"""
|
|
596
|
+
Synchronous function to get registry statistics
|
|
597
|
+
|
|
598
|
+
Returns:
|
|
599
|
+
Dictionary of registry statistics
|
|
600
|
+
"""
|
|
601
|
+
adapter = AgentRegistryAdapter()
|
|
602
|
+
if adapter.registry:
|
|
603
|
+
agents = adapter.registry.list_agents()
|
|
604
|
+
return {
|
|
605
|
+
'total_agents': len(agents),
|
|
606
|
+
'agent_types': len(set(a.type for a in agents)),
|
|
607
|
+
'tiers': list(set(a.tier for a in agents))
|
|
608
|
+
}
|
|
609
|
+
return {'total_agents': 0, 'agent_types': 0, 'tiers': []}
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
# Export all public symbols
|
|
613
|
+
__all__ = [
|
|
614
|
+
'AgentRegistry',
|
|
615
|
+
'AgentRegistryAdapter',
|
|
616
|
+
'AgentMetadata',
|
|
617
|
+
'SimpleAgentRegistry',
|
|
618
|
+
'create_agent_registry',
|
|
619
|
+
'discover_agents',
|
|
620
|
+
'get_core_agent_types',
|
|
621
|
+
'get_specialized_agent_types',
|
|
622
|
+
'listAgents',
|
|
623
|
+
'list_agents',
|
|
624
|
+
'discover_agents_sync',
|
|
625
|
+
'get_agent',
|
|
626
|
+
'get_registry_stats'
|
|
627
|
+
]
|