|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# Tencent is pleased to support the open source community by making tRPC-Agent-Python available. |
| 3 | +# |
| 4 | +# Copyright (C) 2026 Tencent. All rights reserved. |
| 5 | +# |
| 6 | +# tRPC-Agent-Python is licensed under Apache-2.0. |
| 7 | +"""Automated code review agent CLI. |
| 8 | +
|
| 9 | +Usage: |
| 10 | + python run_agent.py review --fixture security_eval --runtime local --dry-run |
| 11 | + python run_agent.py review --diff-file change.diff --runtime container |
| 12 | + python run_agent.py show --task-id <id> |
| 13 | +""" |
| 14 | +import argparse |
| 15 | +import asyncio |
| 16 | +import json |
| 17 | +import sys |
| 18 | +from pathlib import Path |
| 19 | + |
| 20 | +sys.path.insert(0, str(Path(__file__).resolve().parent)) |
| 21 | + |
| 22 | +from agent.config import is_dry_run # noqa: E402 |
| 23 | +from review.diff_input import load_diff # noqa: E402 |
| 24 | +from review.pipeline import ReviewOptions, run_review # noqa: E402 |
| 25 | +from storage.store import ReviewStore # noqa: E402 |
| 26 | + |
| 27 | + |
| 28 | +def _build_parser() -> argparse.ArgumentParser: |
| 29 | + parser = argparse.ArgumentParser(description="Automated code review agent") |
| 30 | + sub = parser.add_subparsers(dest="command", required=True) |
| 31 | + |
| 32 | + review = sub.add_parser("review", help="run a code review") |
| 33 | + review.add_argument("--diff-file", help="path to a unified diff / PR patch") |
| 34 | + review.add_argument("--repo-path", help="git repo; reviews `git diff HEAD`") |
| 35 | + review.add_argument("--fixture", help="bundled fixture name (e.g. security_eval)") |
| 36 | + review.add_argument("--runtime", default="container", |
| 37 | + choices=["local", "container", "cube"], |
| 38 | + help="sandbox runtime (default: container; local is dev-only)") |
| 39 | + review.add_argument("--dry-run", action="store_true", |
| 40 | + help="use the deterministic fake model (no API key needed)") |
| 41 | + review.add_argument("--db-url", default="sqlite:///code_review.db") |
| 42 | + review.add_argument("--output-dir", default="out") |
| 43 | + review.add_argument("--no-llm", action="store_true", help="skip the LLM step") |
| 44 | + |
| 45 | + show = sub.add_parser("show", help="print a stored review task by id") |
| 46 | + show.add_argument("--task-id", required=True) |
| 47 | + show.add_argument("--db-url", default="sqlite:///code_review.db") |
| 48 | + return parser |
| 49 | + |
| 50 | + |
| 51 | +async def main(argv=None) -> int: |
| 52 | + args = _build_parser().parse_args(argv) |
| 53 | + if args.command == "review": |
| 54 | + diff_text, input_type, input_ref = load_diff( |
| 55 | + diff_file=args.diff_file, repo_path=args.repo_path, fixture=args.fixture) |
| 56 | + dry_run = is_dry_run(args.dry_run) |
| 57 | + if dry_run and not args.dry_run: |
| 58 | + print("no TRPC_AGENT_API_KEY configured; falling back to --dry-run mode") |
| 59 | + result = await run_review(ReviewOptions( |
| 60 | + diff_text=diff_text, input_type=input_type, input_ref=input_ref, |
| 61 | + runtime=args.runtime, dry_run=dry_run, db_url=args.db_url, |
| 62 | + output_dir=args.output_dir, enable_llm=not args.no_llm)) |
| 63 | + print(f"task_id={result.task_id}") |
| 64 | + print(f"conclusion={result.report['conclusion']}") |
| 65 | + print(f"report_json={result.json_path}") |
| 66 | + print(f"report_md={result.md_path}") |
| 67 | + return 0 |
| 68 | + |
| 69 | + store = ReviewStore(db_url=args.db_url) |
| 70 | + try: |
| 71 | + bundle = await store.get_task_bundle(args.task_id) |
| 72 | + finally: |
| 73 | + await store.close() |
| 74 | + if bundle["task"] is None: |
| 75 | + print(f"task {args.task_id!r} not found", file=sys.stderr) |
| 76 | + return 1 |
| 77 | + print(json.dumps(bundle, indent=2, ensure_ascii=False, default=str)) |
| 78 | + return 0 |
| 79 | + |
| 80 | + |
| 81 | +if __name__ == "__main__": |
| 82 | + sys.exit(asyncio.run(main())) |
0 commit comments