claude-mpm 4.1.2__py3-none-any.whl → 4.1.3__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 +1 -1
- claude_mpm/agents/templates/engineer.json +33 -11
- claude_mpm/cli/commands/agents.py +556 -1009
- claude_mpm/cli/commands/memory.py +248 -927
- claude_mpm/cli/commands/run.py +139 -484
- claude_mpm/cli/startup_logging.py +76 -0
- claude_mpm/core/agent_registry.py +6 -10
- claude_mpm/core/framework_loader.py +114 -595
- claude_mpm/core/logging_config.py +2 -4
- claude_mpm/hooks/claude_hooks/event_handlers.py +7 -117
- claude_mpm/hooks/claude_hooks/hook_handler.py +91 -755
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
- claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
- claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +42 -454
- claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
- claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
- claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
- claude_mpm/services/agents/memory/agent_memory_manager.py +42 -508
- claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
- claude_mpm/services/agents/memory/memory_file_service.py +103 -0
- claude_mpm/services/agents/memory/memory_format_service.py +201 -0
- claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
- claude_mpm/services/agents/registry/__init__.py +1 -1
- claude_mpm/services/cli/__init__.py +18 -0
- claude_mpm/services/cli/agent_cleanup_service.py +407 -0
- claude_mpm/services/cli/agent_dependency_service.py +395 -0
- claude_mpm/services/cli/agent_listing_service.py +463 -0
- claude_mpm/services/cli/agent_output_formatter.py +605 -0
- claude_mpm/services/cli/agent_validation_service.py +589 -0
- claude_mpm/services/cli/dashboard_launcher.py +424 -0
- claude_mpm/services/cli/memory_crud_service.py +617 -0
- claude_mpm/services/cli/memory_output_formatter.py +604 -0
- claude_mpm/services/cli/session_manager.py +513 -0
- claude_mpm/services/cli/socketio_manager.py +498 -0
- claude_mpm/services/cli/startup_checker.py +370 -0
- claude_mpm/services/core/cache_manager.py +311 -0
- claude_mpm/services/core/memory_manager.py +637 -0
- claude_mpm/services/core/path_resolver.py +498 -0
- claude_mpm/services/core/service_container.py +520 -0
- claude_mpm/services/core/service_interfaces.py +436 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +65 -19
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/METADATA +1 -1
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/RECORD +52 -22
- claude_mpm/cli/commands/run_config_checker.py +0 -159
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/top_level.txt +0 -0
|
@@ -12,16 +12,13 @@ DESIGN DECISIONS:
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
import json
|
|
15
|
-
import os
|
|
16
|
-
from pathlib import Path
|
|
17
15
|
|
|
18
|
-
import yaml
|
|
19
|
-
|
|
20
|
-
from ...agents.frontmatter_validator import FrontmatterValidator
|
|
21
16
|
from ...constants import AgentCommands
|
|
22
|
-
from ...
|
|
23
|
-
from ...
|
|
24
|
-
from ...
|
|
17
|
+
from ...services.cli.agent_cleanup_service import AgentCleanupService
|
|
18
|
+
from ...services.cli.agent_dependency_service import AgentDependencyService
|
|
19
|
+
from ...services.cli.agent_listing_service import AgentListingService
|
|
20
|
+
from ...services.cli.agent_output_formatter import AgentOutputFormatter
|
|
21
|
+
from ...services.cli.agent_validation_service import AgentValidationService
|
|
25
22
|
from ..shared import (
|
|
26
23
|
AgentCommand,
|
|
27
24
|
CommandResult,
|
|
@@ -35,6 +32,11 @@ class AgentsCommand(AgentCommand):
|
|
|
35
32
|
def __init__(self):
|
|
36
33
|
super().__init__("agents")
|
|
37
34
|
self._deployment_service = None
|
|
35
|
+
self._listing_service = None
|
|
36
|
+
self._validation_service = None
|
|
37
|
+
self._dependency_service = None
|
|
38
|
+
self._cleanup_service = None
|
|
39
|
+
self._formatter = AgentOutputFormatter()
|
|
38
40
|
|
|
39
41
|
@property
|
|
40
42
|
def deployment_service(self):
|
|
@@ -52,6 +54,38 @@ class AgentsCommand(AgentCommand):
|
|
|
52
54
|
raise ImportError("Agent deployment service not available")
|
|
53
55
|
return self._deployment_service
|
|
54
56
|
|
|
57
|
+
@property
|
|
58
|
+
def listing_service(self):
|
|
59
|
+
"""Get listing service instance (lazy loaded)."""
|
|
60
|
+
if self._listing_service is None:
|
|
61
|
+
self._listing_service = AgentListingService(
|
|
62
|
+
deployment_service=self.deployment_service
|
|
63
|
+
)
|
|
64
|
+
return self._listing_service
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def validation_service(self):
|
|
68
|
+
"""Get validation service instance (lazy loaded)."""
|
|
69
|
+
if self._validation_service is None:
|
|
70
|
+
self._validation_service = AgentValidationService()
|
|
71
|
+
return self._validation_service
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def dependency_service(self):
|
|
75
|
+
"""Get dependency service instance (lazy loaded)."""
|
|
76
|
+
if self._dependency_service is None:
|
|
77
|
+
self._dependency_service = AgentDependencyService()
|
|
78
|
+
return self._dependency_service
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def cleanup_service(self):
|
|
82
|
+
"""Get cleanup service instance (lazy loaded)."""
|
|
83
|
+
if self._cleanup_service is None:
|
|
84
|
+
self._cleanup_service = AgentCleanupService(
|
|
85
|
+
deployment_service=self.deployment_service
|
|
86
|
+
)
|
|
87
|
+
return self._cleanup_service
|
|
88
|
+
|
|
55
89
|
def validate_args(self, args) -> str:
|
|
56
90
|
"""Validate command arguments."""
|
|
57
91
|
# Most agent commands are optional, so basic validation
|
|
@@ -106,6 +140,12 @@ class AgentsCommand(AgentCommand):
|
|
|
106
140
|
# Parse the agent versions display into structured data
|
|
107
141
|
if agent_versions:
|
|
108
142
|
data = {"agent_versions": agent_versions, "has_agents": True}
|
|
143
|
+
formatted = (
|
|
144
|
+
self._formatter.format_as_json(data)
|
|
145
|
+
if output_format == "json"
|
|
146
|
+
else self._formatter.format_as_yaml(data)
|
|
147
|
+
)
|
|
148
|
+
print(formatted)
|
|
109
149
|
return CommandResult.success_result(
|
|
110
150
|
"Agent versions retrieved", data=data
|
|
111
151
|
)
|
|
@@ -114,6 +154,12 @@ class AgentsCommand(AgentCommand):
|
|
|
114
154
|
"has_agents": False,
|
|
115
155
|
"suggestion": "To deploy agents, run: claude-mpm --mpm:agents deploy",
|
|
116
156
|
}
|
|
157
|
+
formatted = (
|
|
158
|
+
self._formatter.format_as_json(data)
|
|
159
|
+
if output_format == "json"
|
|
160
|
+
else self._formatter.format_as_yaml(data)
|
|
161
|
+
)
|
|
162
|
+
print(formatted)
|
|
117
163
|
return CommandResult.success_result(
|
|
118
164
|
"No deployed agents found", data=data
|
|
119
165
|
)
|
|
@@ -161,31 +207,34 @@ class AgentsCommand(AgentCommand):
|
|
|
161
207
|
def _list_system_agents(self, args) -> CommandResult:
|
|
162
208
|
"""List available agent templates."""
|
|
163
209
|
try:
|
|
164
|
-
|
|
210
|
+
verbose = getattr(args, "verbose", False)
|
|
211
|
+
agents = self.listing_service.list_system_agents(verbose=verbose)
|
|
212
|
+
|
|
165
213
|
output_format = getattr(args, "format", "text")
|
|
214
|
+
quiet = getattr(args, "quiet", False)
|
|
166
215
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if "version" in agent:
|
|
185
|
-
print(f" Version: {agent['version']}")
|
|
186
|
-
print()
|
|
216
|
+
# Convert AgentInfo objects to dicts for formatter
|
|
217
|
+
agents_data = [
|
|
218
|
+
{
|
|
219
|
+
"name": agent.name,
|
|
220
|
+
"type": agent.type,
|
|
221
|
+
"path": agent.path,
|
|
222
|
+
"description": agent.description,
|
|
223
|
+
"specializations": agent.specializations,
|
|
224
|
+
"version": agent.version,
|
|
225
|
+
}
|
|
226
|
+
for agent in agents
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
formatted = self._formatter.format_agent_list(
|
|
230
|
+
agents_data, output_format=output_format, verbose=verbose, quiet=quiet
|
|
231
|
+
)
|
|
232
|
+
print(formatted)
|
|
187
233
|
|
|
188
|
-
return CommandResult.success_result(
|
|
234
|
+
return CommandResult.success_result(
|
|
235
|
+
f"Listed {len(agents)} agent templates",
|
|
236
|
+
data={"agents": agents_data, "count": len(agents)},
|
|
237
|
+
)
|
|
189
238
|
|
|
190
239
|
except Exception as e:
|
|
191
240
|
self.logger.error(f"Error listing system agents: {e}", exc_info=True)
|
|
@@ -194,39 +243,47 @@ class AgentsCommand(AgentCommand):
|
|
|
194
243
|
def _list_deployed_agents(self, args) -> CommandResult:
|
|
195
244
|
"""List deployed agents."""
|
|
196
245
|
try:
|
|
197
|
-
|
|
246
|
+
verbose = getattr(args, "verbose", False)
|
|
247
|
+
agents, warnings = self.listing_service.list_deployed_agents(
|
|
248
|
+
verbose=verbose
|
|
249
|
+
)
|
|
250
|
+
|
|
198
251
|
output_format = getattr(args, "format", "text")
|
|
252
|
+
quiet = getattr(args, "quiet", False)
|
|
199
253
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if "path" in agent:
|
|
220
|
-
print(f" Path: {agent['path']}")
|
|
221
|
-
print()
|
|
254
|
+
# Convert AgentInfo objects to dicts for formatter
|
|
255
|
+
agents_data = [
|
|
256
|
+
{
|
|
257
|
+
"name": agent.name,
|
|
258
|
+
"type": agent.type,
|
|
259
|
+
"tier": agent.tier,
|
|
260
|
+
"path": agent.path,
|
|
261
|
+
"description": agent.description,
|
|
262
|
+
"specializations": agent.specializations,
|
|
263
|
+
"version": agent.version,
|
|
264
|
+
}
|
|
265
|
+
for agent in agents
|
|
266
|
+
]
|
|
267
|
+
|
|
268
|
+
# Format the agent list
|
|
269
|
+
formatted = self._formatter.format_agent_list(
|
|
270
|
+
agents_data, output_format=output_format, verbose=verbose, quiet=quiet
|
|
271
|
+
)
|
|
272
|
+
print(formatted)
|
|
222
273
|
|
|
223
|
-
|
|
274
|
+
# Add warnings for text output
|
|
275
|
+
if output_format == "text" and warnings:
|
|
224
276
|
print("\nWarnings:")
|
|
225
|
-
for warning in
|
|
277
|
+
for warning in warnings:
|
|
226
278
|
print(f" ⚠️ {warning}")
|
|
227
279
|
|
|
228
280
|
return CommandResult.success_result(
|
|
229
|
-
f"Listed {len(
|
|
281
|
+
f"Listed {len(agents)} deployed agents",
|
|
282
|
+
data={
|
|
283
|
+
"agents": agents_data,
|
|
284
|
+
"warnings": warnings,
|
|
285
|
+
"count": len(agents),
|
|
286
|
+
},
|
|
230
287
|
)
|
|
231
288
|
|
|
232
289
|
except Exception as e:
|
|
@@ -236,27 +293,58 @@ class AgentsCommand(AgentCommand):
|
|
|
236
293
|
def _list_agents_by_tier(self, args) -> CommandResult:
|
|
237
294
|
"""List agents grouped by tier/precedence."""
|
|
238
295
|
try:
|
|
239
|
-
|
|
296
|
+
tier_info = self.listing_service.list_agents_by_tier()
|
|
240
297
|
output_format = getattr(args, "format", "text")
|
|
241
298
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
299
|
+
# Convert to format expected by formatter
|
|
300
|
+
agents_by_tier = {
|
|
301
|
+
"project": [
|
|
302
|
+
{
|
|
303
|
+
"name": agent.name,
|
|
304
|
+
"type": agent.type,
|
|
305
|
+
"path": agent.path,
|
|
306
|
+
"active": agent.active,
|
|
307
|
+
"overridden_by": agent.overridden_by,
|
|
308
|
+
}
|
|
309
|
+
for agent in tier_info.project
|
|
310
|
+
],
|
|
311
|
+
"user": [
|
|
312
|
+
{
|
|
313
|
+
"name": agent.name,
|
|
314
|
+
"type": agent.type,
|
|
315
|
+
"path": agent.path,
|
|
316
|
+
"active": agent.active,
|
|
317
|
+
"overridden_by": agent.overridden_by,
|
|
318
|
+
}
|
|
319
|
+
for agent in tier_info.user
|
|
320
|
+
],
|
|
321
|
+
"system": [
|
|
322
|
+
{
|
|
323
|
+
"name": agent.name,
|
|
324
|
+
"type": agent.type,
|
|
325
|
+
"path": agent.path,
|
|
326
|
+
"active": agent.active,
|
|
327
|
+
"overridden_by": agent.overridden_by,
|
|
328
|
+
}
|
|
329
|
+
for agent in tier_info.system
|
|
330
|
+
],
|
|
331
|
+
"summary": {
|
|
332
|
+
"total_count": tier_info.total_count,
|
|
333
|
+
"active_count": tier_info.active_count,
|
|
334
|
+
"project_count": len(tier_info.project),
|
|
335
|
+
"user_count": len(tier_info.user),
|
|
336
|
+
"system_count": len(tier_info.system),
|
|
337
|
+
},
|
|
338
|
+
}
|
|
258
339
|
|
|
259
|
-
|
|
340
|
+
formatted = self._formatter.format_agents_by_tier(
|
|
341
|
+
agents_by_tier, output_format=output_format
|
|
342
|
+
)
|
|
343
|
+
print(formatted)
|
|
344
|
+
|
|
345
|
+
return CommandResult.success_result(
|
|
346
|
+
"Agents listed by tier", data=agents_by_tier
|
|
347
|
+
)
|
|
260
348
|
|
|
261
349
|
except Exception as e:
|
|
262
350
|
self.logger.error(f"Error listing agents by tier: {e}", exc_info=True)
|
|
@@ -272,30 +360,39 @@ class AgentsCommand(AgentCommand):
|
|
|
272
360
|
project_result = self.deployment_service.deploy_project_agents(force=force)
|
|
273
361
|
|
|
274
362
|
# Combine results
|
|
275
|
-
|
|
276
|
-
"deployed_count", 0
|
|
277
|
-
|
|
363
|
+
combined_result = {
|
|
364
|
+
"deployed_count": system_result.get("deployed_count", 0)
|
|
365
|
+
+ project_result.get("deployed_count", 0),
|
|
366
|
+
"deployed": system_result.get("deployed", [])
|
|
367
|
+
+ project_result.get("deployed", []),
|
|
368
|
+
"updated_count": system_result.get("updated_count", 0)
|
|
369
|
+
+ project_result.get("updated_count", 0),
|
|
370
|
+
"updated": system_result.get("updated", [])
|
|
371
|
+
+ project_result.get("updated", []),
|
|
372
|
+
"skipped": system_result.get("skipped", [])
|
|
373
|
+
+ project_result.get("skipped", []),
|
|
374
|
+
"errors": system_result.get("errors", [])
|
|
375
|
+
+ project_result.get("errors", []),
|
|
376
|
+
"target_dir": system_result.get("target_dir")
|
|
377
|
+
or project_result.get("target_dir"),
|
|
378
|
+
}
|
|
278
379
|
|
|
279
380
|
output_format = getattr(args, "format", "text")
|
|
280
|
-
|
|
281
|
-
return CommandResult.success_result(
|
|
282
|
-
f"Deployed {total_deployed} agents",
|
|
283
|
-
data={
|
|
284
|
-
"system_agents": system_result,
|
|
285
|
-
"project_agents": project_result,
|
|
286
|
-
"total_deployed": total_deployed,
|
|
287
|
-
},
|
|
288
|
-
)
|
|
289
|
-
# Text output
|
|
290
|
-
if system_result.get("deployed_count", 0) > 0:
|
|
291
|
-
print(f"✓ Deployed {system_result['deployed_count']} system agents")
|
|
292
|
-
if project_result.get("deployed_count", 0) > 0:
|
|
293
|
-
print(f"✓ Deployed {project_result['deployed_count']} project agents")
|
|
381
|
+
verbose = getattr(args, "verbose", False)
|
|
294
382
|
|
|
295
|
-
|
|
296
|
-
|
|
383
|
+
formatted = self._formatter.format_deployment_result(
|
|
384
|
+
combined_result, output_format=output_format, verbose=verbose
|
|
385
|
+
)
|
|
386
|
+
print(formatted)
|
|
297
387
|
|
|
298
|
-
return CommandResult.success_result(
|
|
388
|
+
return CommandResult.success_result(
|
|
389
|
+
f"Deployed {combined_result['deployed_count']} agents",
|
|
390
|
+
data={
|
|
391
|
+
"system_agents": system_result,
|
|
392
|
+
"project_agents": project_result,
|
|
393
|
+
"total_deployed": combined_result["deployed_count"],
|
|
394
|
+
},
|
|
395
|
+
)
|
|
299
396
|
|
|
300
397
|
except Exception as e:
|
|
301
398
|
self.logger.error(f"Error deploying agents: {e}", exc_info=True)
|
|
@@ -304,21 +401,20 @@ class AgentsCommand(AgentCommand):
|
|
|
304
401
|
def _clean_agents(self, args) -> CommandResult:
|
|
305
402
|
"""Clean deployed agents."""
|
|
306
403
|
try:
|
|
307
|
-
result = self.
|
|
404
|
+
result = self.cleanup_service.clean_deployed_agents()
|
|
308
405
|
|
|
309
406
|
output_format = getattr(args, "format", "text")
|
|
310
|
-
|
|
311
|
-
return CommandResult.success_result(
|
|
312
|
-
f"Cleaned {result.get('cleaned_count', 0)} agents", data=result
|
|
313
|
-
)
|
|
314
|
-
# Text output
|
|
315
|
-
cleaned_count = result.get("cleaned_count", 0)
|
|
316
|
-
if cleaned_count > 0:
|
|
317
|
-
print(f"✓ Cleaned {cleaned_count} deployed agents")
|
|
318
|
-
else:
|
|
319
|
-
print("No deployed agents to clean")
|
|
407
|
+
dry_run = False # Regular clean is not a dry run
|
|
320
408
|
|
|
321
|
-
|
|
409
|
+
formatted = self._formatter.format_cleanup_result(
|
|
410
|
+
result, output_format=output_format, dry_run=dry_run
|
|
411
|
+
)
|
|
412
|
+
print(formatted)
|
|
413
|
+
|
|
414
|
+
cleaned_count = result.get("cleaned_count", 0)
|
|
415
|
+
return CommandResult.success_result(
|
|
416
|
+
f"Cleaned {cleaned_count} agents", data=result
|
|
417
|
+
)
|
|
322
418
|
|
|
323
419
|
except Exception as e:
|
|
324
420
|
self.logger.error(f"Error cleaning agents: {e}", exc_info=True)
|
|
@@ -333,43 +429,178 @@ class AgentsCommand(AgentCommand):
|
|
|
333
429
|
"Agent name is required for view command"
|
|
334
430
|
)
|
|
335
431
|
|
|
336
|
-
# Get agent details from
|
|
337
|
-
agent_details = self.
|
|
432
|
+
# Get agent details from listing service
|
|
433
|
+
agent_details = self.listing_service.get_agent_details(agent_name)
|
|
338
434
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
435
|
+
if not agent_details:
|
|
436
|
+
# Try to find the agent to provide helpful error message
|
|
437
|
+
agent = self.listing_service.find_agent(agent_name)
|
|
438
|
+
if not agent:
|
|
439
|
+
return CommandResult.error_result(f"Agent '{agent_name}' not found")
|
|
440
|
+
return CommandResult.error_result(
|
|
441
|
+
f"Could not retrieve details for agent '{agent_name}'"
|
|
343
442
|
)
|
|
344
|
-
# Text output
|
|
345
|
-
print(f"Agent: {agent_name}")
|
|
346
|
-
print("-" * 40)
|
|
347
|
-
for key, value in agent_details.items():
|
|
348
|
-
print(f"{key}: {value}")
|
|
349
443
|
|
|
350
|
-
|
|
444
|
+
output_format = getattr(args, "format", "text")
|
|
445
|
+
verbose = getattr(args, "verbose", False)
|
|
446
|
+
|
|
447
|
+
formatted = self._formatter.format_agent_details(
|
|
448
|
+
agent_details, output_format=output_format, verbose=verbose
|
|
449
|
+
)
|
|
450
|
+
print(formatted)
|
|
451
|
+
|
|
452
|
+
return CommandResult.success_result(
|
|
453
|
+
f"Displayed details for {agent_name}", data=agent_details
|
|
454
|
+
)
|
|
351
455
|
|
|
352
456
|
except Exception as e:
|
|
353
457
|
self.logger.error(f"Error viewing agent: {e}", exc_info=True)
|
|
354
458
|
return CommandResult.error_result(f"Error viewing agent: {e}")
|
|
355
459
|
|
|
356
460
|
def _fix_agents(self, args) -> CommandResult:
|
|
357
|
-
"""Fix agent
|
|
461
|
+
"""Fix agent frontmatter issues using validation service."""
|
|
358
462
|
try:
|
|
359
|
-
|
|
463
|
+
dry_run = getattr(args, "dry_run", False)
|
|
464
|
+
agent_name = getattr(args, "agent_name", None)
|
|
465
|
+
fix_all = getattr(args, "all", False)
|
|
360
466
|
|
|
361
467
|
output_format = getattr(args, "format", "text")
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
468
|
+
|
|
469
|
+
# Determine what to fix
|
|
470
|
+
if fix_all:
|
|
471
|
+
# Fix all agents
|
|
472
|
+
result = self.validation_service.fix_all_agents(dry_run=dry_run)
|
|
473
|
+
|
|
474
|
+
if output_format in ["json", "yaml"]:
|
|
475
|
+
formatted = (
|
|
476
|
+
self._formatter.format_as_json(result)
|
|
477
|
+
if output_format == "json"
|
|
478
|
+
else self._formatter.format_as_yaml(result)
|
|
479
|
+
)
|
|
480
|
+
print(formatted)
|
|
481
|
+
else:
|
|
482
|
+
# Text output
|
|
483
|
+
mode = "DRY RUN" if dry_run else "FIX"
|
|
484
|
+
print(
|
|
485
|
+
f"\n🔧 {mode}: Checking {result.get('total_agents', 0)} agent(s) for frontmatter issues...\n"
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
if result.get("results"):
|
|
489
|
+
for agent_result in result["results"]:
|
|
490
|
+
print(f"📄 {agent_result['agent']}:")
|
|
491
|
+
if agent_result.get("skipped"):
|
|
492
|
+
print(
|
|
493
|
+
f" ⚠️ Skipped: {agent_result.get('reason', 'Unknown reason')}"
|
|
494
|
+
)
|
|
495
|
+
elif agent_result.get("was_valid"):
|
|
496
|
+
print(" ✓ No issues found")
|
|
497
|
+
else:
|
|
498
|
+
if agent_result.get("errors_found", 0) > 0:
|
|
499
|
+
print(
|
|
500
|
+
f" ❌ Errors found: {agent_result['errors_found']}"
|
|
501
|
+
)
|
|
502
|
+
if agent_result.get("warnings_found", 0) > 0:
|
|
503
|
+
print(
|
|
504
|
+
f" ⚠️ Warnings found: {agent_result['warnings_found']}"
|
|
505
|
+
)
|
|
506
|
+
if dry_run:
|
|
507
|
+
if agent_result.get("corrections_available", 0) > 0:
|
|
508
|
+
print(
|
|
509
|
+
f" 🔧 Would fix: {agent_result['corrections_available']} issues"
|
|
510
|
+
)
|
|
511
|
+
elif agent_result.get("corrections_made", 0) > 0:
|
|
512
|
+
print(
|
|
513
|
+
f" ✓ Fixed: {agent_result['corrections_made']} issues"
|
|
514
|
+
)
|
|
515
|
+
print()
|
|
516
|
+
|
|
517
|
+
# Summary
|
|
518
|
+
print("=" * 80)
|
|
519
|
+
print("SUMMARY:")
|
|
520
|
+
print(f" Agents checked: {result.get('agents_checked', 0)}")
|
|
521
|
+
print(
|
|
522
|
+
f" Total issues found: {result.get('total_issues_found', 0)}"
|
|
523
|
+
)
|
|
524
|
+
if dry_run:
|
|
525
|
+
print(
|
|
526
|
+
f" Issues that would be fixed: {result.get('total_corrections_available', 0)}"
|
|
527
|
+
)
|
|
528
|
+
print("\n💡 Run without --dry-run to apply fixes")
|
|
529
|
+
else:
|
|
530
|
+
print(
|
|
531
|
+
f" Issues fixed: {result.get('total_corrections_made', 0)}"
|
|
532
|
+
)
|
|
533
|
+
if result.get("total_corrections_made", 0) > 0:
|
|
534
|
+
print("\n✓ Frontmatter issues have been fixed!")
|
|
535
|
+
print("=" * 80 + "\n")
|
|
536
|
+
|
|
537
|
+
msg = f"{'Would fix' if dry_run else 'Fixed'} {result.get('total_corrections_available' if dry_run else 'total_corrections_made', 0)} issues"
|
|
538
|
+
return CommandResult.success_result(msg, data=result)
|
|
539
|
+
|
|
540
|
+
if agent_name:
|
|
541
|
+
# Fix specific agent
|
|
542
|
+
result = self.validation_service.fix_agent_frontmatter(
|
|
543
|
+
agent_name, dry_run=dry_run
|
|
365
544
|
)
|
|
366
|
-
# Text output
|
|
367
|
-
print("✓ Agent deployment issues fixed")
|
|
368
|
-
if result.get("fixes_applied"):
|
|
369
|
-
for fix in result["fixes_applied"]:
|
|
370
|
-
print(f" - {fix}")
|
|
371
545
|
|
|
372
|
-
|
|
546
|
+
if not result.get("success"):
|
|
547
|
+
return CommandResult.error_result(
|
|
548
|
+
result.get("error", "Failed to fix agent")
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
if output_format in ["json", "yaml"]:
|
|
552
|
+
formatted = (
|
|
553
|
+
self._formatter.format_as_json(result)
|
|
554
|
+
if output_format == "json"
|
|
555
|
+
else self._formatter.format_as_yaml(result)
|
|
556
|
+
)
|
|
557
|
+
print(formatted)
|
|
558
|
+
else:
|
|
559
|
+
# Text output
|
|
560
|
+
mode = "DRY RUN" if dry_run else "FIX"
|
|
561
|
+
print(
|
|
562
|
+
f"\n🔧 {mode}: Checking agent '{agent_name}' for frontmatter issues...\n"
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
print(f"📄 {agent_name}:")
|
|
566
|
+
if result.get("was_valid"):
|
|
567
|
+
print(" ✓ No issues found")
|
|
568
|
+
else:
|
|
569
|
+
if result.get("errors_found"):
|
|
570
|
+
print(" ❌ Errors:")
|
|
571
|
+
for error in result["errors_found"]:
|
|
572
|
+
print(f" - {error}")
|
|
573
|
+
if result.get("warnings_found"):
|
|
574
|
+
print(" ⚠️ Warnings:")
|
|
575
|
+
for warning in result["warnings_found"]:
|
|
576
|
+
print(f" - {warning}")
|
|
577
|
+
if dry_run:
|
|
578
|
+
if result.get("corrections_available"):
|
|
579
|
+
print(" 🔧 Would fix:")
|
|
580
|
+
for correction in result["corrections_available"]:
|
|
581
|
+
print(f" - {correction}")
|
|
582
|
+
elif result.get("corrections_made"):
|
|
583
|
+
print(" ✓ Fixed:")
|
|
584
|
+
for correction in result["corrections_made"]:
|
|
585
|
+
print(f" - {correction}")
|
|
586
|
+
print()
|
|
587
|
+
|
|
588
|
+
if dry_run and result.get("corrections_available"):
|
|
589
|
+
print("💡 Run without --dry-run to apply fixes\n")
|
|
590
|
+
elif not dry_run and result.get("corrections_made"):
|
|
591
|
+
print("✓ Frontmatter issues have been fixed!\n")
|
|
592
|
+
|
|
593
|
+
msg = f"{'Would fix' if dry_run else 'Fixed'} agent '{agent_name}'"
|
|
594
|
+
return CommandResult.success_result(msg, data=result)
|
|
595
|
+
|
|
596
|
+
# No agent specified and not --all
|
|
597
|
+
usage_msg = "Please specify an agent name or use --all to fix all agents\nUsage: claude-mpm agents fix [agent_name] [--dry-run] [--all]"
|
|
598
|
+
if output_format in ["json", "yaml"]:
|
|
599
|
+
return CommandResult.error_result(
|
|
600
|
+
"No agent specified", data={"usage": usage_msg}
|
|
601
|
+
)
|
|
602
|
+
print(f"❌ {usage_msg}")
|
|
603
|
+
return CommandResult.error_result("No agent specified")
|
|
373
604
|
|
|
374
605
|
except Exception as e:
|
|
375
606
|
self.logger.error(f"Error fixing agents: {e}", exc_info=True)
|
|
@@ -378,24 +609,25 @@ class AgentsCommand(AgentCommand):
|
|
|
378
609
|
def _check_agent_dependencies(self, args) -> CommandResult:
|
|
379
610
|
"""Check agent dependencies."""
|
|
380
611
|
try:
|
|
381
|
-
|
|
612
|
+
agent_name = getattr(args, "agent", None)
|
|
613
|
+
result = self.dependency_service.check_dependencies(agent_name=agent_name)
|
|
382
614
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
615
|
+
if not result["success"]:
|
|
616
|
+
if "available_agents" in result:
|
|
617
|
+
print(f"❌ Agent '{agent_name}' is not deployed")
|
|
618
|
+
print(
|
|
619
|
+
f" Available agents: {', '.join(result['available_agents'])}"
|
|
620
|
+
)
|
|
621
|
+
return CommandResult.error_result(
|
|
622
|
+
result.get("error", "Dependency check failed")
|
|
387
623
|
)
|
|
388
|
-
# Text output
|
|
389
|
-
print("Agent Dependencies Check:")
|
|
390
|
-
print("-" * 40)
|
|
391
|
-
if result.get("missing_dependencies"):
|
|
392
|
-
print("Missing dependencies:")
|
|
393
|
-
for dep in result["missing_dependencies"]:
|
|
394
|
-
print(f" - {dep}")
|
|
395
|
-
else:
|
|
396
|
-
print("✓ All dependencies satisfied")
|
|
397
624
|
|
|
398
|
-
|
|
625
|
+
# Print the formatted report
|
|
626
|
+
print(result["report"])
|
|
627
|
+
|
|
628
|
+
return CommandResult.success_result(
|
|
629
|
+
"Dependency check completed", data=result
|
|
630
|
+
)
|
|
399
631
|
|
|
400
632
|
except Exception as e:
|
|
401
633
|
self.logger.error(f"Error checking dependencies: {e}", exc_info=True)
|
|
@@ -404,23 +636,45 @@ class AgentsCommand(AgentCommand):
|
|
|
404
636
|
def _install_agent_dependencies(self, args) -> CommandResult:
|
|
405
637
|
"""Install agent dependencies."""
|
|
406
638
|
try:
|
|
407
|
-
|
|
639
|
+
agent_name = getattr(args, "agent", None)
|
|
640
|
+
dry_run = getattr(args, "dry_run", False)
|
|
641
|
+
result = self.dependency_service.install_dependencies(
|
|
642
|
+
agent_name=agent_name, dry_run=dry_run
|
|
643
|
+
)
|
|
408
644
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
645
|
+
if not result["success"]:
|
|
646
|
+
if "available_agents" in result:
|
|
647
|
+
print(f"❌ Agent '{agent_name}' is not deployed")
|
|
648
|
+
print(
|
|
649
|
+
f" Available agents: {', '.join(result['available_agents'])}"
|
|
650
|
+
)
|
|
651
|
+
return CommandResult.error_result(
|
|
652
|
+
result.get("error", "Installation failed")
|
|
414
653
|
)
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
654
|
+
|
|
655
|
+
if result.get("missing_count") == 0:
|
|
656
|
+
print("✅ All Python dependencies are already installed")
|
|
657
|
+
elif dry_run:
|
|
658
|
+
print(
|
|
659
|
+
f"Found {len(result['missing_dependencies'])} missing dependencies:"
|
|
660
|
+
)
|
|
661
|
+
for dep in result["missing_dependencies"]:
|
|
662
|
+
print(f" - {dep}")
|
|
663
|
+
print("\n--dry-run specified, not installing anything")
|
|
664
|
+
print(f"Would install: {result['install_command']}")
|
|
419
665
|
else:
|
|
420
|
-
print(
|
|
666
|
+
print(
|
|
667
|
+
f"✅ Successfully installed {len(result.get('installed', []))} dependencies"
|
|
668
|
+
)
|
|
669
|
+
if result.get("still_missing"):
|
|
670
|
+
print(
|
|
671
|
+
f"⚠️ {len(result['still_missing'])} dependencies still missing after installation"
|
|
672
|
+
)
|
|
673
|
+
elif result.get("fully_resolved"):
|
|
674
|
+
print("✅ All dependencies verified after installation")
|
|
421
675
|
|
|
422
676
|
return CommandResult.success_result(
|
|
423
|
-
|
|
677
|
+
"Dependency installation completed", data=result
|
|
424
678
|
)
|
|
425
679
|
|
|
426
680
|
except Exception as e:
|
|
@@ -430,27 +684,57 @@ class AgentsCommand(AgentCommand):
|
|
|
430
684
|
def _list_agent_dependencies(self, args) -> CommandResult:
|
|
431
685
|
"""List agent dependencies."""
|
|
432
686
|
try:
|
|
433
|
-
result = self.deployment_service.list_dependencies()
|
|
434
|
-
|
|
435
687
|
output_format = getattr(args, "format", "text")
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
else:
|
|
450
|
-
print("
|
|
688
|
+
result = self.dependency_service.list_dependencies(
|
|
689
|
+
format_type=output_format
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
if not result["success"]:
|
|
693
|
+
return CommandResult.error_result(result.get("error", "Listing failed"))
|
|
694
|
+
|
|
695
|
+
# Format output based on requested format
|
|
696
|
+
if output_format == "pip":
|
|
697
|
+
for dep in result["dependencies"]:
|
|
698
|
+
print(dep)
|
|
699
|
+
elif output_format == "json":
|
|
700
|
+
print(json.dumps(result["data"], indent=2))
|
|
701
|
+
else: # text format
|
|
702
|
+
print("=" * 60)
|
|
703
|
+
print("DEPENDENCIES FROM DEPLOYED AGENTS")
|
|
704
|
+
print("=" * 60)
|
|
705
|
+
print()
|
|
706
|
+
|
|
707
|
+
if result["python_dependencies"]:
|
|
708
|
+
print(
|
|
709
|
+
f"Python Dependencies ({len(result['python_dependencies'])}):"
|
|
710
|
+
)
|
|
711
|
+
print("-" * 30)
|
|
712
|
+
for dep in result["python_dependencies"]:
|
|
713
|
+
print(f" {dep}")
|
|
714
|
+
print()
|
|
715
|
+
|
|
716
|
+
if result["system_dependencies"]:
|
|
717
|
+
print(
|
|
718
|
+
f"System Dependencies ({len(result['system_dependencies'])}):"
|
|
719
|
+
)
|
|
720
|
+
print("-" * 30)
|
|
721
|
+
for dep in result["system_dependencies"]:
|
|
722
|
+
print(f" {dep}")
|
|
723
|
+
print()
|
|
724
|
+
|
|
725
|
+
print("Per-Agent Dependencies:")
|
|
726
|
+
print("-" * 30)
|
|
727
|
+
for agent_id in sorted(result["per_agent"].keys()):
|
|
728
|
+
deps = result["per_agent"][agent_id]
|
|
729
|
+
python_count = len(deps.get("python", []))
|
|
730
|
+
system_count = len(deps.get("system", []))
|
|
731
|
+
if python_count or system_count:
|
|
732
|
+
print(
|
|
733
|
+
f" {agent_id}: {python_count} Python, {system_count} System"
|
|
734
|
+
)
|
|
451
735
|
|
|
452
736
|
return CommandResult.success_result(
|
|
453
|
-
|
|
737
|
+
"Dependency listing completed", data=result
|
|
454
738
|
)
|
|
455
739
|
|
|
456
740
|
except Exception as e:
|
|
@@ -460,20 +744,93 @@ class AgentsCommand(AgentCommand):
|
|
|
460
744
|
def _fix_agent_dependencies(self, args) -> CommandResult:
|
|
461
745
|
"""Fix agent dependency issues."""
|
|
462
746
|
try:
|
|
463
|
-
|
|
747
|
+
max_retries = getattr(args, "max_retries", 3)
|
|
748
|
+
agent_name = getattr(args, "agent", None)
|
|
464
749
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
750
|
+
print("=" * 70)
|
|
751
|
+
print("FIXING AGENT DEPENDENCIES WITH RETRY LOGIC")
|
|
752
|
+
print("=" * 70)
|
|
753
|
+
print()
|
|
754
|
+
|
|
755
|
+
result = self.dependency_service.fix_dependencies(
|
|
756
|
+
max_retries=max_retries, agent_name=agent_name
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
if not result["success"]:
|
|
760
|
+
if "error" in result and "not deployed" in result["error"]:
|
|
761
|
+
print(f"❌ {result['error']}")
|
|
762
|
+
return CommandResult.error_result(result.get("error", "Fix failed"))
|
|
763
|
+
|
|
764
|
+
if result.get("message") == "No deployed agents found":
|
|
765
|
+
print("No deployed agents found")
|
|
766
|
+
return CommandResult.success_result("No agents to fix")
|
|
767
|
+
|
|
768
|
+
if result.get("message") == "All dependencies are already satisfied":
|
|
769
|
+
print("\n✅ All dependencies are already satisfied!")
|
|
770
|
+
return CommandResult.success_result("All dependencies satisfied")
|
|
771
|
+
|
|
772
|
+
# Show what's missing
|
|
773
|
+
if result.get("missing_python"):
|
|
774
|
+
print(f"\n❌ Missing Python packages: {len(result['missing_python'])}")
|
|
775
|
+
for pkg in result["missing_python"][:10]:
|
|
776
|
+
print(f" - {pkg}")
|
|
777
|
+
if len(result["missing_python"]) > 10:
|
|
778
|
+
print(f" ... and {len(result['missing_python']) - 10} more")
|
|
779
|
+
|
|
780
|
+
if result.get("missing_system"):
|
|
781
|
+
print(f"\n❌ Missing system commands: {len(result['missing_system'])}")
|
|
782
|
+
for cmd in result["missing_system"]:
|
|
783
|
+
print(f" - {cmd}")
|
|
784
|
+
print("\n⚠️ System dependencies must be installed manually:")
|
|
785
|
+
print(f" macOS: brew install {' '.join(result['missing_system'])}")
|
|
786
|
+
print(f" Ubuntu: apt-get install {' '.join(result['missing_system'])}")
|
|
787
|
+
|
|
788
|
+
# Show incompatible packages
|
|
789
|
+
if result.get("incompatible"):
|
|
790
|
+
print(
|
|
791
|
+
f"\n⚠️ Skipping {len(result['incompatible'])} incompatible packages:"
|
|
469
792
|
)
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
793
|
+
for pkg in result["incompatible"][:5]:
|
|
794
|
+
print(f" - {pkg}")
|
|
795
|
+
if len(result["incompatible"]) > 5:
|
|
796
|
+
print(f" ... and {len(result['incompatible']) - 5} more")
|
|
797
|
+
|
|
798
|
+
# Show installation results
|
|
799
|
+
if result.get("fixed_python") or result.get("failed_python"):
|
|
800
|
+
print("\n" + "=" * 70)
|
|
801
|
+
print("INSTALLATION RESULTS:")
|
|
802
|
+
print("=" * 70)
|
|
803
|
+
|
|
804
|
+
if result.get("fixed_python"):
|
|
805
|
+
print(
|
|
806
|
+
f"✅ Successfully installed: {len(result['fixed_python'])} packages"
|
|
807
|
+
)
|
|
475
808
|
|
|
476
|
-
|
|
809
|
+
if result.get("failed_python"):
|
|
810
|
+
print(
|
|
811
|
+
f"❌ Failed to install: {len(result['failed_python'])} packages"
|
|
812
|
+
)
|
|
813
|
+
errors = result.get("errors", {})
|
|
814
|
+
for pkg in result["failed_python"]:
|
|
815
|
+
print(f" - {pkg}: {errors.get(pkg, 'Unknown error')}")
|
|
816
|
+
|
|
817
|
+
# Final verification
|
|
818
|
+
if result.get("still_missing") is not None:
|
|
819
|
+
if not result["still_missing"]:
|
|
820
|
+
print("\n✅ All Python dependencies are now satisfied!")
|
|
821
|
+
else:
|
|
822
|
+
print(
|
|
823
|
+
f"\n⚠️ Still missing {len(result['still_missing'])} packages"
|
|
824
|
+
)
|
|
825
|
+
print("\nTry running again or install manually:")
|
|
826
|
+
missing_sample = result["still_missing"][:3]
|
|
827
|
+
print(f" pip install {' '.join(missing_sample)}")
|
|
828
|
+
|
|
829
|
+
print("\n" + "=" * 70)
|
|
830
|
+
print("DONE")
|
|
831
|
+
print("=" * 70)
|
|
832
|
+
|
|
833
|
+
return CommandResult.success_result("Dependency fix completed", data=result)
|
|
477
834
|
|
|
478
835
|
except Exception as e:
|
|
479
836
|
self.logger.error(f"Error fixing dependencies: {e}", exc_info=True)
|
|
@@ -482,75 +839,34 @@ class AgentsCommand(AgentCommand):
|
|
|
482
839
|
def _cleanup_orphaned_agents(self, args) -> CommandResult:
|
|
483
840
|
"""Clean up orphaned agents that don't have templates."""
|
|
484
841
|
try:
|
|
485
|
-
from ...services.agents.deployment.multi_source_deployment_service import (
|
|
486
|
-
MultiSourceAgentDeploymentService,
|
|
487
|
-
)
|
|
488
|
-
|
|
489
842
|
# Determine agents directory
|
|
843
|
+
agents_dir = None
|
|
490
844
|
if hasattr(args, "agents_dir") and args.agents_dir:
|
|
491
845
|
agents_dir = args.agents_dir
|
|
492
|
-
else:
|
|
493
|
-
# Check for project-level .claude/agents first
|
|
494
|
-
project_agents_dir = Path.cwd() / ".claude" / "agents"
|
|
495
|
-
if project_agents_dir.exists():
|
|
496
|
-
agents_dir = project_agents_dir
|
|
497
|
-
else:
|
|
498
|
-
# Fall back to user home directory
|
|
499
|
-
agents_dir = Path.home() / ".claude" / "agents"
|
|
500
|
-
|
|
501
|
-
if not agents_dir.exists():
|
|
502
|
-
return CommandResult.success_result(
|
|
503
|
-
f"Agents directory not found: {agents_dir}"
|
|
504
|
-
)
|
|
505
|
-
|
|
506
|
-
# Initialize service
|
|
507
|
-
service = MultiSourceAgentDeploymentService()
|
|
508
846
|
|
|
509
847
|
# Determine if we're doing a dry run
|
|
510
848
|
dry_run = getattr(args, "dry_run", True)
|
|
511
849
|
if hasattr(args, "force") and args.force:
|
|
512
850
|
dry_run = False
|
|
513
851
|
|
|
514
|
-
# Perform cleanup
|
|
515
|
-
results =
|
|
852
|
+
# Perform cleanup using the cleanup service
|
|
853
|
+
results = self.cleanup_service.clean_orphaned_agents(
|
|
854
|
+
agents_dir=agents_dir, dry_run=dry_run
|
|
855
|
+
)
|
|
516
856
|
|
|
517
857
|
output_format = getattr(args, "format", "text")
|
|
518
|
-
quiet = getattr(args, "quiet", False)
|
|
519
|
-
|
|
520
|
-
if output_format in ["json", "yaml"]:
|
|
521
|
-
return CommandResult.success_result(
|
|
522
|
-
f"Found {len(results.get('orphaned', []))} orphaned agents",
|
|
523
|
-
data=results,
|
|
524
|
-
)
|
|
525
|
-
# Text output
|
|
526
|
-
if not results.get("orphaned"):
|
|
527
|
-
print("✅ No orphaned agents found")
|
|
528
|
-
return CommandResult.success_result("No orphaned agents found")
|
|
529
858
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
859
|
+
formatted = self._formatter.format_cleanup_result(
|
|
860
|
+
results, output_format=output_format, dry_run=dry_run
|
|
861
|
+
)
|
|
862
|
+
print(formatted)
|
|
534
863
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
f"{len(results['
|
|
864
|
+
# Determine success/error based on results
|
|
865
|
+
if results.get("errors") and not dry_run:
|
|
866
|
+
return CommandResult.error_result(
|
|
867
|
+
f"Cleanup completed with {len(results['errors'])} errors",
|
|
868
|
+
data=results,
|
|
539
869
|
)
|
|
540
|
-
else:
|
|
541
|
-
if results.get("removed"):
|
|
542
|
-
print(
|
|
543
|
-
f"\n✅ Successfully removed {len(results['removed'])} orphaned agent(s)"
|
|
544
|
-
)
|
|
545
|
-
|
|
546
|
-
if results.get("errors"):
|
|
547
|
-
print(f"\n❌ Encountered {len(results['errors'])} error(s):")
|
|
548
|
-
for error in results["errors"]:
|
|
549
|
-
print(f" - {error}")
|
|
550
|
-
return CommandResult.error_result(
|
|
551
|
-
f"Cleanup completed with {len(results['errors'])} errors",
|
|
552
|
-
data=results,
|
|
553
|
-
)
|
|
554
870
|
|
|
555
871
|
return CommandResult.success_result(
|
|
556
872
|
f"Cleanup {'preview' if dry_run else 'completed'}", data=results
|
|
@@ -575,772 +891,3 @@ def manage_agents(args):
|
|
|
575
891
|
command.print_result(result, args)
|
|
576
892
|
|
|
577
893
|
return result.exit_code
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
def _list_agents(args, deployment_service):
|
|
581
|
-
"""Legacy function for backward compatibility."""
|
|
582
|
-
command = AgentsCommand()
|
|
583
|
-
command._deployment_service = deployment_service
|
|
584
|
-
result = command._list_agents(args)
|
|
585
|
-
return result.exit_code
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
def _deploy_agents(args, deployment_service, force=False):
|
|
589
|
-
"""
|
|
590
|
-
Deploy both system and project agents.
|
|
591
|
-
|
|
592
|
-
WHY: Agents need to be deployed to the working directory for Claude Code to use them.
|
|
593
|
-
This function handles both regular and forced deployment, including project-specific agents.
|
|
594
|
-
|
|
595
|
-
Args:
|
|
596
|
-
args: Command arguments with optional 'target' path
|
|
597
|
-
deployment_service: Agent deployment service instance
|
|
598
|
-
force: Whether to force rebuild all agents
|
|
599
|
-
"""
|
|
600
|
-
# Load configuration to get exclusion settings using ConfigLoader
|
|
601
|
-
config_loader = ConfigLoader()
|
|
602
|
-
config = config_loader.load_main_config()
|
|
603
|
-
|
|
604
|
-
# Check if user wants to override exclusions
|
|
605
|
-
if hasattr(args, "include_all") and args.include_all:
|
|
606
|
-
# Clear exclusion list if --include-all flag is set
|
|
607
|
-
config.set("agent_deployment.excluded_agents", [])
|
|
608
|
-
print("✅ Including all agents (exclusion configuration overridden)\n")
|
|
609
|
-
else:
|
|
610
|
-
excluded_agents = config.get("agent_deployment.excluded_agents", [])
|
|
611
|
-
|
|
612
|
-
# Display exclusion information if agents are being excluded
|
|
613
|
-
if excluded_agents:
|
|
614
|
-
logger = get_logger("cli")
|
|
615
|
-
logger.info(f"Configured agent exclusions: {excluded_agents}")
|
|
616
|
-
print(
|
|
617
|
-
f"\n⚠️ Excluding agents from deployment: {', '.join(excluded_agents)}"
|
|
618
|
-
)
|
|
619
|
-
|
|
620
|
-
# Warn if commonly used agents are being excluded
|
|
621
|
-
common_agents = {"engineer", "qa", "security", "documentation"}
|
|
622
|
-
excluded_common = {a.lower() for a in excluded_agents} & common_agents
|
|
623
|
-
if excluded_common:
|
|
624
|
-
print(
|
|
625
|
-
f"⚠️ Warning: Common agents are being excluded: {', '.join(excluded_common)}"
|
|
626
|
-
)
|
|
627
|
-
print(
|
|
628
|
-
" This may affect normal operations. Use 'claude-mpm agents deploy --include-all' to override.\n"
|
|
629
|
-
)
|
|
630
|
-
|
|
631
|
-
# Deploy system agents first
|
|
632
|
-
if force:
|
|
633
|
-
print("Force deploying all system agents...")
|
|
634
|
-
else:
|
|
635
|
-
print("Deploying system agents...")
|
|
636
|
-
|
|
637
|
-
results = deployment_service.deploy_agents(None, force_rebuild=force, config=config)
|
|
638
|
-
|
|
639
|
-
# Also deploy project agents if they exist
|
|
640
|
-
from pathlib import Path
|
|
641
|
-
|
|
642
|
-
# Use the user's working directory if available
|
|
643
|
-
if "CLAUDE_MPM_USER_PWD" in os.environ:
|
|
644
|
-
project_dir = Path(os.environ["CLAUDE_MPM_USER_PWD"])
|
|
645
|
-
else:
|
|
646
|
-
project_dir = Path.cwd()
|
|
647
|
-
|
|
648
|
-
project_agents_dir = project_dir / ".claude-mpm" / "agents"
|
|
649
|
-
if project_agents_dir.exists():
|
|
650
|
-
json_files = list(project_agents_dir.glob("*.json"))
|
|
651
|
-
if json_files:
|
|
652
|
-
print(f"\nDeploying {len(json_files)} project agents...")
|
|
653
|
-
from claude_mpm.services.agents.deployment.agent_deployment import (
|
|
654
|
-
AgentDeploymentService,
|
|
655
|
-
)
|
|
656
|
-
|
|
657
|
-
project_service = AgentDeploymentService(
|
|
658
|
-
templates_dir=project_agents_dir,
|
|
659
|
-
base_agent_path=(
|
|
660
|
-
project_agents_dir / "base_agent.json"
|
|
661
|
-
if (project_agents_dir / "base_agent.json").exists()
|
|
662
|
-
else None
|
|
663
|
-
),
|
|
664
|
-
working_directory=project_dir, # Pass the project directory
|
|
665
|
-
)
|
|
666
|
-
# Pass the same configuration to project agent deployment
|
|
667
|
-
# For project agents, let the service determine they should stay in project directory
|
|
668
|
-
project_results = project_service.deploy_agents(
|
|
669
|
-
target_dir=None, # Let service detect it's a project deployment
|
|
670
|
-
force_rebuild=force,
|
|
671
|
-
deployment_mode="project",
|
|
672
|
-
config=config,
|
|
673
|
-
)
|
|
674
|
-
|
|
675
|
-
# Merge project results into main results
|
|
676
|
-
if project_results.get("deployed"):
|
|
677
|
-
results["deployed"].extend(project_results["deployed"])
|
|
678
|
-
print(f"✓ Deployed {len(project_results['deployed'])} project agents")
|
|
679
|
-
if project_results.get("updated"):
|
|
680
|
-
results["updated"].extend(project_results["updated"])
|
|
681
|
-
print(f"✓ Updated {len(project_results['updated'])} project agents")
|
|
682
|
-
if project_results.get("errors"):
|
|
683
|
-
results["errors"].extend(project_results["errors"])
|
|
684
|
-
|
|
685
|
-
if results["deployed"]:
|
|
686
|
-
print(
|
|
687
|
-
f"\n✓ Successfully deployed {len(results['deployed'])} agents to {results['target_dir']}"
|
|
688
|
-
)
|
|
689
|
-
for agent in results["deployed"]:
|
|
690
|
-
print(f" - {agent['name']}")
|
|
691
|
-
|
|
692
|
-
if force and results.get("updated", []):
|
|
693
|
-
print(f"\n✓ Updated {len(results['updated'])} agents")
|
|
694
|
-
for agent in results["updated"]:
|
|
695
|
-
print(f" - {agent['name']}")
|
|
696
|
-
|
|
697
|
-
if force and results.get("skipped", []):
|
|
698
|
-
print(f"\n✓ Skipped {len(results['skipped'])} up-to-date agents")
|
|
699
|
-
|
|
700
|
-
if results["errors"]:
|
|
701
|
-
print("\n❌ Errors during deployment:")
|
|
702
|
-
for error in results["errors"]:
|
|
703
|
-
print(f" - {error}")
|
|
704
|
-
|
|
705
|
-
if force:
|
|
706
|
-
# Set environment for force deploy
|
|
707
|
-
env_vars = deployment_service.set_claude_environment(
|
|
708
|
-
args.target.parent if args.target else None
|
|
709
|
-
)
|
|
710
|
-
print("\n✓ Set Claude environment variables:")
|
|
711
|
-
for key, value in env_vars.items():
|
|
712
|
-
print(f" - {key}={value}")
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
def _clean_agents(args, deployment_service):
|
|
716
|
-
"""
|
|
717
|
-
Clean deployed system agents.
|
|
718
|
-
|
|
719
|
-
WHY: Users may want to remove deployed agents to start fresh or clean up
|
|
720
|
-
their working directory.
|
|
721
|
-
|
|
722
|
-
Args:
|
|
723
|
-
args: Command arguments with optional 'target' path
|
|
724
|
-
deployment_service: Agent deployment service instance
|
|
725
|
-
"""
|
|
726
|
-
print("Cleaning deployed system agents...")
|
|
727
|
-
results = deployment_service.clean_deployment(args.target)
|
|
728
|
-
|
|
729
|
-
if results["removed"]:
|
|
730
|
-
print(f"\n✓ Removed {len(results['removed'])} agents")
|
|
731
|
-
for path in results["removed"]:
|
|
732
|
-
print(f" - {Path(path).name}")
|
|
733
|
-
else:
|
|
734
|
-
print("No system agents found to remove")
|
|
735
|
-
|
|
736
|
-
if results["errors"]:
|
|
737
|
-
print("\n❌ Errors during cleanup:")
|
|
738
|
-
for error in results["errors"]:
|
|
739
|
-
print(f" - {error}")
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
def _list_agents_by_tier():
|
|
743
|
-
"""
|
|
744
|
-
List agents grouped by precedence tier.
|
|
745
|
-
|
|
746
|
-
WHY: Users need to understand which agents are active across different tiers
|
|
747
|
-
and which version takes precedence when multiple versions exist.
|
|
748
|
-
"""
|
|
749
|
-
try:
|
|
750
|
-
adapter = AgentRegistryAdapter()
|
|
751
|
-
if not adapter.registry:
|
|
752
|
-
print("❌ Could not initialize agent registry")
|
|
753
|
-
return
|
|
754
|
-
|
|
755
|
-
# Get all agents and group by tier
|
|
756
|
-
all_agents = adapter.registry.list_agents()
|
|
757
|
-
|
|
758
|
-
# Group agents by tier and name
|
|
759
|
-
tiers = {"project": {}, "user": {}, "system": {}}
|
|
760
|
-
agent_names = set()
|
|
761
|
-
|
|
762
|
-
for agent_id, metadata in all_agents.items():
|
|
763
|
-
tier = metadata.get("tier", "system")
|
|
764
|
-
if tier in tiers:
|
|
765
|
-
tiers[tier][agent_id] = metadata
|
|
766
|
-
agent_names.add(agent_id)
|
|
767
|
-
|
|
768
|
-
# Display header
|
|
769
|
-
print("\n" + "=" * 80)
|
|
770
|
-
print(" " * 25 + "AGENT HIERARCHY BY TIER")
|
|
771
|
-
print("=" * 80)
|
|
772
|
-
print("\nPrecedence: PROJECT > USER > SYSTEM")
|
|
773
|
-
print("(Agents in higher tiers override those in lower tiers)\n")
|
|
774
|
-
|
|
775
|
-
# Display each tier
|
|
776
|
-
tier_order = [("PROJECT", "project"), ("USER", "user"), ("SYSTEM", "system")]
|
|
777
|
-
|
|
778
|
-
for tier_display, tier_key in tier_order:
|
|
779
|
-
agents = tiers[tier_key]
|
|
780
|
-
print(f"\n{'─' * 35} {tier_display} TIER {'─' * 35}")
|
|
781
|
-
|
|
782
|
-
if not agents:
|
|
783
|
-
print(f" No agents at {tier_key} level")
|
|
784
|
-
else:
|
|
785
|
-
# Check paths to determine actual locations
|
|
786
|
-
if tier_key == "project":
|
|
787
|
-
print(" Location: .claude-mpm/agents/ (in current project)")
|
|
788
|
-
elif tier_key == "user":
|
|
789
|
-
print(" Location: ~/.claude-mpm/agents/")
|
|
790
|
-
else:
|
|
791
|
-
print(" Location: Built-in framework agents")
|
|
792
|
-
|
|
793
|
-
print(f"\n Found {len(agents)} agent(s):\n")
|
|
794
|
-
|
|
795
|
-
for agent_id, metadata in sorted(agents.items()):
|
|
796
|
-
# Check if this agent is overridden by higher tiers
|
|
797
|
-
is_active = True
|
|
798
|
-
overridden_by = []
|
|
799
|
-
|
|
800
|
-
for check_tier_display, check_tier_key in tier_order:
|
|
801
|
-
if check_tier_key == tier_key:
|
|
802
|
-
break
|
|
803
|
-
if agent_id in tiers[check_tier_key]:
|
|
804
|
-
is_active = False
|
|
805
|
-
overridden_by.append(check_tier_display)
|
|
806
|
-
|
|
807
|
-
# Display agent info
|
|
808
|
-
status = (
|
|
809
|
-
"✓ ACTIVE"
|
|
810
|
-
if is_active
|
|
811
|
-
else f"⊗ OVERRIDDEN by {', '.join(overridden_by)}"
|
|
812
|
-
)
|
|
813
|
-
print(f" 📄 {agent_id:<20} [{status}]")
|
|
814
|
-
|
|
815
|
-
# Show metadata
|
|
816
|
-
if "description" in metadata:
|
|
817
|
-
print(f" Description: {metadata['description']}")
|
|
818
|
-
if "path" in metadata:
|
|
819
|
-
path = Path(metadata["path"])
|
|
820
|
-
print(f" File: {path.name}")
|
|
821
|
-
print()
|
|
822
|
-
|
|
823
|
-
# Summary
|
|
824
|
-
print("\n" + "=" * 80)
|
|
825
|
-
print("SUMMARY:")
|
|
826
|
-
print(f" Total unique agents: {len(agent_names)}")
|
|
827
|
-
print(f" Project agents: {len(tiers['project'])}")
|
|
828
|
-
print(f" User agents: {len(tiers['user'])}")
|
|
829
|
-
print(f" System agents: {len(tiers['system'])}")
|
|
830
|
-
print("=" * 80 + "\n")
|
|
831
|
-
|
|
832
|
-
except Exception as e:
|
|
833
|
-
print(f"❌ Error listing agents by tier: {e}")
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
def _view_agent(args):
|
|
837
|
-
"""
|
|
838
|
-
View detailed information about a specific agent.
|
|
839
|
-
|
|
840
|
-
WHY: Users need to inspect agent configurations, frontmatter, and instructions
|
|
841
|
-
to understand what an agent does and how it's configured.
|
|
842
|
-
|
|
843
|
-
Args:
|
|
844
|
-
args: Command arguments with 'agent_name' attribute
|
|
845
|
-
"""
|
|
846
|
-
if not hasattr(args, "agent_name") or not args.agent_name:
|
|
847
|
-
print("❌ Please specify an agent name to view")
|
|
848
|
-
print("Usage: claude-mpm agents view <agent_name>")
|
|
849
|
-
return
|
|
850
|
-
|
|
851
|
-
try:
|
|
852
|
-
adapter = AgentRegistryAdapter()
|
|
853
|
-
if not adapter.registry:
|
|
854
|
-
print("❌ Could not initialize agent registry")
|
|
855
|
-
return
|
|
856
|
-
|
|
857
|
-
# Get the agent
|
|
858
|
-
agent = adapter.registry.get_agent(args.agent_name)
|
|
859
|
-
if not agent:
|
|
860
|
-
print(f"❌ Agent '{args.agent_name}' not found")
|
|
861
|
-
print("\nAvailable agents:")
|
|
862
|
-
all_agents = adapter.registry.list_agents()
|
|
863
|
-
for agent_id in sorted(all_agents.keys()):
|
|
864
|
-
print(f" - {agent_id}")
|
|
865
|
-
return
|
|
866
|
-
|
|
867
|
-
# Read the agent file
|
|
868
|
-
agent_path = Path(agent.path)
|
|
869
|
-
if not agent_path.exists():
|
|
870
|
-
print(f"❌ Agent file not found: {agent_path}")
|
|
871
|
-
return
|
|
872
|
-
|
|
873
|
-
with open(agent_path) as f:
|
|
874
|
-
content = f.read()
|
|
875
|
-
|
|
876
|
-
# Display agent information
|
|
877
|
-
print("\n" + "=" * 80)
|
|
878
|
-
print(f" AGENT: {agent.name}")
|
|
879
|
-
print("=" * 80)
|
|
880
|
-
|
|
881
|
-
# Basic info
|
|
882
|
-
print("\n📋 BASIC INFORMATION:")
|
|
883
|
-
print(f" Name: {agent.name}")
|
|
884
|
-
print(f" Type: {agent.type}")
|
|
885
|
-
print(f" Tier: {agent.tier.upper()}")
|
|
886
|
-
print(f" Path: {agent_path}")
|
|
887
|
-
if agent.description:
|
|
888
|
-
print(f" Description: {agent.description}")
|
|
889
|
-
if agent.specializations:
|
|
890
|
-
print(f" Specializations: {', '.join(agent.specializations)}")
|
|
891
|
-
|
|
892
|
-
# Extract and display frontmatter
|
|
893
|
-
if content.startswith("---"):
|
|
894
|
-
try:
|
|
895
|
-
end_marker = content.find("\n---\n", 4)
|
|
896
|
-
if end_marker == -1:
|
|
897
|
-
end_marker = content.find("\n---\r\n", 4)
|
|
898
|
-
|
|
899
|
-
if end_marker != -1:
|
|
900
|
-
frontmatter_str = content[4:end_marker]
|
|
901
|
-
frontmatter = yaml.safe_load(frontmatter_str)
|
|
902
|
-
|
|
903
|
-
print("\n📝 FRONTMATTER:")
|
|
904
|
-
for key, value in frontmatter.items():
|
|
905
|
-
if isinstance(value, list):
|
|
906
|
-
print(f" {key}: [{', '.join(str(v) for v in value)}]")
|
|
907
|
-
elif isinstance(value, dict):
|
|
908
|
-
print(f" {key}:")
|
|
909
|
-
for k, v in value.items():
|
|
910
|
-
print(f" {k}: {v}")
|
|
911
|
-
else:
|
|
912
|
-
print(f" {key}: {value}")
|
|
913
|
-
|
|
914
|
-
# Extract instructions preview
|
|
915
|
-
instructions_start = end_marker + 5
|
|
916
|
-
instructions = content[instructions_start:].strip()
|
|
917
|
-
|
|
918
|
-
if instructions:
|
|
919
|
-
print("\n📖 INSTRUCTIONS PREVIEW (first 500 chars):")
|
|
920
|
-
print(" " + "-" * 76)
|
|
921
|
-
preview = instructions[:500]
|
|
922
|
-
if len(instructions) > 500:
|
|
923
|
-
preview += f"...\n\n [Truncated - {len(instructions) / 1024:.1f}KB total]"
|
|
924
|
-
|
|
925
|
-
for line in preview.split("\n"):
|
|
926
|
-
print(f" {line}")
|
|
927
|
-
print(" " + "-" * 76)
|
|
928
|
-
except Exception as e:
|
|
929
|
-
print(f"\n⚠️ Could not parse frontmatter: {e}")
|
|
930
|
-
else:
|
|
931
|
-
print("\n⚠️ No frontmatter found in agent file")
|
|
932
|
-
|
|
933
|
-
# File stats
|
|
934
|
-
import os
|
|
935
|
-
|
|
936
|
-
stat = os.stat(agent_path)
|
|
937
|
-
from datetime import datetime
|
|
938
|
-
|
|
939
|
-
modified = datetime.fromtimestamp(stat.st_mtime).strftime("%Y-%m-%d %H:%M:%S")
|
|
940
|
-
print("\n📊 FILE STATS:")
|
|
941
|
-
print(f" Size: {stat.st_size:,} bytes")
|
|
942
|
-
print(f" Last modified: {modified}")
|
|
943
|
-
|
|
944
|
-
print("\n" + "=" * 80 + "\n")
|
|
945
|
-
|
|
946
|
-
except Exception as e:
|
|
947
|
-
print(f"❌ Error viewing agent: {e}")
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
def _fix_agents(args):
|
|
951
|
-
"""
|
|
952
|
-
Fix agent frontmatter issues using FrontmatterValidator.
|
|
953
|
-
|
|
954
|
-
WHY: Agent files may have formatting issues in their frontmatter that prevent
|
|
955
|
-
proper loading. This command automatically fixes common issues.
|
|
956
|
-
|
|
957
|
-
Args:
|
|
958
|
-
args: Command arguments with 'agent_name', 'dry_run', and 'all' flags
|
|
959
|
-
"""
|
|
960
|
-
validator = FrontmatterValidator()
|
|
961
|
-
|
|
962
|
-
try:
|
|
963
|
-
adapter = AgentRegistryAdapter()
|
|
964
|
-
if not adapter.registry:
|
|
965
|
-
print("❌ Could not initialize agent registry")
|
|
966
|
-
return
|
|
967
|
-
|
|
968
|
-
# Determine which agents to fix
|
|
969
|
-
agents_to_fix = []
|
|
970
|
-
|
|
971
|
-
if hasattr(args, "all") and args.all:
|
|
972
|
-
# Fix all agents
|
|
973
|
-
all_agents = adapter.registry.list_agents()
|
|
974
|
-
for agent_id, metadata in all_agents.items():
|
|
975
|
-
agents_to_fix.append((agent_id, metadata["path"]))
|
|
976
|
-
print(
|
|
977
|
-
f"\n🔧 Checking {len(agents_to_fix)} agent(s) for frontmatter issues...\n"
|
|
978
|
-
)
|
|
979
|
-
elif hasattr(args, "agent_name") and args.agent_name:
|
|
980
|
-
# Fix specific agent
|
|
981
|
-
agent = adapter.registry.get_agent(args.agent_name)
|
|
982
|
-
if not agent:
|
|
983
|
-
print(f"❌ Agent '{args.agent_name}' not found")
|
|
984
|
-
return
|
|
985
|
-
agents_to_fix.append((agent.name, agent.path))
|
|
986
|
-
print(f"\n🔧 Checking agent '{agent.name}' for frontmatter issues...\n")
|
|
987
|
-
else:
|
|
988
|
-
print("❌ Please specify an agent name or use --all to fix all agents")
|
|
989
|
-
print("Usage: claude-mpm agents fix [agent_name] [--dry-run] [--all]")
|
|
990
|
-
return
|
|
991
|
-
|
|
992
|
-
dry_run = hasattr(args, "dry_run") and args.dry_run
|
|
993
|
-
if dry_run:
|
|
994
|
-
print("🔍 DRY RUN MODE - No changes will be made\n")
|
|
995
|
-
|
|
996
|
-
# Process each agent
|
|
997
|
-
total_issues = 0
|
|
998
|
-
total_fixed = 0
|
|
999
|
-
|
|
1000
|
-
for agent_name, agent_path in agents_to_fix:
|
|
1001
|
-
path = Path(agent_path)
|
|
1002
|
-
if not path.exists():
|
|
1003
|
-
print(f"⚠️ Skipping {agent_name}: File not found at {path}")
|
|
1004
|
-
continue
|
|
1005
|
-
|
|
1006
|
-
print(f"📄 {agent_name}:")
|
|
1007
|
-
|
|
1008
|
-
# Validate and potentially fix
|
|
1009
|
-
result = validator.correct_file(path, dry_run=dry_run)
|
|
1010
|
-
|
|
1011
|
-
if result.is_valid and not result.corrections:
|
|
1012
|
-
print(" ✓ No issues found")
|
|
1013
|
-
else:
|
|
1014
|
-
if result.errors:
|
|
1015
|
-
print(" ❌ Errors:")
|
|
1016
|
-
for error in result.errors:
|
|
1017
|
-
print(f" - {error}")
|
|
1018
|
-
total_issues += len(result.errors)
|
|
1019
|
-
|
|
1020
|
-
if result.warnings:
|
|
1021
|
-
print(" ⚠️ Warnings:")
|
|
1022
|
-
for warning in result.warnings:
|
|
1023
|
-
print(f" - {warning}")
|
|
1024
|
-
total_issues += len(result.warnings)
|
|
1025
|
-
|
|
1026
|
-
if result.corrections:
|
|
1027
|
-
if dry_run:
|
|
1028
|
-
print(" 🔧 Would fix:")
|
|
1029
|
-
else:
|
|
1030
|
-
print(" ✓ Fixed:")
|
|
1031
|
-
total_fixed += len(result.corrections)
|
|
1032
|
-
for correction in result.corrections:
|
|
1033
|
-
print(f" - {correction}")
|
|
1034
|
-
|
|
1035
|
-
print()
|
|
1036
|
-
|
|
1037
|
-
# Summary
|
|
1038
|
-
print("=" * 80)
|
|
1039
|
-
print("SUMMARY:")
|
|
1040
|
-
print(f" Agents checked: {len(agents_to_fix)}")
|
|
1041
|
-
print(f" Total issues found: {total_issues}")
|
|
1042
|
-
if dry_run:
|
|
1043
|
-
print(
|
|
1044
|
-
f" Issues that would be fixed: {sum(1 for _, path in agents_to_fix if validator.validate_file(Path(path)).corrections)}"
|
|
1045
|
-
)
|
|
1046
|
-
print("\n💡 Run without --dry-run to apply fixes")
|
|
1047
|
-
else:
|
|
1048
|
-
print(f" Issues fixed: {total_fixed}")
|
|
1049
|
-
if total_fixed > 0:
|
|
1050
|
-
print("\n✓ Frontmatter issues have been fixed!")
|
|
1051
|
-
print("=" * 80 + "\n")
|
|
1052
|
-
|
|
1053
|
-
except Exception as e:
|
|
1054
|
-
print(f"❌ Error fixing agents: {e}")
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
def _check_agent_dependencies(args):
|
|
1058
|
-
"""
|
|
1059
|
-
Check dependencies for deployed agents.
|
|
1060
|
-
|
|
1061
|
-
Args:
|
|
1062
|
-
args: Parsed command line arguments
|
|
1063
|
-
"""
|
|
1064
|
-
from ...utils.agent_dependency_loader import AgentDependencyLoader
|
|
1065
|
-
|
|
1066
|
-
getattr(args, "verbose", False)
|
|
1067
|
-
specific_agent = getattr(args, "agent", None)
|
|
1068
|
-
|
|
1069
|
-
loader = AgentDependencyLoader(auto_install=False)
|
|
1070
|
-
|
|
1071
|
-
# Discover deployed agents
|
|
1072
|
-
loader.discover_deployed_agents()
|
|
1073
|
-
|
|
1074
|
-
# Filter to specific agent if requested
|
|
1075
|
-
if specific_agent:
|
|
1076
|
-
if specific_agent not in loader.deployed_agents:
|
|
1077
|
-
print(f"❌ Agent '{specific_agent}' is not deployed")
|
|
1078
|
-
print(f" Available agents: {', '.join(loader.deployed_agents.keys())}")
|
|
1079
|
-
return
|
|
1080
|
-
# Keep only the specified agent
|
|
1081
|
-
loader.deployed_agents = {
|
|
1082
|
-
specific_agent: loader.deployed_agents[specific_agent]
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
# Load dependencies and check
|
|
1086
|
-
loader.load_agent_dependencies()
|
|
1087
|
-
results = loader.analyze_dependencies()
|
|
1088
|
-
|
|
1089
|
-
# Print report
|
|
1090
|
-
report = loader.format_report(results)
|
|
1091
|
-
print(report)
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
def _install_agent_dependencies(args):
|
|
1095
|
-
"""
|
|
1096
|
-
Install missing dependencies for deployed agents.
|
|
1097
|
-
|
|
1098
|
-
Args:
|
|
1099
|
-
args: Parsed command line arguments
|
|
1100
|
-
"""
|
|
1101
|
-
|
|
1102
|
-
from ...utils.agent_dependency_loader import AgentDependencyLoader
|
|
1103
|
-
|
|
1104
|
-
specific_agent = getattr(args, "agent", None)
|
|
1105
|
-
dry_run = getattr(args, "dry_run", False)
|
|
1106
|
-
|
|
1107
|
-
loader = AgentDependencyLoader(auto_install=not dry_run)
|
|
1108
|
-
|
|
1109
|
-
# Discover deployed agents
|
|
1110
|
-
loader.discover_deployed_agents()
|
|
1111
|
-
|
|
1112
|
-
# Filter to specific agent if requested
|
|
1113
|
-
if specific_agent:
|
|
1114
|
-
if specific_agent not in loader.deployed_agents:
|
|
1115
|
-
print(f"❌ Agent '{specific_agent}' is not deployed")
|
|
1116
|
-
print(f" Available agents: {', '.join(loader.deployed_agents.keys())}")
|
|
1117
|
-
return
|
|
1118
|
-
loader.deployed_agents = {
|
|
1119
|
-
specific_agent: loader.deployed_agents[specific_agent]
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
# Load dependencies
|
|
1123
|
-
loader.load_agent_dependencies()
|
|
1124
|
-
results = loader.analyze_dependencies()
|
|
1125
|
-
|
|
1126
|
-
missing_deps = results["summary"]["missing_python"]
|
|
1127
|
-
|
|
1128
|
-
if not missing_deps:
|
|
1129
|
-
print("✅ All Python dependencies are already installed")
|
|
1130
|
-
return
|
|
1131
|
-
|
|
1132
|
-
print(f"Found {len(missing_deps)} missing dependencies:")
|
|
1133
|
-
for dep in missing_deps:
|
|
1134
|
-
print(f" - {dep}")
|
|
1135
|
-
|
|
1136
|
-
if dry_run:
|
|
1137
|
-
print("\n--dry-run specified, not installing anything")
|
|
1138
|
-
print(f"Would install: pip install {' '.join(missing_deps)}")
|
|
1139
|
-
else:
|
|
1140
|
-
print(f"\nInstalling {len(missing_deps)} dependencies...")
|
|
1141
|
-
success, error = loader.install_missing_dependencies(missing_deps)
|
|
1142
|
-
|
|
1143
|
-
if success:
|
|
1144
|
-
print("✅ Successfully installed all dependencies")
|
|
1145
|
-
|
|
1146
|
-
# Re-check after installation
|
|
1147
|
-
loader.checked_packages.clear()
|
|
1148
|
-
results = loader.analyze_dependencies()
|
|
1149
|
-
|
|
1150
|
-
if results["summary"]["missing_python"]:
|
|
1151
|
-
print(
|
|
1152
|
-
f"⚠️ {len(results['summary']['missing_python'])} dependencies still missing after installation"
|
|
1153
|
-
)
|
|
1154
|
-
else:
|
|
1155
|
-
print("✅ All dependencies verified after installation")
|
|
1156
|
-
else:
|
|
1157
|
-
print(f"❌ Failed to install dependencies: {error}")
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
def _list_agent_dependencies(args):
|
|
1161
|
-
"""
|
|
1162
|
-
List all dependencies from deployed agents.
|
|
1163
|
-
|
|
1164
|
-
Args:
|
|
1165
|
-
args: Parsed command line arguments
|
|
1166
|
-
"""
|
|
1167
|
-
|
|
1168
|
-
from ...utils.agent_dependency_loader import AgentDependencyLoader
|
|
1169
|
-
|
|
1170
|
-
output_format = getattr(args, "format", "text")
|
|
1171
|
-
|
|
1172
|
-
loader = AgentDependencyLoader(auto_install=False)
|
|
1173
|
-
|
|
1174
|
-
# Discover and load
|
|
1175
|
-
loader.discover_deployed_agents()
|
|
1176
|
-
loader.load_agent_dependencies()
|
|
1177
|
-
|
|
1178
|
-
# Collect all unique dependencies
|
|
1179
|
-
all_python_deps = set()
|
|
1180
|
-
all_system_deps = set()
|
|
1181
|
-
|
|
1182
|
-
for agent_id, deps in loader.agent_dependencies.items():
|
|
1183
|
-
if "python" in deps:
|
|
1184
|
-
all_python_deps.update(deps["python"])
|
|
1185
|
-
if "system" in deps:
|
|
1186
|
-
all_system_deps.update(deps["system"])
|
|
1187
|
-
|
|
1188
|
-
# Format output based on requested format
|
|
1189
|
-
if output_format == "pip":
|
|
1190
|
-
# Output pip-installable format
|
|
1191
|
-
for dep in sorted(all_python_deps):
|
|
1192
|
-
print(dep)
|
|
1193
|
-
|
|
1194
|
-
elif output_format == "json":
|
|
1195
|
-
# Output JSON format
|
|
1196
|
-
output = {
|
|
1197
|
-
"python": sorted(all_python_deps),
|
|
1198
|
-
"system": sorted(all_system_deps),
|
|
1199
|
-
"agents": {},
|
|
1200
|
-
}
|
|
1201
|
-
for agent_id, deps in loader.agent_dependencies.items():
|
|
1202
|
-
output["agents"][agent_id] = deps
|
|
1203
|
-
print(json.dumps(output, indent=2))
|
|
1204
|
-
|
|
1205
|
-
else: # text format
|
|
1206
|
-
print("=" * 60)
|
|
1207
|
-
print("DEPENDENCIES FROM DEPLOYED AGENTS")
|
|
1208
|
-
print("=" * 60)
|
|
1209
|
-
print()
|
|
1210
|
-
|
|
1211
|
-
if all_python_deps:
|
|
1212
|
-
print(f"Python Dependencies ({len(all_python_deps)}):")
|
|
1213
|
-
print("-" * 30)
|
|
1214
|
-
for dep in sorted(all_python_deps):
|
|
1215
|
-
print(f" {dep}")
|
|
1216
|
-
print()
|
|
1217
|
-
|
|
1218
|
-
if all_system_deps:
|
|
1219
|
-
print(f"System Dependencies ({len(all_system_deps)}):")
|
|
1220
|
-
print("-" * 30)
|
|
1221
|
-
for dep in sorted(all_system_deps):
|
|
1222
|
-
print(f" {dep}")
|
|
1223
|
-
print()
|
|
1224
|
-
|
|
1225
|
-
print("Per-Agent Dependencies:")
|
|
1226
|
-
print("-" * 30)
|
|
1227
|
-
for agent_id in sorted(loader.agent_dependencies.keys()):
|
|
1228
|
-
deps = loader.agent_dependencies[agent_id]
|
|
1229
|
-
python_count = len(deps.get("python", []))
|
|
1230
|
-
system_count = len(deps.get("system", []))
|
|
1231
|
-
if python_count or system_count:
|
|
1232
|
-
print(f" {agent_id}: {python_count} Python, {system_count} System")
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
def _fix_agent_dependencies(args):
|
|
1236
|
-
"""
|
|
1237
|
-
Fix missing agent dependencies with robust retry logic.
|
|
1238
|
-
|
|
1239
|
-
WHY: Network issues and temporary package unavailability can cause
|
|
1240
|
-
dependency installation to fail. This command uses robust retry logic
|
|
1241
|
-
to maximize success rate.
|
|
1242
|
-
|
|
1243
|
-
Args:
|
|
1244
|
-
args: Parsed command line arguments
|
|
1245
|
-
"""
|
|
1246
|
-
from ...utils.agent_dependency_loader import AgentDependencyLoader
|
|
1247
|
-
from ...utils.robust_installer import RobustPackageInstaller
|
|
1248
|
-
|
|
1249
|
-
max_retries = getattr(args, "max_retries", 3)
|
|
1250
|
-
|
|
1251
|
-
print("=" * 70)
|
|
1252
|
-
print("FIXING AGENT DEPENDENCIES WITH RETRY LOGIC")
|
|
1253
|
-
print("=" * 70)
|
|
1254
|
-
print()
|
|
1255
|
-
|
|
1256
|
-
loader = AgentDependencyLoader(auto_install=False)
|
|
1257
|
-
|
|
1258
|
-
# Discover and analyze
|
|
1259
|
-
print("Discovering deployed agents...")
|
|
1260
|
-
loader.discover_deployed_agents()
|
|
1261
|
-
|
|
1262
|
-
if not loader.deployed_agents:
|
|
1263
|
-
print("No deployed agents found")
|
|
1264
|
-
return
|
|
1265
|
-
|
|
1266
|
-
print(f"Found {len(loader.deployed_agents)} deployed agents")
|
|
1267
|
-
print("Analyzing dependencies...")
|
|
1268
|
-
|
|
1269
|
-
loader.load_agent_dependencies()
|
|
1270
|
-
results = loader.analyze_dependencies()
|
|
1271
|
-
|
|
1272
|
-
missing_python = results["summary"]["missing_python"]
|
|
1273
|
-
missing_system = results["summary"]["missing_system"]
|
|
1274
|
-
|
|
1275
|
-
if not missing_python and not missing_system:
|
|
1276
|
-
print("\n✅ All dependencies are already satisfied!")
|
|
1277
|
-
return
|
|
1278
|
-
|
|
1279
|
-
# Show what's missing
|
|
1280
|
-
if missing_python:
|
|
1281
|
-
print(f"\n❌ Missing Python packages: {len(missing_python)}")
|
|
1282
|
-
for pkg in missing_python[:10]:
|
|
1283
|
-
print(f" - {pkg}")
|
|
1284
|
-
if len(missing_python) > 10:
|
|
1285
|
-
print(f" ... and {len(missing_python) - 10} more")
|
|
1286
|
-
|
|
1287
|
-
if missing_system:
|
|
1288
|
-
print(f"\n❌ Missing system commands: {len(missing_system)}")
|
|
1289
|
-
for cmd in missing_system:
|
|
1290
|
-
print(f" - {cmd}")
|
|
1291
|
-
print("\n⚠️ System dependencies must be installed manually:")
|
|
1292
|
-
print(f" macOS: brew install {' '.join(missing_system)}")
|
|
1293
|
-
print(f" Ubuntu: apt-get install {' '.join(missing_system)}")
|
|
1294
|
-
|
|
1295
|
-
# Fix Python dependencies with robust installer
|
|
1296
|
-
if missing_python:
|
|
1297
|
-
print(
|
|
1298
|
-
f"\n🔧 Fixing Python dependencies with {max_retries} retries per package..."
|
|
1299
|
-
)
|
|
1300
|
-
|
|
1301
|
-
# Check compatibility
|
|
1302
|
-
compatible, incompatible = loader.check_python_compatibility(missing_python)
|
|
1303
|
-
|
|
1304
|
-
if incompatible:
|
|
1305
|
-
print(f"\n⚠️ Skipping {len(incompatible)} incompatible packages:")
|
|
1306
|
-
for pkg in incompatible[:5]:
|
|
1307
|
-
print(f" - {pkg}")
|
|
1308
|
-
if len(incompatible) > 5:
|
|
1309
|
-
print(f" ... and {len(incompatible) - 5} more")
|
|
1310
|
-
|
|
1311
|
-
if compatible:
|
|
1312
|
-
installer = RobustPackageInstaller(
|
|
1313
|
-
max_retries=max_retries, retry_delay=2.0, timeout=300
|
|
1314
|
-
)
|
|
1315
|
-
|
|
1316
|
-
print(f"\nInstalling {len(compatible)} compatible packages...")
|
|
1317
|
-
successful, failed, errors = installer.install_packages(compatible)
|
|
1318
|
-
|
|
1319
|
-
print("\n" + "=" * 70)
|
|
1320
|
-
print("INSTALLATION RESULTS:")
|
|
1321
|
-
print("=" * 70)
|
|
1322
|
-
|
|
1323
|
-
if successful:
|
|
1324
|
-
print(f"✅ Successfully installed: {len(successful)} packages")
|
|
1325
|
-
|
|
1326
|
-
if failed:
|
|
1327
|
-
print(f"❌ Failed to install: {len(failed)} packages")
|
|
1328
|
-
for pkg in failed:
|
|
1329
|
-
print(f" - {pkg}: {errors.get(pkg, 'Unknown error')}")
|
|
1330
|
-
|
|
1331
|
-
# Re-check
|
|
1332
|
-
print("\nVerifying installation...")
|
|
1333
|
-
loader.checked_packages.clear()
|
|
1334
|
-
final_results = loader.analyze_dependencies()
|
|
1335
|
-
|
|
1336
|
-
final_missing = final_results["summary"]["missing_python"]
|
|
1337
|
-
if not final_missing:
|
|
1338
|
-
print("✅ All Python dependencies are now satisfied!")
|
|
1339
|
-
else:
|
|
1340
|
-
print(f"⚠️ Still missing {len(final_missing)} packages")
|
|
1341
|
-
print("\nTry running again or install manually:")
|
|
1342
|
-
print(f" pip install {' '.join(final_missing[:3])}")
|
|
1343
|
-
|
|
1344
|
-
print("\n" + "=" * 70)
|
|
1345
|
-
print("DONE")
|
|
1346
|
-
print("=" * 70)
|