claude-mpm 0.3.0__py3-none-any.whl → 1.1.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.py +3 -2
- claude_mpm/agents/INSTRUCTIONS.md +23 -0
- claude_mpm/agents/__init__.py +2 -2
- claude_mpm/agents/agent-template.yaml +83 -0
- claude_mpm/agents/agent_loader.py +66 -90
- claude_mpm/agents/base_agent_loader.py +10 -15
- claude_mpm/cli.py +41 -47
- claude_mpm/cli_enhancements.py +297 -0
- claude_mpm/core/agent_name_normalizer.py +49 -0
- claude_mpm/core/factories.py +1 -46
- claude_mpm/core/service_registry.py +0 -8
- claude_mpm/core/simple_runner.py +50 -0
- claude_mpm/generators/__init__.py +5 -0
- claude_mpm/generators/agent_profile_generator.py +137 -0
- claude_mpm/hooks/README.md +75 -221
- claude_mpm/hooks/builtin/mpm_command_hook.py +125 -0
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +8 -7
- claude_mpm/hooks/claude_hooks/__init__.py +5 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +399 -0
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +47 -0
- claude_mpm/hooks/validation_hooks.py +181 -0
- claude_mpm/services/agent_management_service.py +4 -4
- claude_mpm/services/agent_profile_loader.py +1 -1
- claude_mpm/services/agent_registry.py +0 -1
- claude_mpm/services/base_agent_manager.py +3 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +57 -31
- claude_mpm/utils/error_handler.py +247 -0
- claude_mpm/validation/__init__.py +5 -0
- claude_mpm/validation/agent_validator.py +175 -0
- {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/METADATA +44 -7
- {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/RECORD +34 -30
- claude_mpm/config/hook_config.py +0 -42
- claude_mpm/hooks/hook_client.py +0 -264
- claude_mpm/hooks/hook_runner.py +0 -370
- claude_mpm/hooks/json_rpc_executor.py +0 -259
- claude_mpm/hooks/json_rpc_hook_client.py +0 -319
- claude_mpm/services/hook_service.py +0 -388
- claude_mpm/services/hook_service_manager.py +0 -223
- claude_mpm/services/json_rpc_hook_manager.py +0 -92
- {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/WHEEL +0 -0
- {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -25,31 +25,56 @@ class TodoTaskToolsGenerator(BaseSectionGenerator):
|
|
|
25
25
|
### Agent Name Prefix System
|
|
26
26
|
|
|
27
27
|
**Standard TodoWrite Entry Format:**
|
|
28
|
-
- **Research tasks** → `
|
|
29
|
-
- **Documentation tasks** → `
|
|
30
|
-
- **Changelog tasks** → `
|
|
31
|
-
- **QA tasks** → `QA
|
|
32
|
-
- **DevOps tasks** → `Ops
|
|
33
|
-
- **Security tasks** → `Security
|
|
34
|
-
- **Version Control tasks** → `
|
|
35
|
-
- **Version Management tasks** → `
|
|
36
|
-
- **Code Implementation tasks** → `Engineer
|
|
37
|
-
- **Data Operations tasks** → `Data Engineer
|
|
28
|
+
- **Research tasks** → `[Research] Analyze patterns and investigate implementation`
|
|
29
|
+
- **Documentation tasks** → `[Documentation] Update API reference and user guide`
|
|
30
|
+
- **Changelog tasks** → `[Documentation] Generate changelog for version 2.0`
|
|
31
|
+
- **QA tasks** → `[QA] Execute test suite and validate functionality`
|
|
32
|
+
- **DevOps tasks** → `[Ops] Configure deployment pipeline`
|
|
33
|
+
- **Security tasks** → `[Security] Perform vulnerability assessment`
|
|
34
|
+
- **Version Control tasks** → `[Version Control] Create feature branch and manage tags`
|
|
35
|
+
- **Version Management tasks** → `[Version Control] Apply semantic version bump`
|
|
36
|
+
- **Code Implementation tasks** → `[Engineer] Implement authentication system`
|
|
37
|
+
- **Data Operations tasks** → `[Data Engineer] Optimize database queries`
|
|
38
38
|
|
|
39
39
|
### Task Tool Subprocess Naming Conventions
|
|
40
40
|
|
|
41
|
-
**
|
|
41
|
+
**Task Tool Usage Pattern:**
|
|
42
42
|
```
|
|
43
|
-
|
|
43
|
+
Task(description="[task description]", subagent_type="[agent-type]")
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
**
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
46
|
+
**Valid subagent_type values (both formats accepted):**
|
|
47
|
+
|
|
48
|
+
**Lowercase-hyphenated format (traditional):**
|
|
49
|
+
- `subagent_type="research"` - For investigation and analysis
|
|
50
|
+
- `subagent_type="engineer"` - For coding and implementation
|
|
51
|
+
- `subagent_type="qa"` - For testing and quality assurance
|
|
52
|
+
- `subagent_type="documentation"` - For docs and guides
|
|
53
|
+
- `subagent_type="security"` - For security assessments
|
|
54
|
+
- `subagent_type="ops"` - For deployment and infrastructure
|
|
55
|
+
- `subagent_type="version-control"` - For git and version management
|
|
56
|
+
- `subagent_type="data-engineer"` - For data processing and APIs
|
|
57
|
+
|
|
58
|
+
**Capitalized format (matching TodoWrite prefixes - also accepted):**
|
|
59
|
+
- `subagent_type="Research"` - For investigation and analysis
|
|
60
|
+
- `subagent_type="Engineer"` - For coding and implementation
|
|
61
|
+
- `subagent_type="QA"` - For testing and quality assurance
|
|
62
|
+
- `subagent_type="Documentation"` - For docs and guides
|
|
63
|
+
- `subagent_type="Security"` - For security assessments
|
|
64
|
+
- `subagent_type="Ops"` - For deployment and infrastructure
|
|
65
|
+
- `subagent_type="Version Control"` - For git and version management
|
|
66
|
+
- `subagent_type="Data Engineer"` - For data processing and APIs
|
|
67
|
+
|
|
68
|
+
**Examples of Proper Task Tool Usage (both formats work):**
|
|
69
|
+
- ✅ `Task(description="Update framework documentation", subagent_type="documentation")`
|
|
70
|
+
- ✅ `Task(description="Execute test suite validation", subagent_type="qa")`
|
|
71
|
+
- ✅ `Task(description="Create feature branch and sync", subagent_type="version-control")`
|
|
72
|
+
- ✅ `Task(description="Investigate performance patterns", subagent_type="research")`
|
|
73
|
+
- ✅ `Task(description="Implement authentication system", subagent_type="engineer")`
|
|
74
|
+
- ✅ `Task(description="Configure database and optimize queries", subagent_type="data-engineer")`
|
|
75
|
+
- ✅ `Task(description="Analyze code patterns", subagent_type="Research")` (capitalized format)
|
|
76
|
+
- ✅ `Task(description="Update API docs", subagent_type="Documentation")` (capitalized format)
|
|
77
|
+
- ✅ `Task(description="Create release tags", subagent_type="Version Control")` (capitalized format)
|
|
53
78
|
|
|
54
79
|
### 🚨 MANDATORY: THREE SHORTCUT COMMANDS
|
|
55
80
|
|
|
@@ -72,18 +97,19 @@ class TodoTaskToolsGenerator(BaseSectionGenerator):
|
|
|
72
97
|
|
|
73
98
|
**Example Integration:**
|
|
74
99
|
```
|
|
75
|
-
TodoWrite
|
|
76
|
-
- ☐ Documentation
|
|
77
|
-
- ☐ QA
|
|
78
|
-
- ☐ Data Engineer
|
|
79
|
-
- ☐ Version Control
|
|
80
|
-
|
|
81
|
-
Task Tool
|
|
82
|
-
Task
|
|
83
|
-
Task
|
|
84
|
-
Task
|
|
85
|
-
|
|
86
|
-
|
|
100
|
+
# TodoWrite entries with proper agent prefixes:
|
|
101
|
+
- ☐ [Documentation] Generate changelog and analyze version impact
|
|
102
|
+
- ☐ [QA] Execute full test suite and quality validation
|
|
103
|
+
- ☐ [Data Engineer] Validate data integrity and verify API connectivity
|
|
104
|
+
- ☐ [Version Control] Apply semantic version bump and create release tags
|
|
105
|
+
|
|
106
|
+
# Corresponding Task Tool delegations:
|
|
107
|
+
Task(description="Generate changelog and analyze version impact", subagent_type="documentation")
|
|
108
|
+
Task(description="Execute full test suite and quality validation", subagent_type="qa")
|
|
109
|
+
Task(description="Validate data integrity and verify API connectivity", subagent_type="data-engineer")
|
|
110
|
+
Task(description="Apply semantic version bump and create release tags", subagent_type="version-control")
|
|
111
|
+
|
|
112
|
+
# Update TodoWrite status based on agent completions
|
|
87
113
|
```
|
|
88
114
|
|
|
89
115
|
---"""
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Enhanced error handling utilities for claude-mpm.
|
|
3
|
+
|
|
4
|
+
Inspired by awesome-claude-code's comprehensive error handling approach.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Optional, Type, Callable, Any, Dict
|
|
10
|
+
from functools import wraps
|
|
11
|
+
import traceback
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MPMError(Exception):
|
|
18
|
+
"""Base exception for claude-mpm errors."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, message: str, details: Optional[Dict[str, Any]] = None,
|
|
21
|
+
suggestions: Optional[List[str]] = None):
|
|
22
|
+
"""Initialize MPM error with details and suggestions."""
|
|
23
|
+
super().__init__(message)
|
|
24
|
+
self.details = details or {}
|
|
25
|
+
self.suggestions = suggestions or []
|
|
26
|
+
self.timestamp = datetime.now()
|
|
27
|
+
|
|
28
|
+
def get_user_friendly_message(self) -> str:
|
|
29
|
+
"""Get a user-friendly error message."""
|
|
30
|
+
lines = [f"❌ Error: {str(self)}"]
|
|
31
|
+
|
|
32
|
+
if self.details:
|
|
33
|
+
lines.append("\nDetails:")
|
|
34
|
+
for key, value in self.details.items():
|
|
35
|
+
lines.append(f" {key}: {value}")
|
|
36
|
+
|
|
37
|
+
if self.suggestions:
|
|
38
|
+
lines.append("\n💡 Suggestions:")
|
|
39
|
+
for suggestion in self.suggestions:
|
|
40
|
+
lines.append(f" - {suggestion}")
|
|
41
|
+
|
|
42
|
+
return '\n'.join(lines)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class AgentLoadError(MPMError):
|
|
46
|
+
"""Raised when agent loading fails."""
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ValidationError(MPMError):
|
|
51
|
+
"""Raised when validation fails."""
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ExecutionError(MPMError):
|
|
56
|
+
"""Raised when agent execution fails."""
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ConfigurationError(MPMError):
|
|
61
|
+
"""Raised when configuration is invalid."""
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def handle_errors(error_type: Type[Exception] = Exception,
|
|
66
|
+
fallback_value: Any = None,
|
|
67
|
+
log_level: int = logging.ERROR) -> Callable:
|
|
68
|
+
"""
|
|
69
|
+
Decorator for handling errors with detailed logging and user feedback.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
error_type: Type of exception to catch
|
|
73
|
+
fallback_value: Value to return on error
|
|
74
|
+
log_level: Logging level for errors
|
|
75
|
+
"""
|
|
76
|
+
def decorator(func: Callable) -> Callable:
|
|
77
|
+
@wraps(func)
|
|
78
|
+
def wrapper(*args, **kwargs):
|
|
79
|
+
try:
|
|
80
|
+
return func(*args, **kwargs)
|
|
81
|
+
except error_type as e:
|
|
82
|
+
# Log the error with full traceback
|
|
83
|
+
logger.log(log_level, f"Error in {func.__name__}: {e}", exc_info=True)
|
|
84
|
+
|
|
85
|
+
# Provide user-friendly feedback
|
|
86
|
+
if isinstance(e, MPMError):
|
|
87
|
+
print(e.get_user_friendly_message(), file=sys.stderr)
|
|
88
|
+
else:
|
|
89
|
+
print(f"❌ Error: {e}", file=sys.stderr)
|
|
90
|
+
|
|
91
|
+
return fallback_value
|
|
92
|
+
except Exception as e:
|
|
93
|
+
# Catch unexpected errors
|
|
94
|
+
logger.critical(f"Unexpected error in {func.__name__}: {e}", exc_info=True)
|
|
95
|
+
print(f"❌ Unexpected error: {e}", file=sys.stderr)
|
|
96
|
+
print("💡 This might be a bug. Please report it.", file=sys.stderr)
|
|
97
|
+
return fallback_value
|
|
98
|
+
|
|
99
|
+
return wrapper
|
|
100
|
+
return decorator
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class ErrorContext:
|
|
104
|
+
"""Context manager for enhanced error reporting."""
|
|
105
|
+
|
|
106
|
+
def __init__(self, operation: str, details: Optional[Dict[str, Any]] = None):
|
|
107
|
+
"""Initialize error context."""
|
|
108
|
+
self.operation = operation
|
|
109
|
+
self.details = details or {}
|
|
110
|
+
|
|
111
|
+
def __enter__(self):
|
|
112
|
+
"""Enter the context."""
|
|
113
|
+
logger.debug(f"Starting operation: {self.operation}")
|
|
114
|
+
return self
|
|
115
|
+
|
|
116
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
117
|
+
"""Exit the context, handling any errors."""
|
|
118
|
+
if exc_type is None:
|
|
119
|
+
logger.debug(f"Completed operation: {self.operation}")
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
# Log the error with context
|
|
123
|
+
logger.error(
|
|
124
|
+
f"Error during {self.operation}: {exc_val}",
|
|
125
|
+
extra={'operation': self.operation, 'details': self.details},
|
|
126
|
+
exc_info=True
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Don't suppress the exception
|
|
130
|
+
return False
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def retry_on_error(max_attempts: int = 3,
|
|
134
|
+
delay: float = 1.0,
|
|
135
|
+
backoff_factor: float = 2.0,
|
|
136
|
+
exceptions: tuple = (Exception,)) -> Callable:
|
|
137
|
+
"""
|
|
138
|
+
Decorator for retrying operations on error.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
max_attempts: Maximum number of attempts
|
|
142
|
+
delay: Initial delay between attempts
|
|
143
|
+
backoff_factor: Multiplier for delay after each failure
|
|
144
|
+
exceptions: Tuple of exceptions to retry on
|
|
145
|
+
"""
|
|
146
|
+
def decorator(func: Callable) -> Callable:
|
|
147
|
+
@wraps(func)
|
|
148
|
+
async def async_wrapper(*args, **kwargs):
|
|
149
|
+
import asyncio
|
|
150
|
+
|
|
151
|
+
last_exception = None
|
|
152
|
+
current_delay = delay
|
|
153
|
+
|
|
154
|
+
for attempt in range(max_attempts):
|
|
155
|
+
try:
|
|
156
|
+
return await func(*args, **kwargs)
|
|
157
|
+
except exceptions as e:
|
|
158
|
+
last_exception = e
|
|
159
|
+
if attempt < max_attempts - 1:
|
|
160
|
+
logger.warning(
|
|
161
|
+
f"Attempt {attempt + 1}/{max_attempts} failed for {func.__name__}: {e}"
|
|
162
|
+
)
|
|
163
|
+
await asyncio.sleep(current_delay)
|
|
164
|
+
current_delay *= backoff_factor
|
|
165
|
+
else:
|
|
166
|
+
logger.error(
|
|
167
|
+
f"All {max_attempts} attempts failed for {func.__name__}"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
raise last_exception
|
|
171
|
+
|
|
172
|
+
@wraps(func)
|
|
173
|
+
def sync_wrapper(*args, **kwargs):
|
|
174
|
+
import time
|
|
175
|
+
|
|
176
|
+
last_exception = None
|
|
177
|
+
current_delay = delay
|
|
178
|
+
|
|
179
|
+
for attempt in range(max_attempts):
|
|
180
|
+
try:
|
|
181
|
+
return func(*args, **kwargs)
|
|
182
|
+
except exceptions as e:
|
|
183
|
+
last_exception = e
|
|
184
|
+
if attempt < max_attempts - 1:
|
|
185
|
+
logger.warning(
|
|
186
|
+
f"Attempt {attempt + 1}/{max_attempts} failed for {func.__name__}: {e}"
|
|
187
|
+
)
|
|
188
|
+
time.sleep(current_delay)
|
|
189
|
+
current_delay *= backoff_factor
|
|
190
|
+
else:
|
|
191
|
+
logger.error(
|
|
192
|
+
f"All {max_attempts} attempts failed for {func.__name__}"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
raise last_exception
|
|
196
|
+
|
|
197
|
+
# Return appropriate wrapper based on function type
|
|
198
|
+
import asyncio
|
|
199
|
+
if asyncio.iscoroutinefunction(func):
|
|
200
|
+
return async_wrapper
|
|
201
|
+
else:
|
|
202
|
+
return sync_wrapper
|
|
203
|
+
|
|
204
|
+
return decorator
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def format_exception_chain(exc: Exception) -> str:
|
|
208
|
+
"""Format an exception chain for display."""
|
|
209
|
+
lines = []
|
|
210
|
+
current = exc
|
|
211
|
+
level = 0
|
|
212
|
+
|
|
213
|
+
while current is not None:
|
|
214
|
+
indent = " " * level
|
|
215
|
+
lines.append(f"{indent}{'└─' if level > 0 else ''}{type(current).__name__}: {current}")
|
|
216
|
+
|
|
217
|
+
if hasattr(current, '__cause__'):
|
|
218
|
+
current = current.__cause__
|
|
219
|
+
level += 1
|
|
220
|
+
else:
|
|
221
|
+
break
|
|
222
|
+
|
|
223
|
+
return '\n'.join(lines)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# Setup patterns from awesome-claude-code
|
|
227
|
+
def suggest_setup_fix(error: Exception) -> List[str]:
|
|
228
|
+
"""Suggest fixes for common setup errors."""
|
|
229
|
+
suggestions = []
|
|
230
|
+
error_msg = str(error).lower()
|
|
231
|
+
|
|
232
|
+
if 'git' in error_msg and 'not found' in error_msg:
|
|
233
|
+
suggestions.append("Install git from https://git-scm.com/downloads")
|
|
234
|
+
|
|
235
|
+
if 'python' in error_msg and 'module' in error_msg:
|
|
236
|
+
suggestions.append("Ensure you're in a virtual environment")
|
|
237
|
+
suggestions.append("Run: pip install -e .")
|
|
238
|
+
|
|
239
|
+
if 'permission' in error_msg:
|
|
240
|
+
suggestions.append("Check file permissions")
|
|
241
|
+
suggestions.append("You may need to run with appropriate privileges")
|
|
242
|
+
|
|
243
|
+
if 'config' in error_msg or 'configuration' in error_msg:
|
|
244
|
+
suggestions.append("Check your configuration files")
|
|
245
|
+
suggestions.append("Run: mpm validate-config")
|
|
246
|
+
|
|
247
|
+
return suggestions
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent validation framework inspired by awesome-claude-code validation patterns.
|
|
3
|
+
|
|
4
|
+
This module provides comprehensive validation for agent configurations with
|
|
5
|
+
override support, field locking, and detailed error reporting.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, List, Tuple, Optional, Set, Any
|
|
11
|
+
import yaml
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class ValidationResult:
|
|
19
|
+
"""Result of agent validation."""
|
|
20
|
+
is_valid: bool
|
|
21
|
+
errors: List[str] = field(default_factory=list)
|
|
22
|
+
warnings: List[str] = field(default_factory=list)
|
|
23
|
+
locked_fields: Set[str] = field(default_factory=set)
|
|
24
|
+
applied_overrides: Dict[str, Any] = field(default_factory=dict)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AgentValidator:
|
|
28
|
+
"""Validates agent configurations with override support."""
|
|
29
|
+
|
|
30
|
+
REQUIRED_FIELDS = ['name', 'version', 'description', 'agents']
|
|
31
|
+
AGENT_REQUIRED_FIELDS = ['name', 'role', 'prompt_template']
|
|
32
|
+
|
|
33
|
+
def __init__(self, override_file: Optional[Path] = None):
|
|
34
|
+
"""Initialize the validator with optional override configuration."""
|
|
35
|
+
self.overrides = {}
|
|
36
|
+
if override_file and override_file.exists():
|
|
37
|
+
self.overrides = self._load_overrides(override_file)
|
|
38
|
+
|
|
39
|
+
def _load_overrides(self, override_file: Path) -> Dict[str, Any]:
|
|
40
|
+
"""Load override configuration from YAML file."""
|
|
41
|
+
try:
|
|
42
|
+
with open(override_file, 'r') as f:
|
|
43
|
+
data = yaml.safe_load(f)
|
|
44
|
+
return data.get('overrides', {})
|
|
45
|
+
except Exception as e:
|
|
46
|
+
logger.warning(f"Failed to load overrides from {override_file}: {e}")
|
|
47
|
+
return {}
|
|
48
|
+
|
|
49
|
+
def validate_agent_config(self, config: Dict[str, Any], agent_id: str) -> ValidationResult:
|
|
50
|
+
"""Validate a single agent configuration."""
|
|
51
|
+
result = ValidationResult(is_valid=True)
|
|
52
|
+
|
|
53
|
+
# Apply overrides
|
|
54
|
+
config, locked_fields, skip_validation = self._apply_overrides(config, agent_id)
|
|
55
|
+
result.locked_fields = locked_fields
|
|
56
|
+
|
|
57
|
+
if skip_validation:
|
|
58
|
+
logger.info(f"Skipping validation for agent {agent_id} - marked as skip_validation")
|
|
59
|
+
return result
|
|
60
|
+
|
|
61
|
+
# Validate required fields
|
|
62
|
+
for field in self.AGENT_REQUIRED_FIELDS:
|
|
63
|
+
if field not in locked_fields and field not in config:
|
|
64
|
+
result.errors.append(f"Missing required field: {field}")
|
|
65
|
+
result.is_valid = False
|
|
66
|
+
|
|
67
|
+
# Validate prompt template
|
|
68
|
+
if 'prompt_template' in config and 'prompt_template' not in locked_fields:
|
|
69
|
+
template_result = self._validate_prompt_template(config['prompt_template'])
|
|
70
|
+
if not template_result[0]:
|
|
71
|
+
result.errors.extend(template_result[1])
|
|
72
|
+
result.is_valid = False
|
|
73
|
+
|
|
74
|
+
# Validate tools if present
|
|
75
|
+
if 'tools' in config:
|
|
76
|
+
tools_result = self._validate_tools(config['tools'])
|
|
77
|
+
if not tools_result[0]:
|
|
78
|
+
result.errors.extend(tools_result[1])
|
|
79
|
+
result.is_valid = False
|
|
80
|
+
|
|
81
|
+
return result
|
|
82
|
+
|
|
83
|
+
def _apply_overrides(self, config: Dict[str, Any], agent_id: str) -> Tuple[Dict[str, Any], Set[str], bool]:
|
|
84
|
+
"""Apply overrides to an agent configuration."""
|
|
85
|
+
if agent_id not in self.overrides:
|
|
86
|
+
return config, set(), False
|
|
87
|
+
|
|
88
|
+
override_config = self.overrides[agent_id]
|
|
89
|
+
locked_fields = set()
|
|
90
|
+
skip_validation = override_config.get('skip_validation', False)
|
|
91
|
+
|
|
92
|
+
# Apply each override
|
|
93
|
+
for field, value in override_config.items():
|
|
94
|
+
if field.endswith('_locked'):
|
|
95
|
+
base_field = field.replace('_locked', '')
|
|
96
|
+
if override_config.get(field, False):
|
|
97
|
+
locked_fields.add(base_field)
|
|
98
|
+
elif field not in ['notes', 'skip_validation']:
|
|
99
|
+
config[field] = value
|
|
100
|
+
|
|
101
|
+
return config, locked_fields, skip_validation
|
|
102
|
+
|
|
103
|
+
def _validate_prompt_template(self, template: str) -> Tuple[bool, List[str]]:
|
|
104
|
+
"""Validate prompt template format and placeholders."""
|
|
105
|
+
errors = []
|
|
106
|
+
|
|
107
|
+
if not template or not isinstance(template, str):
|
|
108
|
+
errors.append("Prompt template must be a non-empty string")
|
|
109
|
+
return False, errors
|
|
110
|
+
|
|
111
|
+
# Check for common placeholders
|
|
112
|
+
expected_placeholders = ['{context}', '{task}', '{constraints}']
|
|
113
|
+
missing_placeholders = []
|
|
114
|
+
|
|
115
|
+
for placeholder in expected_placeholders:
|
|
116
|
+
if placeholder not in template:
|
|
117
|
+
missing_placeholders.append(placeholder)
|
|
118
|
+
|
|
119
|
+
if missing_placeholders:
|
|
120
|
+
errors.append(f"Prompt template missing placeholders: {', '.join(missing_placeholders)}")
|
|
121
|
+
|
|
122
|
+
return len(errors) == 0, errors
|
|
123
|
+
|
|
124
|
+
def _validate_tools(self, tools: List[str]) -> Tuple[bool, List[str]]:
|
|
125
|
+
"""Validate agent tools configuration."""
|
|
126
|
+
errors = []
|
|
127
|
+
|
|
128
|
+
if not isinstance(tools, list):
|
|
129
|
+
errors.append("Tools must be a list")
|
|
130
|
+
return False, errors
|
|
131
|
+
|
|
132
|
+
# Known valid tools
|
|
133
|
+
valid_tools = {
|
|
134
|
+
'file_operations', 'code_analysis', 'git_operations',
|
|
135
|
+
'testing', 'documentation', 'security_scan'
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for tool in tools:
|
|
139
|
+
if tool not in valid_tools:
|
|
140
|
+
errors.append(f"Unknown tool: {tool}")
|
|
141
|
+
|
|
142
|
+
return len(errors) == 0, errors
|
|
143
|
+
|
|
144
|
+
def validate_profile(self, profile_path: Path) -> ValidationResult:
|
|
145
|
+
"""Validate an entire agent profile."""
|
|
146
|
+
result = ValidationResult(is_valid=True)
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
with open(profile_path, 'r') as f:
|
|
150
|
+
profile_data = yaml.safe_load(f)
|
|
151
|
+
except Exception as e:
|
|
152
|
+
result.errors.append(f"Failed to load profile: {e}")
|
|
153
|
+
result.is_valid = False
|
|
154
|
+
return result
|
|
155
|
+
|
|
156
|
+
# Validate top-level fields
|
|
157
|
+
for field in self.REQUIRED_FIELDS:
|
|
158
|
+
if field not in profile_data:
|
|
159
|
+
result.errors.append(f"Missing required top-level field: {field}")
|
|
160
|
+
result.is_valid = False
|
|
161
|
+
|
|
162
|
+
# Validate each agent
|
|
163
|
+
if 'agents' in profile_data:
|
|
164
|
+
for agent_config in profile_data['agents']:
|
|
165
|
+
agent_id = agent_config.get('name', 'unknown')
|
|
166
|
+
agent_result = self.validate_agent_config(agent_config, agent_id)
|
|
167
|
+
|
|
168
|
+
if not agent_result.is_valid:
|
|
169
|
+
result.is_valid = False
|
|
170
|
+
result.errors.extend([f"Agent '{agent_id}': {e}" for e in agent_result.errors])
|
|
171
|
+
|
|
172
|
+
result.warnings.extend([f"Agent '{agent_id}': {w}" for w in agent_result.warnings])
|
|
173
|
+
result.locked_fields.update(agent_result.locked_fields)
|
|
174
|
+
|
|
175
|
+
return result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-mpm
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Claude Multi-agent Project Manager - Clean orchestration with ticket management
|
|
5
5
|
Home-page: https://github.com/bobmatnyc/claude-mpm
|
|
6
6
|
Author: Claude MPM Team
|
|
@@ -44,6 +44,8 @@ Dynamic: requires-python
|
|
|
44
44
|
|
|
45
45
|
# Claude MPM - Multi-Agent Project Manager
|
|
46
46
|
|
|
47
|
+
> **Note**: This project is a fork of [claude-multiagent-pm](https://github.com/kfsone/claude-multiagent-pm), enhanced to integrate with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) v1.0.60+'s native agent system. This integration enables seamless orchestration of Claude Code's built-in agents (research, engineer, qa, documentation, security, ops, version_control, data_engineer) through a unified project management interface.
|
|
48
|
+
|
|
47
49
|
A framework for Claude that enables multi-agent workflows and extensible capabilities through a modular architecture.
|
|
48
50
|
|
|
49
51
|
|
|
@@ -117,17 +119,52 @@ Claude MPM provides a modular framework for extending Claude's capabilities:
|
|
|
117
119
|
|
|
118
120
|
## Installation
|
|
119
121
|
|
|
120
|
-
###
|
|
122
|
+
### Quick Install Options
|
|
123
|
+
|
|
124
|
+
#### Option 1: Using UV (Recommended - Fast)
|
|
125
|
+
UV is a lightning-fast Python package manager written in Rust, offering 10-100x speed improvements over pip.
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Install UV (if not already installed)
|
|
129
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
130
|
+
|
|
131
|
+
# Install claude-mpm with UV
|
|
132
|
+
uv pip install claude-mpm
|
|
133
|
+
|
|
134
|
+
# Or install from git
|
|
135
|
+
uv pip install git+https://github.com/bobmatnyc/claude-mpm.git
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### Option 2: Using pip (Traditional)
|
|
139
|
+
```bash
|
|
140
|
+
# Install from PyPI
|
|
141
|
+
pip install claude-mpm
|
|
142
|
+
|
|
143
|
+
# Or install from git
|
|
144
|
+
pip install git+https://github.com/bobmatnyc/claude-mpm.git
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Option 3: Using npm (Wrapper)
|
|
148
|
+
**Note**: This installs a wrapper that still requires Python. It's provided for convenience but the Python package is the primary distribution method.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm install -g @bobmatnyc/claude-mpm
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### From Source (Development)
|
|
121
155
|
|
|
122
156
|
```bash
|
|
123
157
|
# Clone the repository
|
|
124
|
-
git clone https://github.com/
|
|
158
|
+
git clone https://github.com/bobmatnyc/claude-mpm.git
|
|
125
159
|
cd claude-mpm
|
|
126
160
|
|
|
127
|
-
#
|
|
128
|
-
|
|
161
|
+
# Option A: Using UV (Recommended - Much faster)
|
|
162
|
+
uv venv
|
|
163
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
164
|
+
uv pip install -e .
|
|
129
165
|
|
|
130
|
-
#
|
|
166
|
+
# Option B: Traditional approach
|
|
167
|
+
./install_dev.sh
|
|
131
168
|
source venv/bin/activate
|
|
132
169
|
```
|
|
133
170
|
|
|
@@ -135,7 +172,7 @@ source venv/bin/activate
|
|
|
135
172
|
|
|
136
173
|
#### Core Requirements
|
|
137
174
|
- Python 3.8+
|
|
138
|
-
- Claude CLI (must be installed and in PATH)
|
|
175
|
+
- Claude Code CLI 1.0.60+ (must be installed and in PATH)
|
|
139
176
|
|
|
140
177
|
#### Automatically Installed
|
|
141
178
|
- tree-sitter & language packs (for code analysis)
|