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.
- 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 +123 -0
- package/dist/cli/formatters/formatter-utils.d.ts +23 -0
- package/dist/cli/formatters/formatter-utils.js +67 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/utils/command-handler.d.ts +23 -0
- package/dist/cli/utils/command-handler.js +39 -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 +304 -175
- 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 -219
- 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 +2 -1
- 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 -318
- 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/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/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/utils/image-processor.d.ts +0 -5
- package/dist/utils/image-processor.js +0 -55
- 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,357 +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
|
-
|
|
35
|
-
|
|
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
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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(
|
|
87
|
+
logInfo(`💾 Saving technical design (iteration ${currentIteration})...`);
|
|
66
88
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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 (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
282
|
+
// Try to parse the entire response as JSON
|
|
283
|
+
jsonResult = JSON.parse(responseText);
|
|
204
284
|
}
|
|
205
285
|
}
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
225
|
-
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
|
372
|
-
|
|
373
|
-
|
|
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
|