claude-mpm 1.0.0__py3-none-any.whl → 2.0.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 +4 -2
- claude_mpm/agents/INSTRUCTIONS.md +117 -312
- claude_mpm/agents/__init__.py +2 -2
- claude_mpm/agents/agent-template.yaml +83 -0
- claude_mpm/agents/agent_loader.py +192 -310
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +10 -15
- claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +46 -0
- claude_mpm/agents/templates/{engineer_agent.json → backup/engineer_agent_20250726_234551.json} +1 -1
- claude_mpm/agents/templates/data_engineer.json +107 -0
- claude_mpm/agents/templates/documentation.json +106 -0
- claude_mpm/agents/templates/engineer.json +110 -0
- claude_mpm/agents/templates/ops.json +106 -0
- claude_mpm/agents/templates/qa.json +106 -0
- claude_mpm/agents/templates/research.json +107 -0
- claude_mpm/agents/templates/security.json +105 -0
- claude_mpm/agents/templates/version_control.json +103 -0
- claude_mpm/cli.py +41 -47
- claude_mpm/cli_enhancements.py +297 -0
- claude_mpm/core/factories.py +1 -46
- claude_mpm/core/service_registry.py +0 -8
- claude_mpm/core/simple_runner.py +43 -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/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/schemas/agent_schema.json +328 -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/utils/error_handler.py +247 -0
- claude_mpm/validation/__init__.py +5 -0
- claude_mpm/validation/agent_validator.py +302 -0
- {claude_mpm-1.0.0.dist-info → claude_mpm-2.0.0.dist-info}/METADATA +133 -22
- {claude_mpm-1.0.0.dist-info → claude_mpm-2.0.0.dist-info}/RECORD +49 -37
- claude_mpm/agents/templates/data_engineer_agent.json +0 -46
- claude_mpm/agents/templates/update-optimized-specialized-agents.json +0 -374
- 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/agents/templates/{documentation_agent.json → backup/documentation_agent_20250726_234551.json} +0 -0
- /claude_mpm/agents/templates/{ops_agent.json → backup/ops_agent_20250726_234551.json} +0 -0
- /claude_mpm/agents/templates/{qa_agent.json → backup/qa_agent_20250726_234551.json} +0 -0
- /claude_mpm/agents/templates/{research_agent.json → backup/research_agent_20250726_234551.json} +0 -0
- /claude_mpm/agents/templates/{security_agent.json → backup/security_agent_20250726_234551.json} +0 -0
- /claude_mpm/agents/templates/{version_control_agent.json → backup/version_control_agent_20250726_234551.json} +0 -0
- {claude_mpm-1.0.0.dist-info → claude_mpm-2.0.0.dist-info}/WHEEL +0 -0
- {claude_mpm-1.0.0.dist-info → claude_mpm-2.0.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-1.0.0.dist-info → claude_mpm-2.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Claude MPM Agent Schema",
|
|
4
|
+
"description": "Schema definition for Claude MPM agent templates",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["id", "version", "metadata", "capabilities", "instructions"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"id": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"pattern": "^[a-z][a-z0-9_]*$",
|
|
11
|
+
"description": "Unique agent identifier (lowercase, alphanumeric with underscores)",
|
|
12
|
+
"examples": ["research", "engineer", "qa", "security"]
|
|
13
|
+
},
|
|
14
|
+
"version": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
|
17
|
+
"description": "Semantic version of the agent template",
|
|
18
|
+
"examples": ["1.0.0", "2.1.3"]
|
|
19
|
+
},
|
|
20
|
+
"metadata": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"required": ["name", "description", "category", "tags"],
|
|
23
|
+
"properties": {
|
|
24
|
+
"name": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"minLength": 3,
|
|
27
|
+
"maxLength": 50,
|
|
28
|
+
"description": "Human-readable agent name"
|
|
29
|
+
},
|
|
30
|
+
"description": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"minLength": 10,
|
|
33
|
+
"maxLength": 200,
|
|
34
|
+
"description": "Brief description of agent purpose"
|
|
35
|
+
},
|
|
36
|
+
"category": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"enum": ["engineering", "research", "quality", "operations", "specialized"],
|
|
39
|
+
"description": "Agent category for organization"
|
|
40
|
+
},
|
|
41
|
+
"tags": {
|
|
42
|
+
"type": "array",
|
|
43
|
+
"items": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"pattern": "^[a-z][a-z0-9-]*$"
|
|
46
|
+
},
|
|
47
|
+
"minItems": 1,
|
|
48
|
+
"maxItems": 10,
|
|
49
|
+
"uniqueItems": true,
|
|
50
|
+
"description": "Tags for agent discovery"
|
|
51
|
+
},
|
|
52
|
+
"author": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"description": "Agent template author"
|
|
55
|
+
},
|
|
56
|
+
"created_at": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"format": "date-time",
|
|
59
|
+
"description": "Creation timestamp"
|
|
60
|
+
},
|
|
61
|
+
"updated_at": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"format": "date-time",
|
|
64
|
+
"description": "Last update timestamp"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"capabilities": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"required": ["model", "tools", "resource_tier"],
|
|
71
|
+
"properties": {
|
|
72
|
+
"model": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"enum": [
|
|
75
|
+
"claude-3-haiku-20240307",
|
|
76
|
+
"claude-3-5-haiku-20241022",
|
|
77
|
+
"claude-3-sonnet-20240229",
|
|
78
|
+
"claude-3-5-sonnet-20241022",
|
|
79
|
+
"claude-3-opus-20240229",
|
|
80
|
+
"claude-3-5-sonnet-20240620",
|
|
81
|
+
"claude-sonnet-4-20250514",
|
|
82
|
+
"claude-4-sonnet-20250514",
|
|
83
|
+
"claude-opus-4-20250514",
|
|
84
|
+
"claude-4-opus-20250514"
|
|
85
|
+
],
|
|
86
|
+
"description": "Claude model to use for this agent"
|
|
87
|
+
},
|
|
88
|
+
"tools": {
|
|
89
|
+
"type": "array",
|
|
90
|
+
"items": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"enum": [
|
|
93
|
+
"Read", "Write", "Edit", "MultiEdit",
|
|
94
|
+
"Grep", "Glob", "LS", "Bash",
|
|
95
|
+
"WebSearch", "WebFetch",
|
|
96
|
+
"NotebookRead", "NotebookEdit",
|
|
97
|
+
"TodoWrite", "ExitPlanMode",
|
|
98
|
+
"git", "docker", "kubectl", "terraform",
|
|
99
|
+
"aws", "gcloud", "azure"
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
"uniqueItems": true,
|
|
103
|
+
"description": "Available tools for the agent"
|
|
104
|
+
},
|
|
105
|
+
"resource_tier": {
|
|
106
|
+
"type": "string",
|
|
107
|
+
"enum": ["intensive", "standard", "lightweight"],
|
|
108
|
+
"description": "Resource allocation tier"
|
|
109
|
+
},
|
|
110
|
+
"max_tokens": {
|
|
111
|
+
"type": "integer",
|
|
112
|
+
"minimum": 1000,
|
|
113
|
+
"maximum": 200000,
|
|
114
|
+
"default": 8192,
|
|
115
|
+
"description": "Maximum tokens for response"
|
|
116
|
+
},
|
|
117
|
+
"temperature": {
|
|
118
|
+
"type": "number",
|
|
119
|
+
"minimum": 0,
|
|
120
|
+
"maximum": 1,
|
|
121
|
+
"default": 0.7,
|
|
122
|
+
"description": "Model temperature setting"
|
|
123
|
+
},
|
|
124
|
+
"timeout": {
|
|
125
|
+
"type": "integer",
|
|
126
|
+
"minimum": 30,
|
|
127
|
+
"maximum": 3600,
|
|
128
|
+
"default": 300,
|
|
129
|
+
"description": "Operation timeout in seconds"
|
|
130
|
+
},
|
|
131
|
+
"memory_limit": {
|
|
132
|
+
"type": "integer",
|
|
133
|
+
"minimum": 512,
|
|
134
|
+
"maximum": 8192,
|
|
135
|
+
"description": "Memory limit in MB (for resource tier)"
|
|
136
|
+
},
|
|
137
|
+
"cpu_limit": {
|
|
138
|
+
"type": "integer",
|
|
139
|
+
"minimum": 10,
|
|
140
|
+
"maximum": 100,
|
|
141
|
+
"description": "CPU limit percentage (for resource tier)"
|
|
142
|
+
},
|
|
143
|
+
"network_access": {
|
|
144
|
+
"type": "boolean",
|
|
145
|
+
"default": false,
|
|
146
|
+
"description": "Whether agent needs network access"
|
|
147
|
+
},
|
|
148
|
+
"file_access": {
|
|
149
|
+
"type": "object",
|
|
150
|
+
"properties": {
|
|
151
|
+
"read_paths": {
|
|
152
|
+
"type": "array",
|
|
153
|
+
"items": {"type": "string"},
|
|
154
|
+
"description": "Allowed read paths"
|
|
155
|
+
},
|
|
156
|
+
"write_paths": {
|
|
157
|
+
"type": "array",
|
|
158
|
+
"items": {"type": "string"},
|
|
159
|
+
"description": "Allowed write paths"
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"instructions": {
|
|
166
|
+
"type": "string",
|
|
167
|
+
"minLength": 100,
|
|
168
|
+
"maxLength": 8000,
|
|
169
|
+
"description": "Agent system instructions (8000 character limit)"
|
|
170
|
+
},
|
|
171
|
+
"knowledge": {
|
|
172
|
+
"type": "object",
|
|
173
|
+
"description": "Agent-specific knowledge and context",
|
|
174
|
+
"properties": {
|
|
175
|
+
"domain_expertise": {
|
|
176
|
+
"type": "array",
|
|
177
|
+
"items": {"type": "string"},
|
|
178
|
+
"description": "Areas of expertise"
|
|
179
|
+
},
|
|
180
|
+
"best_practices": {
|
|
181
|
+
"type": "array",
|
|
182
|
+
"items": {"type": "string"},
|
|
183
|
+
"description": "Best practices the agent follows"
|
|
184
|
+
},
|
|
185
|
+
"constraints": {
|
|
186
|
+
"type": "array",
|
|
187
|
+
"items": {"type": "string"},
|
|
188
|
+
"description": "Operating constraints"
|
|
189
|
+
},
|
|
190
|
+
"examples": {
|
|
191
|
+
"type": "array",
|
|
192
|
+
"items": {
|
|
193
|
+
"type": "object",
|
|
194
|
+
"properties": {
|
|
195
|
+
"scenario": {"type": "string"},
|
|
196
|
+
"approach": {"type": "string"}
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
"description": "Example scenarios and approaches"
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"interactions": {
|
|
204
|
+
"type": "object",
|
|
205
|
+
"description": "Agent interaction patterns",
|
|
206
|
+
"properties": {
|
|
207
|
+
"input_format": {
|
|
208
|
+
"type": "object",
|
|
209
|
+
"properties": {
|
|
210
|
+
"required_fields": {
|
|
211
|
+
"type": "array",
|
|
212
|
+
"items": {"type": "string"}
|
|
213
|
+
},
|
|
214
|
+
"optional_fields": {
|
|
215
|
+
"type": "array",
|
|
216
|
+
"items": {"type": "string"}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
"output_format": {
|
|
221
|
+
"type": "object",
|
|
222
|
+
"properties": {
|
|
223
|
+
"structure": {
|
|
224
|
+
"type": "string",
|
|
225
|
+
"enum": ["markdown", "json", "structured", "free-form"]
|
|
226
|
+
},
|
|
227
|
+
"includes": {
|
|
228
|
+
"type": "array",
|
|
229
|
+
"items": {"type": "string"}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
"handoff_agents": {
|
|
234
|
+
"type": "array",
|
|
235
|
+
"items": {"type": "string"},
|
|
236
|
+
"description": "Agents this agent can hand off to"
|
|
237
|
+
},
|
|
238
|
+
"triggers": {
|
|
239
|
+
"type": "array",
|
|
240
|
+
"items": {
|
|
241
|
+
"type": "object",
|
|
242
|
+
"properties": {
|
|
243
|
+
"condition": {"type": "string"},
|
|
244
|
+
"action": {"type": "string"}
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"description": "Conditions that trigger specific actions"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"testing": {
|
|
252
|
+
"type": "object",
|
|
253
|
+
"description": "Testing configuration for the agent",
|
|
254
|
+
"properties": {
|
|
255
|
+
"test_cases": {
|
|
256
|
+
"type": "array",
|
|
257
|
+
"items": {
|
|
258
|
+
"type": "object",
|
|
259
|
+
"required": ["input", "expected_behavior"],
|
|
260
|
+
"properties": {
|
|
261
|
+
"name": {"type": "string"},
|
|
262
|
+
"input": {"type": "string"},
|
|
263
|
+
"expected_behavior": {"type": "string"},
|
|
264
|
+
"validation_criteria": {
|
|
265
|
+
"type": "array",
|
|
266
|
+
"items": {"type": "string"}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
"performance_benchmarks": {
|
|
272
|
+
"type": "object",
|
|
273
|
+
"properties": {
|
|
274
|
+
"response_time": {"type": "integer"},
|
|
275
|
+
"token_usage": {"type": "integer"},
|
|
276
|
+
"success_rate": {"type": "number"}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
"hooks": {
|
|
282
|
+
"type": "object",
|
|
283
|
+
"description": "Hook configurations for extensibility",
|
|
284
|
+
"properties": {
|
|
285
|
+
"pre_execution": {
|
|
286
|
+
"type": "array",
|
|
287
|
+
"items": {
|
|
288
|
+
"type": "object",
|
|
289
|
+
"properties": {
|
|
290
|
+
"name": {"type": "string"},
|
|
291
|
+
"enabled": {"type": "boolean"}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
"post_execution": {
|
|
296
|
+
"type": "array",
|
|
297
|
+
"items": {
|
|
298
|
+
"type": "object",
|
|
299
|
+
"properties": {
|
|
300
|
+
"name": {"type": "string"},
|
|
301
|
+
"enabled": {"type": "boolean"}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
"additionalProperties": false,
|
|
309
|
+
"definitions": {
|
|
310
|
+
"resource_tier_limits": {
|
|
311
|
+
"intensive": {
|
|
312
|
+
"memory_limit": {"min": 4096, "max": 8192},
|
|
313
|
+
"cpu_limit": {"min": 60, "max": 100},
|
|
314
|
+
"timeout": {"min": 600, "max": 3600}
|
|
315
|
+
},
|
|
316
|
+
"standard": {
|
|
317
|
+
"memory_limit": {"min": 2048, "max": 4096},
|
|
318
|
+
"cpu_limit": {"min": 30, "max": 60},
|
|
319
|
+
"timeout": {"min": 300, "max": 1200}
|
|
320
|
+
},
|
|
321
|
+
"lightweight": {
|
|
322
|
+
"memory_limit": {"min": 512, "max": 2048},
|
|
323
|
+
"cpu_limit": {"min": 10, "max": 30},
|
|
324
|
+
"timeout": {"min": 30, "max": 600}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
@@ -40,15 +40,15 @@ class AgentManager:
|
|
|
40
40
|
Initialize AgentManager.
|
|
41
41
|
|
|
42
42
|
Args:
|
|
43
|
-
framework_dir: Path to
|
|
43
|
+
framework_dir: Path to agents templates directory
|
|
44
44
|
project_dir: Path to project-specific agents directory
|
|
45
45
|
"""
|
|
46
46
|
# Use PathResolver for consistent path discovery
|
|
47
47
|
if framework_dir is None:
|
|
48
48
|
try:
|
|
49
|
-
|
|
50
|
-
self.framework_dir =
|
|
51
|
-
except
|
|
49
|
+
# Use agents templates directory
|
|
50
|
+
self.framework_dir = Path(__file__).parent.parent / "agents" / "templates"
|
|
51
|
+
except Exception:
|
|
52
52
|
# Fallback to agents directory
|
|
53
53
|
self.framework_dir = PathResolver.get_agents_dir()
|
|
54
54
|
else:
|
|
@@ -118,7 +118,7 @@ class AgentProfileLoader(BaseService):
|
|
|
118
118
|
self.tier_paths = {
|
|
119
119
|
ProfileTier.PROJECT: self.working_directory / 'agents',
|
|
120
120
|
ProfileTier.USER: self.user_home / '.claude-pm' / 'agents',
|
|
121
|
-
ProfileTier.SYSTEM:
|
|
121
|
+
ProfileTier.SYSTEM: Path(__file__).parent.parent / 'agents' / 'templates'
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
# Remove None values
|
|
@@ -155,7 +155,6 @@ class AgentRegistry:
|
|
|
155
155
|
# System-level agents - multiple possible locations
|
|
156
156
|
system_paths = [
|
|
157
157
|
Path(__file__).parent.parent / 'agents' / 'templates',
|
|
158
|
-
Path(__file__).parent.parent / 'framework' / 'agent-roles',
|
|
159
158
|
Path('/opt/claude-pm/agents'),
|
|
160
159
|
Path('/usr/local/claude-pm/agents')
|
|
161
160
|
]
|
|
@@ -64,10 +64,10 @@ class BaseAgentStructure:
|
|
|
64
64
|
class BaseAgentManager:
|
|
65
65
|
"""Manages base_agent.md with structured updates and validation."""
|
|
66
66
|
|
|
67
|
-
def __init__(self,
|
|
67
|
+
def __init__(self, agents_dir: Optional[Path] = None):
|
|
68
68
|
"""Initialize BaseAgentManager."""
|
|
69
|
-
self.
|
|
70
|
-
self.base_agent_path = self.
|
|
69
|
+
self.agents_dir = agents_dir or Path(__file__).parent.parent / "agents"
|
|
70
|
+
self.base_agent_path = self.agents_dir / "BASE_AGENT_TEMPLATE.md"
|
|
71
71
|
self.cache = SharedPromptCache.get_instance()
|
|
72
72
|
|
|
73
73
|
def read_base_agent(self) -> Optional[BaseAgentStructure]:
|
|
@@ -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
|