const fs = require('fs'); const content = fs.readFileSync('temp/rich-outline-fail_57_1779017412608.txt', 'utf-8'); const marker = '========== AI εŽŸε§‹ε“εΊ” '; const markerIdx = content.indexOf(marker); const headerEndIdx = content.indexOf(' ==========', markerIdx); const aiStart = content.indexOf('\n', headerEndIdx) + 1; const aiEnd = content.indexOf('\n\n', aiStart); const aiResponse = content.substring(aiStart, aiEnd); console.log('AI response length:', aiResponse.length); // Check if the new fail file has a different error const pos = parseInt(content.match(/position (\d+)/)?.[1] || '0'); console.log('Error position in file:', pos); // Check for truncation let openBraces = 0, closeBraces = 0; let inString = false, escapeNext = false; for (let i = 0; i < aiResponse.length; i++) { const ch = aiResponse[i]; if (escapeNext) { escapeNext = false; continue; } if (ch === '\\') { escapeNext = true; continue; } if (ch === '"') { inString = !inString; continue; } if (inString) continue; if (ch === '{') openBraces++; else if (ch === '}') closeBraces++; } console.log('Open braces:', openBraces, 'Close:', closeBraces, 'Diff:', openBraces - closeBraces); console.log('Ends with }:', aiResponse.trim().endsWith('}')); try { JSON.parse(aiResponse); console.log('Raw parse: SUCCESS'); } catch(e) { console.log('Raw parse FAILED:', e.message.substring(0, 100)); // Check position const errorPos = parseInt(e.message.match(/position (\d+)/)?.[1] || '0'); if (errorPos > 0 && errorPos < aiResponse.length) { console.log('Context around error:'); console.log(JSON.stringify(aiResponse.substring(Math.max(0, errorPos-50), errorPos+50))); } } // Try with fixMissingQuotes function fixMissingQuotes(jsonStr) { return jsonStr .replace(/\{number":/g, '{"number":') .replace(/\{title":/g, '{"title":') .replace(/\{summary":/g, '{"summary":') .replace(/\{opening":/g, '{"opening":') .replace(/\{structure":/g, '{"structure":') .replace(/\{mustCover":/g, '{"mustCover":') .replace(/\{mustNotRepeat":/g, '{"mustNotRepeat":') .replace(/\{toneAdjustment":/g, '{"toneAdjustment":') .replace(/\{keyTakeaway":/g, '{"keyTakeaway":') .replace(/\{keyPoints":/g, '{"keyPoints":') .replace(/\{estimatedWords":/g, '{"estimatedWords":') .replace(/\{writingInstructions":/g, '{"writingInstructions":'); } const fixed = fixMissingQuotes(aiResponse); try { JSON.parse(fixed); console.log('Fixed parse: SUCCESS'); } catch(e) { console.log('Fixed parse FAILED:', e.message.substring(0, 100)); } // Check what strategy 9 would extract console.log('\n=== Testing strategy 9 extraction ==='); const chaptersArrayStart = aiResponse.indexOf('"chapters":['); if (chaptersArrayStart !== -1) { let bracketDepth = 0; let inString = false; let escapeNext = false; let arrayEnd = -1; for (let i = chaptersArrayStart + 12; i < aiResponse.length; i++) { const ch = aiResponse[i]; if (escapeNext) { escapeNext = false; continue; } if (ch === '\\') { escapeNext = true; continue; } if (ch === '"') { inString = !inString; continue; } if (inString) continue; if (ch === '[') bracketDepth++; else if (ch === ']') { bracketDepth--; if (bracketDepth === 0) { arrayEnd = i; break; } } } console.log('Chapters array ends at:', arrayEnd); const arrayContent = aiResponse.substring(chaptersArrayStart + 12, arrayEnd); const fixedContent = fixMissingQuotes(arrayContent); try { const chapters = JSON.parse('[' + fixedContent + ']'); console.log('Parsed chapters array: SUCCESS, count:', chapters.length); } catch(e) { console.log('Parsed chapters array: FAILED -', e.message.substring(0, 80)); // Try one by one const chapterMatches = fixedContent.match(/\{[^}]+\}/g) || []; console.log('Found', chapterMatches.length, 'chapter objects by regex'); let validChapters = 0; for (const chStr of chapterMatches) { try { JSON.parse(chStr); validChapters++; } catch(e) {} } console.log('Valid chapter objects:', validChapters); } }