first commit

This commit is contained in:
Tomas Dvorak
2026-02-22 10:42:17 +01:00
commit 55885a0e8f
239 changed files with 103690 additions and 0 deletions
+315
View File
@@ -0,0 +1,315 @@
package review
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
"github.com/yourorg/devour/internal/quality"
)
type ReviewPacket struct {
Generated time.Time `json:"generated"`
ProjectPath string `json:"project_path"`
Language string `json:"language"`
Scorecard *quality.Scorecard `json:"scorecard"`
Findings []FindingReview `json:"findings"`
Context ReviewContext `json:"context"`
Questions []ReviewQuestion `json:"questions"`
}
type FindingReview struct {
ID string `json:"id"`
Type string `json:"type"`
Title string `json:"title"`
Description string `json:"description"`
File string `json:"file"`
Line int `json:"line"`
Severity quality.Severity `json:"severity"`
Score int `json:"score"`
Status quality.Status `json:"status"`
NeedsReview bool `json:"needs_review"`
Context string `json:"context"`
Metadata map[string]string `json:"metadata"`
}
type ReviewContext struct {
TotalFiles int `json:"total_files"`
TotalLOC int `json:"total_loc"`
FindingsByDim map[string]int `json:"findings_by_dimension"`
TopIssues []string `json:"top_issues"`
Trends map[string]string `json:"trends"`
}
type ReviewQuestion struct {
ID string `json:"id"`
Category string `json:"category"`
Question string `json:"question"`
Options []string `json:"options,omitempty"`
}
type PacketGenerator struct {
dataDir string
}
func NewPacketGenerator(dataDir string) *PacketGenerator {
return &PacketGenerator{dataDir: dataDir}
}
func (g *PacketGenerator) Generate(findings []quality.Finding, scorecard *quality.Scorecard, lang string) (*ReviewPacket, error) {
packet := &ReviewPacket{
Generated: time.Now(),
ProjectPath: g.dataDir,
Language: lang,
Scorecard: scorecard,
Findings: g.convertFindings(findings),
Context: g.buildContext(findings),
Questions: g.generateQuestions(findings),
}
return packet, nil
}
func (g *PacketGenerator) convertFindings(findings []quality.Finding) []FindingReview {
var reviews []FindingReview
for _, f := range findings {
if f.Status != quality.StatusOpen {
continue
}
review := FindingReview{
ID: f.ID,
Type: f.Type,
Title: f.Title,
Description: f.Description,
File: f.File,
Line: f.Line,
Severity: f.Severity,
Score: f.Score,
Status: f.Status,
NeedsReview: f.Severity >= quality.SeverityT3,
Metadata: f.Metadata,
}
review.Context = g.generateContext(f)
reviews = append(reviews, review)
}
return reviews
}
func (g *PacketGenerator) generateContext(f quality.Finding) string {
switch f.Type {
case "complexity", "complexity_ast":
return "This function may be difficult to maintain. Consider if it can be simplified or broken down."
case "duplication":
return "Similar code exists elsewhere. Consider extracting common functionality."
case "dead_code":
return "This code appears unused. Verify before removing - it may be called via reflection or external tools."
case "security":
return "Potential security concern. Review carefully and consider security implications."
case "import_cycle":
return "Circular dependency detected. This can cause initialization issues and makes code harder to understand."
default:
return "Review this finding and decide if it needs addressing."
}
}
func (g *PacketGenerator) buildContext(findings []quality.Finding) ReviewContext {
byDim := make(map[string]int)
var topIssues []string
for _, f := range findings {
if f.Status == quality.StatusOpen {
dim := g.classifyDimension(f)
byDim[dim]++
}
}
topCount := 0
for _, f := range findings {
if f.Status == quality.StatusOpen && topCount < 5 {
topIssues = append(topIssues, fmt.Sprintf("%s: %s", f.Type, f.Title))
topCount++
}
}
return ReviewContext{
FindingsByDim: byDim,
TopIssues: topIssues,
Trends: make(map[string]string),
}
}
func (g *PacketGenerator) classifyDimension(f quality.Finding) string {
switch f.Type {
case "complexity", "complexity_ast":
return "Code Quality"
case "duplication":
return "Duplication"
case "dead_code", "unused_import", "unused":
return "File Health"
case "security":
return "Security"
case "naming":
return "Naming Quality"
case "import_cycle":
return "Architecture"
default:
return "Other"
}
}
func (g *PacketGenerator) generateQuestions(findings []quality.Finding) []ReviewQuestion {
var questions []ReviewQuestion
hasDupes := false
hasComplex := false
hasDead := false
for _, f := range findings {
if f.Status != quality.StatusOpen {
continue
}
switch f.Type {
case "duplication":
hasDupes = true
case "complexity", "complexity_ast":
hasComplex = true
case "dead_code":
hasDead = true
}
}
if hasDupes {
questions = append(questions, ReviewQuestion{
ID: "dupe_strategy",
Category: "duplication",
Question: "How should duplicated code be consolidated?",
Options: []string{
"Extract to shared utility",
"Keep separate (different use cases)",
"Refactor to common interface",
},
})
}
if hasComplex {
questions = append(questions, ReviewQuestion{
ID: "complexity_strategy",
Category: "complexity",
Question: "What's the best approach for complex functions?",
Options: []string{
"Break into smaller functions",
"Introduce helper types",
"Accept current complexity",
},
})
}
if hasDead {
questions = append(questions, ReviewQuestion{
ID: "dead_code_strategy",
Category: "maintenance",
Question: "Should unused code be removed?",
Options: []string{
"Remove if truly unused",
"Keep for future use",
"Mark as deprecated",
},
})
}
questions = append(questions, ReviewQuestion{
ID: "priority",
Category: "planning",
Question: "Which area should be prioritized for improvement?",
Options: []string{
"Security issues first",
"Complexity reduction",
"Dead code cleanup",
"Architecture improvements",
},
})
return questions
}
func (g *PacketGenerator) Save(packet *ReviewPacket, filename string) error {
reviewDir := filepath.Join(g.dataDir, "review")
if err := os.MkdirAll(reviewDir, 0755); err != nil {
return fmt.Errorf("failed to create review directory: %w", err)
}
path := filepath.Join(reviewDir, filename)
data, err := json.MarshalIndent(packet, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal packet: %w", err)
}
if err := os.WriteFile(path, data, 0644); err != nil {
return fmt.Errorf("failed to write packet: %w", err)
}
return nil
}
func (g *PacketGenerator) Load(filename string) (*ReviewPacket, error) {
path := filepath.Join(g.dataDir, "review", filename)
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read packet: %w", err)
}
var packet ReviewPacket
if err := json.Unmarshal(data, &packet); err != nil {
return nil, fmt.Errorf("failed to parse packet: %w", err)
}
return &packet, nil
}
func (g *PacketGenerator) ImportReview(filename string, responses map[string]string) error {
_, err := g.Load(filename)
if err != nil {
return err
}
findingsPath := filepath.Join(g.dataDir, "quality", "status.json")
data, err := os.ReadFile(findingsPath)
if err != nil {
return fmt.Errorf("failed to read findings: %w", err)
}
var state struct {
Findings []quality.Finding `json:"findings"`
}
if err := json.Unmarshal(data, &state); err != nil {
return fmt.Errorf("failed to parse findings: %w", err)
}
for _, f := range state.Findings {
if response, ok := responses[f.ID]; ok {
if f.Metadata == nil {
f.Metadata = make(map[string]string)
}
f.Metadata["review_response"] = response
f.Metadata["reviewed_at"] = time.Now().Format(time.RFC3339)
}
}
updatedData, err := json.MarshalIndent(state, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal updated findings: %w", err)
}
if err := os.WriteFile(findingsPath, updatedData, 0644); err != nil {
return fmt.Errorf("failed to write updated findings: %w", err)
}
return nil
}