edsger 0.34.0 → 0.35.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.
- package/dist/api/features/approval-checker.js +6 -8
- package/dist/commands/agent-workflow/chat-worker.js +2 -1
- package/dist/commands/agent-workflow/processor.js +3 -1
- package/dist/commands/code-review/index.js +5 -7
- package/dist/commands/workflow/phase-orchestrator.js +16 -32
- package/dist/constants.js +2 -0
- package/dist/index.js +3 -1
- package/dist/phases/app-store-generation/__tests__/agent.test.js +1 -3
- package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.js +2 -4
- package/dist/phases/app-store-generation/agent.js +1 -3
- package/dist/phases/app-store-generation/index.js +4 -3
- package/dist/phases/app-store-generation/screenshot-composer.js +3 -5
- package/dist/phases/autonomous/index.js +4 -3
- package/dist/phases/branch-planning/context.js +7 -7
- package/dist/phases/branch-planning/index.js +5 -3
- package/dist/phases/bug-fixing/analyzer.js +13 -13
- package/dist/phases/bug-fixing/context-fetcher.js +9 -9
- package/dist/phases/bug-fixing/mcp-server.js +14 -17
- package/dist/phases/code-implementation/context.js +5 -5
- package/dist/phases/code-implementation/index.js +5 -4
- package/dist/phases/code-implementation-verification/agent.js +3 -1
- package/dist/phases/code-refine/context.js +1 -1
- package/dist/phases/code-refine/index.js +4 -6
- package/dist/phases/code-refine/refine-iteration.js +10 -10
- package/dist/phases/code-refine-verification/index.js +80 -82
- package/dist/phases/code-review/index.js +8 -8
- package/dist/phases/code-testing/analyzer.js +3 -1
- package/dist/phases/code-testing/context-fetcher.js +5 -5
- package/dist/phases/feature-analysis/agent.js +1 -3
- package/dist/phases/feature-analysis/context.js +5 -5
- package/dist/phases/feature-analysis/index.js +4 -4
- package/dist/phases/feature-analysis/outcome.d.ts +1 -1
- package/dist/phases/feature-analysis/outcome.js +7 -7
- package/dist/phases/feature-analysis-verification/index.js +6 -6
- package/dist/phases/functional-testing/analyzer.js +4 -2
- package/dist/phases/functional-testing/context-fetcher.js +5 -5
- package/dist/phases/functional-testing/http-fallback.js +4 -2
- package/dist/phases/functional-testing/mcp-server.js +3 -3
- package/dist/phases/growth-analysis/agent.js +1 -3
- package/dist/phases/pr-execution/index.js +3 -1
- package/dist/phases/pr-splitting/context.js +6 -6
- package/dist/phases/pr-splitting/index.js +3 -1
- package/dist/phases/pull-request/handler.js +2 -4
- package/dist/phases/task/agent.js +6 -8
- package/dist/phases/technical-design/context.js +5 -5
- package/dist/phases/technical-design/index.js +9 -8
- package/dist/phases/test-cases-analysis/agent.js +1 -3
- package/dist/phases/test-cases-analysis/context.js +5 -5
- package/dist/phases/test-cases-analysis/index.js +3 -3
- package/dist/phases/test-cases-analysis/outcome.d.ts +1 -1
- package/dist/phases/test-cases-analysis/outcome.js +4 -4
- package/dist/phases/user-stories-analysis/agent.js +1 -3
- package/dist/phases/user-stories-analysis/context.js +5 -5
- package/dist/phases/user-stories-analysis/index.js +3 -3
- package/dist/phases/user-stories-analysis/outcome.d.ts +1 -1
- package/dist/phases/user-stories-analysis/outcome.js +4 -4
- package/dist/services/coaching/coaching-loop.js +1 -1
- package/dist/services/coaching/self-rating.js +1 -1
- package/dist/services/phase-ratings.js +2 -1
- package/dist/services/video/__tests__/video-pipeline.test.js +3 -1
- package/dist/services/video/retry.js +5 -2
- package/dist/services/video/screenshot-generator.js +1 -1
- package/dist/services/video/video-assembler.d.ts +1 -1
- package/dist/services/video/video-assembler.js +3 -3
- package/dist/utils/conflict-resolver.js +13 -15
- package/dist/utils/git-branch-manager-async.js +5 -7
- package/dist/utils/git-push.js +2 -2
- package/package.json +1 -1
|
@@ -17,7 +17,9 @@ function userMessage(content) {
|
|
|
17
17
|
}
|
|
18
18
|
async function* prompt(refinePrompt) {
|
|
19
19
|
yield userMessage(refinePrompt);
|
|
20
|
-
await new Promise((res) =>
|
|
20
|
+
await new Promise((res) => {
|
|
21
|
+
setTimeout(res, 10000);
|
|
22
|
+
});
|
|
21
23
|
}
|
|
22
24
|
// eslint-disable-next-line complexity -- agent loop with message processing and result extraction
|
|
23
25
|
export async function executeRefineIteration(opts) {
|
|
@@ -135,12 +137,10 @@ export async function executeRefineIteration(opts) {
|
|
|
135
137
|
execution_session_id: executionSessionId || undefined,
|
|
136
138
|
};
|
|
137
139
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
};
|
|
143
|
-
}
|
|
140
|
+
return {
|
|
141
|
+
status: 'error',
|
|
142
|
+
message: 'Code refine failed or incomplete',
|
|
143
|
+
};
|
|
144
144
|
}
|
|
145
145
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
146
146
|
function parseCodeRefineResponse(response) {
|
|
@@ -149,16 +149,16 @@ function parseCodeRefineResponse(response) {
|
|
|
149
149
|
? summaryMatch[1].trim()
|
|
150
150
|
: 'Code refined based on PR feedback';
|
|
151
151
|
const filesMatch = response.match(/## Files Modified\n([\s\S]*?)(?=\n##|\n\n|$)/);
|
|
152
|
-
let
|
|
152
|
+
let filesModified = [];
|
|
153
153
|
if (filesMatch) {
|
|
154
|
-
|
|
154
|
+
filesModified = filesMatch[1]
|
|
155
155
|
.split('\n')
|
|
156
156
|
.filter((line) => line.trim().startsWith('-'))
|
|
157
157
|
.map((line) => line.replace(/^-\s*/, '').trim());
|
|
158
158
|
}
|
|
159
159
|
return {
|
|
160
160
|
summary,
|
|
161
|
-
files_modified,
|
|
161
|
+
files_modified: filesModified,
|
|
162
162
|
commits_created: 1,
|
|
163
163
|
feedback_addressed: [],
|
|
164
164
|
};
|
|
@@ -147,97 +147,95 @@ export async function verifyAndResolveComments(options) {
|
|
|
147
147
|
},
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (verbose) {
|
|
155
|
-
if (remainingReviewsCount > 0) {
|
|
156
|
-
logInfo(`⚠️ ${remainingReviewsCount} reviews still requesting changes (failed to dismiss)`);
|
|
157
|
-
changesRequestedReviews.forEach((review) => {
|
|
158
|
-
logInfo(` - Review ${review.id} by @${review.user.login}`);
|
|
159
|
-
if (review.body) {
|
|
160
|
-
logInfo(` ${review.body.substring(0, 100)}...`);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
if (trulyUnresolvedThreads.length > 0) {
|
|
165
|
-
logInfo(`⚠️ ${trulyUnresolvedThreads.length} review threads still need to be addressed`);
|
|
166
|
-
trulyUnresolvedThreads.forEach((result) => {
|
|
167
|
-
const firstComment = result.thread.comments.nodes[0];
|
|
168
|
-
if (firstComment) {
|
|
169
|
-
logInfo(` - Comment by @${firstComment.author.login}`);
|
|
170
|
-
logInfo(` File: ${firstComment.path}:${firstComment.line || '?'}`);
|
|
171
|
-
logInfo(` ${firstComment.body.substring(0, 100)}...`);
|
|
172
|
-
logInfo(` LLM Analysis: ${result.analysis.reason}`);
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
// Build targeted suggestions based on LLM analysis
|
|
178
|
-
const suggestions = [];
|
|
179
|
-
// Create specific suggestions for each truly unresolved comment (based on LLM analysis)
|
|
180
|
-
trulyUnresolvedThreads.forEach((result, index) => {
|
|
181
|
-
const firstComment = result.thread.comments.nodes[0];
|
|
182
|
-
if (firstComment) {
|
|
183
|
-
suggestions.push(`${index + 1}. [${firstComment.path}:${firstComment.line || '?'}] by @${firstComment.author.login}: ${result.analysis.reason}`);
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
// Add review-specific suggestions if any reviews failed to dismiss
|
|
150
|
+
// Verification failed - build detailed info with specific failure reasons from LLM analysis
|
|
151
|
+
// Calculate remaining reviews that weren't dismissed
|
|
152
|
+
const remainingReviewsCount = changesRequestedReviews.length - dismissedReviewsCount;
|
|
153
|
+
if (verbose) {
|
|
187
154
|
if (remainingReviewsCount > 0) {
|
|
188
|
-
|
|
155
|
+
logInfo(`⚠️ ${remainingReviewsCount} reviews still requesting changes (failed to dismiss)`);
|
|
189
156
|
changesRequestedReviews.forEach((review) => {
|
|
190
|
-
|
|
157
|
+
logInfo(` - Review ${review.id} by @${review.user.login}`);
|
|
158
|
+
if (review.body) {
|
|
159
|
+
logInfo(` ${review.body.substring(0, 100)}...`);
|
|
160
|
+
}
|
|
191
161
|
});
|
|
192
162
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
};
|
|
205
|
-
});
|
|
206
|
-
const unresolvedReviewDetails = changesRequestedReviews.map((review) => ({
|
|
207
|
-
reviewId: review.id,
|
|
208
|
-
author: review.user.login,
|
|
209
|
-
state: review.state,
|
|
210
|
-
body: review.body,
|
|
211
|
-
submittedAt: review.submitted_at,
|
|
212
|
-
}));
|
|
213
|
-
let errorMessage = '';
|
|
214
|
-
if (remainingReviewsCount > 0 && trulyUnresolvedThreads.length > 0) {
|
|
215
|
-
errorMessage = `${remainingReviewsCount} reviews and ${trulyUnresolvedThreads.length} review threads still need to be addressed (based on LLM analysis)`;
|
|
216
|
-
}
|
|
217
|
-
else if (remainingReviewsCount > 0) {
|
|
218
|
-
errorMessage = `${remainingReviewsCount} reviews could not be dismissed`;
|
|
163
|
+
if (trulyUnresolvedThreads.length > 0) {
|
|
164
|
+
logInfo(`⚠️ ${trulyUnresolvedThreads.length} review threads still need to be addressed`);
|
|
165
|
+
trulyUnresolvedThreads.forEach((result) => {
|
|
166
|
+
const firstComment = result.thread.comments.nodes[0];
|
|
167
|
+
if (firstComment) {
|
|
168
|
+
logInfo(` - Comment by @${firstComment.author.login}`);
|
|
169
|
+
logInfo(` File: ${firstComment.path}:${firstComment.line || '?'}`);
|
|
170
|
+
logInfo(` ${firstComment.body.substring(0, 100)}...`);
|
|
171
|
+
logInfo(` LLM Analysis: ${result.analysis.reason}`);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
219
174
|
}
|
|
220
|
-
|
|
221
|
-
|
|
175
|
+
}
|
|
176
|
+
// Build targeted suggestions based on LLM analysis
|
|
177
|
+
const suggestions = [];
|
|
178
|
+
// Create specific suggestions for each truly unresolved comment (based on LLM analysis)
|
|
179
|
+
trulyUnresolvedThreads.forEach((result, index) => {
|
|
180
|
+
const firstComment = result.thread.comments.nodes[0];
|
|
181
|
+
if (firstComment) {
|
|
182
|
+
suggestions.push(`${index + 1}. [${firstComment.path}:${firstComment.line || '?'}] by @${firstComment.author.login}: ${result.analysis.reason}`);
|
|
222
183
|
}
|
|
184
|
+
});
|
|
185
|
+
// Add review-specific suggestions if any reviews failed to dismiss
|
|
186
|
+
if (remainingReviewsCount > 0) {
|
|
187
|
+
suggestions.push(`\n${remainingReviewsCount} review(s) requesting changes could not be automatically dismissed:`);
|
|
188
|
+
changesRequestedReviews.forEach((review) => {
|
|
189
|
+
suggestions.push(` - @${review.user.login}: ${review.body ? review.body.substring(0, 150) : 'No details provided'}${review.body && review.body.length > 150 ? '...' : ''}`);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// Build detailed unresolved info with LLM-analyzed failure reasons
|
|
193
|
+
const unresolvedCommentDetails = trulyUnresolvedThreads.map((result) => {
|
|
194
|
+
const firstComment = result.thread.comments.nodes[0];
|
|
223
195
|
return {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
resolvedComments: addressedThreads.length, // LLM determined these are addressed
|
|
232
|
-
unresolvedComments: trulyUnresolvedThreads.length, // LLM determined these still need work
|
|
233
|
-
commentsMarkedResolved: addressedThreads.length,
|
|
234
|
-
dismissedReviews: dismissedReviewsCount,
|
|
235
|
-
suggestions,
|
|
236
|
-
unresolvedReviewDetails,
|
|
237
|
-
unresolvedCommentDetails,
|
|
238
|
-
},
|
|
196
|
+
commentId: firstComment.id,
|
|
197
|
+
author: firstComment.author.login,
|
|
198
|
+
file: firstComment.path,
|
|
199
|
+
line: firstComment.line,
|
|
200
|
+
body: firstComment.body,
|
|
201
|
+
failureReason: result.analysis.reason, // Use LLM analysis result
|
|
202
|
+
url: firstComment.url,
|
|
239
203
|
};
|
|
204
|
+
});
|
|
205
|
+
const unresolvedReviewDetails = changesRequestedReviews.map((review) => ({
|
|
206
|
+
reviewId: review.id,
|
|
207
|
+
author: review.user.login,
|
|
208
|
+
state: review.state,
|
|
209
|
+
body: review.body,
|
|
210
|
+
submittedAt: review.submitted_at,
|
|
211
|
+
}));
|
|
212
|
+
let errorMessage = '';
|
|
213
|
+
if (remainingReviewsCount > 0 && trulyUnresolvedThreads.length > 0) {
|
|
214
|
+
errorMessage = `${remainingReviewsCount} reviews and ${trulyUnresolvedThreads.length} review threads still need to be addressed (based on LLM analysis)`;
|
|
215
|
+
}
|
|
216
|
+
else if (remainingReviewsCount > 0) {
|
|
217
|
+
errorMessage = `${remainingReviewsCount} reviews could not be dismissed`;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
errorMessage = `${trulyUnresolvedThreads.length} review comments still need to be addressed (based on LLM analysis)`;
|
|
240
221
|
}
|
|
222
|
+
return {
|
|
223
|
+
status: 'error',
|
|
224
|
+
message: errorMessage,
|
|
225
|
+
data: {
|
|
226
|
+
featureId,
|
|
227
|
+
totalReviews: reviews.length,
|
|
228
|
+
unresolvedReviews: remainingReviewsCount,
|
|
229
|
+
totalComments: unresolvedThreads.length, // Original count before LLM analysis
|
|
230
|
+
resolvedComments: addressedThreads.length, // LLM determined these are addressed
|
|
231
|
+
unresolvedComments: trulyUnresolvedThreads.length, // LLM determined these still need work
|
|
232
|
+
commentsMarkedResolved: addressedThreads.length,
|
|
233
|
+
dismissedReviews: dismissedReviewsCount,
|
|
234
|
+
suggestions,
|
|
235
|
+
unresolvedReviewDetails,
|
|
236
|
+
unresolvedCommentDetails,
|
|
237
|
+
},
|
|
238
|
+
};
|
|
241
239
|
}
|
|
242
240
|
catch (error) {
|
|
243
241
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -21,7 +21,9 @@ function userMessage(content) {
|
|
|
21
21
|
}
|
|
22
22
|
async function* prompt(reviewPrompt) {
|
|
23
23
|
yield userMessage(reviewPrompt);
|
|
24
|
-
await new Promise((res) =>
|
|
24
|
+
await new Promise((res) => {
|
|
25
|
+
setTimeout(res, 10000);
|
|
26
|
+
});
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
27
29
|
* Parse unified diff patch to build a mapping from file line numbers to diff positions
|
|
@@ -561,13 +563,11 @@ export const reviewPullRequest = async (options, config, checklistContext
|
|
|
561
563
|
},
|
|
562
564
|
};
|
|
563
565
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
};
|
|
570
|
-
}
|
|
566
|
+
return {
|
|
567
|
+
featureId,
|
|
568
|
+
status: 'error',
|
|
569
|
+
message: 'Code review analysis failed or incomplete',
|
|
570
|
+
};
|
|
571
571
|
}
|
|
572
572
|
catch (error) {
|
|
573
573
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -27,7 +27,9 @@ function userMessage(content) {
|
|
|
27
27
|
}
|
|
28
28
|
async function* prompt(testingPrompt) {
|
|
29
29
|
yield userMessage(testingPrompt);
|
|
30
|
-
await new Promise((res) =>
|
|
30
|
+
await new Promise((res) => {
|
|
31
|
+
setTimeout(res, 10000);
|
|
32
|
+
});
|
|
31
33
|
}
|
|
32
34
|
export const writeCodeTests = async (options, config
|
|
33
35
|
// eslint-disable-next-line complexity -- orchestration function with context fetching, agent execution, and test result processing
|
|
@@ -10,7 +10,7 @@ export async function fetchCodeTestingContext(featureId, verbose) {
|
|
|
10
10
|
logInfo(`Fetching complete code testing context for feature: ${featureId}`);
|
|
11
11
|
}
|
|
12
12
|
// Fetch all required data in parallel for better performance
|
|
13
|
-
const [feature,
|
|
13
|
+
const [feature, userStories, testCases] = await Promise.all([
|
|
14
14
|
getFeature(featureId, verbose),
|
|
15
15
|
getUserStories(featureId, verbose),
|
|
16
16
|
getTestCases(featureId, verbose),
|
|
@@ -20,14 +20,14 @@ export async function fetchCodeTestingContext(featureId, verbose) {
|
|
|
20
20
|
logInfo(`✅ Code testing context fetched successfully:`);
|
|
21
21
|
logInfo(` Feature: ${feature.name}`);
|
|
22
22
|
logInfo(` Product: ${product.name}`);
|
|
23
|
-
logInfo(` User Stories: ${
|
|
24
|
-
logInfo(` Test Cases: ${
|
|
23
|
+
logInfo(` User Stories: ${userStories.length}`);
|
|
24
|
+
logInfo(` Test Cases: ${testCases.length}`);
|
|
25
25
|
}
|
|
26
26
|
return {
|
|
27
27
|
feature,
|
|
28
28
|
product,
|
|
29
|
-
user_stories,
|
|
30
|
-
test_cases,
|
|
29
|
+
user_stories: userStories,
|
|
30
|
+
test_cases: testCases,
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
catch (error) {
|
|
@@ -20,9 +20,7 @@ export function parseAnalysisResult(responseText) {
|
|
|
20
20
|
if (jsonResult && jsonResult.analysis) {
|
|
21
21
|
return { analysis: jsonResult.analysis };
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
return { error: 'Invalid JSON structure' };
|
|
25
|
-
}
|
|
23
|
+
return { error: 'Invalid JSON structure' };
|
|
26
24
|
}
|
|
27
25
|
catch (error) {
|
|
28
26
|
return {
|
|
@@ -14,7 +14,7 @@ export async function fetchFeatureAnalysisContext(featureId, verbose) {
|
|
|
14
14
|
logInfo(`Fetching complete feature analysis context for feature: ${featureId}`);
|
|
15
15
|
}
|
|
16
16
|
// Fetch all required data in parallel for better performance
|
|
17
|
-
const [feature,
|
|
17
|
+
const [feature, existingUserStories, existingTestCases] = await Promise.all([
|
|
18
18
|
getFeature(featureId, verbose),
|
|
19
19
|
getUserStories(featureId, verbose),
|
|
20
20
|
getTestCases(featureId, verbose),
|
|
@@ -24,14 +24,14 @@ export async function fetchFeatureAnalysisContext(featureId, verbose) {
|
|
|
24
24
|
logInfo(`✅ Feature analysis context fetched successfully:`);
|
|
25
25
|
logInfo(` Feature: ${feature.name}`);
|
|
26
26
|
logInfo(` Product: ${product.name}`);
|
|
27
|
-
logInfo(` Existing User Stories: ${
|
|
28
|
-
logInfo(` Existing Test Cases: ${
|
|
27
|
+
logInfo(` Existing User Stories: ${existingUserStories.length}`);
|
|
28
|
+
logInfo(` Existing Test Cases: ${existingTestCases.length}`);
|
|
29
29
|
}
|
|
30
30
|
return {
|
|
31
31
|
feature,
|
|
32
32
|
product,
|
|
33
|
-
existing_user_stories,
|
|
34
|
-
existing_test_cases,
|
|
33
|
+
existing_user_stories: existingUserStories,
|
|
34
|
+
existing_test_cases: existingTestCases,
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
catch (error) {
|
|
@@ -99,9 +99,9 @@ export const analyseFeature = async (options, config, checklistContext
|
|
|
99
99
|
if (parsed.analysis) {
|
|
100
100
|
// Delete old drafts and save improved ones
|
|
101
101
|
await deleteArtifacts(currentDraftUserStoryIds, currentDraftTestCaseIds, verbose);
|
|
102
|
-
const { userStoryIds, testCaseIds } = await saveAnalysisArtifactsAsDraft(featureId, parsed.analysis.created_user_stories || [], parsed.analysis.created_test_cases || [], verbose);
|
|
103
|
-
currentDraftUserStoryIds =
|
|
104
|
-
currentDraftTestCaseIds =
|
|
102
|
+
const { userStoryIds: newUserStoryIds, testCaseIds: newTestCaseIds, } = await saveAnalysisArtifactsAsDraft(featureId, parsed.analysis.created_user_stories || [], parsed.analysis.created_test_cases || [], verbose);
|
|
103
|
+
currentDraftUserStoryIds = newUserStoryIds;
|
|
104
|
+
currentDraftTestCaseIds = newTestCaseIds;
|
|
105
105
|
structuredAnalysisResult = parsed.analysis;
|
|
106
106
|
}
|
|
107
107
|
else {
|
|
@@ -121,7 +121,7 @@ export const analyseFeature = async (options, config, checklistContext
|
|
|
121
121
|
featureId,
|
|
122
122
|
verbose,
|
|
123
123
|
});
|
|
124
|
-
verificationResult = verificationCycle
|
|
124
|
+
({ verificationResult } = verificationCycle);
|
|
125
125
|
// If verification passed, update ALL remaining draft artifacts to pending_approval and exit
|
|
126
126
|
if (verificationCycle.passed) {
|
|
127
127
|
if (verbose) {
|
|
@@ -30,7 +30,7 @@ export declare function updateArtifactsToReady(userStoryIds: string[], testCaseI
|
|
|
30
30
|
/**
|
|
31
31
|
* Save analysis artifacts as draft and return their IDs
|
|
32
32
|
*/
|
|
33
|
-
export declare function saveAnalysisArtifactsAsDraft(featureId: string,
|
|
33
|
+
export declare function saveAnalysisArtifactsAsDraft(featureId: string, createdUserStories: Record<string, unknown>[], createdTestCases: Record<string, unknown>[], verbose?: boolean): Promise<{
|
|
34
34
|
userStoryIds: string[];
|
|
35
35
|
testCaseIds: string[];
|
|
36
36
|
}>;
|
|
@@ -163,18 +163,18 @@ export async function updateArtifactsToReady(userStoryIds, testCaseIds, verbose)
|
|
|
163
163
|
/**
|
|
164
164
|
* Save analysis artifacts as draft and return their IDs
|
|
165
165
|
*/
|
|
166
|
-
export async function saveAnalysisArtifactsAsDraft(featureId,
|
|
166
|
+
export async function saveAnalysisArtifactsAsDraft(featureId, createdUserStories, createdTestCases, verbose) {
|
|
167
167
|
const userStoryIds = [];
|
|
168
168
|
const testCaseIds = [];
|
|
169
169
|
// Save user stories as draft
|
|
170
|
-
if (
|
|
170
|
+
if (createdUserStories && createdUserStories.length > 0) {
|
|
171
171
|
if (verbose) {
|
|
172
|
-
logInfo(`Saving ${
|
|
172
|
+
logInfo(`Saving ${createdUserStories.length} user stories as draft...`);
|
|
173
173
|
}
|
|
174
174
|
try {
|
|
175
175
|
const result = (await callMcpEndpoint('user_stories/create', {
|
|
176
176
|
feature_id: featureId,
|
|
177
|
-
user_stories:
|
|
177
|
+
user_stories: createdUserStories.map((story) => ({
|
|
178
178
|
title: story.title,
|
|
179
179
|
description: story.description,
|
|
180
180
|
status: 'draft',
|
|
@@ -192,14 +192,14 @@ export async function saveAnalysisArtifactsAsDraft(featureId, created_user_stori
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
// Save test cases as draft
|
|
195
|
-
if (
|
|
195
|
+
if (createdTestCases && createdTestCases.length > 0) {
|
|
196
196
|
if (verbose) {
|
|
197
|
-
logInfo(`Saving ${
|
|
197
|
+
logInfo(`Saving ${createdTestCases.length} test cases as draft...`);
|
|
198
198
|
}
|
|
199
199
|
try {
|
|
200
200
|
const result = (await callMcpEndpoint('test_cases/create', {
|
|
201
201
|
feature_id: featureId,
|
|
202
|
-
test_cases:
|
|
202
|
+
test_cases: createdTestCases.map((testCase) => ({
|
|
203
203
|
name: testCase.name,
|
|
204
204
|
description: testCase.description,
|
|
205
205
|
is_critical: testCase.is_critical || false,
|
|
@@ -8,9 +8,9 @@ export async function performVerificationCycle(opts) {
|
|
|
8
8
|
if (!checklistContext || checklistContext.checklists.length === 0) {
|
|
9
9
|
return { passed: true, verificationResult: null };
|
|
10
10
|
}
|
|
11
|
-
const
|
|
11
|
+
const createdUserStories = (structuredAnalysisResult.created_user_stories ||
|
|
12
12
|
[]);
|
|
13
|
-
const
|
|
13
|
+
const createdTestCases = (structuredAnalysisResult.created_test_cases ||
|
|
14
14
|
[]);
|
|
15
15
|
if (verbose) {
|
|
16
16
|
logInfo('🔍 Starting checklist verification...');
|
|
@@ -18,8 +18,8 @@ export async function performVerificationCycle(opts) {
|
|
|
18
18
|
const verificationResult = await verifyChecklistCompliance({
|
|
19
19
|
checklistContext,
|
|
20
20
|
analysisContext: context,
|
|
21
|
-
createdUserStories:
|
|
22
|
-
createdTestCases:
|
|
21
|
+
createdUserStories: createdUserStories || [],
|
|
22
|
+
createdTestCases: createdTestCases || [],
|
|
23
23
|
verbose,
|
|
24
24
|
}, config);
|
|
25
25
|
// Verification passed
|
|
@@ -81,8 +81,8 @@ export async function performVerificationCycle(opts) {
|
|
|
81
81
|
}
|
|
82
82
|
// Create improvement prompt for next iteration
|
|
83
83
|
const nextPrompt = createImprovementPrompt(verificationResult, {
|
|
84
|
-
created_user_stories:
|
|
85
|
-
created_test_cases:
|
|
84
|
+
created_user_stories: createdUserStories || [],
|
|
85
|
+
created_test_cases: createdTestCases || [],
|
|
86
86
|
});
|
|
87
87
|
return { passed: false, verificationResult, nextPrompt };
|
|
88
88
|
}
|
|
@@ -33,7 +33,9 @@ function userMessage(content) {
|
|
|
33
33
|
}
|
|
34
34
|
async function* prompt(testingPrompt) {
|
|
35
35
|
yield userMessage(testingPrompt);
|
|
36
|
-
await new Promise((res) =>
|
|
36
|
+
await new Promise((res) => {
|
|
37
|
+
setTimeout(res, 10000);
|
|
38
|
+
});
|
|
37
39
|
}
|
|
38
40
|
/**
|
|
39
41
|
* Extract JSON from a response using multiple strategies:
|
|
@@ -377,7 +379,7 @@ export const runFunctionalTesting = async (options, config, checklistContext
|
|
|
377
379
|
const parsed = parseTestResult(responseText);
|
|
378
380
|
if (parsed) {
|
|
379
381
|
structuredTestResult = parsed.testResult;
|
|
380
|
-
testStatus = parsed
|
|
382
|
+
({ testStatus } = parsed);
|
|
381
383
|
}
|
|
382
384
|
else {
|
|
383
385
|
structuredTestResult = {
|
|
@@ -10,7 +10,7 @@ export async function fetchFunctionalTestingContext(featureId, verbose) {
|
|
|
10
10
|
logInfo(`Fetching complete functional testing context for feature: ${featureId}`);
|
|
11
11
|
}
|
|
12
12
|
// Fetch all required data in parallel for better performance
|
|
13
|
-
const [feature,
|
|
13
|
+
const [feature, userStories, testCases] = await Promise.all([
|
|
14
14
|
getFeature(featureId, verbose),
|
|
15
15
|
getUserStories(featureId, verbose),
|
|
16
16
|
getTestCases(featureId, verbose),
|
|
@@ -20,14 +20,14 @@ export async function fetchFunctionalTestingContext(featureId, verbose) {
|
|
|
20
20
|
logInfo(`✅ Functional testing context fetched successfully:`);
|
|
21
21
|
logInfo(` Feature: ${feature.name}`);
|
|
22
22
|
logInfo(` Product: ${product.name}`);
|
|
23
|
-
logInfo(` User Stories: ${
|
|
24
|
-
logInfo(` Test Cases: ${
|
|
23
|
+
logInfo(` User Stories: ${userStories.length}`);
|
|
24
|
+
logInfo(` Test Cases: ${testCases.length}`);
|
|
25
25
|
}
|
|
26
26
|
return {
|
|
27
27
|
feature,
|
|
28
28
|
product,
|
|
29
|
-
user_stories,
|
|
30
|
-
test_cases,
|
|
29
|
+
user_stories: userStories,
|
|
30
|
+
test_cases: testCases,
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
catch (error) {
|
|
@@ -119,7 +119,7 @@ export async function saveFunctionalTestResultsWithRetry(options, maxRetries = 3
|
|
|
119
119
|
}
|
|
120
120
|
return true;
|
|
121
121
|
}
|
|
122
|
-
|
|
122
|
+
if (verbose) {
|
|
123
123
|
logError(`❌ Save appeared successful but verification failed (attempt ${attempt})`);
|
|
124
124
|
}
|
|
125
125
|
}
|
|
@@ -128,7 +128,9 @@ export async function saveFunctionalTestResultsWithRetry(options, maxRetries = 3
|
|
|
128
128
|
if (verbose) {
|
|
129
129
|
logInfo(`⏳ Waiting ${delay}ms before retry...`);
|
|
130
130
|
}
|
|
131
|
-
await new Promise((resolve) =>
|
|
131
|
+
await new Promise((resolve) => {
|
|
132
|
+
setTimeout(resolve, delay);
|
|
133
|
+
});
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
if (verbose) {
|
|
@@ -108,7 +108,7 @@ export const createFunctionalTestingMcpServer = () => {
|
|
|
108
108
|
.optional()
|
|
109
109
|
.default('testing')
|
|
110
110
|
.describe('Environment name (testing, staging, production)'),
|
|
111
|
-
},
|
|
111
|
+
}, (args) => {
|
|
112
112
|
// Get environment variables for testing configuration
|
|
113
113
|
const testingConfig = {
|
|
114
114
|
environment: args.environment || 'testing',
|
|
@@ -119,14 +119,14 @@ export const createFunctionalTestingMcpServer = () => {
|
|
|
119
119
|
if (!testingConfig.login_username || !testingConfig.login_password) {
|
|
120
120
|
throw new Error('Testing credentials not configured. Set TESTING_LOGIN_USERNAME and TESTING_LOGIN_PASSWORD environment variables.');
|
|
121
121
|
}
|
|
122
|
-
return {
|
|
122
|
+
return Promise.resolve({
|
|
123
123
|
content: [
|
|
124
124
|
{
|
|
125
125
|
type: 'text',
|
|
126
126
|
text: JSON.stringify(testingConfig, null, 2),
|
|
127
127
|
},
|
|
128
128
|
],
|
|
129
|
-
};
|
|
129
|
+
});
|
|
130
130
|
}),
|
|
131
131
|
],
|
|
132
132
|
});
|
|
@@ -20,9 +20,7 @@ function parseGrowthResult(responseText) {
|
|
|
20
20
|
if (jsonResult && jsonResult.analysis) {
|
|
21
21
|
return { analysis: jsonResult.analysis };
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
return { error: 'Invalid JSON structure' };
|
|
25
|
-
}
|
|
23
|
+
return { error: 'Invalid JSON structure' };
|
|
26
24
|
}
|
|
27
25
|
catch (error) {
|
|
28
26
|
return {
|
|
@@ -16,7 +16,9 @@ function userMessage(content) {
|
|
|
16
16
|
}
|
|
17
17
|
async function* prompt(executionPrompt) {
|
|
18
18
|
yield userMessage(executionPrompt);
|
|
19
|
-
await new Promise((res) =>
|
|
19
|
+
await new Promise((res) => {
|
|
20
|
+
setTimeout(res, 10000);
|
|
21
|
+
});
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
24
|
* PR Execution Phase: Create git branches, push code, and generate compare URLs
|
|
@@ -81,7 +81,7 @@ export async function fetchPRSplittingContext(featureId, verbose, replaceExistin
|
|
|
81
81
|
}
|
|
82
82
|
const devBranchName = getDevBranchName(featureId);
|
|
83
83
|
// Fetch database data and GitHub config in parallel (need token before remote branch check)
|
|
84
|
-
const [feature,
|
|
84
|
+
const [feature, existingBranches, existingPullRequests, githubConfig] = await Promise.all([
|
|
85
85
|
getFeature(featureId, verbose),
|
|
86
86
|
getBranches({ featureId, verbose }).catch(() => []),
|
|
87
87
|
getPullRequests({ featureId, verbose }).catch(() => []),
|
|
@@ -128,7 +128,7 @@ export async function fetchPRSplittingContext(featureId, verbose, replaceExistin
|
|
|
128
128
|
}
|
|
129
129
|
// Determine diff range
|
|
130
130
|
const devRef = localExists ? devBranchName : `origin/${devBranchName}`;
|
|
131
|
-
const baseRef = determineDiffBaseRef(
|
|
131
|
+
const baseRef = determineDiffBaseRef(existingPullRequests, replaceExisting);
|
|
132
132
|
const devBranchHeadSha = getBranchHeadSha(devRef);
|
|
133
133
|
// Check if there are new changes since last sync
|
|
134
134
|
if (baseRef !== 'main' && baseRef === devBranchHeadSha) {
|
|
@@ -147,15 +147,15 @@ export async function fetchPRSplittingContext(featureId, verbose, replaceExistin
|
|
|
147
147
|
logInfo(` Dev Branch HEAD: ${devBranchHeadSha}`);
|
|
148
148
|
logInfo(` Diff Base: ${baseRef}`);
|
|
149
149
|
logInfo(` Changed Files: ${changedFiles.length}`);
|
|
150
|
-
logInfo(` Existing Branches: ${
|
|
151
|
-
logInfo(` Existing Pull Requests: ${
|
|
150
|
+
logInfo(` Existing Branches: ${existingBranches.length}`);
|
|
151
|
+
logInfo(` Existing Pull Requests: ${existingPullRequests.length}`);
|
|
152
152
|
logInfo(` GitHub: ${githubConfig.configured ? 'Configured' : 'Not configured'}`);
|
|
153
153
|
}
|
|
154
154
|
return {
|
|
155
155
|
feature,
|
|
156
156
|
product,
|
|
157
|
-
existing_branches,
|
|
158
|
-
existing_pull_requests,
|
|
157
|
+
existing_branches: existingBranches,
|
|
158
|
+
existing_pull_requests: existingPullRequests,
|
|
159
159
|
diffStat,
|
|
160
160
|
changedFiles,
|
|
161
161
|
devBranchName,
|
|
@@ -15,7 +15,9 @@ function userMessage(content) {
|
|
|
15
15
|
}
|
|
16
16
|
async function* prompt(analysisPrompt) {
|
|
17
17
|
yield userMessage(analysisPrompt);
|
|
18
|
-
await new Promise((res) =>
|
|
18
|
+
await new Promise((res) => {
|
|
19
|
+
setTimeout(res, 10000);
|
|
20
|
+
});
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
21
23
|
* PR Splitting Phase: Analyze diff and create PR split plan
|
|
@@ -85,8 +85,6 @@ export async function handlePullRequestCreation({ featureId, results, testingRes
|
|
|
85
85
|
logDebug(`Pull request created${currentBranch ? ' and branch updated' : ''}`, verbose);
|
|
86
86
|
return true;
|
|
87
87
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
88
|
+
logDebug(`Pull request creation failed: ${prResult.error}`, verbose);
|
|
89
|
+
return false;
|
|
92
90
|
}
|