claude-mpm 4.12.1__py3-none-any.whl → 4.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +110 -459
- claude_mpm/agents/templates/README.md +465 -0
- claude_mpm/agents/templates/circuit_breakers.md +638 -0
- claude_mpm/agents/templates/git_file_tracking.md +584 -0
- claude_mpm/agents/templates/pm_examples.md +474 -0
- claude_mpm/agents/templates/pm_red_flags.md +240 -0
- claude_mpm/agents/templates/response_format.md +583 -0
- claude_mpm/agents/templates/validation_templates.md +312 -0
- claude_mpm/cli/__init__.py +10 -0
- claude_mpm/cli/commands/agents.py +31 -0
- claude_mpm/cli/commands/agents_detect.py +380 -0
- claude_mpm/cli/commands/agents_recommend.py +309 -0
- claude_mpm/cli/commands/auto_configure.py +564 -0
- claude_mpm/cli/parsers/agents_parser.py +9 -0
- claude_mpm/cli/parsers/auto_configure_parser.py +253 -0
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/core/log_manager.py +2 -0
- claude_mpm/services/agents/__init__.py +18 -5
- claude_mpm/services/agents/auto_config_manager.py +797 -0
- claude_mpm/services/agents/observers.py +547 -0
- claude_mpm/services/agents/recommender.py +568 -0
- claude_mpm/services/core/__init__.py +33 -1
- claude_mpm/services/core/interfaces/__init__.py +16 -1
- claude_mpm/services/core/interfaces/agent.py +184 -0
- claude_mpm/services/core/interfaces/project.py +121 -0
- claude_mpm/services/core/models/__init__.py +46 -0
- claude_mpm/services/core/models/agent_config.py +397 -0
- claude_mpm/services/core/models/toolchain.py +306 -0
- claude_mpm/services/project/__init__.py +23 -0
- claude_mpm/services/project/detection_strategies.py +719 -0
- claude_mpm/services/project/toolchain_analyzer.py +581 -0
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/METADATA +1 -1
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/RECORD +38 -18
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auto-Configuration CLI Command for Claude MPM Framework
|
|
3
|
+
========================================================
|
|
4
|
+
|
|
5
|
+
WHY: This module provides a user-friendly CLI interface for the auto-configuration
|
|
6
|
+
feature, allowing users to automatically configure agents based on detected toolchain.
|
|
7
|
+
|
|
8
|
+
DESIGN DECISION: Uses rich for beautiful terminal output, implements interactive
|
|
9
|
+
confirmation, and provides comprehensive error handling. Supports both interactive
|
|
10
|
+
and non-interactive modes for flexibility.
|
|
11
|
+
|
|
12
|
+
Part of TSK-0054: Auto-Configuration Feature - Phase 5
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from rich.console import Console
|
|
21
|
+
from rich.panel import Panel
|
|
22
|
+
from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
|
|
23
|
+
from rich.table import Table
|
|
24
|
+
|
|
25
|
+
RICH_AVAILABLE = True
|
|
26
|
+
except ImportError:
|
|
27
|
+
RICH_AVAILABLE = False
|
|
28
|
+
|
|
29
|
+
from ...services.agents.auto_config_manager import AutoConfigManagerService
|
|
30
|
+
from ...services.agents.observers import NullObserver
|
|
31
|
+
from ...services.core.models.agent_config import (
|
|
32
|
+
ConfigurationResult,
|
|
33
|
+
ConfigurationStatus,
|
|
34
|
+
)
|
|
35
|
+
from ..shared import BaseCommand, CommandResult
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class RichProgressObserver(NullObserver):
|
|
39
|
+
"""
|
|
40
|
+
Observer that displays deployment progress using Rich.
|
|
41
|
+
|
|
42
|
+
WHY: Extends NullObserver to inherit all required abstract method
|
|
43
|
+
implementations while overriding only the methods needed for
|
|
44
|
+
Rich console output.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, console: "Console"):
|
|
48
|
+
"""Initialize the observer.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
console: Rich console for output
|
|
52
|
+
"""
|
|
53
|
+
self.console = console
|
|
54
|
+
self.progress = None
|
|
55
|
+
self.task_id = None
|
|
56
|
+
|
|
57
|
+
def on_agent_deployment_started(
|
|
58
|
+
self, agent_id: str, agent_name: str, index: int, total: int
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Called when agent deployment starts."""
|
|
61
|
+
if not self.progress:
|
|
62
|
+
self.progress = Progress(
|
|
63
|
+
SpinnerColumn(),
|
|
64
|
+
TextColumn("[progress.description]{task.description}"),
|
|
65
|
+
BarColumn(),
|
|
66
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
67
|
+
console=self.console,
|
|
68
|
+
)
|
|
69
|
+
self.progress.start()
|
|
70
|
+
|
|
71
|
+
self.task_id = self.progress.add_task(f"Deploying {agent_name}...", total=100)
|
|
72
|
+
|
|
73
|
+
def on_agent_deployment_progress(
|
|
74
|
+
self, agent_id: str, progress: int, message: str = ""
|
|
75
|
+
) -> None:
|
|
76
|
+
"""Called when deployment makes progress."""
|
|
77
|
+
if self.progress and self.task_id is not None:
|
|
78
|
+
self.progress.update(self.task_id, completed=progress)
|
|
79
|
+
|
|
80
|
+
def on_agent_deployment_completed(
|
|
81
|
+
self, agent_id: str, agent_name: str, success: bool, error: str | None = None
|
|
82
|
+
) -> None:
|
|
83
|
+
"""Called when agent deployment completes."""
|
|
84
|
+
if self.progress and self.task_id is not None:
|
|
85
|
+
if success:
|
|
86
|
+
self.progress.update(self.task_id, completed=100)
|
|
87
|
+
self.console.print(f"✅ {agent_name} deployed successfully")
|
|
88
|
+
else:
|
|
89
|
+
error_msg = f": {error}" if error else ""
|
|
90
|
+
self.console.print(f"❌ {agent_name} deployment failed{error_msg}")
|
|
91
|
+
|
|
92
|
+
def on_deployment_completed(
|
|
93
|
+
self, success_count: int, failure_count: int, duration_ms: float
|
|
94
|
+
) -> None:
|
|
95
|
+
"""Called when all deployments complete."""
|
|
96
|
+
if self.progress:
|
|
97
|
+
self.progress.stop()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class AutoConfigureCommand(BaseCommand):
|
|
101
|
+
"""
|
|
102
|
+
Handle auto-configuration CLI commands.
|
|
103
|
+
|
|
104
|
+
This command provides a user-friendly interface for automatically configuring
|
|
105
|
+
agents based on detected project toolchain.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
def __init__(self):
|
|
109
|
+
"""Initialize the auto-configure command."""
|
|
110
|
+
super().__init__("auto-configure")
|
|
111
|
+
self.console = Console() if RICH_AVAILABLE else None
|
|
112
|
+
self._auto_config_manager = None
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def auto_config_manager(self) -> AutoConfigManagerService:
|
|
116
|
+
"""Get auto-configuration manager (lazy loaded)."""
|
|
117
|
+
if self._auto_config_manager is None:
|
|
118
|
+
from ...services.agents.auto_config_manager import (
|
|
119
|
+
AutoConfigManagerService,
|
|
120
|
+
)
|
|
121
|
+
from ...services.agents.recommender import AgentRecommenderService
|
|
122
|
+
from ...services.agents.registry import AgentRegistry
|
|
123
|
+
from ...services.project.toolchain_analyzer import (
|
|
124
|
+
ToolchainAnalyzerService,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Initialize services with dependency injection
|
|
128
|
+
toolchain_analyzer = ToolchainAnalyzerService()
|
|
129
|
+
agent_registry = AgentRegistry()
|
|
130
|
+
agent_recommender = AgentRecommenderService(agent_registry=agent_registry)
|
|
131
|
+
|
|
132
|
+
# Get deployment service
|
|
133
|
+
try:
|
|
134
|
+
from ...services.agents.deployment import AgentDeploymentService
|
|
135
|
+
|
|
136
|
+
agent_deployment = AgentDeploymentService()
|
|
137
|
+
except ImportError:
|
|
138
|
+
agent_deployment = None
|
|
139
|
+
|
|
140
|
+
self._auto_config_manager = AutoConfigManagerService(
|
|
141
|
+
toolchain_analyzer=toolchain_analyzer,
|
|
142
|
+
agent_recommender=agent_recommender,
|
|
143
|
+
agent_registry=agent_registry,
|
|
144
|
+
agent_deployment=agent_deployment,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return self._auto_config_manager
|
|
148
|
+
|
|
149
|
+
def validate_args(self, args) -> Optional[str]:
|
|
150
|
+
"""Validate command arguments."""
|
|
151
|
+
# Validate project path
|
|
152
|
+
project_path = (
|
|
153
|
+
Path(args.project_path)
|
|
154
|
+
if hasattr(args, "project_path") and args.project_path
|
|
155
|
+
else Path.cwd()
|
|
156
|
+
)
|
|
157
|
+
if not project_path.exists():
|
|
158
|
+
return f"Project path does not exist: {project_path}"
|
|
159
|
+
|
|
160
|
+
# Validate min_confidence range
|
|
161
|
+
if hasattr(args, "min_confidence") and args.min_confidence:
|
|
162
|
+
if not 0.0 <= args.min_confidence <= 1.0:
|
|
163
|
+
return "min_confidence must be between 0.0 and 1.0"
|
|
164
|
+
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
def run(self, args) -> CommandResult:
|
|
168
|
+
"""
|
|
169
|
+
Execute auto-configuration command.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
CommandResult with success status and exit code
|
|
173
|
+
"""
|
|
174
|
+
try:
|
|
175
|
+
# Setup logging
|
|
176
|
+
self.setup_logging(args)
|
|
177
|
+
|
|
178
|
+
# Validate arguments
|
|
179
|
+
error = self.validate_args(args)
|
|
180
|
+
if error:
|
|
181
|
+
return CommandResult.error_result(error)
|
|
182
|
+
|
|
183
|
+
# Get configuration options
|
|
184
|
+
project_path = (
|
|
185
|
+
Path(args.project_path)
|
|
186
|
+
if hasattr(args, "project_path") and args.project_path
|
|
187
|
+
else Path.cwd()
|
|
188
|
+
)
|
|
189
|
+
min_confidence = (
|
|
190
|
+
args.min_confidence
|
|
191
|
+
if hasattr(args, "min_confidence") and args.min_confidence
|
|
192
|
+
else 0.8
|
|
193
|
+
)
|
|
194
|
+
dry_run = (
|
|
195
|
+
args.preview or args.dry_run if hasattr(args, "preview") else False
|
|
196
|
+
)
|
|
197
|
+
skip_confirmation = args.yes if hasattr(args, "yes") and args.yes else False
|
|
198
|
+
json_output = args.json if hasattr(args, "json") and args.json else False
|
|
199
|
+
|
|
200
|
+
# Run preview or full configuration
|
|
201
|
+
if dry_run or args.preview if hasattr(args, "preview") else False:
|
|
202
|
+
return self._run_preview(project_path, min_confidence, json_output)
|
|
203
|
+
return self._run_full_configuration(
|
|
204
|
+
project_path, min_confidence, skip_confirmation, json_output
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
except KeyboardInterrupt:
|
|
208
|
+
if self.console:
|
|
209
|
+
self.console.print("\n\n❌ Operation cancelled by user")
|
|
210
|
+
else:
|
|
211
|
+
print("\n\nOperation cancelled by user")
|
|
212
|
+
return CommandResult.error_result("Operation cancelled", exit_code=130)
|
|
213
|
+
|
|
214
|
+
except Exception as e:
|
|
215
|
+
self.logger.exception("Auto-configuration failed")
|
|
216
|
+
error_msg = f"Auto-configuration failed: {e!s}"
|
|
217
|
+
if self.console:
|
|
218
|
+
self.console.print(f"\n❌ {error_msg}")
|
|
219
|
+
else:
|
|
220
|
+
print(f"\n{error_msg}")
|
|
221
|
+
return CommandResult.error_result(error_msg)
|
|
222
|
+
|
|
223
|
+
def _run_preview(
|
|
224
|
+
self, project_path: Path, min_confidence: float, json_output: bool
|
|
225
|
+
) -> CommandResult:
|
|
226
|
+
"""Run configuration preview without deploying."""
|
|
227
|
+
# Show analysis spinner
|
|
228
|
+
if self.console and not json_output:
|
|
229
|
+
with self.console.status("[bold green]Analyzing project toolchain..."):
|
|
230
|
+
preview = self.auto_config_manager.preview_configuration(
|
|
231
|
+
str(project_path), min_confidence
|
|
232
|
+
)
|
|
233
|
+
else:
|
|
234
|
+
preview = self.auto_config_manager.preview_configuration(
|
|
235
|
+
str(project_path), min_confidence
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Output results
|
|
239
|
+
if json_output:
|
|
240
|
+
return self._output_preview_json(preview)
|
|
241
|
+
return self._display_preview(preview)
|
|
242
|
+
|
|
243
|
+
def _run_full_configuration(
|
|
244
|
+
self,
|
|
245
|
+
project_path: Path,
|
|
246
|
+
min_confidence: float,
|
|
247
|
+
skip_confirmation: bool,
|
|
248
|
+
json_output: bool,
|
|
249
|
+
) -> CommandResult:
|
|
250
|
+
"""Run full auto-configuration with deployment."""
|
|
251
|
+
# Get preview first
|
|
252
|
+
if self.console and not json_output:
|
|
253
|
+
with self.console.status("[bold green]Analyzing project toolchain..."):
|
|
254
|
+
preview = self.auto_config_manager.preview_configuration(
|
|
255
|
+
str(project_path), min_confidence
|
|
256
|
+
)
|
|
257
|
+
else:
|
|
258
|
+
preview = self.auto_config_manager.preview_configuration(
|
|
259
|
+
str(project_path), min_confidence
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Display preview (unless JSON output)
|
|
263
|
+
if not json_output:
|
|
264
|
+
self._display_preview(preview)
|
|
265
|
+
|
|
266
|
+
# Ask for confirmation (unless skipped)
|
|
267
|
+
if not skip_confirmation and not json_output:
|
|
268
|
+
if not self._confirm_deployment(preview):
|
|
269
|
+
if self.console:
|
|
270
|
+
self.console.print("\n❌ Operation cancelled by user")
|
|
271
|
+
else:
|
|
272
|
+
print("\nOperation cancelled by user")
|
|
273
|
+
return CommandResult.error_result("Operation cancelled", exit_code=0)
|
|
274
|
+
|
|
275
|
+
# Execute configuration
|
|
276
|
+
observer = RichProgressObserver(self.console) if self.console else None
|
|
277
|
+
result = self.auto_config_manager.execute_configuration(
|
|
278
|
+
str(project_path), min_confidence, observer=observer
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Output results
|
|
282
|
+
if json_output:
|
|
283
|
+
return self._output_result_json(result)
|
|
284
|
+
return self._display_result(result)
|
|
285
|
+
|
|
286
|
+
def _display_preview(self, preview) -> CommandResult:
|
|
287
|
+
"""Display configuration preview with Rich formatting."""
|
|
288
|
+
if not self.console:
|
|
289
|
+
# Fallback to plain text
|
|
290
|
+
return self._display_preview_plain(preview)
|
|
291
|
+
|
|
292
|
+
# Display detected toolchain
|
|
293
|
+
self.console.print("\n📊 Detected Toolchain:", style="bold blue")
|
|
294
|
+
if preview.detected_toolchain and preview.detected_toolchain.components:
|
|
295
|
+
toolchain_table = Table(show_header=True, header_style="bold")
|
|
296
|
+
toolchain_table.add_column("Component", style="cyan")
|
|
297
|
+
toolchain_table.add_column("Version", style="yellow")
|
|
298
|
+
toolchain_table.add_column("Confidence", style="green")
|
|
299
|
+
|
|
300
|
+
for component in preview.detected_toolchain.components:
|
|
301
|
+
confidence_pct = int(component.confidence * 100)
|
|
302
|
+
bar = "█" * (confidence_pct // 10) + "░" * (10 - confidence_pct // 10)
|
|
303
|
+
confidence_str = f"{bar} {confidence_pct}%"
|
|
304
|
+
|
|
305
|
+
toolchain_table.add_row(
|
|
306
|
+
(
|
|
307
|
+
component.type.value
|
|
308
|
+
if hasattr(component.type, "value")
|
|
309
|
+
else str(component.type)
|
|
310
|
+
),
|
|
311
|
+
component.version or "Unknown",
|
|
312
|
+
confidence_str,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
self.console.print(toolchain_table)
|
|
316
|
+
else:
|
|
317
|
+
self.console.print(" No toolchain detected", style="yellow")
|
|
318
|
+
|
|
319
|
+
# Display recommended agents
|
|
320
|
+
self.console.print("\n🤖 Recommended Agents:", style="bold blue")
|
|
321
|
+
if preview.recommendations:
|
|
322
|
+
for rec in preview.recommendations:
|
|
323
|
+
confidence_pct = int(rec.confidence * 100)
|
|
324
|
+
icon = "✓" if rec.confidence >= 0.8 else "○"
|
|
325
|
+
self.console.print(
|
|
326
|
+
f" {icon} [bold]{rec.agent_id}[/bold] ({confidence_pct}% confidence)"
|
|
327
|
+
)
|
|
328
|
+
self.console.print(f" Reason: {rec.reasoning}", style="dim")
|
|
329
|
+
else:
|
|
330
|
+
self.console.print(" No agents recommended", style="yellow")
|
|
331
|
+
|
|
332
|
+
# Display validation issues
|
|
333
|
+
if preview.validation_result and preview.validation_result.issues:
|
|
334
|
+
self.console.print("\n⚠️ Validation Issues:", style="bold yellow")
|
|
335
|
+
for issue in preview.validation_result.issues:
|
|
336
|
+
severity_icon = {"error": "❌", "warning": "⚠️", "info": "ℹ️"}.get(
|
|
337
|
+
(
|
|
338
|
+
issue.severity.value
|
|
339
|
+
if hasattr(issue.severity, "value")
|
|
340
|
+
else str(issue.severity)
|
|
341
|
+
),
|
|
342
|
+
"•",
|
|
343
|
+
)
|
|
344
|
+
self.console.print(f" {severity_icon} {issue.message}", style="yellow")
|
|
345
|
+
|
|
346
|
+
return CommandResult.success_result()
|
|
347
|
+
|
|
348
|
+
def _display_preview_plain(self, preview) -> CommandResult:
|
|
349
|
+
"""Display preview in plain text (fallback when Rich not available)."""
|
|
350
|
+
print("\nDetected Toolchain:")
|
|
351
|
+
if preview.detected_toolchain and preview.detected_toolchain.components:
|
|
352
|
+
for component in preview.detected_toolchain.components:
|
|
353
|
+
confidence_pct = int(component.confidence * 100)
|
|
354
|
+
print(f" - {component.type}: {component.version} ({confidence_pct}%)")
|
|
355
|
+
else:
|
|
356
|
+
print(" No toolchain detected")
|
|
357
|
+
|
|
358
|
+
print("\nRecommended Agents:")
|
|
359
|
+
if preview.recommendations:
|
|
360
|
+
for rec in preview.recommendations:
|
|
361
|
+
confidence_pct = int(rec.confidence * 100)
|
|
362
|
+
print(f" - {rec.agent_id} ({confidence_pct}%)")
|
|
363
|
+
print(f" Reason: {rec.reasoning}")
|
|
364
|
+
else:
|
|
365
|
+
print(" No agents recommended")
|
|
366
|
+
|
|
367
|
+
if preview.validation_result and preview.validation_result.issues:
|
|
368
|
+
print("\nValidation Issues:")
|
|
369
|
+
for issue in preview.validation_result.issues:
|
|
370
|
+
print(f" - {issue.severity}: {issue.message}")
|
|
371
|
+
|
|
372
|
+
return CommandResult.success_result()
|
|
373
|
+
|
|
374
|
+
def _confirm_deployment(self, preview) -> bool:
|
|
375
|
+
"""Ask user to confirm deployment."""
|
|
376
|
+
if not preview.recommendations:
|
|
377
|
+
return False
|
|
378
|
+
|
|
379
|
+
if self.console:
|
|
380
|
+
self.console.print("\n" + "=" * 60)
|
|
381
|
+
self.console.print("Deploy these agents?", style="bold yellow")
|
|
382
|
+
self.console.print("=" * 60)
|
|
383
|
+
response = (
|
|
384
|
+
self.console.input("\n[bold]Proceed? (y/n/s for select):[/bold] ")
|
|
385
|
+
.strip()
|
|
386
|
+
.lower()
|
|
387
|
+
)
|
|
388
|
+
else:
|
|
389
|
+
print("\n" + "=" * 60)
|
|
390
|
+
print("Deploy these agents?")
|
|
391
|
+
print("=" * 60)
|
|
392
|
+
response = input("\nProceed? (y/n/s for select): ").strip().lower()
|
|
393
|
+
|
|
394
|
+
if response in ["y", "yes"]:
|
|
395
|
+
return True
|
|
396
|
+
if response in ["s", "select"]:
|
|
397
|
+
# TODO: Implement interactive selection
|
|
398
|
+
if self.console:
|
|
399
|
+
self.console.print(
|
|
400
|
+
"\n⚠️ Interactive selection not yet implemented",
|
|
401
|
+
style="yellow",
|
|
402
|
+
)
|
|
403
|
+
else:
|
|
404
|
+
print("\nInteractive selection not yet implemented")
|
|
405
|
+
return False
|
|
406
|
+
return False
|
|
407
|
+
|
|
408
|
+
def _display_result(self, result: ConfigurationResult) -> CommandResult:
|
|
409
|
+
"""Display configuration result."""
|
|
410
|
+
if not self.console:
|
|
411
|
+
return self._display_result_plain(result)
|
|
412
|
+
|
|
413
|
+
# Display summary
|
|
414
|
+
if result.status == ConfigurationStatus.SUCCESS:
|
|
415
|
+
panel = Panel(
|
|
416
|
+
f"✅ Auto-configuration completed successfully!\n\n"
|
|
417
|
+
f"Deployed {len(result.deployed_agents)} agent(s)",
|
|
418
|
+
title="Success",
|
|
419
|
+
border_style="green",
|
|
420
|
+
)
|
|
421
|
+
self.console.print(panel)
|
|
422
|
+
|
|
423
|
+
# Show deployed agents
|
|
424
|
+
if result.deployed_agents:
|
|
425
|
+
self.console.print("\n📦 Deployed Agents:", style="bold green")
|
|
426
|
+
for agent_id in result.deployed_agents:
|
|
427
|
+
self.console.print(f" ✓ {agent_id}")
|
|
428
|
+
|
|
429
|
+
return CommandResult.success_result()
|
|
430
|
+
|
|
431
|
+
if result.status == ConfigurationStatus.PARTIAL_SUCCESS:
|
|
432
|
+
panel = Panel(
|
|
433
|
+
f"⚠️ Auto-configuration partially completed\n\n"
|
|
434
|
+
f"Deployed: {len(result.deployed_agents)}\n"
|
|
435
|
+
f"Failed: {len(result.failed_agents)}",
|
|
436
|
+
title="Partial Success",
|
|
437
|
+
border_style="yellow",
|
|
438
|
+
)
|
|
439
|
+
self.console.print(panel)
|
|
440
|
+
|
|
441
|
+
if result.failed_agents:
|
|
442
|
+
self.console.print("\n❌ Failed Agents:", style="bold red")
|
|
443
|
+
for agent_id in result.failed_agents:
|
|
444
|
+
error = result.errors.get(agent_id, "Unknown error")
|
|
445
|
+
self.console.print(f" ✗ {agent_id}: {error}")
|
|
446
|
+
|
|
447
|
+
return CommandResult.error_result("Partial configuration", exit_code=1)
|
|
448
|
+
|
|
449
|
+
panel = Panel(
|
|
450
|
+
f"❌ Auto-configuration failed\n\n{result.errors.get('general', 'Unknown error')}",
|
|
451
|
+
title="Error",
|
|
452
|
+
border_style="red",
|
|
453
|
+
)
|
|
454
|
+
self.console.print(panel)
|
|
455
|
+
|
|
456
|
+
return CommandResult.error_result("Configuration failed", exit_code=1)
|
|
457
|
+
|
|
458
|
+
def _display_result_plain(self, result: ConfigurationResult) -> CommandResult:
|
|
459
|
+
"""Display result in plain text (fallback)."""
|
|
460
|
+
if result.status == ConfigurationStatus.SUCCESS:
|
|
461
|
+
print("\n✅ Auto-configuration completed successfully!")
|
|
462
|
+
print(f"Deployed {len(result.deployed_agents)} agent(s)")
|
|
463
|
+
|
|
464
|
+
if result.deployed_agents:
|
|
465
|
+
print("\nDeployed Agents:")
|
|
466
|
+
for agent_id in result.deployed_agents:
|
|
467
|
+
print(f" - {agent_id}")
|
|
468
|
+
|
|
469
|
+
return CommandResult.success_result()
|
|
470
|
+
|
|
471
|
+
if result.status == ConfigurationStatus.PARTIAL_SUCCESS:
|
|
472
|
+
print("\n⚠️ Auto-configuration partially completed")
|
|
473
|
+
print(f"Deployed: {len(result.deployed_agents)}")
|
|
474
|
+
print(f"Failed: {len(result.failed_agents)}")
|
|
475
|
+
|
|
476
|
+
if result.failed_agents:
|
|
477
|
+
print("\nFailed Agents:")
|
|
478
|
+
for agent_id in result.failed_agents:
|
|
479
|
+
error = result.errors.get(agent_id, "Unknown error")
|
|
480
|
+
print(f" - {agent_id}: {error}")
|
|
481
|
+
|
|
482
|
+
return CommandResult.error_result("Partial configuration", exit_code=1)
|
|
483
|
+
|
|
484
|
+
print("\n❌ Auto-configuration failed")
|
|
485
|
+
print(result.errors.get("general", "Unknown error"))
|
|
486
|
+
|
|
487
|
+
return CommandResult.error_result("Configuration failed", exit_code=1)
|
|
488
|
+
|
|
489
|
+
def _output_preview_json(self, preview) -> CommandResult:
|
|
490
|
+
"""Output preview as JSON."""
|
|
491
|
+
output = {
|
|
492
|
+
"detected_toolchain": {
|
|
493
|
+
"components": (
|
|
494
|
+
[
|
|
495
|
+
{
|
|
496
|
+
"type": (
|
|
497
|
+
c.type.value
|
|
498
|
+
if hasattr(c.type, "value")
|
|
499
|
+
else str(c.type)
|
|
500
|
+
),
|
|
501
|
+
"version": c.version,
|
|
502
|
+
"confidence": c.confidence,
|
|
503
|
+
}
|
|
504
|
+
for c in preview.detected_toolchain.components
|
|
505
|
+
]
|
|
506
|
+
if preview.detected_toolchain
|
|
507
|
+
else []
|
|
508
|
+
)
|
|
509
|
+
},
|
|
510
|
+
"recommendations": [
|
|
511
|
+
{
|
|
512
|
+
"agent_id": r.agent_id,
|
|
513
|
+
"confidence": r.confidence,
|
|
514
|
+
"reasoning": r.reasoning,
|
|
515
|
+
}
|
|
516
|
+
for r in preview.recommendations
|
|
517
|
+
],
|
|
518
|
+
"validation": {
|
|
519
|
+
"is_valid": (
|
|
520
|
+
preview.validation_result.is_valid
|
|
521
|
+
if preview.validation_result
|
|
522
|
+
else True
|
|
523
|
+
),
|
|
524
|
+
"issues": (
|
|
525
|
+
[
|
|
526
|
+
{
|
|
527
|
+
"severity": (
|
|
528
|
+
i.severity.value
|
|
529
|
+
if hasattr(i.severity, "value")
|
|
530
|
+
else str(i.severity)
|
|
531
|
+
),
|
|
532
|
+
"message": i.message,
|
|
533
|
+
}
|
|
534
|
+
for i in preview.validation_result.issues
|
|
535
|
+
]
|
|
536
|
+
if preview.validation_result
|
|
537
|
+
else []
|
|
538
|
+
),
|
|
539
|
+
},
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
print(json.dumps(output, indent=2))
|
|
543
|
+
return CommandResult.success_result(data=output)
|
|
544
|
+
|
|
545
|
+
def _output_result_json(self, result: ConfigurationResult) -> CommandResult:
|
|
546
|
+
"""Output result as JSON."""
|
|
547
|
+
output = {
|
|
548
|
+
"status": (
|
|
549
|
+
result.status.value
|
|
550
|
+
if hasattr(result.status, "value")
|
|
551
|
+
else str(result.status)
|
|
552
|
+
),
|
|
553
|
+
"deployed_agents": result.deployed_agents,
|
|
554
|
+
"failed_agents": result.failed_agents,
|
|
555
|
+
"errors": result.errors,
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
print(json.dumps(output, indent=2))
|
|
559
|
+
|
|
560
|
+
if result.status == ConfigurationStatus.SUCCESS:
|
|
561
|
+
return CommandResult.success_result(data=output)
|
|
562
|
+
return CommandResult.error_result(
|
|
563
|
+
"Configuration failed or partial", exit_code=1, data=output
|
|
564
|
+
)
|
|
@@ -266,4 +266,13 @@ def add_agents_subparser(subparsers) -> argparse.ArgumentParser:
|
|
|
266
266
|
help="Only show summary, not individual agents",
|
|
267
267
|
)
|
|
268
268
|
|
|
269
|
+
# Auto-configuration commands (TSK-0054 Phase 5)
|
|
270
|
+
from .auto_configure_parser import (
|
|
271
|
+
add_agents_detect_subparser,
|
|
272
|
+
add_agents_recommend_subparser,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
add_agents_detect_subparser(agents_subparsers)
|
|
276
|
+
add_agents_recommend_subparser(agents_subparsers)
|
|
277
|
+
|
|
269
278
|
return agents_parser
|