claude-mpm 4.0.28__py3-none-any.whl → 4.0.30__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/BASE_AGENT_TEMPLATE.md +48 -3
- claude_mpm/agents/BASE_PM.md +20 -15
- claude_mpm/agents/INSTRUCTIONS.md +12 -2
- claude_mpm/agents/templates/agent-manager.json +24 -0
- claude_mpm/agents/templates/agent-manager.md +304 -0
- claude_mpm/agents/templates/documentation.json +16 -3
- claude_mpm/agents/templates/engineer.json +19 -5
- claude_mpm/agents/templates/ops.json +19 -5
- claude_mpm/agents/templates/qa.json +16 -3
- claude_mpm/agents/templates/refactoring_engineer.json +25 -7
- claude_mpm/agents/templates/research.json +19 -5
- claude_mpm/cli/__init__.py +4 -0
- claude_mpm/cli/commands/__init__.py +4 -0
- claude_mpm/cli/commands/agent_manager.py +521 -0
- claude_mpm/cli/commands/agents.py +2 -1
- claude_mpm/cli/commands/cleanup.py +1 -1
- claude_mpm/cli/commands/doctor.py +209 -0
- claude_mpm/cli/commands/mcp.py +3 -3
- claude_mpm/cli/commands/mcp_install_commands.py +12 -30
- claude_mpm/cli/commands/mcp_server_commands.py +9 -9
- claude_mpm/cli/commands/memory.py +1 -1
- claude_mpm/cli/commands/run.py +31 -2
- claude_mpm/cli/commands/run_config_checker.py +1 -1
- claude_mpm/cli/parsers/agent_manager_parser.py +247 -0
- claude_mpm/cli/parsers/base_parser.py +12 -1
- claude_mpm/cli/parsers/mcp_parser.py +1 -1
- claude_mpm/cli/parsers/run_parser.py +1 -1
- claude_mpm/cli/shared/__init__.py +1 -1
- claude_mpm/cli/startup_logging.py +463 -0
- claude_mpm/constants.py +2 -0
- claude_mpm/core/claude_runner.py +81 -2
- claude_mpm/core/constants.py +2 -2
- claude_mpm/core/framework_loader.py +45 -11
- claude_mpm/core/interactive_session.py +82 -3
- claude_mpm/core/output_style_manager.py +6 -6
- claude_mpm/core/socketio_pool.py +2 -2
- claude_mpm/core/unified_paths.py +128 -0
- claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/built/dashboard.js +1 -1
- claude_mpm/dashboard/static/built/socket-client.js +1 -1
- claude_mpm/dashboard/static/css/dashboard.css +170 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +21 -3
- claude_mpm/dashboard/static/js/components/module-viewer.js +129 -1
- claude_mpm/dashboard/static/js/dashboard.js +116 -0
- claude_mpm/dashboard/static/js/socket-client.js +0 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +1 -1
- claude_mpm/hooks/claude_hooks/hook_handler.py +1 -1
- claude_mpm/scripts/mcp_server.py +2 -2
- claude_mpm/services/agents/agent_builder.py +455 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +10 -3
- claude_mpm/services/agents/deployment/agent_validator.py +1 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +69 -1
- claude_mpm/services/diagnostics/__init__.py +18 -0
- claude_mpm/services/diagnostics/checks/__init__.py +30 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +319 -0
- claude_mpm/services/diagnostics/checks/base_check.py +64 -0
- claude_mpm/services/diagnostics/checks/claude_desktop_check.py +283 -0
- claude_mpm/services/diagnostics/checks/common_issues_check.py +354 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +300 -0
- claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +255 -0
- claude_mpm/services/diagnostics/checks/mcp_check.py +315 -0
- claude_mpm/services/diagnostics/checks/monitor_check.py +282 -0
- claude_mpm/services/diagnostics/checks/startup_log_check.py +322 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +247 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +283 -0
- claude_mpm/services/diagnostics/models.py +120 -0
- claude_mpm/services/mcp_gateway/core/interfaces.py +1 -1
- claude_mpm/services/mcp_gateway/main.py +1 -1
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +3 -3
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
- claude_mpm/services/mcp_gateway/server/stdio_server.py +3 -3
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +2 -2
- claude_mpm/services/memory/__init__.py +2 -0
- claude_mpm/services/socketio/handlers/connection.py +27 -33
- claude_mpm/services/socketio/handlers/registry.py +39 -7
- claude_mpm/services/socketio/server/core.py +72 -22
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/METADATA +4 -1
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/RECORD +89 -67
- /claude_mpm/cli/shared/{command_base.py → base_command.py} +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/top_level.txt +0 -0
claude_mpm/scripts/mcp_server.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""MCP Server launcher script for Claude
|
|
2
|
+
"""MCP Server launcher script for Claude Code.
|
|
3
3
|
|
|
4
|
-
This script launches the MCP gateway server for Claude
|
|
4
|
+
This script launches the MCP gateway server for Claude Code integration.
|
|
5
5
|
It handles proper Python path setup and error reporting to stderr.
|
|
6
6
|
"""
|
|
7
7
|
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
"""Agent Builder Service for programmatic agent creation and management.
|
|
2
|
+
|
|
3
|
+
This service provides comprehensive agent lifecycle management including:
|
|
4
|
+
- Template-based agent generation
|
|
5
|
+
- Agent variant creation with inheritance
|
|
6
|
+
- Configuration validation and sanitization
|
|
7
|
+
- PM instruction customization
|
|
8
|
+
- Integration with deployment services
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
import re
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
17
|
+
|
|
18
|
+
from claude_mpm.core.exceptions import AgentDeploymentError
|
|
19
|
+
from claude_mpm.core.logging_config import get_logger
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AgentBuilderService:
|
|
23
|
+
"""Service for building and managing agent configurations."""
|
|
24
|
+
|
|
25
|
+
# Valid agent models
|
|
26
|
+
VALID_MODELS = ["sonnet", "opus", "haiku"]
|
|
27
|
+
|
|
28
|
+
# Valid tool choices
|
|
29
|
+
VALID_TOOL_CHOICES = ["auto", "required", "any", "none"]
|
|
30
|
+
|
|
31
|
+
# Agent categories
|
|
32
|
+
AGENT_CATEGORIES = [
|
|
33
|
+
"engineering", "qa", "documentation", "ops",
|
|
34
|
+
"research", "security", "system", "utility"
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
def __init__(self, templates_dir: Optional[Path] = None):
|
|
38
|
+
"""Initialize the Agent Builder Service.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
templates_dir: Path to agent templates directory
|
|
42
|
+
"""
|
|
43
|
+
self.logger = get_logger(__name__)
|
|
44
|
+
self.templates_dir = templates_dir or Path(__file__).parent.parent.parent / "agents" / "templates"
|
|
45
|
+
self._template_cache = {}
|
|
46
|
+
|
|
47
|
+
def create_agent(
|
|
48
|
+
self,
|
|
49
|
+
agent_id: str,
|
|
50
|
+
name: str,
|
|
51
|
+
description: str,
|
|
52
|
+
model: str = "sonnet",
|
|
53
|
+
tool_choice: str = "auto",
|
|
54
|
+
instructions: Optional[str] = None,
|
|
55
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
56
|
+
base_template: Optional[str] = None
|
|
57
|
+
) -> Dict[str, Any]:
|
|
58
|
+
"""Create a new agent configuration.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
agent_id: Unique identifier for the agent
|
|
62
|
+
name: Display name for the agent
|
|
63
|
+
description: Agent purpose and capabilities
|
|
64
|
+
model: LLM model to use (sonnet/opus/haiku)
|
|
65
|
+
tool_choice: Tool selection strategy
|
|
66
|
+
instructions: Markdown instructions content
|
|
67
|
+
metadata: Additional agent metadata
|
|
68
|
+
base_template: Optional base template to extend
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Complete agent configuration dictionary
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
AgentDeploymentError: If validation fails
|
|
75
|
+
"""
|
|
76
|
+
# Validate inputs
|
|
77
|
+
self._validate_agent_id(agent_id)
|
|
78
|
+
self._validate_model(model)
|
|
79
|
+
self._validate_tool_choice(tool_choice)
|
|
80
|
+
|
|
81
|
+
# Start with base template if provided
|
|
82
|
+
if base_template:
|
|
83
|
+
config = self._load_template(base_template)
|
|
84
|
+
config["id"] = agent_id # Override ID
|
|
85
|
+
else:
|
|
86
|
+
config = {}
|
|
87
|
+
|
|
88
|
+
# Build agent configuration
|
|
89
|
+
config.update({
|
|
90
|
+
"id": agent_id,
|
|
91
|
+
"name": name,
|
|
92
|
+
"prompt": f"{agent_id}.md",
|
|
93
|
+
"model": model,
|
|
94
|
+
"tool_choice": tool_choice
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
# Build metadata
|
|
98
|
+
agent_metadata = {
|
|
99
|
+
"description": description,
|
|
100
|
+
"version": "1.0.0",
|
|
101
|
+
"created": datetime.now().isoformat(),
|
|
102
|
+
"author": "Agent Manager",
|
|
103
|
+
"category": "custom"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if metadata:
|
|
107
|
+
agent_metadata.update(metadata)
|
|
108
|
+
|
|
109
|
+
config["metadata"] = agent_metadata
|
|
110
|
+
|
|
111
|
+
# Generate instructions if not provided
|
|
112
|
+
if instructions is None:
|
|
113
|
+
instructions = self._generate_default_instructions(agent_id, name, description)
|
|
114
|
+
|
|
115
|
+
return config, instructions
|
|
116
|
+
|
|
117
|
+
def create_variant(
|
|
118
|
+
self,
|
|
119
|
+
base_agent_id: str,
|
|
120
|
+
variant_id: str,
|
|
121
|
+
variant_name: str,
|
|
122
|
+
modifications: Dict[str, Any],
|
|
123
|
+
instructions_append: Optional[str] = None
|
|
124
|
+
) -> Tuple[Dict[str, Any], str]:
|
|
125
|
+
"""Create an agent variant based on an existing agent.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
base_agent_id: ID of the base agent to extend
|
|
129
|
+
variant_id: Unique ID for the variant
|
|
130
|
+
variant_name: Display name for the variant
|
|
131
|
+
modifications: Configuration changes for the variant
|
|
132
|
+
instructions_append: Additional instructions to append
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Tuple of (variant configuration, variant instructions)
|
|
136
|
+
|
|
137
|
+
Raises:
|
|
138
|
+
AgentDeploymentError: If base agent not found or validation fails
|
|
139
|
+
"""
|
|
140
|
+
# Load base agent
|
|
141
|
+
base_config = self._load_template(base_agent_id)
|
|
142
|
+
base_instructions = self._load_instructions(base_agent_id)
|
|
143
|
+
|
|
144
|
+
# Validate variant ID
|
|
145
|
+
self._validate_agent_id(variant_id)
|
|
146
|
+
|
|
147
|
+
# Create variant configuration
|
|
148
|
+
variant_config = base_config.copy()
|
|
149
|
+
variant_config["id"] = variant_id
|
|
150
|
+
variant_config["name"] = variant_name
|
|
151
|
+
variant_config["prompt"] = f"{variant_id}.md"
|
|
152
|
+
|
|
153
|
+
# Apply modifications
|
|
154
|
+
for key, value in modifications.items():
|
|
155
|
+
if key in ["model", "tool_choice"]:
|
|
156
|
+
if key == "model":
|
|
157
|
+
self._validate_model(value)
|
|
158
|
+
elif key == "tool_choice":
|
|
159
|
+
self._validate_tool_choice(value)
|
|
160
|
+
variant_config[key] = value
|
|
161
|
+
|
|
162
|
+
# Update metadata
|
|
163
|
+
if "metadata" not in variant_config:
|
|
164
|
+
variant_config["metadata"] = {}
|
|
165
|
+
|
|
166
|
+
variant_config["metadata"].update({
|
|
167
|
+
"base_agent": base_agent_id,
|
|
168
|
+
"variant": True,
|
|
169
|
+
"variant_created": datetime.now().isoformat()
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
# Build variant instructions
|
|
173
|
+
variant_instructions = f"# {variant_name} (Variant of {base_config.get('name', base_agent_id)})\n\n"
|
|
174
|
+
variant_instructions += base_instructions
|
|
175
|
+
|
|
176
|
+
if instructions_append:
|
|
177
|
+
variant_instructions += f"\n\n## Variant-Specific Instructions\n\n{instructions_append}"
|
|
178
|
+
|
|
179
|
+
return variant_config, variant_instructions
|
|
180
|
+
|
|
181
|
+
def validate_configuration(self, config: Dict[str, Any]) -> List[str]:
|
|
182
|
+
"""Validate an agent configuration.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
config: Agent configuration to validate
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
List of validation errors (empty if valid)
|
|
189
|
+
"""
|
|
190
|
+
errors = []
|
|
191
|
+
|
|
192
|
+
# Required fields
|
|
193
|
+
required_fields = ["id", "name", "prompt", "model"]
|
|
194
|
+
for field in required_fields:
|
|
195
|
+
if field not in config:
|
|
196
|
+
errors.append(f"Missing required field: {field}")
|
|
197
|
+
|
|
198
|
+
# Validate ID
|
|
199
|
+
if "id" in config:
|
|
200
|
+
try:
|
|
201
|
+
self._validate_agent_id(config["id"])
|
|
202
|
+
except AgentDeploymentError as e:
|
|
203
|
+
errors.append(str(e))
|
|
204
|
+
|
|
205
|
+
# Validate model
|
|
206
|
+
if "model" in config:
|
|
207
|
+
try:
|
|
208
|
+
self._validate_model(config["model"])
|
|
209
|
+
except AgentDeploymentError as e:
|
|
210
|
+
errors.append(str(e))
|
|
211
|
+
|
|
212
|
+
# Validate tool_choice
|
|
213
|
+
if "tool_choice" in config:
|
|
214
|
+
try:
|
|
215
|
+
self._validate_tool_choice(config["tool_choice"])
|
|
216
|
+
except AgentDeploymentError as e:
|
|
217
|
+
errors.append(str(e))
|
|
218
|
+
|
|
219
|
+
# Validate metadata
|
|
220
|
+
if "metadata" in config:
|
|
221
|
+
if not isinstance(config["metadata"], dict):
|
|
222
|
+
errors.append("Metadata must be a dictionary")
|
|
223
|
+
|
|
224
|
+
return errors
|
|
225
|
+
|
|
226
|
+
def generate_pm_instructions(
|
|
227
|
+
self,
|
|
228
|
+
delegation_patterns: Optional[List[str]] = None,
|
|
229
|
+
workflow_overrides: Optional[Dict[str, str]] = None,
|
|
230
|
+
custom_rules: Optional[List[str]] = None
|
|
231
|
+
) -> str:
|
|
232
|
+
"""Generate customized PM instructions.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
delegation_patterns: Custom delegation patterns
|
|
236
|
+
workflow_overrides: Workflow sequence modifications
|
|
237
|
+
custom_rules: Additional PM rules
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Customized PM instructions markdown
|
|
241
|
+
"""
|
|
242
|
+
instructions = "# Custom PM Instructions\n\n"
|
|
243
|
+
|
|
244
|
+
if delegation_patterns:
|
|
245
|
+
instructions += "## Custom Delegation Patterns\n\n"
|
|
246
|
+
for pattern in delegation_patterns:
|
|
247
|
+
instructions += f"- {pattern}\n"
|
|
248
|
+
instructions += "\n"
|
|
249
|
+
|
|
250
|
+
if workflow_overrides:
|
|
251
|
+
instructions += "## Workflow Overrides\n\n"
|
|
252
|
+
for workflow, override in workflow_overrides.items():
|
|
253
|
+
instructions += f"### {workflow}\n{override}\n\n"
|
|
254
|
+
|
|
255
|
+
if custom_rules:
|
|
256
|
+
instructions += "## Additional Rules\n\n"
|
|
257
|
+
for rule in custom_rules:
|
|
258
|
+
instructions += f"- {rule}\n"
|
|
259
|
+
|
|
260
|
+
return instructions
|
|
261
|
+
|
|
262
|
+
def list_available_templates(self) -> List[Dict[str, Any]]:
|
|
263
|
+
"""List all available agent templates.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
List of template metadata dictionaries
|
|
267
|
+
"""
|
|
268
|
+
templates = []
|
|
269
|
+
|
|
270
|
+
if not self.templates_dir.exists():
|
|
271
|
+
return templates
|
|
272
|
+
|
|
273
|
+
for template_file in self.templates_dir.glob("*.json"):
|
|
274
|
+
try:
|
|
275
|
+
with open(template_file, 'r') as f:
|
|
276
|
+
config = json.load(f)
|
|
277
|
+
|
|
278
|
+
# Use filename stem as ID if not specified in config
|
|
279
|
+
template_id = config.get("id") or template_file.stem
|
|
280
|
+
|
|
281
|
+
templates.append({
|
|
282
|
+
"id": template_id,
|
|
283
|
+
"name": config.get("name", template_id),
|
|
284
|
+
"description": config.get("metadata", {}).get("description"),
|
|
285
|
+
"category": config.get("metadata", {}).get("category"),
|
|
286
|
+
"version": config.get("metadata", {}).get("version"),
|
|
287
|
+
"file": str(template_file)
|
|
288
|
+
})
|
|
289
|
+
except Exception as e:
|
|
290
|
+
self.logger.warning(f"Failed to load template {template_file}: {e}")
|
|
291
|
+
|
|
292
|
+
return templates
|
|
293
|
+
|
|
294
|
+
def _validate_agent_id(self, agent_id: str) -> None:
|
|
295
|
+
"""Validate agent ID format.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
agent_id: Agent ID to validate
|
|
299
|
+
|
|
300
|
+
Raises:
|
|
301
|
+
AgentDeploymentError: If ID is invalid
|
|
302
|
+
"""
|
|
303
|
+
if not agent_id:
|
|
304
|
+
raise AgentDeploymentError("Agent ID cannot be empty")
|
|
305
|
+
|
|
306
|
+
if len(agent_id) > 50:
|
|
307
|
+
raise AgentDeploymentError("Agent ID must be 50 characters or less")
|
|
308
|
+
|
|
309
|
+
if not re.match(r'^[a-z0-9-]+$', agent_id):
|
|
310
|
+
raise AgentDeploymentError(
|
|
311
|
+
"Agent ID must contain only lowercase letters, numbers, and hyphens"
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
def _validate_model(self, model: str) -> None:
|
|
315
|
+
"""Validate model selection.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
model: Model to validate
|
|
319
|
+
|
|
320
|
+
Raises:
|
|
321
|
+
AgentDeploymentError: If model is invalid
|
|
322
|
+
"""
|
|
323
|
+
if model not in self.VALID_MODELS:
|
|
324
|
+
raise AgentDeploymentError(
|
|
325
|
+
f"Invalid model '{model}'. Must be one of: {', '.join(self.VALID_MODELS)}"
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
def _validate_tool_choice(self, tool_choice: str) -> None:
|
|
329
|
+
"""Validate tool choice setting.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
tool_choice: Tool choice to validate
|
|
333
|
+
|
|
334
|
+
Raises:
|
|
335
|
+
AgentDeploymentError: If tool choice is invalid
|
|
336
|
+
"""
|
|
337
|
+
if tool_choice not in self.VALID_TOOL_CHOICES:
|
|
338
|
+
raise AgentDeploymentError(
|
|
339
|
+
f"Invalid tool_choice '{tool_choice}'. Must be one of: {', '.join(self.VALID_TOOL_CHOICES)}"
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
def _load_template(self, template_id: str) -> Dict[str, Any]:
|
|
343
|
+
"""Load an agent template.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
template_id: Template ID to load
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
Template configuration dictionary
|
|
350
|
+
|
|
351
|
+
Raises:
|
|
352
|
+
AgentDeploymentError: If template not found
|
|
353
|
+
"""
|
|
354
|
+
if template_id in self._template_cache:
|
|
355
|
+
return self._template_cache[template_id].copy()
|
|
356
|
+
|
|
357
|
+
template_file = self.templates_dir / f"{template_id}.json"
|
|
358
|
+
|
|
359
|
+
if not template_file.exists():
|
|
360
|
+
raise AgentDeploymentError(f"Template '{template_id}' not found")
|
|
361
|
+
|
|
362
|
+
try:
|
|
363
|
+
with open(template_file, 'r') as f:
|
|
364
|
+
config = json.load(f)
|
|
365
|
+
self._template_cache[template_id] = config
|
|
366
|
+
return config.copy()
|
|
367
|
+
except Exception as e:
|
|
368
|
+
raise AgentDeploymentError(f"Failed to load template '{template_id}': {e}")
|
|
369
|
+
|
|
370
|
+
def _load_instructions(self, agent_id: str) -> str:
|
|
371
|
+
"""Load agent instructions.
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
agent_id: Agent ID to load instructions for
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
Instructions markdown content
|
|
378
|
+
|
|
379
|
+
Raises:
|
|
380
|
+
AgentDeploymentError: If instructions not found
|
|
381
|
+
"""
|
|
382
|
+
# Try multiple possible locations
|
|
383
|
+
possible_files = [
|
|
384
|
+
self.templates_dir / f"{agent_id}.md",
|
|
385
|
+
self.templates_dir / f"{agent_id}_instructions.md",
|
|
386
|
+
self.templates_dir / f"{agent_id}-instructions.md"
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
for instructions_file in possible_files:
|
|
390
|
+
if instructions_file.exists():
|
|
391
|
+
try:
|
|
392
|
+
with open(instructions_file, 'r') as f:
|
|
393
|
+
return f.read()
|
|
394
|
+
except Exception as e:
|
|
395
|
+
self.logger.warning(f"Failed to read instructions from {instructions_file}: {e}")
|
|
396
|
+
|
|
397
|
+
# If no instructions found, return empty
|
|
398
|
+
return ""
|
|
399
|
+
|
|
400
|
+
def _generate_default_instructions(self, agent_id: str, name: str, description: str) -> str:
|
|
401
|
+
"""Generate default agent instructions.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
agent_id: Agent identifier
|
|
405
|
+
name: Agent display name
|
|
406
|
+
description: Agent description
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
Default instructions markdown
|
|
410
|
+
"""
|
|
411
|
+
return f"""# {name}
|
|
412
|
+
|
|
413
|
+
## Core Identity
|
|
414
|
+
|
|
415
|
+
You are {name}, a specialized agent in the Claude MPM framework.
|
|
416
|
+
|
|
417
|
+
## Purpose
|
|
418
|
+
|
|
419
|
+
{description}
|
|
420
|
+
|
|
421
|
+
## Responsibilities
|
|
422
|
+
|
|
423
|
+
- Primary focus on your specialized domain
|
|
424
|
+
- Collaborate with other agents as needed
|
|
425
|
+
- Follow Claude MPM framework conventions
|
|
426
|
+
- Maintain high quality standards
|
|
427
|
+
|
|
428
|
+
## Operating Principles
|
|
429
|
+
|
|
430
|
+
1. **Expertise**: Apply deep knowledge in your domain
|
|
431
|
+
2. **Efficiency**: Complete tasks effectively and quickly
|
|
432
|
+
3. **Communication**: Provide clear, actionable responses
|
|
433
|
+
4. **Collaboration**: Work well with other agents
|
|
434
|
+
5. **Quality**: Maintain high standards in all outputs
|
|
435
|
+
|
|
436
|
+
## Output Format
|
|
437
|
+
|
|
438
|
+
Provide structured responses with:
|
|
439
|
+
- Clear summaries of actions taken
|
|
440
|
+
- Detailed results when appropriate
|
|
441
|
+
- Any issues or blockers encountered
|
|
442
|
+
- Recommendations for next steps
|
|
443
|
+
|
|
444
|
+
## Integration
|
|
445
|
+
|
|
446
|
+
- Follow framework patterns and conventions
|
|
447
|
+
- Use appropriate tools for the task
|
|
448
|
+
- Coordinate with PM for complex workflows
|
|
449
|
+
- Report completion status clearly
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
*Agent ID: {agent_id}*
|
|
454
|
+
*Generated by Agent Manager*
|
|
455
|
+
"""
|
|
@@ -60,17 +60,23 @@ class AgentTemplateBuilder:
|
|
|
60
60
|
raise
|
|
61
61
|
|
|
62
62
|
# Extract tools from template with fallback
|
|
63
|
+
# Handle both dict and list formats for capabilities (backward compatibility)
|
|
64
|
+
capabilities = template_data.get("capabilities", {})
|
|
65
|
+
capabilities_tools = capabilities.get("tools") if isinstance(capabilities, dict) else None
|
|
66
|
+
|
|
63
67
|
tools = (
|
|
64
68
|
template_data.get("tools")
|
|
65
|
-
or
|
|
69
|
+
or capabilities_tools
|
|
66
70
|
or template_data.get("configuration_fields", {}).get("tools")
|
|
67
71
|
or ["Read", "Write", "Edit", "Grep", "Glob", "LS"] # Default fallback
|
|
68
72
|
)
|
|
69
73
|
|
|
70
74
|
# Extract model from template with fallback
|
|
75
|
+
capabilities_model = capabilities.get("model") if isinstance(capabilities, dict) else None
|
|
76
|
+
|
|
71
77
|
model = (
|
|
72
78
|
template_data.get("model")
|
|
73
|
-
or
|
|
79
|
+
or capabilities_model
|
|
74
80
|
or template_data.get("configuration_fields", {}).get("model")
|
|
75
81
|
or "sonnet" # Default fallback
|
|
76
82
|
)
|
|
@@ -132,7 +138,8 @@ class AgentTemplateBuilder:
|
|
|
132
138
|
# Extract custom metadata fields
|
|
133
139
|
agent_version = template_data.get("agent_version", "1.0.0")
|
|
134
140
|
agent_type = template_data.get("agent_type", "general")
|
|
135
|
-
|
|
141
|
+
# Use the capabilities_model we already extracted earlier
|
|
142
|
+
model_type = capabilities_model or "sonnet"
|
|
136
143
|
|
|
137
144
|
# Map our model types to Claude Code format
|
|
138
145
|
if model_type in ["opus", "sonnet", "haiku"]:
|
|
@@ -363,6 +363,10 @@ class MultiSourceAgentDeploymentService:
|
|
|
363
363
|
source_match = re.search(r'^source:\s*(.+)$', deployed_content, re.MULTILINE)
|
|
364
364
|
if source_match:
|
|
365
365
|
deployed_source = source_match.group(1).strip()
|
|
366
|
+
|
|
367
|
+
# If source is still unknown, try to infer it from deployment context
|
|
368
|
+
if deployed_source == "unknown":
|
|
369
|
+
deployed_source = self._infer_agent_source_from_context(agent_name, deployed_agents_dir)
|
|
366
370
|
except Exception as e:
|
|
367
371
|
self.logger.warning(f"Error reading deployed agent '{agent_name}': {e}")
|
|
368
372
|
comparison_results["needs_update"].append(agent_name)
|
|
@@ -438,4 +442,68 @@ class MultiSourceAgentDeploymentService:
|
|
|
438
442
|
f"{downgrade['template_version']}"
|
|
439
443
|
)
|
|
440
444
|
|
|
441
|
-
return comparison_results
|
|
445
|
+
return comparison_results
|
|
446
|
+
|
|
447
|
+
def _infer_agent_source_from_context(self, agent_name: str, deployed_agents_dir: Path) -> str:
|
|
448
|
+
"""Infer the source of a deployed agent when source metadata is missing.
|
|
449
|
+
|
|
450
|
+
This method attempts to determine the agent source based on:
|
|
451
|
+
1. Deployment context (development vs pipx)
|
|
452
|
+
2. Agent naming patterns
|
|
453
|
+
3. Known system agents
|
|
454
|
+
|
|
455
|
+
Args:
|
|
456
|
+
agent_name: Name of the agent
|
|
457
|
+
deployed_agents_dir: Directory where agent is deployed
|
|
458
|
+
|
|
459
|
+
Returns:
|
|
460
|
+
Inferred source string (system/project/user)
|
|
461
|
+
"""
|
|
462
|
+
# List of known system agents that ship with claude-mpm
|
|
463
|
+
system_agents = {
|
|
464
|
+
"pm", "engineer", "qa", "research", "documentation", "ops",
|
|
465
|
+
"security", "web-ui", "api-qa", "version-control"
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
# If this is a known system agent, it's from system
|
|
469
|
+
if agent_name in system_agents:
|
|
470
|
+
return "system"
|
|
471
|
+
|
|
472
|
+
# Check deployment context
|
|
473
|
+
from ....core.unified_paths import get_path_manager
|
|
474
|
+
path_manager = get_path_manager()
|
|
475
|
+
|
|
476
|
+
# If deployed_agents_dir is under user home/.claude/agents, check context
|
|
477
|
+
user_claude_dir = Path.home() / ".claude" / "agents"
|
|
478
|
+
if deployed_agents_dir == user_claude_dir:
|
|
479
|
+
# Check if we're in development mode
|
|
480
|
+
try:
|
|
481
|
+
from ....core.unified_paths import DeploymentContext, PathContext
|
|
482
|
+
deployment_context = PathContext.detect_deployment_context()
|
|
483
|
+
|
|
484
|
+
if deployment_context in (DeploymentContext.DEVELOPMENT, DeploymentContext.EDITABLE_INSTALL):
|
|
485
|
+
# In development mode, unknown agents are likely system agents being tested
|
|
486
|
+
return "system"
|
|
487
|
+
elif deployment_context == DeploymentContext.PIPX_INSTALL:
|
|
488
|
+
# In pipx mode, unknown agents could be system agents
|
|
489
|
+
# Check if agent follows system naming patterns
|
|
490
|
+
if agent_name.count('-') <= 2 and len(agent_name) <= 20:
|
|
491
|
+
return "system"
|
|
492
|
+
except Exception:
|
|
493
|
+
pass
|
|
494
|
+
|
|
495
|
+
# Check if deployed to project-specific directory
|
|
496
|
+
try:
|
|
497
|
+
project_root = path_manager.project_root
|
|
498
|
+
if str(deployed_agents_dir).startswith(str(project_root)):
|
|
499
|
+
return "project"
|
|
500
|
+
except Exception:
|
|
501
|
+
pass
|
|
502
|
+
|
|
503
|
+
# Default inference based on naming patterns
|
|
504
|
+
# System agents typically have simple names
|
|
505
|
+
if '-' not in agent_name or agent_name.count('-') <= 1:
|
|
506
|
+
return "system"
|
|
507
|
+
|
|
508
|
+
# Complex names are more likely to be user/project agents
|
|
509
|
+
return "user"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Diagnostic service for claude-mpm health checks.
|
|
3
|
+
|
|
4
|
+
WHY: Provide a comprehensive diagnostic tool to help users identify and fix
|
|
5
|
+
common issues with their claude-mpm installation and configuration.
|
|
6
|
+
|
|
7
|
+
DESIGN DECISIONS:
|
|
8
|
+
- Modular check system for easy extension
|
|
9
|
+
- Interface-based design for consistency
|
|
10
|
+
- Clear status levels (ok, warning, error)
|
|
11
|
+
- Actionable fix suggestions
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from .diagnostic_runner import DiagnosticRunner
|
|
15
|
+
from .doctor_reporter import DoctorReporter
|
|
16
|
+
from .models import DiagnosticResult, DiagnosticStatus
|
|
17
|
+
|
|
18
|
+
__all__ = ["DiagnosticRunner", "DoctorReporter", "DiagnosticResult", "DiagnosticStatus"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Diagnostic checks for claude-mpm doctor command.
|
|
3
|
+
|
|
4
|
+
WHY: Modular checks allow for easy extension and testing of individual
|
|
5
|
+
diagnostic components.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .agent_check import AgentCheck
|
|
9
|
+
from .base_check import BaseDiagnosticCheck
|
|
10
|
+
from .claude_desktop_check import ClaudeDesktopCheck
|
|
11
|
+
from .common_issues_check import CommonIssuesCheck
|
|
12
|
+
from .configuration_check import ConfigurationCheck
|
|
13
|
+
from .filesystem_check import FilesystemCheck
|
|
14
|
+
from .installation_check import InstallationCheck
|
|
15
|
+
from .mcp_check import MCPCheck
|
|
16
|
+
from .monitor_check import MonitorCheck
|
|
17
|
+
from .startup_log_check import StartupLogCheck
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"BaseDiagnosticCheck",
|
|
21
|
+
"InstallationCheck",
|
|
22
|
+
"ConfigurationCheck",
|
|
23
|
+
"ClaudeDesktopCheck",
|
|
24
|
+
"AgentCheck",
|
|
25
|
+
"MCPCheck",
|
|
26
|
+
"MonitorCheck",
|
|
27
|
+
"FilesystemCheck",
|
|
28
|
+
"CommonIssuesCheck",
|
|
29
|
+
"StartupLogCheck",
|
|
30
|
+
]
|