edsger 0.2.2 → 0.2.4
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 +17 -0
- package/dist/api/features/batch-operations.d.ts +16 -0
- package/dist/api/features/batch-operations.js +100 -0
- package/dist/api/features/index.d.ts +1 -0
- package/dist/api/features/index.js +1 -0
- package/dist/api/features/test-cases.d.ts +8 -0
- package/dist/api/features/test-cases.js +45 -0
- package/dist/api/features/user-stories.d.ts +8 -0
- package/dist/api/features/user-stories.js +45 -0
- package/dist/cli/commands/refactor-command.d.ts +2 -0
- package/dist/cli/commands/refactor-command.js +107 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +4 -99
- package/dist/phases/code-implementation/analyzer-helpers.d.ts +28 -0
- package/dist/phases/code-implementation/analyzer-helpers.js +177 -0
- package/dist/phases/code-implementation/analyzer.d.ts +2 -0
- package/dist/phases/code-implementation/analyzer.js +308 -179
- package/dist/phases/code-implementation-verification/index.d.ts +1 -0
- package/dist/phases/code-implementation-verification/index.js +1 -0
- package/dist/phases/code-implementation-verification/verifier.d.ts +31 -0
- package/dist/phases/code-implementation-verification/verifier.js +196 -0
- package/dist/phases/feature-analysis/analyzer-helpers.d.ts +62 -0
- package/dist/phases/feature-analysis/analyzer-helpers.js +450 -0
- package/dist/phases/feature-analysis/analyzer.d.ts +1 -0
- package/dist/phases/feature-analysis/analyzer.js +132 -213
- package/dist/phases/feature-analysis-verification/index.d.ts +1 -0
- package/dist/phases/feature-analysis-verification/index.js +1 -0
- package/dist/phases/feature-analysis-verification/verifier.d.ts +37 -0
- package/dist/phases/feature-analysis-verification/verifier.js +147 -0
- package/dist/phases/pull-request/creator.js +10 -9
- package/dist/phases/technical-design/analyzer-helpers.d.ts +37 -0
- package/dist/phases/technical-design/analyzer-helpers.js +144 -0
- package/dist/phases/technical-design/analyzer.d.ts +3 -0
- package/dist/phases/technical-design/analyzer.js +282 -312
- package/dist/phases/technical-design-verification/index.d.ts +1 -0
- package/dist/phases/technical-design-verification/index.js +1 -0
- package/dist/phases/technical-design-verification/verifier.d.ts +36 -0
- package/dist/phases/technical-design-verification/verifier.js +147 -0
- package/dist/prompts/checklist-verification.d.ts +11 -0
- package/dist/prompts/checklist-verification.js +153 -0
- package/dist/prompts/code-implementation-improvement.d.ts +5 -0
- package/dist/prompts/code-implementation-improvement.js +108 -0
- package/dist/prompts/code-implementation-verification.d.ts +3 -0
- package/dist/prompts/code-implementation-verification.js +176 -0
- package/dist/prompts/feature-analysis-improvement.d.ts +8 -0
- package/dist/prompts/feature-analysis-improvement.js +109 -0
- package/dist/prompts/feature-analysis.js +1 -1
- package/dist/prompts/formatters.d.ts +17 -4
- package/dist/prompts/formatters.js +41 -12
- package/dist/prompts/technical-design-improvement.d.ts +5 -0
- package/dist/prompts/technical-design-improvement.js +93 -0
- package/dist/prompts/technical-design-verification.d.ts +11 -0
- package/dist/prompts/technical-design-verification.js +134 -0
- package/dist/prompts/technical-design.js +1 -1
- package/dist/services/audit-logs.d.ts +60 -0
- package/dist/services/audit-logs.js +115 -0
- package/dist/services/checklist.d.ts +1 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/utils/image-downloader.d.ts +32 -0
- package/dist/utils/image-downloader.js +144 -0
- package/dist/workflow-runner/executors/phase-executor.js +56 -12
- package/package.json +1 -1
- package/dist/api/features.d.ts +0 -100
- package/dist/api/features.js +0 -219
- package/dist/logger.d.ts +0 -19
- package/dist/logger.js +0 -52
- package/dist/types.d.ts +0 -99
- package/dist/types.js +0 -1
- package/dist/workflow-runner/config/stage-configs.d.ts +0 -5
- package/dist/workflow-runner/config/stage-configs.js +0 -34
- package/dist/workflow-runner/core/feature-filter.test.d.ts +0 -4
- package/dist/workflow-runner/core/feature-filter.test.js +0 -127
- package/dist/workflow-runner/executors/stage-executor.d.ts +0 -8
- package/dist/workflow-runner/executors/stage-executor.js +0 -49
- package/dist/workflow-runner/feature-fetcher.d.ts +0 -41
- package/dist/workflow-runner/feature-fetcher.js +0 -121
- package/dist/workflow-runner/feature-service.d.ts +0 -17
- package/dist/workflow-runner/feature-service.js +0 -60
- package/dist/workflow-runner/pipeline.d.ts +0 -18
- package/dist/workflow-runner/pipeline.js +0 -197
- package/dist/workflow-runner/processor.d.ts +0 -40
- package/dist/workflow-runner/processor.js +0 -191
- package/dist/workflow-runner/status-updater.d.ts +0 -27
- package/dist/workflow-runner/status-updater.js +0 -80
- package/dist/workflow-runner/types.d.ts +0 -48
- package/dist/workflow-runner/types.js +0 -4
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for feature analysis
|
|
3
|
+
* Extracted from analyzer.ts to improve modularity and testability
|
|
4
|
+
*/
|
|
5
|
+
import { query } from '@anthropic-ai/claude-code';
|
|
6
|
+
import { logInfo, logError } from '../../utils/logger.js';
|
|
7
|
+
import { saveDataViaHttp } from './http-fallback.js';
|
|
8
|
+
import { createUserStories, createTestCases, batchUpdateUserStoryStatus, batchUpdateTestCaseStatus, batchDeleteUserStories, batchDeleteTestCases, } from '../../api/features/index.js';
|
|
9
|
+
import { callMcpEndpoint } from '../../api/mcp-client.js';
|
|
10
|
+
import { verifyChecklistCompliance, } from '../feature-analysis-verification/index.js';
|
|
11
|
+
import { createImprovementPrompt } from '../../prompts/feature-analysis-improvement.js';
|
|
12
|
+
import { logFeatureVerificationEvent } from '../../services/audit-logs.js';
|
|
13
|
+
function userMessage(content) {
|
|
14
|
+
return {
|
|
15
|
+
type: 'user',
|
|
16
|
+
message: { role: 'user', content: content },
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async function* prompt(analysisPrompt) {
|
|
20
|
+
yield userMessage(analysisPrompt);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Parse JSON result from Claude Code response
|
|
24
|
+
*/
|
|
25
|
+
export function parseAnalysisResult(responseText) {
|
|
26
|
+
try {
|
|
27
|
+
let jsonResult = null;
|
|
28
|
+
// First try to extract JSON from markdown code block
|
|
29
|
+
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
30
|
+
if (jsonBlockMatch) {
|
|
31
|
+
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Try to parse the entire response as JSON
|
|
35
|
+
jsonResult = JSON.parse(responseText);
|
|
36
|
+
}
|
|
37
|
+
if (jsonResult && jsonResult.analysis) {
|
|
38
|
+
return { analysis: jsonResult.analysis };
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return { error: 'Invalid JSON structure' };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
return {
|
|
46
|
+
error: `JSON parsing failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Execute a single analysis query
|
|
52
|
+
*/
|
|
53
|
+
export async function executeAnalysisQuery(currentPrompt, systemPrompt, config, verbose) {
|
|
54
|
+
let lastAssistantResponse = '';
|
|
55
|
+
let structuredAnalysisResult = null;
|
|
56
|
+
for await (const message of query({
|
|
57
|
+
prompt: prompt(currentPrompt),
|
|
58
|
+
options: {
|
|
59
|
+
appendSystemPrompt: systemPrompt,
|
|
60
|
+
model: config.claude.model || 'sonnet',
|
|
61
|
+
maxTurns: 1000,
|
|
62
|
+
permissionMode: 'bypassPermissions',
|
|
63
|
+
},
|
|
64
|
+
})) {
|
|
65
|
+
if (verbose) {
|
|
66
|
+
logInfo(`Received message type: ${message.type}`);
|
|
67
|
+
}
|
|
68
|
+
// Stream the analysis process and capture assistant responses
|
|
69
|
+
if (message.type === 'assistant' && message.message?.content) {
|
|
70
|
+
for (const content of message.message.content) {
|
|
71
|
+
if (content.type === 'text') {
|
|
72
|
+
lastAssistantResponse += content.text + '\n';
|
|
73
|
+
if (verbose) {
|
|
74
|
+
console.log(`\n🤖 ${content.text}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else if (content.type === 'tool_use') {
|
|
78
|
+
if (verbose) {
|
|
79
|
+
console.log(`\n🔧 ${content.name}: ${content.input.description || 'Running...'}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (message.type === 'result') {
|
|
85
|
+
if (message.subtype === 'success') {
|
|
86
|
+
logInfo('\n📊 Feature analysis completed, parsing results...');
|
|
87
|
+
const responseText = message.result || lastAssistantResponse;
|
|
88
|
+
const parsed = parseAnalysisResult(responseText);
|
|
89
|
+
if (parsed.error) {
|
|
90
|
+
logError(`Failed to parse structured analysis result: ${parsed.error}`);
|
|
91
|
+
structuredAnalysisResult = {
|
|
92
|
+
status: 'error',
|
|
93
|
+
summary: 'Failed to parse analysis results from Claude Code response',
|
|
94
|
+
created_user_stories: [],
|
|
95
|
+
created_test_cases: [],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
structuredAnalysisResult = parsed.analysis;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
logError(`\n⚠️ Analysis incomplete: ${message.subtype}`);
|
|
104
|
+
if (message.subtype === 'error_max_turns') {
|
|
105
|
+
logError('💡 Try increasing timeout or reducing complexity');
|
|
106
|
+
}
|
|
107
|
+
// Try to parse results from the last assistant response
|
|
108
|
+
if (lastAssistantResponse) {
|
|
109
|
+
const parsed = parseAnalysisResult(lastAssistantResponse);
|
|
110
|
+
if (!parsed.error) {
|
|
111
|
+
structuredAnalysisResult = parsed.analysis;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return structuredAnalysisResult;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Perform verification and determine if iteration should continue
|
|
121
|
+
* This function only verifies - it does NOT manage artifacts (save/delete/update)
|
|
122
|
+
*/
|
|
123
|
+
export async function performVerificationCycle(structuredAnalysisResult, checklistContext, context, config, currentIteration, maxIterations, mcpServerUrl, mcpToken, featureId, verbose) {
|
|
124
|
+
// No verification needed if no checklist context
|
|
125
|
+
if (!checklistContext || checklistContext.checklists.length === 0) {
|
|
126
|
+
return { passed: true, verificationResult: null };
|
|
127
|
+
}
|
|
128
|
+
const { created_user_stories, created_test_cases } = structuredAnalysisResult;
|
|
129
|
+
if (verbose) {
|
|
130
|
+
logInfo('🔍 Starting checklist verification...');
|
|
131
|
+
}
|
|
132
|
+
const verificationResult = await verifyChecklistCompliance({
|
|
133
|
+
checklistContext,
|
|
134
|
+
analysisContext: context,
|
|
135
|
+
createdUserStories: created_user_stories || [],
|
|
136
|
+
createdTestCases: created_test_cases || [],
|
|
137
|
+
verbose,
|
|
138
|
+
}, config);
|
|
139
|
+
// Verification passed
|
|
140
|
+
if (verificationResult.rejected_count === 0) {
|
|
141
|
+
if (verbose) {
|
|
142
|
+
logInfo(`✅ Checklist verification passed: ${verificationResult.confirmed_count} confirmed, ${verificationResult.uncertain_count} uncertain`);
|
|
143
|
+
}
|
|
144
|
+
// Log verification success
|
|
145
|
+
await logFeatureVerificationEvent(mcpServerUrl, mcpToken, {
|
|
146
|
+
featureId,
|
|
147
|
+
phase: 'feature_analysis',
|
|
148
|
+
iteration: currentIteration,
|
|
149
|
+
result: 'success',
|
|
150
|
+
verificationData: {
|
|
151
|
+
confirmed_count: verificationResult.confirmed_count,
|
|
152
|
+
rejected_count: verificationResult.rejected_count,
|
|
153
|
+
uncertain_count: verificationResult.uncertain_count,
|
|
154
|
+
summary: verificationResult.summary,
|
|
155
|
+
},
|
|
156
|
+
}, verbose);
|
|
157
|
+
return { passed: true, verificationResult };
|
|
158
|
+
}
|
|
159
|
+
// Verification failed
|
|
160
|
+
logError(`❌ Iteration ${currentIteration}: Checklist verification FAILED - ${verificationResult.rejected_count} items rejected, ${verificationResult.uncertain_count} uncertain`);
|
|
161
|
+
// Log verification failure with improvement suggestions
|
|
162
|
+
await logFeatureVerificationEvent(mcpServerUrl, mcpToken, {
|
|
163
|
+
featureId,
|
|
164
|
+
phase: 'feature_analysis',
|
|
165
|
+
iteration: currentIteration,
|
|
166
|
+
result: 'error',
|
|
167
|
+
verificationData: {
|
|
168
|
+
confirmed_count: verificationResult.confirmed_count,
|
|
169
|
+
rejected_count: verificationResult.rejected_count,
|
|
170
|
+
uncertain_count: verificationResult.uncertain_count,
|
|
171
|
+
rejected_items: verificationResult.item_verifications
|
|
172
|
+
.filter((item) => item.verification_status === 'rejected')
|
|
173
|
+
.map((item) => ({
|
|
174
|
+
checklist_item_id: item.checklist_item_id,
|
|
175
|
+
reason: item.verification_reason || 'No reason provided',
|
|
176
|
+
concerns: item.concerns || [],
|
|
177
|
+
improvement_suggestions: item.improvement_suggestions || [],
|
|
178
|
+
})),
|
|
179
|
+
uncertain_items: verificationResult.item_verifications
|
|
180
|
+
.filter((item) => item.verification_status === 'uncertain')
|
|
181
|
+
.map((item) => ({
|
|
182
|
+
checklist_item_id: item.checklist_item_id,
|
|
183
|
+
reason: item.verification_reason || 'No reason provided',
|
|
184
|
+
concerns: item.concerns || [],
|
|
185
|
+
improvement_suggestions: item.improvement_suggestions || [],
|
|
186
|
+
})),
|
|
187
|
+
summary: verificationResult.summary,
|
|
188
|
+
overall_suggestions: verificationResult.overall_suggestions || [],
|
|
189
|
+
},
|
|
190
|
+
}, verbose);
|
|
191
|
+
// Check if we can iterate again
|
|
192
|
+
if (currentIteration < maxIterations) {
|
|
193
|
+
if (verbose) {
|
|
194
|
+
logInfo(` Will retry with improvement feedback (${maxIterations - currentIteration} attempts remaining)`);
|
|
195
|
+
}
|
|
196
|
+
// Create improvement prompt for next iteration
|
|
197
|
+
const nextPrompt = createImprovementPrompt(verificationResult, {
|
|
198
|
+
created_user_stories: created_user_stories || [],
|
|
199
|
+
created_test_cases: created_test_cases || [],
|
|
200
|
+
});
|
|
201
|
+
return { passed: false, verificationResult, nextPrompt };
|
|
202
|
+
}
|
|
203
|
+
// Max iterations reached
|
|
204
|
+
logError(`❌ Maximum iterations (${maxIterations}) reached. Checklist verification still failing.`);
|
|
205
|
+
return { passed: false, verificationResult };
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Save analysis artifacts as draft and return their IDs
|
|
209
|
+
*/
|
|
210
|
+
export async function saveAnalysisArtifactsAsDraft(mcpServerUrl, mcpToken, featureId, created_user_stories, created_test_cases, verbose) {
|
|
211
|
+
const userStoryIds = [];
|
|
212
|
+
const testCaseIds = [];
|
|
213
|
+
// Save user stories as draft
|
|
214
|
+
if (created_user_stories && created_user_stories.length > 0) {
|
|
215
|
+
if (verbose) {
|
|
216
|
+
logInfo(`Saving ${created_user_stories.length} user stories as draft...`);
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
const result = (await callMcpEndpoint(mcpServerUrl, mcpToken, 'user_stories/create', {
|
|
220
|
+
feature_id: featureId,
|
|
221
|
+
user_stories: created_user_stories.map((story) => ({
|
|
222
|
+
title: story.title,
|
|
223
|
+
description: story.description,
|
|
224
|
+
status: 'draft',
|
|
225
|
+
})),
|
|
226
|
+
}));
|
|
227
|
+
if (result.created_user_stories) {
|
|
228
|
+
userStoryIds.push(...result.created_user_stories.map((story) => story.id));
|
|
229
|
+
if (verbose) {
|
|
230
|
+
logInfo(`✅ Saved ${userStoryIds.length} user stories as draft`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
logError(`Failed to save user stories: ${error instanceof Error ? error.message : String(error)}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// Save test cases as draft
|
|
239
|
+
if (created_test_cases && created_test_cases.length > 0) {
|
|
240
|
+
if (verbose) {
|
|
241
|
+
logInfo(`Saving ${created_test_cases.length} test cases as draft...`);
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const result = (await callMcpEndpoint(mcpServerUrl, mcpToken, 'test_cases/create', {
|
|
245
|
+
feature_id: featureId,
|
|
246
|
+
test_cases: created_test_cases.map((testCase) => ({
|
|
247
|
+
name: testCase.name,
|
|
248
|
+
description: testCase.description,
|
|
249
|
+
is_critical: testCase.is_critical || false,
|
|
250
|
+
status: 'draft',
|
|
251
|
+
})),
|
|
252
|
+
}));
|
|
253
|
+
if (result.created_test_cases) {
|
|
254
|
+
testCaseIds.push(...result.created_test_cases.map((testCase) => testCase.id));
|
|
255
|
+
if (verbose) {
|
|
256
|
+
logInfo(`✅ Saved ${testCaseIds.length} test cases as draft`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
logError(`Failed to save test cases: ${error instanceof Error ? error.message : String(error)}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return { userStoryIds, testCaseIds };
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Update artifacts to ready status after successful verification
|
|
268
|
+
*/
|
|
269
|
+
export async function updateArtifactsToReady(mcpServerUrl, mcpToken, userStoryIds, testCaseIds, verbose) {
|
|
270
|
+
if (userStoryIds.length > 0) {
|
|
271
|
+
if (verbose) {
|
|
272
|
+
logInfo(`Updating ${userStoryIds.length} user stories from draft to ready...`);
|
|
273
|
+
}
|
|
274
|
+
await batchUpdateUserStoryStatus(mcpServerUrl, mcpToken, userStoryIds, 'ready', verbose);
|
|
275
|
+
}
|
|
276
|
+
if (testCaseIds.length > 0) {
|
|
277
|
+
if (verbose) {
|
|
278
|
+
logInfo(`Updating ${testCaseIds.length} test cases from draft to ready...`);
|
|
279
|
+
}
|
|
280
|
+
await batchUpdateTestCaseStatus(mcpServerUrl, mcpToken, testCaseIds, 'ready', verbose);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Delete artifacts after verification failure
|
|
285
|
+
*/
|
|
286
|
+
export async function deleteArtifacts(mcpServerUrl, mcpToken, userStoryIds, testCaseIds, verbose) {
|
|
287
|
+
if (userStoryIds.length > 0) {
|
|
288
|
+
if (verbose) {
|
|
289
|
+
logInfo(`Deleting ${userStoryIds.length} user stories due to verification failure...`);
|
|
290
|
+
}
|
|
291
|
+
await batchDeleteUserStories(mcpServerUrl, mcpToken, userStoryIds, verbose);
|
|
292
|
+
}
|
|
293
|
+
if (testCaseIds.length > 0) {
|
|
294
|
+
if (verbose) {
|
|
295
|
+
logInfo(`Deleting ${testCaseIds.length} test cases due to verification failure...`);
|
|
296
|
+
}
|
|
297
|
+
await batchDeleteTestCases(mcpServerUrl, mcpToken, testCaseIds, verbose);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Save analysis artifacts (user stories and test cases) - Legacy function for non-verification flows
|
|
302
|
+
*/
|
|
303
|
+
export async function saveAnalysisArtifacts(mcpServerUrl, mcpToken, featureId, created_user_stories, created_test_cases, status, verbose) {
|
|
304
|
+
if (created_user_stories && created_user_stories.length > 0) {
|
|
305
|
+
if (verbose) {
|
|
306
|
+
logInfo('Saving created user stories...');
|
|
307
|
+
}
|
|
308
|
+
const userStoriesCreated = await createUserStories(mcpServerUrl, mcpToken, featureId, created_user_stories, verbose);
|
|
309
|
+
if (!userStoriesCreated && verbose) {
|
|
310
|
+
logError('⚠️ Failed to save user stories');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (created_test_cases && created_test_cases.length > 0) {
|
|
314
|
+
if (verbose) {
|
|
315
|
+
logInfo('Saving created test cases...');
|
|
316
|
+
}
|
|
317
|
+
const testCasesCreated = await createTestCases(mcpServerUrl, mcpToken, featureId, created_test_cases, verbose);
|
|
318
|
+
if (!testCasesCreated && verbose) {
|
|
319
|
+
logError('⚠️ Failed to save test cases');
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// Try HTTP fallback if direct save failed
|
|
323
|
+
if (status === 'error' &&
|
|
324
|
+
(created_user_stories?.length > 0 || created_test_cases?.length > 0)) {
|
|
325
|
+
if (verbose) {
|
|
326
|
+
logInfo('Direct save failed, trying HTTP fallback...');
|
|
327
|
+
}
|
|
328
|
+
const httpSuccess = await saveDataViaHttp({
|
|
329
|
+
mcpServerUrl,
|
|
330
|
+
mcpToken,
|
|
331
|
+
featureId,
|
|
332
|
+
userStories: created_user_stories || [],
|
|
333
|
+
testCases: created_test_cases || [],
|
|
334
|
+
verbose,
|
|
335
|
+
});
|
|
336
|
+
if (httpSuccess) {
|
|
337
|
+
logInfo('✅ Successfully saved data via HTTP fallback');
|
|
338
|
+
}
|
|
339
|
+
else if (verbose) {
|
|
340
|
+
logError('❌ HTTP fallback also failed');
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Build the final analysis result object
|
|
346
|
+
*/
|
|
347
|
+
export function buildAnalysisResult(featureId, context, structuredAnalysisResult, currentIteration) {
|
|
348
|
+
const { created_user_stories, created_test_cases, checklist_results, checklist_item_results, } = structuredAnalysisResult;
|
|
349
|
+
return {
|
|
350
|
+
featureId,
|
|
351
|
+
productInfo: context.product,
|
|
352
|
+
featureInfo: context.feature,
|
|
353
|
+
existingUserStories: context.existing_user_stories.map((story) => ({
|
|
354
|
+
...story,
|
|
355
|
+
status: story.status,
|
|
356
|
+
created_at: story.created_at || new Date().toISOString(),
|
|
357
|
+
updated_at: story.updated_at || new Date().toISOString(),
|
|
358
|
+
})),
|
|
359
|
+
existingTestCases: context.existing_test_cases.map((testCase) => ({
|
|
360
|
+
...testCase,
|
|
361
|
+
created_at: testCase.created_at || new Date().toISOString(),
|
|
362
|
+
updated_at: testCase.updated_at || new Date().toISOString(),
|
|
363
|
+
})),
|
|
364
|
+
createdUserStories: (created_user_stories || []).map((story) => ({
|
|
365
|
+
id: '',
|
|
366
|
+
title: story.title,
|
|
367
|
+
description: story.description,
|
|
368
|
+
status: story.status || 'draft',
|
|
369
|
+
created_at: new Date().toISOString(),
|
|
370
|
+
updated_at: new Date().toISOString(),
|
|
371
|
+
})),
|
|
372
|
+
createdTestCases: (created_test_cases || []).map((testCase) => ({
|
|
373
|
+
id: '',
|
|
374
|
+
name: testCase.name,
|
|
375
|
+
description: testCase.description,
|
|
376
|
+
is_critical: testCase.is_critical || false,
|
|
377
|
+
created_at: new Date().toISOString(),
|
|
378
|
+
updated_at: new Date().toISOString(),
|
|
379
|
+
})),
|
|
380
|
+
summary: structuredAnalysisResult.summary ||
|
|
381
|
+
`Analysis completed${currentIteration > 1 ? ` after ${currentIteration} iterations` : ''}`,
|
|
382
|
+
status: structuredAnalysisResult.status === 'success' ? 'success' : 'error',
|
|
383
|
+
data: {
|
|
384
|
+
checklist_results,
|
|
385
|
+
checklist_item_results,
|
|
386
|
+
verification_result: structuredAnalysisResult.verification_result,
|
|
387
|
+
iterations: currentIteration,
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Build error result when verification fails after all iterations
|
|
393
|
+
*/
|
|
394
|
+
export function buildVerificationFailureResult(featureId, context, verificationResult, checklist_results, checklist_item_results, currentIteration) {
|
|
395
|
+
return {
|
|
396
|
+
featureId,
|
|
397
|
+
productInfo: context.product,
|
|
398
|
+
featureInfo: context.feature,
|
|
399
|
+
existingUserStories: context.existing_user_stories.map((story) => ({
|
|
400
|
+
...story,
|
|
401
|
+
status: story.status,
|
|
402
|
+
created_at: story.created_at || new Date().toISOString(),
|
|
403
|
+
updated_at: story.updated_at || new Date().toISOString(),
|
|
404
|
+
})),
|
|
405
|
+
existingTestCases: context.existing_test_cases.map((testCase) => ({
|
|
406
|
+
...testCase,
|
|
407
|
+
created_at: testCase.created_at || new Date().toISOString(),
|
|
408
|
+
updated_at: testCase.updated_at || new Date().toISOString(),
|
|
409
|
+
})),
|
|
410
|
+
createdUserStories: [],
|
|
411
|
+
createdTestCases: [],
|
|
412
|
+
summary: `Checklist verification failed after ${currentIteration} iterations: ${verificationResult.summary}`,
|
|
413
|
+
status: 'error',
|
|
414
|
+
data: {
|
|
415
|
+
checklist_results,
|
|
416
|
+
checklist_item_results,
|
|
417
|
+
verification_result: verificationResult,
|
|
418
|
+
iterations: currentIteration,
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Build error result when no analysis results are received
|
|
424
|
+
*/
|
|
425
|
+
export function buildNoResultsError(featureId, context) {
|
|
426
|
+
return {
|
|
427
|
+
featureId,
|
|
428
|
+
productInfo: context.product,
|
|
429
|
+
featureInfo: context.feature,
|
|
430
|
+
existingUserStories: context.existing_user_stories.map((story) => ({
|
|
431
|
+
...story,
|
|
432
|
+
status: story.status,
|
|
433
|
+
created_at: story.created_at || new Date().toISOString(),
|
|
434
|
+
updated_at: story.updated_at || new Date().toISOString(),
|
|
435
|
+
})),
|
|
436
|
+
existingTestCases: context.existing_test_cases.map((testCase) => ({
|
|
437
|
+
...testCase,
|
|
438
|
+
created_at: testCase.created_at || new Date().toISOString(),
|
|
439
|
+
updated_at: testCase.updated_at || new Date().toISOString(),
|
|
440
|
+
})),
|
|
441
|
+
createdUserStories: [],
|
|
442
|
+
createdTestCases: [],
|
|
443
|
+
summary: 'No analysis results received',
|
|
444
|
+
status: 'error',
|
|
445
|
+
data: {
|
|
446
|
+
checklist_results: undefined,
|
|
447
|
+
checklist_item_results: undefined,
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
}
|
|
@@ -5,6 +5,7 @@ export interface FeatureAnalysisOptions {
|
|
|
5
5
|
mcpServerUrl: string;
|
|
6
6
|
mcpToken: string;
|
|
7
7
|
verbose?: boolean;
|
|
8
|
+
maxVerificationIterations?: number;
|
|
8
9
|
}
|
|
9
10
|
export declare const analyzeFeatureWithMCP: (options: FeatureAnalysisOptions, config: EdsgerConfig, checklistContext?: ChecklistPhaseContext | null) => Promise<FeatureAnalysisResult>;
|
|
10
11
|
export declare const checkFeatureAnalysisRequirements: () => Promise<boolean>;
|