feat: add project-review workflow (whole-project / single-feature review)
Adds the project-review workflow for code review independent of the git diff. The scope is parsed from the user instruction: 全面/整个项目 -> whole-project (score every source file), otherwise feature + target keyword (locate the code with semantic search + graph queries). - scoring_tools.py: score_review_func gains all_files=True to score every source file in the graph via store.get_all_files() - main.py: score_review_tool gains all_files param; registers the project_review MCP prompt (prompts 6->7) - prompts.py: project_review_prompt(scope, target) with whole-project and feature branches (fixed a precedence bug that truncated the feature text) - skills.py + skills/project-review/: new read-only project-review skill with shared checklists - .opencode/command/code-review-graph-project-review.md: slash command - tests: test_project_review.py (prompt rendering), TestProjectReviewPrompt, skill count assertions 5->6, all_files wiring checks - docs: prompts (6->7) + project-review entries across COMMANDS, CLAUDE, README (+localized), INDEX, architecture, LLM-OPTIMIZED-REFERENCE, CHANGELOG
This commit is contained in:
@@ -624,6 +624,8 @@ def build_report_data(
|
||||
"files": review_data.get("files", ""),
|
||||
"baseline": review_data.get("baseline", "generic"),
|
||||
"verdict": review_data.get("verdict", "❌ FAIL"),
|
||||
"quality_score": review_data.get("quality_score"),
|
||||
"counts": review_data.get("counts", {}),
|
||||
"metrics": {},
|
||||
"issues": [],
|
||||
"manual_review": review_data.get("manual_review", []),
|
||||
@@ -656,3 +658,135 @@ def build_report_data(
|
||||
})
|
||||
|
||||
return data
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Markdown report rendering (Chinese)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
#: Human-readable labels for the Chinese Markdown report.
|
||||
_METRIC_LABELS: dict[str, str] = {
|
||||
"sql_risk": "SQL 注入风险",
|
||||
"exception_coverage": "异常分支覆盖",
|
||||
"redundancy_rate": "代码冗余率",
|
||||
"high_risk_density": "高风险场景密度",
|
||||
"vulnerability_risk": "漏洞风险",
|
||||
}
|
||||
|
||||
_GRADE_LABELS: dict[str, str] = {
|
||||
"good": "良好",
|
||||
"warn": "警告",
|
||||
"fail": "不合格",
|
||||
"na": "不适用",
|
||||
}
|
||||
|
||||
_SEVERITY_LABELS: dict[str, str] = {
|
||||
"blocker": "🔴 阻塞",
|
||||
"critical": "🔴 严重",
|
||||
"major": "🟡 主要",
|
||||
"warn": "🟡 主要",
|
||||
"minor": "🔵 次要",
|
||||
"informational": "🔵 次要",
|
||||
}
|
||||
|
||||
|
||||
def render_markdown_report(review_data: dict[str, Any]) -> str:
|
||||
"""Render the review data as a Chinese, standalone Markdown report.
|
||||
|
||||
Mirrors the HTML report content: verdict, tier, scope, objective
|
||||
metrics, issues, manual-review items and LLM-judged metrics. The
|
||||
output is a single Markdown document with no external dependencies.
|
||||
|
||||
Args:
|
||||
review_data: Review data dict (see :func:`build_report_data`).
|
||||
|
||||
Returns:
|
||||
The Markdown report text.
|
||||
"""
|
||||
data = build_report_data(review_data)
|
||||
lines: list[str] = []
|
||||
|
||||
lines.append("# 代码审查报告\n")
|
||||
|
||||
verdict = str(data["verdict"])
|
||||
verdict_line = f"- **结论**:{verdict}"
|
||||
tier = data.get("tier") or "standard"
|
||||
scope = data.get("scope") or "change-level"
|
||||
baseline = data.get("baseline") or "generic"
|
||||
lines.append(verdict_line)
|
||||
lines.append(
|
||||
f"- **档位**:{tier} · **范围**:{scope} · **基线**:{baseline}"
|
||||
)
|
||||
if data.get("timestamp"):
|
||||
lines.append(f"- **生成时间**:{data['timestamp']}")
|
||||
if data.get("files"):
|
||||
lines.append(f"- **文件**:{data['files']}")
|
||||
qs = data.get("quality_score")
|
||||
if qs is not None:
|
||||
lines.append(f"- **PR 质量分**:{qs}/10")
|
||||
counts = data.get("counts") or {}
|
||||
if counts:
|
||||
lines.append(
|
||||
f"- **问题统计**:{counts.get('critical', 0)} 严重 · "
|
||||
f"{counts.get('informational', 0)} 次要"
|
||||
)
|
||||
lines.append("")
|
||||
|
||||
# Objective metrics
|
||||
metrics = data.get("metrics") or {}
|
||||
if metrics:
|
||||
lines.append("## 客观指标\n")
|
||||
lines.append("| 指标 | 数值 | 评级 | 说明 |")
|
||||
lines.append("|---|---|---|---|")
|
||||
for name, m in metrics.items():
|
||||
label = _METRIC_LABELS.get(name, name)
|
||||
value = m.get("value")
|
||||
value_text = str(value) if value is not None else "N/A"
|
||||
grade = m.get("grade") or "na"
|
||||
grade_text = _GRADE_LABELS.get(grade, grade)
|
||||
note = str(m.get("note", "") or "")
|
||||
note = note.replace("\n", " ")
|
||||
lines.append(f"| {label} | {value_text} | {grade_text} | {note} |")
|
||||
lines.append("")
|
||||
|
||||
# Issues
|
||||
issues = data.get("issues") or []
|
||||
lines.append(f"## 问题清单({len(issues)})\n")
|
||||
if not issues:
|
||||
lines.append("未发现问题。\n")
|
||||
for i, issue in enumerate(issues, start=1):
|
||||
sev = str(issue.get("severity") or "minor").lower()
|
||||
sev_label = _SEVERITY_LABELS.get(sev, sev)
|
||||
category = str(issue.get("category") or "")
|
||||
message = str(issue.get("message") or "")
|
||||
location = str(issue.get("location") or "")
|
||||
conf = issue.get("confidence")
|
||||
fix = str(issue.get("fix") or "")
|
||||
lines.append(
|
||||
f"{i}. **{sev_label}** {message}"
|
||||
f"{f'(置信度 {conf}/10)' if conf is not None else ''}"
|
||||
)
|
||||
if category:
|
||||
lines.append(f" - **类别**:{category}")
|
||||
if location:
|
||||
lines.append(f" - **位置**:`{location}`")
|
||||
if fix:
|
||||
lines.append(f" - **修复建议**:{fix}")
|
||||
lines.append("")
|
||||
|
||||
# Manual review
|
||||
manual = data.get("manual_review") or []
|
||||
if manual:
|
||||
lines.append("## 需要人工审查\n")
|
||||
for m in manual:
|
||||
lines.append(f"- {m}")
|
||||
lines.append("")
|
||||
|
||||
# LLM-judged metrics
|
||||
judged = data.get("llm_judged") or []
|
||||
if judged:
|
||||
lines.append("## 需 LLM 判断的指标\n")
|
||||
lines.append(", ".join(str(j) for j in judged))
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines).strip() + "\n"
|
||||
|
||||
Reference in New Issue
Block a user