@caupulican/pi-adaptative 0.81.43 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
  3. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +14 -8
  4. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
  5. package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +14 -8
  6. package/dist/bundled-resources/skills/deduplicate-by-evidence/SKILL.md +7 -5
  7. package/dist/bundled-resources/skills/worker-profile-writer/SKILL.md +63 -0
  8. package/dist/config.d.ts +1 -1
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +1 -1
  11. package/dist/config.js.map +1 -1
  12. package/dist/core/agent-paths.d.ts +1 -1
  13. package/dist/core/agent-paths.d.ts.map +1 -1
  14. package/dist/core/agent-paths.js +1 -1
  15. package/dist/core/agent-paths.js.map +1 -1
  16. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  17. package/dist/core/autonomy/lane-tool-surface.js +4 -2
  18. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  19. package/dist/core/background-lane-controller.d.ts +3 -0
  20. package/dist/core/background-lane-controller.d.ts.map +1 -1
  21. package/dist/core/background-lane-controller.js +6 -0
  22. package/dist/core/background-lane-controller.js.map +1 -1
  23. package/dist/core/default-tool-surface.d.ts.map +1 -1
  24. package/dist/core/default-tool-surface.js +1 -0
  25. package/dist/core/default-tool-surface.js.map +1 -1
  26. package/dist/core/delegation/worker-delegation-controller.d.ts +7 -0
  27. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  28. package/dist/core/delegation/worker-delegation-controller.js +28 -0
  29. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  30. package/dist/core/delegation/worker-profile-resolver.d.ts +2 -0
  31. package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -1
  32. package/dist/core/delegation/worker-profile-resolver.js +46 -11
  33. package/dist/core/delegation/worker-profile-resolver.js.map +1 -1
  34. package/dist/core/doctor.d.ts +5 -5
  35. package/dist/core/doctor.d.ts.map +1 -1
  36. package/dist/core/doctor.js +17 -20
  37. package/dist/core/doctor.js.map +1 -1
  38. package/dist/core/model-capability.d.ts.map +1 -1
  39. package/dist/core/model-capability.js +1 -0
  40. package/dist/core/model-capability.js.map +1 -1
  41. package/dist/core/orchestration/lane-tool-manifests.d.ts +1 -1
  42. package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -1
  43. package/dist/core/orchestration/lane-tool-manifests.js +6 -1
  44. package/dist/core/orchestration/lane-tool-manifests.js.map +1 -1
  45. package/dist/core/orchestration/session-task-profile-store.d.ts +31 -0
  46. package/dist/core/orchestration/session-task-profile-store.d.ts.map +1 -0
  47. package/dist/core/orchestration/session-task-profile-store.js +138 -0
  48. package/dist/core/orchestration/session-task-profile-store.js.map +1 -0
  49. package/dist/core/orchestration/task-profile-writer.d.ts +58 -0
  50. package/dist/core/orchestration/task-profile-writer.d.ts.map +1 -0
  51. package/dist/core/orchestration/task-profile-writer.js +165 -0
  52. package/dist/core/orchestration/task-profile-writer.js.map +1 -0
  53. package/dist/core/runtime-builder.d.ts +3 -1
  54. package/dist/core/runtime-builder.d.ts.map +1 -1
  55. package/dist/core/runtime-builder.js +14 -0
  56. package/dist/core/runtime-builder.js.map +1 -1
  57. package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -1
  58. package/dist/core/secrets/credential-exposure-guard.js +32 -0
  59. package/dist/core/secrets/credential-exposure-guard.js.map +1 -1
  60. package/dist/core/security/untrusted-boundary.d.ts.map +1 -1
  61. package/dist/core/security/untrusted-boundary.js +4 -6
  62. package/dist/core/security/untrusted-boundary.js.map +1 -1
  63. package/dist/core/session-role.d.ts.map +1 -1
  64. package/dist/core/session-role.js +1 -0
  65. package/dist/core/session-role.js.map +1 -1
  66. package/dist/core/system-prompt.d.ts.map +1 -1
  67. package/dist/core/system-prompt.js +21 -22
  68. package/dist/core/system-prompt.js.map +1 -1
  69. package/dist/core/tool-capability-policy.js +1 -1
  70. package/dist/core/tool-capability-policy.js.map +1 -1
  71. package/dist/core/tools/bash.d.ts +1 -6
  72. package/dist/core/tools/bash.d.ts.map +1 -1
  73. package/dist/core/tools/bash.js +2 -26
  74. package/dist/core/tools/bash.js.map +1 -1
  75. package/dist/core/tools/delegate.js +4 -4
  76. package/dist/core/tools/delegate.js.map +1 -1
  77. package/dist/core/tools/edit-diff.d.ts +32 -5
  78. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  79. package/dist/core/tools/edit-diff.js +137 -69
  80. package/dist/core/tools/edit-diff.js.map +1 -1
  81. package/dist/core/tools/edit.d.ts +22 -7
  82. package/dist/core/tools/edit.d.ts.map +1 -1
  83. package/dist/core/tools/edit.js +128 -55
  84. package/dist/core/tools/edit.js.map +1 -1
  85. package/dist/core/tools/file-mutation-intent.d.ts +70 -0
  86. package/dist/core/tools/file-mutation-intent.d.ts.map +1 -0
  87. package/dist/core/tools/file-mutation-intent.js +325 -0
  88. package/dist/core/tools/file-mutation-intent.js.map +1 -0
  89. package/dist/core/tools/goal.d.ts.map +1 -1
  90. package/dist/core/tools/goal.js +41 -18
  91. package/dist/core/tools/goal.js.map +1 -1
  92. package/dist/core/tools/index.d.ts +3 -1
  93. package/dist/core/tools/index.d.ts.map +1 -1
  94. package/dist/core/tools/index.js +31 -8
  95. package/dist/core/tools/index.js.map +1 -1
  96. package/dist/core/tools/profile-writer.d.ts +33 -0
  97. package/dist/core/tools/profile-writer.d.ts.map +1 -0
  98. package/dist/core/tools/profile-writer.js +67 -0
  99. package/dist/core/tools/profile-writer.js.map +1 -0
  100. package/dist/core/tools/task-steps.d.ts.map +1 -1
  101. package/dist/core/tools/task-steps.js +6 -11
  102. package/dist/core/tools/task-steps.js.map +1 -1
  103. package/dist/core/tools/tmux-dispatch.d.ts +2 -0
  104. package/dist/core/tools/tmux-dispatch.d.ts.map +1 -1
  105. package/dist/core/tools/tmux-dispatch.js +16 -2
  106. package/dist/core/tools/tmux-dispatch.js.map +1 -1
  107. package/dist/core/tools/write.d.ts +24 -7
  108. package/dist/core/tools/write.d.ts.map +1 -1
  109. package/dist/core/tools/write.js +182 -107
  110. package/dist/core/tools/write.js.map +1 -1
  111. package/dist/index.d.ts +1 -1
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +1 -1
  114. package/dist/index.js.map +1 -1
  115. package/dist/jscpd-cli.d.ts +3 -0
  116. package/dist/jscpd-cli.d.ts.map +1 -0
  117. package/dist/jscpd-cli.js +22 -0
  118. package/dist/jscpd-cli.js.map +1 -0
  119. package/dist/modes/interactive/components/profile-resource-editor.js +2 -2
  120. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
  121. package/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  122. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  123. package/dist/modes/interactive/components/tool-execution.js +85 -3
  124. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  125. package/dist/modes/interactive/interactive-mode.js +3 -3
  126. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  127. package/dist/utils/bundled-jscpd.d.ts +20 -0
  128. package/dist/utils/bundled-jscpd.d.ts.map +1 -0
  129. package/dist/utils/bundled-jscpd.js +154 -0
  130. package/dist/utils/bundled-jscpd.js.map +1 -0
  131. package/dist/utils/shell.d.ts.map +1 -1
  132. package/dist/utils/shell.js +11 -0
  133. package/dist/utils/shell.js.map +1 -1
  134. package/dist/utils/tools-manager.d.ts +6 -1
  135. package/dist/utils/tools-manager.d.ts.map +1 -1
  136. package/dist/utils/tools-manager.js +81 -2
  137. package/dist/utils/tools-manager.js.map +1 -1
  138. package/docs/development.md +1 -1
  139. package/docs/index.md +3 -0
  140. package/docs/managed-data-tools.md +15 -0
  141. package/docs/quickstart.md +2 -2
  142. package/docs/tool-boundary-performance-roadmap-2026-08-02.md +166 -0
  143. package/docs/tool-repair.md +1 -1
  144. package/docs/windows.md +2 -2
  145. package/docs/work-directory.md +1 -1
  146. package/docs/worker-profiles.md +16 -0
  147. package/examples/extensions/built-in-tool-renderer.ts +12 -5
  148. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  149. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  150. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  151. package/examples/extensions/minimal-mode.ts +8 -7
  152. package/examples/extensions/prompt-customizer.ts +4 -2
  153. package/examples/extensions/sandbox/package-lock.json +2 -2
  154. package/examples/extensions/sandbox/package.json +1 -1
  155. package/examples/extensions/ssh.ts +113 -24
  156. package/examples/extensions/with-deps/package-lock.json +2 -2
  157. package/examples/extensions/with-deps/package.json +1 -1
  158. package/npm-shrinkwrap.json +123 -23
  159. package/package.json +8 -6
@@ -1 +1 @@
1
- {"version":3,"file":"edit-diff.js","sourceRoot":"","sources":["../../../src/core/tools/edit-diff.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared diff computation utilities for the edit tool.\n * Used by both edit.ts (for execution) and tool-execution.ts (for preview rendering).\n */\n\nimport * as Diff from \"diff\";\nimport { constants } from \"fs\";\nimport { access, readFile } from \"fs/promises\";\nimport { resolveToCwd } from \"./path-utils.ts\";\n\nexport function detectLineEnding(content: string): \"\\r\\n\" | \"\\n\" {\n\tconst crlfIdx = content.indexOf(\"\\r\\n\");\n\tconst lfIdx = content.indexOf(\"\\n\");\n\tif (lfIdx === -1) return \"\\n\";\n\tif (crlfIdx === -1) return \"\\n\";\n\treturn crlfIdx < lfIdx ? \"\\r\\n\" : \"\\n\";\n}\n\nexport function normalizeToLF(text: string): string {\n\treturn text.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n}\n\nexport function restoreLineEndings(text: string, ending: \"\\r\\n\" | \"\\n\"): string {\n\treturn ending === \"\\r\\n\" ? text.replace(/\\n/g, \"\\r\\n\") : text;\n}\n\n/**\n * Normalize text for fuzzy matching. Applies progressive transformations:\n * - Strip trailing whitespace from each line\n * - Normalize smart quotes to ASCII equivalents\n * - Normalize Unicode dashes/hyphens to ASCII hyphen\n * - Normalize special Unicode spaces to regular space\n */\nexport function normalizeForFuzzyMatch(text: string): string {\n\treturn (\n\t\ttext\n\t\t\t.normalize(\"NFKC\")\n\t\t\t// Strip trailing whitespace per line\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => line.trimEnd())\n\t\t\t.join(\"\\n\")\n\t\t\t// Smart single quotes → '\n\t\t\t.replace(/[\\u2018\\u2019\\u201A\\u201B]/g, \"'\")\n\t\t\t// Smart double quotes → \"\n\t\t\t.replace(/[\\u201C\\u201D\\u201E\\u201F]/g, '\"')\n\t\t\t// Various dashes/hyphens → -\n\t\t\t// U+2010 hyphen, U+2011 non-breaking hyphen, U+2012 figure dash,\n\t\t\t// U+2013 en-dash, U+2014 em-dash, U+2015 horizontal bar, U+2212 minus\n\t\t\t.replace(/[\\u2010\\u2011\\u2012\\u2013\\u2014\\u2015\\u2212]/g, \"-\")\n\t\t\t// Special spaces → regular space\n\t\t\t// U+00A0 NBSP, U+2002-U+200A various spaces, U+202F narrow NBSP,\n\t\t\t// U+205F medium math space, U+3000 ideographic space\n\t\t\t.replace(/[\\u00A0\\u2002-\\u200A\\u202F\\u205F\\u3000]/g, \" \")\n\t);\n}\n\nexport interface FuzzyMatchResult {\n\t/** Whether a match was found */\n\tfound: boolean;\n\t/** The index where the match starts (in the content that should be used for replacement) */\n\tindex: number;\n\t/** Length of the matched text */\n\tmatchLength: number;\n\t/** Whether fuzzy matching was used (false = exact match) */\n\tusedFuzzyMatch: boolean;\n\t/** The original content replacements should be applied to. */\n\tcontentForReplacement: string;\n}\n\nexport interface Edit {\n\toldText: string;\n\tnewText: string;\n}\n\ninterface MatchedEdit {\n\teditIndex: number;\n\tmatchIndex: number;\n\tmatchLength: number;\n\tnewText: string;\n}\n\nexport interface AppliedEditsResult {\n\tbaseContent: string;\n\tnewContent: string;\n}\n\n/**\n * Find oldText in content, trying exact match first, then fuzzy match.\n * Fuzzy matching searches normalized text, but returned spans still refer to\n * the original content so replacement does not normalize unrelated text.\n */\nexport function fuzzyFindText(content: string, oldText: string): FuzzyMatchResult {\n\t// Try exact match first\n\tconst exactIndex = content.indexOf(oldText);\n\tif (exactIndex !== -1) {\n\t\treturn {\n\t\t\tfound: true,\n\t\t\tindex: exactIndex,\n\t\t\tmatchLength: oldText.length,\n\t\t\tusedFuzzyMatch: false,\n\t\t\tcontentForReplacement: content,\n\t\t};\n\t}\n\n\t// Try fuzzy match - work entirely in normalized space\n\tconst fuzzyContent = normalizeForFuzzyMatch(content);\n\tconst fuzzyOldText = normalizeForFuzzyMatch(oldText);\n\tconst fuzzyIndex = fuzzyContent.indexOf(fuzzyOldText);\n\n\tif (fuzzyIndex === -1) {\n\t\treturn {\n\t\t\tfound: false,\n\t\t\tindex: -1,\n\t\t\tmatchLength: 0,\n\t\t\tusedFuzzyMatch: false,\n\t\t\tcontentForReplacement: content,\n\t\t};\n\t}\n\n\treturn {\n\t\tfound: true,\n\t\tindex: fuzzyIndex,\n\t\tmatchLength: fuzzyOldText.length,\n\t\tusedFuzzyMatch: true,\n\t\tcontentForReplacement: content,\n\t};\n}\n\n/** Strip UTF-8 BOM if present, return both the BOM (if any) and the text without it */\nexport function stripBom(content: string): { bom: string; text: string } {\n\treturn content.startsWith(\"\\uFEFF\") ? { bom: \"\\uFEFF\", text: content.slice(1) } : { bom: \"\", text: content };\n}\n\nfunction countExactOccurrences(content: string, oldText: string): number {\n\treturn content.split(oldText).length - 1;\n}\n\nfunction countFuzzyOccurrences(content: string, oldText: string): number {\n\tconst fuzzyContent = normalizeForFuzzyMatch(content);\n\tconst fuzzyOldText = normalizeForFuzzyMatch(oldText);\n\treturn fuzzyContent.split(fuzzyOldText).length - 1;\n}\n\nfunction getLineStarts(content: string): number[] {\n\tconst starts = [0];\n\tfor (let i = 0; i < content.length; i++) {\n\t\tif (content[i] === \"\\n\") starts.push(i + 1);\n\t}\n\treturn starts;\n}\n\nfunction lineColumnAt(lineStarts: number[], index: number): { lineIndex: number; column: number } {\n\tlet low = 0;\n\tlet high = lineStarts.length - 1;\n\twhile (low <= high) {\n\t\tconst mid = Math.floor((low + high) / 2);\n\t\tif (lineStarts[mid] <= index) low = mid + 1;\n\t\telse high = mid - 1;\n\t}\n\tconst lineIndex = Math.max(0, high);\n\treturn { lineIndex, column: index - lineStarts[lineIndex] };\n}\n\nfunction lineTextAt(content: string, lineStarts: number[], lineIndex: number): string {\n\tconst start = lineStarts[lineIndex];\n\tconst nextStart = lineStarts[lineIndex + 1] ?? content.length;\n\tconst end = nextStart > start && content[nextStart - 1] === \"\\n\" ? nextStart - 1 : nextStart;\n\treturn content.slice(start, end);\n}\n\nfunction normalizedColumnToOriginalColumn(line: string, normalizedColumn: number): number {\n\tif (normalizedColumn <= 0) return 0;\n\tlet originalColumn = 0;\n\tfor (const char of line) {\n\t\tconst nextColumn = originalColumn + char.length;\n\t\tif (normalizeForFuzzyMatch(line.slice(0, nextColumn)).length >= normalizedColumn) return nextColumn;\n\t\toriginalColumn = nextColumn;\n\t}\n\treturn line.length;\n}\n\nfunction mapFuzzySpanToOriginal(\n\toriginalContent: string,\n\tnormalizedIndex: number,\n\tnormalizedLength: number,\n): { index: number; length: number } {\n\tconst fuzzyContent = normalizeForFuzzyMatch(originalContent);\n\tconst fuzzyLineStarts = getLineStarts(fuzzyContent);\n\tconst originalLineStarts = getLineStarts(originalContent);\n\tconst start = lineColumnAt(fuzzyLineStarts, normalizedIndex);\n\tconst end = lineColumnAt(fuzzyLineStarts, normalizedIndex + normalizedLength);\n\tconst startLine = lineTextAt(originalContent, originalLineStarts, start.lineIndex);\n\tconst endLine = lineTextAt(originalContent, originalLineStarts, end.lineIndex);\n\tconst originalStart =\n\t\toriginalLineStarts[start.lineIndex] + normalizedColumnToOriginalColumn(startLine, start.column);\n\tconst originalEnd = originalLineStarts[end.lineIndex] + normalizedColumnToOriginalColumn(endLine, end.column);\n\treturn { index: originalStart, length: originalEnd - originalStart };\n}\n\nfunction getNotFoundError(path: string, editIndex: number, totalEdits: number): Error {\n\tif (totalEdits === 1) {\n\t\treturn new Error(\n\t\t\t`Could not find the exact text in ${path}. The old text must match exactly including all whitespace and newlines.`,\n\t\t);\n\t}\n\treturn new Error(\n\t\t`Could not find edits[${editIndex}] in ${path}. The oldText must match exactly including all whitespace and newlines.`,\n\t);\n}\n\nfunction getDuplicateError(path: string, editIndex: number, totalEdits: number, occurrences: number): Error {\n\tif (totalEdits === 1) {\n\t\treturn new Error(\n\t\t\t`Found ${occurrences} occurrences of the text in ${path}. The text must be unique. Please provide more context to make it unique.`,\n\t\t);\n\t}\n\treturn new Error(\n\t\t`Found ${occurrences} occurrences of edits[${editIndex}] in ${path}. Each oldText must be unique. Please provide more context to make it unique.`,\n\t);\n}\n\nfunction getEmptyOldTextError(path: string, editIndex: number, totalEdits: number): Error {\n\tif (totalEdits === 1) {\n\t\treturn new Error(`oldText must not be empty in ${path}.`);\n\t}\n\treturn new Error(`edits[${editIndex}].oldText must not be empty in ${path}.`);\n}\n\nfunction getNoChangeError(path: string, totalEdits: number): Error {\n\tif (totalEdits === 1) {\n\t\treturn new Error(\n\t\t\t`No changes made to ${path}. The replacement produced identical content. This might indicate an issue with special characters or the text not existing as expected.`,\n\t\t);\n\t}\n\treturn new Error(`No changes made to ${path}. The replacements produced identical content.`);\n}\n\n/**\n * Apply one or more exact-text replacements to LF-normalized content.\n *\n * All edits are matched against the same original content. Replacements are\n * then applied in reverse order so offsets remain stable. Fuzzy matching uses\n * normalized text only for finding the span; the final splice is always made\n * into the original content.\n */\nexport function applyEditsToNormalizedContent(\n\tnormalizedContent: string,\n\tedits: Edit[],\n\tpath: string,\n): AppliedEditsResult {\n\tconst normalizedEdits = edits.map((edit) => ({\n\t\toldText: normalizeToLF(edit.oldText),\n\t\tnewText: normalizeToLF(edit.newText),\n\t}));\n\n\tfor (let i = 0; i < normalizedEdits.length; i++) {\n\t\tif (normalizedEdits[i].oldText.length === 0) {\n\t\t\tthrow getEmptyOldTextError(path, i, normalizedEdits.length);\n\t\t}\n\t}\n\n\tconst baseContent = normalizedContent;\n\tconst matchedEdits: MatchedEdit[] = [];\n\tfor (let i = 0; i < normalizedEdits.length; i++) {\n\t\tconst edit = normalizedEdits[i];\n\t\tconst matchResult = fuzzyFindText(baseContent, edit.oldText);\n\t\tif (!matchResult.found) {\n\t\t\tthrow getNotFoundError(path, i, normalizedEdits.length);\n\t\t}\n\n\t\tconst occurrences = matchResult.usedFuzzyMatch\n\t\t\t? countFuzzyOccurrences(baseContent, edit.oldText)\n\t\t\t: countExactOccurrences(baseContent, edit.oldText);\n\t\tif (occurrences > 1) {\n\t\t\tthrow getDuplicateError(path, i, normalizedEdits.length, occurrences);\n\t\t}\n\n\t\tconst replacementSpan = matchResult.usedFuzzyMatch\n\t\t\t? mapFuzzySpanToOriginal(baseContent, matchResult.index, matchResult.matchLength)\n\t\t\t: { index: matchResult.index, length: matchResult.matchLength };\n\n\t\tmatchedEdits.push({\n\t\t\teditIndex: i,\n\t\t\tmatchIndex: replacementSpan.index,\n\t\t\tmatchLength: replacementSpan.length,\n\t\t\tnewText: edit.newText,\n\t\t});\n\t}\n\n\tmatchedEdits.sort((a, b) => a.matchIndex - b.matchIndex);\n\tfor (let i = 1; i < matchedEdits.length; i++) {\n\t\tconst previous = matchedEdits[i - 1];\n\t\tconst current = matchedEdits[i];\n\t\tif (previous.matchIndex + previous.matchLength > current.matchIndex) {\n\t\t\tthrow new Error(\n\t\t\t\t`edits[${previous.editIndex}] and edits[${current.editIndex}] overlap in ${path}. Merge them into one edit or target disjoint regions.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tlet newContent = baseContent;\n\tfor (let i = matchedEdits.length - 1; i >= 0; i--) {\n\t\tconst edit = matchedEdits[i];\n\t\tnewContent =\n\t\t\tnewContent.substring(0, edit.matchIndex) +\n\t\t\tedit.newText +\n\t\t\tnewContent.substring(edit.matchIndex + edit.matchLength);\n\t}\n\n\tif (baseContent === newContent) {\n\t\tthrow getNoChangeError(path, normalizedEdits.length);\n\t}\n\n\treturn { baseContent, newContent };\n}\n\n/** Generate a standard unified patch. */\nexport function generateUnifiedPatch(path: string, oldContent: string, newContent: string, contextLines = 4): string {\n\treturn Diff.createTwoFilesPatch(path, path, oldContent, newContent, undefined, undefined, {\n\t\tcontext: contextLines,\n\t\theaderOptions: Diff.FILE_HEADERS_ONLY,\n\t});\n}\n\n/**\n * Generate a display-oriented diff string with line numbers and context.\n * Returns both the diff string and the first changed line number (in the new file).\n */\nexport function generateDiffString(\n\toldContent: string,\n\tnewContent: string,\n\tcontextLines = 4,\n): { diff: string; firstChangedLine: number | undefined } {\n\tconst parts = Diff.diffLines(oldContent, newContent);\n\tconst output: string[] = [];\n\n\tconst oldLines = oldContent.split(\"\\n\");\n\tconst newLines = newContent.split(\"\\n\");\n\tconst maxLineNum = Math.max(oldLines.length, newLines.length);\n\tconst lineNumWidth = String(maxLineNum).length;\n\n\tlet oldLineNum = 1;\n\tlet newLineNum = 1;\n\tlet lastWasChange = false;\n\tlet firstChangedLine: number | undefined;\n\n\tfor (let i = 0; i < parts.length; i++) {\n\t\tconst part = parts[i];\n\t\tconst raw = part.value.split(\"\\n\");\n\t\tif (raw[raw.length - 1] === \"\") {\n\t\t\traw.pop();\n\t\t}\n\n\t\tif (part.added || part.removed) {\n\t\t\t// Capture the first changed line (in the new file)\n\t\t\tif (firstChangedLine === undefined) {\n\t\t\t\tfirstChangedLine = newLineNum;\n\t\t\t}\n\n\t\t\t// Show the change\n\t\t\tfor (const line of raw) {\n\t\t\t\tif (part.added) {\n\t\t\t\t\tconst lineNum = String(newLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\toutput.push(`+${lineNum} ${line}`);\n\t\t\t\t\tnewLineNum++;\n\t\t\t\t} else {\n\t\t\t\t\t// removed\n\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\toutput.push(`-${lineNum} ${line}`);\n\t\t\t\t\toldLineNum++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tlastWasChange = true;\n\t\t} else {\n\t\t\t// Context lines - only show a few before/after changes\n\t\t\tconst nextPartIsChange = i < parts.length - 1 && (parts[i + 1].added || parts[i + 1].removed);\n\t\t\tconst hasLeadingChange = lastWasChange;\n\t\t\tconst hasTrailingChange = nextPartIsChange;\n\n\t\t\tif (hasLeadingChange && hasTrailingChange) {\n\t\t\t\tif (raw.length <= contextLines * 2) {\n\t\t\t\t\tfor (const line of raw) {\n\t\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\t\toutput.push(` ${lineNum} ${line}`);\n\t\t\t\t\t\toldLineNum++;\n\t\t\t\t\t\tnewLineNum++;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst leadingLines = raw.slice(0, contextLines);\n\t\t\t\t\tconst trailingLines = raw.slice(raw.length - contextLines);\n\t\t\t\t\tconst skippedLines = raw.length - leadingLines.length - trailingLines.length;\n\n\t\t\t\t\tfor (const line of leadingLines) {\n\t\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\t\toutput.push(` ${lineNum} ${line}`);\n\t\t\t\t\t\toldLineNum++;\n\t\t\t\t\t\tnewLineNum++;\n\t\t\t\t\t}\n\n\t\t\t\t\toutput.push(` ${\"\".padStart(lineNumWidth, \" \")} ...`);\n\t\t\t\t\toldLineNum += skippedLines;\n\t\t\t\t\tnewLineNum += skippedLines;\n\n\t\t\t\t\tfor (const line of trailingLines) {\n\t\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\t\toutput.push(` ${lineNum} ${line}`);\n\t\t\t\t\t\toldLineNum++;\n\t\t\t\t\t\tnewLineNum++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (hasLeadingChange) {\n\t\t\t\tconst shownLines = raw.slice(0, contextLines);\n\t\t\t\tconst skippedLines = raw.length - shownLines.length;\n\n\t\t\t\tfor (const line of shownLines) {\n\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\toutput.push(` ${lineNum} ${line}`);\n\t\t\t\t\toldLineNum++;\n\t\t\t\t\tnewLineNum++;\n\t\t\t\t}\n\n\t\t\t\tif (skippedLines > 0) {\n\t\t\t\t\toutput.push(` ${\"\".padStart(lineNumWidth, \" \")} ...`);\n\t\t\t\t\toldLineNum += skippedLines;\n\t\t\t\t\tnewLineNum += skippedLines;\n\t\t\t\t}\n\t\t\t} else if (hasTrailingChange) {\n\t\t\t\tconst skippedLines = Math.max(0, raw.length - contextLines);\n\t\t\t\tif (skippedLines > 0) {\n\t\t\t\t\toutput.push(` ${\"\".padStart(lineNumWidth, \" \")} ...`);\n\t\t\t\t\toldLineNum += skippedLines;\n\t\t\t\t\tnewLineNum += skippedLines;\n\t\t\t\t}\n\n\t\t\t\tfor (const line of raw.slice(skippedLines)) {\n\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\toutput.push(` ${lineNum} ${line}`);\n\t\t\t\t\toldLineNum++;\n\t\t\t\t\tnewLineNum++;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Skip these context lines entirely\n\t\t\t\toldLineNum += raw.length;\n\t\t\t\tnewLineNum += raw.length;\n\t\t\t}\n\n\t\t\tlastWasChange = false;\n\t\t}\n\t}\n\n\treturn { diff: output.join(\"\\n\"), firstChangedLine };\n}\n\nexport interface EditDiffResult {\n\tdiff: string;\n\tfirstChangedLine: number | undefined;\n}\n\nexport interface EditDiffError {\n\terror: string;\n}\n\n/**\n * Compute the diff for one or more edit operations without applying them.\n * Used for preview rendering in the TUI before the tool executes.\n */\nexport async function computeEditsDiff(\n\tpath: string,\n\tedits: Edit[],\n\tcwd: string,\n): Promise<EditDiffResult | EditDiffError> {\n\tconst absolutePath = resolveToCwd(path, cwd);\n\n\ttry {\n\t\t// Check if file exists and is readable\n\t\ttry {\n\t\t\tawait access(absolutePath, constants.R_OK);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error && \"code\" in error ? `Error code: ${error.code}` : String(error);\n\t\t\treturn { error: `Could not edit file: ${path}. ${errorMessage}.` };\n\t\t}\n\n\t\t// Read the file\n\t\tconst rawContent = await readFile(absolutePath, \"utf-8\");\n\n\t\t// Strip BOM before matching (LLM won't include invisible BOM in oldText)\n\t\tconst { text: content } = stripBom(rawContent);\n\t\tconst normalizedContent = normalizeToLF(content);\n\t\tconst { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);\n\n\t\t// Generate the diff\n\t\treturn generateDiffString(baseContent, newContent);\n\t} catch (err) {\n\t\treturn { error: err instanceof Error ? err.message : String(err) };\n\t}\n}\n\n/**\n * Compute the diff for a single edit operation without applying it.\n * Kept as a convenience wrapper for single-edit callers.\n */\nexport async function computeEditDiff(\n\tpath: string,\n\toldText: string,\n\tnewText: string,\n\tcwd: string,\n): Promise<EditDiffResult | EditDiffError> {\n\treturn computeEditsDiff(path, [{ oldText, newText }], cwd);\n}\n"]}
1
+ {"version":3,"file":"edit-diff.js","sourceRoot":"","sources":["../../../src/core/tools/edit-diff.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared diff computation utilities for the edit tool.\n * Used by both edit.ts (for execution) and tool-execution.ts (for preview rendering).\n */\n\nimport * as Diff from \"diff\";\nimport { constants } from \"fs\";\nimport { access, readFile } from \"fs/promises\";\nimport { resolveToCwd } from \"./path-utils.ts\";\n\nexport function detectLineEnding(content: string): \"\\r\\n\" | \"\\n\" {\n\tconst crlfIdx = content.indexOf(\"\\r\\n\");\n\tconst lfIdx = content.indexOf(\"\\n\");\n\tif (lfIdx === -1) return \"\\n\";\n\tif (crlfIdx === -1) return \"\\n\";\n\treturn crlfIdx < lfIdx ? \"\\r\\n\" : \"\\n\";\n}\n\nexport function normalizeToLF(text: string): string {\n\treturn text.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n}\n\nexport function restoreLineEndings(text: string, ending: \"\\r\\n\" | \"\\n\"): string {\n\treturn ending === \"\\r\\n\" ? text.replace(/\\n/g, \"\\r\\n\") : text;\n}\n\n/**\n * Normalize text for fuzzy matching. Applies progressive transformations:\n * - Strip trailing whitespace from each line\n * - Normalize smart quotes to ASCII equivalents\n * - Normalize Unicode dashes/hyphens to ASCII hyphen\n * - Normalize special Unicode spaces to regular space\n */\nexport function normalizeForFuzzyMatch(text: string): string {\n\treturn normalizeFuzzyCharacters(text).replace(/[^\\S\\n]+(?=\\n|$)/gu, \"\");\n}\n\nfunction normalizeFuzzyCharacters(text: string): string {\n\treturn (\n\t\ttext\n\t\t\t.normalize(\"NFKC\")\n\t\t\t// Smart single quotes → '\n\t\t\t.replace(/[\\u2018\\u2019\\u201A\\u201B]/g, \"'\")\n\t\t\t// Smart double quotes → \"\n\t\t\t.replace(/[\\u201C\\u201D\\u201E\\u201F]/g, '\"')\n\t\t\t// Various dashes/hyphens → -\n\t\t\t// U+2010 hyphen, U+2011 non-breaking hyphen, U+2012 figure dash,\n\t\t\t// U+2013 en-dash, U+2014 em-dash, U+2015 horizontal bar, U+2212 minus\n\t\t\t.replace(/[\\u2010\\u2011\\u2012\\u2013\\u2014\\u2015\\u2212]/g, \"-\")\n\t\t\t// Special spaces → regular space\n\t\t\t// U+00A0 NBSP, U+2002-U+200A various spaces, U+202F narrow NBSP,\n\t\t\t// U+205F medium math space, U+3000 ideographic space\n\t\t\t.replace(/[\\u00A0\\u2002-\\u200A\\u202F\\u205F\\u3000]/g, \" \")\n\t);\n}\n\nexport interface FuzzyMatchResult {\n\t/** Whether a match was found */\n\tfound: boolean;\n\t/** The index where the match starts (in the content that should be used for replacement) */\n\tindex: number;\n\t/** Length of the matched text */\n\tmatchLength: number;\n\t/** Whether fuzzy matching was used (false = exact match) */\n\tusedFuzzyMatch: boolean;\n\t/** The original content replacements should be applied to. */\n\tcontentForReplacement: string;\n}\n\nexport interface Edit {\n\toldText: string;\n\tnewText: string;\n\t/** Inclusive 1-based line bounds from the read that supplied oldText. */\n\trange?: EditRange;\n}\n\nexport interface EditRange {\n\tstartLine: number;\n\tendLine: number;\n}\n\ninterface PlannedEdit {\n\teditIndex: number;\n\tmatchIndex: number;\n\tmatchLength: number;\n\tnewText: string;\n\texpectedText: string;\n}\n\nexport interface EditMatchPlan {\n\t/** Length of the LF-normalized source against which this plan was validated. */\n\tbaseLength: number;\n\teditCount: number;\n\t/** Compact, source-ordered replacement spans; the source body is not retained. */\n\tedits: readonly PlannedEdit[];\n}\n\nexport interface AppliedEditsResult {\n\tbaseContent: string;\n\tnewContent: string;\n}\n\n/**\n * Find oldText in content, trying exact match first, then fuzzy match.\n * Fuzzy matching searches normalized text, but returned spans still refer to\n * the original content so replacement does not normalize unrelated text.\n */\nexport function fuzzyFindText(content: string, oldText: string): FuzzyMatchResult {\n\t// Try exact match first\n\tconst exactIndex = content.indexOf(oldText);\n\tif (exactIndex !== -1) {\n\t\treturn {\n\t\t\tfound: true,\n\t\t\tindex: exactIndex,\n\t\t\tmatchLength: oldText.length,\n\t\t\tusedFuzzyMatch: false,\n\t\t\tcontentForReplacement: content,\n\t\t};\n\t}\n\n\t// Try fuzzy match - work entirely in normalized space\n\tconst fuzzyContent = normalizeForFuzzyMatch(content);\n\tconst fuzzyOldText = normalizeForFuzzyMatch(oldText);\n\tconst fuzzyIndex = fuzzyContent.indexOf(fuzzyOldText);\n\n\tif (fuzzyIndex === -1) {\n\t\treturn {\n\t\t\tfound: false,\n\t\t\tindex: -1,\n\t\t\tmatchLength: 0,\n\t\t\tusedFuzzyMatch: false,\n\t\t\tcontentForReplacement: content,\n\t\t};\n\t}\n\n\treturn {\n\t\tfound: true,\n\t\tindex: fuzzyIndex,\n\t\tmatchLength: fuzzyOldText.length,\n\t\tusedFuzzyMatch: true,\n\t\tcontentForReplacement: content,\n\t};\n}\n\n/** Strip UTF-8 BOM if present, return both the BOM (if any) and the text without it */\nexport function stripBom(content: string): { bom: string; text: string } {\n\treturn content.startsWith(\"\\uFEFF\") ? { bom: \"\\uFEFF\", text: content.slice(1) } : { bom: \"\", text: content };\n}\n\nfunction countExactOccurrences(content: string, oldText: string): number {\n\tif (oldText.length === 0) return 0;\n\tlet count = 0;\n\tlet searchFrom = 0;\n\twhile (searchFrom <= content.length - oldText.length) {\n\t\tconst index = content.indexOf(oldText, searchFrom);\n\t\tif (index === -1) break;\n\t\tcount++;\n\t\tsearchFrom = index + 1;\n\t}\n\treturn count;\n}\n\nfunction getLineStarts(content: string): number[] {\n\tconst starts = [0];\n\tfor (let i = 0; i < content.length; i++) {\n\t\tif (content[i] === \"\\n\") starts.push(i + 1);\n\t}\n\treturn starts;\n}\n\nfunction lineColumnAt(lineStarts: number[], index: number): { lineIndex: number; column: number } {\n\tlet low = 0;\n\tlet high = lineStarts.length - 1;\n\twhile (low <= high) {\n\t\tconst mid = Math.floor((low + high) / 2);\n\t\tif (lineStarts[mid] <= index) low = mid + 1;\n\t\telse high = mid - 1;\n\t}\n\tconst lineIndex = Math.max(0, high);\n\treturn { lineIndex, column: index - lineStarts[lineIndex] };\n}\n\nfunction lineTextAt(content: string, lineStarts: number[], lineIndex: number): string {\n\tconst start = lineStarts[lineIndex];\n\tconst nextStart = lineStarts[lineIndex + 1] ?? content.length;\n\tconst end = nextStart > start && content[nextStart - 1] === \"\\n\" ? nextStart - 1 : nextStart;\n\treturn content.slice(start, end);\n}\n\nfunction normalizedColumnToOriginalColumn(line: string, normalizedColumn: number): number {\n\tif (normalizedColumn <= 0) return 0;\n\tconst boundaries = [0];\n\tfor (let index = 0; index < line.length; ) {\n\t\tconst codePoint = line.codePointAt(index);\n\t\tindex += codePoint !== undefined && codePoint > 0xffff ? 2 : 1;\n\t\tboundaries.push(index);\n\t}\n\n\tlet low = 1;\n\tlet high = boundaries.length - 1;\n\twhile (low < high) {\n\t\tconst mid = Math.floor((low + high) / 2);\n\t\tif (normalizeForFuzzyMatch(line.slice(0, boundaries[mid])).length >= normalizedColumn) high = mid;\n\t\telse low = mid + 1;\n\t}\n\treturn boundaries[low] ?? line.length;\n}\n\nfunction mapFuzzySpanToOriginal(\n\toriginalContent: string,\n\toriginalLineStarts: number[],\n\tfuzzyLineStarts: number[],\n\tnormalizedIndex: number,\n\tnormalizedLength: number,\n): { index: number; length: number } {\n\tconst start = lineColumnAt(fuzzyLineStarts, normalizedIndex);\n\tconst end = lineColumnAt(fuzzyLineStarts, normalizedIndex + normalizedLength);\n\tconst startLine = lineTextAt(originalContent, originalLineStarts, start.lineIndex);\n\tconst endLine = lineTextAt(originalContent, originalLineStarts, end.lineIndex);\n\tconst originalStart =\n\t\toriginalLineStarts[start.lineIndex] + normalizedColumnToOriginalColumn(startLine, start.column);\n\tconst originalEnd = originalLineStarts[end.lineIndex] + normalizedColumnToOriginalColumn(endLine, end.column);\n\treturn { index: originalStart, length: originalEnd - originalStart };\n}\n\nfunction getNotFoundError(path: string, editIndex: number, totalEdits: number, range?: EditRange): Error {\n\tconst rangeSuffix = range ? ` within lines ${range.startLine}-${range.endLine}` : \"\";\n\tif (totalEdits === 1) {\n\t\treturn new Error(\n\t\t\t`Could not find the exact text in ${path}${rangeSuffix}. The old text must match exactly including all whitespace and newlines.`,\n\t\t);\n\t}\n\treturn new Error(\n\t\t`Could not find edits[${editIndex}] in ${path}${rangeSuffix}. The oldText must match exactly including all whitespace and newlines.`,\n\t);\n}\n\nfunction getDuplicateError(path: string, editIndex: number, totalEdits: number, occurrences: number): Error {\n\tif (totalEdits === 1) {\n\t\treturn new Error(\n\t\t\t`Found ${occurrences} occurrences of the text in ${path}. The text must be unique. Please provide more context to make it unique.`,\n\t\t);\n\t}\n\treturn new Error(\n\t\t`Found ${occurrences} occurrences of edits[${editIndex}] in ${path}. Each oldText must be unique. Please provide more context to make it unique.`,\n\t);\n}\n\nfunction getEmptyOldTextError(path: string, editIndex: number, totalEdits: number): Error {\n\tif (totalEdits === 1) {\n\t\treturn new Error(`oldText must not be empty in ${path}.`);\n\t}\n\treturn new Error(`edits[${editIndex}].oldText must not be empty in ${path}.`);\n}\n\nfunction getNoChangeError(path: string, totalEdits: number): Error {\n\tif (totalEdits === 1) {\n\t\treturn new Error(\n\t\t\t`No changes made to ${path}. The replacement produced identical content. This might indicate an issue with special characters or the text not existing as expected.`,\n\t\t);\n\t}\n\treturn new Error(`No changes made to ${path}. The replacements produced identical content.`);\n}\n\ninterface MatchWindow {\n\tstart: number;\n\tend: number;\n}\n\nfunction validateRange(\n\trange: EditRange | undefined,\n\tlineStarts: number[] | undefined,\n\tcontentLength: number,\n\tpath: string,\n\teditIndex: number,\n\ttotalEdits: number,\n): MatchWindow {\n\tif (!range) return { start: 0, end: contentLength };\n\tconst label = totalEdits === 1 ? \"range\" : `edits[${editIndex}].range`;\n\tif (\n\t\t!Number.isSafeInteger(range.startLine) ||\n\t\t!Number.isSafeInteger(range.endLine) ||\n\t\trange.startLine < 1 ||\n\t\trange.endLine < range.startLine\n\t) {\n\t\tthrow new Error(`${label} in ${path} must contain inclusive positive line numbers with startLine <= endLine.`);\n\t}\n\tif (!lineStarts || range.endLine > lineStarts.length) {\n\t\tthrow new Error(`${label} in ${path} exceeds the file's ${lineStarts?.length ?? 0} lines.`);\n\t}\n\treturn {\n\t\tstart: lineStarts[range.startLine - 1],\n\t\tend: lineStarts[range.endLine] ?? contentLength,\n\t};\n}\n\nfunction findWithin(content: string, oldText: string, window: MatchWindow): number {\n\tif (oldText.length === 0 || oldText.length > window.end - window.start) return -1;\n\tconst index = content.indexOf(oldText, window.start);\n\treturn index !== -1 && index + oldText.length <= window.end ? index : -1;\n}\n\nfunction rangesOverlap(previous: PlannedEdit, current: PlannedEdit): boolean {\n\treturn previous.matchIndex + previous.matchLength > current.matchIndex;\n}\n\n/**\n * Match and validate one or more replacements against LF-normalized content.\n *\n * The returned plan retains only the validated spans and their replacement\n * text. Exact matching uses native substring search, bounded by optional line\n * ranges, while uniqueness is still proven across the whole source.\n */\nexport function planEditsToNormalizedContent(normalizedContent: string, edits: Edit[], path: string): EditMatchPlan {\n\tlet originalLineStarts: number[] | undefined;\n\tlet fuzzyContent: string | undefined;\n\tlet fuzzyLineStarts: number[] | undefined;\n\tconst needsLineStarts = edits.some((edit) => edit.range !== undefined);\n\tif (needsLineStarts) originalLineStarts = getLineStarts(normalizedContent);\n\n\tconst matchedEdits: PlannedEdit[] = [];\n\tfor (let i = 0; i < edits.length; i++) {\n\t\tconst sourceEdit = edits[i];\n\t\tconst oldText = normalizeToLF(sourceEdit.oldText);\n\t\tconst newText = normalizeToLF(sourceEdit.newText);\n\t\tif (oldText.length === 0) throw getEmptyOldTextError(path, i, edits.length);\n\n\t\tconst exactWindow = validateRange(\n\t\t\tsourceEdit.range,\n\t\t\toriginalLineStarts,\n\t\t\tnormalizedContent.length,\n\t\t\tpath,\n\t\t\ti,\n\t\t\tedits.length,\n\t\t);\n\t\tlet matchIndex = findWithin(normalizedContent, oldText, exactWindow);\n\t\tlet matchLength = oldText.length;\n\t\tlet occurrences: number;\n\n\t\tif (matchIndex !== -1) {\n\t\t\toccurrences = countExactOccurrences(normalizedContent, oldText);\n\t\t} else {\n\t\t\tfuzzyContent ??= normalizeForFuzzyMatch(normalizedContent);\n\t\t\tfuzzyLineStarts ??= getLineStarts(fuzzyContent);\n\t\t\toriginalLineStarts ??= getLineStarts(normalizedContent);\n\t\t\tconst fuzzyOldText = normalizeForFuzzyMatch(oldText);\n\t\t\tconst fuzzyWindow = validateRange(\n\t\t\t\tsourceEdit.range,\n\t\t\t\tfuzzyLineStarts,\n\t\t\t\tfuzzyContent.length,\n\t\t\t\tpath,\n\t\t\t\ti,\n\t\t\t\tedits.length,\n\t\t\t);\n\t\t\tconst fuzzyIndex = findWithin(fuzzyContent, fuzzyOldText, fuzzyWindow);\n\t\t\tif (fuzzyIndex === -1) throw getNotFoundError(path, i, edits.length, sourceEdit.range);\n\t\t\toccurrences = countExactOccurrences(fuzzyContent, fuzzyOldText);\n\t\t\tconst replacementSpan = mapFuzzySpanToOriginal(\n\t\t\t\tnormalizedContent,\n\t\t\t\toriginalLineStarts,\n\t\t\t\tfuzzyLineStarts,\n\t\t\t\tfuzzyIndex,\n\t\t\t\tfuzzyOldText.length,\n\t\t\t);\n\t\t\tmatchIndex = replacementSpan.index;\n\t\t\tmatchLength = replacementSpan.length;\n\t\t}\n\n\t\tif (occurrences > 1) {\n\t\t\tthrow getDuplicateError(path, i, edits.length, occurrences);\n\t\t}\n\n\t\tmatchedEdits.push({\n\t\t\teditIndex: i,\n\t\t\tmatchIndex,\n\t\t\tmatchLength,\n\t\t\tnewText,\n\t\t\texpectedText: normalizedContent.slice(matchIndex, matchIndex + matchLength),\n\t\t});\n\t}\n\n\tmatchedEdits.sort((a, b) => a.matchIndex - b.matchIndex);\n\tfor (let i = 1; i < matchedEdits.length; i++) {\n\t\tconst previous = matchedEdits[i - 1];\n\t\tconst current = matchedEdits[i];\n\t\tif (rangesOverlap(previous, current)) {\n\t\t\tthrow new Error(\n\t\t\t\t`edits[${previous.editIndex}] and edits[${current.editIndex}] overlap in ${path}. Merge them into one edit or target disjoint regions.`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { baseLength: normalizedContent.length, editCount: edits.length, edits: matchedEdits };\n}\n\n/** Apply a validated match plan to the same LF-normalized source in one linear join. */\nexport function applyEditMatchPlan(normalizedContent: string, plan: EditMatchPlan, path: string): AppliedEditsResult {\n\tif (normalizedContent.length !== plan.baseLength) {\n\t\tthrow new Error(`The validated edit plan for ${path} is stale because the source length changed.`);\n\t}\n\n\tconst parts: string[] = [];\n\tlet sourceIndex = 0;\n\tfor (const edit of plan.edits) {\n\t\tif (!normalizedContent.startsWith(edit.expectedText, edit.matchIndex)) {\n\t\t\tthrow new Error(`The validated edit plan for ${path} is stale at edits[${edit.editIndex}].`);\n\t\t}\n\t\tparts.push(normalizedContent.slice(sourceIndex, edit.matchIndex), edit.newText);\n\t\tsourceIndex = edit.matchIndex + edit.matchLength;\n\t}\n\tparts.push(normalizedContent.slice(sourceIndex));\n\tconst newContent = parts.join(\"\");\n\n\tif (normalizedContent === newContent) throw getNoChangeError(path, plan.editCount);\n\treturn { baseContent: normalizedContent, newContent };\n}\n\nexport function applyEditsToNormalizedContent(\n\tnormalizedContent: string,\n\tedits: Edit[],\n\tpath: string,\n): AppliedEditsResult {\n\treturn applyEditMatchPlan(normalizedContent, planEditsToNormalizedContent(normalizedContent, edits, path), path);\n}\n\n/** Generate a standard unified patch. */\nexport function generateUnifiedPatch(path: string, oldContent: string, newContent: string, contextLines = 4): string {\n\treturn Diff.createTwoFilesPatch(path, path, oldContent, newContent, undefined, undefined, {\n\t\tcontext: contextLines,\n\t\theaderOptions: Diff.FILE_HEADERS_ONLY,\n\t});\n}\n\n/**\n * Generate a display-oriented diff string with line numbers and context.\n * Returns both the diff string and the first changed line number (in the new file).\n */\nexport function generateDiffString(\n\toldContent: string,\n\tnewContent: string,\n\tcontextLines = 4,\n): { diff: string; firstChangedLine: number | undefined } {\n\tconst parts = Diff.diffLines(oldContent, newContent);\n\tconst output: string[] = [];\n\n\tconst oldLines = oldContent.split(\"\\n\");\n\tconst newLines = newContent.split(\"\\n\");\n\tconst maxLineNum = Math.max(oldLines.length, newLines.length);\n\tconst lineNumWidth = String(maxLineNum).length;\n\n\tlet oldLineNum = 1;\n\tlet newLineNum = 1;\n\tlet lastWasChange = false;\n\tlet firstChangedLine: number | undefined;\n\n\tfor (let i = 0; i < parts.length; i++) {\n\t\tconst part = parts[i];\n\t\tconst raw = part.value.split(\"\\n\");\n\t\tif (raw[raw.length - 1] === \"\") {\n\t\t\traw.pop();\n\t\t}\n\n\t\tif (part.added || part.removed) {\n\t\t\t// Capture the first changed line (in the new file)\n\t\t\tif (firstChangedLine === undefined) {\n\t\t\t\tfirstChangedLine = newLineNum;\n\t\t\t}\n\n\t\t\t// Show the change\n\t\t\tfor (const line of raw) {\n\t\t\t\tif (part.added) {\n\t\t\t\t\tconst lineNum = String(newLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\toutput.push(`+${lineNum} ${line}`);\n\t\t\t\t\tnewLineNum++;\n\t\t\t\t} else {\n\t\t\t\t\t// removed\n\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\toutput.push(`-${lineNum} ${line}`);\n\t\t\t\t\toldLineNum++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tlastWasChange = true;\n\t\t} else {\n\t\t\t// Context lines - only show a few before/after changes\n\t\t\tconst nextPartIsChange = i < parts.length - 1 && (parts[i + 1].added || parts[i + 1].removed);\n\t\t\tconst hasLeadingChange = lastWasChange;\n\t\t\tconst hasTrailingChange = nextPartIsChange;\n\n\t\t\tif (hasLeadingChange && hasTrailingChange) {\n\t\t\t\tif (raw.length <= contextLines * 2) {\n\t\t\t\t\tfor (const line of raw) {\n\t\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\t\toutput.push(` ${lineNum} ${line}`);\n\t\t\t\t\t\toldLineNum++;\n\t\t\t\t\t\tnewLineNum++;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst leadingLines = raw.slice(0, contextLines);\n\t\t\t\t\tconst trailingLines = raw.slice(raw.length - contextLines);\n\t\t\t\t\tconst skippedLines = raw.length - leadingLines.length - trailingLines.length;\n\n\t\t\t\t\tfor (const line of leadingLines) {\n\t\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\t\toutput.push(` ${lineNum} ${line}`);\n\t\t\t\t\t\toldLineNum++;\n\t\t\t\t\t\tnewLineNum++;\n\t\t\t\t\t}\n\n\t\t\t\t\toutput.push(` ${\"\".padStart(lineNumWidth, \" \")} ...`);\n\t\t\t\t\toldLineNum += skippedLines;\n\t\t\t\t\tnewLineNum += skippedLines;\n\n\t\t\t\t\tfor (const line of trailingLines) {\n\t\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\t\toutput.push(` ${lineNum} ${line}`);\n\t\t\t\t\t\toldLineNum++;\n\t\t\t\t\t\tnewLineNum++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (hasLeadingChange) {\n\t\t\t\tconst shownLines = raw.slice(0, contextLines);\n\t\t\t\tconst skippedLines = raw.length - shownLines.length;\n\n\t\t\t\tfor (const line of shownLines) {\n\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\toutput.push(` ${lineNum} ${line}`);\n\t\t\t\t\toldLineNum++;\n\t\t\t\t\tnewLineNum++;\n\t\t\t\t}\n\n\t\t\t\tif (skippedLines > 0) {\n\t\t\t\t\toutput.push(` ${\"\".padStart(lineNumWidth, \" \")} ...`);\n\t\t\t\t\toldLineNum += skippedLines;\n\t\t\t\t\tnewLineNum += skippedLines;\n\t\t\t\t}\n\t\t\t} else if (hasTrailingChange) {\n\t\t\t\tconst skippedLines = Math.max(0, raw.length - contextLines);\n\t\t\t\tif (skippedLines > 0) {\n\t\t\t\t\toutput.push(` ${\"\".padStart(lineNumWidth, \" \")} ...`);\n\t\t\t\t\toldLineNum += skippedLines;\n\t\t\t\t\tnewLineNum += skippedLines;\n\t\t\t\t}\n\n\t\t\t\tfor (const line of raw.slice(skippedLines)) {\n\t\t\t\t\tconst lineNum = String(oldLineNum).padStart(lineNumWidth, \" \");\n\t\t\t\t\toutput.push(` ${lineNum} ${line}`);\n\t\t\t\t\toldLineNum++;\n\t\t\t\t\tnewLineNum++;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Skip these context lines entirely\n\t\t\t\toldLineNum += raw.length;\n\t\t\t\tnewLineNum += raw.length;\n\t\t\t}\n\n\t\t\tlastWasChange = false;\n\t\t}\n\t}\n\n\treturn { diff: output.join(\"\\n\"), firstChangedLine };\n}\n\nexport interface EditDiffResult {\n\tdiff: string;\n\tfirstChangedLine: number | undefined;\n}\n\nexport interface EditDiffError {\n\terror: string;\n}\n\nexport interface EditPlannedDiffResult extends EditDiffResult {\n\tplan: EditMatchPlan;\n}\n\n/**\n * Compute the diff for one or more edit operations without applying them.\n * Used for preview rendering in the TUI before the tool executes.\n */\nexport async function computeEditsPlannedDiff(\n\tpath: string,\n\tedits: Edit[],\n\tcwd: string,\n): Promise<EditPlannedDiffResult | EditDiffError> {\n\tconst absolutePath = resolveToCwd(path, cwd);\n\n\ttry {\n\t\t// Check if file exists and is readable\n\t\ttry {\n\t\t\tawait access(absolutePath, constants.R_OK);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error && \"code\" in error ? `Error code: ${error.code}` : String(error);\n\t\t\treturn { error: `Could not edit file: ${path}. ${errorMessage}.` };\n\t\t}\n\n\t\t// Read the file\n\t\tconst rawContent = await readFile(absolutePath, \"utf-8\");\n\n\t\t// Strip BOM before matching (LLM won't include invisible BOM in oldText)\n\t\tconst { text: content } = stripBom(rawContent);\n\t\tconst normalizedContent = normalizeToLF(content);\n\t\tconst plan = planEditsToNormalizedContent(normalizedContent, edits, path);\n\t\tconst { baseContent, newContent } = applyEditMatchPlan(normalizedContent, plan, path);\n\n\t\t// Generate the diff\n\t\treturn { ...generateDiffString(baseContent, newContent), plan };\n\t} catch (err) {\n\t\treturn { error: err instanceof Error ? err.message : String(err) };\n\t}\n}\n\nexport async function computeEditsDiff(\n\tpath: string,\n\tedits: Edit[],\n\tcwd: string,\n): Promise<EditDiffResult | EditDiffError> {\n\tconst result = await computeEditsPlannedDiff(path, edits, cwd);\n\tif (\"error\" in result) return result;\n\treturn { diff: result.diff, firstChangedLine: result.firstChangedLine };\n}\n\n/**\n * Compute the diff for a single edit operation without applying it.\n * Kept as a convenience wrapper for single-edit callers.\n */\nexport async function computeEditDiff(\n\tpath: string,\n\toldText: string,\n\tnewText: string,\n\tcwd: string,\n): Promise<EditDiffResult | EditDiffError> {\n\treturn computeEditsDiff(path, [{ oldText, newText }], cwd);\n}\n"]}
@@ -3,25 +3,40 @@ import { Box } from "@caupulican/pi-tui";
3
3
  import { type Static, Type } from "typebox";
4
4
  import type { ToolDefinition } from "../extensions/types.ts";
5
5
  import { type EditDiffError, type EditDiffResult } from "./edit-diff.ts";
6
+ import { FileMutationIntentController } from "./file-mutation-intent.ts";
6
7
  type EditPreview = EditDiffResult | EditDiffError;
7
8
  type EditRenderState = {
8
9
  callComponent?: EditCallRenderComponent;
9
10
  };
10
- declare const editSchema: Type.TObject<{
11
+ declare const editSchema: Type.TUnion<[Type.TObject<{
12
+ action: Type.TLiteral<"prepare">;
11
13
  path: Type.TString;
14
+ }>, Type.TObject<{
15
+ action: Type.TLiteral<"commit">;
16
+ path: Type.TString;
17
+ intentId: Type.TString;
12
18
  edits: Type.TArray<Type.TObject<{
13
19
  oldText: Type.TString;
14
20
  newText: Type.TString;
21
+ range: Type.TOptional<Type.TObject<{
22
+ startLine: Type.TInteger;
23
+ endLine: Type.TInteger;
24
+ }>>;
15
25
  }>>;
16
- }>;
26
+ }>]>;
17
27
  export type EditToolInput = Static<typeof editSchema>;
18
28
  export interface EditToolDetails {
29
+ phase: "prepared" | "committed";
30
+ intentId?: string;
31
+ contentRef?: string;
19
32
  /** Display-oriented diff of the changes made */
20
- diff: string;
33
+ diff?: string;
21
34
  /** Standard unified patch of the changes made */
22
- patch: string;
35
+ patch?: string;
23
36
  /** Line number of the first change in the new file (for editor navigation) */
24
37
  firstChangedLine?: number;
38
+ /** True when execution reused the match plan already validated for the call preview. */
39
+ matchPlanReused?: boolean;
25
40
  }
26
41
  /**
27
42
  * Pluggable operations for the edit tool.
@@ -32,16 +47,16 @@ export interface EditOperations {
32
47
  readFile: (absolutePath: string) => Promise<Buffer>;
33
48
  /** Write content to a file */
34
49
  writeFile: (absolutePath: string, content: string) => Promise<void>;
35
- /** Check if file is readable and writable (throw if not) */
36
- access: (absolutePath: string) => Promise<void>;
37
50
  }
38
51
  export interface EditToolOptions {
39
52
  /** Custom operations for file editing. Default: local filesystem */
40
53
  operations?: EditOperations;
54
+ /** Session-owned two-phase intent and exact-content-reference authority. */
55
+ intentController?: FileMutationIntentController;
41
56
  }
42
57
  type EditCallRenderComponent = Box & {
43
58
  preview?: EditPreview;
44
- previewArgsKey?: string;
59
+ previewRequestId: number;
45
60
  previewPending?: boolean;
46
61
  settledError?: boolean;
47
62
  };
@@ -1 +1 @@
1
- {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,GAAG,EAA2B,MAAM,oBAAoB,CAAC;AAGlE,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAKN,KAAK,aAAa,EAClB,KAAK,cAAc,EAMnB,MAAM,gBAAgB,CAAC;AAOxB,KAAK,WAAW,GAAG,cAAc,GAAG,aAAa,CAAC;AAElD,KAAK,eAAe,GAAG;IACtB,aAAa,CAAC,EAAE,uBAAuB,CAAC;CACxC,CAAC;AAaF,QAAA,MAAM,UAAU;;;;;;EASf,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAMtD,MAAM,WAAW,eAAe;IAC/B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,qCAAqC;IACrC,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,8BAA8B;IAC9B,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,4DAA4D;IAC5D,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAQD,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;CAC5B;AAwCD,KAAK,uBAAuB,GAAG,GAAG,GAAG;IACpC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AA8IF,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,EAAE,eAAe,CAAC,CAoJjF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG"}
1
+ {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,GAAG,EAA2B,MAAM,oBAAoB,CAAC;AAElE,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAMN,KAAK,aAAa,EAClB,KAAK,cAAc,EAOnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAMzE,KAAK,WAAW,GAAG,cAAc,GAAG,aAAa,CAAC;AAElD,KAAK,eAAe,GAAG;IACtB,aAAa,CAAC,EAAE,uBAAuB,CAAC;CACxC,CAAC;AAoBF,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;IAmBd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAUtD,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,UAAU,GAAG,WAAW,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wFAAwF;IACxF,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,qCAAqC;IACrC,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,8BAA8B;IAC9B,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACpE;AAOD,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,4BAA4B,CAAC;CAChD;AA6CD,KAAK,uBAAuB,GAAG,GAAG,GAAG;IACpC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAgLF,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,EAAE,eAAe,CAAC,CAkLjF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG"}
@@ -1,30 +1,40 @@
1
1
  import { Box, Container, Spacer, Text } from "@caupulican/pi-tui";
2
- import { constants } from "fs";
3
- import { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from "fs/promises";
2
+ import { readFile as fsReadFile, writeFile as fsWriteFile } from "fs/promises";
4
3
  import { Type } from "typebox";
5
4
  import { renderDiff } from "../../modes/interactive/components/diff.js";
6
- import { applyEditsToNormalizedContent, computeEditsDiff, detectLineEnding, generateDiffString, generateUnifiedPatch, normalizeToLF, restoreLineEndings, stripBom, } from "./edit-diff.js";
5
+ import { applyEditMatchPlan, applyEditsToNormalizedContent, computeEditsPlannedDiff, detectLineEnding, generateDiffString, generateUnifiedPatch, normalizeToLF, restoreLineEndings, stripBom, } from "./edit-diff.js";
7
6
  import { isValidUTF8 } from "./file-encoding-policy.js";
7
+ import { FileMutationIntentController } from "./file-mutation-intent.js";
8
8
  import { withFileMutationQueue } from "./file-mutation-queue.js";
9
9
  import { resolveToCwd } from "./path-utils.js";
10
10
  import { renderToolPath, str } from "./render-utils.js";
11
11
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
12
12
  const replaceEditSchema = Type.Object({
13
- oldText: Type.String({
14
- description: "Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.",
15
- }),
16
- newText: Type.String({ description: "Replacement text for this targeted edit." }),
17
- }, { additionalProperties: false });
18
- const editSchema = Type.Object({
19
- path: Type.String({ description: "Path to the file to edit (relative or absolute)" }),
20
- edits: Type.Array(replaceEditSchema, {
21
- description: "One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.",
22
- }),
13
+ oldText: Type.String({ minLength: 1 }),
14
+ newText: Type.String(),
15
+ range: Type.Optional(Type.Object({
16
+ startLine: Type.Integer({ minimum: 1 }),
17
+ endLine: Type.Integer({ minimum: 1 }),
18
+ }, { additionalProperties: false })),
23
19
  }, { additionalProperties: false });
20
+ const editPathSchema = Type.String({ minLength: 1 });
21
+ const editSchema = Type.Union([
22
+ Type.Object({
23
+ action: Type.Literal("prepare"),
24
+ path: editPathSchema,
25
+ }, { additionalProperties: false }),
26
+ Type.Object({
27
+ action: Type.Literal("commit"),
28
+ path: editPathSchema,
29
+ intentId: Type.String({ minLength: 1 }),
30
+ edits: Type.Array(replaceEditSchema, {
31
+ minItems: 1,
32
+ }),
33
+ }, { additionalProperties: false }),
34
+ ]);
24
35
  const defaultEditOperations = {
25
36
  readFile: (path) => fsReadFile(path),
26
37
  writeFile: (path, content) => fsWriteFile(path, content, "utf-8"),
27
- access: (path) => fsAccess(path, constants.R_OK | constants.W_OK),
28
38
  };
29
39
  function prepareEditArguments(input) {
30
40
  if (!input || typeof input !== "object") {
@@ -41,15 +51,17 @@ function prepareEditArguments(input) {
41
51
  return { ...rest, edits };
42
52
  }
43
53
  function validateEditInput(input) {
54
+ if (input.action === "prepare")
55
+ return input;
44
56
  if (!Array.isArray(input.edits) || input.edits.length === 0) {
45
57
  throw new Error("Edit tool input is invalid. edits must contain at least one replacement.");
46
58
  }
47
- return { path: input.path, edits: input.edits };
59
+ return { action: "commit", path: input.path, intentId: input.intentId, edits: input.edits };
48
60
  }
49
61
  function createEditCallRenderComponent() {
50
62
  return Object.assign(new Box(1, 1, (text) => text), {
51
63
  preview: undefined,
52
- previewArgsKey: undefined,
64
+ previewRequestId: 0,
53
65
  previewPending: false,
54
66
  settledError: false,
55
67
  });
@@ -71,23 +83,50 @@ function getRenderablePreviewInput(args) {
71
83
  if (!args) {
72
84
  return null;
73
85
  }
86
+ if (args.action !== "commit")
87
+ return null;
74
88
  const path = typeof args.path === "string" ? args.path : typeof args.file_path === "string" ? args.file_path : null;
75
89
  if (!path) {
76
90
  return null;
77
91
  }
92
+ if (typeof args.intentId !== "string" || args.intentId.length === 0)
93
+ return null;
78
94
  if (Array.isArray(args.edits) &&
79
95
  args.edits.length > 0 &&
80
96
  args.edits.every((edit) => typeof edit?.oldText === "string" && typeof edit?.newText === "string")) {
81
- return { path, edits: args.edits };
97
+ return { path, intentId: args.intentId, edits: args.edits };
82
98
  }
83
99
  if (typeof args.oldText === "string" && typeof args.newText === "string") {
84
- return { path, edits: [{ oldText: args.oldText, newText: args.newText }] };
100
+ return { path, intentId: args.intentId, edits: [{ oldText: args.oldText, newText: args.newText }] };
85
101
  }
86
102
  return null;
87
103
  }
104
+ function snapshotEdits(edits) {
105
+ return edits.map((edit) => ({
106
+ oldText: edit.oldText,
107
+ newText: edit.newText,
108
+ ...(edit.range ? { range: { startLine: edit.range.startLine, endLine: edit.range.endLine } } : {}),
109
+ }));
110
+ }
111
+ function editsMatch(left, right) {
112
+ if (left.length !== right.length)
113
+ return false;
114
+ for (let index = 0; index < left.length; index++) {
115
+ const leftEdit = left[index];
116
+ const rightEdit = right[index];
117
+ if (leftEdit.oldText !== rightEdit.oldText ||
118
+ leftEdit.newText !== rightEdit.newText ||
119
+ leftEdit.range?.startLine !== rightEdit.range?.startLine ||
120
+ leftEdit.range?.endLine !== rightEdit.range?.endLine) {
121
+ return false;
122
+ }
123
+ }
124
+ return true;
125
+ }
88
126
  function formatEditCall(args, theme, cwd) {
89
127
  const pathDisplay = renderToolPath(str(args?.file_path ?? args?.path), theme, cwd);
90
- return `${theme.fg("toolTitle", theme.bold("edit"))} ${pathDisplay}`;
128
+ const action = args?.action === "prepare" ? "prepare" : args?.action === "commit" ? "commit" : undefined;
129
+ return `${theme.fg("toolTitle", theme.bold("edit"))}${action ? ` ${theme.fg("muted", action)}` : ""} ${pathDisplay}`;
91
130
  }
92
131
  function formatEditResult(args, preview, result, theme, isError) {
93
132
  const rawPath = str(args?.file_path ?? args?.path);
@@ -133,7 +172,7 @@ function buildEditCallComponent(component, args, theme, cwd) {
133
172
  component.addChild(new Text(body, 0, 0));
134
173
  return component;
135
174
  }
136
- function setEditPreview(component, preview, argsKey) {
175
+ function setEditPreview(component, preview) {
137
176
  const current = component.preview;
138
177
  const changed = current === undefined ||
139
178
  ("error" in current && "error" in preview
@@ -143,28 +182,31 @@ function setEditPreview(component, preview, argsKey) {
143
182
  !("error" in preview) &&
144
183
  (current.diff !== preview.diff || current.firstChangedLine !== preview.firstChangedLine));
145
184
  component.preview = preview;
146
- component.previewArgsKey = argsKey;
147
185
  component.previewPending = false;
148
186
  return changed;
149
187
  }
150
188
  export function createEditToolDefinition(cwd, options) {
151
189
  const ops = options?.operations ?? defaultEditOperations;
190
+ if (options?.operations && !options.intentController) {
191
+ throw new Error("Custom edit operations require a matching file mutation intent controller.");
192
+ }
193
+ const intentController = options?.intentController ?? new FileMutationIntentController();
194
+ let cachedMatchPlan;
152
195
  return {
153
196
  name: "edit",
154
197
  label: "edit",
155
- description: "Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.",
156
- promptSnippet: "Make precise file edits with exact text replacement, including multiple disjoint edits in one call",
198
+ description: "Edit existing UTF-8 text: prepare(path), then commit(path,intentId,edits). oldText must be exact, unique, and non-overlapping; optional inclusive line ranges bind matches to prior reads; stale targets are rejected.",
199
+ promptSnippet: "Preflight existing files; commit exact, stale-safe edits",
157
200
  promptGuidelines: [
158
- "Use edit for precise changes (edits[].oldText must match exactly)",
159
- "When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls",
160
- "Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.",
161
- "Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.",
201
+ "Before replacements, prepare(path); then commit once with intentId and all edits.",
202
+ "oldText is exact, unique, minimal, and original-file based; pass the inclusive line range from the supplying read when known, batch separate edits, and merge overlaps.",
162
203
  ],
163
204
  parameters: editSchema,
164
205
  renderShell: "self",
165
206
  prepareArguments: prepareEditArguments,
166
207
  async execute(_toolCallId, input, signal, _onUpdate, _ctx) {
167
- const { path, edits } = validateEditInput(input);
208
+ const validated = validateEditInput(input);
209
+ const { path } = validated;
168
210
  const absolutePath = resolveToCwd(path, cwd);
169
211
  return withFileMutationQueue(absolutePath, async () => {
170
212
  // Do not reject from an abort event listener here: that would release the
@@ -176,20 +218,26 @@ export function createEditToolDefinition(cwd, options) {
176
218
  throw new Error("Operation aborted");
177
219
  };
178
220
  throwIfAborted();
179
- // Check if file exists.
180
- try {
181
- await ops.access(absolutePath);
182
- }
183
- catch (error) {
184
- throwIfAborted();
185
- const errorMessage = error instanceof Error && "code" in error ? `Error code: ${error.code}` : String(error);
186
- throw new Error(`Could not edit file: ${path}. ${errorMessage}.`);
221
+ if (validated.action === "prepare") {
222
+ const prepared = await intentController.prepare("edit", absolutePath, signal, path);
223
+ return {
224
+ content: [
225
+ {
226
+ type: "text",
227
+ text: `Edit target accepted. Commit ${path} with intentId ${prepared.intentId}.`,
228
+ },
229
+ ],
230
+ details: { phase: "prepared", intentId: prepared.intentId },
231
+ };
187
232
  }
233
+ const { edits, intentId } = validated;
234
+ const lease = intentController.consume(intentId, "edit", absolutePath);
235
+ await intentController.assertCurrent(lease, signal);
188
236
  throwIfAborted();
189
237
  // Read the file.
190
238
  const buffer = await ops.readFile(absolutePath);
191
239
  if (!isValidUTF8(buffer)) {
192
- throw new Error(`Could not edit file: ${path}. Binary or non-UTF-8 text cannot be safely edited by the text edit tool.`);
240
+ throw new Error(`PI_FILE_ENCODING_CORRUPTION: ${path} is not valid UTF-8 text; exact text replacement is unsafe and could corrupt its bytes.`);
193
241
  }
194
242
  const rawContent = buffer.toString("utf-8");
195
243
  throwIfAborted();
@@ -197,42 +245,67 @@ export function createEditToolDefinition(cwd, options) {
197
245
  const { bom, text: content } = stripBom(rawContent);
198
246
  const originalEnding = detectLineEnding(content);
199
247
  const normalizedContent = normalizeToLF(content);
200
- const { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);
248
+ const cachedForIntent = cachedMatchPlan?.intentId === intentId ? cachedMatchPlan : undefined;
249
+ if (cachedForIntent)
250
+ cachedMatchPlan = undefined;
251
+ const matchPlanReused = cachedForIntent !== undefined &&
252
+ cachedForIntent.absolutePath === absolutePath &&
253
+ editsMatch(cachedForIntent.edits, edits);
254
+ const { baseContent, newContent } = matchPlanReused
255
+ ? applyEditMatchPlan(normalizedContent, cachedForIntent.plan, path)
256
+ : applyEditsToNormalizedContent(normalizedContent, edits, path);
201
257
  throwIfAborted();
202
258
  const finalContent = bom + restoreLineEndings(newContent, originalEnding);
203
259
  await ops.writeFile(absolutePath, finalContent);
204
260
  throwIfAborted();
205
- const diffResult = generateDiffString(baseContent, newContent);
261
+ const contentReference = intentController.rememberContent(absolutePath, finalContent);
262
+ const diffResult = matchPlanReused
263
+ ? { diff: cachedForIntent.diff, firstChangedLine: cachedForIntent.firstChangedLine }
264
+ : generateDiffString(baseContent, newContent);
206
265
  const patch = generateUnifiedPatch(path, baseContent, newContent);
207
266
  return {
208
267
  content: [
209
268
  {
210
269
  type: "text",
211
- text: `Successfully replaced ${edits.length} block(s) in ${path}.`,
270
+ text: `Successfully replaced ${edits.length} block(s) in ${path}. Reuse exact bytes with contentRef ${contentReference.contentRef}.`,
212
271
  },
213
272
  ],
214
- details: { diff: diffResult.diff, patch, firstChangedLine: diffResult.firstChangedLine },
273
+ details: {
274
+ phase: "committed",
275
+ contentRef: contentReference.contentRef,
276
+ diff: diffResult.diff,
277
+ patch,
278
+ firstChangedLine: diffResult.firstChangedLine,
279
+ matchPlanReused,
280
+ },
215
281
  };
216
282
  });
217
283
  },
218
284
  renderCall(args, theme, context) {
219
285
  const component = getEditCallRenderComponent(context.state, context.lastComponent);
220
286
  const previewInput = getRenderablePreviewInput(args);
221
- const argsKey = previewInput
222
- ? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })
223
- : undefined;
224
- if (component.previewArgsKey !== argsKey) {
287
+ if ((!context.argsComplete || !previewInput) && (component.preview || component.previewPending)) {
225
288
  component.preview = undefined;
226
- component.previewArgsKey = argsKey;
227
289
  component.previewPending = false;
228
290
  component.settledError = false;
291
+ component.previewRequestId++;
229
292
  }
230
293
  if (context.argsComplete && previewInput && !component.preview && !component.previewPending) {
231
294
  component.previewPending = true;
232
- const requestKey = argsKey;
233
- void computeEditsDiff(previewInput.path, previewInput.edits, context.cwd).then((preview) => {
234
- if (component.previewArgsKey === requestKey) {
235
- setEditPreview(component, preview, requestKey);
295
+ const requestId = ++component.previewRequestId;
296
+ void computeEditsPlannedDiff(previewInput.path, previewInput.edits, context.cwd).then((preview) => {
297
+ if (component.previewRequestId === requestId) {
298
+ if (!("error" in preview) && !options?.operations) {
299
+ cachedMatchPlan = {
300
+ intentId: previewInput.intentId,
301
+ absolutePath: resolveToCwd(previewInput.path, context.cwd),
302
+ edits: snapshotEdits(previewInput.edits),
303
+ plan: preview.plan,
304
+ diff: preview.diff,
305
+ firstChangedLine: preview.firstChangedLine,
306
+ };
307
+ }
308
+ setEditPreview(component, preview);
236
309
  context.invalidate();
237
310
  }
238
311
  });
@@ -241,17 +314,17 @@ export function createEditToolDefinition(cwd, options) {
241
314
  },
242
315
  renderResult(result, _options, theme, context) {
243
316
  const callComponent = context.state.callComponent;
244
- const previewInput = getRenderablePreviewInput(context.args);
245
- const argsKey = previewInput
246
- ? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })
247
- : undefined;
248
317
  const typedResult = result;
249
318
  const resultDiff = !context.isError ? typedResult.details?.diff : undefined;
250
319
  let changed = false;
251
320
  if (callComponent) {
321
+ callComponent.previewRequestId++;
252
322
  if (typeof resultDiff === "string") {
253
323
  changed =
254
- setEditPreview(callComponent, { diff: resultDiff, firstChangedLine: typedResult.details?.firstChangedLine }, argsKey) || changed;
324
+ setEditPreview(callComponent, {
325
+ diff: resultDiff,
326
+ firstChangedLine: typedResult.details?.firstChangedLine,
327
+ }) || changed;
255
328
  }
256
329
  if (callComponent.settledError !== context.isError) {
257
330
  callComponent.settledError = context.isError;