@oh-my-pi/pi-coding-agent 14.5.9 → 14.5.11
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/CHANGELOG.md +50 -0
- package/package.json +7 -15
- package/scripts/build-binary.ts +1 -1
- package/src/cli/update-cli.ts +25 -1
- package/src/config/model-registry.ts +21 -19
- package/src/config/settings-schema.ts +11 -16
- package/src/discovery/claude-plugins.ts +28 -3
- package/src/edit/modes/atom.ts +50 -19
- package/src/edit/modes/hashline.ts +171 -110
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +14 -2
- package/src/extensibility/extensions/runner.ts +34 -1
- package/src/extensibility/extensions/types.ts +8 -0
- package/src/internal-urls/docs-index.generated.ts +54 -54
- package/src/lsp/client.ts +27 -35
- package/src/memories/index.ts +5 -0
- package/src/modes/components/settings-defs.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +2 -2
- package/src/modes/controllers/todo-command-controller.ts +22 -74
- package/src/modes/interactive-mode.ts +36 -9
- package/src/modes/theme/theme.ts +10 -1
- package/src/modes/types.ts +1 -3
- package/src/modes/utils/ui-helpers.ts +19 -6
- package/src/prompts/system/auto-continue.md +1 -0
- package/src/prompts/system/eager-todo.md +1 -1
- package/src/prompts/tools/github.md +3 -3
- package/src/prompts/tools/todo-write.md +19 -19
- package/src/sdk.ts +13 -2
- package/src/session/agent-session.ts +196 -96
- package/src/session/session-manager.ts +19 -2
- package/src/tools/bash.ts +9 -4
- package/src/tools/gh.ts +267 -119
- package/src/tools/todo-write.ts +157 -195
- package/src/utils/git.ts +61 -2
- package/src/web/search/providers/searxng.ts +71 -13
- package/examples/custom-tools/todo/index.ts +0 -211
- package/examples/extensions/todo.ts +0 -295
|
@@ -53,7 +53,7 @@ export type HashlineEdit =
|
|
|
53
53
|
// Accept both `|` (canonical) and `:` (legacy) so re-reads of older outputs still parse.
|
|
54
54
|
const HASHLINE_CONTENT_SEPARATOR_RE = "[:|]";
|
|
55
55
|
const HASHLINE_PREFIX_RE = new RegExp(
|
|
56
|
-
`^\\s*(?:>>>|>>)?\\s*(
|
|
56
|
+
`^\\s*(?:>>>|>>)?\\s*(?:[+*]\\s*)?\\d+${HASHLINE_BIGRAM_RE_SRC}${HASHLINE_CONTENT_SEPARATOR_RE}`,
|
|
57
57
|
);
|
|
58
58
|
const HASHLINE_PREFIX_PLUS_RE = new RegExp(
|
|
59
59
|
`^\\s*(?:>>>|>>)?\\s*\\+\\s*\\d+${HASHLINE_BIGRAM_RE_SRC}${HASHLINE_CONTENT_SEPARATOR_RE}`,
|
|
@@ -503,7 +503,7 @@ export function parseTag(ref: string): { line: number; hash: string } {
|
|
|
503
503
|
// 1. optional leading ">+-" markers and whitespace
|
|
504
504
|
// 2. line number (1+ digits)
|
|
505
505
|
// 3. hash (one BPE bigram from HASHLINE_BIGRAMS) directly adjacent (no separator)
|
|
506
|
-
const match = ref.match(new RegExp(`^\\s*[
|
|
506
|
+
const match = ref.match(new RegExp(`^\\s*[>+\\-*]*\\s*(\\d+)(${HASHLINE_BIGRAM_RE_SRC})`));
|
|
507
507
|
if (!match) {
|
|
508
508
|
throw new Error(`Invalid line reference. Expected ${formatFullAnchorRequirement(ref)}.`);
|
|
509
509
|
}
|
|
@@ -606,27 +606,6 @@ export class HashlineMismatchError extends Error {
|
|
|
606
606
|
"The edit was NOT applied, please use the updated file content shown below, and issue another edit tool-call.",
|
|
607
607
|
);
|
|
608
608
|
|
|
609
|
-
// Content-based recovery hint: the two-letter hash is weak, so a
|
|
610
|
-
// unique match elsewhere is only a candidate. Keep this advisory; never
|
|
611
|
-
// silently retarget stale edits based on a whole-file hash-only match.
|
|
612
|
-
const hints: string[] = [];
|
|
613
|
-
for (const m of mismatches) {
|
|
614
|
-
const matches: number[] = [];
|
|
615
|
-
for (let line = 1; line <= fileLines.length; line++) {
|
|
616
|
-
if (computeLineHash(line, fileLines[line - 1]) === m.expected) matches.push(line);
|
|
617
|
-
if (matches.length > 1) break;
|
|
618
|
-
}
|
|
619
|
-
if (matches.length === 1 && matches[0] !== m.line) {
|
|
620
|
-
hints.push(` ${m.line}${m.expected} → ${matches[0]}${m.expected}`);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
if (hints.length > 0) {
|
|
624
|
-
lines.push("Hash-only shifted candidate; verify content/context before using:");
|
|
625
|
-
lines.push(...hints);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
lines.push("");
|
|
629
|
-
|
|
630
609
|
let prevLine = -1;
|
|
631
610
|
for (const lineNum of sorted) {
|
|
632
611
|
// Gap separator between non-contiguous regions
|
|
@@ -1028,23 +1007,40 @@ export interface CompactHashlineDiffPreview {
|
|
|
1028
1007
|
}
|
|
1029
1008
|
|
|
1030
1009
|
export interface CompactHashlineDiffOptions {
|
|
1010
|
+
/** Maximum entries kept on each side of an unchanged-context truncation (default: 2). */
|
|
1031
1011
|
maxUnchangedRun?: number;
|
|
1032
|
-
maxDeletionRun?: number;
|
|
1033
|
-
maxOutputLines?: number;
|
|
1034
1012
|
}
|
|
1035
1013
|
|
|
1036
1014
|
const NUMBERED_DIFF_LINE_RE = /^([ +-])(\s*\d+)\|(.*)$/;
|
|
1037
1015
|
const HASHLINE_PREVIEW_PLACEHOLDER = " ";
|
|
1016
|
+
const ELLIPSIS = "...";
|
|
1038
1017
|
|
|
1039
|
-
type
|
|
1040
|
-
type
|
|
1018
|
+
type DiffEntryKind = " " | "+" | "-" | "*";
|
|
1019
|
+
type RunKind = DiffEntryKind | "meta";
|
|
1020
|
+
|
|
1021
|
+
interface DiffEntry {
|
|
1022
|
+
kind: DiffEntryKind;
|
|
1023
|
+
oldLine: number;
|
|
1024
|
+
newLine: number;
|
|
1025
|
+
content: string;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
interface MetaEntry {
|
|
1029
|
+
kind: "meta";
|
|
1030
|
+
raw: string;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
type Entry = DiffEntry | MetaEntry;
|
|
1034
|
+
|
|
1035
|
+
interface Run {
|
|
1036
|
+
kind: RunKind;
|
|
1037
|
+
entries: Entry[];
|
|
1038
|
+
}
|
|
1041
1039
|
|
|
1042
1040
|
interface ParsedNumberedDiffLine {
|
|
1043
1041
|
kind: " " | "+" | "-";
|
|
1044
1042
|
lineNumber: number;
|
|
1045
|
-
lineWidth: number;
|
|
1046
1043
|
content: string;
|
|
1047
|
-
raw: string;
|
|
1048
1044
|
}
|
|
1049
1045
|
|
|
1050
1046
|
interface CompactPreviewCounters {
|
|
@@ -1059,11 +1055,10 @@ function parseNumberedDiffLine(line: string): ParsedNumberedDiffLine | undefined
|
|
|
1059
1055
|
const kind = match[1];
|
|
1060
1056
|
if (kind !== " " && kind !== "+" && kind !== "-") return undefined;
|
|
1061
1057
|
|
|
1062
|
-
const
|
|
1063
|
-
const lineNumber = Number(lineField.trim());
|
|
1058
|
+
const lineNumber = Number(match[2].trim());
|
|
1064
1059
|
if (!Number.isInteger(lineNumber)) return undefined;
|
|
1065
1060
|
|
|
1066
|
-
return { kind, lineNumber,
|
|
1061
|
+
return { kind, lineNumber, content: match[3] };
|
|
1067
1062
|
}
|
|
1068
1063
|
|
|
1069
1064
|
function syncOldLineCounters(counters: CompactPreviewCounters, lineNumber: number): void {
|
|
@@ -1090,105 +1085,169 @@ function syncNewLineCounters(counters: CompactPreviewCounters, lineNumber: numbe
|
|
|
1090
1085
|
counters.newLine = lineNumber;
|
|
1091
1086
|
}
|
|
1092
1087
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1088
|
+
/**
|
|
1089
|
+
* Parse a unified-diff-with-line-numbers blob into structured entries while
|
|
1090
|
+
* tracking both old- and new-file line numbers. `...` markers (emitted by
|
|
1091
|
+
* {@link generateDiffString} for collapsed context) sync counters but are
|
|
1092
|
+
* preserved as passthrough entries so the original ellipsis remains visible.
|
|
1093
|
+
*/
|
|
1094
|
+
function parseDiffEntries(lines: string[]): Entry[] {
|
|
1095
|
+
const entries: Entry[] = [];
|
|
1096
|
+
const counters: CompactPreviewCounters = {};
|
|
1096
1097
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1098
|
+
for (const line of lines) {
|
|
1099
|
+
const parsed = parseNumberedDiffLine(line);
|
|
1100
|
+
if (!parsed) {
|
|
1101
|
+
entries.push({ kind: "meta", raw: line });
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1100
1104
|
|
|
1101
|
-
|
|
1102
|
-
const parsed = parseNumberedDiffLine(line);
|
|
1103
|
-
if (!parsed) return { kind: "meta", text: line };
|
|
1105
|
+
const isEllipsis = parsed.content === ELLIPSIS;
|
|
1104
1106
|
|
|
1105
|
-
if (parsed.content === "...") {
|
|
1106
1107
|
if (parsed.kind === "+") {
|
|
1107
1108
|
syncNewLineCounters(counters, parsed.lineNumber);
|
|
1108
|
-
|
|
1109
|
-
|
|
1109
|
+
const newLine = counters.newLine ?? parsed.lineNumber;
|
|
1110
|
+
const oldLine = counters.oldLine ?? parsed.lineNumber;
|
|
1111
|
+
entries.push({ kind: "+", oldLine, newLine, content: parsed.content });
|
|
1112
|
+
if (!isEllipsis) counters.newLine = newLine + 1;
|
|
1113
|
+
continue;
|
|
1110
1114
|
}
|
|
1111
|
-
return { kind: parsed.kind, text: parsed.raw };
|
|
1112
|
-
}
|
|
1113
1115
|
|
|
1114
|
-
|
|
1115
|
-
case "+": {
|
|
1116
|
-
syncNewLineCounters(counters, parsed.lineNumber);
|
|
1117
|
-
const newLine = counters.newLine;
|
|
1118
|
-
if (newLine === undefined) return { kind: "+", text: parsed.raw };
|
|
1119
|
-
const text = formatCompactHashlineLine("+", newLine, parsed.content);
|
|
1120
|
-
counters.newLine = newLine + 1;
|
|
1121
|
-
return { kind: "+", text };
|
|
1122
|
-
}
|
|
1123
|
-
case "-": {
|
|
1116
|
+
if (parsed.kind === "-") {
|
|
1124
1117
|
syncOldLineCounters(counters, parsed.lineNumber);
|
|
1125
|
-
const
|
|
1126
|
-
counters.
|
|
1127
|
-
|
|
1118
|
+
const oldLine = parsed.lineNumber;
|
|
1119
|
+
const newLine = counters.newLine ?? parsed.lineNumber;
|
|
1120
|
+
entries.push({ kind: "-", oldLine, newLine, content: parsed.content });
|
|
1121
|
+
if (!isEllipsis) counters.oldLine = oldLine + 1;
|
|
1122
|
+
continue;
|
|
1128
1123
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1124
|
+
|
|
1125
|
+
// Context line.
|
|
1126
|
+
syncOldLineCounters(counters, parsed.lineNumber);
|
|
1127
|
+
const oldLine = parsed.lineNumber;
|
|
1128
|
+
const newLine = counters.newLine ?? parsed.lineNumber;
|
|
1129
|
+
entries.push({ kind: " ", oldLine, newLine, content: parsed.content });
|
|
1130
|
+
if (!isEllipsis) {
|
|
1131
|
+
counters.oldLine = oldLine + 1;
|
|
1135
1132
|
counters.newLine = newLine + 1;
|
|
1136
|
-
return { kind: " ", text };
|
|
1137
1133
|
}
|
|
1138
1134
|
}
|
|
1139
|
-
}
|
|
1140
1135
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
const counters: CompactPreviewCounters = {};
|
|
1136
|
+
return entries;
|
|
1137
|
+
}
|
|
1144
1138
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1139
|
+
function groupRuns(entries: Entry[]): Run[] {
|
|
1140
|
+
const runs: Run[] = [];
|
|
1141
|
+
for (const entry of entries) {
|
|
1147
1142
|
const prev = runs[runs.length - 1];
|
|
1148
|
-
if (prev && prev.kind ===
|
|
1149
|
-
prev.
|
|
1143
|
+
if (prev && prev.kind === entry.kind) {
|
|
1144
|
+
prev.entries.push(entry);
|
|
1150
1145
|
continue;
|
|
1151
1146
|
}
|
|
1152
|
-
runs.push({ kind:
|
|
1147
|
+
runs.push({ kind: entry.kind, entries: [entry] });
|
|
1153
1148
|
}
|
|
1154
|
-
|
|
1155
1149
|
return runs;
|
|
1156
1150
|
}
|
|
1157
1151
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1152
|
+
/**
|
|
1153
|
+
* Collapse adjacent `(-, +)` runs into a single `*` run for paired
|
|
1154
|
+
* modifications. The i-th removed line pairs with the i-th added line — in
|
|
1155
|
+
* unified-diff convention they replaced each other in place — and is shown as
|
|
1156
|
+
* `*<newLine><hash>|<newContent>` instead of two lines `-<old>` + `+<new>`.
|
|
1157
|
+
* Surplus removals or additions remain as their own runs after the paired
|
|
1158
|
+
* block, preserving the unified-diff `del-then-add` ordering.
|
|
1159
|
+
*/
|
|
1160
|
+
function pairModifications(runs: Run[]): Run[] {
|
|
1161
|
+
const isPairable = (entry: Entry): entry is DiffEntry => entry.kind !== "meta" && entry.content !== ELLIPSIS;
|
|
1162
|
+
|
|
1163
|
+
const out: Run[] = [];
|
|
1164
|
+
for (let i = 0; i < runs.length; i++) {
|
|
1165
|
+
const run = runs[i];
|
|
1166
|
+
const next = runs[i + 1];
|
|
1167
|
+
if (run.kind !== "-" || !next || next.kind !== "+") {
|
|
1168
|
+
out.push(run);
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
const dels = run.entries.filter(isPairable);
|
|
1173
|
+
const adds = next.entries.filter(isPairable);
|
|
1174
|
+
const pairCount = Math.min(dels.length, adds.length);
|
|
1175
|
+
if (pairCount === 0) {
|
|
1176
|
+
out.push(run);
|
|
1177
|
+
continue;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
const mods: Entry[] = [];
|
|
1181
|
+
for (let p = 0; p < pairCount; p++) {
|
|
1182
|
+
mods.push({
|
|
1183
|
+
kind: "*",
|
|
1184
|
+
oldLine: dels[p].oldLine,
|
|
1185
|
+
newLine: adds[p].newLine,
|
|
1186
|
+
content: adds[p].content,
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
out.push({ kind: "*", entries: mods });
|
|
1190
|
+
|
|
1191
|
+
if (dels.length > pairCount) {
|
|
1192
|
+
out.push({ kind: "-", entries: dels.slice(pairCount) });
|
|
1193
|
+
}
|
|
1194
|
+
if (adds.length > pairCount) {
|
|
1195
|
+
out.push({ kind: "+", entries: adds.slice(pairCount) });
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
i++; // consume the `+` run
|
|
1199
|
+
}
|
|
1200
|
+
return out;
|
|
1162
1201
|
}
|
|
1163
1202
|
|
|
1164
|
-
function
|
|
1165
|
-
if (
|
|
1166
|
-
|
|
1167
|
-
|
|
1203
|
+
function formatEntry(entry: Entry): string {
|
|
1204
|
+
if (entry.kind === "meta") return entry.raw;
|
|
1205
|
+
|
|
1206
|
+
if (entry.content === ELLIPSIS) {
|
|
1207
|
+
// Preserve the `... <line>|...` ellipsis marker emitted by generateDiffString.
|
|
1208
|
+
const lineNum = entry.kind === "+" || entry.kind === "*" ? entry.newLine : entry.oldLine;
|
|
1209
|
+
const prefix = entry.kind === "*" ? "+" : entry.kind;
|
|
1210
|
+
return `${prefix}${lineNum}${HASHLINE_PREVIEW_PLACEHOLDER}${HASHLINE_CONTENT_SEPARATOR}${ELLIPSIS}`;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
switch (entry.kind) {
|
|
1214
|
+
case "+":
|
|
1215
|
+
return `+${entry.newLine}${computeLineHash(entry.newLine, entry.content)}${HASHLINE_CONTENT_SEPARATOR}${entry.content}`;
|
|
1216
|
+
case "-":
|
|
1217
|
+
return `-${entry.oldLine}${HASHLINE_PREVIEW_PLACEHOLDER}${HASHLINE_CONTENT_SEPARATOR}${entry.content}`;
|
|
1218
|
+
case " ":
|
|
1219
|
+
return ` ${entry.newLine}${computeLineHash(entry.newLine, entry.content)}${HASHLINE_CONTENT_SEPARATOR}${entry.content}`;
|
|
1220
|
+
case "*":
|
|
1221
|
+
return `*${entry.newLine}${computeLineHash(entry.newLine, entry.content)}${HASHLINE_CONTENT_SEPARATOR}${entry.content}`;
|
|
1222
|
+
}
|
|
1168
1223
|
}
|
|
1169
1224
|
|
|
1170
|
-
function
|
|
1171
|
-
if (
|
|
1172
|
-
const hidden =
|
|
1173
|
-
return [
|
|
1225
|
+
function collapseUnchangedMiddle(entries: Entry[], maxRun: number): string[] {
|
|
1226
|
+
if (entries.length <= maxRun * 2) return entries.map(formatEntry);
|
|
1227
|
+
const hidden = entries.length - maxRun * 2;
|
|
1228
|
+
return [
|
|
1229
|
+
...entries.slice(0, maxRun).map(formatEntry),
|
|
1230
|
+
` ... ${hidden} more unchanged lines`,
|
|
1231
|
+
...entries.slice(-maxRun).map(formatEntry),
|
|
1232
|
+
];
|
|
1174
1233
|
}
|
|
1175
1234
|
|
|
1176
1235
|
/**
|
|
1177
1236
|
* Build a compact diff preview suitable for model-visible tool responses.
|
|
1178
1237
|
*
|
|
1179
|
-
*
|
|
1180
|
-
*
|
|
1238
|
+
* Every changed line — added, removed, or modified — is shown in full. Only
|
|
1239
|
+
* unchanged context blocks between or around changes get truncated. Adjacent
|
|
1240
|
+
* `-`/`+` pairs are folded into single `*` modification lines so the common
|
|
1241
|
+
* 1:1 line-replacement case stays compact.
|
|
1181
1242
|
*/
|
|
1182
1243
|
export function buildCompactHashlineDiffPreview(
|
|
1183
1244
|
diff: string,
|
|
1184
1245
|
options: CompactHashlineDiffOptions = {},
|
|
1185
1246
|
): CompactHashlineDiffPreview {
|
|
1186
1247
|
const maxUnchangedRun = options.maxUnchangedRun ?? 2;
|
|
1187
|
-
const maxDeletionRun = options.maxDeletionRun ?? 2;
|
|
1188
|
-
const maxOutputLines = options.maxOutputLines ?? 16;
|
|
1189
1248
|
|
|
1190
1249
|
const inputLines = diff.length === 0 ? [] : diff.split("\n");
|
|
1191
|
-
const runs =
|
|
1250
|
+
const runs = pairModifications(groupRuns(parseDiffEntries(inputLines)));
|
|
1192
1251
|
|
|
1193
1252
|
const out: string[] = [];
|
|
1194
1253
|
let addedLines = 0;
|
|
@@ -1198,39 +1257,41 @@ export function buildCompactHashlineDiffPreview(
|
|
|
1198
1257
|
const run = runs[runIndex];
|
|
1199
1258
|
switch (run.kind) {
|
|
1200
1259
|
case "meta":
|
|
1201
|
-
out.push(
|
|
1260
|
+
for (const entry of run.entries) out.push(formatEntry(entry));
|
|
1202
1261
|
break;
|
|
1203
1262
|
case "+":
|
|
1204
|
-
|
|
1205
|
-
|
|
1263
|
+
for (const entry of run.entries) {
|
|
1264
|
+
if (entry.kind !== "meta" && entry.content !== ELLIPSIS) addedLines++;
|
|
1265
|
+
out.push(formatEntry(entry));
|
|
1266
|
+
}
|
|
1206
1267
|
break;
|
|
1207
1268
|
case "-":
|
|
1208
|
-
|
|
1209
|
-
|
|
1269
|
+
for (const entry of run.entries) {
|
|
1270
|
+
if (entry.kind !== "meta" && entry.content !== ELLIPSIS) removedLines++;
|
|
1271
|
+
out.push(formatEntry(entry));
|
|
1272
|
+
}
|
|
1273
|
+
break;
|
|
1274
|
+
case "*":
|
|
1275
|
+
for (const entry of run.entries) {
|
|
1276
|
+
addedLines++;
|
|
1277
|
+
removedLines++;
|
|
1278
|
+
out.push(formatEntry(entry));
|
|
1279
|
+
}
|
|
1210
1280
|
break;
|
|
1211
1281
|
case " ":
|
|
1212
1282
|
if (runIndex === 0) {
|
|
1213
|
-
out.push(...run.
|
|
1283
|
+
out.push(...run.entries.slice(-maxUnchangedRun).map(formatEntry));
|
|
1214
1284
|
break;
|
|
1215
1285
|
}
|
|
1216
1286
|
if (runIndex === runs.length - 1) {
|
|
1217
|
-
out.push(...run.
|
|
1287
|
+
out.push(...run.entries.slice(0, maxUnchangedRun).map(formatEntry));
|
|
1218
1288
|
break;
|
|
1219
1289
|
}
|
|
1220
|
-
out.push(...
|
|
1290
|
+
out.push(...collapseUnchangedMiddle(run.entries, maxUnchangedRun));
|
|
1221
1291
|
break;
|
|
1222
1292
|
}
|
|
1223
1293
|
}
|
|
1224
1294
|
|
|
1225
|
-
if (out.length > maxOutputLines) {
|
|
1226
|
-
const hidden = out.length - maxOutputLines;
|
|
1227
|
-
return {
|
|
1228
|
-
preview: [...out.slice(0, maxOutputLines), ` ... ${hidden} more preview lines`].join("\n"),
|
|
1229
|
-
addedLines,
|
|
1230
|
-
removedLines,
|
|
1231
|
-
};
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
1295
|
return { preview: out.join("\n"), addedLines, removedLines };
|
|
1235
1296
|
}
|
|
1236
1297
|
|