edsger 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/api/features/batch-operations.d.ts +16 -0
- package/dist/api/features/batch-operations.js +100 -0
- package/dist/api/features/index.d.ts +1 -0
- package/dist/api/features/index.js +1 -0
- package/dist/api/features/test-cases.d.ts +8 -0
- package/dist/api/features/test-cases.js +45 -0
- package/dist/api/features/user-stories.d.ts +8 -0
- package/dist/api/features/user-stories.js +45 -0
- package/dist/cli/commands/refactor-command.d.ts +2 -0
- package/dist/cli/commands/refactor-command.js +107 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +4 -99
- package/dist/phases/code-implementation/analyzer-helpers.d.ts +28 -0
- package/dist/phases/code-implementation/analyzer-helpers.js +177 -0
- package/dist/phases/code-implementation/analyzer.d.ts +2 -0
- package/dist/phases/code-implementation/analyzer.js +308 -179
- package/dist/phases/code-implementation-verification/index.d.ts +1 -0
- package/dist/phases/code-implementation-verification/index.js +1 -0
- package/dist/phases/code-implementation-verification/verifier.d.ts +31 -0
- package/dist/phases/code-implementation-verification/verifier.js +196 -0
- package/dist/phases/feature-analysis/analyzer-helpers.d.ts +62 -0
- package/dist/phases/feature-analysis/analyzer-helpers.js +450 -0
- package/dist/phases/feature-analysis/analyzer.d.ts +1 -0
- package/dist/phases/feature-analysis/analyzer.js +132 -213
- package/dist/phases/feature-analysis-verification/index.d.ts +1 -0
- package/dist/phases/feature-analysis-verification/index.js +1 -0
- package/dist/phases/feature-analysis-verification/verifier.d.ts +37 -0
- package/dist/phases/feature-analysis-verification/verifier.js +147 -0
- package/dist/phases/pull-request/creator.js +10 -9
- package/dist/phases/technical-design/analyzer-helpers.d.ts +37 -0
- package/dist/phases/technical-design/analyzer-helpers.js +144 -0
- package/dist/phases/technical-design/analyzer.d.ts +3 -0
- package/dist/phases/technical-design/analyzer.js +282 -312
- package/dist/phases/technical-design-verification/index.d.ts +1 -0
- package/dist/phases/technical-design-verification/index.js +1 -0
- package/dist/phases/technical-design-verification/verifier.d.ts +36 -0
- package/dist/phases/technical-design-verification/verifier.js +147 -0
- package/dist/prompts/checklist-verification.d.ts +11 -0
- package/dist/prompts/checklist-verification.js +153 -0
- package/dist/prompts/code-implementation-improvement.d.ts +5 -0
- package/dist/prompts/code-implementation-improvement.js +108 -0
- package/dist/prompts/code-implementation-verification.d.ts +3 -0
- package/dist/prompts/code-implementation-verification.js +176 -0
- package/dist/prompts/feature-analysis-improvement.d.ts +8 -0
- package/dist/prompts/feature-analysis-improvement.js +109 -0
- package/dist/prompts/feature-analysis.js +1 -1
- package/dist/prompts/formatters.d.ts +17 -4
- package/dist/prompts/formatters.js +41 -12
- package/dist/prompts/technical-design-improvement.d.ts +5 -0
- package/dist/prompts/technical-design-improvement.js +93 -0
- package/dist/prompts/technical-design-verification.d.ts +11 -0
- package/dist/prompts/technical-design-verification.js +134 -0
- package/dist/prompts/technical-design.js +1 -1
- package/dist/services/audit-logs.d.ts +60 -0
- package/dist/services/audit-logs.js +115 -0
- package/dist/services/checklist.d.ts +1 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/utils/image-downloader.d.ts +32 -0
- package/dist/utils/image-downloader.js +144 -0
- package/dist/workflow-runner/executors/phase-executor.js +56 -12
- package/package.json +1 -1
- package/dist/api/features.d.ts +0 -100
- package/dist/api/features.js +0 -219
- package/dist/logger.d.ts +0 -19
- package/dist/logger.js +0 -52
- package/dist/types.d.ts +0 -99
- package/dist/types.js +0 -1
- package/dist/workflow-runner/config/stage-configs.d.ts +0 -5
- package/dist/workflow-runner/config/stage-configs.js +0 -34
- package/dist/workflow-runner/core/feature-filter.test.d.ts +0 -4
- package/dist/workflow-runner/core/feature-filter.test.js +0 -127
- package/dist/workflow-runner/executors/stage-executor.d.ts +0 -8
- package/dist/workflow-runner/executors/stage-executor.js +0 -49
- package/dist/workflow-runner/feature-fetcher.d.ts +0 -41
- package/dist/workflow-runner/feature-fetcher.js +0 -121
- package/dist/workflow-runner/feature-service.d.ts +0 -17
- package/dist/workflow-runner/feature-service.js +0 -60
- package/dist/workflow-runner/pipeline.d.ts +0 -18
- package/dist/workflow-runner/pipeline.js +0 -197
- package/dist/workflow-runner/processor.d.ts +0 -40
- package/dist/workflow-runner/processor.js +0 -191
- package/dist/workflow-runner/status-updater.d.ts +0 -27
- package/dist/workflow-runner/status-updater.js +0 -80
- package/dist/workflow-runner/types.d.ts +0 -48
- package/dist/workflow-runner/types.js +0 -4
|
@@ -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
|
|
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
|
|
27
|
-
|
|
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
|
|
33
|
-
|
|
34
|
-
let
|
|
35
|
-
|
|
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
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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(
|
|
87
|
+
logInfo(`💾 Saving technical design (iteration ${currentIteration})...`);
|
|
60
88
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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 (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
282
|
+
// Try to parse the entire response as JSON
|
|
283
|
+
jsonResult = JSON.parse(responseText);
|
|
198
284
|
}
|
|
199
285
|
}
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
219
|
-
|
|
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
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
|
366
|
-
|
|
367
|
-
|
|
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
|