Files
review-bot/review/prompt.go
T
Rodin 57e62a345f
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 9m31s
CI / review (/anthropic/v1, anthropic--claude-4.6-sonnet, sonnet, anthropic, SONNET_REVIEW_TOKEN) (pull_request) Successful in 10m3s
CI / review (/openai/v1, gpt-5, gpt, openai, GPT_REVIEW_TOKEN) (pull_request) Successful in 11m30s
CI / review (/openai/v1, gpt-5, security, openai, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 10m56s
feat(persona): add role-based review personas
Add persona system for specialized review roles. Each persona defines:
- A specific review focus (security, architecture, documentation)
- Custom system prompt additions
- Personality/tone adjustments

Built-in personas: security, architect, docs
Custom personas: load from JSON via persona-file flag

Includes workspace validation to prevent path traversal attacks.

Closes #51
2026-05-10 09:14:48 -07:00

119 lines
4.5 KiB
Go

// Package review builds prompts for AI code review and parses LLM responses
// into structured review results.
package review
import (
"fmt"
"strings"
)
// outputSchemaJSON is the shared JSON output format specification used by both
// the generic reviewer and persona-based reviewers.
const outputSchemaJSON = `{
"verdict": "APPROVE" or "REQUEST_CHANGES",
"summary": "Brief overall assessment (1-3 sentences)",
"findings": [
{
"severity": "MAJOR" or "MINOR" or "NIT",
"file": "path/to/file",
"line": <line number from the diff>,
"finding": "Description of the issue"
}
],
"recommendation": "Full recommendation text explaining your verdict"
}`
// verdictRules is the shared verdict determination rules.
const verdictRules = `Rules:
- If there are any MAJOR findings → verdict must be REQUEST_CHANGES
- If there are no MAJOR findings → verdict should be APPROVE
- If CI has failed → verdict must be REQUEST_CHANGES with a finding noting the CI failure`
// BuildSystemBase returns the core system prompt instructions without
// patterns or conventions. Used by the budget package to separate
// trimmable from non-trimmable content.
func BuildSystemBase() string {
var sb strings.Builder
sb.WriteString("You are an expert code reviewer. Review the provided pull request diff carefully.\n\n")
sb.WriteString("CONTEXT:\n")
sb.WriteString("- You will receive the full content of modified files for reference, followed by the diff showing what changed.\n")
sb.WriteString("- The diff shows ONLY what was added/removed. The full file content provides complete context.\n")
sb.WriteString("- Focus your review on the CHANGES (the diff), using the full files for context.\n\n")
sb.WriteString("Your task:\n")
sb.WriteString("1. Review the diff for correctness, idiomatic code, potential bugs, and design issues.\n")
sb.WriteString("2. Consider the CI status — if CI has failed, that is an automatic REQUEST_CHANGES regardless of code quality.\n")
sb.WriteString("3. Output your review as structured JSON (and ONLY JSON, no markdown fences or other text).\n\n")
sb.WriteString("Output format:\n")
sb.WriteString(outputSchemaJSON)
sb.WriteString("\n\n")
sb.WriteString(verdictRules)
sb.WriteString("\n- Be thorough but fair. Don't nitpick style unless it impacts readability significantly.\n")
sb.WriteString("- Line numbers should reference the new file line numbers from the diff headers.\n")
sb.WriteString("- If the diff is empty or trivial (only formatting/whitespace), APPROVE with no findings.\n")
return sb.String()
}
// BuildSystemPrompt constructs the full system prompt with patterns and conventions.
// Deprecated: Use BuildSystemBase with budget.Fit for context-aware assembly.
func BuildSystemPrompt(conventions, patterns string) string {
var sb strings.Builder
sb.WriteString(BuildSystemBase())
if patterns != "" {
sb.WriteString(fmt.Sprintf("\n\n## Language Patterns & Idioms\n\nUse the following patterns as review criteria. Code that violates these established patterns is a finding:\n\n%s\n", patterns))
}
if conventions != "" {
sb.WriteString(fmt.Sprintf("\n\n## Repository Conventions\n\nThe repository has the following coding conventions that must be respected:\n\n%s\n", conventions))
}
return sb.String()
}
// BuildUserMeta returns the PR metadata header (title, description, CI status)
// without the diff or file context. Used by the budget package.
func BuildUserMeta(title, description string, ciPassed bool, ciDetails string) string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("## Pull Request: %s\n\n", title))
if description != "" {
sb.WriteString(fmt.Sprintf("### Description\n%s\n\n", description))
}
ciStatus := "PASSED"
if !ciPassed {
ciStatus = "FAILED"
}
sb.WriteString(fmt.Sprintf("### CI Status: %s\n", ciStatus))
if ciDetails != "" {
sb.WriteString(fmt.Sprintf("CI Details: %s\n", ciDetails))
}
return sb.String()
}
// BuildUserPrompt constructs the user message with PR context.
// Deprecated: Use BuildUserMeta with budget.Fit for context-aware assembly.
func BuildUserPrompt(title, description, diff, fileContext string, ciPassed bool, ciDetails string) string {
var sb strings.Builder
sb.WriteString(BuildUserMeta(title, description, ciPassed, ciDetails))
if fileContext != "" {
sb.WriteString("\n### Full File Context (modified files)\n\n")
sb.WriteString(fileContext)
sb.WriteString("\n")
}
sb.WriteString("\n### Diff (changes to review)\n\n")
sb.WriteString("```diff\n")
sb.WriteString(diff)
sb.WriteString("\n```\n")
return sb.String()
}