scai 0.1.74 → 0.1.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/ReviewCmd.js +134 -197
- package/dist/utils/summarizer.js +1 -1
- package/package.json +1 -1
|
@@ -1,334 +1,271 @@
|
|
|
1
1
|
import readline from 'readline';
|
|
2
|
-
import { fetchOpenPullRequests,
|
|
2
|
+
import { fetchOpenPullRequests, getGitHubUsername, submitReview } from '../github/github.js';
|
|
3
3
|
import { getRepoDetails } from '../github/repo.js';
|
|
4
4
|
import { ensureGitHubAuth } from '../github/auth.js';
|
|
5
|
-
import { postReviewComment } from '../github/postComments.js';
|
|
6
5
|
import { reviewModule } from '../pipeline/modules/reviewModule.js';
|
|
7
6
|
import chalk from 'chalk';
|
|
8
|
-
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { spawnSync } from 'child_process';
|
|
11
|
+
import columnify from 'columnify';
|
|
12
|
+
function truncate(str, length) {
|
|
13
|
+
return str.length > length ? str.slice(0, length - 3) + '...' : str;
|
|
14
|
+
}
|
|
15
|
+
// Fetch open PRs with review requested
|
|
9
16
|
export async function getPullRequestsForReview(token, owner, repo, username, branch = 'main', filterForUser = true) {
|
|
10
17
|
const prs = await fetchOpenPullRequests(token, owner, repo);
|
|
11
18
|
const filtered = [];
|
|
12
19
|
const failedPRs = [];
|
|
13
20
|
for (const pr of prs) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const shouldInclude = !isDraft &&
|
|
17
|
-
!isMerged &&
|
|
21
|
+
const shouldInclude = !pr.draft &&
|
|
22
|
+
!pr.merged_at &&
|
|
18
23
|
(!filterForUser || pr.requested_reviewers?.some(r => r.login === username));
|
|
19
24
|
if (shouldInclude) {
|
|
20
|
-
const prNumber = pr.number;
|
|
21
25
|
try {
|
|
22
|
-
|
|
26
|
+
const prNumber = pr.number;
|
|
23
27
|
const prRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}`, {
|
|
24
28
|
headers: {
|
|
25
29
|
Authorization: `token ${token}`,
|
|
26
30
|
Accept: 'application/vnd.github.v3+json',
|
|
27
31
|
},
|
|
28
32
|
});
|
|
29
|
-
if (!prRes.ok)
|
|
30
|
-
throw new Error(`Failed to fetch full PR
|
|
31
|
-
}
|
|
33
|
+
if (!prRes.ok)
|
|
34
|
+
throw new Error(`Failed to fetch full PR #${prNumber}`);
|
|
32
35
|
const fullPR = await prRes.json();
|
|
33
|
-
pr.body = fullPR.body ?? '';
|
|
34
|
-
|
|
35
|
-
const diffUrl = `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}.diff`;
|
|
36
|
-
const diffRes = await fetch(diffUrl, {
|
|
36
|
+
pr.body = fullPR.body ?? '';
|
|
37
|
+
const diffRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}.diff`, {
|
|
37
38
|
headers: {
|
|
38
39
|
Authorization: `token ${token}`,
|
|
39
40
|
Accept: 'application/vnd.github.v3.diff',
|
|
40
41
|
},
|
|
41
42
|
});
|
|
42
|
-
if (!diffRes.ok)
|
|
43
|
-
throw new Error(
|
|
44
|
-
}
|
|
43
|
+
if (!diffRes.ok)
|
|
44
|
+
throw new Error(`Failed to fetch diff for PR #${prNumber}`);
|
|
45
45
|
const diff = await diffRes.text();
|
|
46
46
|
filtered.push({ pr, diff });
|
|
47
47
|
}
|
|
48
48
|
catch (err) {
|
|
49
|
-
console.warn(`⚠️ Skipping PR #${pr.number}
|
|
49
|
+
console.warn(`⚠️ Skipping PR #${pr.number}: ${err.message}`);
|
|
50
50
|
failedPRs.push(pr);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
// After collecting filtered PRs
|
|
55
|
+
if (filtered.length === 0) {
|
|
56
|
+
if (filterForUser) {
|
|
57
|
+
console.log(`ℹ️ No open pull requests found for review by '${username}'.`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.log(`ℹ️ No open pull requests found.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
54
63
|
if (failedPRs.length > 0) {
|
|
55
64
|
const failedList = failedPRs.map(pr => `#${pr.number}`).join(', ');
|
|
56
|
-
console.warn(`⚠️ Skipped ${failedPRs.length} PR(s)
|
|
65
|
+
console.warn(`⚠️ Skipped ${failedPRs.length} PR(s): ${failedList}`);
|
|
57
66
|
}
|
|
58
67
|
return filtered;
|
|
59
68
|
}
|
|
60
|
-
//
|
|
69
|
+
// Prompt user to select PR
|
|
61
70
|
function askUserToPickPR(prs) {
|
|
62
71
|
return new Promise((resolve) => {
|
|
63
72
|
if (prs.length === 0) {
|
|
64
73
|
console.log("⚠️ No pull requests with review requested.");
|
|
65
74
|
return resolve(null);
|
|
66
75
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
const rows = prs.map((pr, i) => ({
|
|
77
|
+
'#': i + 1,
|
|
78
|
+
ID: `#${pr.number}`,
|
|
79
|
+
Title: truncate(pr.title, 50),
|
|
80
|
+
Author: pr.user || '—',
|
|
81
|
+
Status: pr.draft ? 'Draft' : 'Open',
|
|
82
|
+
Created: pr.created_at?.split('T')[0] || '',
|
|
83
|
+
Reviewers: pr.requested_reviewers?.map(r => r.login).join(', ') || '—',
|
|
84
|
+
}));
|
|
85
|
+
console.log(chalk.blue("\n📦 Open Pull Requests:"));
|
|
86
|
+
console.log(columnify(rows, {
|
|
87
|
+
columnSplitter: ' ',
|
|
88
|
+
headingTransform: (h) => chalk.cyan(h.toUpperCase()),
|
|
89
|
+
config: {
|
|
90
|
+
Title: { maxWidth: 50 },
|
|
91
|
+
Reviewers: { maxWidth: 30 }
|
|
92
|
+
}
|
|
93
|
+
}));
|
|
94
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
95
|
+
rl.question(`\n👉 Choose a PR to review [1-${prs.length}]: `, (answer) => {
|
|
96
|
+
rl.close();
|
|
97
|
+
const index = parseInt(answer, 10);
|
|
98
|
+
if (!isNaN(index) && index >= 1 && index <= prs.length) {
|
|
99
|
+
resolve(index - 1);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
console.log('⚠️ Invalid selection.');
|
|
103
|
+
resolve(null);
|
|
104
|
+
}
|
|
74
105
|
});
|
|
75
|
-
const askQuestion = () => {
|
|
76
|
-
rl.question(`\n👉 Choose a PR to review [1-${prs.length}]: `, (answer) => {
|
|
77
|
-
const index = parseInt(answer, 10);
|
|
78
|
-
if (!isNaN(index) && index >= 1 && index <= prs.length) {
|
|
79
|
-
resolve(index - 1); // Return array index, not PR number
|
|
80
|
-
rl.close();
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
console.log('⚠️ Invalid selection. Please enter a number between 1 and ' + prs.length);
|
|
84
|
-
askQuestion(); // Retry asking for input
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
};
|
|
88
|
-
askQuestion(); // Initial call to ask the user
|
|
89
106
|
});
|
|
90
107
|
}
|
|
91
|
-
//
|
|
108
|
+
// Prompt for review method
|
|
92
109
|
function askReviewMethod() {
|
|
93
110
|
return new Promise((resolve) => {
|
|
94
|
-
const rl = readline.createInterface({
|
|
95
|
-
input: process.stdin,
|
|
96
|
-
output: process.stdout,
|
|
97
|
-
});
|
|
111
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
98
112
|
console.log("\n🔍 Choose review method:");
|
|
99
113
|
console.log('1) Review whole PR at once');
|
|
100
114
|
console.log('2) Review chunk by chunk');
|
|
101
115
|
rl.question(`👉 Choose an option [1-2]: `, (answer) => {
|
|
102
116
|
rl.close();
|
|
103
|
-
|
|
104
|
-
resolve('whole');
|
|
105
|
-
}
|
|
106
|
-
else if (answer === '2') {
|
|
107
|
-
resolve('chunk');
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
console.log('⚠️ Invalid selection. Defaulting to "whole".');
|
|
111
|
-
resolve('whole');
|
|
112
|
-
}
|
|
117
|
+
resolve(answer === '2' ? 'chunk' : 'whole');
|
|
113
118
|
});
|
|
114
119
|
});
|
|
115
120
|
}
|
|
116
|
-
//
|
|
121
|
+
// Prompt for review approval
|
|
117
122
|
function askReviewApproval(suggestion) {
|
|
118
123
|
return new Promise((resolve) => {
|
|
119
|
-
console.log('\n💡 AI-suggested review:\n');
|
|
120
|
-
console.log(suggestion);
|
|
121
124
|
console.log('\n---');
|
|
122
125
|
console.log('1) ✅ Approve');
|
|
123
126
|
console.log('2) ❌ Reject');
|
|
124
127
|
console.log('3) ✍️ Edit');
|
|
125
128
|
console.log('4) ⌨️ Write your own review');
|
|
126
129
|
console.log('5) 🚫 Cancel');
|
|
127
|
-
const rl = readline.createInterface({
|
|
128
|
-
input: process.stdin,
|
|
129
|
-
output: process.stdout,
|
|
130
|
-
});
|
|
130
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
131
131
|
rl.question(`\n👉 Choose an option [1-5]: `, (answer) => {
|
|
132
132
|
rl.close();
|
|
133
|
-
if (answer === '1')
|
|
133
|
+
if (answer === '1')
|
|
134
134
|
resolve('approve');
|
|
135
|
-
|
|
136
|
-
else if (answer === '2') {
|
|
135
|
+
else if (answer === '2')
|
|
137
136
|
resolve('reject');
|
|
138
|
-
|
|
139
|
-
else if (answer === '3') {
|
|
137
|
+
else if (answer === '3')
|
|
140
138
|
resolve('edit');
|
|
141
|
-
|
|
142
|
-
else if (answer === '4') {
|
|
139
|
+
else if (answer === '4')
|
|
143
140
|
resolve('custom');
|
|
144
|
-
|
|
145
|
-
else if (answer === '5') {
|
|
141
|
+
else
|
|
146
142
|
resolve('cancel');
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
console.log('⚠️ Invalid selection. Defaulting to "approve".');
|
|
150
|
-
resolve('approve');
|
|
151
|
-
}
|
|
152
143
|
});
|
|
153
144
|
});
|
|
154
145
|
}
|
|
155
|
-
// Prompt for custom review
|
|
146
|
+
// Prompt for custom review
|
|
156
147
|
function promptCustomReview() {
|
|
157
148
|
return new Promise((resolve) => {
|
|
158
|
-
const rl = readline.createInterface({
|
|
159
|
-
input: process.stdin,
|
|
160
|
-
output: process.stdout,
|
|
161
|
-
});
|
|
149
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
162
150
|
rl.question('\n📝 Enter your custom review:\n> ', (input) => {
|
|
163
151
|
rl.close();
|
|
164
152
|
resolve(input.trim());
|
|
165
153
|
});
|
|
166
154
|
});
|
|
167
155
|
}
|
|
156
|
+
// Prompt for editing review
|
|
157
|
+
export async function promptEditReview(suggestedReview) {
|
|
158
|
+
const tmpFilePath = path.join(os.tmpdir(), 'scai-review.txt');
|
|
159
|
+
fs.writeFileSync(tmpFilePath, `# Edit your review below.\n# Lines starting with '#' will be ignored.\n\n${suggestedReview}`);
|
|
160
|
+
const editor = process.env.EDITOR || (process.platform === 'win32' ? 'notepad' : 'vi');
|
|
161
|
+
spawnSync(editor, [tmpFilePath], { stdio: 'inherit' });
|
|
162
|
+
const editedContent = fs.readFileSync(tmpFilePath, 'utf-8');
|
|
163
|
+
return editedContent
|
|
164
|
+
.split('\n')
|
|
165
|
+
.filter(line => !line.trim().startsWith('#'))
|
|
166
|
+
.join('\n')
|
|
167
|
+
.trim() || suggestedReview;
|
|
168
|
+
}
|
|
169
|
+
// Split diff into file-based chunks
|
|
168
170
|
function chunkDiff(diff) {
|
|
169
171
|
const rawChunks = diff.split(/^diff --git /m).filter(Boolean);
|
|
170
172
|
return rawChunks.map(chunk => {
|
|
171
173
|
const fullChunk = 'diff --git ' + chunk;
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
const filePathMatch = fullChunk.match(/^diff --git a\/(.+?) b\//);
|
|
175
|
+
const filePath = filePathMatch ? filePathMatch[1] : 'unknown';
|
|
176
|
+
return {
|
|
177
|
+
filePath,
|
|
178
|
+
content: fullChunk,
|
|
179
|
+
};
|
|
176
180
|
});
|
|
177
181
|
}
|
|
178
|
-
//
|
|
182
|
+
// Color lines in diff
|
|
179
183
|
function colorDiffLine(line) {
|
|
180
|
-
if (line.startsWith('+'))
|
|
181
|
-
return chalk.green(line);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return chalk.yellow(line); // Modified lines (with context)
|
|
188
|
-
}
|
|
189
|
-
return line; // Default case (unchanged lines)
|
|
184
|
+
if (line.startsWith('+'))
|
|
185
|
+
return chalk.green(line);
|
|
186
|
+
if (line.startsWith('-'))
|
|
187
|
+
return chalk.red(line);
|
|
188
|
+
if (line.startsWith('@@'))
|
|
189
|
+
return chalk.yellow(line);
|
|
190
|
+
return line;
|
|
190
191
|
}
|
|
191
|
-
//
|
|
192
|
+
// Review a single chunk
|
|
192
193
|
export async function reviewChunk(chunk, chunkIndex, totalChunks) {
|
|
193
194
|
console.log(chalk.yellow(`\n🔍 Reviewing chunk ${chunkIndex + 1} of ${totalChunks}:`));
|
|
194
195
|
console.log(`File: ${chunk.filePath}`);
|
|
195
|
-
// Split the chunk content into lines and color each line accordingly
|
|
196
196
|
const coloredDiff = chunk.content.split('\n').map(colorDiffLine).join('\n');
|
|
197
|
-
// Log the colored diff with line numbers and progress
|
|
198
|
-
console.log(`Starting line number: 30`); // Adjust based on actual starting line logic
|
|
199
197
|
console.log(coloredDiff);
|
|
200
|
-
// Get the review suggestion from the model (as before)
|
|
201
198
|
const suggestion = await reviewModule.run({ content: chunk.content, filepath: chunk.filePath });
|
|
202
199
|
console.log("\n💡 AI-suggested review:\n");
|
|
203
200
|
console.log(suggestion.content);
|
|
204
|
-
|
|
205
|
-
|
|
201
|
+
const reviewChoice = await askReviewApproval(suggestion.content);
|
|
202
|
+
if (reviewChoice === 'edit') {
|
|
203
|
+
return await promptEditReview(suggestion.content);
|
|
204
|
+
}
|
|
206
205
|
return reviewChoice;
|
|
207
206
|
}
|
|
207
|
+
// Main command to review PR
|
|
208
208
|
export async function reviewPullRequestCmd(branch = 'main', showAll = false) {
|
|
209
209
|
try {
|
|
210
|
-
|
|
211
|
-
const token = await ensureGitHubAuth(); // Get GitHub token
|
|
210
|
+
const token = await ensureGitHubAuth();
|
|
212
211
|
const username = await getGitHubUsername(token);
|
|
213
|
-
const { owner, repo } = getRepoDetails();
|
|
214
|
-
console.log(`👤 Authenticated user: ${username}`);
|
|
215
|
-
console.log(`📦 GitHub repo: ${owner}/${repo}`);
|
|
216
|
-
console.log(`🔍 Filtering ${showAll ? "all" : "user-specific"} PRs for branch: ${branch}`);
|
|
212
|
+
const { owner, repo } = getRepoDetails();
|
|
217
213
|
const prsWithReviewRequested = await getPullRequestsForReview(token, owner, repo, username, branch, !showAll);
|
|
218
|
-
|
|
219
|
-
if (prsWithReviewRequested.length === 0) {
|
|
220
|
-
console.log("⚠️ No PRs found with review requested.");
|
|
214
|
+
if (prsWithReviewRequested.length === 0)
|
|
221
215
|
return;
|
|
222
|
-
}
|
|
223
216
|
const selectedIndex = await askUserToPickPR(prsWithReviewRequested.map(p => p.pr));
|
|
224
217
|
if (selectedIndex === null)
|
|
225
218
|
return;
|
|
226
219
|
const { pr, diff } = prsWithReviewRequested[selectedIndex];
|
|
227
220
|
if (pr.body) {
|
|
228
|
-
console.log(chalk.magentaBright('\n📝
|
|
229
|
-
console.log(chalk.gray(pr.body));
|
|
230
|
-
console.log(chalk.magentaBright('\n---\n'));
|
|
231
|
-
}
|
|
232
|
-
let prDiff = diff;
|
|
233
|
-
if (!prDiff) {
|
|
234
|
-
console.log(`🔍 Fetching diff for PR #${pr.number}...`);
|
|
235
|
-
prDiff = await fetchPullRequestDiff(pr, token);
|
|
221
|
+
console.log(chalk.magentaBright('\n📝 PR Description:\n') + chalk.gray(pr.body));
|
|
236
222
|
}
|
|
237
|
-
const
|
|
238
|
-
const reviewMethod = await askReviewMethod(); // Ask user for review method (whole or chunk)
|
|
223
|
+
const reviewMethod = await askReviewMethod();
|
|
239
224
|
if (reviewMethod === 'whole') {
|
|
240
|
-
|
|
241
|
-
const suggestion = await reviewModule.run({ content: prDiff, filepath: 'Whole PR Diff' });
|
|
242
|
-
console.log("\n💡 AI-suggested review:\n");
|
|
225
|
+
const suggestion = await reviewModule.run({ content: diff, filepath: 'Whole PR Diff' });
|
|
243
226
|
console.log(suggestion.content);
|
|
244
|
-
|
|
245
|
-
|
|
227
|
+
const finalReviewChoice = await askReviewApproval(suggestion.content);
|
|
228
|
+
let reviewText = '';
|
|
246
229
|
if (finalReviewChoice === 'approve') {
|
|
247
|
-
|
|
248
|
-
await
|
|
249
|
-
await submitReview(pr.number, 'PR approved', 'APPROVE');
|
|
230
|
+
reviewText = 'PR approved';
|
|
231
|
+
await submitReview(pr.number, reviewText, 'APPROVE');
|
|
250
232
|
}
|
|
251
233
|
else if (finalReviewChoice === 'reject') {
|
|
252
|
-
|
|
253
|
-
await
|
|
254
|
-
await submitReview(pr.number, 'Changes requested', 'REQUEST_CHANGES');
|
|
234
|
+
reviewText = 'Changes requested';
|
|
235
|
+
await submitReview(pr.number, reviewText, 'REQUEST_CHANGES');
|
|
255
236
|
}
|
|
256
|
-
else if (finalReviewChoice === '
|
|
257
|
-
|
|
258
|
-
|
|
237
|
+
else if (finalReviewChoice === 'custom') {
|
|
238
|
+
reviewText = await promptCustomReview();
|
|
239
|
+
await submitReview(pr.number, reviewText, 'COMMENT');
|
|
259
240
|
}
|
|
260
|
-
else {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
await postReviewComments(pr, chunkComments, token);
|
|
264
|
-
await submitReview(pr.number, customReview, 'COMMENT');
|
|
241
|
+
else if (finalReviewChoice === 'edit') {
|
|
242
|
+
reviewText = await promptEditReview(suggestion.content);
|
|
243
|
+
await submitReview(pr.number, reviewText, 'COMMENT');
|
|
265
244
|
}
|
|
266
245
|
}
|
|
267
246
|
else {
|
|
268
|
-
const chunks = chunkDiff(
|
|
269
|
-
// Log the total number of chunks
|
|
247
|
+
const chunks = chunkDiff(diff);
|
|
270
248
|
console.log(chalk.cyan(`🔍 Total Chunks: ${chunks.length}`));
|
|
271
|
-
// Iterate over each chunk, passing the index and total chunk count
|
|
272
249
|
for (let i = 0; i < chunks.length; i++) {
|
|
273
250
|
const chunk = chunks[i];
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
console.log('✅ Approving this chunk.');
|
|
278
|
-
chunkComments[chunk.filePath] = ['Approved'];
|
|
251
|
+
const reviewResult = await reviewChunk(chunk, i, chunks.length);
|
|
252
|
+
if (reviewResult === 'approve') {
|
|
253
|
+
await submitReview(pr.number, 'Approved chunk', 'APPROVE');
|
|
279
254
|
}
|
|
280
|
-
else if (
|
|
281
|
-
|
|
282
|
-
chunkComments[chunk.filePath] = ['Rejected'];
|
|
255
|
+
else if (reviewResult === 'reject') {
|
|
256
|
+
await submitReview(pr.number, 'Changes requested for chunk', 'REQUEST_CHANGES');
|
|
283
257
|
}
|
|
284
|
-
else if (
|
|
285
|
-
|
|
286
|
-
|
|
258
|
+
else if (reviewResult === 'custom') {
|
|
259
|
+
const customReview = await promptCustomReview();
|
|
260
|
+
await submitReview(pr.number, customReview, 'COMMENT');
|
|
287
261
|
}
|
|
288
262
|
else {
|
|
289
|
-
|
|
290
|
-
const customReview = await promptCustomReview();
|
|
291
|
-
console.log(`💬 Custom review: ${customReview}`);
|
|
292
|
-
chunkComments[chunk.filePath] = [customReview];
|
|
263
|
+
await submitReview(pr.number, reviewResult, 'COMMENT');
|
|
293
264
|
}
|
|
294
265
|
}
|
|
295
|
-
// After chunk review, ask for the final approval decision
|
|
296
|
-
const finalReviewChoice = await askReviewApproval('Do you approve, reject, or leave a final review for this PR?');
|
|
297
|
-
if (finalReviewChoice === 'approve') {
|
|
298
|
-
console.log(`✅ Review for PR #${pr.number} approved.`);
|
|
299
|
-
await postReviewComments(pr, chunkComments, token);
|
|
300
|
-
await submitReview(pr.number, 'PR approved', 'APPROVE');
|
|
301
|
-
}
|
|
302
|
-
else if (finalReviewChoice === 'reject') {
|
|
303
|
-
console.log(`❌ Review for PR #${pr.number} rejected.`);
|
|
304
|
-
await postReviewComments(pr, chunkComments, token);
|
|
305
|
-
await submitReview(pr.number, 'Changes requested', 'REQUEST_CHANGES');
|
|
306
|
-
}
|
|
307
|
-
else if (finalReviewChoice === 'cancel') {
|
|
308
|
-
console.log(`🚪 Review process cancelled.`);
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
const customReview = await promptCustomReview();
|
|
313
|
-
console.log(`💬 Custom review: ${customReview}`);
|
|
314
|
-
await postReviewComments(pr, chunkComments, token);
|
|
315
|
-
await submitReview(pr.number, customReview, 'COMMENT');
|
|
316
|
-
}
|
|
317
266
|
}
|
|
318
267
|
}
|
|
319
268
|
catch (err) {
|
|
320
269
|
console.error("❌ Error reviewing PR:", err.message);
|
|
321
270
|
}
|
|
322
271
|
}
|
|
323
|
-
// Function to post all comments to GitHub after the review
|
|
324
|
-
async function postReviewComments(pr, chunkComments, token) {
|
|
325
|
-
const { owner, repo } = getRepoDetails(); // Get the repo details
|
|
326
|
-
for (const chunk in chunkComments) {
|
|
327
|
-
const comments = chunkComments[chunk];
|
|
328
|
-
const fileName = chunk; // Use chunk's file path
|
|
329
|
-
const lineNumber = 10; // Extract the actual line number from the chunk content
|
|
330
|
-
for (const comment of comments) {
|
|
331
|
-
await postReviewComment(token, owner, repo, pr.number, fileName, lineNumber, comment);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
package/dist/utils/summarizer.js
CHANGED