feat: add unified-review workflow (scoring tools + skill)

Adds the unified-review integration that fuses CRG graph context with the
ai-code-review scoring methodology and gstack-review fix-first workflow:

- scoring.py: objective Layer-2 metrics (sql_risk, exception_coverage,
  redundancy_rate, high_risk_density, vulnerability_risk) with
  good/warn/fail grades, plus dedupe_findings (fingerprint merge,
  multi-source confidence boost, PR quality score) and report data builder
- tools/scoring_tools.py + main.py: three new MCP tools
  (score_review_tool, dedupe_findings_tool, generate_report_tool)
- assets/report-template.html: self-contained HTML report template
- skills.py + skills/unified-review/: new read-only unified-review skill
  with language/manual-review/specialist checklists
- docs and CHANGELOG updated; tests added (test_scoring, test_report,
  test_unified_review) and test_skills updated for 5 skills
This commit is contained in:
dev
2026-08-05 13:31:55 +08:00
parent 82b7c6dc9e
commit 84ae9b817e
32 changed files with 2229 additions and 19 deletions
+5 -3
View File
@@ -118,7 +118,7 @@ class TestGenerateSkills:
assert result.is_dir()
assert result == tmp_path / ".claude" / "skills"
def test_creates_four_skill_subdirs(self, tmp_path):
def test_creates_five_skill_subdirs(self, tmp_path):
skills_dir = generate_skills(tmp_path)
subdirs = sorted(f.name for f in skills_dir.iterdir() if f.is_dir())
assert subdirs == [
@@ -126,6 +126,7 @@ class TestGenerateSkills:
"explore-codebase",
"refactor-safely",
"review-changes",
"unified-review",
]
for d in skills_dir.iterdir():
assert (d / "SKILL.md").is_file()
@@ -154,6 +155,7 @@ class TestGenerateSkills:
"explore-codebase",
"refactor-safely",
"review-changes",
"unified-review",
):
for skill_file in (
generated / skill_name / "SKILL.md",
@@ -167,7 +169,7 @@ class TestGenerateSkills:
result = generate_skills(tmp_path, skills_dir=custom)
assert result == custom
assert result.is_dir()
assert len(list(result.iterdir())) == 4
assert len(list(result.iterdir())) == 5
def test_skill_content_includes_get_minimal_context(self, tmp_path):
"""Every skill template must reference get_minimal_context."""
@@ -192,7 +194,7 @@ class TestGenerateSkills:
generate_skills(tmp_path)
generate_skills(tmp_path)
skills_dir = tmp_path / ".claude" / "skills"
assert len(list(skills_dir.iterdir())) == 4
assert len(list(skills_dir.iterdir())) == 5
class TestGenerateHooksConfig: