Use standard review guide
This commit is contained in:
@@ -11,8 +11,9 @@ description: "Code Review Agent: 通过 Gitea API 获取 PR diff,分析代码
|
|||||||
|
|
||||||
1. 读取提供的 PR diff 文件(路径在 prompt 中)
|
1. 读取提供的 PR diff 文件(路径在 prompt 中)
|
||||||
2. **(如提供)读取关联 Issue 文件,了解原始需求/缺陷描述**
|
2. **(如提供)读取关联 Issue 文件,了解原始需求/缺陷描述**
|
||||||
3. **(如提供)读取全局影响报告,了解变更的影响范围**
|
3. **(如提供)读取 code review 规范文档,根据 PR 变更类型查阅相关章节**
|
||||||
4. 逐文件分析代码变更
|
4. **(如提供)读取全局影响报告,了解变更的影响范围**
|
||||||
|
5. 逐文件分析代码变更
|
||||||
5. **使用搜索工具(Grep)追踪变更函数/类的调用方,验证接口兼容性**
|
5. **使用搜索工具(Grep)追踪变更函数/类的调用方,验证接口兼容性**
|
||||||
6. **检查相关测试文件是否覆盖了变更逻辑**
|
6. **检查相关测试文件是否覆盖了变更逻辑**
|
||||||
7. **对比 Issue 验收条件,验证改动是否完整满足需求**
|
7. **对比 Issue 验收条件,验证改动是否完整满足需求**
|
||||||
|
|||||||
@@ -178,3 +178,19 @@ def format_issues_markdown(issues):
|
|||||||
f"\n{iss['body']}\n"
|
f"\n{iss['body']}\n"
|
||||||
)
|
)
|
||||||
return "\n".join(parts)
|
return "\n".join(parts)
|
||||||
|
|
||||||
|
|
||||||
|
_STANDARDS_URL = "/api/v1/repos/zeekrAI/knowledge-base/raw/docs/process/code-review.md?ref=main"
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_standards_doc(gitea_url, api_token):
|
||||||
|
"""Fetch the code-review standards document from Gitea via API.
|
||||||
|
|
||||||
|
Returns the raw markdown text, or ``""`` on failure.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return _api_req(gitea_url, api_token, "GET", _STANDARDS_URL).decode(
|
||||||
|
"utf-8", errors="replace"
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|||||||
+39
-2
@@ -225,7 +225,7 @@ def _strip_frontmatter(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):
|
issue_context_path=None, standards_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)
|
||||||
@@ -264,6 +264,14 @@ def build_prompt(meta, files, diff_path, agent_md, impact_report_path=None,
|
|||||||
f"Review the diff in isolation.\n\n"
|
f"Review the diff in isolation.\n\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if standards_path:
|
||||||
|
prompt += (
|
||||||
|
f"Code review standards document saved to: {standards_path}\n"
|
||||||
|
f"Read relevant sections of that document based on the files and\n"
|
||||||
|
f"changes in this PR. Use headings to find applicable rules — do NOT\n"
|
||||||
|
f"read the entire file unless necessary.\n\n"
|
||||||
|
)
|
||||||
|
|
||||||
prompt += (
|
prompt += (
|
||||||
f"Analyze the diff thoroughly. Focus on: business correctness (vs linked "
|
f"Analyze the diff thoroughly. Focus on: business correctness (vs linked "
|
||||||
f"issues), security, correctness, error handling, performance, "
|
f"issues), security, correctness, error handling, performance, "
|
||||||
@@ -415,6 +423,8 @@ def main():
|
|||||||
help="Gitea API token with read:repository + write:repository")
|
help="Gitea API token with read:repository + write:repository")
|
||||||
parser.add_argument("--repo-dir", default=None,
|
parser.add_argument("--repo-dir", default=None,
|
||||||
help="Path to local clone of the target repo (enables global impact analysis)")
|
help="Path to local clone of the target repo (enables global impact analysis)")
|
||||||
|
parser.add_argument("--review-standards", default=None,
|
||||||
|
help="Path to a code-review standards .md file to reference during review")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
claude_bin = _check_claude()
|
claude_bin = _check_claude()
|
||||||
@@ -504,12 +514,37 @@ def main():
|
|||||||
f.write(issue_md)
|
f.write(issue_md)
|
||||||
print(f" Issues saved to: {issue_context_path}")
|
print(f" Issues saved to: {issue_context_path}")
|
||||||
|
|
||||||
|
# ── 4d. Fetch / copy review standards doc ───────────────────────────
|
||||||
|
standards_path = None
|
||||||
|
if args.review_standards:
|
||||||
|
# Explicit local path provided
|
||||||
|
if not os.path.isfile(args.review_standards):
|
||||||
|
print(f"ERROR: --review-standards file not found: {args.review_standards}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
standards_path = os.path.join(diff_dir, f"{prefix}_standards.md")
|
||||||
|
shutil.copy2(args.review_standards, standards_path)
|
||||||
|
print(f" Standards doc copied to: {standards_path}")
|
||||||
|
else:
|
||||||
|
# Auto-fetch from knowledge-base repo via Gitea API
|
||||||
|
print("[review_pr] Fetching review standards from knowledge-base...")
|
||||||
|
standards_md = gitea_api.fetch_standards_doc(args.gitea_url, args.api_token)
|
||||||
|
if standards_md:
|
||||||
|
standards_path = os.path.join(diff_dir, f"{prefix}_standards.md")
|
||||||
|
with open(standards_path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(standards_md)
|
||||||
|
print(f" Standards doc saved to: {standards_path}")
|
||||||
|
else:
|
||||||
|
print("[review_pr] Warning: failed to fetch review standards, continuing without")
|
||||||
|
standards_path = os.path.join(diff_dir, f"{prefix}_standards.md")
|
||||||
|
shutil.copy2(args.review_standards, standards_path)
|
||||||
|
print(f" Standards doc saved to: {standards_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,
|
prompt = build_prompt(meta, files, diff_path, agent_md,
|
||||||
impact_report_path, issue_context_path)
|
impact_report_path, issue_context_path, standards_path)
|
||||||
print("[review_pr] Invoking Claude for analysis...")
|
print("[review_pr] Invoking Claude for analysis...")
|
||||||
|
|
||||||
mon = SessionMonitor(project_root)
|
mon = SessionMonitor(project_root)
|
||||||
@@ -541,6 +576,8 @@ def main():
|
|||||||
os.unlink(impact_report_path)
|
os.unlink(impact_report_path)
|
||||||
if issue_context_path and os.path.exists(issue_context_path):
|
if issue_context_path and os.path.exists(issue_context_path):
|
||||||
os.unlink(issue_context_path)
|
os.unlink(issue_context_path)
|
||||||
|
if standards_path and os.path.exists(standards_path):
|
||||||
|
os.unlink(standards_path)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user