const fs = require('fs'); const content = fs.readFileSync('temp/rich-outline-fail_57_1779015509994.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); // Let me try a completely different approach: check if there's a smarter fix // I'll look for ALL occurrences of },{ followed by an unquoted property name // First, let's find what property names are used after },{ const allCases = []; const regex = /\},\{([^"]+):/g; let match; while ((match = regex.exec(aiResponse)) !== null) { allCases.push({pos: match.index, prop: match[1]}); } console.log('Found', allCases.length, 'cases of },{propName:'); if (allCases.length < 20) { allCases.forEach(c => console.log(' pos', c.pos, ':', c.prop)); } // Check if maybe there are nested issues // Let's try parsing from the beginning but stop at position 1624 const truncatedAtError = aiResponse.substring(0, 1624) + '"}]}]}'; // Close all open structures try { JSON.parse(truncatedAtError); console.log('Truncated parse: SUCCESS'); } catch(e) { console.log('Truncated parse: FAILED -', e.message.substring(0, 80)); } // Try a different fix: replace ALL },{ with },{" const fixedV2 = aiResponse.replace(/\},\{([a-zA-Z_])/g, '},{"$1'); try { const data = JSON.parse(fixedV2); console.log('\nFix v2: SUCCESS, chapters:', data.chapters?.length); } catch(e) { console.log('\nFix v2: FAILED -', e.message.substring(0, 80)); // Find which occurrence is problematic const fixedPos = parseInt(e.message.match(/position (\d+)/)?.[1] || '0'); console.log('Error at position', fixedPos); console.log('Context:', JSON.stringify(fixedV2.substring(Math.max(0, fixedPos-50), fixedPos+50))); } // Let's also check for a different issue - what if there's a missing comma between array elements? // Check for patterns like "keyPoints":["foo","bar"]}{"next" const badTransition = /\]\}\{/g; const badTransitions = aiResponse.match(badTransition); console.log('\nFound', badTransitions ? badTransitions.length : 0, 'instances of ]}{ (missing comma)'); // Let's check if the entire JSON is actually truncated - look at the very end console.log('\n=== End of JSON ==='); console.log('Last 200 chars:', JSON.stringify(aiResponse.substring(aiResponse.length - 200))); // Try parsing from start, but let's see if we can find any workaround // What if we just try to extract the chapters array without the outer structure? const chaptersMatch = aiResponse.match(/"chapters"\s*:\s*\[([\s\S]*)\]\s*\}$/); if (chaptersMatch) { console.log('\nFound chapters array match, length:', chaptersMatch[1].length); try { const chaptersStr = '[{"number":1' + chaptersMatch[1]; const data = JSON.parse(chaptersStr); console.log('Chapters extraction: SUCCESS, count:', data.length); } catch(e) { console.log('Chapters extraction: FAILED -', e.message.substring(0, 80)); } } else { console.log('\nNo chapters array found at expected location'); }