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
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
105 lines
3.8 KiB
Go
105 lines
3.8 KiB
Go
package review
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// BuildPersonaSystemPrompt constructs a system prompt from a persona definition.
|
|
// This replaces BuildSystemBase when a persona is provided.
|
|
func BuildPersonaSystemPrompt(p *Persona) string {
|
|
var sb strings.Builder
|
|
|
|
// Identity section
|
|
sb.WriteString(p.Identity)
|
|
sb.WriteString("\n\n")
|
|
|
|
// Focus section
|
|
if len(p.Focus) > 0 {
|
|
sb.WriteString("## Focus Areas\n\n")
|
|
sb.WriteString("Concentrate your review on:\n")
|
|
for _, f := range p.Focus {
|
|
sb.WriteString(fmt.Sprintf("- %s\n", f))
|
|
}
|
|
sb.WriteString("\n")
|
|
}
|
|
|
|
// Ignore section
|
|
if len(p.Ignore) > 0 {
|
|
sb.WriteString("## Explicitly Out of Scope\n\n")
|
|
sb.WriteString("Do NOT comment on:\n")
|
|
for _, i := range p.Ignore {
|
|
sb.WriteString(fmt.Sprintf("- %s\n", i))
|
|
}
|
|
sb.WriteString("\n")
|
|
}
|
|
|
|
// Severity calibration
|
|
if p.Severity.Major != "" || p.Severity.Minor != "" || p.Severity.Nit != "" {
|
|
sb.WriteString("## Severity Calibration\n\n")
|
|
sb.WriteString("Use these severity definitions for YOUR domain:\n")
|
|
if p.Severity.Major != "" {
|
|
sb.WriteString(fmt.Sprintf("- **MAJOR**: %s\n", p.Severity.Major))
|
|
}
|
|
if p.Severity.Minor != "" {
|
|
sb.WriteString(fmt.Sprintf("- **MINOR**: %s\n", p.Severity.Minor))
|
|
}
|
|
if p.Severity.Nit != "" {
|
|
sb.WriteString(fmt.Sprintf("- **NIT**: %s\n", p.Severity.Nit))
|
|
}
|
|
sb.WriteString("\n")
|
|
}
|
|
|
|
// Output format instructions (shared schema from prompt.go)
|
|
sb.WriteString("## Review Instructions\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 issues within YOUR focus areas only.\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- Only report findings within your focus areas. Ignore everything else.\n")
|
|
sb.WriteString("- Line numbers should reference the new file line numbers from the diff headers.\n")
|
|
sb.WriteString("- If the diff has no changes relevant to your focus areas, APPROVE with no findings.\n")
|
|
|
|
// Custom output format if provided
|
|
if p.OutputFormat != "" {
|
|
sb.WriteString("\n\n## Additional Output Guidelines\n\n")
|
|
sb.WriteString(p.OutputFormat)
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// BuildSystemPromptWithPersona constructs the full system prompt, using either
|
|
// a persona or the default generic prompt. This is a convenience wrapper that
|
|
// combines BuildPersonaSystemPrompt (or BuildSystemBase) with patterns and conventions.
|
|
// It is exported for use by callers who want one-shot prompt assembly.
|
|
func BuildSystemPromptWithPersona(persona *Persona, conventions, patterns string) string {
|
|
var base string
|
|
if persona != nil {
|
|
base = BuildPersonaSystemPrompt(persona)
|
|
} else {
|
|
base = BuildSystemBase()
|
|
}
|
|
|
|
var sb strings.Builder
|
|
sb.WriteString(base)
|
|
|
|
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()
|
|
}
|