edsger 0.4.2 → 0.4.3
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Context fetcher for code refine phase
|
|
3
|
-
* Fetches GitHub PR review comments
|
|
3
|
+
* Fetches GitHub PR review comments and reviews using GraphQL API
|
|
4
4
|
*/
|
|
5
5
|
import { Octokit } from '@octokit/rest';
|
|
6
6
|
export interface PRReviewComment {
|
|
@@ -16,6 +16,7 @@ export interface PRReviewComment {
|
|
|
16
16
|
original_position: number | null;
|
|
17
17
|
diff_hunk: string | null;
|
|
18
18
|
in_reply_to_id?: number;
|
|
19
|
+
url?: string;
|
|
19
20
|
}
|
|
20
21
|
export interface PRReview {
|
|
21
22
|
id: number;
|
|
@@ -26,6 +27,17 @@ export interface PRReview {
|
|
|
26
27
|
state: string;
|
|
27
28
|
submitted_at: string | null;
|
|
28
29
|
}
|
|
30
|
+
export interface PRReviewThread {
|
|
31
|
+
id: string;
|
|
32
|
+
isResolved: boolean;
|
|
33
|
+
isOutdated: boolean;
|
|
34
|
+
line: number | null;
|
|
35
|
+
originalLine: number | null;
|
|
36
|
+
startLine: number | null;
|
|
37
|
+
originalStartLine: number | null;
|
|
38
|
+
diffSide: string;
|
|
39
|
+
comments: PRReviewComment[];
|
|
40
|
+
}
|
|
29
41
|
export interface CodeRefineContext {
|
|
30
42
|
featureId: string;
|
|
31
43
|
featureName: string;
|
|
@@ -36,6 +48,7 @@ export interface CodeRefineContext {
|
|
|
36
48
|
repo: string;
|
|
37
49
|
reviews: PRReview[];
|
|
38
50
|
reviewComments: PRReviewComment[];
|
|
51
|
+
reviewThreads?: PRReviewThread[];
|
|
39
52
|
technicalDesign?: string;
|
|
40
53
|
userStories?: any[];
|
|
41
54
|
testCases?: any[];
|
|
@@ -56,6 +69,13 @@ export declare function fetchPRReviews(octokit: Octokit, owner: string, repo: st
|
|
|
56
69
|
* Fetch PR review comments
|
|
57
70
|
*/
|
|
58
71
|
export declare function fetchPRReviewComments(octokit: Octokit, owner: string, repo: string, prNumber: number, verbose?: boolean): Promise<PRReviewComment[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Fetch PR data using GraphQL API including review threads and reviews
|
|
74
|
+
*/
|
|
75
|
+
export declare function fetchPRDataGraphQL(octokit: Octokit, owner: string, repo: string, prNumber: number, verbose?: boolean): Promise<{
|
|
76
|
+
reviewThreads: PRReviewThread[];
|
|
77
|
+
reviews: PRReview[];
|
|
78
|
+
}>;
|
|
59
79
|
/**
|
|
60
80
|
* Fetch user stories via MCP
|
|
61
81
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Context fetcher for code refine phase
|
|
3
|
-
* Fetches GitHub PR review comments
|
|
3
|
+
* Fetches GitHub PR review comments and reviews using GraphQL API
|
|
4
4
|
*/
|
|
5
5
|
import { Octokit } from '@octokit/rest';
|
|
6
6
|
import { getFeature } from '../../api/features/get-feature.js';
|
|
@@ -54,6 +54,114 @@ export async function fetchPRReviewComments(octokit, owner, repo, prNumber, verb
|
|
|
54
54
|
}
|
|
55
55
|
return comments;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Fetch PR data using GraphQL API including review threads and reviews
|
|
59
|
+
*/
|
|
60
|
+
export async function fetchPRDataGraphQL(octokit, owner, repo, prNumber, verbose) {
|
|
61
|
+
if (verbose) {
|
|
62
|
+
console.log(`🔍 Fetching PR data via GraphQL for ${owner}/${repo}#${prNumber}...`);
|
|
63
|
+
}
|
|
64
|
+
const query = `
|
|
65
|
+
query($owner: String!, $repo: String!, $prNumber: Int!) {
|
|
66
|
+
repository(owner: $owner, name: $repo) {
|
|
67
|
+
pullRequest(number: $prNumber) {
|
|
68
|
+
reviews(first: 100) {
|
|
69
|
+
nodes {
|
|
70
|
+
id
|
|
71
|
+
databaseId
|
|
72
|
+
author {
|
|
73
|
+
login
|
|
74
|
+
}
|
|
75
|
+
body
|
|
76
|
+
state
|
|
77
|
+
submittedAt
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
reviewThreads(first: 100) {
|
|
81
|
+
nodes {
|
|
82
|
+
id
|
|
83
|
+
isResolved
|
|
84
|
+
isOutdated
|
|
85
|
+
line
|
|
86
|
+
originalLine
|
|
87
|
+
startLine
|
|
88
|
+
originalStartLine
|
|
89
|
+
diffSide
|
|
90
|
+
comments(first: 100) {
|
|
91
|
+
nodes {
|
|
92
|
+
id
|
|
93
|
+
databaseId
|
|
94
|
+
body
|
|
95
|
+
path
|
|
96
|
+
originalPosition
|
|
97
|
+
diffHunk
|
|
98
|
+
createdAt
|
|
99
|
+
url
|
|
100
|
+
author {
|
|
101
|
+
login
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
const result = await octokit.graphql(query, {
|
|
112
|
+
owner,
|
|
113
|
+
repo,
|
|
114
|
+
prNumber,
|
|
115
|
+
});
|
|
116
|
+
const pullRequest = result.repository.pullRequest;
|
|
117
|
+
// Transform GraphQL reviews to our interface
|
|
118
|
+
const reviews = (pullRequest.reviews.nodes || []).map((review) => ({
|
|
119
|
+
id: review.databaseId,
|
|
120
|
+
user: {
|
|
121
|
+
login: review.author?.login || 'unknown',
|
|
122
|
+
},
|
|
123
|
+
body: review.body,
|
|
124
|
+
state: review.state,
|
|
125
|
+
submitted_at: review.submittedAt,
|
|
126
|
+
}));
|
|
127
|
+
// Transform GraphQL review threads to our interface
|
|
128
|
+
const reviewThreads = (pullRequest.reviewThreads.nodes || []).map((thread) => ({
|
|
129
|
+
id: thread.id,
|
|
130
|
+
isResolved: thread.isResolved,
|
|
131
|
+
isOutdated: thread.isOutdated,
|
|
132
|
+
line: thread.line,
|
|
133
|
+
originalLine: thread.originalLine,
|
|
134
|
+
startLine: thread.startLine,
|
|
135
|
+
originalStartLine: thread.originalStartLine,
|
|
136
|
+
diffSide: thread.diffSide,
|
|
137
|
+
comments: (thread.comments.nodes || []).map((comment) => ({
|
|
138
|
+
id: comment.databaseId,
|
|
139
|
+
body: comment.body,
|
|
140
|
+
path: comment.path,
|
|
141
|
+
line: thread.line, // Use thread's line for consistency
|
|
142
|
+
user: {
|
|
143
|
+
login: comment.author?.login || 'unknown',
|
|
144
|
+
},
|
|
145
|
+
created_at: comment.createdAt,
|
|
146
|
+
position: comment.originalPosition,
|
|
147
|
+
original_position: comment.originalPosition,
|
|
148
|
+
diff_hunk: comment.diffHunk,
|
|
149
|
+
url: comment.url,
|
|
150
|
+
})),
|
|
151
|
+
}));
|
|
152
|
+
// Filter for reviews requesting changes
|
|
153
|
+
const changesRequestedReviews = reviews.filter((review) => review.state === 'CHANGES_REQUESTED');
|
|
154
|
+
// Filter for unresolved threads
|
|
155
|
+
const unresolvedThreads = reviewThreads.filter((thread) => !thread.isResolved);
|
|
156
|
+
if (verbose) {
|
|
157
|
+
console.log(`✅ Found ${changesRequestedReviews.length} reviews requesting changes`);
|
|
158
|
+
console.log(`✅ Found ${unresolvedThreads.length} unresolved review threads`);
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
reviewThreads: unresolvedThreads,
|
|
162
|
+
reviews: changesRequestedReviews,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
57
165
|
/**
|
|
58
166
|
* Fetch user stories via MCP
|
|
59
167
|
*/
|
|
@@ -163,13 +271,17 @@ export async function fetchCodeRefineContext(mcpServerUrl, mcpToken, featureId,
|
|
|
163
271
|
const octokit = new Octokit({
|
|
164
272
|
auth: githubToken,
|
|
165
273
|
});
|
|
166
|
-
// Fetch PR
|
|
167
|
-
const [
|
|
168
|
-
|
|
169
|
-
fetchPRReviewComments(octokit, owner, repo, prNumber, verbose),
|
|
274
|
+
// Fetch PR data using GraphQL API and additional context
|
|
275
|
+
const [prData, userStories, testCases] = await Promise.all([
|
|
276
|
+
fetchPRDataGraphQL(octokit, owner, repo, prNumber, verbose),
|
|
170
277
|
fetchUserStories(mcpServerUrl, mcpToken, featureId, verbose),
|
|
171
278
|
fetchTestCases(mcpServerUrl, mcpToken, featureId, verbose),
|
|
172
279
|
]);
|
|
280
|
+
// Extract review comments from unresolved threads
|
|
281
|
+
const reviewComments = prData.reviewThreads.flatMap((thread) => thread.comments);
|
|
282
|
+
if (verbose) {
|
|
283
|
+
console.log(`📊 Summary: ${prData.reviews.length} reviews requesting changes, ${prData.reviewThreads.length} unresolved threads, ${reviewComments.length} total comments`);
|
|
284
|
+
}
|
|
173
285
|
return {
|
|
174
286
|
featureId,
|
|
175
287
|
featureName: feature.name,
|
|
@@ -178,8 +290,9 @@ export async function fetchCodeRefineContext(mcpServerUrl, mcpToken, featureId,
|
|
|
178
290
|
pullRequestNumber: prNumber,
|
|
179
291
|
owner,
|
|
180
292
|
repo,
|
|
181
|
-
reviews,
|
|
293
|
+
reviews: prData.reviews,
|
|
182
294
|
reviewComments,
|
|
295
|
+
reviewThreads: prData.reviewThreads,
|
|
183
296
|
technicalDesign: feature.technical_design,
|
|
184
297
|
userStories,
|
|
185
298
|
testCases,
|
|
@@ -213,8 +326,45 @@ export function formatContextForPrompt(context) {
|
|
|
213
326
|
sections.push('');
|
|
214
327
|
});
|
|
215
328
|
}
|
|
216
|
-
// Review
|
|
217
|
-
if (context.
|
|
329
|
+
// Review threads (unresolved conversations)
|
|
330
|
+
if (context.reviewThreads && context.reviewThreads.length > 0) {
|
|
331
|
+
sections.push(`## Unresolved Review Threads`);
|
|
332
|
+
sections.push(`*These conversations must be resolved before the PR can be merged.*`);
|
|
333
|
+
sections.push('');
|
|
334
|
+
context.reviewThreads.forEach((thread, idx) => {
|
|
335
|
+
sections.push(`### Thread ${idx + 1} ${thread.isOutdated ? '(Outdated)' : ''}`);
|
|
336
|
+
// Show thread location
|
|
337
|
+
if (thread.line !== null) {
|
|
338
|
+
sections.push(`**Line**: ${thread.line}`);
|
|
339
|
+
}
|
|
340
|
+
if (thread.startLine !== null && thread.startLine !== thread.line) {
|
|
341
|
+
sections.push(`**Line Range**: ${thread.startLine} - ${thread.line || thread.originalLine}`);
|
|
342
|
+
}
|
|
343
|
+
sections.push(`**Status**: ❌ Unresolved`);
|
|
344
|
+
sections.push('');
|
|
345
|
+
// Show all comments in this thread
|
|
346
|
+
thread.comments.forEach((comment, commentIdx) => {
|
|
347
|
+
sections.push(`#### ${commentIdx === 0 ? 'Original Comment' : `Reply ${commentIdx}`} by @${comment.user.login}`);
|
|
348
|
+
if (comment.path) {
|
|
349
|
+
sections.push(`**File**: ${comment.path}`);
|
|
350
|
+
}
|
|
351
|
+
if (comment.url) {
|
|
352
|
+
sections.push(`**URL**: ${comment.url}`);
|
|
353
|
+
}
|
|
354
|
+
if (commentIdx === 0 && comment.diff_hunk) {
|
|
355
|
+
sections.push(`**Context**:`);
|
|
356
|
+
sections.push('```diff');
|
|
357
|
+
sections.push(comment.diff_hunk);
|
|
358
|
+
sections.push('```');
|
|
359
|
+
}
|
|
360
|
+
sections.push(`**Comment**:`);
|
|
361
|
+
sections.push(comment.body);
|
|
362
|
+
sections.push('');
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
else if (context.reviewComments.length > 0) {
|
|
367
|
+
// Fallback to old format if no thread data available
|
|
218
368
|
sections.push(`## Review Comments`);
|
|
219
369
|
sections.push('');
|
|
220
370
|
context.reviewComments.forEach((comment, idx) => {
|