claude-mpm 5.0.2__py3-none-any.whl → 5.1.9__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.

Files changed (76) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
  3. claude_mpm/agents/PM_INSTRUCTIONS.md +1176 -909
  4. claude_mpm/agents/base_agent_loader.py +10 -35
  5. claude_mpm/agents/frontmatter_validator.py +68 -0
  6. claude_mpm/agents/templates/circuit-breakers.md +293 -44
  7. claude_mpm/cli/__init__.py +0 -1
  8. claude_mpm/cli/commands/__init__.py +2 -0
  9. claude_mpm/cli/commands/agent_state_manager.py +64 -11
  10. claude_mpm/cli/commands/agents.py +446 -25
  11. claude_mpm/cli/commands/auto_configure.py +535 -233
  12. claude_mpm/cli/commands/configure.py +545 -89
  13. claude_mpm/cli/commands/postmortem.py +401 -0
  14. claude_mpm/cli/commands/run.py +1 -39
  15. claude_mpm/cli/commands/skills.py +322 -19
  16. claude_mpm/cli/interactive/agent_wizard.py +302 -195
  17. claude_mpm/cli/parsers/agents_parser.py +137 -0
  18. claude_mpm/cli/parsers/auto_configure_parser.py +13 -0
  19. claude_mpm/cli/parsers/base_parser.py +4 -0
  20. claude_mpm/cli/parsers/skills_parser.py +7 -0
  21. claude_mpm/cli/startup.py +73 -32
  22. claude_mpm/commands/mpm-agents-auto-configure.md +2 -2
  23. claude_mpm/commands/mpm-agents-list.md +2 -2
  24. claude_mpm/commands/mpm-config-view.md +2 -2
  25. claude_mpm/commands/mpm-help.md +3 -0
  26. claude_mpm/commands/mpm-postmortem.md +123 -0
  27. claude_mpm/commands/mpm-session-resume.md +2 -2
  28. claude_mpm/commands/mpm-ticket-organize.md +2 -2
  29. claude_mpm/commands/mpm-ticket-view.md +2 -2
  30. claude_mpm/config/agent_presets.py +312 -82
  31. claude_mpm/config/skill_presets.py +392 -0
  32. claude_mpm/constants.py +1 -0
  33. claude_mpm/core/claude_runner.py +2 -25
  34. claude_mpm/core/framework/loaders/file_loader.py +54 -101
  35. claude_mpm/core/interactive_session.py +19 -5
  36. claude_mpm/core/oneshot_session.py +16 -4
  37. claude_mpm/core/output_style_manager.py +173 -43
  38. claude_mpm/core/protocols/__init__.py +23 -0
  39. claude_mpm/core/protocols/runner_protocol.py +103 -0
  40. claude_mpm/core/protocols/session_protocol.py +131 -0
  41. claude_mpm/core/shared/singleton_manager.py +11 -4
  42. claude_mpm/core/system_context.py +38 -0
  43. claude_mpm/core/unified_agent_registry.py +129 -1
  44. claude_mpm/core/unified_config.py +22 -0
  45. claude_mpm/hooks/claude_hooks/memory_integration.py +12 -1
  46. claude_mpm/models/agent_definition.py +7 -0
  47. claude_mpm/services/agents/cache_git_manager.py +621 -0
  48. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +110 -3
  49. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +195 -1
  50. claude_mpm/services/agents/sources/git_source_sync_service.py +37 -5
  51. claude_mpm/services/analysis/__init__.py +25 -0
  52. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  53. claude_mpm/services/analysis/postmortem_service.py +765 -0
  54. claude_mpm/services/command_deployment_service.py +108 -5
  55. claude_mpm/services/core/base.py +7 -2
  56. claude_mpm/services/diagnostics/checks/mcp_services_check.py +7 -15
  57. claude_mpm/services/git/git_operations_service.py +8 -8
  58. claude_mpm/services/mcp_config_manager.py +75 -145
  59. claude_mpm/services/mcp_gateway/core/process_pool.py +22 -16
  60. claude_mpm/services/mcp_service_verifier.py +6 -3
  61. claude_mpm/services/monitor/daemon.py +28 -8
  62. claude_mpm/services/monitor/daemon_manager.py +96 -19
  63. claude_mpm/services/project/project_organizer.py +4 -0
  64. claude_mpm/services/runner_configuration_service.py +16 -3
  65. claude_mpm/services/session_management_service.py +16 -4
  66. claude_mpm/utils/agent_filters.py +288 -0
  67. claude_mpm/utils/gitignore.py +3 -0
  68. claude_mpm/utils/migration.py +372 -0
  69. claude_mpm/utils/progress.py +5 -1
  70. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/METADATA +69 -8
  71. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/RECORD +76 -62
  72. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  73. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/WHEEL +0 -0
  74. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/entry_points.txt +0 -0
  75. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/licenses/LICENSE +0 -0
  76. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/top_level.txt +0 -0
@@ -3,13 +3,16 @@ Auto-Configuration CLI Command for Claude MPM Framework
3
3
  ========================================================
4
4
 
5
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.
6
+ feature, allowing users to automatically configure BOTH agents AND skills based on
7
+ detected toolchain.
7
8
 
8
9
  DESIGN DECISION: Uses rich for beautiful terminal output, implements interactive
9
10
  confirmation, and provides comprehensive error handling. Supports both interactive
10
- and non-interactive modes for flexibility.
11
+ and non-interactive modes for flexibility. Orchestrates both agent auto-config
12
+ (via AutoConfigManagerService) and skill recommendations (via SkillsDeployer).
11
13
 
12
14
  Part of TSK-0054: Auto-Configuration Feature - Phase 5
15
+ Unified Auto-Configure: 1M-502 Phase 2
13
16
  """
14
17
 
15
18
  import json
@@ -29,7 +32,6 @@ except ImportError:
29
32
  from ...core.enums import OperationResult
30
33
  from ...services.agents.auto_config_manager import AutoConfigManagerService
31
34
  from ...services.agents.observers import NullObserver
32
- from ...services.core.models.agent_config import ConfigurationResult
33
35
  from ..shared import BaseCommand, CommandResult
34
36
 
35
37
 
@@ -100,7 +102,11 @@ class AutoConfigureCommand(BaseCommand):
100
102
  Handle auto-configuration CLI commands.
101
103
 
102
104
  This command provides a user-friendly interface for automatically configuring
103
- agents based on detected project toolchain.
105
+ BOTH agents AND skills based on detected project toolchain.
106
+
107
+ Orchestrates:
108
+ 1. Agent auto-configuration (via AutoConfigManagerService)
109
+ 2. Skills recommendations and deployment (via SkillsDeployer + agent-skill mapping)
104
110
  """
105
111
 
106
112
  def __init__(self):
@@ -108,6 +114,7 @@ class AutoConfigureCommand(BaseCommand):
108
114
  super().__init__("auto-configure")
109
115
  self.console = Console() if RICH_AVAILABLE else None
110
116
  self._auto_config_manager = None
117
+ self._skills_deployer = None
111
118
 
112
119
  @property
113
120
  def auto_config_manager(self) -> AutoConfigManagerService:
@@ -140,6 +147,15 @@ class AutoConfigureCommand(BaseCommand):
140
147
 
141
148
  return self._auto_config_manager
142
149
 
150
+ @property
151
+ def skills_deployer(self):
152
+ """Get skills deployer instance (lazy loaded)."""
153
+ if self._skills_deployer is None:
154
+ from ...services.skills_deployer import SkillsDeployerService
155
+
156
+ self._skills_deployer = SkillsDeployerService()
157
+ return self._skills_deployer
158
+
143
159
  def validate_args(self, args) -> Optional[str]:
144
160
  """Validate command arguments."""
145
161
  # Validate project path
@@ -191,11 +207,26 @@ class AutoConfigureCommand(BaseCommand):
191
207
  skip_confirmation = args.yes if hasattr(args, "yes") and args.yes else False
192
208
  json_output = args.json if hasattr(args, "json") and args.json else False
193
209
 
210
+ # Determine what to configure (agents, skills, or both)
211
+ configure_agents = not getattr(args, "skills_only", False)
212
+ configure_skills = not getattr(args, "agents_only", False)
213
+
194
214
  # Run preview or full configuration
195
215
  if dry_run or args.preview if hasattr(args, "preview") else False:
196
- return self._run_preview(project_path, min_confidence, json_output)
216
+ return self._run_preview(
217
+ project_path,
218
+ min_confidence,
219
+ json_output,
220
+ configure_agents,
221
+ configure_skills,
222
+ )
197
223
  return self._run_full_configuration(
198
- project_path, min_confidence, skip_confirmation, json_output
224
+ project_path,
225
+ min_confidence,
226
+ skip_confirmation,
227
+ json_output,
228
+ configure_agents,
229
+ configure_skills,
199
230
  )
200
231
 
201
232
  except KeyboardInterrupt:
@@ -215,24 +246,47 @@ class AutoConfigureCommand(BaseCommand):
215
246
  return CommandResult.error_result(error_msg)
216
247
 
217
248
  def _run_preview(
218
- self, project_path: Path, min_confidence: float, json_output: bool
249
+ self,
250
+ project_path: Path,
251
+ min_confidence: float,
252
+ json_output: bool,
253
+ configure_agents: bool = True,
254
+ configure_skills: bool = True,
219
255
  ) -> CommandResult:
220
256
  """Run configuration preview without deploying."""
221
- # Show analysis spinner
222
- if self.console and not json_output:
223
- with self.console.status("[bold green]Analyzing project toolchain..."):
224
- preview = self.auto_config_manager.preview_configuration(
257
+ # Get agent preview
258
+ agent_preview = None
259
+ if configure_agents:
260
+ if self.console and not json_output:
261
+ with self.console.status("[bold green]Analyzing project toolchain..."):
262
+ agent_preview = self.auto_config_manager.preview_configuration(
263
+ project_path, min_confidence
264
+ )
265
+ else:
266
+ agent_preview = self.auto_config_manager.preview_configuration(
225
267
  project_path, min_confidence
226
268
  )
227
- else:
228
- preview = self.auto_config_manager.preview_configuration(
229
- project_path, min_confidence
230
- )
269
+
270
+ # Get skills recommendations
271
+ skills_recommendations = None
272
+ if configure_skills:
273
+ if self.console and not json_output:
274
+ with self.console.status("[bold green]Analyzing skill requirements..."):
275
+ skills_recommendations = self._recommend_skills(agent_preview)
276
+ else:
277
+ skills_recommendations = self._recommend_skills(agent_preview)
231
278
 
232
279
  # Output results
233
280
  if json_output:
234
- return self._output_preview_json(preview)
235
- return self._display_preview(preview)
281
+ return self._output_preview_json(
282
+ agent_preview,
283
+ skills_recommendations,
284
+ configure_agents,
285
+ configure_skills,
286
+ )
287
+ return self._display_preview(
288
+ agent_preview, skills_recommendations, configure_agents, configure_skills
289
+ )
236
290
 
237
291
  def _run_full_configuration(
238
292
  self,
@@ -240,147 +294,251 @@ class AutoConfigureCommand(BaseCommand):
240
294
  min_confidence: float,
241
295
  skip_confirmation: bool,
242
296
  json_output: bool,
297
+ configure_agents: bool = True,
298
+ configure_skills: bool = True,
243
299
  ) -> CommandResult:
244
300
  """Run full auto-configuration with deployment."""
245
- # Get preview first
246
- if self.console and not json_output:
247
- with self.console.status("[bold green]Analyzing project toolchain..."):
248
- preview = self.auto_config_manager.preview_configuration(
301
+ # Get agent preview
302
+ agent_preview = None
303
+ if configure_agents:
304
+ if self.console and not json_output:
305
+ with self.console.status("[bold green]Analyzing project toolchain..."):
306
+ agent_preview = self.auto_config_manager.preview_configuration(
307
+ project_path, min_confidence
308
+ )
309
+ else:
310
+ agent_preview = self.auto_config_manager.preview_configuration(
249
311
  project_path, min_confidence
250
312
  )
251
- else:
252
- preview = self.auto_config_manager.preview_configuration(
253
- project_path, min_confidence
254
- )
313
+
314
+ # Get skills recommendations
315
+ skills_recommendations = None
316
+ if configure_skills:
317
+ if self.console and not json_output:
318
+ with self.console.status("[bold green]Analyzing skill requirements..."):
319
+ skills_recommendations = self._recommend_skills(agent_preview)
320
+ else:
321
+ skills_recommendations = self._recommend_skills(agent_preview)
255
322
 
256
323
  # Display preview (unless JSON output)
257
324
  if not json_output:
258
- self._display_preview(preview)
325
+ self._display_preview(
326
+ agent_preview,
327
+ skills_recommendations,
328
+ configure_agents,
329
+ configure_skills,
330
+ )
259
331
 
260
332
  # Ask for confirmation (unless skipped)
261
333
  if not skip_confirmation and not json_output:
262
- if not self._confirm_deployment(preview):
334
+ if not self._confirm_deployment(
335
+ agent_preview,
336
+ skills_recommendations,
337
+ configure_agents,
338
+ configure_skills,
339
+ ):
263
340
  if self.console:
264
341
  self.console.print("\n❌ Operation cancelled by user")
265
342
  else:
266
343
  print("\nOperation cancelled by user")
267
344
  return CommandResult.error_result("Operation cancelled", exit_code=0)
268
345
 
269
- # Execute configuration
270
- import asyncio
271
-
272
- observer = RichProgressObserver(self.console) if self.console else None
273
- result = asyncio.run(
274
- self.auto_config_manager.auto_configure(
275
- project_path,
276
- confirmation_required=False, # Already confirmed above
277
- dry_run=False,
278
- min_confidence=min_confidence,
279
- observer=observer,
346
+ # Execute agent configuration
347
+ agent_result = None
348
+ if configure_agents and agent_preview:
349
+ import asyncio
350
+
351
+ observer = RichProgressObserver(self.console) if self.console else None
352
+ agent_result = asyncio.run(
353
+ self.auto_config_manager.auto_configure(
354
+ project_path,
355
+ confirmation_required=False, # Already confirmed above
356
+ dry_run=False,
357
+ min_confidence=min_confidence,
358
+ observer=observer,
359
+ )
280
360
  )
281
- )
361
+
362
+ # Deploy skills
363
+ skills_result = None
364
+ if configure_skills and skills_recommendations:
365
+ if self.console and not json_output:
366
+ self.console.print("\n[bold cyan]Deploying skills...[/bold cyan]\n")
367
+ skills_result = self._deploy_skills(skills_recommendations)
282
368
 
283
369
  # Output results
284
370
  if json_output:
285
- return self._output_result_json(result)
286
- return self._display_result(result)
371
+ return self._output_result_json(agent_result, skills_result)
372
+ return self._display_result(agent_result, skills_result)
287
373
 
288
- def _display_preview(self, preview) -> CommandResult:
374
+ def _display_preview(
375
+ self,
376
+ agent_preview,
377
+ skills_recommendations=None,
378
+ configure_agents=True,
379
+ configure_skills=True,
380
+ ) -> CommandResult:
289
381
  """Display configuration preview with Rich formatting."""
290
382
  if not self.console:
291
383
  # Fallback to plain text
292
- return self._display_preview_plain(preview)
384
+ return self._display_preview_plain(
385
+ agent_preview,
386
+ skills_recommendations,
387
+ configure_agents,
388
+ configure_skills,
389
+ )
293
390
 
294
- # Display detected toolchain
295
- self.console.print("\n📊 Detected Toolchain:", style="bold blue")
296
- if preview.detected_toolchain and preview.detected_toolchain.components:
297
- toolchain_table = Table(show_header=True, header_style="bold")
298
- toolchain_table.add_column("Component", style="cyan")
299
- toolchain_table.add_column("Version", style="yellow")
300
- toolchain_table.add_column("Confidence", style="green")
301
-
302
- for component in preview.detected_toolchain.components:
303
- confidence_pct = int(component.confidence * 100)
304
- bar = "█" * (confidence_pct // 10) + "░" * (10 - confidence_pct // 10)
305
- confidence_str = f"{bar} {confidence_pct}%"
306
-
307
- toolchain_table.add_row(
308
- (
309
- component.type.value
310
- if hasattr(component.type, "value")
311
- else str(component.type)
312
- ),
313
- component.version or "Unknown",
314
- confidence_str,
315
- )
391
+ # Only show toolchain and agents if configuring agents
392
+ if not configure_agents:
393
+ agent_preview = None
316
394
 
317
- self.console.print(toolchain_table)
318
- else:
319
- self.console.print(" No toolchain detected", style="yellow")
320
-
321
- # Display recommended agents
322
- self.console.print("\n🤖 Recommended Agents:", style="bold blue")
323
- if preview.recommendations:
324
- for rec in preview.recommendations:
325
- confidence_pct = int(rec.confidence * 100)
326
- icon = "" if rec.confidence >= 0.8 else ""
327
- self.console.print(
328
- f" {icon} [bold]{rec.agent_id}[/bold] ({confidence_pct}% confidence)"
329
- )
330
- self.console.print(f" Reason: {rec.reasoning}", style="dim")
331
- else:
332
- self.console.print(" No agents recommended", style="yellow")
333
-
334
- # Display validation issues
335
- if preview.validation_result and preview.validation_result.issues:
336
- self.console.print("\n⚠️ Validation Issues:", style="bold yellow")
337
- for issue in preview.validation_result.issues:
338
- severity_icon = {"error": "❌", "warning": "⚠️", "info": "ℹ️"}.get(
339
- (
340
- issue.severity.value
341
- if hasattr(issue.severity, "value")
342
- else str(issue.severity)
343
- ),
344
- "•",
345
- )
346
- self.console.print(f" {severity_icon} {issue.message}", style="yellow")
395
+ # Display detected toolchain
396
+ if configure_agents and agent_preview:
397
+ self.console.print("\n📊 Detected Toolchain:", style="bold blue")
398
+ if (
399
+ agent_preview.detected_toolchain
400
+ and agent_preview.detected_toolchain.components
401
+ ):
402
+ toolchain_table = Table(show_header=True, header_style="bold")
403
+ toolchain_table.add_column("Component", style="cyan")
404
+ toolchain_table.add_column("Version", style="yellow")
405
+ toolchain_table.add_column("Confidence", style="green")
406
+
407
+ for component in agent_preview.detected_toolchain.components:
408
+ confidence_pct = int(component.confidence * 100)
409
+ bar = "█" * (confidence_pct // 10) + "░" * (
410
+ 10 - confidence_pct // 10
411
+ )
412
+ confidence_str = f"{bar} {confidence_pct}%"
413
+
414
+ toolchain_table.add_row(
415
+ (
416
+ component.type.value
417
+ if hasattr(component.type, "value")
418
+ else str(component.type)
419
+ ),
420
+ component.version or "Unknown",
421
+ confidence_str,
422
+ )
423
+
424
+ self.console.print(toolchain_table)
425
+ else:
426
+ self.console.print(" No toolchain detected", style="yellow")
427
+
428
+ # Display recommended agents
429
+ self.console.print("\n🤖 Recommended Agents:", style="bold blue")
430
+ if agent_preview.recommendations:
431
+ for rec in agent_preview.recommendations:
432
+ confidence_pct = int(rec.confidence * 100)
433
+ icon = "✓" if rec.confidence >= 0.8 else "○"
434
+ self.console.print(
435
+ f" {icon} [bold]{rec.agent_id}[/bold] ({confidence_pct}% confidence)"
436
+ )
437
+ self.console.print(f" Reason: {rec.reasoning}", style="dim")
438
+ else:
439
+ self.console.print(" No agents recommended", style="yellow")
440
+
441
+ # Display validation issues
442
+ if (
443
+ agent_preview.validation_result
444
+ and agent_preview.validation_result.issues
445
+ ):
446
+ self.console.print("\n⚠️ Validation Issues:", style="bold yellow")
447
+ for issue in agent_preview.validation_result.issues:
448
+ severity_icon = {"error": "❌", "warning": "⚠️", "info": "ℹ️"}.get(
449
+ (
450
+ issue.severity.value
451
+ if hasattr(issue.severity, "value")
452
+ else str(issue.severity)
453
+ ),
454
+ "•",
455
+ )
456
+ self.console.print(
457
+ f" {severity_icon} {issue.message}", style="yellow"
458
+ )
459
+
460
+ # Display recommended skills
461
+ if configure_skills and skills_recommendations:
462
+ self.console.print("\n🎯 Recommended Skills:", style="bold blue")
463
+ for skill in skills_recommendations:
464
+ self.console.print(f" ✓ [bold]{skill}[/bold]")
347
465
 
348
466
  return CommandResult.success_result()
349
467
 
350
- def _display_preview_plain(self, preview) -> CommandResult:
468
+ def _display_preview_plain(
469
+ self,
470
+ agent_preview,
471
+ skills_recommendations=None,
472
+ configure_agents=True,
473
+ configure_skills=True,
474
+ ) -> CommandResult:
351
475
  """Display preview in plain text (fallback when Rich not available)."""
352
- print("\nDetected Toolchain:")
353
- if preview.detected_toolchain and preview.detected_toolchain.components:
354
- for component in preview.detected_toolchain.components:
355
- confidence_pct = int(component.confidence * 100)
356
- print(f" - {component.type}: {component.version} ({confidence_pct}%)")
357
- else:
358
- print(" No toolchain detected")
359
-
360
- print("\nRecommended Agents:")
361
- if preview.recommendations:
362
- for rec in preview.recommendations:
363
- confidence_pct = int(rec.confidence * 100)
364
- print(f" - {rec.agent_id} ({confidence_pct}%)")
365
- print(f" Reason: {rec.reasoning}")
366
- else:
367
- print(" No agents recommended")
476
+ if configure_agents and agent_preview:
477
+ print("\nDetected Toolchain:")
478
+ if (
479
+ agent_preview.detected_toolchain
480
+ and agent_preview.detected_toolchain.components
481
+ ):
482
+ for component in agent_preview.detected_toolchain.components:
483
+ confidence_pct = int(component.confidence * 100)
484
+ print(
485
+ f" - {component.type}: {component.version} ({confidence_pct}%)"
486
+ )
487
+ else:
488
+ print(" No toolchain detected")
489
+
490
+ print("\nRecommended Agents:")
491
+ if agent_preview.recommendations:
492
+ for rec in agent_preview.recommendations:
493
+ confidence_pct = int(rec.confidence * 100)
494
+ print(f" - {rec.agent_id} ({confidence_pct}%)")
495
+ print(f" Reason: {rec.reasoning}")
496
+ else:
497
+ print(" No agents recommended")
368
498
 
369
- if preview.validation_result and preview.validation_result.issues:
370
- print("\nValidation Issues:")
371
- for issue in preview.validation_result.issues:
372
- print(f" - {issue.severity}: {issue.message}")
499
+ if (
500
+ agent_preview.validation_result
501
+ and agent_preview.validation_result.issues
502
+ ):
503
+ print("\nValidation Issues:")
504
+ for issue in agent_preview.validation_result.issues:
505
+ print(f" - {issue.severity}: {issue.message}")
506
+
507
+ if configure_skills and skills_recommendations:
508
+ print("\nRecommended Skills:")
509
+ for skill in skills_recommendations:
510
+ print(f" - {skill}")
373
511
 
374
512
  return CommandResult.success_result()
375
513
 
376
- def _confirm_deployment(self, preview) -> bool:
514
+ def _confirm_deployment(
515
+ self,
516
+ agent_preview,
517
+ skills_recommendations=None,
518
+ configure_agents=True,
519
+ configure_skills=True,
520
+ ) -> bool:
377
521
  """Ask user to confirm deployment."""
378
- if not preview.recommendations:
522
+ has_agents = (
523
+ configure_agents and agent_preview and agent_preview.recommendations
524
+ )
525
+ has_skills = configure_skills and skills_recommendations
526
+
527
+ if not has_agents and not has_skills:
379
528
  return False
380
529
 
530
+ # Build confirmation message
531
+ items = []
532
+ if has_agents:
533
+ items.append(f"{len(agent_preview.recommendations)} agent(s)")
534
+ if has_skills:
535
+ items.append(f"{len(skills_recommendations)} skill(s)")
536
+
537
+ message = f"Deploy {' and '.join(items)}?"
538
+
381
539
  if self.console:
382
540
  self.console.print("\n" + "=" * 60)
383
- self.console.print("Deploy these agents?", style="bold yellow")
541
+ self.console.print(message, style="bold yellow")
384
542
  self.console.print("=" * 60)
385
543
  response = (
386
544
  self.console.input("\n[bold]Proceed? (y/n/s for select):[/bold] ")
@@ -389,7 +547,7 @@ class AutoConfigureCommand(BaseCommand):
389
547
  )
390
548
  else:
391
549
  print("\n" + "=" * 60)
392
- print("Deploy these agents?")
550
+ print(message)
393
551
  print("=" * 60)
394
552
  response = input("\nProceed? (y/n/s for select): ").strip().lower()
395
553
 
@@ -407,160 +565,304 @@ class AutoConfigureCommand(BaseCommand):
407
565
  return False
408
566
  return False
409
567
 
410
- def _display_result(self, result: ConfigurationResult) -> CommandResult:
568
+ def _display_result(
569
+ self, agent_result: Optional = None, skills_result: Optional[dict] = None
570
+ ) -> CommandResult:
411
571
  """Display configuration result."""
412
572
  if not self.console:
413
- return self._display_result_plain(result)
573
+ return self._display_result_plain(agent_result, skills_result)
574
+
575
+ # Determine overall success
576
+ agent_success = (
577
+ (agent_result and agent_result.status == OperationResult.SUCCESS)
578
+ if agent_result
579
+ else True
580
+ )
581
+ skills_success = not skills_result or (
582
+ skills_result and not skills_result.get("errors")
583
+ )
584
+ overall_success = agent_success and skills_success
414
585
 
415
586
  # Display summary
416
- if result.status == OperationResult.SUCCESS:
417
- panel = Panel(
418
- f"✅ Auto-configuration completed successfully!\n\n"
419
- f"Deployed {len(result.deployed_agents)} agent(s)",
420
- title="Success",
421
- border_style="green",
422
- )
587
+ if overall_success:
588
+ # Build summary message
589
+ deployed_items = []
590
+ if agent_result and agent_result.deployed_agents:
591
+ deployed_items.append(f"{len(agent_result.deployed_agents)} agent(s)")
592
+ if skills_result and skills_result.get("deployed"):
593
+ deployed_items.append(f"{len(skills_result['deployed'])} skill(s)")
594
+
595
+ panel_text = "✅ Auto-configuration completed successfully!\n\n"
596
+ if deployed_items:
597
+ panel_text += f"Deployed {' and '.join(deployed_items)}"
598
+ else:
599
+ panel_text += "No deployments needed"
600
+
601
+ panel = Panel(panel_text, title="Success", border_style="green")
423
602
  self.console.print(panel)
424
603
 
425
604
  # Show deployed agents
426
- if result.deployed_agents:
605
+ if agent_result and agent_result.deployed_agents:
427
606
  self.console.print("\n📦 Deployed Agents:", style="bold green")
428
- for agent_id in result.deployed_agents:
607
+ for agent_id in agent_result.deployed_agents:
429
608
  self.console.print(f" ✓ {agent_id}")
430
609
 
610
+ # Show deployed skills
611
+ if skills_result and skills_result.get("deployed"):
612
+ self.console.print("\n🎯 Deployed Skills:", style="bold green")
613
+ for skill in skills_result["deployed"]:
614
+ self.console.print(f" ✓ {skill}")
615
+
431
616
  return CommandResult.success_result()
432
617
 
433
- if result.status == OperationResult.WARNING:
434
- panel = Panel(
435
- f"⚠️ Auto-configuration partially completed\n\n"
436
- f"Deployed: {len(result.deployed_agents)}\n"
437
- f"Failed: {len(result.failed_agents)}",
438
- title="Partial Success",
439
- border_style="yellow",
440
- )
441
- self.console.print(panel)
618
+ # Partial or complete failure
619
+ has_errors = False
620
+ if agent_result and agent_result.status in [
621
+ OperationResult.WARNING,
622
+ OperationResult.FAILED,
623
+ ]:
624
+ has_errors = True
442
625
 
443
- if result.failed_agents:
626
+ if agent_result.status == OperationResult.WARNING:
627
+ self.console.print(
628
+ "\n⚠️ Agent configuration partially completed", style="yellow"
629
+ )
630
+ else:
631
+ self.console.print("\n❌ Agent configuration failed", style="red")
632
+
633
+ if agent_result.failed_agents:
444
634
  self.console.print("\n❌ Failed Agents:", style="bold red")
445
- for agent_id in result.failed_agents:
446
- error = result.errors.get(agent_id, "Unknown error")
635
+ for agent_id in agent_result.failed_agents:
636
+ error = agent_result.errors.get(agent_id, "Unknown error")
447
637
  self.console.print(f" ✗ {agent_id}: {error}")
448
638
 
449
- return CommandResult.error_result("Partial configuration", exit_code=1)
450
-
451
- panel = Panel(
452
- f"❌ Auto-configuration failed\n\n{result.errors.get('general', 'Unknown error')}",
453
- title="Error",
454
- border_style="red",
639
+ if skills_result and skills_result.get("errors"):
640
+ has_errors = True
641
+ self.console.print("\n❌ Skill deployment failed", style="red")
642
+ for error in skills_result["errors"]:
643
+ self.console.print(f" ✗ {error}")
644
+
645
+ return (
646
+ CommandResult.error_result(
647
+ "Configuration partially succeeded"
648
+ if (agent_success or skills_success)
649
+ else "Configuration failed",
650
+ exit_code=1,
651
+ )
652
+ if has_errors
653
+ else CommandResult.success_result()
455
654
  )
456
- self.console.print(panel)
457
-
458
- return CommandResult.error_result("Configuration failed", exit_code=1)
459
655
 
460
- def _display_result_plain(self, result: ConfigurationResult) -> CommandResult:
656
+ def _display_result_plain(
657
+ self, agent_result: Optional = None, skills_result: Optional[dict] = None
658
+ ) -> CommandResult:
461
659
  """Display result in plain text (fallback)."""
462
- if result.status == OperationResult.SUCCESS:
660
+ # Determine overall success
661
+ agent_success = (
662
+ (agent_result and agent_result.status == OperationResult.SUCCESS)
663
+ if agent_result
664
+ else True
665
+ )
666
+ skills_success = not skills_result or not skills_result.get("errors")
667
+ overall_success = agent_success and skills_success
668
+
669
+ if overall_success:
463
670
  print("\n✅ Auto-configuration completed successfully!")
464
- print(f"Deployed {len(result.deployed_agents)} agent(s)")
465
671
 
466
- if result.deployed_agents:
672
+ if agent_result and agent_result.deployed_agents:
673
+ print(f"Deployed {len(agent_result.deployed_agents)} agent(s)")
467
674
  print("\nDeployed Agents:")
468
- for agent_id in result.deployed_agents:
675
+ for agent_id in agent_result.deployed_agents:
469
676
  print(f" - {agent_id}")
470
677
 
678
+ if skills_result and skills_result.get("deployed"):
679
+ print(f"\nDeployed {len(skills_result['deployed'])} skill(s)")
680
+ print("\nDeployed Skills:")
681
+ for skill in skills_result["deployed"]:
682
+ print(f" - {skill}")
683
+
471
684
  return CommandResult.success_result()
472
685
 
473
- if result.status == OperationResult.WARNING:
474
- print("\n⚠️ Auto-configuration partially completed")
475
- print(f"Deployed: {len(result.deployed_agents)}")
476
- print(f"Failed: {len(result.failed_agents)}")
686
+ # Partial or complete failure
687
+ has_errors = False
688
+ if agent_result and agent_result.status in [
689
+ OperationResult.WARNING,
690
+ OperationResult.FAILED,
691
+ ]:
692
+ has_errors = True
693
+ print(
694
+ "\n⚠️ Agent configuration partially completed"
695
+ if agent_result.status == OperationResult.WARNING
696
+ else "\n❌ Agent configuration failed"
697
+ )
477
698
 
478
- if result.failed_agents:
699
+ if agent_result.failed_agents:
479
700
  print("\nFailed Agents:")
480
- for agent_id in result.failed_agents:
481
- error = result.errors.get(agent_id, "Unknown error")
701
+ for agent_id in agent_result.failed_agents:
702
+ error = agent_result.errors.get(agent_id, "Unknown error")
482
703
  print(f" - {agent_id}: {error}")
483
704
 
484
- return CommandResult.error_result("Partial configuration", exit_code=1)
485
-
486
- print("\n❌ Auto-configuration failed")
487
- print(result.errors.get("general", "Unknown error"))
488
-
489
- return CommandResult.error_result("Configuration failed", exit_code=1)
705
+ if skills_result and skills_result.get("errors"):
706
+ has_errors = True
707
+ print("\n❌ Skill deployment failed")
708
+ for error in skills_result["errors"]:
709
+ print(f" - {error}")
710
+
711
+ return (
712
+ CommandResult.error_result(
713
+ "Configuration partially succeeded"
714
+ if (agent_success or skills_success)
715
+ else "Configuration failed",
716
+ exit_code=1,
717
+ )
718
+ if has_errors
719
+ else CommandResult.success_result()
720
+ )
490
721
 
491
- def _output_preview_json(self, preview) -> CommandResult:
722
+ def _output_preview_json(
723
+ self,
724
+ agent_preview,
725
+ skills_recommendations=None,
726
+ configure_agents=True,
727
+ configure_skills=True,
728
+ ) -> CommandResult:
492
729
  """Output preview as JSON."""
493
- output = {
494
- "detected_toolchain": {
495
- "components": (
496
- [
497
- {
498
- "type": (
499
- c.type.value
500
- if hasattr(c.type, "value")
501
- else str(c.type)
502
- ),
503
- "version": c.version,
504
- "confidence": c.confidence,
505
- }
506
- for c in preview.detected_toolchain.components
507
- ]
508
- if preview.detected_toolchain
509
- else []
510
- )
511
- },
512
- "recommendations": [
513
- {
514
- "agent_id": r.agent_id,
515
- "confidence": r.confidence,
516
- "reasoning": r.reasoning,
517
- }
518
- for r in preview.recommendations
519
- ],
520
- "validation": {
521
- "is_valid": (
522
- preview.validation_result.is_valid
523
- if preview.validation_result
524
- else True
525
- ),
526
- "issues": (
527
- [
528
- {
529
- "severity": (
530
- i.severity.value
531
- if hasattr(i.severity, "value")
532
- else str(i.severity)
533
- ),
534
- "message": i.message,
535
- }
536
- for i in preview.validation_result.issues
537
- ]
538
- if preview.validation_result
539
- else []
540
- ),
541
- },
542
- }
730
+ output = {}
731
+
732
+ if configure_agents and agent_preview:
733
+ output["agents"] = {
734
+ "detected_toolchain": {
735
+ "components": (
736
+ [
737
+ {
738
+ "type": (
739
+ c.type.value
740
+ if hasattr(c.type, "value")
741
+ else str(c.type)
742
+ ),
743
+ "version": c.version,
744
+ "confidence": c.confidence,
745
+ }
746
+ for c in agent_preview.detected_toolchain.components
747
+ ]
748
+ if agent_preview.detected_toolchain
749
+ else []
750
+ )
751
+ },
752
+ "recommendations": [
753
+ {
754
+ "agent_id": r.agent_id,
755
+ "confidence": r.confidence,
756
+ "reasoning": r.reasoning,
757
+ }
758
+ for r in agent_preview.recommendations
759
+ ],
760
+ "validation": {
761
+ "is_valid": (
762
+ agent_preview.validation_result.is_valid
763
+ if agent_preview.validation_result
764
+ else True
765
+ ),
766
+ "issues": (
767
+ [
768
+ {
769
+ "severity": (
770
+ i.severity.value
771
+ if hasattr(i.severity, "value")
772
+ else str(i.severity)
773
+ ),
774
+ "message": i.message,
775
+ }
776
+ for i in agent_preview.validation_result.issues
777
+ ]
778
+ if agent_preview.validation_result
779
+ else []
780
+ ),
781
+ },
782
+ }
783
+
784
+ if configure_skills and skills_recommendations:
785
+ output["skills"] = {
786
+ "recommendations": skills_recommendations,
787
+ }
543
788
 
544
789
  print(json.dumps(output, indent=2))
545
790
  return CommandResult.success_result(data=output)
546
791
 
547
- def _output_result_json(self, result: ConfigurationResult) -> CommandResult:
792
+ def _output_result_json(
793
+ self, agent_result: Optional = None, skills_result: Optional[dict] = None
794
+ ) -> CommandResult:
548
795
  """Output result as JSON."""
549
- output = {
550
- "status": (
551
- result.status.value
552
- if hasattr(result.status, "value")
553
- else str(result.status)
554
- ),
555
- "deployed_agents": result.deployed_agents,
556
- "failed_agents": result.failed_agents,
557
- "errors": result.errors,
558
- }
796
+ output = {}
797
+
798
+ if agent_result:
799
+ output["agents"] = {
800
+ "status": (
801
+ agent_result.status.value
802
+ if hasattr(agent_result.status, "value")
803
+ else str(agent_result.status)
804
+ ),
805
+ "deployed_agents": agent_result.deployed_agents,
806
+ "failed_agents": agent_result.failed_agents,
807
+ "errors": agent_result.errors,
808
+ }
809
+
810
+ if skills_result:
811
+ output["skills"] = skills_result
559
812
 
560
813
  print(json.dumps(output, indent=2))
561
814
 
562
- if result.status == OperationResult.SUCCESS:
815
+ # Determine overall success
816
+ agent_success = (
817
+ agent_result.status == OperationResult.SUCCESS if agent_result else True
818
+ )
819
+ skills_success = not skills_result or not skills_result.get("errors")
820
+ overall_success = agent_success and skills_success
821
+
822
+ if overall_success:
563
823
  return CommandResult.success_result(data=output)
564
824
  return CommandResult.error_result(
565
825
  "Configuration failed or partial", exit_code=1, data=output
566
826
  )
827
+
828
+ def _recommend_skills(self, agent_preview):
829
+ """Recommend skills based on deployed/recommended agents.
830
+
831
+ Args:
832
+ agent_preview: Agent preview result with recommendations
833
+
834
+ Returns:
835
+ List of recommended skill names, or None if no agents recommended
836
+ """
837
+ if not agent_preview or not agent_preview.recommendations:
838
+ return None
839
+
840
+ # Import agent-skill mapping
841
+ from ...cli.interactive.skills_wizard import AGENT_SKILL_MAPPING
842
+
843
+ # Collect recommended skills based on agent types
844
+ recommended_skills = set()
845
+ for rec in agent_preview.recommendations:
846
+ agent_id = rec.agent_id
847
+ # Map agent ID to skill recommendations
848
+ if agent_id in AGENT_SKILL_MAPPING:
849
+ recommended_skills.update(AGENT_SKILL_MAPPING[agent_id])
850
+
851
+ return list(recommended_skills) if recommended_skills else None
852
+
853
+ def _deploy_skills(self, recommended_skills: list[str]) -> dict:
854
+ """Deploy recommended skills.
855
+
856
+ Args:
857
+ recommended_skills: List of skill names to deploy
858
+
859
+ Returns:
860
+ Dict with deployment results: {"deployed": [...], "errors": [...]}
861
+ """
862
+ try:
863
+ return self.skills_deployer.deploy_skills(
864
+ skill_names=recommended_skills, force=False
865
+ )
866
+ except Exception as e:
867
+ self.logger.error(f"Failed to deploy skills: {e}")
868
+ return {"deployed": [], "errors": [str(e)]}