import { parseSearchReplacePatchResponse } from '../patch-response-parser' import type { CodePatch, CodePatchSet } from './types' export function parsePatchResponse(text: string): CodePatchSet { const patchSet = parseSearchReplacePatchResponse(text, { responseLabel: 'Code retry patch', blockLabel: 'Code retry patch block', emptyError: 'Code retry patch response was empty', noPatchBlocksError: 'Code retry patch response did not contain any [[PATCH]] blocks', noopBehavior: 'error', noopError: 'Code retry patch produced no change', allowMalformedTrailingBlock: true }) if (patchSet.patches.length === 0) { throw new Error('Code retry patch response missing patches') } return patchSet } function getLineNumberAtIndex(text: string, index: number): number { return text.slice(0, index).split('\n').length } export function extractTargetLine(errorMessage: string): number | undefined { const match = errorMessage.match(/line\s+(\d+)/i) || errorMessage.match(/:(\d+)(?::\d+)?/) if (!match) { return undefined } const line = Number.parseInt(match[1], 10) return Number.isFinite(line) && line > 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) }