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() }