Files
gnome-jarvis/vendor/agent-harness/agent/search-replace.js
T
2026-09-11 13:41:22 -04:00

66 lines
1.9 KiB
JavaScript

/**
* Unique search/replace (Grok-shaped). No Bare imports.
*/
function countOccurrences(hay, needle) {
if (!needle) return 0;
const h = String(hay || '');
const n = String(needle);
let count = 0;
let i = 0;
while (i < h.length) {
const at = h.indexOf(n, i);
if (at < 0) break;
count += 1;
i = at + Math.max(1, n.length);
}
return count;
}
function applySearchReplace(cur, oldString, newString, replaceAll) {
const old = oldString == null ? '' : String(oldString);
const neu = newString == null ? '' : String(newString);
const text = cur == null ? '' : String(cur);
if (!old) {
if (text.trim()) {
throw new Error(
'old_string is empty but the file is not empty; refuse overwrite. Use write_file to replace the whole file, or pass a unique old_string.'
);
}
return { text: neu, created: true, replacements: 1 };
}
const n = countOccurrences(text, old);
if (n === 0) throw new Error('old_string not found');
if (n > 1 && !replaceAll) {
throw new Error(
'old_string matched ' + n + ' times; add surrounding lines to make it unique, or set replace_all to true.'
);
}
const next = replaceAll ? text.split(old).join(neu) : text.replace(old, neu);
return { text: next, created: false, replacements: replaceAll ? n : 1 };
}
function contextSnippet(text, needle, radius) {
const lines = String(text || '').split('\n');
const r = radius == null ? 3 : radius;
const want = String(needle || '');
let idx = -1;
if (want) {
for (let i = 0; i < lines.length; i++) {
if (lines[i].indexOf(want) >= 0) {
idx = i;
break;
}
}
}
if (idx < 0) idx = 0;
const start = Math.max(0, idx - r);
const end = Math.min(lines.length, idx + r + 1);
return lines
.slice(start, end)
.map((l, i) => String(start + i + 1).padStart(6) + '| ' + l)
.join('\n');
}
module.exports = { countOccurrences, applySearchReplace, contextSnippet };