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.
Files changed (87) hide show
  1. package/README.md +17 -0
  2. package/dist/api/features/batch-operations.d.ts +16 -0
  3. package/dist/api/features/batch-operations.js +100 -0
  4. package/dist/api/features/index.d.ts +1 -0
  5. package/dist/api/features/index.js +1 -0
  6. package/dist/api/features/test-cases.d.ts +8 -0
  7. package/dist/api/features/test-cases.js +45 -0
  8. package/dist/api/features/user-stories.d.ts +8 -0
  9. package/dist/api/features/user-stories.js +45 -0
  10. package/dist/cli/commands/refactor-command.d.ts +2 -0
  11. package/dist/cli/commands/refactor-command.js +107 -0
  12. package/dist/cli/index.js +7 -0
  13. package/dist/cli.d.ts +2 -2
  14. package/dist/cli.js +4 -99
  15. package/dist/phases/code-implementation/analyzer-helpers.d.ts +28 -0
  16. package/dist/phases/code-implementation/analyzer-helpers.js +177 -0
  17. package/dist/phases/code-implementation/analyzer.d.ts +2 -0
  18. package/dist/phases/code-implementation/analyzer.js +308 -179
  19. package/dist/phases/code-implementation-verification/index.d.ts +1 -0
  20. package/dist/phases/code-implementation-verification/index.js +1 -0
  21. package/dist/phases/code-implementation-verification/verifier.d.ts +31 -0
  22. package/dist/phases/code-implementation-verification/verifier.js +196 -0
  23. package/dist/phases/feature-analysis/analyzer-helpers.d.ts +62 -0
  24. package/dist/phases/feature-analysis/analyzer-helpers.js +450 -0
  25. package/dist/phases/feature-analysis/analyzer.d.ts +1 -0
  26. package/dist/phases/feature-analysis/analyzer.js +132 -213
  27. package/dist/phases/feature-analysis-verification/index.d.ts +1 -0
  28. package/dist/phases/feature-analysis-verification/index.js +1 -0
  29. package/dist/phases/feature-analysis-verification/verifier.d.ts +37 -0
  30. package/dist/phases/feature-analysis-verification/verifier.js +147 -0
  31. package/dist/phases/pull-request/creator.js +10 -9
  32. package/dist/phases/technical-design/analyzer-helpers.d.ts +37 -0
  33. package/dist/phases/technical-design/analyzer-helpers.js +144 -0
  34. package/dist/phases/technical-design/analyzer.d.ts +3 -0
  35. package/dist/phases/technical-design/analyzer.js +282 -312
  36. package/dist/phases/technical-design-verification/index.d.ts +1 -0
  37. package/dist/phases/technical-design-verification/index.js +1 -0
  38. package/dist/phases/technical-design-verification/verifier.d.ts +36 -0
  39. package/dist/phases/technical-design-verification/verifier.js +147 -0
  40. package/dist/prompts/checklist-verification.d.ts +11 -0
  41. package/dist/prompts/checklist-verification.js +153 -0
  42. package/dist/prompts/code-implementation-improvement.d.ts +5 -0
  43. package/dist/prompts/code-implementation-improvement.js +108 -0
  44. package/dist/prompts/code-implementation-verification.d.ts +3 -0
  45. package/dist/prompts/code-implementation-verification.js +176 -0
  46. package/dist/prompts/feature-analysis-improvement.d.ts +8 -0
  47. package/dist/prompts/feature-analysis-improvement.js +109 -0
  48. package/dist/prompts/feature-analysis.js +1 -1
  49. package/dist/prompts/formatters.d.ts +17 -4
  50. package/dist/prompts/formatters.js +41 -12
  51. package/dist/prompts/technical-design-improvement.d.ts +5 -0
  52. package/dist/prompts/technical-design-improvement.js +93 -0
  53. package/dist/prompts/technical-design-verification.d.ts +11 -0
  54. package/dist/prompts/technical-design-verification.js +134 -0
  55. package/dist/prompts/technical-design.js +1 -1
  56. package/dist/services/audit-logs.d.ts +60 -0
  57. package/dist/services/audit-logs.js +115 -0
  58. package/dist/services/checklist.d.ts +1 -0
  59. package/dist/types/index.d.ts +19 -0
  60. package/dist/utils/image-downloader.d.ts +32 -0
  61. package/dist/utils/image-downloader.js +144 -0
  62. package/dist/workflow-runner/executors/phase-executor.js +56 -12
  63. package/package.json +1 -1
  64. package/dist/api/features.d.ts +0 -100
  65. package/dist/api/features.js +0 -219
  66. package/dist/logger.d.ts +0 -19
  67. package/dist/logger.js +0 -52
  68. package/dist/types.d.ts +0 -99
  69. package/dist/types.js +0 -1
  70. package/dist/workflow-runner/config/stage-configs.d.ts +0 -5
  71. package/dist/workflow-runner/config/stage-configs.js +0 -34
  72. package/dist/workflow-runner/core/feature-filter.test.d.ts +0 -4
  73. package/dist/workflow-runner/core/feature-filter.test.js +0 -127
  74. package/dist/workflow-runner/executors/stage-executor.d.ts +0 -8
  75. package/dist/workflow-runner/executors/stage-executor.js +0 -49
  76. package/dist/workflow-runner/feature-fetcher.d.ts +0 -41
  77. package/dist/workflow-runner/feature-fetcher.js +0 -121
  78. package/dist/workflow-runner/feature-service.d.ts +0 -17
  79. package/dist/workflow-runner/feature-service.js +0 -60
  80. package/dist/workflow-runner/pipeline.d.ts +0 -18
  81. package/dist/workflow-runner/pipeline.js +0 -197
  82. package/dist/workflow-runner/processor.d.ts +0 -40
  83. package/dist/workflow-runner/processor.js +0 -191
  84. package/dist/workflow-runner/status-updater.d.ts +0 -27
  85. package/dist/workflow-runner/status-updater.js +0 -80
  86. package/dist/workflow-runner/types.d.ts +0 -48
  87. package/dist/workflow-runner/types.js +0 -4
@@ -1,11 +1,13 @@
1
1
  import { query } from '@anthropic-ai/claude-code';
2
2
  import { logInfo, logError } from '../../utils/logger.js';
3
3
  import { saveTechnicalDesignViaHttp } from './http-fallback.js';
4
- import { fetchTechnicalDesignContext, } from './context-fetcher.js';
4
+ import { fetchTechnicalDesignContext } from './context-fetcher.js';
5
5
  import { updateTechnicalDesign } from '../../api/features/index.js';
6
6
  import { formatTechnicalDesignContext } from '../../prompts/formatters.js';
7
7
  import { createTechnicalDesignSystemPrompt, createTechnicalDesignPromptWithContext, } from '../../prompts/technical-design.js';
8
8
  import { formatChecklistsForContext, } from '../../services/checklist.js';
9
+ import { performVerificationCycle, buildDesignResult, buildVerificationFailureResult, buildNoResultsError, } from './analyzer-helpers.js';
10
+ import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
9
11
  function userMessage(content) {
10
12
  return {
11
13
  type: 'user',
@@ -23,351 +25,319 @@ export const generateTechnicalDesign = async (options, config, checklistContext)
23
25
  logInfo(`Using MCP server: ${mcpServerUrl}`);
24
26
  }
25
27
  try {
26
- // Fetch all required context information via MCP endpoints
27
- if (verbose) {
28
- logInfo('Fetching technical design context via MCP endpoints...');
29
- }
30
- const context = await fetchTechnicalDesignContext(mcpServerUrl, mcpToken, featureId, verbose);
28
+ // Fetch and prepare context
29
+ const context = await prepareDesignContext(mcpServerUrl, mcpToken, featureId, checklistContext, verbose);
31
30
  const systemPrompt = createTechnicalDesignSystemPrompt(config, mcpServerUrl, mcpToken, featureId);
32
- const contextInfo = formatTechnicalDesignContext(context);
33
- // Add checklist context to the design prompt
34
- let finalContextInfo = contextInfo;
35
- if (checklistContext && checklistContext.checklists.length > 0) {
36
- const checklistInfo = formatChecklistsForContext(checklistContext);
37
- finalContextInfo = contextInfo + '\n\n' + checklistInfo;
38
- if (verbose) {
39
- logInfo(`Added ${checklistContext.checklists.length} checklists to design context`);
40
- }
41
- }
42
- const designPrompt = createTechnicalDesignPromptWithContext(featureId, finalContextInfo);
43
- let lastAssistantResponse = '';
31
+ const initialDesignPrompt = context.designPrompt;
32
+ const maxIterations = options.maxVerificationIterations || 10;
33
+ let currentIteration = 0;
34
+ let currentPrompt = initialDesignPrompt;
44
35
  let structuredDesignResult = null;
36
+ let verificationResult = null;
45
37
  if (verbose) {
46
38
  logInfo('Starting Claude Code query with pre-fetched information...');
47
39
  }
48
- // Use Claude Code SDK without MCP servers - all info is pre-fetched
49
- for await (const message of query({
50
- prompt: prompt(designPrompt),
51
- options: {
52
- appendSystemPrompt: systemPrompt,
53
- model: config.claude.model || 'sonnet',
54
- maxTurns: 1000,
55
- permissionMode: 'bypassPermissions',
56
- },
57
- })) {
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(mcpServerUrl, mcpToken, {
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(mcpServerUrl, mcpToken, {
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
58
86
  if (verbose) {
59
- logInfo(`Received message type: ${message.type}`);
87
+ logInfo(`💾 Saving technical design (iteration ${currentIteration})...`);
60
88
  }
61
- // Stream the technical design generation process
62
- if (message.type === 'assistant' && message.message?.content) {
63
- for (const content of message.message.content) {
64
- if (content.type === 'text') {
65
- lastAssistantResponse += content.text + '\n';
66
- if (verbose) {
67
- console.log(`\n🤖 ${content.text}`);
68
- }
89
+ await saveTechnicalDesign(mcpServerUrl, mcpToken, featureId, structuredDesignResult.technical_design, verbose);
90
+ // Perform verification cycle
91
+ const verificationCycle = await performVerificationCycle(structuredDesignResult.technical_design, checklistContext || null, featureId, context.featureName, context.featureDescription, config, currentIteration, maxIterations, mcpServerUrl, mcpToken, 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(mcpServerUrl, mcpToken, featureId, checklistContext, verbose) {
147
+ if (verbose) {
148
+ logInfo('Fetching technical design context via MCP endpoints...');
149
+ }
150
+ const context = await fetchTechnicalDesignContext(mcpServerUrl, mcpToken, 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
+ // Add checklist context to the design prompt
159
+ let finalContextInfo = contextInfo;
160
+ if (checklistContext && checklistContext.checklists.length > 0) {
161
+ const checklistInfo = formatChecklistsForContext(checklistContext);
162
+ finalContextInfo = contextInfo + '\n\n' + checklistInfo;
163
+ if (verbose) {
164
+ logInfo(`Added ${checklistContext.checklists.length} checklists to design context`);
165
+ }
166
+ }
167
+ const designPrompt = createTechnicalDesignPromptWithContext(featureId, finalContextInfo);
168
+ return {
169
+ featureName: context.feature.name,
170
+ featureDescription: context.feature.description || undefined,
171
+ designPrompt,
172
+ };
173
+ }
174
+ /**
175
+ * Execute design query and parse result
176
+ */
177
+ async function executeDesignQuery(designPrompt, systemPrompt, config, verbose) {
178
+ let lastAssistantResponse = '';
179
+ let structuredDesignResult = null;
180
+ if (verbose) {
181
+ logInfo('🤖 Starting design agent query...');
182
+ }
183
+ // Use Claude Code SDK without MCP servers - all info is pre-fetched
184
+ for await (const message of query({
185
+ prompt: prompt(designPrompt),
186
+ options: {
187
+ appendSystemPrompt: systemPrompt,
188
+ model: config.claude.model || 'sonnet',
189
+ maxTurns: 1000,
190
+ permissionMode: 'bypassPermissions',
191
+ },
192
+ })) {
193
+ if (verbose) {
194
+ logInfo(` Received message type: ${message.type}`);
195
+ }
196
+ // Stream the technical design generation process
197
+ if (message.type === 'assistant' && message.message?.content) {
198
+ for (const content of message.message.content) {
199
+ if (content.type === 'text') {
200
+ lastAssistantResponse += content.text + '\n';
201
+ if (verbose) {
202
+ console.log(`\n🤖 ${content.text}`);
69
203
  }
70
- else if (content.type === 'tool_use') {
71
- if (verbose) {
72
- console.log(`\n🔧 ${content.name}: ${content.input.description || 'Running...'}`);
73
- }
204
+ }
205
+ else if (content.type === 'tool_use') {
206
+ if (verbose) {
207
+ console.log(`\n🔧 ${content.name}: ${content.input.description || 'Running...'}`);
74
208
  }
75
209
  }
76
210
  }
77
- if (message.type === 'result') {
78
- if (message.subtype === 'success') {
79
- logInfo('\n🎨 Technical design generation completed, parsing results...');
80
- try {
81
- // Try to extract JSON from markdown code block or parse directly
82
- const responseText = message.result || lastAssistantResponse;
83
- let jsonResult = null;
84
- // DEBUG: Log response text details
85
- console.log('=== DEBUG: Response Text Analysis ===');
86
- console.log('message.result exists:', !!message.result);
87
- console.log('lastAssistantResponse exists:', !!lastAssistantResponse);
88
- console.log('responseText length:', responseText.length);
89
- console.log('responseText first 200 chars:', JSON.stringify(responseText.substring(0, 200)));
90
- console.log('responseText last 200 chars:', JSON.stringify(responseText.substring(responseText.length - 200)));
91
- console.log('Contains ```json:', responseText.includes('```json'));
92
- console.log('Contains technical_design_result:', responseText.includes('"technical_design_result"'));
93
- // First try to extract JSON from markdown code block
94
- const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
95
- console.log('=== DEBUG: Markdown Block Match ===');
96
- console.log('jsonBlockMatch found:', !!jsonBlockMatch);
97
- if (jsonBlockMatch) {
98
- console.log('Matched JSON length:', jsonBlockMatch[1].length);
99
- console.log('Matched JSON first 100 chars:', JSON.stringify(jsonBlockMatch[1].substring(0, 100)));
100
- jsonResult = JSON.parse(jsonBlockMatch[1]);
101
- }
102
- else {
103
- console.log('=== DEBUG: JSON Object Search ===');
104
- // Try to find JSON object containing "technical_design_result"
105
- const lines = responseText.split('\n');
106
- let jsonStartIndex = -1;
107
- console.log('Total lines:', lines.length);
108
- console.log('Lines preview:', lines
109
- .slice(0, 10)
110
- .map((line, i) => `${i}: ${JSON.stringify(line.substring(0, 50))}`));
111
- // Find the line that contains the start of a JSON object with "technical_design_result"
112
- for (let i = 0; i < lines.length; i++) {
113
- const line = lines[i].trim();
114
- if (line.startsWith('{') &&
115
- responseText.includes('"technical_design_result"')) {
116
- console.log(`Found { at line ${i}: ${JSON.stringify(line)}`);
117
- const fromThisLine = lines.slice(i).join('\n');
118
- if (fromThisLine.includes('"technical_design_result"')) {
119
- jsonStartIndex = responseText.indexOf(lines[i]);
120
- console.log(`JSON start found at line ${i}, responseText index: ${jsonStartIndex}`);
121
- break;
122
- }
211
+ }
212
+ if (message.type === 'result') {
213
+ if (message.subtype === 'success') {
214
+ logInfo('\n🎨 Technical design generation completed, parsing results...');
215
+ try {
216
+ // Try to extract JSON from markdown code block or parse directly
217
+ const responseText = message.result || lastAssistantResponse;
218
+ let jsonResult = null;
219
+ // First try to extract JSON from markdown code block
220
+ const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
221
+ if (jsonBlockMatch) {
222
+ jsonResult = JSON.parse(jsonBlockMatch[1]);
223
+ }
224
+ else {
225
+ // Try to find JSON object containing "technical_design_result"
226
+ let jsonStartIndex = -1;
227
+ // Find the position of "technical_design_result" and work backwards to find the opening brace
228
+ if (responseText.includes('"technical_design_result"')) {
229
+ const targetIndex = responseText.indexOf('"technical_design_result"');
230
+ // Work backwards from "technical_design_result" to find the opening '{'
231
+ for (let i = targetIndex; i >= 0; i--) {
232
+ if (responseText[i] === '{') {
233
+ jsonStartIndex = i;
234
+ break;
123
235
  }
124
236
  }
125
- if (jsonStartIndex !== -1) {
126
- // Find the complete JSON object starting from this position
127
- let braceCount = 0;
128
- let endIndex = jsonStartIndex;
129
- let inString = false;
130
- let escapeNext = false;
131
- for (let i = jsonStartIndex; i < responseText.length; i++) {
132
- const char = responseText[i];
133
- if (escapeNext) {
134
- escapeNext = false;
135
- continue;
136
- }
137
- if (char === '\\' && inString) {
138
- escapeNext = true;
139
- continue;
140
- }
141
- if (char === '"') {
142
- inString = !inString;
143
- continue;
237
+ }
238
+ if (jsonStartIndex !== -1) {
239
+ // Find the complete JSON object starting from this position
240
+ let braceCount = 0;
241
+ let endIndex = jsonStartIndex;
242
+ let inString = false;
243
+ let escapeNext = false;
244
+ for (let i = jsonStartIndex; i < responseText.length; i++) {
245
+ const char = responseText[i];
246
+ if (escapeNext) {
247
+ escapeNext = false;
248
+ continue;
249
+ }
250
+ if (char === '\\' && inString) {
251
+ escapeNext = true;
252
+ continue;
253
+ }
254
+ if (char === '"') {
255
+ inString = !inString;
256
+ continue;
257
+ }
258
+ if (!inString) {
259
+ if (char === '{') {
260
+ braceCount++;
144
261
  }
145
- if (!inString) {
146
- if (char === '{') {
147
- braceCount++;
148
- }
149
- else if (char === '}') {
150
- braceCount--;
151
- if (braceCount === 0) {
152
- endIndex = i;
153
- break;
154
- }
262
+ else if (char === '}') {
263
+ braceCount--;
264
+ if (braceCount === 0) {
265
+ endIndex = i;
266
+ break;
155
267
  }
156
268
  }
157
269
  }
158
- const jsonStr = responseText.substring(jsonStartIndex, endIndex + 1);
159
- console.log('=== DEBUG: JSON Extraction ===');
160
- console.log('JSON start index:', jsonStartIndex);
161
- console.log('JSON end index:', endIndex);
162
- console.log('Extracted JSON length:', jsonStr.length);
163
- console.log('Extracted JSON first 100 chars:', JSON.stringify(jsonStr.substring(0, 100)));
164
- console.log('Extracted JSON last 100 chars:', JSON.stringify(jsonStr.substring(jsonStr.length - 100)));
165
- try {
166
- jsonResult = JSON.parse(jsonStr);
167
- console.log('JSON parsing succeeded!');
168
- }
169
- catch (parseError) {
170
- console.log('Failed to parse extracted JSON:', parseError);
171
- console.log('Extracted JSON string:', jsonStr.substring(0, 200) + '...');
172
- console.log('=== DEBUG: Fallback to full response ===');
173
- console.log('Attempting to parse entire responseText as JSON');
174
- // Try to parse the entire response as JSON as fallback
175
- jsonResult = JSON.parse(responseText);
176
- }
177
270
  }
178
- else {
179
- console.log('=== DEBUG: No JSON start found ===');
180
- console.log('Falling back to parse entire responseText as JSON');
181
- // Try to parse the entire response as JSON
182
- jsonResult = JSON.parse(responseText);
271
+ const jsonStr = responseText.substring(jsonStartIndex, endIndex + 1);
272
+ try {
273
+ jsonResult = JSON.parse(jsonStr);
183
274
  }
184
- }
185
- console.log('=== DEBUG: Final Result Processing ===');
186
- console.log('jsonResult exists:', !!jsonResult);
187
- console.log('jsonResult has technical_design_result:', !!(jsonResult && jsonResult.technical_design_result));
188
- if (jsonResult && jsonResult.technical_design_result) {
189
- console.log('checklist_item_results in parsed JSON:', !!jsonResult.technical_design_result.checklist_item_results);
190
- if (jsonResult.technical_design_result.checklist_item_results) {
191
- console.log('checklist_item_results length:', jsonResult.technical_design_result.checklist_item_results
192
- .length);
275
+ catch (parseError) {
276
+ logError(`Failed to parse extracted JSON: ${parseError}`);
277
+ // Try to parse the entire response as JSON as fallback
278
+ jsonResult = JSON.parse(responseText);
193
279
  }
194
- structuredDesignResult = jsonResult.technical_design_result;
195
280
  }
196
281
  else {
197
- throw new Error('Invalid JSON structure');
282
+ // Try to parse the entire response as JSON
283
+ jsonResult = JSON.parse(responseText);
198
284
  }
199
285
  }
200
- catch (error) {
201
- logError(`Failed to parse structured design result: ${error}`);
202
- // Extract technical design from response if JSON parsing fails
203
- const extractedDesign = extractTechnicalDesignFromResponse(message.result || lastAssistantResponse);
204
- structuredDesignResult = {
205
- status: extractedDesign ? 'success' : 'error',
206
- technical_design: extractedDesign,
207
- summary: extractedDesign
208
- ? 'Technical design generated successfully'
209
- : 'Failed to generate technical design',
210
- };
211
- }
212
- }
213
- else {
214
- logError(`\n⚠️ Technical design generation incomplete: ${message.subtype}`);
215
- if (message.subtype === 'error_max_turns') {
216
- logError('💡 Try increasing timeout or reducing complexity');
286
+ if (jsonResult && jsonResult.technical_design_result) {
287
+ structuredDesignResult = jsonResult.technical_design_result;
217
288
  }
218
- // Try to parse results from the last assistant response
219
- if (lastAssistantResponse) {
220
- try {
221
- const responseText = lastAssistantResponse;
222
- let jsonResult = null;
223
- const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
224
- if (jsonBlockMatch) {
225
- jsonResult = JSON.parse(jsonBlockMatch[1]);
226
- if (jsonResult && jsonResult.technical_design_result) {
227
- structuredDesignResult = jsonResult.technical_design_result;
228
- }
229
- }
230
- else {
231
- // Try to find JSON object containing "technical_design_result"
232
- const lines = responseText.split('\n');
233
- let jsonStartIndex = -1;
234
- // Find the line that contains the start of a JSON object with "technical_design_result"
235
- for (let i = 0; i < lines.length; i++) {
236
- const line = lines[i].trim();
237
- if (line.startsWith('{') &&
238
- responseText.includes('"technical_design_result"')) {
239
- const fromThisLine = lines.slice(i).join('\n');
240
- if (fromThisLine.includes('"technical_design_result"')) {
241
- jsonStartIndex = responseText.indexOf(lines[i]);
242
- break;
243
- }
244
- }
245
- }
246
- if (jsonStartIndex !== -1) {
247
- // Find the complete JSON object starting from this position
248
- let braceCount = 0;
249
- let endIndex = jsonStartIndex;
250
- let inString = false;
251
- let escapeNext = false;
252
- for (let i = jsonStartIndex; i < responseText.length; i++) {
253
- const char = responseText[i];
254
- if (escapeNext) {
255
- escapeNext = false;
256
- continue;
257
- }
258
- if (char === '\\' && inString) {
259
- escapeNext = true;
260
- continue;
261
- }
262
- if (char === '"') {
263
- inString = !inString;
264
- continue;
265
- }
266
- if (!inString) {
267
- if (char === '{') {
268
- braceCount++;
269
- }
270
- else if (char === '}') {
271
- braceCount--;
272
- if (braceCount === 0) {
273
- endIndex = i;
274
- break;
275
- }
276
- }
277
- }
278
- }
279
- const jsonStr = responseText.substring(jsonStartIndex, endIndex + 1);
280
- try {
281
- jsonResult = JSON.parse(jsonStr);
282
- if (jsonResult && jsonResult.technical_design_result) {
283
- structuredDesignResult =
284
- jsonResult.technical_design_result;
285
- }
286
- }
287
- catch (parseError) {
288
- logError(`Failed to parse extracted JSON: ${parseError}`);
289
- }
290
- }
291
- if (!structuredDesignResult) {
292
- const extractedDesign = extractTechnicalDesignFromResponse(lastAssistantResponse);
293
- if (extractedDesign) {
294
- structuredDesignResult = {
295
- status: 'success',
296
- technical_design: extractedDesign,
297
- summary: 'Technical design generated successfully',
298
- };
299
- }
300
- }
301
- }
302
- }
303
- catch (error) {
304
- logError(`Failed to parse assistant response: ${error}`);
305
- }
289
+ else {
290
+ throw new Error('Invalid JSON structure');
306
291
  }
307
292
  }
308
- }
309
- }
310
- // Save the technical design if we have it
311
- if (structuredDesignResult?.technical_design) {
312
- if (verbose) {
313
- logInfo('Saving technical design...');
314
- }
315
- const designSaved = await updateTechnicalDesign(mcpServerUrl, mcpToken, featureId, structuredDesignResult.technical_design, verbose);
316
- // Try HTTP fallback if direct update failed
317
- if (!designSaved) {
318
- if (verbose) {
319
- logInfo('Direct update failed, trying HTTP fallback...');
293
+ catch (error) {
294
+ logError(`Failed to parse structured design result: ${error}`);
295
+ // Extract technical design from response if JSON parsing fails
296
+ const extractedDesign = extractTechnicalDesignFromResponse(message.result || lastAssistantResponse);
297
+ structuredDesignResult = {
298
+ status: extractedDesign ? 'success' : 'error',
299
+ technical_design: extractedDesign,
300
+ summary: extractedDesign
301
+ ? 'Technical design generated successfully'
302
+ : 'Failed to generate technical design',
303
+ };
320
304
  }
321
- const fallbackSaved = await saveTechnicalDesignViaHttp({
322
- mcpServerUrl,
323
- mcpToken,
324
- featureId,
325
- technicalDesign: structuredDesignResult.technical_design,
326
- verbose,
327
- });
328
- if (!fallbackSaved && verbose) {
329
- logError('⚠️ Both direct update and HTTP fallback failed');
305
+ }
306
+ else {
307
+ logError(`\n⚠️ Technical design generation incomplete: ${message.subtype}`);
308
+ if (message.subtype === 'error_max_turns') {
309
+ logError('💡 Try increasing timeout or reducing complexity');
330
310
  }
331
- return {
332
- featureId,
333
- technicalDesign: structuredDesignResult.technical_design,
334
- status: fallbackSaved ? 'success' : 'error',
335
- summary: fallbackSaved
336
- ? 'Technical design generated and saved via HTTP fallback'
337
- : 'Technical design generated but failed to save',
338
- savedViaHttp: fallbackSaved,
339
- data: {
340
- checklist_item_results: structuredDesignResult.checklist_item_results || [],
341
- },
342
- };
343
311
  }
344
- return {
345
- featureId,
346
- technicalDesign: structuredDesignResult.technical_design,
347
- status: 'success',
348
- summary: 'Technical design generated and saved successfully',
349
- data: {
350
- checklist_item_results: structuredDesignResult.checklist_item_results || [],
351
- },
352
- };
353
312
  }
354
- return {
355
- featureId,
356
- technicalDesign: null,
357
- status: 'error',
358
- summary: 'No technical design was generated',
359
- };
360
313
  }
361
- catch (error) {
362
- logError(`Technical design generation failed: ${error instanceof Error ? error.message : String(error)}`);
363
- return {
314
+ return structuredDesignResult;
315
+ }
316
+ /**
317
+ * Save technical design document
318
+ */
319
+ async function saveTechnicalDesign(mcpServerUrl, mcpToken, featureId, technicalDesign, verbose) {
320
+ if (verbose) {
321
+ logInfo('Saving technical design...');
322
+ }
323
+ const designSaved = await updateTechnicalDesign(mcpServerUrl, mcpToken, featureId, technicalDesign, verbose);
324
+ // Try HTTP fallback if direct update failed
325
+ if (!designSaved) {
326
+ if (verbose) {
327
+ logInfo('Direct update failed, trying HTTP fallback...');
328
+ }
329
+ const fallbackSaved = await saveTechnicalDesignViaHttp({
330
+ mcpServerUrl,
331
+ mcpToken,
364
332
  featureId,
365
- technicalDesign: null,
366
- status: 'error',
367
- summary: `Generation failed: ${error instanceof Error ? error.message : String(error)}`,
368
- };
333
+ technicalDesign,
334
+ verbose,
335
+ });
336
+ if (!fallbackSaved && verbose) {
337
+ logError('⚠️ Both direct update and HTTP fallback failed');
338
+ }
369
339
  }
370
- };
340
+ }
371
341
  const extractTechnicalDesignFromResponse = (response) => {
372
342
  // Try to extract technical design content from the response
373
343
  // Look for markdown sections that contain technical design