claude-mpm 3.3.0__py3-none-any.whl → 3.4.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/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +1 -1
- claude_mpm/agents/templates/engineer.json +1 -1
- claude_mpm/agents/templates/ops.json +1 -1
- claude_mpm/agents/templates/pm.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/research.json +1 -1
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/test_integration.json +112 -0
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/cli/commands/memory.py +749 -26
- claude_mpm/cli/commands/run.py +115 -14
- claude_mpm/cli/parser.py +89 -1
- claude_mpm/constants.py +6 -0
- claude_mpm/core/claude_runner.py +74 -11
- claude_mpm/core/config.py +1 -1
- claude_mpm/core/session_manager.py +46 -0
- claude_mpm/core/simple_runner.py +74 -11
- claude_mpm/hooks/builtin/mpm_command_hook.py +5 -5
- claude_mpm/hooks/claude_hooks/hook_handler.py +213 -30
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +9 -2
- claude_mpm/hooks/memory_integration_hook.py +51 -5
- claude_mpm/services/__init__.py +23 -5
- claude_mpm/services/agent_memory_manager.py +800 -71
- claude_mpm/services/memory_builder.py +823 -0
- claude_mpm/services/memory_optimizer.py +619 -0
- claude_mpm/services/memory_router.py +445 -0
- claude_mpm/services/project_analyzer.py +771 -0
- claude_mpm/services/socketio_server.py +649 -45
- claude_mpm/services/version_control/git_operations.py +26 -0
- claude_mpm-3.4.0.dist-info/METADATA +183 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/RECORD +36 -52
- claude_mpm/agents/agent-template.yaml +0 -83
- claude_mpm/agents/templates/test-integration-agent.md +0 -34
- claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +0 -6
- claude_mpm/cli/README.md +0 -109
- claude_mpm/cli_module/refactoring_guide.md +0 -253
- claude_mpm/core/agent_registry.py.bak +0 -312
- claude_mpm/core/base_service.py.bak +0 -406
- claude_mpm/core/websocket_handler.py +0 -233
- claude_mpm/hooks/README.md +0 -97
- claude_mpm/orchestration/SUBPROCESS_DESIGN.md +0 -66
- claude_mpm/schemas/README_SECURITY.md +0 -92
- claude_mpm/schemas/agent_schema.json +0 -395
- claude_mpm/schemas/agent_schema_documentation.md +0 -181
- claude_mpm/schemas/agent_schema_security_notes.md +0 -165
- claude_mpm/schemas/examples/standard_workflow.json +0 -505
- claude_mpm/schemas/ticket_workflow_documentation.md +0 -482
- claude_mpm/schemas/ticket_workflow_schema.json +0 -590
- claude_mpm/services/framework_claude_md_generator/README.md +0 -92
- claude_mpm/services/parent_directory_manager/README.md +0 -83
- claude_mpm/services/version_control/VERSION +0 -1
- claude_mpm/services/websocket_server.py +0 -376
- claude_mpm-3.3.0.dist-info/METADATA +0 -432
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/WHEEL +0 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/top_level.txt +0 -0
claude_mpm/cli/README.md
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# Claude MPM CLI Architecture
|
|
2
|
-
|
|
3
|
-
This document describes the refactored CLI architecture for claude-mpm.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
The CLI has been refactored from a single monolithic `cli.py` file into a modular structure that improves maintainability and code organization.
|
|
8
|
-
|
|
9
|
-
## Directory Structure
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
cli/
|
|
13
|
-
├── __init__.py # Main entry point - orchestrates the CLI flow
|
|
14
|
-
├── parser.py # Argument parsing logic - single source of truth for CLI arguments
|
|
15
|
-
├── utils.py # Shared utility functions
|
|
16
|
-
├── commands/ # Individual command implementations
|
|
17
|
-
│ ├── __init__.py
|
|
18
|
-
│ ├── run.py # Default command - runs Claude sessions
|
|
19
|
-
│ ├── tickets.py # Lists tickets
|
|
20
|
-
│ ├── info.py # Shows system information
|
|
21
|
-
│ ├── agents.py # Manages agent deployments
|
|
22
|
-
│ └── ui.py # Terminal UI launcher
|
|
23
|
-
└── README.md # This file
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Key Design Decisions
|
|
27
|
-
|
|
28
|
-
### 1. Modular Command Structure
|
|
29
|
-
Each command is implemented in its own module under `commands/`. This makes it easy to:
|
|
30
|
-
- Add new commands without touching existing code
|
|
31
|
-
- Test commands in isolation
|
|
32
|
-
- Understand what each command does
|
|
33
|
-
|
|
34
|
-
### 2. Centralized Argument Parsing
|
|
35
|
-
All argument definitions are in `parser.py`. This provides:
|
|
36
|
-
- Single source of truth for CLI arguments
|
|
37
|
-
- Reusable argument groups (common arguments, run arguments)
|
|
38
|
-
- Clear separation of parsing from execution
|
|
39
|
-
|
|
40
|
-
### 3. Shared Utilities
|
|
41
|
-
Common functions are in `utils.py`:
|
|
42
|
-
- `get_user_input()` - Handles input from files, stdin, or command line
|
|
43
|
-
- `get_agent_versions_display()` - Formats agent version information
|
|
44
|
-
- `setup_logging()` - Configures logging based on arguments
|
|
45
|
-
- `ensure_directories()` - Creates required directories on first run
|
|
46
|
-
|
|
47
|
-
### 4. Backward Compatibility
|
|
48
|
-
The refactoring maintains full backward compatibility:
|
|
49
|
-
- `__main__.py` still imports from `claude_mpm.cli`
|
|
50
|
-
- The main `cli/__init__.py` exports the same `main()` function
|
|
51
|
-
- All existing commands and arguments work exactly as before
|
|
52
|
-
|
|
53
|
-
## Entry Points
|
|
54
|
-
|
|
55
|
-
1. **Package execution**: `python -m claude_mpm`
|
|
56
|
-
- Uses `__main__.py` which imports from `cli/__init__.py`
|
|
57
|
-
|
|
58
|
-
2. **Direct import**: `from claude_mpm.cli import main`
|
|
59
|
-
- Imports the main function from `cli/__init__.py`
|
|
60
|
-
|
|
61
|
-
3. **Shell script**: `claude-mpm` command
|
|
62
|
-
- Calls `python -m claude_mpm` with proper environment setup
|
|
63
|
-
|
|
64
|
-
## Adding New Commands
|
|
65
|
-
|
|
66
|
-
To add a new command:
|
|
67
|
-
|
|
68
|
-
1. Create a new module in `commands/`:
|
|
69
|
-
```python
|
|
70
|
-
# commands/mycommand.py
|
|
71
|
-
def my_command(args):
|
|
72
|
-
"""Execute my command."""
|
|
73
|
-
# Implementation here
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
2. Add the command to `commands/__init__.py`:
|
|
77
|
-
```python
|
|
78
|
-
from .mycommand import my_command
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
3. Add parser configuration in `parser.py`:
|
|
82
|
-
```python
|
|
83
|
-
# In create_parser()
|
|
84
|
-
mycommand_parser = subparsers.add_parser(
|
|
85
|
-
"mycommand",
|
|
86
|
-
help="Description of my command"
|
|
87
|
-
)
|
|
88
|
-
# Add command-specific arguments
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
4. Add the command mapping in `cli/__init__.py`:
|
|
92
|
-
```python
|
|
93
|
-
# In _execute_command()
|
|
94
|
-
command_map = {
|
|
95
|
-
# ... existing commands ...
|
|
96
|
-
"mycommand": my_command,
|
|
97
|
-
}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Removed Files
|
|
101
|
-
|
|
102
|
-
- `cli_main.py` - Redundant entry point, functionality moved to `__main__.py`
|
|
103
|
-
- Original `cli.py` - Split into the modular structure described above
|
|
104
|
-
|
|
105
|
-
## Preserved Files
|
|
106
|
-
|
|
107
|
-
- `cli_enhancements.py` - Experimental Click-based CLI with enhanced features
|
|
108
|
-
- Kept for reference and future enhancement ideas
|
|
109
|
-
- Not currently used in production
|
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
# CLI Refactoring Guide
|
|
2
|
-
|
|
3
|
-
This guide shows how to refactor the main() function in `/src/claude_mpm/cli.py` to reduce complexity from 16 to under 10.
|
|
4
|
-
|
|
5
|
-
## Current Issues
|
|
6
|
-
|
|
7
|
-
1. **High Cyclomatic Complexity (16)**
|
|
8
|
-
- Multiple nested conditionals
|
|
9
|
-
- Duplicate argument definitions
|
|
10
|
-
- Mixed concerns in one function
|
|
11
|
-
|
|
12
|
-
2. **Code Duplication**
|
|
13
|
-
- Arguments defined twice (global level + run subcommand)
|
|
14
|
-
- Similar patterns repeated for each command
|
|
15
|
-
|
|
16
|
-
3. **Poor Maintainability**
|
|
17
|
-
- Adding new commands requires multiple changes
|
|
18
|
-
- Hard to test individual components
|
|
19
|
-
|
|
20
|
-
## Refactoring Steps
|
|
21
|
-
|
|
22
|
-
### Step 1: Update imports in cli.py
|
|
23
|
-
|
|
24
|
-
```python
|
|
25
|
-
# Add to imports
|
|
26
|
-
from .cli import ArgumentRegistry, CommandRegistry, register_standard_commands
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### Step 2: Replace main() function
|
|
30
|
-
|
|
31
|
-
Replace the entire `main()` function with:
|
|
32
|
-
|
|
33
|
-
```python
|
|
34
|
-
def main(argv: Optional[list] = None):
|
|
35
|
-
"""Main CLI entry point with reduced complexity."""
|
|
36
|
-
# Initialize registries
|
|
37
|
-
arg_registry = ArgumentRegistry()
|
|
38
|
-
cmd_registry = CommandRegistry(arg_registry)
|
|
39
|
-
|
|
40
|
-
# Register standard commands
|
|
41
|
-
register_standard_commands(cmd_registry)
|
|
42
|
-
|
|
43
|
-
# Create parser
|
|
44
|
-
parser = argparse.ArgumentParser(
|
|
45
|
-
prog="claude-mpm",
|
|
46
|
-
description=f"Claude Multi-Agent Project Manager v{__version__}",
|
|
47
|
-
epilog="By default, runs an orchestrated Claude session."
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
# Store version for ArgumentRegistry
|
|
51
|
-
parser._version = f"claude-mpm {__version__}"
|
|
52
|
-
|
|
53
|
-
# Apply global arguments
|
|
54
|
-
arg_registry.apply_arguments(parser, groups=['global'])
|
|
55
|
-
|
|
56
|
-
# Apply run arguments at top level (for default behavior)
|
|
57
|
-
arg_registry.apply_arguments(parser, groups=['run'], exclude=['no_hooks'])
|
|
58
|
-
|
|
59
|
-
# Set up subcommands
|
|
60
|
-
cmd_registry.setup_subcommands(parser)
|
|
61
|
-
|
|
62
|
-
# Parse arguments
|
|
63
|
-
args = parser.parse_args(argv)
|
|
64
|
-
|
|
65
|
-
# Set up logging
|
|
66
|
-
_setup_logging(args)
|
|
67
|
-
|
|
68
|
-
# Initialize hook service
|
|
69
|
-
hook_manager = _initialize_hook_service(args)
|
|
70
|
-
|
|
71
|
-
try:
|
|
72
|
-
# Execute command
|
|
73
|
-
result = cmd_registry.execute_command(args, hook_manager=hook_manager)
|
|
74
|
-
if result is None and not args.command:
|
|
75
|
-
parser.print_help()
|
|
76
|
-
return 1
|
|
77
|
-
return result or 0
|
|
78
|
-
|
|
79
|
-
except KeyboardInterrupt:
|
|
80
|
-
get_logger("cli").info("Session interrupted by user")
|
|
81
|
-
return 0
|
|
82
|
-
except Exception as e:
|
|
83
|
-
logger = get_logger("cli")
|
|
84
|
-
logger.error(f"Error: {e}")
|
|
85
|
-
if args.debug:
|
|
86
|
-
import traceback
|
|
87
|
-
traceback.print_exc()
|
|
88
|
-
return 1
|
|
89
|
-
finally:
|
|
90
|
-
if hook_manager:
|
|
91
|
-
hook_manager.stop_service()
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Step 3: Extract helper functions
|
|
95
|
-
|
|
96
|
-
Add these helper functions after main():
|
|
97
|
-
|
|
98
|
-
```python
|
|
99
|
-
def _setup_logging(args):
|
|
100
|
-
"""Set up logging based on arguments."""
|
|
101
|
-
if args.debug and args.logging == "OFF":
|
|
102
|
-
args.logging = "DEBUG"
|
|
103
|
-
|
|
104
|
-
if args.logging != "OFF":
|
|
105
|
-
setup_logging(level=args.logging, log_dir=args.log_dir)
|
|
106
|
-
else:
|
|
107
|
-
import logging
|
|
108
|
-
logger = logging.getLogger("cli")
|
|
109
|
-
logger.setLevel(logging.WARNING)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def _initialize_hook_service(args):
|
|
113
|
-
"""Initialize hook service if enabled."""
|
|
114
|
-
if getattr(args, 'no_hooks', False):
|
|
115
|
-
return None
|
|
116
|
-
|
|
117
|
-
try:
|
|
118
|
-
from .config.hook_config import HookConfig
|
|
119
|
-
|
|
120
|
-
if not HookConfig.is_hooks_enabled():
|
|
121
|
-
get_logger("cli").info("Hooks disabled via configuration")
|
|
122
|
-
return None
|
|
123
|
-
|
|
124
|
-
hook_manager = HookServiceManager(log_dir=args.log_dir)
|
|
125
|
-
if hook_manager.start_service():
|
|
126
|
-
logger = get_logger("cli")
|
|
127
|
-
logger.info(f"Hook service started on port {hook_manager.port}")
|
|
128
|
-
print(f"Hook service started on port {hook_manager.port}")
|
|
129
|
-
return hook_manager
|
|
130
|
-
else:
|
|
131
|
-
logger = get_logger("cli")
|
|
132
|
-
logger.warning("Failed to start hook service")
|
|
133
|
-
print("Failed to start hook service, continuing without hooks")
|
|
134
|
-
return None
|
|
135
|
-
|
|
136
|
-
except Exception as e:
|
|
137
|
-
get_logger("cli").warning(f"Hook service init failed: {e}")
|
|
138
|
-
return None
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Step 4: Update command handler signatures
|
|
142
|
-
|
|
143
|
-
Ensure all command handlers accept `**kwargs`:
|
|
144
|
-
|
|
145
|
-
```python
|
|
146
|
-
def run_session(args, hook_manager=None, **kwargs):
|
|
147
|
-
"""Run an orchestrated Claude session."""
|
|
148
|
-
# ... existing implementation
|
|
149
|
-
|
|
150
|
-
def list_tickets(args, **kwargs):
|
|
151
|
-
"""List recent tickets."""
|
|
152
|
-
# ... existing implementation
|
|
153
|
-
|
|
154
|
-
def show_info(args, hook_manager=None, **kwargs):
|
|
155
|
-
"""Show framework and configuration information."""
|
|
156
|
-
# ... existing implementation
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
## Benefits Achieved
|
|
160
|
-
|
|
161
|
-
### Complexity Reduction
|
|
162
|
-
- **Before**: Cyclomatic complexity of 16
|
|
163
|
-
- **After**: Cyclomatic complexity of ~8
|
|
164
|
-
|
|
165
|
-
### Code Organization
|
|
166
|
-
- Centralized argument definitions
|
|
167
|
-
- No duplicate argument definitions
|
|
168
|
-
- Clear separation of concerns
|
|
169
|
-
- Easier to add new commands
|
|
170
|
-
|
|
171
|
-
### Maintainability
|
|
172
|
-
- New commands can be added with a single `register()` call
|
|
173
|
-
- Arguments are defined once and reused
|
|
174
|
-
- Helper functions are testable in isolation
|
|
175
|
-
- Registry pattern allows for extension
|
|
176
|
-
|
|
177
|
-
## Adding New Commands
|
|
178
|
-
|
|
179
|
-
With the registry system, adding a new command is simple:
|
|
180
|
-
|
|
181
|
-
```python
|
|
182
|
-
# In your code or plugin
|
|
183
|
-
def my_command(args, **kwargs):
|
|
184
|
-
"""Implementation of your command."""
|
|
185
|
-
print(f"Running my command with args: {args}")
|
|
186
|
-
return 0
|
|
187
|
-
|
|
188
|
-
# Register it
|
|
189
|
-
cmd_registry.register(
|
|
190
|
-
name='mycommand',
|
|
191
|
-
help_text='Description of my command',
|
|
192
|
-
handler=my_command,
|
|
193
|
-
argument_groups=['framework'], # Reuse existing argument groups
|
|
194
|
-
extra_args={
|
|
195
|
-
'custom_arg': {
|
|
196
|
-
'flags': ['--custom'],
|
|
197
|
-
'type': str,
|
|
198
|
-
'help': 'A custom argument for this command'
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
)
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
## Testing
|
|
205
|
-
|
|
206
|
-
The refactored code is easier to test:
|
|
207
|
-
|
|
208
|
-
```python
|
|
209
|
-
# Test argument registry
|
|
210
|
-
def test_argument_registry():
|
|
211
|
-
registry = ArgumentRegistry()
|
|
212
|
-
parser = argparse.ArgumentParser()
|
|
213
|
-
registry.apply_arguments(parser, groups=['logging'])
|
|
214
|
-
|
|
215
|
-
# Verify logging arguments were added
|
|
216
|
-
args = parser.parse_args(['--logging', 'DEBUG'])
|
|
217
|
-
assert args.logging == 'DEBUG'
|
|
218
|
-
|
|
219
|
-
# Test command registry
|
|
220
|
-
def test_command_registry():
|
|
221
|
-
arg_reg = ArgumentRegistry()
|
|
222
|
-
cmd_reg = CommandRegistry(arg_reg)
|
|
223
|
-
|
|
224
|
-
called = False
|
|
225
|
-
def test_handler(args, **kwargs):
|
|
226
|
-
nonlocal called
|
|
227
|
-
called = True
|
|
228
|
-
return 0
|
|
229
|
-
|
|
230
|
-
cmd_reg.register('test', 'Test command', test_handler)
|
|
231
|
-
|
|
232
|
-
parser = argparse.ArgumentParser()
|
|
233
|
-
cmd_reg.setup_subcommands(parser)
|
|
234
|
-
|
|
235
|
-
args = parser.parse_args(['test'])
|
|
236
|
-
result = cmd_reg.execute_command(args)
|
|
237
|
-
|
|
238
|
-
assert called
|
|
239
|
-
assert result == 0
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
## Migration Checklist
|
|
243
|
-
|
|
244
|
-
- [ ] Create `/src/claude_mpm/cli/` directory
|
|
245
|
-
- [ ] Create `args.py` with ArgumentRegistry
|
|
246
|
-
- [ ] Create `commands.py` with CommandRegistry
|
|
247
|
-
- [ ] Create `__init__.py` to export classes
|
|
248
|
-
- [ ] Update imports in `cli.py`
|
|
249
|
-
- [ ] Replace main() function
|
|
250
|
-
- [ ] Add helper functions
|
|
251
|
-
- [ ] Update command handler signatures
|
|
252
|
-
- [ ] Test the refactored CLI
|
|
253
|
-
- [ ] Verify complexity is reduced to ≤10
|
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
"""Agent registry integration for Claude MPM."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import sys
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Optional, Dict, Any, List
|
|
7
|
-
import importlib.util
|
|
8
|
-
|
|
9
|
-
try:
|
|
10
|
-
from ..core.logger import get_logger
|
|
11
|
-
except ImportError:
|
|
12
|
-
from core.logger import get_logger
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class SimpleAgentRegistry:
|
|
16
|
-
"""Simple agent registry implementation."""
|
|
17
|
-
|
|
18
|
-
def __init__(self, framework_path: Path):
|
|
19
|
-
self.framework_path = framework_path
|
|
20
|
-
self.agents = {}
|
|
21
|
-
self._discover_agents()
|
|
22
|
-
|
|
23
|
-
def _discover_agents(self):
|
|
24
|
-
"""Discover agents from the framework."""
|
|
25
|
-
agents_dir = self.framework_path / "src" / "claude_mpm" / "agents"
|
|
26
|
-
if agents_dir.exists():
|
|
27
|
-
for agent_file in agents_dir.glob("*.md"):
|
|
28
|
-
agent_id = agent_file.stem
|
|
29
|
-
self.agents[agent_id] = {
|
|
30
|
-
'type': agent_id,
|
|
31
|
-
'path': str(agent_file),
|
|
32
|
-
'last_modified': agent_file.stat().st_mtime
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
def listAgents(self, **kwargs):
|
|
36
|
-
"""List all agents."""
|
|
37
|
-
return self.agents
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class AgentRegistryAdapter:
|
|
41
|
-
"""
|
|
42
|
-
Adapter to integrate claude-multiagent-pm's agent registry.
|
|
43
|
-
|
|
44
|
-
This adapter:
|
|
45
|
-
1. Locates the claude-multiagent-pm installation
|
|
46
|
-
2. Dynamically imports the agent registry
|
|
47
|
-
3. Provides a clean interface for agent operations
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
def __init__(self, framework_path: Optional[Path] = None):
|
|
51
|
-
"""
|
|
52
|
-
Initialize the agent registry adapter.
|
|
53
|
-
|
|
54
|
-
Args:
|
|
55
|
-
framework_path: Path to claude-multiagent-pm (auto-detected if None)
|
|
56
|
-
"""
|
|
57
|
-
self.logger = get_logger("agent_registry")
|
|
58
|
-
self.framework_path = framework_path or self._find_framework()
|
|
59
|
-
self.registry = None
|
|
60
|
-
self._initialize_registry()
|
|
61
|
-
|
|
62
|
-
def _find_framework(self) -> Optional[Path]:
|
|
63
|
-
"""Find claude-mpm installation.
|
|
64
|
-
|
|
65
|
-
Search order:
|
|
66
|
-
1. CLAUDE_MPM_PATH environment variable
|
|
67
|
-
2. Current working directory (if it's claude-mpm)
|
|
68
|
-
3. Walk up from current file location
|
|
69
|
-
4. Common development locations
|
|
70
|
-
"""
|
|
71
|
-
# Check environment variable first
|
|
72
|
-
env_path = os.environ.get("CLAUDE_MPM_PATH")
|
|
73
|
-
if env_path:
|
|
74
|
-
candidate = Path(env_path)
|
|
75
|
-
if self._is_valid_framework_path(candidate):
|
|
76
|
-
self.logger.info(f"Using claude-mpm from CLAUDE_MPM_PATH: {candidate}")
|
|
77
|
-
return candidate
|
|
78
|
-
else:
|
|
79
|
-
self.logger.warning(f"CLAUDE_MPM_PATH is set but invalid: {env_path}")
|
|
80
|
-
|
|
81
|
-
# Check current working directory
|
|
82
|
-
cwd = Path.cwd()
|
|
83
|
-
if self._is_valid_framework_path(cwd):
|
|
84
|
-
return cwd
|
|
85
|
-
|
|
86
|
-
# Check if we're running from within the installed package
|
|
87
|
-
current_file = Path(__file__).resolve()
|
|
88
|
-
for parent in current_file.parents:
|
|
89
|
-
if self._is_valid_framework_path(parent):
|
|
90
|
-
return parent
|
|
91
|
-
# Stop at site-packages or similar
|
|
92
|
-
if parent.name in ("site-packages", "dist-packages", "lib"):
|
|
93
|
-
break
|
|
94
|
-
|
|
95
|
-
# Check common development locations
|
|
96
|
-
candidates = [
|
|
97
|
-
Path.home() / "Projects" / "claude-mpm",
|
|
98
|
-
Path.home() / "claude-mpm",
|
|
99
|
-
]
|
|
100
|
-
|
|
101
|
-
for candidate in candidates:
|
|
102
|
-
if self._is_valid_framework_path(candidate):
|
|
103
|
-
self.logger.info(f"Found claude-mpm at: {candidate}")
|
|
104
|
-
return candidate
|
|
105
|
-
|
|
106
|
-
return None
|
|
107
|
-
|
|
108
|
-
def _is_valid_framework_path(self, path: Path) -> bool:
|
|
109
|
-
"""Check if a path is a valid claude-mpm installation."""
|
|
110
|
-
return (
|
|
111
|
-
path.exists() and
|
|
112
|
-
(path / "src" / "claude_mpm").exists() and
|
|
113
|
-
(path / "src" / "claude_mpm" / "agents").exists()
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
def _initialize_registry(self):
|
|
117
|
-
"""Initialize the agent registry."""
|
|
118
|
-
if not self.framework_path:
|
|
119
|
-
self.logger.warning("No framework path, registry unavailable")
|
|
120
|
-
return
|
|
121
|
-
|
|
122
|
-
try:
|
|
123
|
-
# For now, create a simple registry implementation
|
|
124
|
-
# This will be replaced with proper agent discovery later
|
|
125
|
-
self.registry = SimpleAgentRegistry(self.framework_path)
|
|
126
|
-
self.logger.info("Agent registry initialized successfully")
|
|
127
|
-
|
|
128
|
-
except Exception as e:
|
|
129
|
-
self.logger.error(f"Failed to initialize registry: {e}")
|
|
130
|
-
|
|
131
|
-
def list_agents(self, **kwargs) -> Dict[str, Any]:
|
|
132
|
-
"""
|
|
133
|
-
List available agents.
|
|
134
|
-
|
|
135
|
-
Args:
|
|
136
|
-
**kwargs: Arguments to pass to AgentRegistry.listAgents()
|
|
137
|
-
|
|
138
|
-
Returns:
|
|
139
|
-
Dictionary of agents with metadata
|
|
140
|
-
"""
|
|
141
|
-
if not self.registry:
|
|
142
|
-
return {}
|
|
143
|
-
|
|
144
|
-
try:
|
|
145
|
-
return self.registry.listAgents(**kwargs)
|
|
146
|
-
except Exception as e:
|
|
147
|
-
self.logger.error(f"Error listing agents: {e}")
|
|
148
|
-
return {}
|
|
149
|
-
|
|
150
|
-
def get_agent_definition(self, agent_name: str) -> Optional[str]:
|
|
151
|
-
"""
|
|
152
|
-
Get agent definition by name.
|
|
153
|
-
|
|
154
|
-
Args:
|
|
155
|
-
agent_name: Name of the agent
|
|
156
|
-
|
|
157
|
-
Returns:
|
|
158
|
-
Agent definition content or None
|
|
159
|
-
"""
|
|
160
|
-
if not self.registry:
|
|
161
|
-
return None
|
|
162
|
-
|
|
163
|
-
try:
|
|
164
|
-
# Try to load agent definition
|
|
165
|
-
agents = self.registry.listAgents()
|
|
166
|
-
for agent_id, metadata in agents.items():
|
|
167
|
-
if agent_name in agent_id or agent_name == metadata.get('type'):
|
|
168
|
-
# Load the agent file
|
|
169
|
-
agent_path = Path(metadata['path'])
|
|
170
|
-
if agent_path.exists():
|
|
171
|
-
return agent_path.read_text()
|
|
172
|
-
|
|
173
|
-
return None
|
|
174
|
-
|
|
175
|
-
except Exception as e:
|
|
176
|
-
self.logger.error(f"Error getting agent definition: {e}")
|
|
177
|
-
return None
|
|
178
|
-
|
|
179
|
-
def select_agent_for_task(self, task_description: str, required_specializations: Optional[List[str]] = None) -> Optional[Dict[str, Any]]:
|
|
180
|
-
"""
|
|
181
|
-
Select optimal agent for a task.
|
|
182
|
-
|
|
183
|
-
Args:
|
|
184
|
-
task_description: Description of the task
|
|
185
|
-
required_specializations: Required agent specializations
|
|
186
|
-
|
|
187
|
-
Returns:
|
|
188
|
-
Agent metadata or None
|
|
189
|
-
"""
|
|
190
|
-
if not self.registry:
|
|
191
|
-
return None
|
|
192
|
-
|
|
193
|
-
try:
|
|
194
|
-
# Get agents with required specializations
|
|
195
|
-
if required_specializations:
|
|
196
|
-
agents = self.registry.listAgents(specializations=required_specializations)
|
|
197
|
-
else:
|
|
198
|
-
agents = self.registry.listAgents()
|
|
199
|
-
|
|
200
|
-
if not agents:
|
|
201
|
-
return None
|
|
202
|
-
|
|
203
|
-
# For now, return the first matching agent
|
|
204
|
-
# In future, could implement more sophisticated selection
|
|
205
|
-
agent_id = next(iter(agents))
|
|
206
|
-
return {
|
|
207
|
-
'id': agent_id,
|
|
208
|
-
'metadata': agents[agent_id]
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
except Exception as e:
|
|
212
|
-
self.logger.error(f"Error selecting agent: {e}")
|
|
213
|
-
return None
|
|
214
|
-
|
|
215
|
-
def get_agent_hierarchy(self) -> Dict[str, List[str]]:
|
|
216
|
-
"""
|
|
217
|
-
Get agent hierarchy (project → user → system).
|
|
218
|
-
|
|
219
|
-
Returns:
|
|
220
|
-
Dictionary with hierarchy levels and agent names
|
|
221
|
-
"""
|
|
222
|
-
if not self.registry:
|
|
223
|
-
return {
|
|
224
|
-
'project': [],
|
|
225
|
-
'user': [],
|
|
226
|
-
'system': []
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
try:
|
|
230
|
-
# Get all agents
|
|
231
|
-
all_agents = self.registry.listAgents()
|
|
232
|
-
|
|
233
|
-
hierarchy = {
|
|
234
|
-
'project': [],
|
|
235
|
-
'user': [],
|
|
236
|
-
'system': []
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
# Categorize by path
|
|
240
|
-
for agent_id, metadata in all_agents.items():
|
|
241
|
-
agent_path = metadata.get('path', '')
|
|
242
|
-
|
|
243
|
-
if 'project-specific' in agent_path:
|
|
244
|
-
hierarchy['project'].append(agent_id)
|
|
245
|
-
elif 'user-agents' in agent_path or 'user-defined' in agent_path:
|
|
246
|
-
hierarchy['user'].append(agent_id)
|
|
247
|
-
else:
|
|
248
|
-
hierarchy['system'].append(agent_id)
|
|
249
|
-
|
|
250
|
-
return hierarchy
|
|
251
|
-
|
|
252
|
-
except Exception as e:
|
|
253
|
-
self.logger.error(f"Error getting hierarchy: {e}")
|
|
254
|
-
return {'project': [], 'user': [], 'system': []}
|
|
255
|
-
|
|
256
|
-
def get_core_agents(self) -> List[str]:
|
|
257
|
-
"""
|
|
258
|
-
Get list of core system agents.
|
|
259
|
-
|
|
260
|
-
Returns:
|
|
261
|
-
List of core agent names
|
|
262
|
-
"""
|
|
263
|
-
return [
|
|
264
|
-
'documentation',
|
|
265
|
-
'engineer',
|
|
266
|
-
'qa',
|
|
267
|
-
'research',
|
|
268
|
-
'ops',
|
|
269
|
-
'security',
|
|
270
|
-
'version-control',
|
|
271
|
-
'data-engineer'
|
|
272
|
-
]
|
|
273
|
-
|
|
274
|
-
def format_agent_for_task_tool(self, agent_name: str, task: str, context: str = "") -> str:
|
|
275
|
-
"""
|
|
276
|
-
Format agent delegation for Task Tool.
|
|
277
|
-
|
|
278
|
-
Args:
|
|
279
|
-
agent_name: Name of the agent
|
|
280
|
-
task: Task description
|
|
281
|
-
context: Additional context
|
|
282
|
-
|
|
283
|
-
Returns:
|
|
284
|
-
Formatted Task Tool prompt
|
|
285
|
-
"""
|
|
286
|
-
# Map agent names to nicknames
|
|
287
|
-
nicknames = {
|
|
288
|
-
'documentation': 'Documenter',
|
|
289
|
-
'engineer': 'Engineer',
|
|
290
|
-
'qa': 'QA',
|
|
291
|
-
'research': 'Researcher',
|
|
292
|
-
'ops': 'Ops',
|
|
293
|
-
'security': 'Security',
|
|
294
|
-
'version-control': 'Versioner',
|
|
295
|
-
'data-engineer': 'Data Engineer'
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
nickname = nicknames.get(agent_name, agent_name.title())
|
|
299
|
-
|
|
300
|
-
from datetime import datetime
|
|
301
|
-
today = datetime.now().strftime("%Y-%m-%d")
|
|
302
|
-
|
|
303
|
-
return f"""**{nickname}**: {task}
|
|
304
|
-
|
|
305
|
-
TEMPORAL CONTEXT: Today is {today}. Apply date awareness to task execution.
|
|
306
|
-
|
|
307
|
-
**Task**: {task}
|
|
308
|
-
|
|
309
|
-
**Context**: {context}
|
|
310
|
-
|
|
311
|
-
**Authority**: Agent has full authority for {agent_name} operations
|
|
312
|
-
**Expected Results**: Completed task with operational insights"""
|