@heyhuynhgiabuu/pi-diff 0.7.2 → 0.7.4
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.
- package/dist/core/apply-patch.d.ts +41 -0
- package/dist/core/apply-patch.d.ts.map +1 -0
- package/dist/core/apply-patch.js +208 -0
- package/dist/core/apply-patch.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +244 -279
- package/dist/index.js.map +1 -1
- package/dist/review/hunk-preview.d.ts +5 -2
- package/dist/review/hunk-preview.d.ts.map +1 -1
- package/dist/review/hunk-preview.js +16 -11
- package/dist/review/hunk-preview.js.map +1 -1
- package/package.json +60 -115
package/dist/index.js
CHANGED
|
@@ -21,16 +21,14 @@
|
|
|
21
21
|
* • Async rendering with invalidate() for non-blocking preview
|
|
22
22
|
*/
|
|
23
23
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
24
|
-
import { extname, relative
|
|
24
|
+
import { extname, relative } from "node:path";
|
|
25
25
|
import { codeToANSI } from "@shikijs/cli";
|
|
26
26
|
import * as Diff from "diff";
|
|
27
|
+
import { executeApplyPatch, formatApplyPatchResult } from "./core/apply-patch.js";
|
|
27
28
|
import { configIndicatorStyle } from "./core/config.js";
|
|
28
29
|
import { computeHunkBlocks, getSepStyle, parseDiff, parsePatchFiles, resolveSepStyle, sepLabelSplit, sepLabelUnified, } from "./core/diff.js";
|
|
29
30
|
import { replace } from "./core/replace.js";
|
|
30
31
|
import { registerEditGuard } from "./edit-guard.js";
|
|
31
|
-
import { initHashline } from "./hashline.js";
|
|
32
|
-
import { executeHashlineRead, HASHLINE_EDIT_DESC, HASHLINE_READ_DESC, runHashlineEdit, } from "./core/hashline-execute.js";
|
|
33
|
-
import * as fs from "node:fs";
|
|
34
32
|
import { applyDiffPalette as applySharedDiffPalette, lang as detectDiffLanguage, renderSplit as renderSharedSplit, resolveDiffColors as resolveSharedDiffColors, themeCacheKey as sharedThemeCacheKey, } from "./review/hunk-preview.js";
|
|
35
33
|
const DIFF_PRESETS = {
|
|
36
34
|
default: {
|
|
@@ -207,10 +205,7 @@ function autoDeriveBgFromTheme(theme) {
|
|
|
207
205
|
}
|
|
208
206
|
/** Load diff theme config from .pi/settings.json (project-level, then global). */
|
|
209
207
|
function loadDiffConfig() {
|
|
210
|
-
const paths = [
|
|
211
|
-
`${process.cwd()}/.pi/settings.json`,
|
|
212
|
-
`${process.env.HOME ?? ""}/.pi/settings.json`,
|
|
213
|
-
];
|
|
208
|
+
const paths = [`${process.cwd()}/.pi/settings.json`, `${process.env.HOME ?? ""}/.pi/settings.json`];
|
|
214
209
|
for (const p of paths) {
|
|
215
210
|
try {
|
|
216
211
|
if (existsSync(p)) {
|
|
@@ -923,10 +918,7 @@ async function renderUnified(diff, language, max = MAX_RENDER_LINES, dc = DEFAUL
|
|
|
923
918
|
newSrc.push(l.content);
|
|
924
919
|
}
|
|
925
920
|
const [oldHL, newHL] = canHL
|
|
926
|
-
? await Promise.all([
|
|
927
|
-
hlBlock(oldSrc.join("\n"), language),
|
|
928
|
-
hlBlock(newSrc.join("\n"), language),
|
|
929
|
-
])
|
|
921
|
+
? await Promise.all([hlBlock(oldSrc.join("\n"), language), hlBlock(newSrc.join("\n"), language)])
|
|
930
922
|
: [oldSrc, newSrc];
|
|
931
923
|
let oI = 0, nI = 0, idx = 0;
|
|
932
924
|
const out = [];
|
|
@@ -1069,10 +1061,7 @@ async function renderSplit(diff, language, max = MAX_PREVIEW_LINES, dc = DEFAULT
|
|
|
1069
1061
|
rightSrc.push(r.right.content);
|
|
1070
1062
|
}
|
|
1071
1063
|
const [leftHL, rightHL] = canHL
|
|
1072
|
-
? await Promise.all([
|
|
1073
|
-
hlBlock(leftSrc.join("\n"), language),
|
|
1074
|
-
hlBlock(rightSrc.join("\n"), language),
|
|
1075
|
-
])
|
|
1064
|
+
? await Promise.all([hlBlock(leftSrc.join("\n"), language), hlBlock(rightSrc.join("\n"), language)])
|
|
1076
1065
|
: [leftSrc, rightSrc];
|
|
1077
1066
|
let lI = 0, rI = 0;
|
|
1078
1067
|
let stripeRow = 0; // tracks row index for diagonal stripe offset
|
|
@@ -1094,13 +1083,7 @@ async function renderSplit(diff, language, max = MAX_PREVIEW_LINES, dc = DEFAULT
|
|
|
1094
1083
|
const cBg = isDel ? BG_DEL : isAdd ? BG_ADD : BG_BASE;
|
|
1095
1084
|
const sFg = isDel ? dc.fgDel : isAdd ? dc.fgAdd : dc.fgCtx;
|
|
1096
1085
|
const sign = isDel ? "-" : isAdd ? "+" : " ";
|
|
1097
|
-
const num = isDel
|
|
1098
|
-
? line.oldNum
|
|
1099
|
-
: isAdd
|
|
1100
|
-
? line.newNum
|
|
1101
|
-
: side === "left"
|
|
1102
|
-
? line.oldNum
|
|
1103
|
-
: line.newNum;
|
|
1086
|
+
const num = isDel ? line.oldNum : isAdd ? line.newNum : side === "left" ? line.oldNum : line.newNum;
|
|
1104
1087
|
// Border bar + colored line numbers for changed lines
|
|
1105
1088
|
const borderFg = isDel ? dc.fgDel : isAdd ? dc.fgAdd : "";
|
|
1106
1089
|
const border = borderFg ? `${borderFg}${getBorderBar()}${RST}` : `${BG_BASE}`;
|
|
@@ -1217,31 +1200,20 @@ export default async function diffRendererExtension(pi) {
|
|
|
1217
1200
|
return;
|
|
1218
1201
|
writeHeaderStatsByCallId.set(toolCallId, { added, removed });
|
|
1219
1202
|
}
|
|
1220
|
-
const hashlineEditHeaderStatsByCallId = new Map();
|
|
1221
|
-
function stashHashlineEditHeaderStats(toolCallId, diffLines, added, removed) {
|
|
1222
|
-
if (!toolCallId)
|
|
1223
|
-
return;
|
|
1224
|
-
hashlineEditHeaderStatsByCallId.set(toolCallId, { diffLines, added, removed });
|
|
1225
|
-
}
|
|
1226
|
-
function hashlineEditCallStatsSuffix(toolCallId, theme) {
|
|
1227
|
-
const raw = toolCallId ? hashlineEditHeaderStatsByCallId.get(toolCallId) : undefined;
|
|
1228
|
-
if (!raw)
|
|
1229
|
-
return "";
|
|
1230
|
-
const loc = raw.diffLines > 0
|
|
1231
|
-
? `${theme.fg("muted", `${raw.diffLines} diff line${raw.diffLines === 1 ? "" : "s"}`)} `
|
|
1232
|
-
: "";
|
|
1233
|
-
return `${TOOL_RESULT_INDENT}${loc}${summarizeThemed(raw.added, raw.removed, theme)}`;
|
|
1234
|
-
}
|
|
1235
1203
|
const cwd = process.cwd();
|
|
1236
1204
|
const home = process.env.HOME ?? "";
|
|
1237
1205
|
const sp = (p) => shortPath(cwd, home, p);
|
|
1238
1206
|
const TOOL_RESULT_INDENT = " ";
|
|
1239
1207
|
const TOOL_HEADER_LEFT_PAD = 0;
|
|
1240
1208
|
const DIFF_BODY_LEFT_PAD = 0;
|
|
1241
|
-
/**
|
|
1242
|
-
const EDIT_DIFF_RESULT_FRAME = {
|
|
1243
|
-
|
|
1244
|
-
|
|
1209
|
+
/** Built-in `edit` tool diff result frame; intentionally offset from read/write/apply_patch previews. */
|
|
1210
|
+
const EDIT_DIFF_RESULT_FRAME = {
|
|
1211
|
+
headerLeftPad: 1,
|
|
1212
|
+
bodyLeftPad: 1,
|
|
1213
|
+
topPad: 1,
|
|
1214
|
+
bottomPad: 0,
|
|
1215
|
+
previewBottomPad: 1,
|
|
1216
|
+
};
|
|
1245
1217
|
function resolvePreviewDiffColors(theme) {
|
|
1246
1218
|
resolveDiffColors(theme);
|
|
1247
1219
|
return resolveSharedDiffColors(theme);
|
|
@@ -1251,19 +1223,114 @@ export default async function diffRendererExtension(pi) {
|
|
|
1251
1223
|
const padding = " ".repeat(Math.max(0, renderWidth - strip(content).length));
|
|
1252
1224
|
return injectBg(`${content}${padding}`, [], BG_BASE, BG_BASE);
|
|
1253
1225
|
}
|
|
1254
|
-
function
|
|
1255
|
-
const {
|
|
1226
|
+
function formatToolFrameHeaderText(opts) {
|
|
1227
|
+
const { topPad = 0, bottomPad = 0, headerLeftPad, suffix = "", label, filePath, theme, meta } = opts;
|
|
1256
1228
|
const leftPad = " ".repeat(headerLeftPad ?? TOOL_HEADER_LEFT_PAD);
|
|
1257
|
-
const content =
|
|
1258
|
-
? `${leftPad}${
|
|
1259
|
-
: `${leftPad}${
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1229
|
+
const content = meta !== undefined && meta !== null
|
|
1230
|
+
? `${leftPad}${meta}${suffix}`
|
|
1231
|
+
: `${leftPad}${theme.fg("toolTitle", theme.bold(label ?? ""))} ${theme.fg("accent", sp(filePath ?? ""))}${suffix}`;
|
|
1232
|
+
return `${"\n".repeat(topPad)}${content}${"\n".repeat(bottomPad)}`;
|
|
1233
|
+
}
|
|
1234
|
+
function formatToolFrameHeader(opts) {
|
|
1235
|
+
const { width, ...rest } = opts;
|
|
1236
|
+
return bgLine(formatToolFrameHeaderText(rest), width);
|
|
1237
|
+
}
|
|
1238
|
+
function setToolHeaderBg(text) {
|
|
1239
|
+
text.customBgFn = (line) => injectBg(line, [], BG_BASE, BG_BASE);
|
|
1240
|
+
}
|
|
1241
|
+
function clearToolHeaderBg(text) {
|
|
1242
|
+
text.customBgFn = undefined;
|
|
1243
|
+
}
|
|
1244
|
+
function summarizeApplyPatchChanges(changes, theme) {
|
|
1245
|
+
const labels = changes.map((change) => theme.fg("accent", sp(change.path)));
|
|
1246
|
+
if (labels.length <= 1)
|
|
1247
|
+
return labels.join("");
|
|
1248
|
+
return `${labels[0]}${theme.fg("muted", `, +${labels.length - 1} more`)}`;
|
|
1249
|
+
}
|
|
1250
|
+
function renderApplyPatchPreview(text, result, theme, ctx) {
|
|
1251
|
+
const applied = Array.isArray(result?.applied) ? result.applied : [];
|
|
1252
|
+
if (!applied.length)
|
|
1253
|
+
return false;
|
|
1254
|
+
const w = termW();
|
|
1255
|
+
const previewable = applied.filter((change) => {
|
|
1256
|
+
if (typeof change?.path !== "string")
|
|
1257
|
+
return false;
|
|
1258
|
+
if (change.action === "add")
|
|
1259
|
+
return typeof change.newContent === "string";
|
|
1260
|
+
if (change.action === "update" || change.action === "delete")
|
|
1261
|
+
return typeof change.oldContent === "string";
|
|
1262
|
+
return false;
|
|
1263
|
+
});
|
|
1264
|
+
if (previewable.length !== applied.length)
|
|
1265
|
+
return false;
|
|
1266
|
+
if (previewable.length === 1) {
|
|
1267
|
+
const change = previewable[0];
|
|
1268
|
+
if (change.action === "add" && typeof change.newContent === "string") {
|
|
1269
|
+
clearToolHeaderBg(text);
|
|
1270
|
+
resolvePreviewDiffColors(theme);
|
|
1271
|
+
const lineCount = change.newContent.split("\n").length;
|
|
1272
|
+
const newHdr = bgLine(`${theme.fg("success", `✓ new file (${lineCount} lines)`)}`, w);
|
|
1273
|
+
const fp = change.path;
|
|
1274
|
+
const pk = `ap:nf:${sharedThemeCacheKey(theme)}:${fp}:${lineCount}`;
|
|
1275
|
+
if (ctx.state._nfk !== pk) {
|
|
1276
|
+
ctx.state._nfk = pk;
|
|
1277
|
+
const lg = detectDiffLanguage(fp);
|
|
1278
|
+
text.__piDiffTask = {
|
|
1279
|
+
placeholder: `${newHdr}\n${padDiffBody(theme.fg("muted", "rendering file…"))}`,
|
|
1280
|
+
fallback: `${newHdr}`,
|
|
1281
|
+
invalidate: ctx.invalidate,
|
|
1282
|
+
key: (width) => `ap:nf:${sharedThemeCacheKey(theme)}:${fp}:${lineCount}:${width}`,
|
|
1283
|
+
render: async (_width) => {
|
|
1284
|
+
const hlLines = await hlBlock(change.newContent, lg);
|
|
1285
|
+
const preview = hlLines.join("\n").replace(/\n+$/, "");
|
|
1286
|
+
return `${newHdr}\n${padDiffBody(preview)}`;
|
|
1287
|
+
},
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
return true;
|
|
1291
|
+
}
|
|
1292
|
+
if ((change.action === "update" || change.action === "delete") &&
|
|
1293
|
+
typeof change.oldContent === "string" &&
|
|
1294
|
+
typeof change.path === "string") {
|
|
1295
|
+
const parsed = parseDiff(change.oldContent, change.newContent ?? "");
|
|
1296
|
+
setDiffPreviewTask(text, "ap", (width) => formatToolFrameHeader({
|
|
1297
|
+
meta: `${theme.fg("toolTitle", theme.bold("apply_patch"))}${TOOL_RESULT_INDENT}${theme.fg("muted", `(1 change)`)}${TOOL_RESULT_INDENT}${theme.fg("accent", sp(change.path))}`,
|
|
1298
|
+
theme,
|
|
1299
|
+
width,
|
|
1300
|
+
topPad: 0,
|
|
1301
|
+
bottomPad: 0,
|
|
1302
|
+
}), parsed, detectDiffLanguage(change.path), MAX_PREVIEW_LINES, theme, ctx, { previewBottomPad: 1, compactGutter: true });
|
|
1303
|
+
return true;
|
|
1304
|
+
}
|
|
1305
|
+
return false;
|
|
1306
|
+
}
|
|
1307
|
+
clearToolHeaderBg(text);
|
|
1308
|
+
let added = 0;
|
|
1309
|
+
let removed = 0;
|
|
1310
|
+
let chars = 0;
|
|
1311
|
+
let language;
|
|
1312
|
+
let mixedLanguage = false;
|
|
1313
|
+
const lines = [];
|
|
1314
|
+
for (const change of previewable) {
|
|
1315
|
+
const parsed = parseDiff(change.oldContent ?? "", change.newContent ?? "");
|
|
1316
|
+
const nextLanguage = detectDiffLanguage(change.path);
|
|
1317
|
+
if (!language)
|
|
1318
|
+
language = nextLanguage;
|
|
1319
|
+
else if (language !== nextLanguage)
|
|
1320
|
+
mixedLanguage = true;
|
|
1321
|
+
added += parsed.added;
|
|
1322
|
+
removed += parsed.removed;
|
|
1323
|
+
chars += parsed.chars;
|
|
1324
|
+
lines.push(...parsed.lines);
|
|
1325
|
+
}
|
|
1326
|
+
setDiffPreviewTask(text, "ap", (width) => formatToolFrameHeader({
|
|
1327
|
+
meta: `${theme.fg("toolTitle", theme.bold("apply_patch"))}${TOOL_RESULT_INDENT}${theme.fg("muted", `(${previewable.length} changes)`)} ${summarizeThemed(added, removed, theme)}${TOOL_RESULT_INDENT}${theme.fg("muted", summarizeApplyPatchChanges(previewable, theme))}`,
|
|
1328
|
+
theme,
|
|
1329
|
+
width,
|
|
1330
|
+
topPad: 0,
|
|
1331
|
+
bottomPad: 0,
|
|
1332
|
+
}), { lines, added, removed, chars }, mixedLanguage ? undefined : language, MAX_PREVIEW_LINES, theme, ctx, { previewBottomPad: 1, compactGutter: true });
|
|
1333
|
+
return true;
|
|
1267
1334
|
}
|
|
1268
1335
|
function editEditsCountLabel(edits, diffLines, theme) {
|
|
1269
1336
|
const n = edits === 1 ? "1 edit" : `${edits} edits`;
|
|
@@ -1279,9 +1346,7 @@ export default async function diffRendererExtension(pi) {
|
|
|
1279
1346
|
function formatEditDiffResultTitle(d, theme, width, frame) {
|
|
1280
1347
|
const fp = d.filePath ?? d.summary ?? "";
|
|
1281
1348
|
const edits = d.edits ?? d.editCount ?? 1;
|
|
1282
|
-
const diffLines = typeof d.diffLineCount === "number"
|
|
1283
|
-
? d.diffLineCount
|
|
1284
|
-
: (d.linesAdded ?? 0) + (d.linesRemoved ?? 0);
|
|
1349
|
+
const diffLines = typeof d.diffLineCount === "number" ? d.diffLineCount : (d.linesAdded ?? 0) + (d.linesRemoved ?? 0);
|
|
1285
1350
|
const suffix = `${TOOL_RESULT_INDENT}${theme.fg("muted", editEditsCountLabel(edits, diffLines, theme))} ${summarizeThemed(d.linesAdded ?? 0, d.linesRemoved ?? 0, theme)}`;
|
|
1286
1351
|
return formatToolFrameHeader({
|
|
1287
1352
|
width,
|
|
@@ -1300,16 +1365,20 @@ export default async function diffRendererExtension(pi) {
|
|
|
1300
1365
|
return "";
|
|
1301
1366
|
return `${TOOL_RESULT_INDENT}${summarizeThemed(raw.added, raw.removed, theme)}`;
|
|
1302
1367
|
}
|
|
1303
|
-
function padDiffBody(rendered) {
|
|
1304
|
-
const leftPad = `${BG_BASE}${" ".repeat(
|
|
1368
|
+
function padDiffBody(rendered, bodyLeftPad = DIFF_BODY_LEFT_PAD) {
|
|
1369
|
+
const leftPad = `${BG_BASE}${" ".repeat(bodyLeftPad)}${RST}`;
|
|
1305
1370
|
return rendered
|
|
1306
1371
|
.split("\n")
|
|
1307
1372
|
.map((line) => `${leftPad}${line}`)
|
|
1308
1373
|
.join("\n");
|
|
1309
1374
|
}
|
|
1310
|
-
async function renderPaddedDiff(diff, language, maxLines, colors, width) {
|
|
1311
|
-
const bodyWidth = Math.max(1, width -
|
|
1312
|
-
return padDiffBody(await renderSharedSplit(diff, language, maxLines, colors, bodyWidth));
|
|
1375
|
+
async function renderPaddedDiff(diff, language, maxLines, colors, width, bodyLeftPad = DIFF_BODY_LEFT_PAD) {
|
|
1376
|
+
const bodyWidth = Math.max(1, width - bodyLeftPad);
|
|
1377
|
+
return padDiffBody(await renderSharedSplit(diff, language, maxLines, colors, bodyWidth), bodyLeftPad);
|
|
1378
|
+
}
|
|
1379
|
+
async function renderPaddedCompactDiff(diff, language, maxLines, colors, width, bodyLeftPad = DIFF_BODY_LEFT_PAD) {
|
|
1380
|
+
const bodyWidth = Math.max(1, width - bodyLeftPad);
|
|
1381
|
+
return padDiffBody(await renderSharedSplit(diff, language, maxLines, colors, bodyWidth, { compactGutter: true }), bodyLeftPad);
|
|
1313
1382
|
}
|
|
1314
1383
|
function diffLineCountLabel(diffLineCount, theme) {
|
|
1315
1384
|
if (typeof diffLineCount !== "number")
|
|
@@ -1319,13 +1388,13 @@ export default async function diffRendererExtension(pi) {
|
|
|
1319
1388
|
function setToolHeaderText(text, meta, theme) {
|
|
1320
1389
|
resolvePreviewDiffColors(theme);
|
|
1321
1390
|
text.__piDiffTask = undefined;
|
|
1322
|
-
|
|
1323
|
-
text.setText(
|
|
1391
|
+
setToolHeaderBg(text);
|
|
1392
|
+
text.setText(formatToolFrameHeaderText({ meta, bottomPad: 0 }));
|
|
1324
1393
|
}
|
|
1325
1394
|
function setDiffPreviewTask(text, keyPrefix, metaOrHeader, diff, language, maxLines, theme, ctx, frame) {
|
|
1395
|
+
clearToolHeaderBg(text);
|
|
1326
1396
|
const themeKey = sharedThemeCacheKey(theme);
|
|
1327
1397
|
const colors = resolvePreviewDiffColors(theme);
|
|
1328
|
-
const headerKey = typeof metaOrHeader === "function" ? "fn:" + keyPrefix : metaOrHeader;
|
|
1329
1398
|
const header = frame?.omitHeader
|
|
1330
1399
|
? (_width) => ""
|
|
1331
1400
|
: typeof metaOrHeader === "function"
|
|
@@ -1345,11 +1414,16 @@ export default async function diffRendererExtension(pi) {
|
|
|
1345
1414
|
return bottom ? `${main}\n${bottom}` : main;
|
|
1346
1415
|
};
|
|
1347
1416
|
text.__piDiffTask = {
|
|
1348
|
-
placeholder: joinHeaderBody(termW(), padDiffBody(theme.fg("muted", " rendering diff…"))),
|
|
1417
|
+
placeholder: joinHeaderBody(termW(), padDiffBody(theme.fg("muted", " rendering diff…"), frame?.bodyLeftPad)),
|
|
1349
1418
|
fallback: header(termW()),
|
|
1350
1419
|
invalidate: ctx.invalidate,
|
|
1351
|
-
key: (width) =>
|
|
1352
|
-
|
|
1420
|
+
key: (width) => {
|
|
1421
|
+
const headerKey = frame?.omitHeader ? "" : header(width);
|
|
1422
|
+
return `${keyPrefix}:${themeKey}:${width}:${headerKey}:${diff.lines.length}:${language ?? ""}:${frame?.omitHeader ? "oh" : "h"}:${frame?.headerLeftPad ?? 0}:${frame?.topPad ?? 0}:${frame?.bottomPad ?? 0}:${frame?.previewBottomPad ?? 0}:${frame?.compactGutter ? "cg" : "rg"}:${frame?.bodyLeftPad ?? 0}`;
|
|
1423
|
+
},
|
|
1424
|
+
render: async (width) => joinHeaderBody(width, await (frame?.compactGutter
|
|
1425
|
+
? renderPaddedCompactDiff(diff, language, maxLines, colors, width, frame?.bodyLeftPad)
|
|
1426
|
+
: renderPaddedDiff(diff, language, maxLines, colors, width, frame?.bodyLeftPad))),
|
|
1353
1427
|
};
|
|
1354
1428
|
}
|
|
1355
1429
|
/** Wrap a Text component so its render(width) kicks off async diff rendering
|
|
@@ -1450,7 +1524,8 @@ export default async function diffRendererExtension(pi) {
|
|
|
1450
1524
|
if (args?.content && !ctx.argsComplete) {
|
|
1451
1525
|
const n = String(args.content).split("\n").length;
|
|
1452
1526
|
const suffix = `${TOOL_RESULT_INDENT}${theme.fg("muted", `(${n} lines…)`)}${stats ? ` ${stats.trimStart()}` : ""}`;
|
|
1453
|
-
text
|
|
1527
|
+
setToolHeaderBg(text);
|
|
1528
|
+
text.setText(formatToolFrameHeaderText({ label, filePath: fp, theme, suffix, topPad: 0, bottomPad: 0 }));
|
|
1454
1529
|
return text;
|
|
1455
1530
|
}
|
|
1456
1531
|
if (args?.content && ctx.argsComplete && isNew) {
|
|
@@ -1469,10 +1544,12 @@ export default async function diffRendererExtension(pi) {
|
|
|
1469
1544
|
})
|
|
1470
1545
|
.catch(() => { });
|
|
1471
1546
|
}
|
|
1547
|
+
clearToolHeaderBg(text);
|
|
1472
1548
|
text.setText(ctx.state._previewText ?? title);
|
|
1473
1549
|
return text;
|
|
1474
1550
|
}
|
|
1475
|
-
text
|
|
1551
|
+
setToolHeaderBg(text);
|
|
1552
|
+
text.setText(formatToolFrameHeaderText({ label, filePath: fp, theme, suffix: stats, topPad: 0, bottomPad: 0 }));
|
|
1476
1553
|
return text;
|
|
1477
1554
|
},
|
|
1478
1555
|
renderResult(result, _opt, theme, ctx) {
|
|
@@ -1483,21 +1560,28 @@ export default async function diffRendererExtension(pi) {
|
|
|
1483
1560
|
.map((c) => c.text || "")
|
|
1484
1561
|
.join("\n") ?? "Error";
|
|
1485
1562
|
text.__piDiffTask = undefined;
|
|
1563
|
+
clearToolHeaderBg(text);
|
|
1486
1564
|
text.setText(`\n${theme.fg("error", e)}`);
|
|
1487
1565
|
return text;
|
|
1488
1566
|
}
|
|
1489
1567
|
const d = result.details;
|
|
1490
1568
|
if (d?._type === "diff") {
|
|
1491
|
-
setDiffPreviewTask(text, "wd", "", d.diff, d.language, MAX_RENDER_LINES, theme, ctx, {
|
|
1569
|
+
setDiffPreviewTask(text, "wd", "", d.diff, d.language, MAX_RENDER_LINES, theme, ctx, {
|
|
1570
|
+
omitHeader: true,
|
|
1571
|
+
previewBottomPad: 1,
|
|
1572
|
+
compactGutter: true,
|
|
1573
|
+
});
|
|
1492
1574
|
return text;
|
|
1493
1575
|
}
|
|
1494
1576
|
if (d?._type === "noChange") {
|
|
1495
1577
|
text.__piDiffTask = undefined;
|
|
1578
|
+
clearToolHeaderBg(text);
|
|
1496
1579
|
text.setText(`${TOOL_RESULT_INDENT}${theme.fg("muted", "✓ no changes")}`);
|
|
1497
1580
|
return text;
|
|
1498
1581
|
}
|
|
1499
1582
|
if (d?._type === "new") {
|
|
1500
1583
|
const { lines: lineCount, content: rawContent, filePath: fp } = d;
|
|
1584
|
+
clearToolHeaderBg(text);
|
|
1501
1585
|
resolvePreviewDiffColors(theme);
|
|
1502
1586
|
const w = termW();
|
|
1503
1587
|
const newHdr = bgLine(`${theme.fg("success", `✓ new file (${lineCount} lines)`)}`, w);
|
|
@@ -1517,15 +1601,14 @@ export default async function diffRendererExtension(pi) {
|
|
|
1517
1601
|
const maxShow = hlLines.length;
|
|
1518
1602
|
const preview = hlLines.slice(0, maxShow).join("\n").replace(/\n+$/, "");
|
|
1519
1603
|
const rem = hlLines.length - maxShow;
|
|
1520
|
-
const moreLine = rem > 0
|
|
1521
|
-
? `\n${bgLine(`${TOOL_RESULT_INDENT}${theme.fg("muted", `… ${rem} more lines`)}`, width)}`
|
|
1522
|
-
: "";
|
|
1604
|
+
const moreLine = rem > 0 ? `\n${bgLine(`${TOOL_RESULT_INDENT}${theme.fg("muted", `… ${rem} more lines`)}`, width)}` : "";
|
|
1523
1605
|
return `${newHdr}\n${padDiffBody(preview)}${moreLine}`;
|
|
1524
1606
|
},
|
|
1525
1607
|
};
|
|
1526
1608
|
}
|
|
1527
1609
|
return text;
|
|
1528
1610
|
}
|
|
1611
|
+
clearToolHeaderBg(text);
|
|
1529
1612
|
text.setText(`${TOOL_RESULT_INDENT}${theme.fg("dim", String(result?.content?.[0]?.text ?? "written").slice(0, 120))}`);
|
|
1530
1613
|
return text;
|
|
1531
1614
|
},
|
|
@@ -1538,29 +1621,13 @@ export default async function diffRendererExtension(pi) {
|
|
|
1538
1621
|
if (Array.isArray(input?.edits)) {
|
|
1539
1622
|
return input.edits
|
|
1540
1623
|
.map((edit) => ({
|
|
1541
|
-
oldText: typeof edit?.oldText === "string"
|
|
1542
|
-
|
|
1543
|
-
: typeof edit?.old_text === "string"
|
|
1544
|
-
? edit.old_text
|
|
1545
|
-
: "",
|
|
1546
|
-
newText: typeof edit?.newText === "string"
|
|
1547
|
-
? edit.newText
|
|
1548
|
-
: typeof edit?.new_text === "string"
|
|
1549
|
-
? edit.new_text
|
|
1550
|
-
: "",
|
|
1624
|
+
oldText: typeof edit?.oldText === "string" ? edit.oldText : typeof edit?.old_text === "string" ? edit.old_text : "",
|
|
1625
|
+
newText: typeof edit?.newText === "string" ? edit.newText : typeof edit?.new_text === "string" ? edit.new_text : "",
|
|
1551
1626
|
}))
|
|
1552
1627
|
.filter((edit) => edit.oldText && edit.oldText !== edit.newText);
|
|
1553
1628
|
}
|
|
1554
|
-
const oldText = typeof input?.oldText === "string"
|
|
1555
|
-
|
|
1556
|
-
: typeof input?.old_text === "string"
|
|
1557
|
-
? input.old_text
|
|
1558
|
-
: "";
|
|
1559
|
-
const newText = typeof input?.newText === "string"
|
|
1560
|
-
? input.newText
|
|
1561
|
-
: typeof input?.new_text === "string"
|
|
1562
|
-
? input.new_text
|
|
1563
|
-
: "";
|
|
1629
|
+
const oldText = typeof input?.oldText === "string" ? input.oldText : typeof input?.old_text === "string" ? input.old_text : "";
|
|
1630
|
+
const newText = typeof input?.newText === "string" ? input.newText : typeof input?.new_text === "string" ? input.new_text : "";
|
|
1564
1631
|
return oldText && oldText !== newText ? [{ oldText, newText }] : [];
|
|
1565
1632
|
}
|
|
1566
1633
|
function summarizeEditOperations(operations) {
|
|
@@ -1580,19 +1647,7 @@ export default async function diffRendererExtension(pi) {
|
|
|
1580
1647
|
parameters: {
|
|
1581
1648
|
...(origEdit.parameters || {}),
|
|
1582
1649
|
properties: {
|
|
1583
|
-
...(
|
|
1584
|
-
start_hash: {
|
|
1585
|
-
type: "string",
|
|
1586
|
-
description: "Hashline anchor for the first line to replace (copied from hashline_read HASH column).",
|
|
1587
|
-
},
|
|
1588
|
-
end_hash: {
|
|
1589
|
-
type: "string",
|
|
1590
|
-
description: "Hashline anchor for the last line to replace. Use same hash as start_hash for single-line edit.",
|
|
1591
|
-
},
|
|
1592
|
-
replacement: {
|
|
1593
|
-
type: "string",
|
|
1594
|
-
description: "Replacement text with literal newlines replacing the anchor range. Empty string deletes the range. Only used with start_hash/end_hash.",
|
|
1595
|
-
},
|
|
1650
|
+
...(origEdit.parameters?.properties || {}),
|
|
1596
1651
|
oldText: {
|
|
1597
1652
|
type: "string",
|
|
1598
1653
|
description: "Exact existing text to replace.",
|
|
@@ -1601,35 +1656,12 @@ export default async function diffRendererExtension(pi) {
|
|
|
1601
1656
|
type: "string",
|
|
1602
1657
|
description: "Replacement text for oldText.",
|
|
1603
1658
|
},
|
|
1604
|
-
dryRun: {
|
|
1605
|
-
type: "boolean",
|
|
1606
|
-
description: "When true: validate anchors and return diff without writing (for hashline path).",
|
|
1607
|
-
},
|
|
1608
1659
|
},
|
|
1609
1660
|
required: ["path"],
|
|
1610
1661
|
additionalProperties: true,
|
|
1611
1662
|
},
|
|
1612
1663
|
async execute(tid, params, sig, upd, ctx) {
|
|
1613
1664
|
const fp = params.path ?? params.file_path ?? "";
|
|
1614
|
-
// Hashline path: strict, no fuzzy fallback, atomic write.
|
|
1615
|
-
if (params.start_hash && params.end_hash) {
|
|
1616
|
-
if (!fp) {
|
|
1617
|
-
return {
|
|
1618
|
-
content: [{ type: "text", text: "[E_BAD_INPUT] path required for hashline" }],
|
|
1619
|
-
isError: true,
|
|
1620
|
-
};
|
|
1621
|
-
}
|
|
1622
|
-
const resolvedFp = resolve(cwd, fp);
|
|
1623
|
-
const content_lines = params.replacement === "" ? [] : (params.replacement?.split("\n") ?? []);
|
|
1624
|
-
const changes = [{ hash_range_inclusive: [params.start_hash, params.end_hash], content_lines }];
|
|
1625
|
-
return runHashlineEdit({
|
|
1626
|
-
resolvedPath: resolvedFp,
|
|
1627
|
-
changes,
|
|
1628
|
-
dryRun: params.dryRun === true,
|
|
1629
|
-
toolCallId: tid,
|
|
1630
|
-
onDiffStats: (id, diff) => stashHashlineEditHeaderStats(id, diff.lines.length, diff.added, diff.removed),
|
|
1631
|
-
});
|
|
1632
|
-
}
|
|
1633
1665
|
const operations = getEditOperations(params);
|
|
1634
1666
|
// Try cascading replace() first — smarter matching than SDK's exact-only edit
|
|
1635
1667
|
if (fp && operations.length > 0 && existsSync(fp)) {
|
|
@@ -1664,9 +1696,7 @@ export default async function diffRendererExtension(pi) {
|
|
|
1664
1696
|
editLine = 0;
|
|
1665
1697
|
}
|
|
1666
1698
|
const useFull = !!params._expandGaps;
|
|
1667
|
-
const diffData = useFull
|
|
1668
|
-
? parseDiff(operations[0].oldText, operations[0].newText, undefined)
|
|
1669
|
-
: diffs[0];
|
|
1699
|
+
const diffData = useFull ? parseDiff(operations[0].oldText, operations[0].newText, undefined) : diffs[0];
|
|
1670
1700
|
return {
|
|
1671
1701
|
content: [{ type: "text", text: `Edited ${sp(fp)}` }],
|
|
1672
1702
|
details: {
|
|
@@ -1748,9 +1778,7 @@ export default async function diffRendererExtension(pi) {
|
|
|
1748
1778
|
editLine = 0;
|
|
1749
1779
|
}
|
|
1750
1780
|
const useFull = !!params._expandGaps;
|
|
1751
|
-
const diffData = useFull
|
|
1752
|
-
? parseDiff(operations[0].oldText, operations[0].newText, undefined)
|
|
1753
|
-
: diffs[0];
|
|
1781
|
+
const diffData = useFull ? parseDiff(operations[0].oldText, operations[0].newText, undefined) : diffs[0];
|
|
1754
1782
|
result.details = {
|
|
1755
1783
|
_type: "editInfo",
|
|
1756
1784
|
summary: editLine > 0 ? `${summary} at line ${editLine}` : summary,
|
|
@@ -1825,13 +1853,13 @@ export default async function diffRendererExtension(pi) {
|
|
|
1825
1853
|
catch {
|
|
1826
1854
|
previewLine = 0;
|
|
1827
1855
|
}
|
|
1828
|
-
const loc = previewLine > 0 ?
|
|
1829
|
-
text
|
|
1856
|
+
const loc = previewLine > 0 ? `${TOOL_RESULT_INDENT}${theme.fg("muted", `at line ${previewLine}`)}` : "";
|
|
1857
|
+
setToolHeaderBg(text);
|
|
1858
|
+
text.setText(formatToolFrameHeaderText({
|
|
1830
1859
|
label: "edit",
|
|
1831
1860
|
filePath: fp,
|
|
1832
1861
|
theme,
|
|
1833
|
-
|
|
1834
|
-
suffix: `${stats}${loc}`,
|
|
1862
|
+
suffix: loc,
|
|
1835
1863
|
topPad: EDIT_DIFF_RESULT_FRAME.topPad,
|
|
1836
1864
|
bottomPad: EDIT_DIFF_RESULT_FRAME.bottomPad,
|
|
1837
1865
|
headerLeftPad: EDIT_DIFF_RESULT_FRAME.headerLeftPad,
|
|
@@ -1864,17 +1892,23 @@ export default async function diffRendererExtension(pi) {
|
|
|
1864
1892
|
}
|
|
1865
1893
|
const d = result.details;
|
|
1866
1894
|
if (d?._type === "editInfo" && d.diff) {
|
|
1867
|
-
setDiffPreviewTask(text, "ed", "", d.diff, d.language, MAX_PREVIEW_LINES, theme, ctx, {
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1895
|
+
setDiffPreviewTask(text, "ed", "", d.diff, d.language, MAX_PREVIEW_LINES, theme, ctx, {
|
|
1896
|
+
omitHeader: true,
|
|
1897
|
+
previewBottomPad: EDIT_DIFF_RESULT_FRAME.previewBottomPad,
|
|
1898
|
+
compactGutter: true,
|
|
1899
|
+
bodyLeftPad: EDIT_DIFF_RESULT_FRAME.bodyLeftPad,
|
|
1900
|
+
});
|
|
1872
1901
|
return text;
|
|
1873
1902
|
}
|
|
1874
1903
|
if (d?._type === "multiEditInfo") {
|
|
1875
1904
|
const { editCount, diffLineCount, diff, language } = d;
|
|
1876
1905
|
if (diff) {
|
|
1877
|
-
setDiffPreviewTask(text, "me", "", diff, language, MAX_PREVIEW_LINES, theme, ctx, {
|
|
1906
|
+
setDiffPreviewTask(text, "me", "", diff, language, MAX_PREVIEW_LINES, theme, ctx, {
|
|
1907
|
+
omitHeader: true,
|
|
1908
|
+
previewBottomPad: EDIT_DIFF_RESULT_FRAME.previewBottomPad,
|
|
1909
|
+
compactGutter: true,
|
|
1910
|
+
bodyLeftPad: EDIT_DIFF_RESULT_FRAME.bodyLeftPad,
|
|
1911
|
+
});
|
|
1878
1912
|
return text;
|
|
1879
1913
|
}
|
|
1880
1914
|
const meta = `${editCount} edits${diffLineCountLabel(diffLineCount, theme)}`;
|
|
@@ -1882,163 +1916,94 @@ export default async function diffRendererExtension(pi) {
|
|
|
1882
1916
|
return text;
|
|
1883
1917
|
}
|
|
1884
1918
|
text.__piDiffTask = undefined;
|
|
1919
|
+
clearToolHeaderBg(text);
|
|
1885
1920
|
text.setText(`${TOOL_RESULT_INDENT}${theme.fg("dim", String(result?.content?.[0]?.text ?? "edited").slice(0, 120))}`);
|
|
1886
1921
|
return text;
|
|
1887
1922
|
},
|
|
1888
1923
|
});
|
|
1889
|
-
// Eager-init the hashline engine on session start so first edit has no WASM latency.
|
|
1890
|
-
pi.on("session_start", async () => {
|
|
1891
|
-
try {
|
|
1892
|
-
await initHashline();
|
|
1893
|
-
}
|
|
1894
|
-
catch {
|
|
1895
|
-
// best-effort; lazy init still works on first edit
|
|
1896
|
-
}
|
|
1897
|
-
});
|
|
1898
1924
|
pi.registerTool({
|
|
1899
|
-
name: "
|
|
1900
|
-
label: "
|
|
1901
|
-
description:
|
|
1925
|
+
name: "apply_patch",
|
|
1926
|
+
label: "apply_patch",
|
|
1927
|
+
description: "Multi-file patch engine. One call can add, update, delete, or move multiple files. Uses structured JSON changes array.",
|
|
1902
1928
|
parameters: {
|
|
1903
1929
|
type: "object",
|
|
1904
1930
|
properties: {
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1931
|
+
changes: {
|
|
1932
|
+
type: "array",
|
|
1933
|
+
description: "Array of file changes to apply atomically.",
|
|
1934
|
+
items: {
|
|
1935
|
+
type: "object",
|
|
1936
|
+
properties: {
|
|
1937
|
+
path: { type: "string", description: "Absolute path to the file." },
|
|
1938
|
+
action: {
|
|
1939
|
+
type: "string",
|
|
1940
|
+
enum: ["add", "update", "delete", "move"],
|
|
1941
|
+
description: "The operation to perform.",
|
|
1942
|
+
},
|
|
1943
|
+
content: { type: "string", description: "Content for new files (action=add)." },
|
|
1944
|
+
oldText: { type: "string", description: "Text to find for updates (action=update)." },
|
|
1945
|
+
newText: { type: "string", description: "Replacement text for updates (action=update)." },
|
|
1946
|
+
movePath: { type: "string", description: "Destination path for moves (action=move)." },
|
|
1947
|
+
},
|
|
1948
|
+
required: ["path", "action"],
|
|
1949
|
+
},
|
|
1950
|
+
},
|
|
1908
1951
|
},
|
|
1909
|
-
required: ["
|
|
1910
|
-
additionalProperties: false,
|
|
1911
|
-
},
|
|
1912
|
-
renderCall(args, theme, ctx) {
|
|
1913
|
-
const fp = args?.path ?? "";
|
|
1914
|
-
const text = ctx.lastComponent ?? new TextComponent("", 0, 0);
|
|
1915
|
-
resolvePreviewDiffColors(theme);
|
|
1916
|
-
const range = typeof args?.startLine === "number" || typeof args?.endLine === "number"
|
|
1917
|
-
? ` ${theme.fg("muted", `lines ${args.startLine ?? 1}-${args.endLine ?? "…"}`)}`
|
|
1918
|
-
: "";
|
|
1919
|
-
text.setText(formatToolFrameHeader({
|
|
1920
|
-
label: "hashline_read",
|
|
1921
|
-
filePath: fp,
|
|
1922
|
-
theme,
|
|
1923
|
-
width: termW(),
|
|
1924
|
-
suffix: range,
|
|
1925
|
-
topPad: 0,
|
|
1926
|
-
bottomPad: HASHLINE_TOOL_RESULT_FRAME.callTitleBottomPad,
|
|
1927
|
-
}));
|
|
1928
|
-
return text;
|
|
1952
|
+
required: ["changes"],
|
|
1929
1953
|
},
|
|
1930
1954
|
async execute(_tid, params) {
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
};
|
|
1947
|
-
}
|
|
1948
|
-
const startLine = typeof params.startLine === "number" ? Math.max(1, params.startLine) : 1;
|
|
1949
|
-
const endLine = typeof params.endLine === "number" ? Math.max(startLine, params.endLine) : Infinity;
|
|
1950
|
-
return executeHashlineRead(fp, content, startLine, endLine);
|
|
1955
|
+
const changes = (params.changes ?? []).map((c) => ({
|
|
1956
|
+
path: c.path,
|
|
1957
|
+
action: c.action,
|
|
1958
|
+
content: c.content,
|
|
1959
|
+
oldText: c.oldText,
|
|
1960
|
+
newText: c.newText,
|
|
1961
|
+
movePath: c.movePath,
|
|
1962
|
+
}));
|
|
1963
|
+
const result = await executeApplyPatch(changes);
|
|
1964
|
+
const output = formatApplyPatchResult(result);
|
|
1965
|
+
return {
|
|
1966
|
+
content: [{ type: "text", text: output }],
|
|
1967
|
+
isError: !result.ok,
|
|
1968
|
+
details: { _type: "applyPatchInfo", result },
|
|
1969
|
+
};
|
|
1951
1970
|
},
|
|
1952
|
-
|
|
1971
|
+
renderCall(args, theme, ctx) {
|
|
1953
1972
|
const text = getWidthAwareText(ctx.lastComponent);
|
|
1954
|
-
const
|
|
1955
|
-
const
|
|
1956
|
-
if (ctx.
|
|
1957
|
-
text
|
|
1958
|
-
|
|
1959
|
-
text.setText(hint);
|
|
1973
|
+
const changes = Array.isArray(args?.changes) ? args.changes : [];
|
|
1974
|
+
const count = changes.length;
|
|
1975
|
+
if (ctx.argsComplete && count > 0) {
|
|
1976
|
+
clearToolHeaderBg(text);
|
|
1977
|
+
text.setText("");
|
|
1960
1978
|
return text;
|
|
1961
1979
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
pi.registerTool({
|
|
1969
|
-
name: "hashline_edit",
|
|
1970
|
-
label: "hashline_edit",
|
|
1971
|
-
description: HASHLINE_EDIT_DESC,
|
|
1972
|
-
parameters: {
|
|
1973
|
-
type: "object",
|
|
1974
|
-
properties: {
|
|
1975
|
-
path: { type: "string", description: "Absolute or relative file path" },
|
|
1976
|
-
start_hash: {
|
|
1977
|
-
type: "string",
|
|
1978
|
-
description: "Hashline anchor for the first line to replace (copied from hashline_read HASH column).",
|
|
1979
|
-
},
|
|
1980
|
-
end_hash: {
|
|
1981
|
-
type: "string",
|
|
1982
|
-
description: "Hashline anchor for the last line to replace. Use same hash as start_hash for single-line edit (copied from hashline_read HASH column).",
|
|
1983
|
-
},
|
|
1984
|
-
replacement: {
|
|
1985
|
-
type: "string",
|
|
1986
|
-
description: "Replacement text with literal newlines replacing the anchor range. Empty string deletes the range.",
|
|
1987
|
-
},
|
|
1988
|
-
dryRun: {
|
|
1989
|
-
type: "boolean",
|
|
1990
|
-
description: "If true, validate anchors and return diff without writing the file.",
|
|
1991
|
-
},
|
|
1992
|
-
},
|
|
1993
|
-
required: ["path", "start_hash", "end_hash"],
|
|
1994
|
-
additionalProperties: false,
|
|
1995
|
-
},
|
|
1996
|
-
renderCall(args, theme, ctx) {
|
|
1997
|
-
const fp = args?.path ?? "";
|
|
1998
|
-
const text = ctx.lastComponent ?? new TextComponent("", 0, 0);
|
|
1999
|
-
resolvePreviewDiffColors(theme);
|
|
2000
|
-
const stats = hashlineEditCallStatsSuffix(ctx.toolCallId, theme);
|
|
2001
|
-
text.setText(formatToolFrameHeader({
|
|
2002
|
-
label: "hashline_edit",
|
|
2003
|
-
filePath: fp,
|
|
2004
|
-
theme,
|
|
2005
|
-
width: termW(),
|
|
2006
|
-
suffix: stats,
|
|
1980
|
+
const suffix = count
|
|
1981
|
+
? `${TOOL_RESULT_INDENT}${theme.fg("muted", `(${count} change${count === 1 ? "" : "s"})`)}${TOOL_RESULT_INDENT}${summarizeApplyPatchChanges(changes, theme)}`
|
|
1982
|
+
: `${TOOL_RESULT_INDENT}${theme.fg("muted", "(waiting for changes)")}`;
|
|
1983
|
+
setToolHeaderBg(text);
|
|
1984
|
+
text.setText(formatToolFrameHeaderText({
|
|
1985
|
+
meta: `${theme.fg("toolTitle", theme.bold("apply_patch"))}${suffix}`,
|
|
2007
1986
|
topPad: 0,
|
|
2008
|
-
bottomPad:
|
|
1987
|
+
bottomPad: 0,
|
|
2009
1988
|
}));
|
|
2010
1989
|
return text;
|
|
2011
1990
|
},
|
|
2012
|
-
async execute(tid, params) {
|
|
2013
|
-
await initHashline();
|
|
2014
|
-
const fp = resolve(cwd, params.path);
|
|
2015
|
-
if (!fp) {
|
|
2016
|
-
return { content: [{ type: "text", text: "[E_BAD_INPUT] path required" }], isError: true, details: {} };
|
|
2017
|
-
}
|
|
2018
|
-
if (!params.start_hash || !params.end_hash) {
|
|
2019
|
-
return { content: [{ type: "text", text: "[E_BAD_INPUT] start_hash and end_hash required" }], isError: true, details: {} };
|
|
2020
|
-
}
|
|
2021
|
-
const content_lines = params.replacement === "" ? [] : (params.replacement?.split("\n") ?? []);
|
|
2022
|
-
const changes = [{ hash_range_inclusive: [params.start_hash, params.end_hash], content_lines }];
|
|
2023
|
-
return runHashlineEdit({
|
|
2024
|
-
resolvedPath: fp,
|
|
2025
|
-
changes,
|
|
2026
|
-
dryRun: params.dryRun === true,
|
|
2027
|
-
toolCallId: tid,
|
|
2028
|
-
onDiffStats: (id, diff) => stashHashlineEditHeaderStats(id, diff.lines.length, diff.added, diff.removed),
|
|
2029
|
-
});
|
|
2030
|
-
},
|
|
2031
1991
|
renderResult(result, _opt, theme, ctx) {
|
|
2032
1992
|
const text = getWidthAwareText(ctx.lastComponent);
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
1993
|
+
if (ctx.isError) {
|
|
1994
|
+
const out = (result.content || []).map((c) => c.text).join("\n") || "Error";
|
|
1995
|
+
text.__piDiffTask = undefined;
|
|
1996
|
+
clearToolHeaderBg(text);
|
|
1997
|
+
text.setText(theme.fg("error", out));
|
|
2036
1998
|
return text;
|
|
2037
1999
|
}
|
|
2038
|
-
|
|
2039
|
-
text
|
|
2000
|
+
const patchResult = result.details?.result;
|
|
2001
|
+
if (renderApplyPatchPreview(text, patchResult, theme, ctx))
|
|
2002
|
+
return text;
|
|
2040
2003
|
const out = (result.content || []).map((c) => c.text).join("\n");
|
|
2041
|
-
text.
|
|
2004
|
+
text.__piDiffTask = undefined;
|
|
2005
|
+
clearToolHeaderBg(text);
|
|
2006
|
+
text.setText(theme.fg("muted", out));
|
|
2042
2007
|
return text;
|
|
2043
2008
|
},
|
|
2044
2009
|
});
|