feat: implement Gitea issue-to-PR workflow
This commit is contained in:
114
src/agent_gitea/rendering.py
Normal file
114
src/agent_gitea/rendering.py
Normal file
@@ -0,0 +1,114 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .models import IssueRecord, RepositoryRecord
|
||||
|
||||
|
||||
VALID_VERDICTS = {"APPROVE", "REQUEST_CHANGES", "NEEDS_HUMAN_DECISION"}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ReviewReport:
|
||||
verdict: str
|
||||
raw: str
|
||||
suggested_pr_comment: str
|
||||
|
||||
|
||||
def render_implementer_prompt(repo: RepositoryRecord, issue: IssueRecord, branch_name: str) -> str:
|
||||
return f"""# Agent Implementation Task
|
||||
|
||||
Repository: {repo.full_name}
|
||||
Base branch: {repo.default_branch}
|
||||
Working branch: {branch_name}
|
||||
Issue: #{issue.issue_number} {issue.title}
|
||||
Issue URL: {issue.html_url}
|
||||
|
||||
## Issue Body
|
||||
|
||||
{issue.body or "(no issue body)"}
|
||||
|
||||
## Instructions
|
||||
|
||||
Implement the requested change in this workspace. Keep the change scoped to this issue.
|
||||
Run the relevant tests before finishing.
|
||||
|
||||
Write `AGENT_IMPLEMENTATION_REPORT.md` in the workspace root using this exact section contract:
|
||||
|
||||
- Summary
|
||||
- Files changed
|
||||
- Test commands run
|
||||
- Test results
|
||||
- Known risks
|
||||
- Follow-up suggestions
|
||||
"""
|
||||
|
||||
|
||||
def render_reviewer_prompt(repo: RepositoryRecord, issue: IssueRecord, pr_number: int) -> str:
|
||||
return f"""# Agent Review Task
|
||||
|
||||
Repository: {repo.full_name}
|
||||
Pull request: #{pr_number}
|
||||
Issue: #{issue.issue_number} {issue.title}
|
||||
|
||||
Review the implementation currently checked out in this workspace. Focus on correctness,
|
||||
scope control, test evidence, and human risks. Do not modify code.
|
||||
|
||||
Write `AGENT_REVIEW_REPORT.md` in the workspace root using this exact section contract:
|
||||
|
||||
- Verdict: APPROVE, REQUEST_CHANGES, or NEEDS_HUMAN_DECISION
|
||||
- Summary
|
||||
- Correctness
|
||||
- Scope Control
|
||||
- Test Evidence
|
||||
- Risks
|
||||
- Required Human Checks
|
||||
- Suggested PR Comment
|
||||
"""
|
||||
|
||||
|
||||
def render_pr_body(issue: IssueRecord, implementation_report: str) -> str:
|
||||
return f"""Closes #{issue.issue_number}
|
||||
|
||||
## Agent Implementation Report
|
||||
|
||||
{implementation_report.strip()}
|
||||
|
||||
## Human Review Gate
|
||||
|
||||
This PR was opened by the local agent manager. It has not been auto-merged.
|
||||
Human maintainers must review, decide, and merge manually.
|
||||
"""
|
||||
|
||||
|
||||
def parse_review_report(raw: str) -> ReviewReport:
|
||||
verdict_match = re.search(r"(?im)^\s*(?:##\s*)?Verdict\s*:?\s*`?([A-Z_]+)`?", raw)
|
||||
verdict = verdict_match.group(1) if verdict_match else "NEEDS_HUMAN_DECISION"
|
||||
if verdict not in VALID_VERDICTS:
|
||||
verdict = "NEEDS_HUMAN_DECISION"
|
||||
suggested = extract_section(raw, "Suggested PR Comment").strip()
|
||||
if not suggested:
|
||||
suggested = raw.strip()
|
||||
return ReviewReport(verdict=verdict, raw=raw, suggested_pr_comment=suggested)
|
||||
|
||||
|
||||
def extract_section(raw: str, title: str) -> str:
|
||||
pattern = re.compile(
|
||||
rf"(?ims)^\s*##\s+{re.escape(title)}\s*$\n(?P<body>.*?)(?=^\s*##\s+|\Z)"
|
||||
)
|
||||
match = pattern.search(raw)
|
||||
return match.group("body") if match else ""
|
||||
|
||||
|
||||
def render_human_review_summary(review: ReviewReport) -> str:
|
||||
return f"""## Agent Review Summary
|
||||
|
||||
Verdict: `{review.verdict}`
|
||||
|
||||
{review.suggested_pr_comment.strip()}
|
||||
|
||||
## Human Action Required
|
||||
|
||||
Please review the PR manually. The agent manager will not merge, close, or request changes automatically.
|
||||
"""
|
||||
Reference in New Issue
Block a user