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