import type { CodePatch, CodePatchSet } from './types' function stripCodeFence(text: string): string { return text .replace(/^```(?:json|text)?\s*/i, '') .replace(/\s*```$/i, '') .trim() } function trimPatchBoundary(text: string): string { return text .replace(/^\s*\r?\n/, '') .replace(/\r?\n\s*$/, '') .replace(/\r\n/g, '\n') } export function parsePatchResponse(text: string): CodePatchSet { const normalized = stripCodeFence(text) if (!normalized) { throw new Error('Code retry patch response was empty') } if (/^\s* 0 ? line : undefined } export function applyPatchToCode(code: string, patch: CodePatch, targetLine?: number): string { const matches: number[] = [] let searchIndex = 0 while (true) { const foundAt = code.indexOf(patch.originalSnippet, searchIndex) if (foundAt < 0) { break } matches.push(foundAt) searchIndex = foundAt + Math.max(1, patch.originalSnippet.length) } if (matches.length === 0) { throw new Error('Code retry patch original_snippet not found in code') } const bestIndex = typeof targetLine === 'number' ? matches.reduce((best, current) => { const bestDistance = Math.abs(getLineNumberAtIndex(code, best) - targetLine) const currentDistance = Math.abs(getLineNumberAtIndex(code, current) - targetLine) return currentDistance < bestDistance ? current : best }) : matches[0] return `${code.slice(0, bestIndex)}${patch.replacementSnippet}${code.slice(bestIndex + patch.originalSnippet.length)}` } export function applyPatchSetToCode(code: string, patchSet: CodePatchSet, targetLine?: number): string { return patchSet.patches.reduce((currentCode, patch, index) => { const lineHint = index === 0 ? targetLine : undefined return applyPatchToCode(currentCode, patch, lineHint) }, code) } export function getErrorType(stderr: string): string { if (!stderr) return 'Unknown' const errorPatterns = [ { name: 'NameError', pattern: /NameError/i }, { name: 'SyntaxError', pattern: /SyntaxError/i }, { name: 'AttributeError', pattern: /AttributeError/i }, { name: 'ImportError', pattern: /ImportError/i }, { name: 'TypeError', pattern: /TypeError/i }, { name: 'ValueError', pattern: /ValueError/i }, { name: 'RuntimeError', pattern: /RuntimeError/i }, { name: 'IndentationError', pattern: /IndentationError/i } ] for (const { name, pattern } of errorPatterns) { if (pattern.test(stderr)) { return name } } return 'Unknown' } export function extractErrorMessage(stderr: string): string { if (!stderr) return 'Unknown error' const lines = stderr.trim().split('\n') const lastLine = lines[lines.length - 1]?.trim() return lastLine || stderr.slice(0, 500) }