edsger 0.3.2 → 0.4.0

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.
@@ -56,10 +56,6 @@ export declare function fetchPRReviews(octokit: Octokit, owner: string, repo: st
56
56
  * Fetch PR review comments
57
57
  */
58
58
  export declare function fetchPRReviewComments(octokit: Octokit, owner: string, repo: string, prNumber: number, verbose?: boolean): Promise<PRReviewComment[]>;
59
- /**
60
- * Fetch technical design via MCP
61
- */
62
- export declare function fetchTechnicalDesign(mcpServerUrl: string, mcpToken: string, featureId: string, verbose?: boolean): Promise<string | undefined>;
63
59
  /**
64
60
  * Fetch user stories via MCP
65
61
  */
@@ -54,51 +54,6 @@ export async function fetchPRReviewComments(octokit, owner, repo, prNumber, verb
54
54
  }
55
55
  return comments;
56
56
  }
57
- /**
58
- * Fetch technical design via MCP
59
- */
60
- export async function fetchTechnicalDesign(mcpServerUrl, mcpToken, featureId, verbose) {
61
- try {
62
- if (verbose) {
63
- console.log(`📐 Fetching technical design for ${featureId}...`);
64
- }
65
- const response = await fetch(`${mcpServerUrl}/mcp`, {
66
- method: 'POST',
67
- headers: {
68
- 'Content-Type': 'application/json',
69
- Authorization: `Bearer ${mcpToken}`,
70
- },
71
- body: JSON.stringify({
72
- jsonrpc: '2.0',
73
- id: 1,
74
- method: 'technical-design/get',
75
- params: {
76
- feature_id: featureId,
77
- },
78
- }),
79
- });
80
- if (!response.ok) {
81
- if (verbose) {
82
- console.log(`⚠️ Could not fetch technical design: ${response.status}`);
83
- }
84
- return undefined;
85
- }
86
- const data = await response.json();
87
- if (data.error || !data.result) {
88
- if (verbose) {
89
- console.log(`⚠️ Technical design not available`);
90
- }
91
- return undefined;
92
- }
93
- return data.result.technical_design;
94
- }
95
- catch (error) {
96
- if (verbose) {
97
- console.log(`⚠️ Error fetching technical design: ${error}`);
98
- }
99
- return undefined;
100
- }
101
- }
102
57
  /**
103
58
  * Fetch user stories via MCP
104
59
  */
@@ -116,7 +71,7 @@ export async function fetchUserStories(mcpServerUrl, mcpToken, featureId, verbos
116
71
  body: JSON.stringify({
117
72
  jsonrpc: '2.0',
118
73
  id: 1,
119
- method: 'user-stories/list',
74
+ method: 'user_stories/list',
120
75
  params: {
121
76
  feature_id: featureId,
122
77
  },
@@ -161,7 +116,7 @@ export async function fetchTestCases(mcpServerUrl, mcpToken, featureId, verbose)
161
116
  body: JSON.stringify({
162
117
  jsonrpc: '2.0',
163
118
  id: 1,
164
- method: 'test-cases/list',
119
+ method: 'test_cases/list',
165
120
  params: {
166
121
  feature_id: featureId,
167
122
  },
@@ -209,10 +164,9 @@ export async function fetchCodeRefineContext(mcpServerUrl, mcpToken, featureId,
209
164
  auth: githubToken,
210
165
  });
211
166
  // Fetch PR reviews and comments
212
- const [reviews, reviewComments, technicalDesign, userStories, testCases] = await Promise.all([
167
+ const [reviews, reviewComments, userStories, testCases] = await Promise.all([
213
168
  fetchPRReviews(octokit, owner, repo, prNumber, verbose),
214
169
  fetchPRReviewComments(octokit, owner, repo, prNumber, verbose),
215
- fetchTechnicalDesign(mcpServerUrl, mcpToken, featureId, verbose),
216
170
  fetchUserStories(mcpServerUrl, mcpToken, featureId, verbose),
217
171
  fetchTestCases(mcpServerUrl, mcpToken, featureId, verbose),
218
172
  ]);
@@ -226,7 +180,7 @@ export async function fetchCodeRefineContext(mcpServerUrl, mcpToken, featureId,
226
180
  repo,
227
181
  reviews,
228
182
  reviewComments,
229
- technicalDesign,
183
+ technicalDesign: feature.technical_design,
230
184
  userStories,
231
185
  testCases,
232
186
  };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Code refine retry handler for pipeline execution
3
+ * Handles verification failures and retries code refine with suggestions
4
+ */
5
+ import { EdsgerConfig } from '../../types/index.js';
6
+ import { PipelinePhaseOptions, PipelineResult } from '../../types/pipeline.js';
7
+ export declare const MAX_REFINE_ATTEMPTS = 10;
8
+ export interface CodeRefineRetryOptions {
9
+ options: PipelinePhaseOptions;
10
+ config: EdsgerConfig;
11
+ results: PipelineResult[];
12
+ verbose?: boolean;
13
+ }
14
+ /**
15
+ * Handle code refine verification failures with automatic retry
16
+ */
17
+ export declare function handleCodeRefineWithRetry({ options, config, results, verbose, }: CodeRefineRetryOptions): Promise<PipelineResult>;
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Code refine retry handler for pipeline execution
3
+ * Handles verification failures and retries code refine with suggestions
4
+ */
5
+ import { logPhaseResult } from '../../utils/pipeline-logger.js';
6
+ import { runCodeRefinePhase, runCodeRefineVerificationPhase, } from '../../workflow-runner/executors/phase-executor.js';
7
+ import { updateFeatureStatusForPhase } from '../../api/features/index.js';
8
+ import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
9
+ export const MAX_REFINE_ATTEMPTS = 10;
10
+ /**
11
+ * Handle code refine verification failures with automatic retry
12
+ */
13
+ export async function handleCodeRefineWithRetry({ options, config, results, verbose, }) {
14
+ let verificationResult;
15
+ let refineAttempt = 0;
16
+ do {
17
+ // 1. Run code refine phase
18
+ const codeRefineResult = await runCodeRefinePhase(options, config);
19
+ results.push(logPhaseResult(codeRefineResult, verbose));
20
+ // If code refine failed, stop immediately
21
+ if (codeRefineResult.status === 'error') {
22
+ if (verbose) {
23
+ console.log(`❌ Code refine failed: ${codeRefineResult.message}. Cannot proceed to verification.`);
24
+ }
25
+ return codeRefineResult;
26
+ }
27
+ // 2. Run code refine verification
28
+ verificationResult = await runCodeRefineVerificationPhase(options, config);
29
+ results.push(logPhaseResult(verificationResult, verbose));
30
+ // If verification passed, we're done
31
+ if (verificationResult.status === 'success') {
32
+ if (verbose) {
33
+ console.log(`✅ Code refine verification passed. All PR comments have been addressed.`);
34
+ }
35
+ break;
36
+ }
37
+ // Verification failed - check if we should retry
38
+ if (refineAttempt < MAX_REFINE_ATTEMPTS) {
39
+ refineAttempt++;
40
+ if (verbose) {
41
+ console.log(`⚠️ Code refine verification failed. Retrying... (Attempt ${refineAttempt}/${MAX_REFINE_ATTEMPTS})`);
42
+ }
43
+ // Extract verification failure details
44
+ const verificationData = verificationResult.data;
45
+ const unresolvedComments = verificationData?.unresolvedComments ||
46
+ verificationData?.unresolved_comments ||
47
+ 0;
48
+ const suggestions = verificationData?.suggestions || [];
49
+ const unresolvedDetails = verificationData?.unresolvedCommentDetails ||
50
+ verificationData?.unresolved_comment_details ||
51
+ [];
52
+ // Log audit event for verification failure with suggestions
53
+ await logFeaturePhaseEvent(options.mcpServerUrl, options.mcpToken, {
54
+ featureId: options.featureId,
55
+ eventType: 'phase_failed',
56
+ phase: 'code_refine_verification',
57
+ result: 'error',
58
+ metadata: {
59
+ attempt: refineAttempt,
60
+ unresolved_comments: unresolvedComments,
61
+ suggestions: suggestions,
62
+ unresolved_comment_details: unresolvedDetails,
63
+ will_retry: true,
64
+ },
65
+ errorMessage: verificationResult.message,
66
+ }, verbose);
67
+ if (verbose && suggestions.length > 0) {
68
+ console.log(`\n💡 Suggestions for next iteration:`);
69
+ suggestions.forEach((suggestion) => {
70
+ console.log(` ${suggestion}`);
71
+ });
72
+ console.log('');
73
+ }
74
+ // Remove the failed verification result - we'll add the new one after retry
75
+ results.pop();
76
+ // Update status to indicate we're retrying code refine
77
+ await updateFeatureStatusForPhase({ mcpServerUrl: options.mcpServerUrl, mcpToken: options.mcpToken }, options.featureId, 'code-refine', verbose);
78
+ // Also remove the code refine result so it can be retried
79
+ results.pop();
80
+ }
81
+ else {
82
+ // Max attempts reached
83
+ if (verbose) {
84
+ console.log(`⚠️ Maximum refine attempts (${MAX_REFINE_ATTEMPTS}) reached. Verification still failing.`);
85
+ }
86
+ // Log final failure with all details
87
+ const verificationData = verificationResult.data;
88
+ const suggestions = verificationData?.suggestions || [];
89
+ const unresolvedDetails = verificationData?.unresolvedCommentDetails ||
90
+ verificationData?.unresolved_comment_details ||
91
+ [];
92
+ await logFeaturePhaseEvent(options.mcpServerUrl, options.mcpToken, {
93
+ featureId: options.featureId,
94
+ eventType: 'phase_failed',
95
+ phase: 'code_refine_verification',
96
+ result: 'error',
97
+ metadata: {
98
+ attempt: refineAttempt,
99
+ max_attempts_reached: true,
100
+ suggestions: suggestions,
101
+ unresolved_comment_details: unresolvedDetails,
102
+ },
103
+ errorMessage: `Max attempts reached: ${verificationResult.message}`,
104
+ }, verbose);
105
+ break;
106
+ }
107
+ } while (refineAttempt <= MAX_REFINE_ATTEMPTS);
108
+ return verificationResult;
109
+ }
@@ -9,14 +9,34 @@ export interface CodeRefineVerificationOptions {
9
9
  githubToken: string;
10
10
  verbose?: boolean;
11
11
  }
12
- export interface CodeRefineVerificationResult {
12
+ export interface CodeRefineVerificationData {
13
13
  featureId: string;
14
- status: 'success' | 'error';
15
- message: string;
14
+ totalReviews: number;
15
+ unresolvedReviews: number;
16
16
  totalComments: number;
17
17
  resolvedComments: number;
18
18
  unresolvedComments: number;
19
19
  commentsMarkedResolved?: number;
20
+ suggestions?: string[];
21
+ unresolvedReviewDetails?: Array<{
22
+ reviewId: number;
23
+ author: string;
24
+ state: string;
25
+ body: string | null;
26
+ submittedAt: string | null;
27
+ }>;
28
+ unresolvedCommentDetails?: Array<{
29
+ commentId: number;
30
+ author: string;
31
+ file: string;
32
+ line: number | null;
33
+ body: string;
34
+ }>;
35
+ }
36
+ export interface CodeRefineVerificationResult {
37
+ status: 'success' | 'error';
38
+ message: string;
39
+ data: CodeRefineVerificationData;
20
40
  }
21
41
  /**
22
42
  * Verify and resolve PR review comments
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { Octokit } from '@octokit/rest';
6
6
  import { logInfo, logError } from '../../utils/logger.js';
7
- import { parsePullRequestUrl, fetchPRReviewComments, } from '../code-refine/context-fetcher.js';
7
+ import { parsePullRequestUrl, fetchPRReviews, fetchPRReviewComments, } from '../code-refine/context-fetcher.js';
8
8
  import { getFeature } from '../../api/features/get-feature.js';
9
9
  /**
10
10
  * Check if a review comment thread is resolved
@@ -107,36 +107,55 @@ export async function verifyAndResolveComments(options) {
107
107
  const octokit = new Octokit({
108
108
  auth: githubToken,
109
109
  });
110
- // Fetch all review comments
111
- const reviewComments = await fetchPRReviewComments(octokit, owner, repo, prNumber, verbose);
112
- if (reviewComments.length === 0) {
110
+ // Fetch all reviews requesting changes and review comments
111
+ const [reviews, reviewComments] = await Promise.all([
112
+ fetchPRReviews(octokit, owner, repo, prNumber, verbose),
113
+ fetchPRReviewComments(octokit, owner, repo, prNumber, verbose),
114
+ ]);
115
+ if (reviews.length === 0 && reviewComments.length === 0) {
113
116
  if (verbose) {
114
- logInfo('✅ No review comments found. Verification complete.');
117
+ logInfo('✅ No reviews or review comments found. Verification complete.');
115
118
  }
116
119
  return {
117
- featureId,
118
120
  status: 'success',
119
- message: 'No review comments to verify',
120
- totalComments: 0,
121
- resolvedComments: 0,
122
- unresolvedComments: 0,
121
+ message: 'No reviews or review comments to verify',
122
+ data: {
123
+ featureId,
124
+ totalReviews: 0,
125
+ unresolvedReviews: 0,
126
+ totalComments: 0,
127
+ resolvedComments: 0,
128
+ unresolvedComments: 0,
129
+ },
123
130
  };
124
131
  }
125
132
  // Check which comments are resolved
126
133
  if (verbose) {
127
- logInfo(`🔍 Checking ${reviewComments.length} review comments...`);
134
+ if (reviews.length > 0) {
135
+ logInfo(`🔍 Checking ${reviews.length} reviews requesting changes...`);
136
+ }
137
+ if (reviewComments.length > 0) {
138
+ logInfo(`🔍 Checking ${reviewComments.length} review comments...`);
139
+ }
128
140
  }
141
+ // Check review comments resolution status
129
142
  const commentStatus = await Promise.all(reviewComments.map(async (comment) => {
130
143
  const isResolved = await isCommentThreadResolved(octokit, owner, repo, prNumber, comment.id, verbose);
131
144
  return { comment, isResolved };
132
145
  }));
133
146
  const resolvedComments = commentStatus.filter((c) => c.isResolved);
134
147
  const unresolvedComments = commentStatus.filter((c) => !c.isResolved);
148
+ // Check reviews - they need to be dismissed or re-reviewed
149
+ // We consider reviews as "unresolved" if they are still in CHANGES_REQUESTED state
150
+ const unresolvedReviews = reviews.filter((review) => review.state === 'CHANGES_REQUESTED');
135
151
  if (verbose) {
136
- logInfo(`📊 Status: ${resolvedComments.length} resolved, ${unresolvedComments.length} unresolved`);
152
+ logInfo(`📊 Comments: ${resolvedComments.length} resolved, ${unresolvedComments.length} unresolved`);
153
+ if (reviews.length > 0) {
154
+ logInfo(`📊 Reviews: ${reviews.length - unresolvedReviews.length} addressed, ${unresolvedReviews.length} still requesting changes`);
155
+ }
137
156
  }
138
- // If all comments are resolved, mark them as resolved on GitHub
139
- if (unresolvedComments.length === 0) {
157
+ // If all comments are resolved AND no reviews requesting changes, success
158
+ if (unresolvedComments.length === 0 && unresolvedReviews.length === 0) {
140
159
  if (verbose) {
141
160
  logInfo('✅ All comments have been addressed! Marking them as resolved...');
142
161
  }
@@ -150,34 +169,118 @@ export async function verifyAndResolveComments(options) {
150
169
  if (verbose) {
151
170
  logInfo(`✅ Marked ${markedCount} comments as resolved on GitHub`);
152
171
  }
172
+ const successMessage = reviews.length > 0
173
+ ? 'All reviews and review comments have been addressed and resolved'
174
+ : 'All review comments have been addressed and resolved';
153
175
  return {
154
- featureId,
155
176
  status: 'success',
156
- message: 'All review comments have been addressed and resolved',
157
- totalComments: reviewComments.length,
158
- resolvedComments: resolvedComments.length,
159
- unresolvedComments: 0,
160
- commentsMarkedResolved: markedCount,
177
+ message: successMessage,
178
+ data: {
179
+ featureId,
180
+ totalReviews: reviews.length,
181
+ unresolvedReviews: 0,
182
+ totalComments: reviewComments.length,
183
+ resolvedComments: resolvedComments.length,
184
+ unresolvedComments: 0,
185
+ commentsMarkedResolved: markedCount,
186
+ },
161
187
  };
162
188
  }
163
189
  else {
164
190
  if (verbose) {
165
- logInfo(`⚠️ ${unresolvedComments.length} comments still need to be addressed`);
191
+ if (unresolvedReviews.length > 0) {
192
+ logInfo(`⚠️ ${unresolvedReviews.length} reviews still requesting changes`);
193
+ unresolvedReviews.forEach((review) => {
194
+ logInfo(` - Review ${review.id} by @${review.user.login}`);
195
+ if (review.body) {
196
+ logInfo(` ${review.body.substring(0, 100)}...`);
197
+ }
198
+ });
199
+ }
200
+ if (unresolvedComments.length > 0) {
201
+ logInfo(`⚠️ ${unresolvedComments.length} comments still need to be addressed`);
202
+ unresolvedComments.forEach(({ comment }) => {
203
+ logInfo(` - Comment ${comment.id} by @${comment.user.login}`);
204
+ if (comment.path) {
205
+ logInfo(` File: ${comment.path}:${comment.line || '?'}`);
206
+ }
207
+ logInfo(` ${comment.body.substring(0, 100)}...`);
208
+ });
209
+ }
210
+ }
211
+ // Build suggestions for code-refine phase
212
+ const suggestions = [];
213
+ // Add reviews information
214
+ if (unresolvedReviews.length > 0) {
215
+ suggestions.push(`Found ${unresolvedReviews.length} reviews still requesting changes:`);
216
+ unresolvedReviews.forEach((review) => {
217
+ suggestions.push(`- Review by @${review.user.login}${review.body ? `: ${review.body}` : ''}`);
218
+ });
219
+ suggestions.push('');
220
+ }
221
+ // Add comments information
222
+ if (unresolvedComments.length > 0) {
223
+ suggestions.push(`Found ${unresolvedComments.length} unresolved PR review comments:`);
166
224
  unresolvedComments.forEach(({ comment }) => {
167
- logInfo(` - Comment ${comment.id} by @${comment.user.login}`);
168
- if (comment.path) {
169
- logInfo(` File: ${comment.path}:${comment.line || '?'}`);
170
- }
171
- logInfo(` ${comment.body.substring(0, 100)}...`);
225
+ const location = comment.path
226
+ ? `${comment.path}${comment.line ? `:${comment.line}` : ''}`
227
+ : 'General comment';
228
+ suggestions.push(`- [${location}] ${comment.user.login}: ${comment.body}`);
172
229
  });
230
+ suggestions.push('');
231
+ }
232
+ suggestions.push('Suggestions for next code-refine iteration:');
233
+ if (unresolvedReviews.length > 0) {
234
+ suggestions.push('1. Address the overall concerns raised in the reviews requesting changes');
235
+ suggestions.push('2. Make significant improvements to satisfy the reviewers');
236
+ }
237
+ if (unresolvedComments.length > 0) {
238
+ suggestions.push(`${unresolvedReviews.length > 0 ? '3' : '1'}. Carefully read each unresolved comment above`);
239
+ suggestions.push(`${unresolvedReviews.length > 0 ? '4' : '2'}. Locate the specific file and line mentioned in each comment`);
240
+ suggestions.push(`${unresolvedReviews.length > 0 ? '5' : '3'}. Make the requested changes to address the reviewer feedback`);
241
+ }
242
+ suggestions.push(`${unresolvedReviews.length > 0 || unresolvedComments.length > 0 ? (unresolvedReviews.length > 0 ? (unresolvedComments.length > 0 ? '6' : '3') : '4') : '1'}. Ensure your changes fully resolve the concerns raised`);
243
+ suggestions.push(`${unresolvedReviews.length > 0 || unresolvedComments.length > 0 ? (unresolvedReviews.length > 0 ? (unresolvedComments.length > 0 ? '7' : '4') : '5') : '2'}. Test your changes before committing`);
244
+ // Build detailed unresolved info
245
+ const unresolvedCommentDetails = unresolvedComments.map(({ comment }) => ({
246
+ commentId: comment.id,
247
+ author: comment.user.login,
248
+ file: comment.path || 'unknown',
249
+ line: comment.line,
250
+ body: comment.body,
251
+ }));
252
+ const unresolvedReviewDetails = unresolvedReviews.map((review) => ({
253
+ reviewId: review.id,
254
+ author: review.user.login,
255
+ state: review.state,
256
+ body: review.body,
257
+ submittedAt: review.submitted_at,
258
+ }));
259
+ const totalUnresolved = unresolvedReviews.length + unresolvedComments.length;
260
+ let errorMessage = '';
261
+ if (unresolvedReviews.length > 0 && unresolvedComments.length > 0) {
262
+ errorMessage = `${unresolvedReviews.length} reviews and ${unresolvedComments.length} comments still need to be addressed`;
263
+ }
264
+ else if (unresolvedReviews.length > 0) {
265
+ errorMessage = `${unresolvedReviews.length} reviews still requesting changes`;
266
+ }
267
+ else {
268
+ errorMessage = `${unresolvedComments.length} review comments still need to be addressed`;
173
269
  }
174
270
  return {
175
- featureId,
176
271
  status: 'error',
177
- message: `${unresolvedComments.length} review comments still need to be addressed`,
178
- totalComments: reviewComments.length,
179
- resolvedComments: resolvedComments.length,
180
- unresolvedComments: unresolvedComments.length,
272
+ message: errorMessage,
273
+ data: {
274
+ featureId,
275
+ totalReviews: reviews.length,
276
+ unresolvedReviews: unresolvedReviews.length,
277
+ totalComments: reviewComments.length,
278
+ resolvedComments: resolvedComments.length,
279
+ unresolvedComments: unresolvedComments.length,
280
+ suggestions,
281
+ unresolvedReviewDetails,
282
+ unresolvedCommentDetails,
283
+ },
181
284
  };
182
285
  }
183
286
  }
@@ -185,12 +288,20 @@ export async function verifyAndResolveComments(options) {
185
288
  const errorMessage = error instanceof Error ? error.message : String(error);
186
289
  logError(`Code refine verification failed: ${errorMessage}`);
187
290
  return {
188
- featureId,
189
291
  status: 'error',
190
292
  message: `Verification failed: ${errorMessage}`,
191
- totalComments: 0,
192
- resolvedComments: 0,
193
- unresolvedComments: 0,
293
+ data: {
294
+ featureId,
295
+ totalReviews: 0,
296
+ unresolvedReviews: 0,
297
+ totalComments: 0,
298
+ resolvedComments: 0,
299
+ unresolvedComments: 0,
300
+ suggestions: [
301
+ `Verification failed with error: ${errorMessage}`,
302
+ 'Please check the error message and try again',
303
+ ],
304
+ },
194
305
  };
195
306
  }
196
307
  }
@@ -173,15 +173,36 @@ export const createPhaseRunner = (phaseConfig) => async (options, config) => {
173
173
  }, verbose);
174
174
  }
175
175
  else {
176
+ // Extract additional failure details from result data
177
+ const failureMetadata = {
178
+ duration_ms: phaseDuration,
179
+ };
180
+ // For verification phases, include suggestions and details
181
+ if (name.includes('verification') &&
182
+ result.data &&
183
+ typeof result.data === 'object') {
184
+ const data = result.data;
185
+ if (data.suggestions) {
186
+ failureMetadata.suggestions = data.suggestions;
187
+ }
188
+ if (data.unresolvedCommentDetails ||
189
+ data.unresolved_comment_details) {
190
+ failureMetadata.unresolved_comment_details =
191
+ data.unresolvedCommentDetails || data.unresolved_comment_details;
192
+ }
193
+ if (data.unresolvedComments !== undefined ||
194
+ data.unresolved_comments !== undefined) {
195
+ failureMetadata.unresolved_comments =
196
+ data.unresolvedComments ?? data.unresolved_comments;
197
+ }
198
+ }
176
199
  await logFeaturePhaseEvent(mcpServerUrl, mcpToken, {
177
200
  featureId,
178
201
  eventType: 'phase_failed',
179
202
  phase: name.replace(/-/g, '_'),
180
203
  result: 'error',
181
- metadata: {
182
- duration_ms: phaseDuration,
183
- },
184
- errorMessage: result.summary || 'Phase execution failed',
204
+ metadata: failureMetadata,
205
+ errorMessage: result.summary || result.message || 'Phase execution failed',
185
206
  }, verbose);
186
207
  }
187
208
  return {
@@ -4,9 +4,10 @@
4
4
  * Uses functional programming principles
5
5
  */
6
6
  import { updateFeatureStatusForPhase } from '../api/features/index.js';
7
- import { runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, } from './executors/phase-executor.js';
7
+ import { runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, } from './executors/phase-executor.js';
8
8
  import { logPipelineStart, logPhaseResult, logPipelineComplete, shouldContinuePipeline, } from '../utils/pipeline-logger.js';
9
9
  import { handleTestFailuresWithRetry } from '../phases/functional-testing/test-retry-handler.js';
10
+ import { handleCodeRefineWithRetry } from '../phases/code-refine/retry-handler.js';
10
11
  import { handlePullRequestCreation } from '../phases/pull-request/handler.js';
11
12
  /**
12
13
  * Run pipeline based on execution mode
@@ -267,19 +268,18 @@ const runFromFunctionalTesting = async (options, config) => {
267
268
  };
268
269
  /**
269
270
  * Run only code refine phase (refine code based on PR feedback)
271
+ * Includes automatic retry loop for verification failures
270
272
  */
271
273
  const runOnlyCodeRefine = async (options, config) => {
272
274
  const { featureId, verbose } = options;
273
275
  logPipelineStart(featureId, verbose);
274
276
  const results = [];
275
- // 1. Code Refine - Address PR review feedback
276
- const codeRefineResult = await runCodeRefinePhase(options, config);
277
- results.push(logPhaseResult(codeRefineResult, verbose));
278
- if (!shouldContinuePipeline(results)) {
279
- return logPipelineComplete(results, verbose);
280
- }
281
- // 2. Code Refine Verification - Verify all comments are addressed
282
- const verificationResult = await runCodeRefineVerificationPhase(options, config);
283
- results.push(logPhaseResult(verificationResult, verbose));
277
+ // Code Refine with automatic retry for verification failures
278
+ await handleCodeRefineWithRetry({
279
+ options,
280
+ config,
281
+ results,
282
+ verbose,
283
+ });
284
284
  return logPipelineComplete(results, verbose);
285
285
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edsger",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {