claude-mpm 4.0.28__py3-none-any.whl → 4.0.29__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/agent-manager.json +24 -0
- claude_mpm/agents/templates/agent-manager.md +304 -0
- claude_mpm/cli/__init__.py +2 -0
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/agent_manager.py +517 -0
- claude_mpm/cli/commands/memory.py +1 -1
- claude_mpm/cli/parsers/agent_manager_parser.py +247 -0
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/shared/__init__.py +1 -1
- claude_mpm/constants.py +1 -0
- claude_mpm/core/claude_runner.py +3 -2
- claude_mpm/core/constants.py +2 -2
- claude_mpm/core/socketio_pool.py +2 -2
- 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/services/agents/agent_builder.py +455 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +10 -3
- claude_mpm/services/memory/__init__.py +2 -0
- claude_mpm/services/socketio/handlers/connection.py +27 -33
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.29.dist-info}/METADATA +1 -1
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.29.dist-info}/RECORD +38 -33
- /claude_mpm/cli/shared/{command_base.py → base_command.py} +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.29.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.29.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.29.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.29.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
"""Agent Manager CLI command for comprehensive agent lifecycle management.
|
|
2
|
+
|
|
3
|
+
This module provides CLI interface for:
|
|
4
|
+
- Creating and customizing agents
|
|
5
|
+
- Managing agent variants
|
|
6
|
+
- Deploying agents across tiers
|
|
7
|
+
- Customizing PM instructions
|
|
8
|
+
- Discovering and listing agents
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import shutil
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, Dict, List, Optional
|
|
17
|
+
|
|
18
|
+
from ...core.config import Config
|
|
19
|
+
from ...core.logging_config import get_logger
|
|
20
|
+
from ...services.agents.agent_builder import AgentBuilderService
|
|
21
|
+
from ...services.agents.deployment.agent_deployment import AgentDeploymentService
|
|
22
|
+
from ..shared import AgentCommand, CommandResult, add_output_arguments
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AgentManagerCommand(AgentCommand):
|
|
26
|
+
"""Agent Manager command for comprehensive agent management."""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
super().__init__("agent-manager")
|
|
30
|
+
self.builder_service = AgentBuilderService()
|
|
31
|
+
self.deployment_service = None
|
|
32
|
+
self.logger = get_logger(__name__)
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def deployment(self):
|
|
36
|
+
"""Lazy load deployment service."""
|
|
37
|
+
if self.deployment_service is None:
|
|
38
|
+
self.deployment_service = AgentDeploymentService()
|
|
39
|
+
return self.deployment_service
|
|
40
|
+
|
|
41
|
+
def run(self, args) -> CommandResult:
|
|
42
|
+
"""Execute agent manager command.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
args: Command arguments
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
CommandResult with operation status
|
|
49
|
+
"""
|
|
50
|
+
if not hasattr(args, 'agent_manager_command'):
|
|
51
|
+
return self._show_help()
|
|
52
|
+
|
|
53
|
+
command_map = {
|
|
54
|
+
'list': self._list_agents,
|
|
55
|
+
'create': self._create_agent,
|
|
56
|
+
'variant': self._create_variant,
|
|
57
|
+
'deploy': self._deploy_agent,
|
|
58
|
+
'customize-pm': self._customize_pm,
|
|
59
|
+
'show': self._show_agent,
|
|
60
|
+
'test': self._test_agent,
|
|
61
|
+
'templates': self._list_templates
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
command = args.agent_manager_command
|
|
65
|
+
if command in command_map:
|
|
66
|
+
return command_map[command](args)
|
|
67
|
+
else:
|
|
68
|
+
return CommandResult.error_result(f"Unknown command: {command}")
|
|
69
|
+
|
|
70
|
+
def _list_agents(self, args) -> CommandResult:
|
|
71
|
+
"""List all agents across tiers with hierarchy.
|
|
72
|
+
|
|
73
|
+
Shows agents from:
|
|
74
|
+
1. Project level (.claude/agents/)
|
|
75
|
+
2. User level (~/.claude/agents/)
|
|
76
|
+
3. System level (framework)
|
|
77
|
+
"""
|
|
78
|
+
agents = {
|
|
79
|
+
"project": [],
|
|
80
|
+
"user": [],
|
|
81
|
+
"system": []
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Check project level
|
|
85
|
+
project_dir = Path.cwd() / ".claude" / "agents"
|
|
86
|
+
if project_dir.exists():
|
|
87
|
+
for agent_file in project_dir.glob("*.yaml"):
|
|
88
|
+
agents["project"].append(self._read_agent_summary(agent_file, "project"))
|
|
89
|
+
|
|
90
|
+
# Check user level
|
|
91
|
+
user_dir = Path.home() / ".claude" / "agents"
|
|
92
|
+
if user_dir.exists():
|
|
93
|
+
for agent_file in user_dir.glob("*.yaml"):
|
|
94
|
+
agent_id = agent_file.stem
|
|
95
|
+
# Skip if overridden by project
|
|
96
|
+
if not any(a["id"] == agent_id for a in agents["project"]):
|
|
97
|
+
agents["user"].append(self._read_agent_summary(agent_file, "user"))
|
|
98
|
+
|
|
99
|
+
# Get system agents
|
|
100
|
+
templates = self.builder_service.list_available_templates()
|
|
101
|
+
for template in templates:
|
|
102
|
+
agent_id = template["id"]
|
|
103
|
+
# Skip if overridden by project or user
|
|
104
|
+
if not any(a["id"] == agent_id for a in agents["project"] + agents["user"]):
|
|
105
|
+
agents["system"].append({
|
|
106
|
+
"id": agent_id,
|
|
107
|
+
"name": template["name"],
|
|
108
|
+
"tier": "system",
|
|
109
|
+
"description": template["description"],
|
|
110
|
+
"category": template.get("category", "custom")
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
# Format output
|
|
114
|
+
output_format = getattr(args, 'format', 'text')
|
|
115
|
+
if output_format == "json":
|
|
116
|
+
return CommandResult.success_result("Agents listed", data=agents)
|
|
117
|
+
else:
|
|
118
|
+
output = self._format_agent_list(agents)
|
|
119
|
+
return CommandResult.success_result(output)
|
|
120
|
+
|
|
121
|
+
def _create_agent(self, args) -> CommandResult:
|
|
122
|
+
"""Create a new agent interactively or from arguments."""
|
|
123
|
+
try:
|
|
124
|
+
# Interactive mode if no arguments
|
|
125
|
+
if not hasattr(args, 'agent_id'):
|
|
126
|
+
return self._interactive_create()
|
|
127
|
+
|
|
128
|
+
# Create from arguments
|
|
129
|
+
config, instructions = self.builder_service.create_agent(
|
|
130
|
+
agent_id=args.agent_id,
|
|
131
|
+
name=getattr(args, 'name', args.agent_id),
|
|
132
|
+
description=getattr(args, 'description', f"Custom agent {args.agent_id}"),
|
|
133
|
+
model=getattr(args, 'model', 'sonnet'),
|
|
134
|
+
tool_choice=getattr(args, 'tool_choice', 'auto'),
|
|
135
|
+
base_template=getattr(args, 'template', None)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Save agent files
|
|
139
|
+
result = self._save_agent(config, instructions, args.agent_id)
|
|
140
|
+
|
|
141
|
+
if result:
|
|
142
|
+
return CommandResult.success_result(f"Agent '{args.agent_id}' created successfully")
|
|
143
|
+
else:
|
|
144
|
+
return CommandResult.error_result("Failed to save agent files")
|
|
145
|
+
|
|
146
|
+
except Exception as e:
|
|
147
|
+
return CommandResult.error_result(f"Failed to create agent: {e}")
|
|
148
|
+
|
|
149
|
+
def _create_variant(self, args) -> CommandResult:
|
|
150
|
+
"""Create an agent variant."""
|
|
151
|
+
try:
|
|
152
|
+
if not hasattr(args, 'base_agent'):
|
|
153
|
+
return CommandResult.error_result("Base agent ID required for variant creation")
|
|
154
|
+
|
|
155
|
+
modifications = {}
|
|
156
|
+
if hasattr(args, 'model'):
|
|
157
|
+
modifications['model'] = args.model
|
|
158
|
+
if hasattr(args, 'tool_choice'):
|
|
159
|
+
modifications['tool_choice'] = args.tool_choice
|
|
160
|
+
|
|
161
|
+
config, instructions = self.builder_service.create_variant(
|
|
162
|
+
base_agent_id=args.base_agent,
|
|
163
|
+
variant_id=args.variant_id,
|
|
164
|
+
variant_name=getattr(args, 'name', f"{args.base_agent}-variant"),
|
|
165
|
+
modifications=modifications,
|
|
166
|
+
instructions_append=getattr(args, 'instructions', None)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Save variant
|
|
170
|
+
result = self._save_agent(config, instructions, args.variant_id)
|
|
171
|
+
|
|
172
|
+
if result:
|
|
173
|
+
return CommandResult.success_result(f"Variant '{args.variant_id}' created successfully")
|
|
174
|
+
else:
|
|
175
|
+
return CommandResult.error_result("Failed to save variant files")
|
|
176
|
+
|
|
177
|
+
except Exception as e:
|
|
178
|
+
return CommandResult.error_result(f"Failed to create variant: {e}")
|
|
179
|
+
|
|
180
|
+
def _deploy_agent(self, args) -> CommandResult:
|
|
181
|
+
"""Deploy an agent to specified tier."""
|
|
182
|
+
try:
|
|
183
|
+
agent_id = args.agent_id
|
|
184
|
+
tier = getattr(args, 'tier', 'user')
|
|
185
|
+
|
|
186
|
+
# Determine deployment path
|
|
187
|
+
if tier == 'project':
|
|
188
|
+
deploy_path = Path.cwd() / ".claude" / "agents"
|
|
189
|
+
elif tier == 'user':
|
|
190
|
+
deploy_path = Path.home() / ".claude" / "agents"
|
|
191
|
+
else:
|
|
192
|
+
return CommandResult.error_result("Invalid tier. Use 'project' or 'user'")
|
|
193
|
+
|
|
194
|
+
# Create directory if needed
|
|
195
|
+
deploy_path.mkdir(parents=True, exist_ok=True)
|
|
196
|
+
|
|
197
|
+
# Find agent files
|
|
198
|
+
template_dir = Path(__file__).parent.parent.parent / "agents" / "templates"
|
|
199
|
+
json_file = template_dir / f"{agent_id}.json"
|
|
200
|
+
md_file = template_dir / f"{agent_id}.md"
|
|
201
|
+
|
|
202
|
+
if not json_file.exists():
|
|
203
|
+
return CommandResult.error_result(f"Agent '{agent_id}' not found")
|
|
204
|
+
|
|
205
|
+
# Deploy using deployment service
|
|
206
|
+
self.deployment.deploy_agent(agent_id, str(deploy_path))
|
|
207
|
+
|
|
208
|
+
return CommandResult.success_result(
|
|
209
|
+
f"Agent '{agent_id}' deployed to {tier} level"
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
return CommandResult.error_result(f"Deployment failed: {e}")
|
|
214
|
+
|
|
215
|
+
def _customize_pm(self, args) -> CommandResult:
|
|
216
|
+
"""Customize PM instructions."""
|
|
217
|
+
try:
|
|
218
|
+
level = getattr(args, 'level', 'user')
|
|
219
|
+
|
|
220
|
+
if level == 'user':
|
|
221
|
+
pm_file = Path.home() / ".claude" / "CLAUDE.md"
|
|
222
|
+
elif level == 'project':
|
|
223
|
+
pm_file = Path.cwd() / "CLAUDE.md"
|
|
224
|
+
else:
|
|
225
|
+
return CommandResult.error_result("Invalid level. Use 'user' or 'project'")
|
|
226
|
+
|
|
227
|
+
# Create backup if file exists
|
|
228
|
+
if pm_file.exists():
|
|
229
|
+
backup_file = pm_file.with_suffix('.md.backup')
|
|
230
|
+
shutil.copy(pm_file, backup_file)
|
|
231
|
+
self.logger.info(f"Backup created: {backup_file}")
|
|
232
|
+
|
|
233
|
+
# Generate or load instructions
|
|
234
|
+
if hasattr(args, 'template'):
|
|
235
|
+
instructions = self._load_pm_template(args.template)
|
|
236
|
+
else:
|
|
237
|
+
instructions = self.builder_service.generate_pm_instructions(
|
|
238
|
+
delegation_patterns=getattr(args, 'patterns', None),
|
|
239
|
+
workflow_overrides=getattr(args, 'workflows', None),
|
|
240
|
+
custom_rules=getattr(args, 'rules', None)
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Save instructions
|
|
244
|
+
pm_file.parent.mkdir(parents=True, exist_ok=True)
|
|
245
|
+
pm_file.write_text(instructions)
|
|
246
|
+
|
|
247
|
+
return CommandResult.success_result(
|
|
248
|
+
f"PM instructions customized at {level} level: {pm_file}"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
except Exception as e:
|
|
252
|
+
return CommandResult.error_result(f"Failed to customize PM: {e}")
|
|
253
|
+
|
|
254
|
+
def _show_agent(self, args) -> CommandResult:
|
|
255
|
+
"""Show detailed agent information."""
|
|
256
|
+
try:
|
|
257
|
+
agent_id = args.agent_id
|
|
258
|
+
|
|
259
|
+
# Find agent across tiers
|
|
260
|
+
agent_info = self._find_agent(agent_id)
|
|
261
|
+
|
|
262
|
+
if not agent_info:
|
|
263
|
+
return CommandResult.error_result(f"Agent '{agent_id}' not found")
|
|
264
|
+
|
|
265
|
+
output_format = getattr(args, 'format', 'text')
|
|
266
|
+
if output_format == "json":
|
|
267
|
+
return CommandResult.success_result("Agent details", data=agent_info)
|
|
268
|
+
else:
|
|
269
|
+
output = self._format_agent_details(agent_info)
|
|
270
|
+
return CommandResult.success_result(output)
|
|
271
|
+
|
|
272
|
+
except Exception as e:
|
|
273
|
+
return CommandResult.error_result(f"Failed to show agent: {e}")
|
|
274
|
+
|
|
275
|
+
def _test_agent(self, args) -> CommandResult:
|
|
276
|
+
"""Test agent configuration."""
|
|
277
|
+
try:
|
|
278
|
+
agent_id = args.agent_id
|
|
279
|
+
|
|
280
|
+
# Find agent configuration
|
|
281
|
+
config = self._load_agent_config(agent_id)
|
|
282
|
+
|
|
283
|
+
if not config:
|
|
284
|
+
return CommandResult.error_result(f"Agent '{agent_id}' not found")
|
|
285
|
+
|
|
286
|
+
# Validate configuration
|
|
287
|
+
errors = self.builder_service.validate_configuration(config)
|
|
288
|
+
|
|
289
|
+
if errors:
|
|
290
|
+
return CommandResult.error_result(
|
|
291
|
+
f"Validation failed:\n" + "\n".join(f" - {e}" for e in errors)
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# Check for conflicts
|
|
295
|
+
conflicts = self._check_conflicts(agent_id)
|
|
296
|
+
|
|
297
|
+
if conflicts:
|
|
298
|
+
warning = f"Warning: Agent overrides {conflicts}"
|
|
299
|
+
else:
|
|
300
|
+
warning = ""
|
|
301
|
+
|
|
302
|
+
return CommandResult.success_result(
|
|
303
|
+
f"Agent '{agent_id}' configuration is valid. {warning}"
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
except Exception as e:
|
|
307
|
+
return CommandResult.error_result(f"Test failed: {e}")
|
|
308
|
+
|
|
309
|
+
def _list_templates(self, args) -> CommandResult:
|
|
310
|
+
"""List available agent templates."""
|
|
311
|
+
templates = self.builder_service.list_available_templates()
|
|
312
|
+
|
|
313
|
+
output_format = getattr(args, 'format', 'text')
|
|
314
|
+
if output_format == "json":
|
|
315
|
+
return CommandResult.success_result("Templates listed", data=templates)
|
|
316
|
+
else:
|
|
317
|
+
output = "Available Agent Templates:\n\n"
|
|
318
|
+
for template in templates:
|
|
319
|
+
template_id = template.get('id', 'unknown')
|
|
320
|
+
template_name = template.get('name', 'Unnamed')
|
|
321
|
+
output += f" {template_id:<20} - {template_name}\n"
|
|
322
|
+
if template.get('description'):
|
|
323
|
+
output += f" {template['description']}\n"
|
|
324
|
+
return CommandResult.success_result(output)
|
|
325
|
+
|
|
326
|
+
def _interactive_create(self) -> CommandResult:
|
|
327
|
+
"""Interactive agent creation wizard."""
|
|
328
|
+
print("\n=== Agent Creation Wizard ===\n")
|
|
329
|
+
|
|
330
|
+
# Get agent ID
|
|
331
|
+
agent_id = input("Agent ID (lowercase, hyphens only): ").strip()
|
|
332
|
+
if not agent_id:
|
|
333
|
+
return CommandResult.error_result("Agent ID is required")
|
|
334
|
+
|
|
335
|
+
# Get name
|
|
336
|
+
name = input(f"Display name [{agent_id}]: ").strip() or agent_id
|
|
337
|
+
|
|
338
|
+
# Get description
|
|
339
|
+
description = input("Description: ").strip()
|
|
340
|
+
if not description:
|
|
341
|
+
return CommandResult.error_result("Description is required")
|
|
342
|
+
|
|
343
|
+
# Get model
|
|
344
|
+
print("\nAvailable models: sonnet, opus, haiku")
|
|
345
|
+
model = input("Model [sonnet]: ").strip() or "sonnet"
|
|
346
|
+
|
|
347
|
+
# Get tool choice
|
|
348
|
+
print("\nTool choices: auto, required, any, none")
|
|
349
|
+
tool_choice = input("Tool choice [auto]: ").strip() or "auto"
|
|
350
|
+
|
|
351
|
+
# Create agent
|
|
352
|
+
try:
|
|
353
|
+
config, instructions = self.builder_service.create_agent(
|
|
354
|
+
agent_id=agent_id,
|
|
355
|
+
name=name,
|
|
356
|
+
description=description,
|
|
357
|
+
model=model,
|
|
358
|
+
tool_choice=tool_choice
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
# Save agent
|
|
362
|
+
if self._save_agent(config, instructions, agent_id):
|
|
363
|
+
return CommandResult.success_result(f"\nAgent '{agent_id}' created successfully!")
|
|
364
|
+
else:
|
|
365
|
+
return CommandResult.error_result("Failed to save agent files")
|
|
366
|
+
|
|
367
|
+
except Exception as e:
|
|
368
|
+
return CommandResult.error_result(f"Creation failed: {e}")
|
|
369
|
+
|
|
370
|
+
def _save_agent(self, config: Dict[str, Any], instructions: str, agent_id: str) -> bool:
|
|
371
|
+
"""Save agent configuration and instructions.
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
config: Agent configuration dictionary
|
|
375
|
+
instructions: Agent instructions markdown
|
|
376
|
+
agent_id: Agent identifier
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
True if saved successfully
|
|
380
|
+
"""
|
|
381
|
+
try:
|
|
382
|
+
template_dir = Path(__file__).parent.parent.parent / "agents" / "templates"
|
|
383
|
+
template_dir.mkdir(parents=True, exist_ok=True)
|
|
384
|
+
|
|
385
|
+
# Save JSON configuration
|
|
386
|
+
json_file = template_dir / f"{agent_id}.json"
|
|
387
|
+
with open(json_file, 'w') as f:
|
|
388
|
+
json.dump(config, f, indent=2)
|
|
389
|
+
|
|
390
|
+
# Save instructions
|
|
391
|
+
md_file = template_dir / f"{agent_id}.md"
|
|
392
|
+
md_file.write_text(instructions)
|
|
393
|
+
|
|
394
|
+
self.logger.info(f"Agent saved: {json_file} and {md_file}")
|
|
395
|
+
return True
|
|
396
|
+
|
|
397
|
+
except Exception as e:
|
|
398
|
+
self.logger.error(f"Failed to save agent: {e}")
|
|
399
|
+
return False
|
|
400
|
+
|
|
401
|
+
def _read_agent_summary(self, agent_file: Path, tier: str) -> Dict[str, Any]:
|
|
402
|
+
"""Read agent summary from file."""
|
|
403
|
+
try:
|
|
404
|
+
# For YAML files, extract basic info
|
|
405
|
+
agent_id = agent_file.stem
|
|
406
|
+
return {
|
|
407
|
+
"id": agent_id,
|
|
408
|
+
"name": agent_id.replace("-", " ").title(),
|
|
409
|
+
"tier": tier,
|
|
410
|
+
"file": str(agent_file)
|
|
411
|
+
}
|
|
412
|
+
except Exception:
|
|
413
|
+
return {}
|
|
414
|
+
|
|
415
|
+
def _format_agent_list(self, agents: Dict[str, List]) -> str:
|
|
416
|
+
"""Format agent list for display."""
|
|
417
|
+
output = "=== Agent Hierarchy ===\n\n"
|
|
418
|
+
|
|
419
|
+
# Project agents
|
|
420
|
+
if agents["project"]:
|
|
421
|
+
output += "[P] PROJECT LEVEL (Highest Priority)\n"
|
|
422
|
+
for agent in agents["project"]:
|
|
423
|
+
output += f" {agent['id']:<20} - {agent.get('name', agent['id'])}\n"
|
|
424
|
+
output += "\n"
|
|
425
|
+
|
|
426
|
+
# User agents
|
|
427
|
+
if agents["user"]:
|
|
428
|
+
output += "[U] USER LEVEL\n"
|
|
429
|
+
for agent in agents["user"]:
|
|
430
|
+
output += f" {agent['id']:<20} - {agent.get('name', agent['id'])}\n"
|
|
431
|
+
output += "\n"
|
|
432
|
+
|
|
433
|
+
# System agents
|
|
434
|
+
if agents["system"]:
|
|
435
|
+
output += "[S] SYSTEM LEVEL (Framework Defaults)\n"
|
|
436
|
+
for agent in agents["system"]:
|
|
437
|
+
output += f" {agent['id']:<20} - {agent.get('name', agent['id'])}\n"
|
|
438
|
+
|
|
439
|
+
return output
|
|
440
|
+
|
|
441
|
+
def _find_agent(self, agent_id: str) -> Optional[Dict[str, Any]]:
|
|
442
|
+
"""Find agent across all tiers."""
|
|
443
|
+
# Implementation would search across tiers
|
|
444
|
+
# This is a simplified version
|
|
445
|
+
return {"id": agent_id, "tier": "system"}
|
|
446
|
+
|
|
447
|
+
def _load_agent_config(self, agent_id: str) -> Optional[Dict[str, Any]]:
|
|
448
|
+
"""Load agent configuration."""
|
|
449
|
+
try:
|
|
450
|
+
return self.builder_service._load_template(agent_id)
|
|
451
|
+
except:
|
|
452
|
+
return None
|
|
453
|
+
|
|
454
|
+
def _check_conflicts(self, agent_id: str) -> Optional[str]:
|
|
455
|
+
"""Check for agent conflicts across tiers."""
|
|
456
|
+
# Check if agent exists in multiple tiers
|
|
457
|
+
# Return tier information if conflicts exist
|
|
458
|
+
return None
|
|
459
|
+
|
|
460
|
+
def _format_agent_details(self, agent_info: Dict[str, Any]) -> str:
|
|
461
|
+
"""Format agent details for display."""
|
|
462
|
+
output = f"=== Agent: {agent_info['id']} ===\n\n"
|
|
463
|
+
for key, value in agent_info.items():
|
|
464
|
+
output += f"{key}: {value}\n"
|
|
465
|
+
return output
|
|
466
|
+
|
|
467
|
+
def _load_pm_template(self, template_name: str) -> str:
|
|
468
|
+
"""Load PM instruction template."""
|
|
469
|
+
# Load predefined PM templates
|
|
470
|
+
return "# PM Instructions Template\n"
|
|
471
|
+
|
|
472
|
+
def _show_help(self) -> CommandResult:
|
|
473
|
+
"""Show help for agent manager."""
|
|
474
|
+
help_text = """
|
|
475
|
+
Agent Manager - Comprehensive Agent Lifecycle Management
|
|
476
|
+
|
|
477
|
+
Commands:
|
|
478
|
+
list List all agents across tiers with hierarchy
|
|
479
|
+
create Create a new agent (interactive or with arguments)
|
|
480
|
+
variant Create an agent variant based on existing agent
|
|
481
|
+
deploy Deploy agent to project or user tier
|
|
482
|
+
customize-pm Customize PM instructions at user or project level
|
|
483
|
+
show Display detailed agent information
|
|
484
|
+
test Validate agent configuration
|
|
485
|
+
templates List available agent templates
|
|
486
|
+
|
|
487
|
+
Examples:
|
|
488
|
+
claude-mpm agent-manager list
|
|
489
|
+
claude-mpm agent-manager create --id my-agent --name "My Agent"
|
|
490
|
+
claude-mpm agent-manager variant --base research --id research-v2
|
|
491
|
+
claude-mpm agent-manager deploy --agent-id my-agent --tier user
|
|
492
|
+
claude-mpm agent-manager customize-pm --level project
|
|
493
|
+
"""
|
|
494
|
+
return CommandResult.success_result(help_text)
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
# Module-level function for CLI integration
|
|
498
|
+
def manage_agent_manager(args) -> int:
|
|
499
|
+
"""Entry point for agent-manager command from CLI.
|
|
500
|
+
|
|
501
|
+
Args:
|
|
502
|
+
args: Parsed command line arguments
|
|
503
|
+
|
|
504
|
+
Returns:
|
|
505
|
+
Exit code (0 for success, non-zero for failure)
|
|
506
|
+
"""
|
|
507
|
+
command = AgentManagerCommand()
|
|
508
|
+
result = command.run(args)
|
|
509
|
+
|
|
510
|
+
if result.success:
|
|
511
|
+
if result.message:
|
|
512
|
+
print(result.message)
|
|
513
|
+
return 0
|
|
514
|
+
else:
|
|
515
|
+
if result.message:
|
|
516
|
+
print(f"Error: {result.message}", file=sys.stderr)
|
|
517
|
+
return 1
|
|
@@ -23,7 +23,7 @@ from ...core.config import Config
|
|
|
23
23
|
from ...core.logger import get_logger
|
|
24
24
|
from ...core.shared.config_loader import ConfigLoader
|
|
25
25
|
from ...services.agents.memory import AgentMemoryManager
|
|
26
|
-
from ..shared.
|
|
26
|
+
from ..shared.base_command import MemoryCommand, CommandResult
|
|
27
27
|
from ..shared.argument_patterns import add_memory_arguments, add_output_arguments
|
|
28
28
|
|
|
29
29
|
|