edsger 0.41.1 → 0.41.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/.claude/settings.local.json +23 -3
  2. package/.env.local +12 -0
  3. package/dist/api/features/__tests__/regression-prevention.test.d.ts +5 -0
  4. package/dist/api/features/__tests__/regression-prevention.test.js +338 -0
  5. package/dist/api/features/__tests__/status-updater.integration.test.d.ts +5 -0
  6. package/dist/api/features/__tests__/status-updater.integration.test.js +497 -0
  7. package/dist/commands/workflow/pipeline-runner.d.ts +17 -0
  8. package/dist/commands/workflow/pipeline-runner.js +393 -0
  9. package/dist/commands/workflow/runner.d.ts +26 -0
  10. package/dist/commands/workflow/runner.js +119 -0
  11. package/dist/commands/workflow/workflow-runner.d.ts +26 -0
  12. package/dist/commands/workflow/workflow-runner.js +119 -0
  13. package/dist/index.js +0 -0
  14. package/dist/phases/code-implementation/analyzer-helpers.d.ts +28 -0
  15. package/dist/phases/code-implementation/analyzer-helpers.js +177 -0
  16. package/dist/phases/code-implementation/analyzer.d.ts +32 -0
  17. package/dist/phases/code-implementation/analyzer.js +629 -0
  18. package/dist/phases/code-implementation/context-fetcher.d.ts +17 -0
  19. package/dist/phases/code-implementation/context-fetcher.js +86 -0
  20. package/dist/phases/code-implementation/mcp-server.d.ts +1 -0
  21. package/dist/phases/code-implementation/mcp-server.js +93 -0
  22. package/dist/phases/code-implementation/prompts-improvement.d.ts +5 -0
  23. package/dist/phases/code-implementation/prompts-improvement.js +108 -0
  24. package/dist/phases/code-implementation-verification/verifier.d.ts +31 -0
  25. package/dist/phases/code-implementation-verification/verifier.js +196 -0
  26. package/dist/phases/code-refine/analyzer.d.ts +41 -0
  27. package/dist/phases/code-refine/analyzer.js +561 -0
  28. package/dist/phases/code-refine/context-fetcher.d.ts +94 -0
  29. package/dist/phases/code-refine/context-fetcher.js +423 -0
  30. package/dist/phases/code-refine-verification/analysis/llm-analyzer.d.ts +22 -0
  31. package/dist/phases/code-refine-verification/analysis/llm-analyzer.js +134 -0
  32. package/dist/phases/code-refine-verification/verifier.d.ts +47 -0
  33. package/dist/phases/code-refine-verification/verifier.js +597 -0
  34. package/dist/phases/code-review/analyzer.d.ts +29 -0
  35. package/dist/phases/code-review/analyzer.js +363 -0
  36. package/dist/phases/code-review/context-fetcher.d.ts +92 -0
  37. package/dist/phases/code-review/context-fetcher.js +296 -0
  38. package/dist/phases/feature-analysis/analyzer-helpers.d.ts +10 -0
  39. package/dist/phases/feature-analysis/analyzer-helpers.js +47 -0
  40. package/dist/phases/feature-analysis/analyzer.d.ts +11 -0
  41. package/dist/phases/feature-analysis/analyzer.js +208 -0
  42. package/dist/phases/feature-analysis/context-fetcher.d.ts +26 -0
  43. package/dist/phases/feature-analysis/context-fetcher.js +134 -0
  44. package/dist/phases/feature-analysis/http-fallback.d.ts +20 -0
  45. package/dist/phases/feature-analysis/http-fallback.js +95 -0
  46. package/dist/phases/feature-analysis/mcp-server.d.ts +1 -0
  47. package/dist/phases/feature-analysis/mcp-server.js +144 -0
  48. package/dist/phases/feature-analysis/prompts-improvement.d.ts +8 -0
  49. package/dist/phases/feature-analysis/prompts-improvement.js +109 -0
  50. package/dist/phases/feature-analysis-verification/verifier.d.ts +37 -0
  51. package/dist/phases/feature-analysis-verification/verifier.js +147 -0
  52. package/dist/phases/pr-execution/file-assigner.js +20 -12
  53. package/dist/phases/technical-design/analyzer-helpers.d.ts +25 -0
  54. package/dist/phases/technical-design/analyzer-helpers.js +39 -0
  55. package/dist/phases/technical-design/analyzer.d.ts +21 -0
  56. package/dist/phases/technical-design/analyzer.js +461 -0
  57. package/dist/phases/technical-design/context-fetcher.d.ts +12 -0
  58. package/dist/phases/technical-design/context-fetcher.js +39 -0
  59. package/dist/phases/technical-design/http-fallback.d.ts +17 -0
  60. package/dist/phases/technical-design/http-fallback.js +151 -0
  61. package/dist/phases/technical-design/mcp-server.d.ts +1 -0
  62. package/dist/phases/technical-design/mcp-server.js +157 -0
  63. package/dist/phases/technical-design/prompts-improvement.d.ts +5 -0
  64. package/dist/phases/technical-design/prompts-improvement.js +93 -0
  65. package/dist/phases/technical-design-verification/verifier.d.ts +53 -0
  66. package/dist/phases/technical-design-verification/verifier.js +170 -0
  67. package/dist/services/feature-branches.d.ts +77 -0
  68. package/dist/services/feature-branches.js +205 -0
  69. package/dist/workflow-runner/config/phase-configs.d.ts +5 -0
  70. package/dist/workflow-runner/config/phase-configs.js +120 -0
  71. package/dist/workflow-runner/core/feature-filter.d.ts +16 -0
  72. package/dist/workflow-runner/core/feature-filter.js +46 -0
  73. package/dist/workflow-runner/core/index.d.ts +8 -0
  74. package/dist/workflow-runner/core/index.js +12 -0
  75. package/dist/workflow-runner/core/pipeline-evaluator.d.ts +24 -0
  76. package/dist/workflow-runner/core/pipeline-evaluator.js +32 -0
  77. package/dist/workflow-runner/core/state-manager.d.ts +24 -0
  78. package/dist/workflow-runner/core/state-manager.js +42 -0
  79. package/dist/workflow-runner/core/workflow-logger.d.ts +20 -0
  80. package/dist/workflow-runner/core/workflow-logger.js +65 -0
  81. package/dist/workflow-runner/executors/phase-executor.d.ts +8 -0
  82. package/dist/workflow-runner/executors/phase-executor.js +248 -0
  83. package/dist/workflow-runner/feature-workflow-runner.d.ts +26 -0
  84. package/dist/workflow-runner/feature-workflow-runner.js +119 -0
  85. package/dist/workflow-runner/index.d.ts +2 -0
  86. package/dist/workflow-runner/index.js +2 -0
  87. package/dist/workflow-runner/pipeline-runner.d.ts +17 -0
  88. package/dist/workflow-runner/pipeline-runner.js +393 -0
  89. package/dist/workflow-runner/workflow-processor.d.ts +54 -0
  90. package/dist/workflow-runner/workflow-processor.js +170 -0
  91. package/package.json +1 -1
  92. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.d.ts +0 -4
  93. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.js +0 -133
  94. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.d.ts +0 -4
  95. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.js +0 -336
  96. package/dist/services/lifecycle-agent/index.d.ts +0 -24
  97. package/dist/services/lifecycle-agent/index.js +0 -25
  98. package/dist/services/lifecycle-agent/phase-criteria.d.ts +0 -57
  99. package/dist/services/lifecycle-agent/phase-criteria.js +0 -335
  100. package/dist/services/lifecycle-agent/transition-rules.d.ts +0 -60
  101. package/dist/services/lifecycle-agent/transition-rules.js +0 -184
  102. package/dist/services/lifecycle-agent/types.d.ts +0 -190
  103. package/dist/services/lifecycle-agent/types.js +0 -12
@@ -0,0 +1,461 @@
1
+ import { query } from '@anthropic-ai/claude-code';
2
+ import { logInfo, logError } from '../../utils/logger.js';
3
+ import { saveTechnicalDesignViaHttp } from './http-fallback.js';
4
+ import { fetchTechnicalDesignContext } from './context-fetcher.js';
5
+ import { updateTechnicalDesign } from '../../api/features/index.js';
6
+ import { formatTechnicalDesignContext } from '../../utils/formatters.js';
7
+ import { createTechnicalDesignSystemPrompt, createTechnicalDesignPromptWithContext, } from './prompts.js';
8
+ import { formatChecklistsForContext, } from '../../services/checklist.js';
9
+ import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
10
+ import { performVerificationCycle, buildDesignResult, buildVerificationFailureResult, buildNoResultsError, } from './analyzer-helpers.js';
11
+ import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
12
+ function userMessage(content) {
13
+ return {
14
+ type: 'user',
15
+ message: { role: 'user', content: content },
16
+ };
17
+ }
18
+ async function* prompt(analysisPrompt) {
19
+ yield userMessage(analysisPrompt);
20
+ await new Promise((res) => setTimeout(res, 10000));
21
+ }
22
+ export const generateTechnicalDesign = async (options, config, checklistContext) => {
23
+ const { featureId, verbose } = options;
24
+ if (verbose) {
25
+ logInfo(`Starting technical design generation for feature ID: ${featureId}`);
26
+ }
27
+ try {
28
+ // Fetch and prepare context
29
+ const context = await prepareDesignContext(featureId, checklistContext, verbose);
30
+ const systemPrompt = createTechnicalDesignSystemPrompt(config, featureId);
31
+ const initialDesignPrompt = context.designPrompt;
32
+ const maxIterations = options.maxVerificationIterations || 10;
33
+ let currentIteration = 0;
34
+ let currentPrompt = initialDesignPrompt;
35
+ let structuredDesignResult = null;
36
+ let verificationResult = null;
37
+ if (verbose) {
38
+ logInfo('Starting Claude Code query with pre-fetched information...');
39
+ }
40
+ // Iterative improvement loop: design → verification → improve → re-design
41
+ while (currentIteration < maxIterations) {
42
+ currentIteration++;
43
+ if (verbose && currentIteration > 1) {
44
+ logInfo(`\n🔄 Iteration ${currentIteration}/${maxIterations}: Improving design based on verification feedback...`);
45
+ }
46
+ // Log iteration start (for iterations after the first)
47
+ if (currentIteration > 1) {
48
+ await logFeaturePhaseEvent({
49
+ featureId,
50
+ eventType: 'phase_started',
51
+ phase: 'technical_design',
52
+ result: 'info',
53
+ metadata: {
54
+ iteration: currentIteration,
55
+ max_iterations: maxIterations,
56
+ re_design: true,
57
+ timestamp: new Date().toISOString(),
58
+ },
59
+ }, verbose);
60
+ }
61
+ // Execute design query
62
+ const newDesignResult = await executeDesignQuery(currentPrompt, systemPrompt, config, verbose);
63
+ // No result produced, break out
64
+ if (!newDesignResult?.technical_design) {
65
+ if (verbose) {
66
+ logError('⚠️ Design generation failed in this iteration');
67
+ }
68
+ break;
69
+ }
70
+ // Update with new design result
71
+ structuredDesignResult = newDesignResult;
72
+ // Log design completion for this iteration
73
+ await logFeaturePhaseEvent({
74
+ featureId,
75
+ eventType: 'phase_completed',
76
+ phase: 'technical_design',
77
+ result: 'success',
78
+ metadata: {
79
+ iteration: currentIteration,
80
+ max_iterations: maxIterations,
81
+ design_step: 'completed',
82
+ timestamp: new Date().toISOString(),
83
+ },
84
+ }, verbose);
85
+ // Save technical design immediately after generation
86
+ if (verbose) {
87
+ logInfo(`💾 Saving technical design (iteration ${currentIteration})...`);
88
+ }
89
+ await saveTechnicalDesign(featureId, structuredDesignResult.technical_design, verbose);
90
+ // Perform verification cycle
91
+ const verificationCycle = await performVerificationCycle(structuredDesignResult.technical_design, checklistContext || null, context.feedbacksContext, featureId, context.featureName, context.featureDescription, config, currentIteration, maxIterations, verbose);
92
+ verificationResult = verificationCycle.verificationResult;
93
+ // If verification passed, exit
94
+ if (verificationCycle.passed) {
95
+ if (verbose) {
96
+ logInfo('✅ Verification passed! Technical design saved.');
97
+ }
98
+ break;
99
+ }
100
+ // Verification failed
101
+ if (currentIteration < maxIterations && verificationCycle.nextPrompt) {
102
+ // We have more iterations - retry with improvement prompt
103
+ if (verbose) {
104
+ logInfo(`🔄 Will improve and save design in next iteration (${maxIterations - currentIteration} attempts remaining)`);
105
+ }
106
+ currentPrompt = verificationCycle.nextPrompt;
107
+ }
108
+ else {
109
+ // Max iterations reached or no next prompt - exit loop
110
+ if (verbose) {
111
+ logInfo('⚠️ Max iterations reached. Last design version saved.');
112
+ }
113
+ break;
114
+ }
115
+ }
116
+ // Handle results
117
+ if (!structuredDesignResult?.technical_design) {
118
+ return buildNoResultsError(featureId);
119
+ }
120
+ // Check if verification failed after all iterations
121
+ if (verificationResult &&
122
+ verificationResult.rejected_count > 0 &&
123
+ checklistContext &&
124
+ checklistContext.checklists.length > 0) {
125
+ logError(`❌ Final result: Checklist verification FAILED after ${currentIteration} iterations`);
126
+ logError(` Technical design saved for manual review`);
127
+ return buildVerificationFailureResult(featureId, structuredDesignResult.technical_design, verificationResult, currentIteration);
128
+ }
129
+ // Return success result
130
+ return buildDesignResult(featureId, structuredDesignResult.technical_design, structuredDesignResult.summary ||
131
+ 'Technical design generated successfully', currentIteration);
132
+ }
133
+ catch (error) {
134
+ logError(`Technical design generation failed: ${error instanceof Error ? error.message : String(error)}`);
135
+ return {
136
+ featureId,
137
+ technicalDesign: null,
138
+ status: 'error',
139
+ summary: `Generation failed: ${error instanceof Error ? error.message : String(error)}`,
140
+ };
141
+ }
142
+ };
143
+ /**
144
+ * Prepare all context information needed for design
145
+ */
146
+ async function prepareDesignContext(featureId, checklistContext, verbose) {
147
+ if (verbose) {
148
+ logInfo('Fetching technical design context via MCP endpoints...');
149
+ }
150
+ const context = await fetchTechnicalDesignContext(featureId, verbose);
151
+ const { content: contextInfo, downloadedImages } = await formatTechnicalDesignContext(context);
152
+ if (verbose && downloadedImages.length > 0) {
153
+ logInfo(`Downloaded ${downloadedImages.length} images for Claude Code:`);
154
+ downloadedImages.forEach((img) => {
155
+ logInfo(` - ${img.url} -> ${img.localPath}`);
156
+ });
157
+ }
158
+ let finalContextInfo = contextInfo;
159
+ let hasFeedbacks = false;
160
+ let feedbacksContext = null;
161
+ // Check if there's existing technical design
162
+ const existingTechnicalDesign = context.feature.technical_design;
163
+ const hasExistingDesign = !!existingTechnicalDesign && existingTechnicalDesign.trim().length > 0;
164
+ // Add existing technical design to context if it exists
165
+ if (hasExistingDesign && verbose) {
166
+ logInfo('📋 Found existing technical design - will perform incremental update if feedbacks exist');
167
+ }
168
+ // Add feedbacks context to the design prompt
169
+ try {
170
+ feedbacksContext = await getFeedbacksForPhase({ featureId, verbose }, 'technical-design');
171
+ if (verbose) {
172
+ logInfo(`📋 Feedbacks fetched successfully: ${feedbacksContext.feedbacks.length} feedbacks found`);
173
+ if (feedbacksContext.feedbacks.length > 0) {
174
+ logInfo('📝 Feedbacks details:');
175
+ feedbacksContext.feedbacks.forEach((fb, idx) => {
176
+ logInfo(` ${idx + 1}. [${fb.feedback_type}] ${fb.title} (priority: ${fb.priority}, resolved: ${fb.is_resolved || false})`);
177
+ logInfo(` Content: ${fb.content.substring(0, 150)}...`);
178
+ if (fb.target_type) {
179
+ logInfo(` 🎯 Target Type: ${fb.target_type}`);
180
+ }
181
+ if (fb.document_type) {
182
+ logInfo(` 📄 Document: ${fb.document_type}`);
183
+ }
184
+ if (fb.line_number) {
185
+ logInfo(` 📏 Line: ${fb.line_number}`);
186
+ }
187
+ else if (fb.line_range_start && fb.line_range_end) {
188
+ logInfo(` 📏 Lines: ${fb.line_range_start}-${fb.line_range_end}`);
189
+ }
190
+ if (fb.context_snippet) {
191
+ logInfo(` Referenced: ${fb.context_snippet.substring(0, 100)}...`);
192
+ }
193
+ });
194
+ }
195
+ }
196
+ if (feedbacksContext.feedbacks.length > 0) {
197
+ hasFeedbacks = true;
198
+ const feedbacksInfo = formatFeedbacksForContext(feedbacksContext);
199
+ finalContextInfo = finalContextInfo + '\n\n' + feedbacksInfo;
200
+ if (verbose) {
201
+ logInfo(`✅ Added ${feedbacksContext.feedbacks.length} human feedbacks to design context`);
202
+ logInfo(`📄 Feedbacks section length: ${feedbacksInfo.length} characters`);
203
+ }
204
+ }
205
+ else {
206
+ if (verbose) {
207
+ logInfo('ℹ️ No unresolved feedbacks found for this phase');
208
+ }
209
+ }
210
+ }
211
+ catch (error) {
212
+ // Don't fail if feedbacks fetch fails - just log and continue
213
+ if (verbose) {
214
+ logError(`❌ Failed to fetch feedbacks: ${error instanceof Error ? error.message : String(error)}`);
215
+ if (error instanceof Error && error.stack) {
216
+ logError(` Stack: ${error.stack}`);
217
+ }
218
+ }
219
+ }
220
+ // Add checklist context to the design prompt
221
+ if (checklistContext && checklistContext.checklists.length > 0) {
222
+ const checklistInfo = formatChecklistsForContext(checklistContext);
223
+ finalContextInfo = finalContextInfo + '\n\n' + checklistInfo;
224
+ if (verbose) {
225
+ logInfo(`Added ${checklistContext.checklists.length} checklists to design context`);
226
+ }
227
+ }
228
+ // Decide on prompt mode based on existing design and feedbacks
229
+ const isIncrementalUpdate = hasExistingDesign && hasFeedbacks;
230
+ if (verbose) {
231
+ logInfo('\n📋 Design Mode Decision:');
232
+ logInfo(` - Has existing design: ${hasExistingDesign}`);
233
+ logInfo(` - Has feedbacks: ${hasFeedbacks} (${feedbacksContext?.feedbacks.length || 0} feedbacks)`);
234
+ logInfo(` - Mode: ${isIncrementalUpdate ? '🔄 Incremental Update' : hasExistingDesign ? '🔄 Full Redesign' : '✨ New Design'}`);
235
+ if (isIncrementalUpdate && feedbacksContext) {
236
+ logInfo('\n🎯 Feedbacks that will be addressed:');
237
+ feedbacksContext.feedbacks.forEach((fb, idx) => {
238
+ const priorityBadge = fb.priority >= 9
239
+ ? '🔴'
240
+ : fb.priority >= 7
241
+ ? '🟠'
242
+ : fb.priority >= 5
243
+ ? '🟡'
244
+ : '🟢';
245
+ logInfo(` ${idx + 1}. [${fb.feedback_type}] ${fb.title} ${priorityBadge}`);
246
+ logInfo(` ${fb.content.substring(0, 100)}${fb.content.length > 100 ? '...' : ''}`);
247
+ });
248
+ }
249
+ }
250
+ const designPrompt = createTechnicalDesignPromptWithContext(featureId, finalContextInfo, existingTechnicalDesign, isIncrementalUpdate);
251
+ return {
252
+ featureName: context.feature.name,
253
+ featureDescription: context.feature.description || undefined,
254
+ designPrompt,
255
+ hasExistingDesign,
256
+ hasFeedbacks,
257
+ feedbacksContext,
258
+ };
259
+ }
260
+ /**
261
+ * Execute design query and parse result
262
+ */
263
+ async function executeDesignQuery(designPrompt, systemPrompt, config, verbose) {
264
+ let lastAssistantResponse = '';
265
+ let structuredDesignResult = null;
266
+ if (verbose) {
267
+ logInfo('🤖 Starting design agent query...');
268
+ }
269
+ // Use Claude Code SDK without MCP servers - all info is pre-fetched
270
+ for await (const message of query({
271
+ prompt: prompt(designPrompt),
272
+ options: {
273
+ appendSystemPrompt: systemPrompt,
274
+ model: config.claude.model || 'sonnet',
275
+ maxTurns: 1000,
276
+ permissionMode: 'bypassPermissions',
277
+ },
278
+ })) {
279
+ if (verbose) {
280
+ logInfo(` Received message type: ${message.type}`);
281
+ }
282
+ // Stream the technical design generation process
283
+ if (message.type === 'assistant' && message.message?.content) {
284
+ for (const content of message.message.content) {
285
+ if (content.type === 'text') {
286
+ lastAssistantResponse += content.text + '\n';
287
+ if (verbose) {
288
+ console.log(`\n🤖 ${content.text}`);
289
+ }
290
+ }
291
+ else if (content.type === 'tool_use') {
292
+ if (verbose) {
293
+ console.log(`\n🔧 ${content.name}: ${content.input.description || 'Running...'}`);
294
+ }
295
+ }
296
+ }
297
+ }
298
+ if (message.type === 'result') {
299
+ if (message.subtype === 'success') {
300
+ logInfo('\n🎨 Technical design generation completed, parsing results...');
301
+ try {
302
+ // Try to extract JSON from markdown code block or parse directly
303
+ const responseText = message.result || lastAssistantResponse;
304
+ let jsonResult = null;
305
+ // First try to extract JSON from markdown code block
306
+ const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
307
+ if (jsonBlockMatch) {
308
+ jsonResult = JSON.parse(jsonBlockMatch[1]);
309
+ }
310
+ else {
311
+ // Try to find JSON object containing "technical_design_result"
312
+ let jsonStartIndex = -1;
313
+ // Find the position of "technical_design_result" and work backwards to find the opening brace
314
+ if (responseText.includes('"technical_design_result"')) {
315
+ const targetIndex = responseText.indexOf('"technical_design_result"');
316
+ // Work backwards from "technical_design_result" to find the opening '{'
317
+ for (let i = targetIndex; i >= 0; i--) {
318
+ if (responseText[i] === '{') {
319
+ jsonStartIndex = i;
320
+ break;
321
+ }
322
+ }
323
+ }
324
+ if (jsonStartIndex !== -1) {
325
+ // Find the complete JSON object starting from this position
326
+ let braceCount = 0;
327
+ let endIndex = jsonStartIndex;
328
+ let inString = false;
329
+ let escapeNext = false;
330
+ for (let i = jsonStartIndex; i < responseText.length; i++) {
331
+ const char = responseText[i];
332
+ if (escapeNext) {
333
+ escapeNext = false;
334
+ continue;
335
+ }
336
+ if (char === '\\' && inString) {
337
+ escapeNext = true;
338
+ continue;
339
+ }
340
+ if (char === '"') {
341
+ inString = !inString;
342
+ continue;
343
+ }
344
+ if (!inString) {
345
+ if (char === '{') {
346
+ braceCount++;
347
+ }
348
+ else if (char === '}') {
349
+ braceCount--;
350
+ if (braceCount === 0) {
351
+ endIndex = i;
352
+ break;
353
+ }
354
+ }
355
+ }
356
+ }
357
+ const jsonStr = responseText.substring(jsonStartIndex, endIndex + 1);
358
+ try {
359
+ jsonResult = JSON.parse(jsonStr);
360
+ }
361
+ catch (parseError) {
362
+ logError(`Failed to parse extracted JSON: ${parseError}`);
363
+ // Try to parse the entire response as JSON as fallback
364
+ jsonResult = JSON.parse(responseText);
365
+ }
366
+ }
367
+ else {
368
+ // Try to parse the entire response as JSON
369
+ jsonResult = JSON.parse(responseText);
370
+ }
371
+ }
372
+ if (jsonResult && jsonResult.technical_design_result) {
373
+ structuredDesignResult = jsonResult.technical_design_result;
374
+ }
375
+ else {
376
+ throw new Error('Invalid JSON structure');
377
+ }
378
+ }
379
+ catch (error) {
380
+ logError(`Failed to parse structured design result: ${error}`);
381
+ // Extract technical design from response if JSON parsing fails
382
+ const extractedDesign = extractTechnicalDesignFromResponse(message.result || lastAssistantResponse);
383
+ structuredDesignResult = {
384
+ status: extractedDesign ? 'success' : 'error',
385
+ technical_design: extractedDesign,
386
+ summary: extractedDesign
387
+ ? 'Technical design generated successfully'
388
+ : 'Failed to generate technical design',
389
+ };
390
+ }
391
+ }
392
+ else {
393
+ logError(`\n⚠️ Technical design generation incomplete: ${message.subtype}`);
394
+ if (message.subtype === 'error_max_turns') {
395
+ logError('💡 Try increasing timeout or reducing complexity');
396
+ }
397
+ }
398
+ }
399
+ }
400
+ return structuredDesignResult;
401
+ }
402
+ /**
403
+ * Save technical design document
404
+ */
405
+ async function saveTechnicalDesign(featureId, technicalDesign, verbose) {
406
+ if (verbose) {
407
+ logInfo('Saving technical design...');
408
+ }
409
+ const designSaved = await updateTechnicalDesign(featureId, technicalDesign, verbose);
410
+ // Try HTTP fallback if direct update failed
411
+ if (!designSaved) {
412
+ if (verbose) {
413
+ logInfo('Direct update failed, trying HTTP fallback...');
414
+ }
415
+ const fallbackSaved = await saveTechnicalDesignViaHttp({
416
+ featureId,
417
+ technicalDesign,
418
+ verbose,
419
+ });
420
+ if (!fallbackSaved && verbose) {
421
+ logError('⚠️ Both direct update and HTTP fallback failed');
422
+ }
423
+ }
424
+ }
425
+ const extractTechnicalDesignFromResponse = (response) => {
426
+ // Try to extract technical design content from the response
427
+ // Look for markdown sections that contain technical design
428
+ const lines = response.split('\n');
429
+ let inTechnicalDesign = false;
430
+ const technicalDesignLines = [];
431
+ for (let i = 0; i < lines.length; i++) {
432
+ const line = lines[i].toLowerCase();
433
+ // Look for technical design section headers
434
+ if (line.includes('technical design') ||
435
+ line.includes('architecture') ||
436
+ line.includes('# design') ||
437
+ line.includes('## technical')) {
438
+ inTechnicalDesign = true;
439
+ technicalDesignLines.push(lines[i]);
440
+ continue;
441
+ }
442
+ if (inTechnicalDesign) {
443
+ // Continue until we hit another major section or JSON
444
+ if (line.startsWith('{') && line.includes('"technical_design_result"')) {
445
+ break;
446
+ }
447
+ technicalDesignLines.push(lines[i]);
448
+ }
449
+ }
450
+ if (technicalDesignLines.length > 0) {
451
+ return technicalDesignLines.join('\n').trim();
452
+ }
453
+ // Ultimate fallback: return the whole response if it seems to contain design content
454
+ if (response.length > 100 &&
455
+ (response.toLowerCase().includes('architecture') ||
456
+ response.toLowerCase().includes('component') ||
457
+ response.toLowerCase().includes('database'))) {
458
+ return response.trim();
459
+ }
460
+ return null;
461
+ };
@@ -0,0 +1,12 @@
1
+ import type { FeatureInfo, UserStory, TestCase } from '../../types/features.js';
2
+ import { type ProductInfo } from '../../api/products.js';
3
+ export interface TechnicalDesignContext {
4
+ feature: FeatureInfo;
5
+ product: ProductInfo;
6
+ user_stories: UserStory[];
7
+ test_cases: TestCase[];
8
+ }
9
+ /**
10
+ * Fetch all technical design context information via MCP endpoints
11
+ */
12
+ export declare function fetchTechnicalDesignContext(featureId: string, verbose?: boolean): Promise<TechnicalDesignContext>;
@@ -0,0 +1,39 @@
1
+ import { logInfo, logError } from '../../utils/logger.js';
2
+ import { getFeature, getUserStories, getTestCases, } from '../../api/features/index.js';
3
+ import { getProduct } from '../../api/products.js';
4
+ /**
5
+ * Fetch all technical design context information via MCP endpoints
6
+ */
7
+ export async function fetchTechnicalDesignContext(featureId, verbose) {
8
+ try {
9
+ if (verbose) {
10
+ logInfo(`Fetching complete technical design context for feature: ${featureId}`);
11
+ }
12
+ // Fetch all required data in parallel for better performance
13
+ const [feature, user_stories, test_cases] = await Promise.all([
14
+ getFeature(featureId, verbose),
15
+ getUserStories(featureId, verbose),
16
+ getTestCases(featureId, verbose),
17
+ ]);
18
+ const product = await getProduct(feature.product_id, verbose);
19
+ if (verbose) {
20
+ logInfo(`✅ Technical design context fetched successfully:`);
21
+ logInfo(` Feature: ${feature.name}`);
22
+ logInfo(` Product: ${product.name}`);
23
+ logInfo(` User Stories: ${user_stories.length}`);
24
+ logInfo(` Test Cases: ${test_cases.length} (${test_cases.filter((tc) => tc.is_critical).length} critical)`);
25
+ logInfo(` Existing Technical Design: ${feature.technical_design ? 'Yes' : 'No'}`);
26
+ }
27
+ return {
28
+ feature,
29
+ product,
30
+ user_stories,
31
+ test_cases,
32
+ };
33
+ }
34
+ catch (error) {
35
+ const errorMessage = error instanceof Error ? error.message : String(error);
36
+ logError(`Failed to fetch technical design context: ${errorMessage}`);
37
+ throw new Error(`Context fetch failed: ${errorMessage}`);
38
+ }
39
+ }
@@ -0,0 +1,17 @@
1
+ export interface HttpFallbackOptions {
2
+ featureId: string;
3
+ technicalDesign: string;
4
+ verbose?: boolean;
5
+ }
6
+ /**
7
+ * Save technical design via HTTP as a fallback when MCP server fails
8
+ */
9
+ export declare function saveTechnicalDesignViaHttp(options: HttpFallbackOptions): Promise<boolean>;
10
+ /**
11
+ * Verify that technical design was saved by retrieving it and optionally comparing content
12
+ */
13
+ export declare function verifyTechnicalDesignSaved(featureId: string, verbose?: boolean, expectedContent?: string): Promise<boolean>;
14
+ /**
15
+ * Enhanced save with retry logic and verification
16
+ */
17
+ export declare function saveTechnicalDesignWithRetry(options: HttpFallbackOptions, maxRetries?: number): Promise<boolean>;
@@ -0,0 +1,151 @@
1
+ import { logInfo, logError } from '../../utils/logger.js';
2
+ /**
3
+ * Save technical design via HTTP as a fallback when MCP server fails
4
+ */
5
+ export async function saveTechnicalDesignViaHttp(options) {
6
+ const { featureId, technicalDesign, verbose } = options;
7
+ const mcpServerUrl = process.env.MCP_SERVER_URL;
8
+ const mcpToken = process.env.MCP_TOKEN;
9
+ try {
10
+ if (verbose) {
11
+ logInfo('🔄 Attempting to save technical design via HTTP fallback...');
12
+ }
13
+ // Make direct HTTP call to the MCP server endpoint
14
+ const response = await fetch(`${mcpServerUrl}/mcp`, {
15
+ method: 'POST',
16
+ headers: {
17
+ 'Content-Type': 'application/json',
18
+ Authorization: `Bearer ${mcpToken}`,
19
+ },
20
+ body: JSON.stringify({
21
+ jsonrpc: '2.0',
22
+ method: 'features/update',
23
+ params: {
24
+ feature_id: featureId,
25
+ technical_design: technicalDesign,
26
+ },
27
+ id: Math.random().toString(36).substring(7),
28
+ }),
29
+ });
30
+ if (!response.ok) {
31
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
32
+ }
33
+ const data = await response.json();
34
+ if (data.error) {
35
+ throw new Error(data.error.message || 'HTTP call failed');
36
+ }
37
+ if (verbose) {
38
+ logInfo('✅ Technical design saved successfully via HTTP fallback');
39
+ }
40
+ return true;
41
+ }
42
+ catch (error) {
43
+ if (verbose) {
44
+ logError(`❌ HTTP fallback failed: ${error instanceof Error ? error.message : String(error)}`);
45
+ }
46
+ return false;
47
+ }
48
+ }
49
+ /**
50
+ * Verify that technical design was saved by retrieving it and optionally comparing content
51
+ */
52
+ export async function verifyTechnicalDesignSaved(featureId, verbose, expectedContent) {
53
+ try {
54
+ if (verbose) {
55
+ logInfo('🔍 Verifying technical design was saved...');
56
+ }
57
+ const mcpServerUrl = process.env.MCP_SERVER_URL;
58
+ const mcpToken = process.env.MCP_TOKEN;
59
+ const response = await fetch(`${mcpServerUrl}/mcp`, {
60
+ method: 'POST',
61
+ headers: {
62
+ 'Content-Type': 'application/json',
63
+ Authorization: `Bearer ${mcpToken}`,
64
+ },
65
+ body: JSON.stringify({
66
+ jsonrpc: '2.0',
67
+ method: 'features/get',
68
+ params: {
69
+ feature_id: featureId,
70
+ },
71
+ id: Math.random().toString(36).substring(7),
72
+ }),
73
+ });
74
+ if (!response.ok) {
75
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
76
+ }
77
+ const data = await response.json();
78
+ if (data.error) {
79
+ throw new Error(data.error.message || 'Verification failed');
80
+ }
81
+ const feature = data.result?.features?.[0];
82
+ const actualTechnicalDesign = feature?.technical_design;
83
+ const hasTechnicalDesign = actualTechnicalDesign && actualTechnicalDesign.trim().length > 0;
84
+ // If expected content is provided, compare it with actual content
85
+ if (expectedContent && hasTechnicalDesign) {
86
+ const contentMatches = actualTechnicalDesign.trim() === expectedContent.trim();
87
+ if (verbose) {
88
+ if (contentMatches) {
89
+ logInfo('✅ Technical design verified - content matches expected');
90
+ }
91
+ else {
92
+ logInfo('⚠️ Technical design exists but content differs from expected');
93
+ logInfo(`Expected length: ${expectedContent.length}, Actual length: ${actualTechnicalDesign.length}`);
94
+ }
95
+ }
96
+ return contentMatches;
97
+ }
98
+ if (verbose) {
99
+ if (hasTechnicalDesign) {
100
+ logInfo('✅ Technical design verified - successfully saved');
101
+ }
102
+ else {
103
+ logInfo('⚠️ Technical design verification failed - not found');
104
+ }
105
+ }
106
+ return hasTechnicalDesign;
107
+ }
108
+ catch (error) {
109
+ if (verbose) {
110
+ logError(`❌ Verification failed: ${error instanceof Error ? error.message : String(error)}`);
111
+ }
112
+ return false;
113
+ }
114
+ }
115
+ /**
116
+ * Enhanced save with retry logic and verification
117
+ */
118
+ export async function saveTechnicalDesignWithRetry(options, maxRetries = 3) {
119
+ const { verbose } = options;
120
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
121
+ if (verbose && attempt > 1) {
122
+ logInfo(`🔄 Retry attempt ${attempt} of ${maxRetries}...`);
123
+ }
124
+ const saved = await saveTechnicalDesignViaHttp(options);
125
+ if (saved) {
126
+ // Verify the save was successful
127
+ const verified = await verifyTechnicalDesignSaved(options.featureId, verbose);
128
+ if (verified) {
129
+ if (verbose) {
130
+ logInfo(`✅ Technical design successfully saved and verified (attempt ${attempt})`);
131
+ }
132
+ return true;
133
+ }
134
+ else if (verbose) {
135
+ logError(`❌ Save appeared successful but verification failed (attempt ${attempt})`);
136
+ }
137
+ }
138
+ if (attempt < maxRetries) {
139
+ // Wait before retrying
140
+ const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000); // Exponential backoff
141
+ if (verbose) {
142
+ logInfo(`⏳ Waiting ${delay}ms before retry...`);
143
+ }
144
+ await new Promise((resolve) => setTimeout(resolve, delay));
145
+ }
146
+ }
147
+ if (verbose) {
148
+ logError(`❌ Failed to save technical design after ${maxRetries} attempts`);
149
+ }
150
+ return false;
151
+ }