Spaces:
Sleeping
Sleeping
| """CLI for executing workflows.""" | |
| import asyncio | |
| import click | |
| import sys | |
| from pathlib import Path | |
| from rich.console import Console | |
| from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn | |
| from rich.table import Table | |
| from rich.panel import Panel | |
| from rich.syntax import Syntax | |
| from .workflow import WorkflowEngine, WorkflowDefinition | |
| from .orchestrator import OrchestratorRegistry | |
| from .config import DelegationConfig | |
| from .logging_config import setup_logging | |
| from .agent_discovery import AgentDiscovery | |
| console = Console() | |
| def cli(ctx, verbose, config): | |
| """Delegation MCP - Multi-Agent Workflow Orchestration.""" | |
| ctx.ensure_object(dict) | |
| ctx.obj['verbose'] = verbose | |
| ctx.obj['config_path'] = Path(config) if config else Path("config/delegation_rules.yaml") | |
| # Setup logging | |
| import logging | |
| setup_logging(level=logging.DEBUG if verbose else logging.INFO, verbose=verbose) | |
| def list_workflows(ctx): | |
| """List all available workflows.""" | |
| workflows_dir = Path("workflows") | |
| if not workflows_dir.exists(): | |
| console.print("[red]β Workflows directory not found[/red]") | |
| sys.exit(1) | |
| console.print("\n[bold cyan]π Available Workflows[/bold cyan]\n") | |
| table = Table(show_header=True, header_style="bold magenta") | |
| table.add_column("Name", style="cyan", no_wrap=True) | |
| table.add_column("Steps", justify="center", style="yellow") | |
| table.add_column("Agents", style="green") | |
| table.add_column("Category", style="blue") | |
| table.add_column("Difficulty", style="magenta") | |
| table.add_column("Duration", justify="right", style="yellow") | |
| for workflow_file in sorted(workflows_dir.glob("*.yaml")): | |
| try: | |
| workflow = WorkflowDefinition.from_yaml(workflow_file) | |
| agents = ", ".join(sorted(set(step.agent for step in workflow.steps))) | |
| category = workflow.metadata.get("category", "general") | |
| difficulty = workflow.metadata.get("difficulty", "intermediate") | |
| duration = workflow.metadata.get("estimated_duration", 0) | |
| table.add_row( | |
| workflow.name, | |
| str(len(workflow.steps)), | |
| agents, | |
| category, | |
| difficulty, | |
| f"{duration // 60}min" | |
| ) | |
| except Exception as e: | |
| console.print(f"[red]β οΈ Failed to load {workflow_file.name}: {e}[/red]") | |
| console.print(table) | |
| console.print() | |
| def show(ctx, workflow_name): | |
| """Show workflow details.""" | |
| workflows_dir = Path("workflows") | |
| workflow = _find_workflow(workflow_name, workflows_dir) | |
| if not workflow: | |
| sys.exit(1) | |
| # Display workflow info | |
| console.print() | |
| console.print(Panel( | |
| f"[bold]{workflow.name}[/bold]\n\n{workflow.description}", | |
| title="π Workflow Details", | |
| border_style="cyan" | |
| )) | |
| # Display steps | |
| console.print("\n[bold cyan]π Workflow Steps[/bold cyan]\n") | |
| for i, step in enumerate(workflow.steps, 1): | |
| console.print(f"[bold yellow]Step {i}:[/bold yellow] {step.id}") | |
| console.print(f" [cyan]Agent:[/cyan] {step.agent}") | |
| console.print(f" [green]Task:[/green] {step.task}") | |
| if step.output: | |
| console.print(f" [blue]Output:[/blue] {step.output}") | |
| if step.condition: | |
| console.print(f" [magenta]Condition:[/magenta] {step.condition}") | |
| console.print() | |
| # Display metadata | |
| if workflow.metadata: | |
| console.print("[bold cyan]π Metadata[/bold cyan]") | |
| for key, value in workflow.metadata.items(): | |
| console.print(f" [cyan]{key}:[/cyan] {value}") | |
| console.print() | |
| def execute(ctx, workflow_name, context, dry_run): | |
| """Execute a workflow.""" | |
| workflows_dir = Path("workflows") | |
| workflow = _find_workflow(workflow_name, workflows_dir) | |
| if not workflow: | |
| sys.exit(1) | |
| # Parse context | |
| context_dict = {} | |
| for ctx_item in context: | |
| if '=' in ctx_item: | |
| key, value = ctx_item.split('=', 1) | |
| context_dict[key.strip()] = value.strip() | |
| if dry_run: | |
| console.print("\n[bold yellow]π Dry Run Mode[/bold yellow]\n") | |
| console.print(f"[cyan]Workflow:[/cyan] {workflow.name}") | |
| console.print(f"[cyan]Steps:[/cyan] {len(workflow.steps)}") | |
| console.print(f"[cyan]Context:[/cyan] {context_dict}") | |
| console.print("\n[bold green]Would execute:[/bold green]\n") | |
| for i, step in enumerate(workflow.steps, 1): | |
| console.print(f" {i}. [{step.agent}] {step.task}") | |
| console.print() | |
| return | |
| # Execute workflow | |
| asyncio.run(_execute_workflow(ctx, workflow, context_dict)) | |
| async def _execute_workflow(ctx, workflow, context): | |
| """Execute workflow with progress display.""" | |
| # Setup | |
| config_path = ctx.obj['config_path'] | |
| if config_path.exists(): | |
| config = DelegationConfig.from_yaml(config_path) | |
| else: | |
| config = DelegationConfig(orchestrator="claude") | |
| registry = OrchestratorRegistry() | |
| for name, orch_config in config.orchestrators.items(): | |
| registry.register(orch_config) | |
| engine = WorkflowEngine(registry) | |
| # Display header | |
| console.print() | |
| console.print(Panel( | |
| f"[bold]{workflow.name}[/bold]\n{workflow.description}", | |
| title="π Executing Workflow", | |
| border_style="green" | |
| )) | |
| console.print() | |
| # Execute with progress | |
| with Progress( | |
| SpinnerColumn(), | |
| TextColumn("[progress.description]{task.description}"), | |
| BarColumn(), | |
| console=console, | |
| ) as progress: | |
| task = progress.add_task( | |
| f"[cyan]Executing {len(workflow.steps)} steps...", | |
| total=len(workflow.steps) | |
| ) | |
| # We need to modify the engine to support progress callbacks | |
| # For now, just execute | |
| result = await engine.execute(workflow, initial_context=context) | |
| progress.update(task, completed=len(workflow.steps)) | |
| # Display results | |
| console.print() | |
| if result.success: | |
| console.print(Panel( | |
| f"[bold green]β Workflow Completed Successfully[/bold green]\n\n" | |
| f"Steps: {result.steps_completed}/{result.total_steps}\n" | |
| f"Duration: {result.duration:.2f}s", | |
| border_style="green" | |
| )) | |
| else: | |
| console.print(Panel( | |
| f"[bold red]β Workflow Failed[/bold red]\n\n" | |
| f"Steps: {result.steps_completed}/{result.total_steps}\n" | |
| f"Duration: {result.duration:.2f}s", | |
| border_style="red" | |
| )) | |
| # Display outputs | |
| if result.outputs: | |
| console.print("\n[bold cyan]π€ Outputs[/bold cyan]\n") | |
| for key, value in result.outputs.items(): | |
| console.print(f"[bold]{key}:[/bold]") | |
| # Truncate long outputs | |
| display_value = str(value) | |
| if len(display_value) > 1000: | |
| display_value = display_value[:1000] + "\n... (truncated)" | |
| console.print(Panel(display_value, border_style="blue")) | |
| # Display errors | |
| if result.errors: | |
| console.print("\n[bold red]β Errors[/bold red]\n") | |
| for error in result.errors: | |
| console.print(f" β’ {error}") | |
| console.print() | |
| def validate(ctx, workflow_file): | |
| """Validate a workflow file.""" | |
| try: | |
| workflow = WorkflowDefinition.from_yaml(Path(workflow_file)) | |
| console.print(f"[green]β Workflow '{workflow.name}' is valid[/green]") | |
| console.print(f" Steps: {len(workflow.steps)}") | |
| console.print(f" Agents: {', '.join(set(step.agent for step in workflow.steps))}") | |
| except Exception as e: | |
| console.print(f"[red]β Invalid workflow: {e}[/red]") | |
| sys.exit(1) | |
| def discover_agents_cmd(ctx, force_refresh, output_json): | |
| """Discover available CLI agents on the system.""" | |
| asyncio.run(_discover_agents(force_refresh, output_json)) | |
| async def _discover_agents(force_refresh, output_json): | |
| """Execute agent discovery.""" | |
| console.print() | |
| console.print("[bold cyan]Discovering CLI Agents...[/bold cyan]\n") | |
| # Create discovery instance and run discovery | |
| discovery = AgentDiscovery() | |
| console.print("[cyan]Scanning system PATH...[/cyan]") | |
| discovered = await discovery.discover_agents(force_refresh=force_refresh) | |
| console.print() | |
| summary = discovery.get_discovery_summary() | |
| if output_json: | |
| # Output as JSON | |
| import json | |
| console.print(json.dumps(summary, indent=2)) | |
| return | |
| # Display results in table format | |
| console.print() | |
| console.print(Panel( | |
| f"[bold]Agent Discovery Results[/bold]\n\n" | |
| f"Total agents scanned: {summary['total_agents']}\n" | |
| f"Available: [green]{summary['available']}[/green]\n" | |
| f"Unavailable: [red]{summary['unavailable']}[/red]", | |
| title="Summary", | |
| border_style="cyan" | |
| )) | |
| console.print() | |
| # Display available agents | |
| if summary['available_agents']: | |
| console.print("[bold green]Available Agents[/bold green]\n") | |
| table = Table(show_header=True, header_style="bold magenta") | |
| table.add_column("Agent", style="cyan", no_wrap=True) | |
| table.add_column("Version", style="yellow") | |
| table.add_column("Path", style="green") | |
| for agent in summary['available_agents']: | |
| table.add_row( | |
| agent['name'], | |
| agent['version'][:50] if agent['version'] else 'Unknown', | |
| agent['path'] | |
| ) | |
| console.print(table) | |
| console.print() | |
| # Display unavailable agents | |
| if summary['unavailable_agents']: | |
| console.print("[bold red]Unavailable Agents[/bold red]\n") | |
| for agent in summary['unavailable_agents']: | |
| console.print(f"[red]x[/red] [bold]{agent['name']}[/bold]") | |
| console.print(f" {agent['error']}\n") | |
| console.print() | |
| console.print("[dim]Tip: Use --force-refresh to re-scan, or --json for machine-readable output[/dim]") | |
| console.print() | |
| def _find_workflow(name, workflows_dir): | |
| """Find workflow by name or filename.""" | |
| if not workflows_dir.exists(): | |
| console.print("[red]β Workflows directory not found[/red]") | |
| return None | |
| # Try exact name match | |
| for workflow_file in workflows_dir.glob("*.yaml"): | |
| try: | |
| workflow = WorkflowDefinition.from_yaml(workflow_file) | |
| if workflow.name.lower() == name.lower(): | |
| return workflow | |
| except Exception: | |
| continue | |
| # Try filename match | |
| workflow_path = workflows_dir / f"{name}.yaml" | |
| if workflow_path.exists(): | |
| try: | |
| return WorkflowDefinition.from_yaml(workflow_path) | |
| except Exception as e: | |
| console.print(f"[red]β Failed to load workflow: {e}[/red]") | |
| return None | |
| console.print(f"[red]β Workflow '{name}' not found[/red]") | |
| console.print("\nTry: [cyan]delegation-workflow list[/cyan] to see available workflows") | |
| return None | |
| def main(): | |
| """Entry point.""" | |
| cli(obj={}) | |
| if __name__ == '__main__': | |
| main() | |