claude-mpm 3.4.27__py3-none-any.whl → 3.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/INSTRUCTIONS.md +182 -299
- claude_mpm/agents/agent_loader.py +283 -57
- claude_mpm/agents/agent_loader_integration.py +6 -9
- claude_mpm/agents/base_agent.json +2 -1
- claude_mpm/agents/base_agent_loader.py +1 -1
- claude_mpm/cli/__init__.py +5 -7
- claude_mpm/cli/commands/__init__.py +0 -2
- claude_mpm/cli/commands/agents.py +1 -1
- claude_mpm/cli/commands/memory.py +1 -1
- claude_mpm/cli/commands/run.py +12 -0
- claude_mpm/cli/parser.py +0 -13
- claude_mpm/cli/utils.py +1 -1
- claude_mpm/config/__init__.py +44 -2
- claude_mpm/config/agent_config.py +348 -0
- claude_mpm/config/paths.py +322 -0
- claude_mpm/constants.py +0 -1
- claude_mpm/core/__init__.py +2 -5
- claude_mpm/core/agent_registry.py +63 -17
- claude_mpm/core/claude_runner.py +354 -43
- claude_mpm/core/config.py +7 -1
- claude_mpm/core/config_aliases.py +4 -3
- claude_mpm/core/config_paths.py +151 -0
- claude_mpm/core/factories.py +4 -50
- claude_mpm/core/logger.py +11 -13
- claude_mpm/core/service_registry.py +2 -2
- claude_mpm/dashboard/static/js/components/agent-inference.js +101 -25
- claude_mpm/dashboard/static/js/components/event-processor.js +3 -2
- claude_mpm/hooks/claude_hooks/hook_handler.py +343 -83
- claude_mpm/hooks/memory_integration_hook.py +1 -1
- claude_mpm/init.py +37 -6
- claude_mpm/scripts/socketio_daemon.py +6 -2
- claude_mpm/services/__init__.py +71 -3
- claude_mpm/services/agents/__init__.py +85 -0
- claude_mpm/services/agents/deployment/__init__.py +21 -0
- claude_mpm/services/{agent_deployment.py → agents/deployment/agent_deployment.py} +192 -41
- claude_mpm/services/{agent_lifecycle_manager.py → agents/deployment/agent_lifecycle_manager.py} +11 -10
- claude_mpm/services/agents/loading/__init__.py +11 -0
- claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +9 -8
- claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +2 -2
- claude_mpm/services/{framework_agent_loader.py → agents/loading/framework_agent_loader.py} +116 -40
- claude_mpm/services/agents/management/__init__.py +9 -0
- claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +6 -5
- claude_mpm/services/agents/memory/__init__.py +21 -0
- claude_mpm/services/{agent_memory_manager.py → agents/memory/agent_memory_manager.py} +3 -3
- claude_mpm/services/agents/registry/__init__.py +29 -0
- claude_mpm/services/{agent_registry.py → agents/registry/agent_registry.py} +101 -16
- claude_mpm/services/{deployed_agent_discovery.py → agents/registry/deployed_agent_discovery.py} +12 -2
- claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +6 -5
- claude_mpm/services/async_session_logger.py +584 -0
- claude_mpm/services/claude_session_logger.py +299 -0
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +2 -2
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +17 -17
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +3 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +1 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +1 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +19 -24
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +1 -1
- claude_mpm/services/framework_claude_md_generator.py +4 -2
- claude_mpm/services/memory/__init__.py +17 -0
- claude_mpm/services/{memory_builder.py → memory/builder.py} +3 -3
- claude_mpm/services/memory/cache/__init__.py +14 -0
- claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +1 -1
- claude_mpm/services/memory/cache/simple_cache.py +317 -0
- claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +1 -1
- claude_mpm/services/{memory_router.py → memory/router.py} +1 -1
- claude_mpm/services/optimized_hook_service.py +542 -0
- claude_mpm/services/project_registry.py +14 -8
- claude_mpm/services/response_tracker.py +237 -0
- claude_mpm/services/ticketing_service_original.py +4 -2
- claude_mpm/services/version_control/branch_strategy.py +3 -1
- claude_mpm/utils/paths.py +12 -10
- claude_mpm/utils/session_logging.py +114 -0
- claude_mpm/validation/agent_validator.py +2 -1
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/METADATA +26 -20
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/RECORD +83 -106
- claude_mpm/cli/commands/ui.py +0 -57
- claude_mpm/core/simple_runner.py +0 -1046
- claude_mpm/hooks/builtin/__init__.py +0 -1
- claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
- claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
- claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
- claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
- claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
- claude_mpm/orchestration/__init__.py +0 -6
- claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
- claude_mpm/orchestration/archive/factory.py +0 -215
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
- claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
- claude_mpm/orchestration/archive/orchestrator.py +0 -501
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
- claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
- claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
- claude_mpm/schemas/workflow_validator.py +0 -411
- claude_mpm/services/parent_directory_manager/__init__.py +0 -577
- claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
- claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
- claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
- claude_mpm/services/parent_directory_manager/operations.py +0 -186
- claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
- claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
- claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
- claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
- claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
- claude_mpm/ui/__init__.py +0 -1
- claude_mpm/ui/rich_terminal_ui.py +0 -295
- claude_mpm/ui/terminal_ui.py +0 -328
- /claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +0 -0
- /claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +0 -0
- /claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/WHEEL +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Centralized path management for claude-mpm.
|
|
3
|
+
|
|
4
|
+
This module provides a consistent, reliable way to access project paths
|
|
5
|
+
without fragile parent.parent.parent patterns.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional, Union
|
|
12
|
+
from functools import cached_property
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ClaudeMPMPaths:
|
|
19
|
+
"""
|
|
20
|
+
Centralized path management for the claude-mpm project.
|
|
21
|
+
|
|
22
|
+
This class provides a singleton instance with properties for all common paths
|
|
23
|
+
in the project, eliminating the need for fragile Path(__file__).parent.parent patterns.
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
from claude_mpm.config.paths import paths
|
|
27
|
+
|
|
28
|
+
# Access common paths
|
|
29
|
+
project_root = paths.project_root
|
|
30
|
+
agents_dir = paths.agents_dir
|
|
31
|
+
config_file = paths.config_dir / "some_config.yaml"
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
_instance: Optional['ClaudeMPMPaths'] = None
|
|
35
|
+
_project_root: Optional[Path] = None
|
|
36
|
+
|
|
37
|
+
def __new__(cls) -> 'ClaudeMPMPaths':
|
|
38
|
+
"""Singleton pattern to ensure single instance."""
|
|
39
|
+
if cls._instance is None:
|
|
40
|
+
cls._instance = super().__new__(cls)
|
|
41
|
+
return cls._instance
|
|
42
|
+
|
|
43
|
+
def __init__(self):
|
|
44
|
+
"""Initialize paths if not already done."""
|
|
45
|
+
if self._project_root is None:
|
|
46
|
+
self._detect_project_root()
|
|
47
|
+
|
|
48
|
+
def _detect_project_root(self) -> None:
|
|
49
|
+
"""
|
|
50
|
+
Detect the project root directory.
|
|
51
|
+
|
|
52
|
+
Strategy:
|
|
53
|
+
1. Look for definitive project markers (pyproject.toml, setup.py)
|
|
54
|
+
2. Look for combination of markers to ensure we're at the right level
|
|
55
|
+
3. Walk up from current file location
|
|
56
|
+
"""
|
|
57
|
+
# Start from this file's location
|
|
58
|
+
current = Path(__file__).resolve()
|
|
59
|
+
|
|
60
|
+
# Walk up the directory tree
|
|
61
|
+
for parent in current.parents:
|
|
62
|
+
# Check for definitive project root indicators
|
|
63
|
+
# Prioritize pyproject.toml and setup.py as they're only at root
|
|
64
|
+
if (parent / 'pyproject.toml').exists() or (parent / 'setup.py').exists():
|
|
65
|
+
self._project_root = parent
|
|
66
|
+
logger.debug(f"Project root detected at: {parent} (found pyproject.toml or setup.py)")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
# Secondary check: .git directory + VERSION file together
|
|
70
|
+
# This combination is more likely to be the real project root
|
|
71
|
+
if (parent / '.git').exists() and (parent / 'VERSION').exists():
|
|
72
|
+
self._project_root = parent
|
|
73
|
+
logger.debug(f"Project root detected at: {parent} (found .git and VERSION)")
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
# Fallback: walk up to find claude-mpm directory name
|
|
77
|
+
for parent in current.parents:
|
|
78
|
+
if parent.name == 'claude-mpm':
|
|
79
|
+
self._project_root = parent
|
|
80
|
+
logger.debug(f"Project root detected at: {parent} (by directory name)")
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
# Last resort fallback: 3 levels up from this file
|
|
84
|
+
# paths.py is in src/claude_mpm/config/
|
|
85
|
+
self._project_root = current.parent.parent.parent
|
|
86
|
+
logger.warning(f"Project root fallback to: {self._project_root}")
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def project_root(self) -> Path:
|
|
90
|
+
"""Get the project root directory."""
|
|
91
|
+
if self._project_root is None:
|
|
92
|
+
self._detect_project_root()
|
|
93
|
+
return self._project_root
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def src_dir(self) -> Path:
|
|
97
|
+
"""Get the src directory."""
|
|
98
|
+
return self.project_root / "src"
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def claude_mpm_dir(self) -> Path:
|
|
102
|
+
"""Get the main claude_mpm package directory."""
|
|
103
|
+
return self.src_dir / "claude_mpm"
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def agents_dir(self) -> Path:
|
|
107
|
+
"""Get the agents directory."""
|
|
108
|
+
return self.claude_mpm_dir / "agents"
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def services_dir(self) -> Path:
|
|
112
|
+
"""Get the services directory."""
|
|
113
|
+
return self.claude_mpm_dir / "services"
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def hooks_dir(self) -> Path:
|
|
117
|
+
"""Get the hooks directory."""
|
|
118
|
+
return self.claude_mpm_dir / "hooks"
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def config_dir(self) -> Path:
|
|
122
|
+
"""Get the config directory."""
|
|
123
|
+
return self.claude_mpm_dir / "config"
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def cli_dir(self) -> Path:
|
|
127
|
+
"""Get the CLI directory."""
|
|
128
|
+
return self.claude_mpm_dir / "cli"
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def core_dir(self) -> Path:
|
|
132
|
+
"""Get the core directory."""
|
|
133
|
+
return self.claude_mpm_dir / "core"
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def schemas_dir(self) -> Path:
|
|
137
|
+
"""Get the schemas directory."""
|
|
138
|
+
return self.claude_mpm_dir / "schemas"
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def scripts_dir(self) -> Path:
|
|
142
|
+
"""Get the scripts directory."""
|
|
143
|
+
return self.project_root / "scripts"
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def tests_dir(self) -> Path:
|
|
147
|
+
"""Get the tests directory."""
|
|
148
|
+
return self.project_root / "tests"
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def docs_dir(self) -> Path:
|
|
152
|
+
"""Get the documentation directory."""
|
|
153
|
+
return self.project_root / "docs"
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def logs_dir(self) -> Path:
|
|
157
|
+
"""Get the logs directory (creates if doesn't exist)."""
|
|
158
|
+
logs = self.project_root / "logs"
|
|
159
|
+
logs.mkdir(exist_ok=True)
|
|
160
|
+
return logs
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def temp_dir(self) -> Path:
|
|
164
|
+
"""Get the temporary files directory (creates if doesn't exist)."""
|
|
165
|
+
temp = self.project_root / ".tmp"
|
|
166
|
+
temp.mkdir(exist_ok=True)
|
|
167
|
+
return temp
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def claude_mpm_dir_hidden(self) -> Path:
|
|
171
|
+
"""Get the hidden .claude-mpm directory (creates if doesn't exist)."""
|
|
172
|
+
hidden = self.project_root / ".claude-mpm"
|
|
173
|
+
hidden.mkdir(exist_ok=True)
|
|
174
|
+
return hidden
|
|
175
|
+
|
|
176
|
+
@cached_property
|
|
177
|
+
def version_file(self) -> Path:
|
|
178
|
+
"""Get the VERSION file path."""
|
|
179
|
+
return self.project_root / "VERSION"
|
|
180
|
+
|
|
181
|
+
@cached_property
|
|
182
|
+
def pyproject_file(self) -> Path:
|
|
183
|
+
"""Get the pyproject.toml file path."""
|
|
184
|
+
return self.project_root / "pyproject.toml"
|
|
185
|
+
|
|
186
|
+
@cached_property
|
|
187
|
+
def package_json_file(self) -> Path:
|
|
188
|
+
"""Get the package.json file path."""
|
|
189
|
+
return self.project_root / "package.json"
|
|
190
|
+
|
|
191
|
+
@cached_property
|
|
192
|
+
def claude_md_file(self) -> Path:
|
|
193
|
+
"""Get the CLAUDE.md file path."""
|
|
194
|
+
return self.project_root / "CLAUDE.md"
|
|
195
|
+
|
|
196
|
+
def get_version(self) -> str:
|
|
197
|
+
"""
|
|
198
|
+
Get the project version from various sources.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Version string or 'unknown' if not found.
|
|
202
|
+
"""
|
|
203
|
+
# Try VERSION file first
|
|
204
|
+
if self.version_file.exists():
|
|
205
|
+
return self.version_file.read_text().strip()
|
|
206
|
+
|
|
207
|
+
# Try package metadata
|
|
208
|
+
try:
|
|
209
|
+
from importlib.metadata import version
|
|
210
|
+
return version('claude-mpm')
|
|
211
|
+
except Exception:
|
|
212
|
+
pass
|
|
213
|
+
|
|
214
|
+
return 'unknown'
|
|
215
|
+
|
|
216
|
+
def ensure_in_path(self) -> None:
|
|
217
|
+
"""Ensure src directory is in Python path."""
|
|
218
|
+
src_str = str(self.src_dir)
|
|
219
|
+
if src_str not in sys.path:
|
|
220
|
+
sys.path.insert(0, src_str)
|
|
221
|
+
|
|
222
|
+
def relative_to_project(self, path: Union[str, Path]) -> Path:
|
|
223
|
+
"""
|
|
224
|
+
Get a path relative to the project root.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
path: Path to make relative
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
Path relative to project root
|
|
231
|
+
"""
|
|
232
|
+
abs_path = Path(path).resolve()
|
|
233
|
+
try:
|
|
234
|
+
return abs_path.relative_to(self.project_root)
|
|
235
|
+
except ValueError:
|
|
236
|
+
return abs_path
|
|
237
|
+
|
|
238
|
+
def resolve_config_path(self, config_name: str) -> Path:
|
|
239
|
+
"""
|
|
240
|
+
Resolve a configuration file path.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
config_name: Name of the config file
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Full path to the config file
|
|
247
|
+
"""
|
|
248
|
+
# Check in config directory first
|
|
249
|
+
config_path = self.config_dir / config_name
|
|
250
|
+
if config_path.exists():
|
|
251
|
+
return config_path
|
|
252
|
+
|
|
253
|
+
# Check in project root
|
|
254
|
+
root_path = self.project_root / config_name
|
|
255
|
+
if root_path.exists():
|
|
256
|
+
return root_path
|
|
257
|
+
|
|
258
|
+
# Return config dir path as default
|
|
259
|
+
return config_path
|
|
260
|
+
|
|
261
|
+
def __str__(self) -> str:
|
|
262
|
+
"""String representation."""
|
|
263
|
+
return f"ClaudeMPMPaths(root={self.project_root})"
|
|
264
|
+
|
|
265
|
+
def __repr__(self) -> str:
|
|
266
|
+
"""Developer representation."""
|
|
267
|
+
return (
|
|
268
|
+
f"ClaudeMPMPaths(\n"
|
|
269
|
+
f" project_root={self.project_root},\n"
|
|
270
|
+
f" src_dir={self.src_dir},\n"
|
|
271
|
+
f" claude_mpm_dir={self.claude_mpm_dir}\n"
|
|
272
|
+
f")"
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# Singleton instance for import
|
|
277
|
+
paths = ClaudeMPMPaths()
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# Convenience functions
|
|
281
|
+
def get_project_root() -> Path:
|
|
282
|
+
"""Get the project root directory."""
|
|
283
|
+
return paths.project_root
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def get_src_dir() -> Path:
|
|
287
|
+
"""Get the src directory."""
|
|
288
|
+
return paths.src_dir
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def get_claude_mpm_dir() -> Path:
|
|
292
|
+
"""Get the main claude_mpm package directory."""
|
|
293
|
+
return paths.claude_mpm_dir
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def get_agents_dir() -> Path:
|
|
297
|
+
"""Get the agents directory."""
|
|
298
|
+
return paths.agents_dir
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def get_services_dir() -> Path:
|
|
302
|
+
"""Get the services directory."""
|
|
303
|
+
return paths.services_dir
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def get_config_dir() -> Path:
|
|
307
|
+
"""Get the config directory."""
|
|
308
|
+
return paths.config_dir
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def get_version() -> str:
|
|
312
|
+
"""Get the project version."""
|
|
313
|
+
return paths.get_version()
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def ensure_src_in_path() -> None:
|
|
317
|
+
"""Ensure src directory is in Python path."""
|
|
318
|
+
paths.ensure_in_path()
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
# Auto-ensure src is in path when module is imported
|
|
322
|
+
ensure_src_in_path()
|
claude_mpm/constants.py
CHANGED
claude_mpm/core/__init__.py
CHANGED
|
@@ -16,9 +16,8 @@ try:
|
|
|
16
16
|
from .service_registry import ServiceRegistry, get_service_registry, initialize_services
|
|
17
17
|
from .injectable_service import InjectableService
|
|
18
18
|
from .factories import (
|
|
19
|
-
ServiceFactory,
|
|
20
|
-
|
|
21
|
-
get_factory_registry
|
|
19
|
+
ServiceFactory, AgentServiceFactory, SessionManagerFactory,
|
|
20
|
+
ConfigurationFactory, get_factory_registry
|
|
22
21
|
)
|
|
23
22
|
except ImportError:
|
|
24
23
|
pass
|
|
@@ -36,8 +35,6 @@ __all__ = [
|
|
|
36
35
|
"initialize_services",
|
|
37
36
|
"InjectableService",
|
|
38
37
|
"ServiceFactory",
|
|
39
|
-
"HookManagerFactory",
|
|
40
|
-
"OrchestratorFactoryWrapper",
|
|
41
38
|
"AgentServiceFactory",
|
|
42
39
|
"SessionManagerFactory",
|
|
43
40
|
"ConfigurationFactory",
|
|
@@ -51,29 +51,49 @@ class SimpleAgentRegistry:
|
|
|
51
51
|
self._discover_agents()
|
|
52
52
|
|
|
53
53
|
def _discover_agents(self):
|
|
54
|
-
"""Discover agents from the framework."""
|
|
55
|
-
# Check multiple possible locations
|
|
54
|
+
"""Discover agents from the framework and project."""
|
|
55
|
+
# Check multiple possible locations, including project-level
|
|
56
56
|
agent_locations = [
|
|
57
|
+
# Project-level agents (highest priority)
|
|
58
|
+
Path.cwd() / ".claude-mpm" / "agents",
|
|
59
|
+
# Framework/system agents
|
|
57
60
|
self.framework_path / "src" / "claude_mpm" / "agents" / "templates",
|
|
58
61
|
self.framework_path / "src" / "claude_mpm" / "agents",
|
|
59
62
|
self.framework_path / "agents",
|
|
60
63
|
]
|
|
61
64
|
|
|
65
|
+
# Track discovered agents to handle precedence
|
|
66
|
+
discovered_agents = {}
|
|
67
|
+
|
|
62
68
|
for agents_dir in agent_locations:
|
|
63
69
|
if agents_dir.exists():
|
|
64
70
|
# Look for both .md and .json files
|
|
65
71
|
for pattern in ["*.md", "*.json"]:
|
|
66
72
|
for agent_file in agents_dir.glob(pattern):
|
|
67
73
|
agent_id = agent_file.stem
|
|
68
|
-
|
|
74
|
+
tier = self._determine_tier(agent_file)
|
|
75
|
+
|
|
76
|
+
# Check if we already have this agent
|
|
77
|
+
if agent_id in discovered_agents:
|
|
78
|
+
existing_tier = discovered_agents[agent_id]['tier']
|
|
79
|
+
# Skip if existing has higher precedence
|
|
80
|
+
# Precedence: project > user > system
|
|
81
|
+
tier_precedence = {'project': 3, 'user': 2, 'system': 1}
|
|
82
|
+
if tier_precedence.get(existing_tier, 0) >= tier_precedence.get(tier, 0):
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
discovered_agents[agent_id] = {
|
|
69
86
|
'name': agent_id,
|
|
70
87
|
'type': agent_id,
|
|
71
88
|
'path': str(agent_file),
|
|
72
89
|
'last_modified': agent_file.stat().st_mtime,
|
|
73
|
-
'tier':
|
|
90
|
+
'tier': tier,
|
|
74
91
|
'specializations': self._extract_specializations(agent_id),
|
|
75
92
|
'description': self._extract_description(agent_id)
|
|
76
93
|
}
|
|
94
|
+
|
|
95
|
+
# Store the final agents
|
|
96
|
+
self.agents = discovered_agents
|
|
77
97
|
|
|
78
98
|
def _determine_tier(self, agent_path: Path) -> str:
|
|
79
99
|
"""Determine agent tier based on path."""
|
|
@@ -113,11 +133,21 @@ class SimpleAgentRegistry:
|
|
|
113
133
|
}
|
|
114
134
|
return descriptions.get(agent_id, f'{agent_id.title()} agent')
|
|
115
135
|
|
|
116
|
-
def
|
|
117
|
-
"""List all agents
|
|
136
|
+
def list_agents(self, **kwargs) -> Dict[str, Any]:
|
|
137
|
+
"""List all agents."""
|
|
118
138
|
return self.agents
|
|
119
139
|
|
|
120
|
-
def
|
|
140
|
+
def listAgents(self, **kwargs) -> Dict[str, Any]:
|
|
141
|
+
"""DEPRECATED: Use list_agents() instead. Kept for backward compatibility."""
|
|
142
|
+
import warnings
|
|
143
|
+
warnings.warn(
|
|
144
|
+
"listAgents() is deprecated, use list_agents() instead",
|
|
145
|
+
DeprecationWarning,
|
|
146
|
+
stacklevel=2
|
|
147
|
+
)
|
|
148
|
+
return self.list_agents(**kwargs)
|
|
149
|
+
|
|
150
|
+
def list_agents_filtered(self, agent_type: Optional[str] = None, tier: Optional[str] = None) -> List[AgentMetadata]:
|
|
121
151
|
"""List agents with optional filtering."""
|
|
122
152
|
results = []
|
|
123
153
|
for agent_id, metadata in self.agents.items():
|
|
@@ -294,7 +324,7 @@ class AgentRegistryAdapter:
|
|
|
294
324
|
return {}
|
|
295
325
|
|
|
296
326
|
try:
|
|
297
|
-
return self.registry.
|
|
327
|
+
return self.registry.list_agents(**kwargs)
|
|
298
328
|
except Exception as e:
|
|
299
329
|
self.logger.error(f"Error listing agents: {e}")
|
|
300
330
|
return {}
|
|
@@ -314,7 +344,7 @@ class AgentRegistryAdapter:
|
|
|
314
344
|
|
|
315
345
|
try:
|
|
316
346
|
# Try to load agent definition
|
|
317
|
-
agents = self.registry.
|
|
347
|
+
agents = self.registry.list_agents()
|
|
318
348
|
for agent_id, metadata in agents.items():
|
|
319
349
|
if agent_name in agent_id or agent_name == metadata.get('type'):
|
|
320
350
|
# Load the agent file
|
|
@@ -344,7 +374,7 @@ class AgentRegistryAdapter:
|
|
|
344
374
|
|
|
345
375
|
try:
|
|
346
376
|
# Get agents with required specializations
|
|
347
|
-
agents = self.registry.
|
|
377
|
+
agents = self.registry.list_agents()
|
|
348
378
|
|
|
349
379
|
if required_specializations:
|
|
350
380
|
# Filter by specializations
|
|
@@ -386,7 +416,7 @@ class AgentRegistryAdapter:
|
|
|
386
416
|
|
|
387
417
|
try:
|
|
388
418
|
# Get all agents
|
|
389
|
-
all_agents = self.registry.
|
|
419
|
+
all_agents = self.registry.list_agents()
|
|
390
420
|
|
|
391
421
|
hierarchy = {
|
|
392
422
|
'project': [],
|
|
@@ -526,18 +556,33 @@ def get_specialized_agent_types() -> Set[str]:
|
|
|
526
556
|
return adapter.registry.specialized_agent_types
|
|
527
557
|
return set()
|
|
528
558
|
|
|
529
|
-
def
|
|
559
|
+
def list_agents_all() -> Dict[str, Dict[str, Any]]:
|
|
530
560
|
"""
|
|
531
|
-
Synchronous function for listing all agents
|
|
561
|
+
Synchronous function for listing all agents
|
|
532
562
|
|
|
533
563
|
Returns:
|
|
534
564
|
Dictionary of agent name -> agent metadata
|
|
535
565
|
"""
|
|
536
566
|
adapter = AgentRegistryAdapter()
|
|
537
567
|
if adapter.registry:
|
|
538
|
-
return adapter.registry.
|
|
568
|
+
return adapter.registry.list_agents()
|
|
539
569
|
return {}
|
|
540
570
|
|
|
571
|
+
def listAgents() -> Dict[str, Dict[str, Any]]:
|
|
572
|
+
"""
|
|
573
|
+
DEPRECATED: Use list_agents_all() instead. Kept for backward compatibility.
|
|
574
|
+
|
|
575
|
+
Returns:
|
|
576
|
+
Dictionary of agent name -> agent metadata
|
|
577
|
+
"""
|
|
578
|
+
import warnings
|
|
579
|
+
warnings.warn(
|
|
580
|
+
"listAgents() is deprecated, use list_agents_all() instead",
|
|
581
|
+
DeprecationWarning,
|
|
582
|
+
stacklevel=2
|
|
583
|
+
)
|
|
584
|
+
return list_agents_all()
|
|
585
|
+
|
|
541
586
|
def list_agents(agent_type: Optional[str] = None, tier: Optional[str] = None) -> List[AgentMetadata]:
|
|
542
587
|
"""
|
|
543
588
|
Synchronous function to list agents with optional filtering
|
|
@@ -551,7 +596,7 @@ def list_agents(agent_type: Optional[str] = None, tier: Optional[str] = None) ->
|
|
|
551
596
|
"""
|
|
552
597
|
adapter = AgentRegistryAdapter()
|
|
553
598
|
if adapter.registry:
|
|
554
|
-
return adapter.registry.
|
|
599
|
+
return adapter.registry.list_agents_filtered(agent_type=agent_type, tier=tier)
|
|
555
600
|
return []
|
|
556
601
|
|
|
557
602
|
def discover_agents_sync(force_refresh: bool = False) -> Dict[str, AgentMetadata]:
|
|
@@ -600,7 +645,7 @@ def get_registry_stats() -> Dict[str, Any]:
|
|
|
600
645
|
"""
|
|
601
646
|
adapter = AgentRegistryAdapter()
|
|
602
647
|
if adapter.registry:
|
|
603
|
-
agents = adapter.registry.
|
|
648
|
+
agents = adapter.registry.list_agents_filtered()
|
|
604
649
|
return {
|
|
605
650
|
'total_agents': len(agents),
|
|
606
651
|
'agent_types': len(set(a.type for a in agents)),
|
|
@@ -619,8 +664,9 @@ __all__ = [
|
|
|
619
664
|
'discover_agents',
|
|
620
665
|
'get_core_agent_types',
|
|
621
666
|
'get_specialized_agent_types',
|
|
622
|
-
'
|
|
667
|
+
'list_agents_all',
|
|
623
668
|
'list_agents',
|
|
669
|
+
'listAgents', # Deprecated
|
|
624
670
|
'discover_agents_sync',
|
|
625
671
|
'get_agent',
|
|
626
672
|
'get_registry_stats'
|