-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
123 lines (106 loc) · 3.47 KB
/
Copy pathmain.go
File metadata and controls
123 lines (106 loc) · 3.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"github.com/anthropics/anthropic-sdk-go"
"github.com/anthropics/anthropic-sdk-go/option"
)
// ReviewComment represents a single structured comment from Claude's code review.
type ReviewComment struct {
File string `json:"file"`
Line int `json:"line,omitempty"`
Severity string `json:"severity"` // "critical", "warning", "suggestion", "praise"
Comment string `json:"comment"`
}
// ReviewResult is the structured output Claude returns for a PR review.
type ReviewResult struct {
Summary string `json:"summary"`
Comments []ReviewComment `json:"comments"`
Approved bool `json:"approved"`
}
// sampleDiff simulates a GitHub PR diff — in a real app this comes from the GitHub API.
var sampleDiff = `
diff --git a/handlers/user.go b/handlers/user.go
index abc123..def456 100644
--- a/handlers/user.go
+++ b/handlers/user.go
@@ -15,6 +15,20 @@ func GetUser(w http.ResponseWriter, r *http.Request) {
+func CreateUser(w http.ResponseWriter, r *http.Request) {
+ var user User
+ json.NewDecoder(r.Body).Decode(&user)
+ password := r.FormValue("password")
+ db.Exec("INSERT INTO users VALUES ('" + user.Name + "', '" + password + "')")
+ w.WriteHeader(201)
+}
`
func main() {
client := anthropic.NewClient(option.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")))
ctx := context.Background()
fmt.Println("Reviewing PR diff...")
result, err := reviewPR(ctx, client, sampleDiff)
if err != nil {
fmt.Fprintf(os.Stderr, "review error: %v\n", err)
os.Exit(1)
}
printReview(result)
}
func reviewPR(ctx context.Context, client *anthropic.Client, diff string) (*ReviewResult, error) {
// Use structured output — Claude returns valid JSON matching ReviewResult
resp, err := client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus4_7,
MaxTokens: 2048,
System: []anthropic.TextBlockParam{
anthropic.NewTextBlockParam(`You are an expert Go code reviewer. Analyse the provided git diff and return a JSON object matching exactly this schema:
{
"summary": "one paragraph overview of the changes",
"comments": [
{"file": "path/to/file.go", "line": 42, "severity": "critical|warning|suggestion|praise", "comment": "specific actionable feedback"}
],
"approved": true|false
}
Return ONLY the JSON object — no markdown, no explanation.`),
},
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Review this PR diff:\n\n" + diff)),
},
})
if err != nil {
return nil, err
}
// Extract the text response
var sb strings.Builder
for _, block := range resp.Content {
switch b := block.AsAny().(type) {
case anthropic.TextBlock:
sb.WriteString(b.Text)
}
}
var result ReviewResult
if err := json.Unmarshal([]byte(sb.String()), &result); err != nil {
return nil, fmt.Errorf("failed to parse review JSON: %w\nRaw response: %s", err, sb.String())
}
return &result, nil
}
func printReview(r *ReviewResult) {
status := "✅ APPROVED"
if !r.Approved {
status = "❌ CHANGES REQUESTED"
}
fmt.Printf("\n%s\n\nSummary: %s\n\n", status, r.Summary)
for _, c := range r.Comments {
icon := map[string]string{
"critical": "🚨",
"warning": "⚠️",
"suggestion": "💡",
"praise": "✨",
}[c.Severity]
location := c.File
if c.Line > 0 {
location = fmt.Sprintf("%s:%d", c.File, c.Line)
}
fmt.Printf("%s [%s] %s\n %s\n\n", icon, strings.ToUpper(c.Severity), location, c.Comment)
}
}