Add issue review
This commit is contained in:
@@ -10,12 +10,14 @@ description: "Code Review Agent: 通过 Gitea API 获取 PR diff,分析代码
|
|||||||
## 工作方式
|
## 工作方式
|
||||||
|
|
||||||
1. 读取提供的 PR diff 文件(路径在 prompt 中)
|
1. 读取提供的 PR diff 文件(路径在 prompt 中)
|
||||||
2. **(如提供)读取全局影响报告,了解变更的影响范围**
|
2. **(如提供)读取关联 Issue 文件,了解原始需求/缺陷描述**
|
||||||
3. 逐文件分析代码变更
|
3. **(如提供)读取全局影响报告,了解变更的影响范围**
|
||||||
4. **使用搜索工具(Grep)追踪变更函数/类的调用方,验证接口兼容性**
|
4. 逐文件分析代码变更
|
||||||
5. **检查相关测试文件是否覆盖了变更逻辑**
|
5. **使用搜索工具(Grep)追踪变更函数/类的调用方,验证接口兼容性**
|
||||||
6. 识别问题,按严重程度分类
|
6. **检查相关测试文件是否覆盖了变更逻辑**
|
||||||
7. 输出结构化 JSON 供 CI 脚本解析并发布到 Gitea
|
7. **对比 Issue 验收条件,验证改动是否完整满足需求**
|
||||||
|
8. 识别问题,按严重程度分类
|
||||||
|
9. 输出结构化 JSON 供 CI 脚本解析并发布到 Gitea
|
||||||
|
|
||||||
## 审查标准
|
## 审查标准
|
||||||
|
|
||||||
@@ -26,6 +28,8 @@ description: "Code Review Agent: 通过 Gitea API 获取 PR diff,分析代码
|
|||||||
- 功能缺陷:明显与 PR 描述不符的实现
|
- 功能缺陷:明显与 PR 描述不符的实现
|
||||||
- **接口兼容性破坏:函数签名变更导致已有调用方编译/运行失败**
|
- **接口兼容性破坏:函数签名变更导致已有调用方编译/运行失败**
|
||||||
- **行为语义变更:返回值、异常、副作用的行为变化影响依赖方**
|
- **行为语义变更:返回值、异常、副作用的行为变化影响依赖方**
|
||||||
|
- **业务不符:改动未满足关联 Issue 的需求描述**
|
||||||
|
- **遗漏场景:Issue 中描述的边界情况或验收条件未被代码覆盖**
|
||||||
|
|
||||||
### 中等(建议修复)
|
### 中等(建议修复)
|
||||||
- 错误处理不完整、异常被吞没
|
- 错误处理不完整、异常被吞没
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
"""Gitea API helpers for the code-review-agent.
|
||||||
|
|
||||||
|
Fetches PR data (metadata, diff, files), linked issues, and posts reviews.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
|
||||||
|
# ── Low-level helper ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _api_req(gitea_url, api_token, method, path, body=None):
|
||||||
|
"""Send a Gitea API request, return parsed JSON or raw bytes."""
|
||||||
|
url = f"{gitea_url}/api/v1/repos{path}"
|
||||||
|
data = json.dumps(body).encode("utf-8") if body else None
|
||||||
|
req = urllib.request.Request(url, method=method, data=data)
|
||||||
|
req.add_header("Authorization", f"token {api_token}")
|
||||||
|
if data:
|
||||||
|
req.add_header("Content-Type", "application/json")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req) as resp:
|
||||||
|
raw = resp.read()
|
||||||
|
if not raw:
|
||||||
|
return {} if path.endswith("/merge") else None
|
||||||
|
content_type = resp.headers.get("Content-Type", "")
|
||||||
|
if "application/json" in content_type:
|
||||||
|
return json.loads(raw)
|
||||||
|
return raw # raw bytes for .diff endpoints
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
err_body = e.read().decode(errors="replace")
|
||||||
|
print(f"API Error {e.code} on {method} {path}: {err_body}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
# ── PR endpoints ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def fetch_pr_metadata(gitea_url, api_token, target_repo, pr_num):
|
||||||
|
"""Fetch PR details: title, description, head SHA."""
|
||||||
|
pr = _api_req(gitea_url, api_token, "GET", f"/{target_repo}/pulls/{pr_num}")
|
||||||
|
return {
|
||||||
|
"number": pr["number"],
|
||||||
|
"title": pr["title"],
|
||||||
|
"body": pr.get("body", ""),
|
||||||
|
"head_sha": pr.get("head", {}).get("sha", ""),
|
||||||
|
"html_url": pr.get("html_url", ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_pr_diff(gitea_url, api_token, target_repo, pr_num):
|
||||||
|
"""Fetch raw unified diff for a PR."""
|
||||||
|
raw = _api_req(gitea_url, api_token, "GET", f"/{target_repo}/pulls/{pr_num}.diff")
|
||||||
|
return raw.decode("utf-8", errors="replace") if isinstance(raw, bytes) else ""
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_pr_files(gitea_url, api_token, target_repo, pr_num):
|
||||||
|
"""Fetch changed file list with stats."""
|
||||||
|
files = _api_req(gitea_url, api_token, "GET", f"/{target_repo}/pulls/{pr_num}/files")
|
||||||
|
result = []
|
||||||
|
for f in files:
|
||||||
|
result.append({
|
||||||
|
"filename": f["filename"],
|
||||||
|
"status": f["status"],
|
||||||
|
"additions": f.get("additions", 0),
|
||||||
|
"deletions": f.get("deletions", 0),
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def post_review(gitea_url, api_token, target_repo, pr_num, review):
|
||||||
|
"""Post a PR review to Gitea."""
|
||||||
|
comments = review.get("comments", [])
|
||||||
|
gitea_comments = []
|
||||||
|
for c in comments:
|
||||||
|
gc = {"path": c["path"], "body": c["body"]}
|
||||||
|
if c.get("line"):
|
||||||
|
gc["new_line"] = c["line"]
|
||||||
|
if c.get("old_line"):
|
||||||
|
gc["old_line"] = c["old_line"]
|
||||||
|
gitea_comments.append(gc)
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"body": review["body"],
|
||||||
|
"event": review.get("event", "COMMENT"),
|
||||||
|
}
|
||||||
|
if gitea_comments:
|
||||||
|
payload["comments"] = gitea_comments
|
||||||
|
|
||||||
|
_api_req(gitea_url, api_token, "POST",
|
||||||
|
f"/{target_repo}/pulls/{pr_num}/reviews", body=payload)
|
||||||
|
print(f"Review posted to PR #{pr_num}")
|
||||||
|
|
||||||
|
|
||||||
|
# ── Issue endpoints ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
_ISSUE_REF_RE = re.compile(
|
||||||
|
r"(?:fixe?s?|close?s?|resolve?s?|refs?|see)\s+"
|
||||||
|
r"((?:[\w.-]+/[\w.-]+)?#\d+)",
|
||||||
|
re.IGNORECASE,
|
||||||
|
)
|
||||||
|
_BARE_ISSUE_RE = re.compile(r"(?:^|\s)(#\d+)")
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_issue_refs(pr_body):
|
||||||
|
"""Extract issue references from PR description.
|
||||||
|
|
||||||
|
Returns list of ``(owner, repo, issue_num)`` tuples.
|
||||||
|
``owner/repo`` is ``None`` when using shorthand ``#123``.
|
||||||
|
"""
|
||||||
|
refs = []
|
||||||
|
for m in _ISSUE_REF_RE.finditer(pr_body):
|
||||||
|
ref = m.group(1)
|
||||||
|
match = re.match(r"(?:([\w.-]+)/([\w.-]+))?#(\d+)", ref)
|
||||||
|
if match:
|
||||||
|
refs.append((match.group(1), match.group(2), int(match.group(3))))
|
||||||
|
|
||||||
|
# Add bare #123 refs not already captured
|
||||||
|
for m in _BARE_ISSUE_RE.finditer(pr_body):
|
||||||
|
num = int(m.group(1)[1:])
|
||||||
|
if not any(r[2] == num for r in refs):
|
||||||
|
refs.append((None, None, num))
|
||||||
|
|
||||||
|
return refs
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_issue(gitea_url, api_token, target_repo, issue_num):
|
||||||
|
"""Fetch a single issue from Gitea.
|
||||||
|
|
||||||
|
Returns dict with keys: number, title, body, state, labels, html_url.
|
||||||
|
"""
|
||||||
|
data = _api_req(gitea_url, api_token, "GET",
|
||||||
|
f"/{target_repo}/issues/{issue_num}")
|
||||||
|
return {
|
||||||
|
"number": data["number"],
|
||||||
|
"title": data["title"],
|
||||||
|
"body": data.get("body", ""),
|
||||||
|
"state": data.get("state", "open"),
|
||||||
|
"labels": [lb["name"] for lb in data.get("labels", [])],
|
||||||
|
"html_url": data.get("html_url", ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_linked_issues(gitea_url, api_token, target_repo, pr_body):
|
||||||
|
"""Extract issue refs from PR body and fetch each issue.
|
||||||
|
|
||||||
|
Returns list of issue dicts (see ``fetch_issue``).
|
||||||
|
"""
|
||||||
|
refs = _extract_issue_refs(pr_body)
|
||||||
|
issues = []
|
||||||
|
for owner, repo, num in refs:
|
||||||
|
if owner and repo:
|
||||||
|
repo_path = f"{owner}/{repo}"
|
||||||
|
else:
|
||||||
|
repo_path = target_repo
|
||||||
|
try:
|
||||||
|
issues.append(fetch_issue(gitea_url, api_token, repo_path, num))
|
||||||
|
except SystemExit:
|
||||||
|
# issue fetch failed (404, etc.) — skip and continue
|
||||||
|
print(f"[gitea] Warning: could not fetch issue #{num}, skipping",
|
||||||
|
file=sys.stderr)
|
||||||
|
return issues
|
||||||
|
|
||||||
|
|
||||||
|
def format_issues_markdown(issues):
|
||||||
|
"""Format a list of issue dicts as a Markdown string."""
|
||||||
|
if not issues:
|
||||||
|
return ""
|
||||||
|
parts = ["# Linked Issues\n"]
|
||||||
|
for iss in issues:
|
||||||
|
labels = ", ".join(iss["labels"]) if iss["labels"] else "none"
|
||||||
|
parts.append(
|
||||||
|
f"## #{iss['number']}: {iss['title']}\n"
|
||||||
|
f"- **State**: {iss['state']}\n"
|
||||||
|
f"- **Labels**: {labels}\n"
|
||||||
|
f"\n{iss['body']}\n"
|
||||||
|
)
|
||||||
|
return "\n".join(parts)
|
||||||
+35
-103
@@ -21,9 +21,8 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import urllib.error
|
|
||||||
import urllib.request
|
|
||||||
|
|
||||||
|
import gitea_api
|
||||||
from monitor_session import SessionMonitor
|
from monitor_session import SessionMonitor
|
||||||
|
|
||||||
MAX_DIFF_CHARS = 200_000 # ~50k words, safe for Claude context
|
MAX_DIFF_CHARS = 200_000 # ~50k words, safe for Claude context
|
||||||
@@ -101,60 +100,6 @@ def _check_claude():
|
|||||||
return claude_bin
|
return claude_bin
|
||||||
|
|
||||||
|
|
||||||
def _api_req(gitea_url, api_token, method, path):
|
|
||||||
"""Send a Gitea API request, return parsed JSON or raw bytes."""
|
|
||||||
url = f"{gitea_url}/api/v1/repos{path}"
|
|
||||||
req = urllib.request.Request(url, method=method)
|
|
||||||
req.add_header("Authorization", f"token {api_token}")
|
|
||||||
req.add_header("Content-Type", "application/json")
|
|
||||||
|
|
||||||
try:
|
|
||||||
with urllib.request.urlopen(req) as resp:
|
|
||||||
raw = resp.read()
|
|
||||||
if not raw:
|
|
||||||
return {} if path.endswith("/merge") else None
|
|
||||||
content_type = resp.headers.get("Content-Type", "")
|
|
||||||
if "application/json" in content_type:
|
|
||||||
return json.loads(raw)
|
|
||||||
return raw # raw bytes for .diff endpoints
|
|
||||||
except urllib.error.HTTPError as e:
|
|
||||||
body = e.read().decode(errors="replace")
|
|
||||||
print(f"API Error {e.code} on {method} {path}: {body}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_pr_metadata(gitea_url, api_token, target_repo, pr_num):
|
|
||||||
"""Fetch PR details: title, description, head SHA."""
|
|
||||||
pr = _api_req(gitea_url, api_token, "GET", f"/{target_repo}/pulls/{pr_num}")
|
|
||||||
return {
|
|
||||||
"number": pr["number"],
|
|
||||||
"title": pr["title"],
|
|
||||||
"body": pr.get("body", ""),
|
|
||||||
"head_sha": pr.get("head", {}).get("sha", ""),
|
|
||||||
"html_url": pr.get("html_url", ""),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_pr_diff(gitea_url, api_token, target_repo, pr_num):
|
|
||||||
"""Fetch raw unified diff for a PR."""
|
|
||||||
raw = _api_req(gitea_url, api_token, "GET", f"/{target_repo}/pulls/{pr_num}.diff")
|
|
||||||
return raw.decode("utf-8", errors="replace") if isinstance(raw, bytes) else ""
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_pr_files(gitea_url, api_token, target_repo, pr_num):
|
|
||||||
"""Fetch changed file list with stats."""
|
|
||||||
files = _api_req(gitea_url, api_token, "GET", f"/{target_repo}/pulls/{pr_num}/files")
|
|
||||||
result = []
|
|
||||||
for f in files:
|
|
||||||
result.append({
|
|
||||||
"filename": f["filename"],
|
|
||||||
"status": f["status"],
|
|
||||||
"additions": f.get("additions", 0),
|
|
||||||
"deletions": f.get("deletions", 0),
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def format_files_summary(files):
|
def format_files_summary(files):
|
||||||
"""Build a one-line-per-file summary for the Claude prompt."""
|
"""Build a one-line-per-file summary for the Claude prompt."""
|
||||||
lines = []
|
lines = []
|
||||||
@@ -279,7 +224,8 @@ def _strip_frontmatter(md_text):
|
|||||||
return md_text
|
return md_text
|
||||||
|
|
||||||
|
|
||||||
def build_prompt(meta, files, diff_path, agent_md, impact_report_path=None):
|
def build_prompt(meta, files, diff_path, agent_md, impact_report_path=None,
|
||||||
|
issue_context_path=None):
|
||||||
"""Build the prompt string for Claude, embedding the agent definition."""
|
"""Build the prompt string for Claude, embedding the agent definition."""
|
||||||
files_summary = format_files_summary(files)
|
files_summary = format_files_summary(files)
|
||||||
total_additions = sum(f["additions"] for f in files)
|
total_additions = sum(f["additions"] for f in files)
|
||||||
@@ -297,6 +243,13 @@ def build_prompt(meta, files, diff_path, agent_md, impact_report_path=None):
|
|||||||
f"Read that file to see every line changed.\n\n"
|
f"Read that file to see every line changed.\n\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if issue_context_path:
|
||||||
|
prompt += (
|
||||||
|
f"Linked issue(s) saved to: {issue_context_path}\n"
|
||||||
|
f"Read that file to see the original requirements / bug report.\n"
|
||||||
|
f"Verify that the code changes address the issue requirements completely.\n\n"
|
||||||
|
)
|
||||||
|
|
||||||
if impact_report_path:
|
if impact_report_path:
|
||||||
prompt += (
|
prompt += (
|
||||||
f"A global impact report has been saved to: {impact_report_path}\n"
|
f"A global impact report has been saved to: {impact_report_path}\n"
|
||||||
@@ -312,8 +265,9 @@ def build_prompt(meta, files, diff_path, agent_md, impact_report_path=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
prompt += (
|
prompt += (
|
||||||
f"Analyze the diff thoroughly. Focus on: security, correctness, "
|
f"Analyze the diff thoroughly. Focus on: business correctness (vs linked "
|
||||||
f"error handling, performance, and code quality — in that order.\n\n"
|
f"issues), security, correctness, error handling, performance, "
|
||||||
|
f"and code quality — in that order.\n\n"
|
||||||
f"Output your review as a single JSON object inside a ```json code block. "
|
f"Output your review as a single JSON object inside a ```json code block. "
|
||||||
f"Do NOT output anything else."
|
f"Do NOT output anything else."
|
||||||
)
|
)
|
||||||
@@ -447,45 +401,6 @@ def validate_review(review):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def post_review(gitea_url, api_token, target_repo, pr_num, review):
|
|
||||||
"""Post a PR review to Gitea."""
|
|
||||||
payload = {
|
|
||||||
"body": review["body"],
|
|
||||||
"event": review.get("event", "COMMENT"),
|
|
||||||
}
|
|
||||||
comments = review.get("comments", [])
|
|
||||||
if comments:
|
|
||||||
# Translate {line} -> {new_line} for Gitea API
|
|
||||||
gitea_comments = []
|
|
||||||
for c in comments:
|
|
||||||
gc = {
|
|
||||||
"path": c["path"],
|
|
||||||
"body": c["body"],
|
|
||||||
}
|
|
||||||
if c.get("line"):
|
|
||||||
gc["new_line"] = c["line"]
|
|
||||||
if c.get("old_line"):
|
|
||||||
gc["old_line"] = c["old_line"]
|
|
||||||
gitea_comments.append(gc)
|
|
||||||
payload["comments"] = gitea_comments
|
|
||||||
|
|
||||||
body = json.dumps(payload).encode("utf-8")
|
|
||||||
url = f"{gitea_url}/api/v1/repos/{target_repo}/pulls/{pr_num}/reviews"
|
|
||||||
req = urllib.request.Request(url, data=body, method="POST")
|
|
||||||
req.add_header("Authorization", f"token {api_token}")
|
|
||||||
req.add_header("Content-Type", "application/json")
|
|
||||||
|
|
||||||
try:
|
|
||||||
with urllib.request.urlopen(req) as resp:
|
|
||||||
result = json.loads(resp.read())
|
|
||||||
print(f"Review posted: {result.get('html_url', result.get('url', 'unknown'))}")
|
|
||||||
return result
|
|
||||||
except urllib.error.HTTPError as e:
|
|
||||||
err_body = e.read().decode(errors="replace")
|
|
||||||
print(f"Failed to post review: {e.code} - {err_body}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Code Review Agent — fetch PR, analyze with Claude, post review"
|
description="Code Review Agent — fetch PR, analyze with Claude, post review"
|
||||||
@@ -517,12 +432,17 @@ def main():
|
|||||||
|
|
||||||
# ── 1. Fetch PR metadata ─────────────────────────────────────────────
|
# ── 1. Fetch PR metadata ─────────────────────────────────────────────
|
||||||
print(f"[review_pr] Fetching PR #{args.pr} metadata from {args.target_repo}...")
|
print(f"[review_pr] Fetching PR #{args.pr} metadata from {args.target_repo}...")
|
||||||
meta = fetch_pr_metadata(args.gitea_url, args.api_token, args.target_repo, args.pr)
|
meta = gitea_api.fetch_pr_metadata(args.gitea_url, args.api_token, args.target_repo, args.pr)
|
||||||
print(f" Title: {meta['title']}")
|
print(f" Title: {meta['title']}")
|
||||||
|
|
||||||
|
# ── 1b. Fetch linked issues ───────────────────────────────────────────
|
||||||
|
issues = gitea_api.fetch_linked_issues(
|
||||||
|
args.gitea_url, args.api_token, args.target_repo, meta.get("body", ""))
|
||||||
|
print(f" {len(issues)} linked issue(s) found")
|
||||||
|
|
||||||
# ── 2. Fetch changed files ───────────────────────────────────────────
|
# ── 2. Fetch changed files ───────────────────────────────────────────
|
||||||
print("[review_pr] Fetching changed files...")
|
print("[review_pr] Fetching changed files...")
|
||||||
files = fetch_pr_files(args.gitea_url, args.api_token, args.target_repo, args.pr)
|
files = gitea_api.fetch_pr_files(args.gitea_url, args.api_token, args.target_repo, args.pr)
|
||||||
print(f" {len(files)} file(s) changed")
|
print(f" {len(files)} file(s) changed")
|
||||||
|
|
||||||
if not files:
|
if not files:
|
||||||
@@ -531,7 +451,7 @@ def main():
|
|||||||
|
|
||||||
# ── 3. Fetch diff ────────────────────────────────────────────────────
|
# ── 3. Fetch diff ────────────────────────────────────────────────────
|
||||||
print("[review_pr] Fetching diff...")
|
print("[review_pr] Fetching diff...")
|
||||||
diff_text = fetch_pr_diff(args.gitea_url, args.api_token, args.target_repo, args.pr)
|
diff_text = gitea_api.fetch_pr_diff(args.gitea_url, args.api_token, args.target_repo, args.pr)
|
||||||
|
|
||||||
if not diff_text.strip():
|
if not diff_text.strip():
|
||||||
print("Empty diff — skipping review.")
|
print("Empty diff — skipping review.")
|
||||||
@@ -575,11 +495,21 @@ def main():
|
|||||||
else:
|
else:
|
||||||
print("[review_pr] No --repo-dir provided — skipping global impact analysis")
|
print("[review_pr] No --repo-dir provided — skipping global impact analysis")
|
||||||
|
|
||||||
|
# ── 4c. Write linked issues to file ──────────────────────────────────
|
||||||
|
issue_context_path = None
|
||||||
|
if issues:
|
||||||
|
issue_md = gitea_api.format_issues_markdown(issues)
|
||||||
|
issue_context_path = os.path.join(diff_dir, f"{prefix}_issue.md")
|
||||||
|
with open(issue_context_path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(issue_md)
|
||||||
|
print(f" Issues saved to: {issue_context_path}")
|
||||||
|
|
||||||
mon = None
|
mon = None
|
||||||
prompt_file = os.path.join(diff_dir, f"{prefix}_prompt.txt")
|
prompt_file = os.path.join(diff_dir, f"{prefix}_prompt.txt")
|
||||||
try:
|
try:
|
||||||
# ── 5. Build prompt & run Claude ─────────────────────────────────
|
# ── 5. Build prompt & run Claude ─────────────────────────────────
|
||||||
prompt = build_prompt(meta, files, diff_path, agent_md, impact_report_path)
|
prompt = build_prompt(meta, files, diff_path, agent_md,
|
||||||
|
impact_report_path, issue_context_path)
|
||||||
print("[review_pr] Invoking Claude for analysis...")
|
print("[review_pr] Invoking Claude for analysis...")
|
||||||
|
|
||||||
mon = SessionMonitor(project_root)
|
mon = SessionMonitor(project_root)
|
||||||
@@ -596,7 +526,7 @@ def main():
|
|||||||
|
|
||||||
# ── 7. Post review to Gitea ──────────────────────────────────────
|
# ── 7. Post review to Gitea ──────────────────────────────────────
|
||||||
print("[review_pr] Posting review to Gitea...")
|
print("[review_pr] Posting review to Gitea...")
|
||||||
post_review(args.gitea_url, args.api_token, args.target_repo, args.pr, review)
|
gitea_api.post_review(args.gitea_url, args.api_token, args.target_repo, args.pr, review)
|
||||||
|
|
||||||
print("[review_pr] Done.")
|
print("[review_pr] Done.")
|
||||||
finally:
|
finally:
|
||||||
@@ -609,6 +539,8 @@ def main():
|
|||||||
os.unlink(prompt_file)
|
os.unlink(prompt_file)
|
||||||
if impact_report_path and os.path.exists(impact_report_path):
|
if impact_report_path and os.path.exists(impact_report_path):
|
||||||
os.unlink(impact_report_path)
|
os.unlink(impact_report_path)
|
||||||
|
if issue_context_path and os.path.exists(issue_context_path):
|
||||||
|
os.unlink(issue_context_path)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user