edsger 0.2.3 → 0.2.5

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