mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 21:03:08 +00:00
fix(suggestions): Improve JSON extraction for Cursor responses
Cursor responses may include text after the JSON object, causing
JSON.parse to fail. Added multi-strategy extraction similar to
validate-issue.ts:
1. Try extracting from ```json code block
2. Try extracting from ``` code block
3. Try finding {"suggestions" and matching braces
4. Try finding any JSON object with suggestions array
Uses bracket counting to find the correct closing brace.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -289,11 +289,10 @@ ${JSON.stringify(suggestionsSchema, null, 2)}`;
|
|||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Fallback: try to parse from text (for backwards compatibility)
|
// Fallback: try to parse from text using multiple strategies
|
||||||
logger.warn('No structured output received, attempting to parse from text');
|
logger.warn('No structured output received, attempting to parse from text');
|
||||||
const jsonMatch = responseText.match(/\{[\s\S]*"suggestions"[\s\S]*\}/);
|
const parsed = extractSuggestionsJson(responseText);
|
||||||
if (jsonMatch) {
|
if (parsed && parsed.suggestions) {
|
||||||
const parsed = JSON.parse(jsonMatch[0]);
|
|
||||||
events.emit('suggestions:event', {
|
events.emit('suggestions:event', {
|
||||||
type: 'suggestions_complete',
|
type: 'suggestions_complete',
|
||||||
suggestions: parsed.suggestions.map((s: Record<string, unknown>, i: number) => ({
|
suggestions: parsed.suggestions.map((s: Record<string, unknown>, i: number) => ({
|
||||||
@@ -323,3 +322,99 @@ ${JSON.stringify(suggestionsSchema, null, 2)}`;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract suggestions JSON from response text using multiple strategies.
|
||||||
|
* Handles various formats: markdown code blocks, raw JSON, etc.
|
||||||
|
*/
|
||||||
|
function extractSuggestionsJson(
|
||||||
|
responseText: string
|
||||||
|
): { suggestions: Array<Record<string, unknown>> } | null {
|
||||||
|
const strategies = [
|
||||||
|
// Strategy 1: JSON in ```json code block
|
||||||
|
() => {
|
||||||
|
const match = responseText.match(/```json\s*([\s\S]*?)```/);
|
||||||
|
if (match) {
|
||||||
|
return JSON.parse(match[1].trim());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
// Strategy 2: JSON in ``` code block (no language specified)
|
||||||
|
() => {
|
||||||
|
const match = responseText.match(/```\s*([\s\S]*?)```/);
|
||||||
|
if (match) {
|
||||||
|
const content = match[1].trim();
|
||||||
|
if (content.startsWith('{') && content.includes('"suggestions"')) {
|
||||||
|
return JSON.parse(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
// Strategy 3: Find JSON object containing "suggestions" array
|
||||||
|
() => {
|
||||||
|
// Find the start of the JSON object
|
||||||
|
const startIdx = responseText.indexOf('{"suggestions"');
|
||||||
|
if (startIdx === -1) return null;
|
||||||
|
|
||||||
|
// Find matching closing brace by counting brackets
|
||||||
|
let depth = 0;
|
||||||
|
let endIdx = -1;
|
||||||
|
for (let i = startIdx; i < responseText.length; i++) {
|
||||||
|
if (responseText[i] === '{') depth++;
|
||||||
|
if (responseText[i] === '}') {
|
||||||
|
depth--;
|
||||||
|
if (depth === 0) {
|
||||||
|
endIdx = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endIdx > startIdx) {
|
||||||
|
return JSON.parse(responseText.slice(startIdx, endIdx));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
// Strategy 4: Find any JSON object with suggestions
|
||||||
|
() => {
|
||||||
|
const startIdx = responseText.indexOf('{');
|
||||||
|
if (startIdx === -1) return null;
|
||||||
|
|
||||||
|
// Find matching closing brace
|
||||||
|
let depth = 0;
|
||||||
|
let endIdx = -1;
|
||||||
|
for (let i = startIdx; i < responseText.length; i++) {
|
||||||
|
if (responseText[i] === '{') depth++;
|
||||||
|
if (responseText[i] === '}') {
|
||||||
|
depth--;
|
||||||
|
if (depth === 0) {
|
||||||
|
endIdx = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endIdx > startIdx) {
|
||||||
|
const parsed = JSON.parse(responseText.slice(startIdx, endIdx));
|
||||||
|
if (parsed.suggestions && Array.isArray(parsed.suggestions)) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const strategy of strategies) {
|
||||||
|
try {
|
||||||
|
const result = strategy();
|
||||||
|
if (result && result.suggestions && Array.isArray(result.suggestions)) {
|
||||||
|
logger.debug('Successfully extracted suggestions JSON');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Strategy failed, try next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user