feat(robomp): added incoming PR review feature with classify and submit
- Added `review_pr` task that checks out PR head in a detached worktree, classifies rank/type/area, and posts a batched GitHub review as `event=COMMENT`. - Added four new host tools: `fetch_pr`, `classify_pr`, `pr_review_comment`, and `submit_pr_review`; review tools self-gate on `review_mode`, push/open-PR tools refuse when `review_mode` is set. - Added sqlite staging table `pr_review_comments` with `stage_review_comment`, `list_staged_review_comments`, and `clear_staged_review_comments` DAOs. - Routed `pull_request.opened/reopened/ready_for_review` to `review_pr` and extended `pull_request.closed` cleanup to any tracked PR regardless of author.
This commit is contained in:
@@ -25,6 +25,7 @@ from robomp.github_client import (
|
||||
GitHubError,
|
||||
IssueInfo,
|
||||
IssueSummary,
|
||||
PullRequestFileInfo,
|
||||
PullRequestInfo,
|
||||
PullRequestReviewInfo,
|
||||
ReactionInfo,
|
||||
@@ -169,6 +170,14 @@ class GitHubProxyClient:
|
||||
data = await self._request("GET", "/gh/v1/pull_request", params={"repo": repo, "number": number})
|
||||
return _pr_from(data)
|
||||
|
||||
async def list_pr_files(self, repo: str, pr_number: int) -> list[PullRequestFileInfo]:
|
||||
data = await self._request(
|
||||
"GET",
|
||||
"/gh/v1/pr_files",
|
||||
params={"repo": repo, "pr_number": pr_number},
|
||||
)
|
||||
return [_pr_file_from(item) for item in (data.get("items") if isinstance(data, dict) else None) or []]
|
||||
|
||||
async def list_issues(
|
||||
self,
|
||||
repo: str,
|
||||
@@ -273,6 +282,28 @@ class GitHubProxyClient:
|
||||
)
|
||||
return tuple(str(lbl) for lbl in (data.get("labels") if isinstance(data, dict) else None) or [])
|
||||
|
||||
async def submit_pr_review(
|
||||
self,
|
||||
*,
|
||||
repo: str,
|
||||
pr_number: int,
|
||||
body: str,
|
||||
event: str,
|
||||
comments: list[Mapping[str, Any]],
|
||||
) -> PullRequestReviewInfo:
|
||||
data = await self._request(
|
||||
"POST",
|
||||
"/gh/v1/submit_pr_review",
|
||||
json_body={
|
||||
"repo": repo,
|
||||
"pr_number": pr_number,
|
||||
"body": body,
|
||||
"event": event,
|
||||
"comments": comments,
|
||||
},
|
||||
)
|
||||
return _pr_review_from(data)
|
||||
|
||||
async def add_assignees(self, repo: str, number: int, assignees: list[str]) -> None:
|
||||
if not assignees:
|
||||
return
|
||||
@@ -360,6 +391,10 @@ class ProxyGitTransport:
|
||||
del pool_dir
|
||||
self._post("/gh/v1/git/fetch_ref", {"repo": repo, "ref": ref})
|
||||
|
||||
def fetch_pr_head(self, *, repo: str, pool_dir: Path, pr_number: int) -> None:
|
||||
del pool_dir
|
||||
self._post("/gh/v1/git/fetch_pr_head", {"repo": repo, "pr_number": pr_number})
|
||||
|
||||
def push_branch(
|
||||
self,
|
||||
*,
|
||||
@@ -477,6 +512,17 @@ def _pr_review_from(data: Any) -> PullRequestReviewInfo:
|
||||
)
|
||||
|
||||
|
||||
def _pr_file_from(data: Any) -> PullRequestFileInfo:
|
||||
if not isinstance(data, dict):
|
||||
raise GitHubError(500, "proxy returned malformed pr_file payload")
|
||||
return PullRequestFileInfo(
|
||||
path=str(data.get("path") or ""),
|
||||
status=str(data.get("status") or ""),
|
||||
additions=int(data.get("additions") or 0),
|
||||
deletions=int(data.get("deletions") or 0),
|
||||
)
|
||||
|
||||
|
||||
def _pr_from(data: Any) -> PullRequestInfo:
|
||||
if not isinstance(data, dict):
|
||||
raise GitHubError(500, "proxy returned malformed pr payload")
|
||||
@@ -489,6 +535,8 @@ def _pr_from(data: Any) -> PullRequestInfo:
|
||||
state=str(data.get("state") or "open"),
|
||||
author=str(data.get("author") or ""),
|
||||
head_repo=str(data.get("head_repo") or ""),
|
||||
title=str(data.get("title") or ""),
|
||||
body=str(data.get("body") or ""),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user