feat: full file context + patterns-repo support
CI / test (pull_request) Successful in 13s
CI / review (gpt-5, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 1m51s
CI / review (gpt-5-mini, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 2m0s

Major improvements to review quality:

1. Full file context: fetch complete content of all modified files from
   the PR branch and include as reference. This eliminates false-positive
   "missing import" findings since the model sees the entire file.

2. Patterns repo: new --patterns-repo / PATTERNS_REPO flag fetches
   language idiom files from a separate Gitea repo (e.g. rodin/elixir-patterns)
   and includes them as review criteria.

3. Multi-file patterns: --patterns-files / PATTERNS_FILES accepts
   comma-separated file paths to fetch from the patterns repo.

New API methods:
- GetPullRequestFiles: list changed files in a PR
- GetFileContentRef: fetch file content from a specific branch/ref

Prompt changes:
- BuildSystemPrompt now accepts (conventions, patterns)
- BuildUserPrompt now accepts fileContext parameter
- File context displayed before diff for model reference
- Patterns presented as "review criteria" in system prompt

Composite action updated with patterns-repo and patterns-files inputs.
This commit is contained in:
Rodin
2026-05-01 12:11:49 -07:00
parent c76362af95
commit e234dca474
6 changed files with 226 additions and 28 deletions
+47 -6
View File
@@ -6,7 +6,7 @@ import (
)
func TestBuildSystemPrompt_NoConventions(t *testing.T) {
prompt := BuildSystemPrompt("")
prompt := BuildSystemPrompt("", "")
if !strings.Contains(prompt, "expert code reviewer") {
t.Error("expected system prompt to mention code reviewer role")
@@ -18,7 +18,7 @@ func TestBuildSystemPrompt_NoConventions(t *testing.T) {
func TestBuildSystemPrompt_WithConventions(t *testing.T) {
conventions := "- Use stdlib only\n- No panics\n"
prompt := BuildSystemPrompt(conventions)
prompt := BuildSystemPrompt(conventions, "")
if !strings.Contains(prompt, "coding conventions") {
t.Error("expected conventions section")
@@ -29,7 +29,7 @@ func TestBuildSystemPrompt_WithConventions(t *testing.T) {
}
func TestBuildUserPrompt_Basic(t *testing.T) {
prompt := BuildUserPrompt("Fix bug", "Fixes the crash", "diff content here", true, "all checks passed")
prompt := BuildUserPrompt("Fix bug", "Fixes the crash", "diff content here", "", true, "all checks passed")
if !strings.Contains(prompt, "Fix bug") {
t.Error("expected PR title")
@@ -46,7 +46,7 @@ func TestBuildUserPrompt_Basic(t *testing.T) {
}
func TestBuildUserPrompt_CIFailed(t *testing.T) {
prompt := BuildUserPrompt("Add tests", "", "some diff", false, "lint: failed")
prompt := BuildUserPrompt("Add tests", "", "some diff", "", false, "lint: failed")
if !strings.Contains(prompt, "FAILED") {
t.Error("expected CI status FAILED")
@@ -57,7 +57,7 @@ func TestBuildUserPrompt_CIFailed(t *testing.T) {
}
func TestBuildUserPrompt_NoDescription(t *testing.T) {
prompt := BuildUserPrompt("Quick fix", "", "diff", true, "")
prompt := BuildUserPrompt("Quick fix", "", "diff", "", true, "")
if strings.Contains(prompt, "### Description") {
t.Error("should not contain Description header when body is empty")
@@ -66,7 +66,7 @@ func TestBuildUserPrompt_NoDescription(t *testing.T) {
func TestBuildUserPrompt_DiffIncluded(t *testing.T) {
diff := "+func Hello() string {\n+\treturn \"hello\"\n+}"
prompt := BuildUserPrompt("Greeting", "Add greeting func", diff, true, "")
prompt := BuildUserPrompt("Greeting", "Add greeting func", diff, "", true, "")
if !strings.Contains(prompt, "```diff") {
t.Error("expected diff fence")
@@ -75,3 +75,44 @@ func TestBuildUserPrompt_DiffIncluded(t *testing.T) {
t.Error("expected diff content in prompt")
}
}
func TestBuildSystemPrompt_WithPatterns(t *testing.T) {
patterns := "## Naming: use snake_case for functions"
prompt := BuildSystemPrompt("", patterns)
if !strings.Contains(prompt, "Language Patterns") {
t.Error("expected patterns section header")
}
if !strings.Contains(prompt, "snake_case") {
t.Error("expected patterns content")
}
}
func TestBuildSystemPrompt_WithBoth(t *testing.T) {
conventions := "Run mix format before commit"
patterns := "Use pipe operator for transformations"
prompt := BuildSystemPrompt(conventions, patterns)
if !strings.Contains(prompt, "Repository Conventions") {
t.Error("expected conventions section")
}
if !strings.Contains(prompt, "Language Patterns") {
t.Error("expected patterns section")
}
}
func TestBuildUserPrompt_WithFileContext(t *testing.T) {
fileContext := "--- main.go ---\npackage main\n"
prompt := BuildUserPrompt("Fix", "desc", "diff here", fileContext, true, "")
if !strings.Contains(prompt, "Full File Context") {
t.Error("expected file context section")
}
if !strings.Contains(prompt, "package main") {
t.Error("expected file content in prompt")
}
}
func TestBuildUserPrompt_WithoutFileContext(t *testing.T) {
prompt := BuildUserPrompt("Fix", "desc", "diff here", "", true, "")
if strings.Contains(prompt, "Full File Context") {
t.Error("should not include file context section when empty")
}
}