""" Repair Agent CLI - 命令行入口 """ import typer from loguru import logger from rich.console import Console from rich.table import Table from .agent import RepairEngine, TaskManager from .config import settings app = typer.Typer( name="repair-agent", help="自动化 Bug 修复代理", ) console = Console() @app.command() def list( project: str = typer.Option(None, "--project", "-p", help="筛选项目ID"), ): """查看待修复的 Bug 列表""" task_manager = TaskManager() bugs = task_manager.fetch_pending_bugs(project) if not bugs: console.print("[yellow]没有待修复的 Bug[/yellow]") return table = Table(title="待修复 Bug 列表") table.add_column("ID", style="cyan") table.add_column("项目", style="green") table.add_column("错误类型", style="red") table.add_column("消息", style="white") table.add_column("文件", style="blue") table.add_column("状态", style="yellow") for bug in bugs: table.add_row( str(bug.id), bug.project_id, bug.error.type, bug.error.message[:50] + "..." if len(bug.error.message) > 50 else bug.error.message, bug.error.file_path or "-", bug.status.value, ) console.print(table) task_manager.close() @app.command() def fix( project: str = typer.Argument(..., help="项目ID (如 rtc_backend)"), test: bool = typer.Option(True, "--test/--no-test", help="是否运行测试"), commit: bool = typer.Option(False, "--commit", "-c", help="是否自动提交"), ): """修复指定项目的所有 Bug""" console.print(f"[bold blue]开始修复项目: {project}[/bold blue]") engine = RepairEngine() result = engine.fix_project( project_id=project, run_tests=test, auto_commit=commit, ) if result.total == 0: console.print("[yellow]没有需要修复的 Bug[/yellow]") else: console.print(f"\n[bold]修复结果:[/bold]") console.print(f" 总计: {result.total}") console.print(f" [green]成功: {result.success_count}[/green]") console.print(f" [red]失败: {result.failed_count}[/red]") if result.results: console.print("\n[bold]详细结果:[/bold]") for r in result.results: status = "[green]✓[/green]" if r.success else "[red]✗[/red]" console.print(f" {status} Bug #{r.bug_id}: {r.message}") if r.modified_files: console.print(f" 修改文件: {', '.join(r.modified_files)}") engine.close() @app.command("fix-one") def fix_one( bug_id: int = typer.Argument(..., help="Bug ID"), test: bool = typer.Option(True, "--test/--no-test", help="是否运行测试"), ): """修复单个 Bug""" console.print(f"[bold blue]开始修复 Bug #{bug_id}[/bold blue]") engine = RepairEngine() result = engine.fix_single_bug(bug_id, run_tests=test) if result.success: console.print(f"[green]✓ 修复成功![/green]") if result.modified_files: console.print(f" 修改文件: {', '.join(result.modified_files)}") else: console.print(f"[red]✗ 修复失败: {result.message}[/red]") engine.close() @app.command() def status(): """查看配置状态""" console.print("[bold]Repair Agent 配置状态[/bold]\n") console.print(f"Log Center URL: [cyan]{settings.log_center_url}[/cyan]") console.print(f"Claude CLI: [cyan]{settings.claude_cli_path}[/cyan]") console.print(f"最大重试次数: [cyan]{settings.max_retry_count}[/cyan]") console.print(f"最大修改行数: [cyan]{settings.max_modified_lines}[/cyan]") console.print(f"最大修改文件数: [cyan]{settings.max_modified_files}[/cyan]") console.print(f"核心文件关键词: [cyan]{settings.critical_files}[/cyan]") console.print("\n[bold]项目路径映射:[/bold]") console.print(f" rtc_backend: [blue]{settings.path_rtc_backend}[/blue]") console.print(f" rtc_web: [blue]{settings.path_rtc_web}[/blue]") console.print(f" airhub_app: [blue]{settings.path_airhub_app}[/blue]") @app.command() def analyze( bug_id: int = typer.Argument(..., help="Bug ID"), ): """分析单个 Bug(不修复)""" engine = RepairEngine() bug = engine.task_manager.get_bug_detail(bug_id) if not bug: console.print(f"[red]Bug #{bug_id} 不存在[/red]") return project_path = settings.get_project_path(bug.project_id) if not project_path: console.print(f"[red]未找到项目路径: {bug.project_id}[/red]") return console.print(f"[bold blue]分析 Bug #{bug_id}[/bold blue]\n") console.print(bug.format_for_prompt()) console.print("\n[bold]AI 分析结果:[/bold]") success, output = engine.claude_service.analyze_bug(bug, project_path) if success: console.print(output) else: console.print(f"[red]分析失败: {output}[/red]") engine.close() def main(): """入口函数""" app() if __name__ == "__main__": main()