edsger 0.13.1 → 0.13.2
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/phases/code-refine/context.d.ts +1 -0
- package/dist/phases/code-refine/context.js +14 -2
- package/dist/phases/code-refine-verification/github.d.ts +7 -0
- package/dist/phases/code-refine-verification/github.js +46 -0
- package/dist/phases/code-refine-verification/index.js +47 -22
- package/dist/phases/code-refine-verification/types.d.ts +1 -0
- package/package.json +1 -1
|
@@ -30,8 +30,19 @@ export async function fetchPRReviews(octokit, owner, repo, prNumber, verbose) {
|
|
|
30
30
|
repo,
|
|
31
31
|
pull_number: prNumber,
|
|
32
32
|
});
|
|
33
|
-
// Filter for reviews that request changes
|
|
34
|
-
const requestChangesReviews = reviews
|
|
33
|
+
// Filter for reviews that request changes and map to our interface
|
|
34
|
+
const requestChangesReviews = reviews
|
|
35
|
+
.filter((review) => review.state === 'CHANGES_REQUESTED')
|
|
36
|
+
.map((review) => ({
|
|
37
|
+
id: review.id,
|
|
38
|
+
node_id: review.node_id, // REST API also returns node_id
|
|
39
|
+
user: {
|
|
40
|
+
login: review.user?.login || 'unknown',
|
|
41
|
+
},
|
|
42
|
+
body: review.body ?? null,
|
|
43
|
+
state: review.state,
|
|
44
|
+
submitted_at: review.submitted_at ?? null,
|
|
45
|
+
}));
|
|
35
46
|
if (verbose) {
|
|
36
47
|
console.log(`✅ Found ${requestChangesReviews.length} reviews requesting changes`);
|
|
37
48
|
}
|
|
@@ -117,6 +128,7 @@ export async function fetchPRDataGraphQL(octokit, owner, repo, prNumber, verbose
|
|
|
117
128
|
// Transform GraphQL reviews to our interface
|
|
118
129
|
const reviews = (pullRequest.reviews.nodes || []).map((review) => ({
|
|
119
130
|
id: review.databaseId,
|
|
131
|
+
node_id: review.id, // GraphQL Node ID for mutations
|
|
120
132
|
user: {
|
|
121
133
|
login: review.author?.login || 'unknown',
|
|
122
134
|
},
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Octokit } from '@octokit/rest';
|
|
6
6
|
import { ReviewThread, PRFileChange } from './types.js';
|
|
7
|
+
import { PRReview } from '../code-refine/context.js';
|
|
7
8
|
/**
|
|
8
9
|
* Fetch complete file content from a specific ref (branch/commit)
|
|
9
10
|
*/
|
|
@@ -21,3 +22,9 @@ export declare function fetchUnresolvedReviewThreads(octokit: Octokit, owner: st
|
|
|
21
22
|
* Mark review threads as resolved using GraphQL API
|
|
22
23
|
*/
|
|
23
24
|
export declare function resolveReviewThreads(octokit: Octokit, threads: ReviewThread[], verbose?: boolean): Promise<number>;
|
|
25
|
+
/**
|
|
26
|
+
* Dismiss pull request reviews using GraphQL API
|
|
27
|
+
* This is used to automatically dismiss "CHANGES_REQUESTED" reviews
|
|
28
|
+
* after all feedback has been addressed by code refine
|
|
29
|
+
*/
|
|
30
|
+
export declare function dismissReviews(octokit: Octokit, reviews: PRReview[], resolvedCommentsCount: number, verbose?: boolean): Promise<number>;
|
|
@@ -188,3 +188,49 @@ export async function resolveReviewThreads(octokit, threads, verbose) {
|
|
|
188
188
|
}
|
|
189
189
|
return markedCount;
|
|
190
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Dismiss pull request reviews using GraphQL API
|
|
193
|
+
* This is used to automatically dismiss "CHANGES_REQUESTED" reviews
|
|
194
|
+
* after all feedback has been addressed by code refine
|
|
195
|
+
*/
|
|
196
|
+
export async function dismissReviews(octokit, reviews, resolvedCommentsCount, verbose) {
|
|
197
|
+
let dismissedCount = 0;
|
|
198
|
+
for (const review of reviews) {
|
|
199
|
+
try {
|
|
200
|
+
if (verbose) {
|
|
201
|
+
logInfo(`🔄 Dismissing review ${review.id} by @${review.user.login}...`);
|
|
202
|
+
}
|
|
203
|
+
// Build dismiss message explaining what was resolved
|
|
204
|
+
const message = `All feedback has been addressed by automated code refine.\n\n` +
|
|
205
|
+
`✅ Resolved ${resolvedCommentsCount} review comment(s).\n\n` +
|
|
206
|
+
`Please re-review if you disagree with any changes.`;
|
|
207
|
+
const mutation = `
|
|
208
|
+
mutation($pullRequestReviewId: ID!, $message: String!) {
|
|
209
|
+
dismissPullRequestReview(input: {
|
|
210
|
+
pullRequestReviewId: $pullRequestReviewId
|
|
211
|
+
message: $message
|
|
212
|
+
}) {
|
|
213
|
+
pullRequestReview {
|
|
214
|
+
id
|
|
215
|
+
state
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
`;
|
|
220
|
+
await octokit.graphql(mutation, {
|
|
221
|
+
pullRequestReviewId: review.node_id,
|
|
222
|
+
message,
|
|
223
|
+
});
|
|
224
|
+
dismissedCount++;
|
|
225
|
+
if (verbose) {
|
|
226
|
+
logInfo(`✅ Dismissed review ${review.id} by @${review.user.login}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
if (verbose) {
|
|
231
|
+
logError(`Failed to dismiss review ${review.id} by @${review.user.login}: ${error}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return dismissedCount;
|
|
236
|
+
}
|
|
@@ -7,7 +7,7 @@ import { Octokit } from '@octokit/rest';
|
|
|
7
7
|
import { logInfo, logError } from '../../utils/logger.js';
|
|
8
8
|
import { parsePullRequestUrl, fetchPRReviews } from '../code-refine/context.js';
|
|
9
9
|
import { getFeature } from '../../api/features/get-feature.js';
|
|
10
|
-
import { fetchPRFileChanges, fetchUnresolvedReviewThreads, resolveReviewThreads, } from './github.js';
|
|
10
|
+
import { fetchPRFileChanges, fetchUnresolvedReviewThreads, resolveReviewThreads, dismissReviews, } from './github.js';
|
|
11
11
|
import { analyzeAllThreads } from './llm-analyzer.js';
|
|
12
12
|
// Re-export types for backward compatibility
|
|
13
13
|
export * from './types.js';
|
|
@@ -65,31 +65,52 @@ export async function verifyAndResolveComments(options) {
|
|
|
65
65
|
const addressedThreads = threadAnalysisResults.filter((result) => result.analysis.isAddressed);
|
|
66
66
|
const trulyUnresolvedThreads = threadAnalysisResults.filter((result) => !result.analysis.isAddressed);
|
|
67
67
|
// Auto-resolve threads that LLM determined are addressed
|
|
68
|
+
let totalResolvedComments = 0;
|
|
68
69
|
if (addressedThreads.length > 0) {
|
|
69
70
|
if (verbose) {
|
|
70
71
|
logInfo(`✅ Auto-resolving ${addressedThreads.length} threads that have been addressed...`);
|
|
71
72
|
}
|
|
72
73
|
const resolvedCount = await resolveReviewThreads(octokit, addressedThreads.map((r) => r.thread), verbose);
|
|
74
|
+
totalResolvedComments = resolvedCount;
|
|
73
75
|
if (verbose) {
|
|
74
76
|
logInfo(`✅ Successfully resolved ${resolvedCount} threads`);
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
|
-
// Check reviews - they need to be dismissed
|
|
78
|
-
const
|
|
79
|
+
// Check reviews - they need to be dismissed if all comments are addressed
|
|
80
|
+
const changesRequestedReviews = reviews.filter((review) => review.state === 'CHANGES_REQUESTED');
|
|
79
81
|
if (verbose) {
|
|
80
82
|
logInfo(`📊 Review Threads: ${trulyUnresolvedThreads.length} still unresolved (after LLM analysis)`);
|
|
81
83
|
if (reviews.length > 0) {
|
|
82
|
-
logInfo(`📊 Reviews: ${
|
|
84
|
+
logInfo(`📊 Reviews: ${changesRequestedReviews.length} requesting changes`);
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
|
-
// If all threads are truly resolved
|
|
86
|
-
|
|
87
|
+
// If all threads are truly resolved, dismiss the "CHANGES_REQUESTED" reviews
|
|
88
|
+
let dismissedReviewsCount = 0;
|
|
89
|
+
if (trulyUnresolvedThreads.length === 0 &&
|
|
90
|
+
changesRequestedReviews.length > 0) {
|
|
91
|
+
if (verbose) {
|
|
92
|
+
logInfo(`🔄 All comments addressed. Dismissing ${changesRequestedReviews.length} review(s) requesting changes...`);
|
|
93
|
+
}
|
|
94
|
+
dismissedReviewsCount = await dismissReviews(octokit, changesRequestedReviews, totalResolvedComments, verbose);
|
|
95
|
+
if (verbose) {
|
|
96
|
+
logInfo(`✅ Successfully dismissed ${dismissedReviewsCount} review(s)`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// If all threads are truly resolved (after LLM analysis) AND reviews are dismissed, success
|
|
100
|
+
const allReviewsDismissed = changesRequestedReviews.length === 0 ||
|
|
101
|
+
dismissedReviewsCount === changesRequestedReviews.length;
|
|
102
|
+
if (trulyUnresolvedThreads.length === 0 && allReviewsDismissed) {
|
|
87
103
|
if (verbose) {
|
|
88
104
|
logInfo('✅ All comments have been addressed! All review threads are resolved.');
|
|
89
105
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
106
|
+
let successMessage = 'All review comments have been addressed and resolved';
|
|
107
|
+
if (dismissedReviewsCount > 0) {
|
|
108
|
+
successMessage = `All reviews and review comments have been addressed. ${dismissedReviewsCount} review(s) dismissed.`;
|
|
109
|
+
}
|
|
110
|
+
else if (reviews.length > 0) {
|
|
111
|
+
successMessage =
|
|
112
|
+
'All reviews and review comments have been addressed and resolved';
|
|
113
|
+
}
|
|
93
114
|
return {
|
|
94
115
|
status: 'success',
|
|
95
116
|
message: successMessage,
|
|
@@ -101,15 +122,18 @@ export async function verifyAndResolveComments(options) {
|
|
|
101
122
|
resolvedComments: addressedThreads.length,
|
|
102
123
|
unresolvedComments: 0,
|
|
103
124
|
commentsMarkedResolved: addressedThreads.length,
|
|
125
|
+
dismissedReviews: dismissedReviewsCount,
|
|
104
126
|
},
|
|
105
127
|
};
|
|
106
128
|
}
|
|
107
129
|
else {
|
|
108
130
|
// Verification failed - build detailed info with specific failure reasons from LLM analysis
|
|
131
|
+
// Calculate remaining reviews that weren't dismissed
|
|
132
|
+
const remainingReviewsCount = changesRequestedReviews.length - dismissedReviewsCount;
|
|
109
133
|
if (verbose) {
|
|
110
|
-
if (
|
|
111
|
-
logInfo(`⚠️ ${
|
|
112
|
-
|
|
134
|
+
if (remainingReviewsCount > 0) {
|
|
135
|
+
logInfo(`⚠️ ${remainingReviewsCount} reviews still requesting changes (failed to dismiss)`);
|
|
136
|
+
changesRequestedReviews.forEach((review) => {
|
|
113
137
|
logInfo(` - Review ${review.id} by @${review.user.login}`);
|
|
114
138
|
if (review.body) {
|
|
115
139
|
logInfo(` ${review.body.substring(0, 100)}...`);
|
|
@@ -138,10 +162,10 @@ export async function verifyAndResolveComments(options) {
|
|
|
138
162
|
suggestions.push(`${index + 1}. [${firstComment.path}:${firstComment.line || '?'}] by @${firstComment.author.login}: ${result.analysis.reason}`);
|
|
139
163
|
}
|
|
140
164
|
});
|
|
141
|
-
// Add review-specific suggestions if any
|
|
142
|
-
if (
|
|
143
|
-
suggestions.push(`\n${
|
|
144
|
-
|
|
165
|
+
// Add review-specific suggestions if any reviews failed to dismiss
|
|
166
|
+
if (remainingReviewsCount > 0) {
|
|
167
|
+
suggestions.push(`\n${remainingReviewsCount} review(s) requesting changes could not be automatically dismissed:`);
|
|
168
|
+
changesRequestedReviews.forEach((review) => {
|
|
145
169
|
suggestions.push(` - @${review.user.login}: ${review.body ? review.body.substring(0, 150) : 'No details provided'}${review.body && review.body.length > 150 ? '...' : ''}`);
|
|
146
170
|
});
|
|
147
171
|
}
|
|
@@ -158,7 +182,7 @@ export async function verifyAndResolveComments(options) {
|
|
|
158
182
|
url: firstComment.url,
|
|
159
183
|
};
|
|
160
184
|
});
|
|
161
|
-
const unresolvedReviewDetails =
|
|
185
|
+
const unresolvedReviewDetails = changesRequestedReviews.map((review) => ({
|
|
162
186
|
reviewId: review.id,
|
|
163
187
|
author: review.user.login,
|
|
164
188
|
state: review.state,
|
|
@@ -166,11 +190,11 @@ export async function verifyAndResolveComments(options) {
|
|
|
166
190
|
submittedAt: review.submitted_at,
|
|
167
191
|
}));
|
|
168
192
|
let errorMessage = '';
|
|
169
|
-
if (
|
|
170
|
-
errorMessage = `${
|
|
193
|
+
if (remainingReviewsCount > 0 && trulyUnresolvedThreads.length > 0) {
|
|
194
|
+
errorMessage = `${remainingReviewsCount} reviews and ${trulyUnresolvedThreads.length} review threads still need to be addressed (based on LLM analysis)`;
|
|
171
195
|
}
|
|
172
|
-
else if (
|
|
173
|
-
errorMessage = `${
|
|
196
|
+
else if (remainingReviewsCount > 0) {
|
|
197
|
+
errorMessage = `${remainingReviewsCount} reviews could not be dismissed`;
|
|
174
198
|
}
|
|
175
199
|
else {
|
|
176
200
|
errorMessage = `${trulyUnresolvedThreads.length} review comments still need to be addressed (based on LLM analysis)`;
|
|
@@ -181,11 +205,12 @@ export async function verifyAndResolveComments(options) {
|
|
|
181
205
|
data: {
|
|
182
206
|
featureId,
|
|
183
207
|
totalReviews: reviews.length,
|
|
184
|
-
unresolvedReviews:
|
|
208
|
+
unresolvedReviews: remainingReviewsCount,
|
|
185
209
|
totalComments: unresolvedThreads.length, // Original count before LLM analysis
|
|
186
210
|
resolvedComments: addressedThreads.length, // LLM determined these are addressed
|
|
187
211
|
unresolvedComments: trulyUnresolvedThreads.length, // LLM determined these still need work
|
|
188
212
|
commentsMarkedResolved: addressedThreads.length,
|
|
213
|
+
dismissedReviews: dismissedReviewsCount,
|
|
189
214
|
suggestions,
|
|
190
215
|
unresolvedReviewDetails,
|
|
191
216
|
unresolvedCommentDetails,
|