@oh-my-pi/pi-coding-agent 14.5.3 → 14.5.6
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 +49 -0
- package/examples/extensions/plan-mode.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/package.json +7 -7
- package/src/config/prompt-templates.ts +103 -8
- package/src/config/settings-schema.ts +14 -13
- package/src/config/settings.ts +1 -1
- package/src/cursor.ts +4 -4
- package/src/edit/index.ts +111 -109
- package/src/edit/line-hash.ts +33 -3
- package/src/edit/modes/apply-patch.ts +6 -4
- package/src/edit/modes/atom.lark +27 -0
- package/src/edit/modes/atom.ts +1039 -841
- package/src/edit/modes/hashline.ts +9 -10
- package/src/edit/modes/patch.ts +23 -19
- package/src/edit/modes/replace.ts +19 -15
- package/src/edit/renderer.ts +65 -8
- package/src/edit/streaming.ts +47 -77
- package/src/extensibility/extensions/types.ts +11 -11
- package/src/extensibility/hooks/types.ts +6 -6
- package/src/lsp/edits.ts +8 -5
- package/src/lsp/index.ts +4 -4
- package/src/lsp/utils.ts +7 -7
- package/src/mcp/discoverable-tool-metadata.ts +1 -1
- package/src/mcp/manager.ts +3 -3
- package/src/mcp/tool-bridge.ts +4 -4
- package/src/memories/index.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +1 -1
- package/src/modes/components/settings-defs.ts +3 -3
- package/src/modes/components/tree-selector.ts +2 -2
- package/src/modes/utils/ui-helpers.ts +31 -7
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/agents/librarian.md +2 -2
- package/src/prompts/agents/plan.md +2 -2
- package/src/prompts/agents/reviewer.md +1 -1
- package/src/prompts/agents/task.md +2 -2
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/system/system-prompt.md +116 -60
- package/src/prompts/tools/apply-patch.md +0 -2
- package/src/prompts/tools/atom.md +81 -63
- package/src/prompts/tools/bash.md +7 -4
- package/src/prompts/tools/checkpoint.md +1 -1
- package/src/prompts/tools/find.md +6 -1
- package/src/prompts/tools/hashline.md +10 -11
- package/src/prompts/tools/patch.md +13 -13
- package/src/prompts/tools/read.md +4 -4
- package/src/prompts/tools/replace.md +3 -3
- package/src/prompts/tools/{grep.md → search.md} +4 -4
- package/src/sdk.ts +19 -9
- package/src/session/agent-session.ts +65 -0
- package/src/system-prompt.ts +15 -5
- package/src/task/executor.ts +5 -0
- package/src/task/index.ts +10 -1
- package/src/tools/ast-edit.ts +4 -6
- package/src/tools/ast-grep.ts +4 -6
- package/src/tools/bash.ts +1 -1
- package/src/tools/file-recorder.ts +6 -6
- package/src/tools/find.ts +11 -13
- package/src/tools/index.ts +7 -7
- package/src/tools/path-utils.ts +31 -4
- package/src/tools/read.ts +12 -6
- package/src/tools/renderers.ts +2 -2
- package/src/tools/{grep.ts → search.ts} +32 -40
- package/src/tools/write.ts +8 -4
- package/src/web/search/index.ts +1 -1
- package/src/edit/block.ts +0 -308
- package/src/edit/indent.ts +0 -150
|
@@ -156,7 +156,6 @@ const locSchema = Type.Union(
|
|
|
156
156
|
|
|
157
157
|
export const hashlineEditSchema = Type.Object(
|
|
158
158
|
{
|
|
159
|
-
path: Type.Optional(Type.String({ description: "File path (omit to use top-level `path`)" })),
|
|
160
159
|
loc: Type.Optional(locSchema),
|
|
161
160
|
content: Type.Optional(linesSchema),
|
|
162
161
|
},
|
|
@@ -165,7 +164,7 @@ export const hashlineEditSchema = Type.Object(
|
|
|
165
164
|
|
|
166
165
|
export const hashlineEditParamsSchema = Type.Object(
|
|
167
166
|
{
|
|
168
|
-
path: Type.
|
|
167
|
+
path: Type.String({ description: "file path for edits" }),
|
|
169
168
|
edits: Type.Array(hashlineEditSchema, { description: "edits" }),
|
|
170
169
|
},
|
|
171
170
|
{ additionalProperties: false },
|
|
@@ -566,8 +565,8 @@ export class HashlineMismatchError extends Error {
|
|
|
566
565
|
|
|
567
566
|
const sorted = [...displayLines].sort((a, b) => a - b);
|
|
568
567
|
const out: string[] = [
|
|
569
|
-
`Edit rejected: ${mismatches.length} line${mismatches.length > 1 ? "s have" : " has"} changed since the last read
|
|
570
|
-
"
|
|
568
|
+
`Edit rejected: ${mismatches.length} line${mismatches.length > 1 ? "s have" : " has"} changed since the last read (marked *).`,
|
|
569
|
+
"The edit was NOT applied, please use the updated file content shown below, and issue another edit tool-call.",
|
|
571
570
|
"",
|
|
572
571
|
];
|
|
573
572
|
|
|
@@ -603,8 +602,8 @@ export class HashlineMismatchError extends Error {
|
|
|
603
602
|
const lines: string[] = [];
|
|
604
603
|
|
|
605
604
|
lines.push(
|
|
606
|
-
`Edit rejected: ${mismatches.length} line${mismatches.length > 1 ? "s have" : " has"} changed since the last read
|
|
607
|
-
"
|
|
605
|
+
`Edit rejected: ${mismatches.length} line${mismatches.length > 1 ? "s have" : " has"} changed since the last read (marked *).`,
|
|
606
|
+
"The edit was NOT applied, please use the updated file content shown below, and issue another edit tool-call.",
|
|
608
607
|
);
|
|
609
608
|
lines.push("");
|
|
610
609
|
|
|
@@ -1015,7 +1014,7 @@ export interface CompactHashlineDiffOptions {
|
|
|
1015
1014
|
}
|
|
1016
1015
|
|
|
1017
1016
|
const NUMBERED_DIFF_LINE_RE = /^([ +-])(\s*\d+)\|(.*)$/;
|
|
1018
|
-
const HASHLINE_PREVIEW_PLACEHOLDER = "
|
|
1017
|
+
const HASHLINE_PREVIEW_PLACEHOLDER = " ";
|
|
1019
1018
|
|
|
1020
1019
|
type DiffRunKind = " " | "+" | "-" | "meta";
|
|
1021
1020
|
type DiffRun = { kind: DiffRunKind; lines: string[] };
|
|
@@ -1142,7 +1141,7 @@ function collapseFromStart(lines: string[], maxLines: number, label: string): st
|
|
|
1142
1141
|
return [...lines.slice(0, maxLines), ` ... ${hidden} more ${label} lines`];
|
|
1143
1142
|
}
|
|
1144
1143
|
|
|
1145
|
-
function
|
|
1144
|
+
function _collapseFromEnd(lines: string[], maxLines: number, label: string): string[] {
|
|
1146
1145
|
if (lines.length <= maxLines) return lines;
|
|
1147
1146
|
const hidden = lines.length - maxLines;
|
|
1148
1147
|
return [` ... ${hidden} more ${label} lines`, ...lines.slice(-maxLines)];
|
|
@@ -1191,11 +1190,11 @@ export function buildCompactHashlineDiffPreview(
|
|
|
1191
1190
|
break;
|
|
1192
1191
|
case " ":
|
|
1193
1192
|
if (runIndex === 0) {
|
|
1194
|
-
out.push(...
|
|
1193
|
+
out.push(...run.lines.slice(-maxUnchangedRun));
|
|
1195
1194
|
break;
|
|
1196
1195
|
}
|
|
1197
1196
|
if (runIndex === runs.length - 1) {
|
|
1198
|
-
out.push(...
|
|
1197
|
+
out.push(...run.lines.slice(0, maxUnchangedRun));
|
|
1199
1198
|
break;
|
|
1200
1199
|
}
|
|
1201
1200
|
out.push(...collapseFromMiddle(run.lines, maxUnchangedRun, "unchanged"));
|
package/src/edit/modes/patch.ts
CHANGED
|
@@ -1576,27 +1576,33 @@ export async function computePatchDiff(
|
|
|
1576
1576
|
}
|
|
1577
1577
|
}
|
|
1578
1578
|
|
|
1579
|
-
export const patchEditEntrySchema = Type.Object(
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1579
|
+
export const patchEditEntrySchema = Type.Object(
|
|
1580
|
+
{
|
|
1581
|
+
op: Type.Optional(
|
|
1582
|
+
StringEnum(["create", "delete", "update"], {
|
|
1583
|
+
description: "Operation (default: update)",
|
|
1584
|
+
}),
|
|
1585
|
+
),
|
|
1586
|
+
rename: Type.Optional(Type.String({ description: "New path for move" })),
|
|
1587
|
+
diff: Type.Optional(Type.String({ description: "Diff hunks (update) or full content (create)" })),
|
|
1588
|
+
},
|
|
1589
|
+
{ additionalProperties: false },
|
|
1590
|
+
);
|
|
1591
|
+
|
|
1592
|
+
export const patchEditSchema = Type.Object(
|
|
1593
|
+
{
|
|
1594
|
+
path: Type.String({ description: "file path for edits" }),
|
|
1595
|
+
edits: Type.Array(patchEditEntrySchema, { description: "Patch operations", minItems: 1 }),
|
|
1596
|
+
},
|
|
1597
|
+
{ additionalProperties: false },
|
|
1598
|
+
);
|
|
1594
1599
|
|
|
1595
1600
|
export type PatchEditEntry = Static<typeof patchEditEntrySchema>;
|
|
1596
1601
|
export type PatchParams = Static<typeof patchEditSchema>;
|
|
1597
1602
|
|
|
1598
1603
|
export interface ExecutePatchSingleOptions {
|
|
1599
1604
|
session: ToolSession;
|
|
1605
|
+
path: string;
|
|
1600
1606
|
params: PatchEditEntry;
|
|
1601
1607
|
signal?: AbortSignal;
|
|
1602
1608
|
batchRequest?: LspBatchRequest;
|
|
@@ -1694,6 +1700,7 @@ export async function executePatchSingle(
|
|
|
1694
1700
|
): Promise<AgentToolResult<EditToolDetails, typeof patchEditEntrySchema>> {
|
|
1695
1701
|
const {
|
|
1696
1702
|
session,
|
|
1703
|
+
path,
|
|
1697
1704
|
params,
|
|
1698
1705
|
signal,
|
|
1699
1706
|
batchRequest,
|
|
@@ -1702,10 +1709,7 @@ export async function executePatchSingle(
|
|
|
1702
1709
|
writethrough,
|
|
1703
1710
|
beginDeferredDiagnosticsForPath,
|
|
1704
1711
|
} = options;
|
|
1705
|
-
const {
|
|
1706
|
-
if (typeof path !== "string" || path.length === 0) {
|
|
1707
|
-
throw new Error("patch edit: missing `path`. Provide `path` on the edit or supply a top-level `path`.");
|
|
1708
|
-
}
|
|
1712
|
+
const { op: rawOp, rename, diff } = params;
|
|
1709
1713
|
|
|
1710
1714
|
const op: Operation = rawOp === "create" || rawOp === "delete" ? rawOp : "update";
|
|
1711
1715
|
|
|
@@ -976,23 +976,29 @@ export function findContextLine(
|
|
|
976
976
|
return { index: undefined, confidence: bestScore };
|
|
977
977
|
}
|
|
978
978
|
|
|
979
|
-
export const replaceEditEntrySchema = Type.Object(
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
979
|
+
export const replaceEditEntrySchema = Type.Object(
|
|
980
|
+
{
|
|
981
|
+
old_text: Type.String({ description: "Text to find (fuzzy whitespace matching enabled)" }),
|
|
982
|
+
new_text: Type.String({ description: "Replacement text" }),
|
|
983
|
+
all: Type.Optional(Type.Boolean({ description: "Replace all occurrences (default: unique match required)" })),
|
|
984
|
+
},
|
|
985
|
+
{ additionalProperties: false },
|
|
986
|
+
);
|
|
987
|
+
|
|
988
|
+
export const replaceEditSchema = Type.Object(
|
|
989
|
+
{
|
|
990
|
+
path: Type.String({ description: "file path for edits" }),
|
|
991
|
+
edits: Type.Array(replaceEditEntrySchema, { description: "Replacements", minItems: 1 }),
|
|
992
|
+
},
|
|
993
|
+
{ additionalProperties: false },
|
|
994
|
+
);
|
|
990
995
|
|
|
991
996
|
export type ReplaceEditEntry = Static<typeof replaceEditEntrySchema>;
|
|
992
997
|
export type ReplaceParams = Static<typeof replaceEditSchema>;
|
|
993
998
|
|
|
994
999
|
export interface ExecuteReplaceSingleOptions {
|
|
995
1000
|
session: ToolSession;
|
|
1001
|
+
path: string;
|
|
996
1002
|
params: ReplaceEditEntry;
|
|
997
1003
|
signal?: AbortSignal;
|
|
998
1004
|
batchRequest?: LspBatchRequest;
|
|
@@ -1007,6 +1013,7 @@ export async function executeReplaceSingle(
|
|
|
1007
1013
|
): Promise<AgentToolResult<EditToolDetails, typeof replaceEditEntrySchema>> {
|
|
1008
1014
|
const {
|
|
1009
1015
|
session,
|
|
1016
|
+
path,
|
|
1010
1017
|
params,
|
|
1011
1018
|
signal,
|
|
1012
1019
|
batchRequest,
|
|
@@ -1015,10 +1022,7 @@ export async function executeReplaceSingle(
|
|
|
1015
1022
|
writethrough,
|
|
1016
1023
|
beginDeferredDiagnosticsForPath,
|
|
1017
1024
|
} = options;
|
|
1018
|
-
const {
|
|
1019
|
-
if (typeof path !== "string" || path.length === 0) {
|
|
1020
|
-
throw new Error("replace edit: missing `path`. Provide `path` on the edit or supply a top-level `path`.");
|
|
1021
|
-
}
|
|
1025
|
+
const { old_text, new_text, all } = params;
|
|
1022
1026
|
|
|
1023
1027
|
enforcePlanModeWrite(session, path);
|
|
1024
1028
|
|
package/src/edit/renderer.ts
CHANGED
|
@@ -28,8 +28,8 @@ import { Hasher, type RenderCache, renderStatusLine, truncateToWidth } from "../
|
|
|
28
28
|
import type { EditMode } from "../utils/edit-mode";
|
|
29
29
|
import type { VimToolDetails } from "../vim/types";
|
|
30
30
|
import type { DiffError, DiffResult } from "./diff";
|
|
31
|
-
import { expandApplyPatchToEntries, expandApplyPatchToPreviewEntries } from "./modes/apply-patch";
|
|
32
|
-
import type { Operation
|
|
31
|
+
import { type ApplyPatchEntry, expandApplyPatchToEntries, expandApplyPatchToPreviewEntries } from "./modes/apply-patch";
|
|
32
|
+
import type { Operation } from "./modes/patch";
|
|
33
33
|
import type { PerFileDiffPreview } from "./streaming";
|
|
34
34
|
|
|
35
35
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -106,8 +106,12 @@ type EditRenderEntry = {
|
|
|
106
106
|
op?: Operation;
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
+
interface AtomRenderSummary {
|
|
110
|
+
entries: Array<{ path: string }>;
|
|
111
|
+
}
|
|
112
|
+
|
|
109
113
|
interface ApplyPatchRenderSummary {
|
|
110
|
-
entries:
|
|
114
|
+
entries: ApplyPatchEntry[];
|
|
111
115
|
error?: string;
|
|
112
116
|
}
|
|
113
117
|
|
|
@@ -305,8 +309,54 @@ function getCallPreview(
|
|
|
305
309
|
}
|
|
306
310
|
|
|
307
311
|
const MISSING_APPLY_PATCH_END_ERROR = "The last line of the patch must be '*** End Patch'";
|
|
312
|
+
const ATOM_HEADER_PREFIX = "---";
|
|
313
|
+
|
|
314
|
+
function normalizeAtomPreviewPath(rawPath: string): string {
|
|
315
|
+
const trimmed = rawPath.trim();
|
|
316
|
+
if (trimmed.length < 2) return trimmed;
|
|
317
|
+
const first = trimmed[0];
|
|
318
|
+
const last = trimmed[trimmed.length - 1];
|
|
319
|
+
if ((first === '"' || first === "'") && first === last) {
|
|
320
|
+
return trimmed.slice(1, -1);
|
|
321
|
+
}
|
|
322
|
+
return trimmed;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function parseAtomPreviewHeader(line: string): string | null {
|
|
326
|
+
if (!line.startsWith(ATOM_HEADER_PREFIX)) return null;
|
|
327
|
+
let body = line.slice(ATOM_HEADER_PREFIX.length);
|
|
328
|
+
if (body.startsWith(" ")) body = body.slice(1);
|
|
329
|
+
const previewPath = normalizeAtomPreviewPath(body);
|
|
330
|
+
return previewPath.length > 0 ? previewPath : null;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function getAtomInputPaths(input: string): string[] {
|
|
334
|
+
const stripped = input.startsWith("\uFEFF") ? input.slice(1) : input;
|
|
335
|
+
const paths: string[] = [];
|
|
336
|
+
for (const rawLine of stripped.split("\n")) {
|
|
337
|
+
const line = rawLine.replace(/\r$/, "");
|
|
338
|
+
const path = parseAtomPreviewHeader(line);
|
|
339
|
+
if (path) paths.push(path);
|
|
340
|
+
}
|
|
341
|
+
return paths;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function getAtomRenderSummary(args: EditRenderArgs, editMode: EditMode | undefined): AtomRenderSummary | undefined {
|
|
345
|
+
if (editMode !== "atom" || typeof args.input !== "string") {
|
|
346
|
+
return undefined;
|
|
347
|
+
}
|
|
348
|
+
return { entries: getAtomInputPaths(args.input).map(path => ({ path })) };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function getApplyPatchRenderSummary(
|
|
352
|
+
args: EditRenderArgs,
|
|
353
|
+
isPartial: boolean,
|
|
354
|
+
editMode: EditMode | undefined,
|
|
355
|
+
): ApplyPatchRenderSummary | undefined {
|
|
356
|
+
if (editMode !== undefined && editMode !== "apply_patch") {
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
308
359
|
|
|
309
|
-
function getApplyPatchRenderSummary(args: EditRenderArgs, isPartial: boolean): ApplyPatchRenderSummary | undefined {
|
|
310
360
|
if (typeof args.input !== "string") {
|
|
311
361
|
return undefined;
|
|
312
362
|
}
|
|
@@ -397,8 +447,10 @@ export const editToolRenderer = {
|
|
|
397
447
|
}
|
|
398
448
|
|
|
399
449
|
const editArgs = args as EditRenderArgs;
|
|
400
|
-
const
|
|
450
|
+
const atomSummary = getAtomRenderSummary(editArgs, renderContext?.editMode);
|
|
451
|
+
const applyPatchSummary = getApplyPatchRenderSummary(editArgs, options.isPartial, renderContext?.editMode);
|
|
401
452
|
const firstApplyPatchEntry = applyPatchSummary?.entries[0];
|
|
453
|
+
const firstAtomEntry = atomSummary?.entries[0];
|
|
402
454
|
// Extract path from first edit entry when top-level path is absent (new schema)
|
|
403
455
|
const firstEdit = Array.isArray(editArgs.edits) && editArgs.edits.length > 0 ? editArgs.edits[0] : undefined;
|
|
404
456
|
const rawPath =
|
|
@@ -406,6 +458,7 @@ export const editToolRenderer = {
|
|
|
406
458
|
editArgs.path ||
|
|
407
459
|
filePathFromEditEntry(firstEdit?.path) ||
|
|
408
460
|
getPartialJsonEditPath(editArgs) ||
|
|
461
|
+
firstAtomEntry?.path ||
|
|
409
462
|
firstApplyPatchEntry?.path ||
|
|
410
463
|
"";
|
|
411
464
|
const rename = editArgs.rename || firstEdit?.rename || firstEdit?.move || firstApplyPatchEntry?.rename;
|
|
@@ -415,9 +468,10 @@ export const editToolRenderer = {
|
|
|
415
468
|
options?.spinnerFrame !== undefined ? formatStatusIcon("running", uiTheme, options.spinnerFrame) : "";
|
|
416
469
|
let text = `${formatTitle(getOperationTitle(op), uiTheme)} ${spinner ? `${spinner} ` : ""}${description}`;
|
|
417
470
|
// Show file count hint for multi-file edits
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
471
|
+
let fileCount = atomSummary?.entries.length ?? applyPatchSummary?.entries.length ?? 0;
|
|
472
|
+
if (Array.isArray(editArgs.edits)) {
|
|
473
|
+
fileCount = countEditFiles(editArgs.edits);
|
|
474
|
+
}
|
|
421
475
|
if (fileCount > 1) {
|
|
422
476
|
text += uiTheme.fg("dim", ` (+${fileCount - 1} more)`);
|
|
423
477
|
}
|
|
@@ -465,11 +519,14 @@ function renderSingleFileResult(
|
|
|
465
519
|
const details = result.details;
|
|
466
520
|
const isError = result.isError ?? (details && "isError" in details ? details.isError : false);
|
|
467
521
|
const firstEdit = args?.edits?.[0];
|
|
522
|
+
const atomSummary = getAtomRenderSummary(args ?? {}, options.renderContext?.editMode);
|
|
523
|
+
const firstAtomEntry = atomSummary?.entries[0];
|
|
468
524
|
const rawPath =
|
|
469
525
|
args?.file_path ||
|
|
470
526
|
args?.path ||
|
|
471
527
|
filePathFromEditEntry(firstEdit?.path) ||
|
|
472
528
|
(details && "path" in details ? details.path : "") ||
|
|
529
|
+
firstAtomEntry?.path ||
|
|
473
530
|
"";
|
|
474
531
|
const op = args?.op || firstEdit?.op || details?.op;
|
|
475
532
|
const rename = args?.rename || firstEdit?.rename || firstEdit?.move || details?.move;
|
package/src/edit/streaming.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import type { Theme } from "../modes/theme/theme";
|
|
16
16
|
import { type EditMode, resolveEditMode } from "../utils/edit-mode";
|
|
17
17
|
import { computeEditDiff, type DiffError, type DiffResult } from "./diff";
|
|
18
|
-
import { expandApplyPatchToEntries, expandApplyPatchToPreviewEntries } from "./modes/apply-patch";
|
|
18
|
+
import { type ApplyPatchEntry, expandApplyPatchToEntries, expandApplyPatchToPreviewEntries } from "./modes/apply-patch";
|
|
19
19
|
import { computeHashlineDiff, type HashlineToolEdit } from "./modes/hashline";
|
|
20
20
|
import { computePatchDiff, type PatchEditEntry } from "./modes/patch";
|
|
21
21
|
import type { ReplaceEditEntry } from "./modes/replace";
|
|
@@ -126,33 +126,19 @@ export function dropIncompleteLastEdit<T>(edits: readonly T[], partialJson: stri
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// -----------------------------------------------------------------------------
|
|
129
|
-
//
|
|
129
|
+
// Apply_patch remains multi-file because the Codex envelope carries paths per hunk.
|
|
130
130
|
// -----------------------------------------------------------------------------
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
const
|
|
132
|
+
function groupApplyPatchEntriesByPath(entries: readonly ApplyPatchEntry[]): Map<string, ApplyPatchEntry[]> {
|
|
133
|
+
const groups = new Map<string, ApplyPatchEntry[]>();
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
* to the top-level `args.path`). Insertion order is preserved and the number of
|
|
138
|
-
* distinct buckets is capped at {@link MAX_PREVIEW_FILES}.
|
|
139
|
-
*/
|
|
140
|
-
function groupEditsByPath<T extends { path?: string }>(
|
|
141
|
-
edits: readonly T[],
|
|
142
|
-
fallbackPath: string | undefined,
|
|
143
|
-
): Map<string, Array<T & { path: string }>> {
|
|
144
|
-
const groups = new Map<string, Array<T & { path: string }>>();
|
|
145
|
-
for (const edit of edits) {
|
|
146
|
-
if (!edit) continue;
|
|
147
|
-
const editPath = edit.path ?? fallbackPath;
|
|
148
|
-
if (!editPath) continue;
|
|
149
|
-
let bucket = groups.get(editPath);
|
|
135
|
+
for (const entry of entries) {
|
|
136
|
+
let bucket = groups.get(entry.path);
|
|
150
137
|
if (!bucket) {
|
|
151
|
-
if (groups.size >= MAX_PREVIEW_FILES) continue;
|
|
152
138
|
bucket = [];
|
|
153
|
-
groups.set(
|
|
139
|
+
groups.set(entry.path, bucket);
|
|
154
140
|
}
|
|
155
|
-
bucket.push(
|
|
141
|
+
bucket.push(entry);
|
|
156
142
|
}
|
|
157
143
|
return groups;
|
|
158
144
|
}
|
|
@@ -173,26 +159,21 @@ const replaceStrategy: EditStreamingStrategy<ReplaceArgs> = {
|
|
|
173
159
|
return { ...args, edits: dropIncompleteLastEdit(args.edits, partialJson, "edits") };
|
|
174
160
|
},
|
|
175
161
|
async computeDiffPreview(args, ctx) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
);
|
|
192
|
-
ctx.signal.throwIfAborted();
|
|
193
|
-
previews.push(toPerFilePreview(path, result));
|
|
194
|
-
}
|
|
195
|
-
return previews.length > 0 ? previews : null;
|
|
162
|
+
if (!args.path) return null;
|
|
163
|
+
const first = args.edits?.[0];
|
|
164
|
+
if (!first || first.old_text === undefined || first.new_text === undefined) return null;
|
|
165
|
+
ctx.signal.throwIfAborted();
|
|
166
|
+
const result = await computeEditDiff(
|
|
167
|
+
args.path,
|
|
168
|
+
first.old_text,
|
|
169
|
+
first.new_text,
|
|
170
|
+
ctx.cwd,
|
|
171
|
+
ctx.allowFuzzy ?? true,
|
|
172
|
+
first.all,
|
|
173
|
+
ctx.fuzzyThreshold,
|
|
174
|
+
);
|
|
175
|
+
ctx.signal.throwIfAborted();
|
|
176
|
+
return [toPerFilePreview(args.path, result)];
|
|
196
177
|
},
|
|
197
178
|
renderStreamingFallback() {
|
|
198
179
|
return "";
|
|
@@ -211,22 +192,17 @@ const patchStrategy: EditStreamingStrategy<PatchArgs> = {
|
|
|
211
192
|
return { ...args, edits: dropIncompleteLastEdit(args.edits, partialJson, "edits") };
|
|
212
193
|
},
|
|
213
194
|
async computeDiffPreview(args, ctx) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
ctx.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
);
|
|
226
|
-
ctx.signal.throwIfAborted();
|
|
227
|
-
previews.push(toPerFilePreview(path, result));
|
|
228
|
-
}
|
|
229
|
-
return previews.length > 0 ? previews : null;
|
|
195
|
+
if (!args.path) return null;
|
|
196
|
+
const first = args.edits?.[0];
|
|
197
|
+
if (!first) return null;
|
|
198
|
+
ctx.signal.throwIfAborted();
|
|
199
|
+
const result = await computePatchDiff(
|
|
200
|
+
{ path: args.path, op: first.op ?? "update", rename: first.rename, diff: first.diff },
|
|
201
|
+
ctx.cwd,
|
|
202
|
+
{ fuzzyThreshold: ctx.fuzzyThreshold, allowFuzzy: ctx.allowFuzzy },
|
|
203
|
+
);
|
|
204
|
+
ctx.signal.throwIfAborted();
|
|
205
|
+
return [toPerFilePreview(args.path, result)];
|
|
230
206
|
},
|
|
231
207
|
renderStreamingFallback() {
|
|
232
208
|
return "";
|
|
@@ -245,16 +221,11 @@ const hashlineStrategy: EditStreamingStrategy<HashlineArgs> = {
|
|
|
245
221
|
return { ...args, edits: dropIncompleteLastEdit(args.edits, partialJson, "edits") };
|
|
246
222
|
},
|
|
247
223
|
async computeDiffPreview(args, ctx) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const result = await computeHashlineDiff({ path, edits: fileEdits }, ctx.cwd);
|
|
254
|
-
ctx.signal.throwIfAborted();
|
|
255
|
-
previews.push(toPerFilePreview(path, result));
|
|
256
|
-
}
|
|
257
|
-
return previews;
|
|
224
|
+
if (!args.path || !args.edits?.length) return null;
|
|
225
|
+
ctx.signal.throwIfAborted();
|
|
226
|
+
const result = await computeHashlineDiff({ path: args.path, edits: args.edits }, ctx.cwd);
|
|
227
|
+
ctx.signal.throwIfAborted();
|
|
228
|
+
return [toPerFilePreview(args.path, result)];
|
|
258
229
|
},
|
|
259
230
|
renderStreamingFallback() {
|
|
260
231
|
return "";
|
|
@@ -272,7 +243,7 @@ const applyPatchStrategy: EditStreamingStrategy<ApplyPatchArgs> = {
|
|
|
272
243
|
},
|
|
273
244
|
async computeDiffPreview(args, ctx) {
|
|
274
245
|
if (typeof args.input !== "string" || args.input.length === 0) return null;
|
|
275
|
-
let entries:
|
|
246
|
+
let entries: ApplyPatchEntry[];
|
|
276
247
|
try {
|
|
277
248
|
entries = expandApplyPatchToEntries({ input: args.input });
|
|
278
249
|
} catch {
|
|
@@ -282,7 +253,7 @@ const applyPatchStrategy: EditStreamingStrategy<ApplyPatchArgs> = {
|
|
|
282
253
|
return [{ path: "", error: err instanceof Error ? err.message : String(err) }];
|
|
283
254
|
}
|
|
284
255
|
}
|
|
285
|
-
const groups =
|
|
256
|
+
const groups = groupApplyPatchEntriesByPath(entries);
|
|
286
257
|
if (groups.size === 0) return null;
|
|
287
258
|
const previews: PerFileDiffPreview[] = [];
|
|
288
259
|
for (const [path, fileEntries] of groups) {
|
|
@@ -319,18 +290,17 @@ const vimStrategy: EditStreamingStrategy<unknown> = {
|
|
|
319
290
|
};
|
|
320
291
|
|
|
321
292
|
interface AtomArgs {
|
|
322
|
-
|
|
323
|
-
|
|
293
|
+
input?: string;
|
|
294
|
+
__partialJson?: string;
|
|
324
295
|
}
|
|
325
296
|
|
|
326
297
|
const atomStrategy: EditStreamingStrategy<AtomArgs> = {
|
|
327
|
-
extractCompleteEdits(args
|
|
328
|
-
|
|
329
|
-
return { ...args, edits: dropIncompleteLastEdit(args.edits, partialJson, "edits") };
|
|
298
|
+
extractCompleteEdits(args) {
|
|
299
|
+
return args;
|
|
330
300
|
},
|
|
331
301
|
async computeDiffPreview() {
|
|
332
|
-
// Atom edits
|
|
333
|
-
//
|
|
302
|
+
// Atom edits can target file headers plus compact diff statements.
|
|
303
|
+
// We intentionally avoid speculative parsing while args are partial.
|
|
334
304
|
return null;
|
|
335
305
|
},
|
|
336
306
|
renderStreamingFallback() {
|
|
@@ -46,10 +46,10 @@ import type {
|
|
|
46
46
|
BashToolInput,
|
|
47
47
|
FindToolDetails,
|
|
48
48
|
FindToolInput,
|
|
49
|
-
GrepToolDetails,
|
|
50
|
-
GrepToolInput,
|
|
51
49
|
ReadToolDetails,
|
|
52
50
|
ReadToolInput,
|
|
51
|
+
SearchToolDetails,
|
|
52
|
+
SearchToolInput,
|
|
53
53
|
WriteToolInput,
|
|
54
54
|
} from "../../tools";
|
|
55
55
|
import type { TodoItem } from "../../tools/todo-write";
|
|
@@ -683,9 +683,9 @@ export interface WriteToolCallEvent extends ToolCallEventBase {
|
|
|
683
683
|
input: WriteToolInput;
|
|
684
684
|
}
|
|
685
685
|
|
|
686
|
-
export interface
|
|
687
|
-
toolName: "
|
|
688
|
-
input:
|
|
686
|
+
export interface SearchToolCallEvent extends ToolCallEventBase {
|
|
687
|
+
toolName: "search";
|
|
688
|
+
input: SearchToolInput;
|
|
689
689
|
}
|
|
690
690
|
|
|
691
691
|
export interface FindToolCallEvent extends ToolCallEventBase {
|
|
@@ -704,7 +704,7 @@ export type ToolCallEvent =
|
|
|
704
704
|
| ReadToolCallEvent
|
|
705
705
|
| EditToolCallEvent
|
|
706
706
|
| WriteToolCallEvent
|
|
707
|
-
|
|
|
707
|
+
| SearchToolCallEvent
|
|
708
708
|
| FindToolCallEvent
|
|
709
709
|
| CustomToolCallEvent;
|
|
710
710
|
|
|
@@ -736,9 +736,9 @@ export interface WriteToolResultEvent extends ToolResultEventBase {
|
|
|
736
736
|
details: undefined;
|
|
737
737
|
}
|
|
738
738
|
|
|
739
|
-
export interface
|
|
740
|
-
toolName: "
|
|
741
|
-
details:
|
|
739
|
+
export interface SearchToolResultEvent extends ToolResultEventBase {
|
|
740
|
+
toolName: "search";
|
|
741
|
+
details: SearchToolDetails | undefined;
|
|
742
742
|
}
|
|
743
743
|
|
|
744
744
|
export interface FindToolResultEvent extends ToolResultEventBase {
|
|
@@ -757,7 +757,7 @@ export type ToolResultEvent =
|
|
|
757
757
|
| ReadToolResultEvent
|
|
758
758
|
| EditToolResultEvent
|
|
759
759
|
| WriteToolResultEvent
|
|
760
|
-
|
|
|
760
|
+
| SearchToolResultEvent
|
|
761
761
|
| FindToolResultEvent
|
|
762
762
|
| CustomToolResultEvent;
|
|
763
763
|
|
|
@@ -785,7 +785,7 @@ export function isToolCallEventType(toolName: "bash", event: ToolCallEvent): eve
|
|
|
785
785
|
export function isToolCallEventType(toolName: "read", event: ToolCallEvent): event is ReadToolCallEvent;
|
|
786
786
|
export function isToolCallEventType(toolName: "edit", event: ToolCallEvent): event is EditToolCallEvent;
|
|
787
787
|
export function isToolCallEventType(toolName: "write", event: ToolCallEvent): event is WriteToolCallEvent;
|
|
788
|
-
export function isToolCallEventType(toolName: "
|
|
788
|
+
export function isToolCallEventType(toolName: "search", event: ToolCallEvent): event is SearchToolCallEvent;
|
|
789
789
|
export function isToolCallEventType(toolName: "find", event: ToolCallEvent): event is FindToolCallEvent;
|
|
790
790
|
export function isToolCallEventType<TName extends string, TInput extends Record<string, unknown>>(
|
|
791
791
|
toolName: TName,
|
|
@@ -21,7 +21,7 @@ import type {
|
|
|
21
21
|
SessionEntry,
|
|
22
22
|
SessionManager,
|
|
23
23
|
} from "../../session/session-manager";
|
|
24
|
-
import type { BashToolDetails, FindToolDetails,
|
|
24
|
+
import type { BashToolDetails, FindToolDetails, ReadToolDetails, SearchToolDetails } from "../../tools";
|
|
25
25
|
import type { TodoItem } from "../../tools/todo-write";
|
|
26
26
|
|
|
27
27
|
// Re-export for backward compatibility
|
|
@@ -494,10 +494,10 @@ export interface WriteToolResultEvent extends ToolResultEventBase {
|
|
|
494
494
|
details: undefined;
|
|
495
495
|
}
|
|
496
496
|
|
|
497
|
-
/** Tool result event for
|
|
498
|
-
export interface
|
|
499
|
-
toolName: "
|
|
500
|
-
details:
|
|
497
|
+
/** Tool result event for search tool */
|
|
498
|
+
export interface SearchToolResultEvent extends ToolResultEventBase {
|
|
499
|
+
toolName: "search";
|
|
500
|
+
details: SearchToolDetails | undefined;
|
|
501
501
|
}
|
|
502
502
|
|
|
503
503
|
/** Tool result event for find tool */
|
|
@@ -522,7 +522,7 @@ export type ToolResultEvent =
|
|
|
522
522
|
| ReadToolResultEvent
|
|
523
523
|
| EditToolResultEvent
|
|
524
524
|
| WriteToolResultEvent
|
|
525
|
-
|
|
|
525
|
+
| SearchToolResultEvent
|
|
526
526
|
| FindToolResultEvent
|
|
527
527
|
| CustomToolResultEvent;
|
|
528
528
|
|