{"version":3,"sources":["../../src/tools/fuzzy-edit.ts"],"sourcesContent":["import type { ToolUseBlock } from \"../types.js\";\n\nexport type ReplacerFn = (content: string, find: string) => Generator<string, void, unknown>;\n\nexport class FuzzyEditError extends Error {\n  constructor(public readonly kind: \"not_found\" | \"ambiguous\", message: string) {\n    super(message);\n    this.name = \"FuzzyEditError\";\n  }\n}\n\nfunction levenshtein(a: string, b: string): number {\n  const m = a.length;\n  const n = b.length;\n  if (m === 0) return n;\n  if (n === 0) return m;\n  if (Math.abs(m - n) > Math.max(m, n) * 0.5) return Math.max(m, n);\n  let prev = new Array(n + 1).fill(0) as number[];\n  let curr = new Array(n + 1).fill(0) as number[];\n  for (let j = 0; j <= n; j++) prev[j] = j;\n  for (let i = 1; i <= m; i++) {\n    curr[0] = i;\n    for (let j = 1; j <= n; j++) {\n      const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n      curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n    }\n    [prev, curr] = [curr, prev];\n  }\n  return prev[n];\n}\n\nfunction similarity(a: string, b: string): number {\n  if (a.length > 1000 || b.length > 1000) return a === b ? 1 : 0;\n  const maxLen = Math.max(a.length, b.length);\n  if (maxLen === 0) return 1;\n  return 1 - levenshtein(a, b) / maxLen;\n}\n\nfunction* simpleReplacer(content: string, find: string): Generator<string, void, unknown> {\n  if (content.includes(find)) yield find;\n}\n\nfunction* lineTrimmedReplacer(content: string, find: string): Generator<string, void, unknown> {\n  const lines = find.split(\"\\n\").map(l => l.trim());\n  const contentLines = content.split(\"\\n\");\n  for (let i = 0; i <= contentLines.length - lines.length; i++) {\n    if (contentLines.slice(i, i + lines.length).every((cl, idx) => cl.trim() === lines[idx])) {\n      yield contentLines.slice(i, i + lines.length).join(\"\\n\");\n    }\n  }\n}\n\nfunction* blockAnchorReplacer(content: string, find: string): Generator<string, void, unknown> {\n  const findLines = find.split(\"\\n\");\n  const contentLines = content.split(\"\\n\");\n\n  if (findLines.length < 2) return;\n\n  const firstLine = findLines[0];\n  const lastLine = findLines[findLines.length - 1];\n  const middleLines = findLines.slice(1, -1);\n\n  for (let i = 0; i <= contentLines.length - findLines.length; i++) {\n    if (contentLines[i] !== firstLine) continue;\n    const endIdx = i + findLines.length - 1;\n    if (contentLines[endIdx] !== lastLine) continue;\n\n    const candidates: { idx: number; score: number }[] = [];\n\n    for (let j = i + 1; j < endIdx; j++) {\n      candidates.push({ idx: j, score: 0 });\n    }\n\n    let totalScore = 0;\n    for (let k = 0; k < middleLines.length; k++) {\n      const contentLine = contentLines[i + 1 + k];\n      const sim = similarity(middleLines[k], contentLine);\n      totalScore += sim;\n      if (candidates[k]) candidates[k].score = sim;\n    }\n\n    const avgScore = totalScore / middleLines.length;\n    const threshold = candidates.length > 1 ? 0.3 : 0.0;\n\n    if (avgScore >= threshold) {\n      yield contentLines.slice(i, i + findLines.length).join(\"\\n\");\n    }\n  }\n}\n\nfunction* whitespaceNormalizedReplacer(content: string, find: string): Generator<string, void, unknown> {\n  const normFind = find.replace(/\\s+/g, \" \").trim();\n  if (!normFind) return;\n  const normContent = content.replace(/\\s+/g, \" \").trim();\n  const idx = normContent.indexOf(normFind);\n  if (idx === -1) return;\n\n  const firstWord = normFind.split(\" \")[0];\n  const lastWord = normFind.split(\" \").filter(Boolean).pop()!;\n  const firstIdx = content.indexOf(firstWord);\n  const lastIdx = content.lastIndexOf(lastWord);\n  if (firstIdx === -1 || lastIdx === -1 || lastIdx < firstIdx) return;\n\n  yield content.substring(firstIdx, lastIdx + lastWord.length);\n}\n\nfunction* indentationFlexibleReplacer(content: string, find: string): Generator<string, void, unknown> {\n  const lines = find.split(\"\\n\");\n  const minIndent = Math.min(...lines.filter(l => l.trim().length > 0).map(l => l.match(/^(\\s*)/)?.[1].length ?? 0));\n  const dedented = lines.map(l => l.substring(minIndent)).join(\"\\n\");\n  if (content.includes(dedented)) yield dedented;\n}\n\nfunction* escapeNormalizedReplacer(content: string, find: string): Generator<string, void, unknown> {\n  const normalized = find\n    .replace(/\\\\n/g, \"\\n\")\n    .replace(/\\\\t/g, \"\\t\")\n    .replace(/\\\\\"/g, '\"')\n    .replace(/\\\\'/g, \"'\");\n  if (content.includes(normalized)) yield normalized;\n}\n\nfunction* trimmedBoundaryReplacer(content: string, find: string): Generator<string, void, unknown> {\n  const trimmed = find.trim();\n  if (content.includes(trimmed)) yield trimmed;\n}\n\nfunction* contextAwareReplacer(content: string, find: string): Generator<string, void, unknown> {\n  const findLines = find.split(\"\\n\");\n  if (findLines.length < 3) return;\n\n  const firstLine = findLines[0].trim();\n  const lastLine = findLines[findLines.length - 1].trim();\n  const contentLines = content.split(\"\\n\");\n\n  for (let i = 0; i <= contentLines.length - 3; i++) {\n    if (contentLines[i].trim() !== firstLine) continue;\n\n    let bestEndIdx = -1;\n    let bestScore = 0;\n\n    for (let j = i + 2; j < contentLines.length; j++) {\n      if (contentLines[j].trim() === lastLine) {\n        const middleCount = j - i - 1;\n        let matchCount = 0;\n        for (let k = 1; k <= middleCount; k++) {\n          const fIdx = Math.floor((k / (middleCount + 1)) * (findLines.length - 2));\n          if (fIdx >= 0 && fIdx < findLines.length - 2) {\n            if (similarity(contentLines[i + k].trim(), findLines[fIdx + 1].trim()) > 0.5) {\n              matchCount++;\n            }\n          }\n        }\n        const score = matchCount / middleCount;\n        if (score > bestScore) {\n          bestScore = score;\n          bestEndIdx = j;\n        }\n      }\n    }\n\n    if (bestEndIdx !== -1 && bestScore > 0.5) {\n      yield contentLines.slice(i, bestEndIdx + 1).join(\"\\n\");\n    }\n  }\n}\n\nconst REPLACERS: ReplacerFn[] = [\n  simpleReplacer,\n  lineTrimmedReplacer,\n  blockAnchorReplacer,\n  whitespaceNormalizedReplacer,\n  indentationFlexibleReplacer,\n  escapeNormalizedReplacer,\n  trimmedBoundaryReplacer,\n  contextAwareReplacer,\n];\n\nexport function fuzzyReplace(\n  content: string,\n  oldString: string,\n  newString: string,\n  replaceAll = false,\n): string {\n  for (const replacer of REPLACERS) {\n    for (const search of replacer(content, oldString)) {\n      const idx = content.indexOf(search);\n      if (idx === -1) continue;\n\n      if (replaceAll) {\n        return content.split(search).join(newString);\n      }\n\n      const firstIdx = content.indexOf(search);\n      const lastIdx = content.lastIndexOf(search);\n      if (firstIdx !== lastIdx) continue;\n\n      return content.substring(0, firstIdx) + newString + content.substring(firstIdx + search.length);\n    }\n  }\n\n  throw new FuzzyEditError(\"not_found\", `Could not find match for replacement (after 8 fuzzy strategies)`);\n}\n\nexport function fuzzyContains(content: string, find: string): boolean {\n  try {\n    fuzzyReplace(content, find, \"PLACEHOLDER_CHECK\");\n    return true;\n  } catch {\n    return false;\n  }\n}\n"],"mappings":"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","names":[]}