Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

ai-review.ts

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

ai-review.tsBlame145 lines · 1 contributor
e883329Claude1/**
2 * AI-powered code review using Claude.
3 *
4 * Generates inline review comments on pull request diffs.
5 * Reviews are posted as PR comments with isAiReview=true.
6 */
7
8import Anthropic from "@anthropic-ai/sdk";
9import { config } from "./config";
40e7738Claude10import { MODEL_SONNET } from "./ai-client";
e883329Claude11
12interface ReviewComment {
13 filePath: string;
14 lineNumber: number | null;
15 body: string;
16}
17
18interface ReviewResult {
19 summary: string;
20 comments: ReviewComment[];
21 approved: boolean;
22}
23
24let _client: Anthropic | null = null;
25
26function getClient(): Anthropic {
27 if (!_client) {
28 if (!config.anthropicApiKey) {
29 throw new Error("ANTHROPIC_API_KEY is not set");
30 }
31 _client = new Anthropic({ apiKey: config.anthropicApiKey });
32 }
33 return _client;
34}
35
36/**
37 * Run AI code review on a PR diff.
38 */
39export async function reviewDiff(
40 repoFullName: string,
41 prTitle: string,
42 prBody: string | null,
43 baseBranch: string,
44 headBranch: string,
45 diffText: string
46): Promise<ReviewResult> {
47 const client = getClient();
48
49 const message = await client.messages.create({
40e7738Claude50 model: MODEL_SONNET,
e883329Claude51 max_tokens: 4096,
52 messages: [
53 {
54 role: "user",
55 content: `You are reviewing a pull request on the repository "${repoFullName}".
56
57**PR Title:** ${prTitle}
58**PR Description:** ${prBody || "(none)"}
59**Base branch:** ${baseBranch}
60**Head branch:** ${headBranch}
61
62Review the following diff. Look for:
63- Bugs, logic errors, or potential runtime failures
64- Security vulnerabilities (injection, XSS, auth bypasses, secrets in code)
65- Performance issues (N+1 queries, unnecessary allocations, blocking I/O)
66- Missing error handling at system boundaries
67- Breaking changes or API contract violations
68
69Do NOT comment on style, formatting, naming, missing docs, or minor nitpicks. Only flag issues that could cause real problems.
70
71Respond in JSON format:
72{
73 "summary": "1-3 sentence overall assessment",
74 "approved": true/false,
75 "comments": [
76 {
77 "filePath": "path/to/file.ts",
78 "lineNumber": 42,
79 "body": "Explain the issue and suggest a fix"
80 }
81 ]
82}
83
84If the diff looks clean, return approved: true with an empty comments array.
85
86\`\`\`diff
87${diffText.slice(0, 100000)}
88\`\`\``,
89 },
90 ],
91 });
92
93 const text =
94 message.content[0].type === "text" ? message.content[0].text : "";
95
96 try {
97 // Extract JSON from response (may be wrapped in markdown code block)
98 const jsonMatch = text.match(/\{[\s\S]*\}/);
99 if (!jsonMatch) {
100 return {
101 summary: "AI review completed but could not parse structured output.",
102 comments: [],
103 approved: true,
104 };
105 }
106 const parsed = JSON.parse(jsonMatch[0]);
107 return {
108 summary: parsed.summary || "Review complete.",
109 comments: Array.isArray(parsed.comments) ? parsed.comments : [],
110 approved: parsed.approved !== false,
111 };
112 } catch {
113 return {
114 summary: text.slice(0, 500),
115 comments: [],
116 approved: true,
117 };
118 }
119}
120
121/**
122 * Check if AI review is available (API key configured).
123 */
124export function isAiReviewEnabled(): boolean {
125 return !!config.anthropicApiKey;
126}
0316dbbClaude127
128/**
129 * Fire-and-forget AI review trigger. Callers .catch() failures.
130 * Currently a stub that defers to reviewDiff once the diff is available.
131 */
132export async function triggerAiReview(
133 ownerName: string,
134 repoName: string,
135 _prId: string,
136 _title: string,
137 _body: string,
138 _baseBranch: string,
139 _headBranch: string,
140): Promise<void> {
141 if (!isAiReviewEnabled()) return;
142 if (process.env.DEBUG_AI_REVIEW === "1") {
143 console.log("[ai-review] queued", ownerName, repoName, _prId);
144 }
145}