Skip to content

fix(pretty): prefer details.patch over details.diff for edit tool rendering - #1

Merged
hypnguyen1209 merged 1 commit into
pifydev:mainfrom
iamxeph:fix/support-pi-edit-diff
Oct 14, 2026
Merged

hypnguyen1209 merged 1 commit into
pifydev:mainfrom
iamxeph:fix/support-pi-edit-diff

Conversation

@iamxeph

@iamxeph iamxeph commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

Problem

When diffSplit: true is enabled, the edit tool diff never renders in side-by-side mode and silently falls back to a unified diff. Similarly, word-level diff emphasis and line-number gutters fail to activate for edit results, degrading to plain line-colored output.

Root Cause

In pi's core edit tool (packages/coding-agent/src/core/tools/edit.ts), EditToolDetails explicitly provides two diff representations:

export interface EditToolDetails {
	/** Display-oriented diff of the changes made */
	diff: string;
	/** Standard unified patch of the changes made */
	patch: string;
	firstChangedLine?: number;
}

When executing an edit:

const diffResult = generateDiffString(baseContent, newContent);
const patch = generateUnifiedPatch(path, baseContent, newContent);
return {
	content: [...],
	details: { diff: diffResult.diff, patch, firstChangedLine: diffResult.firstChangedLine },
};

extensions/pretty.ts previously read only result.details.diff (the display-oriented diff which lacks @@ hunk headers). Because details.diff does not contain @@ hunk headers, the inHunk state in both src/split.ts (parseRows) and src/diff.ts remained false across all lines. Consequently:

  • buildSplit treated every line as an unparsed full row, failing to split into columns.
  • colorizeDiff skipped paired word-diff analysis and line-number gutter formatting.

Solution

  • Added extractEditDiff(result) in extensions/pretty.ts to prefer details.patch when available, falling back to details.diff.
  • With details.patch (the standard unified patch) consumed, buildSplit and colorizeDiff receive the expected hunk headers, allowing side-by-side split rendering, word-level emphasis, and line gutters to work as originally designed without altering any core parser logic.

Verification

  • Added integration tests in test/wire.test.ts:
    • edit result prefers details.patch over details.diff (verifies split view separator │ is present when diffSplit is on)
    • edit result falls back to details.diff when details.patch is absent
  • Ran bun run typecheck: clean (0 errors).
  • Ran bun test: all 124 tests passed (0 failed).
  • Verified interactively in pi: side-by-side split diff and word-level emphasis render cleanly.

@iamxeph
iamxeph force-pushed the fix/support-pi-edit-diff branch from 0054ebc to 4bb3bb2 Compare October 1, 2026 13:40
@iamxeph
iamxeph force-pushed the fix/support-pi-edit-diff branch from 4bb3bb2 to 522d60c Compare October 14, 2026 06:44
@iamxeph

iamxeph commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

@hypnguyen1209 could you take a look at the two small fix PRs I have open here when you get a chance? Both are one-liner changes with tests attached — details are in each description.

@hypnguyen1209 hypnguyen1209 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is a real bug and the diagnosis is right: details.diff has no @@ hunk headers, so buildSplit and colorizeDiff never entered a hunk and the split view / word emphasis / old-new gutters silently did nothing for edit results. I confirmed details.patch has been there since pi 0.75.5 (feat(coding-agent): expose edit tool unified patch), well below this package's ^0.87.0 peer floor, so the fallback only matters for odd hosts.

I rendered the change with a patch generated by pi's own generateUnifiedPatch (diff.createTwoFilesPatch(..., { context: 4, headerOptions: FILE_HEADERS_ONLY })) rather than the fixture, and two things show up that the fixture hides:

  1. The file header lines are drawn. pi's patch starts with --- src/a.ts / +++ src/a.ts; they come through dimmed inside the result, right under a call line that already says Edit src/a.ts. The pre-apply preview (src/linediff.ts) emits hunks only, so the result and the preview now disagree.
  2. A phantom last line. pi's patch always ends with \n, so the split produces a trailing empty line, which the gutter numbers as line 6 of a 5-line file (and the split view draws it as an empty row on both sides). The fixture in the test has no trailing newline, so the test cannot see this.

Both are one strip away, see the inline suggestion. With it applied the result is @@ -1,5 +1,5 @@ + gutters/split, nothing else, and the whole suite (134 tests) stays green; I ran it locally against your branch.

One more small ask for the test: the fixture's leading =====... line is not something pi emits (FILE_HEADERS_ONLY drops the Index: and ==== lines), and the fixture should end with \n like a real patch, so that the phantom-line case is actually covered. Then this is good to merge.

Comment thread extensions/pretty.ts Outdated
function extractEditDiff(result: unknown): string {
if (!isRecord(result) || !isRecord(result.details)) return "";
const { patch, diff } = result.details;
if (typeof patch === "string" && patch) return patch;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pi's patch is --- path, +++ path, hunks, trailing newline. The call line already names the path, and the trailing newline draws as a phantom, numbered empty last line. Strip both here so the result matches the pre-apply preview, which emits hunks only:

Suggested change
if (typeof patch === "string" && patch) return patch;
if (typeof patch === "string" && patch) {
// pi's patch is `--- path`, `+++ path`, then hunks, with a trailing
// newline. The call line already names the path, and the trailing
// newline would draw as a phantom, numbered empty last line.
return patch
.replace(/\n$/, "")
.split("\n")
.filter((line, i) => !(i < 2 && (line.startsWith("--- ") || line.startsWith("+++ "))))
.join("\n");
}

…dering

pi's edit tool result details provides both `patch` (standard unified diff with
hunk headers) and `diff` (pre-formatted line-numbered diff without hunk headers).
The edit renderer previously read only `details.diff`, causing both word-level
emphasis and side-by-side split (`diffSplit`) to silently fall back because
they require `@@` hunk headers to pair lines.

Add `extractEditDiff` to prefer `details.patch` when available, falling back to
`details.diff`. The patch's `--- path` / `+++ path` header lines and its trailing
newline (which draws as a phantom, numbered empty last line) are stripped, so the
result body matches the pre-apply preview, which emits hunks only.
@iamxeph
iamxeph force-pushed the fix/support-pi-edit-diff branch from 522d60c to 8af7444 Compare October 14, 2026 07:15
@iamxeph

iamxeph commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

Applied — the patch's ---/+++ headers and its trailing newline are stripped now, so the result matches the pre-apply preview. The fixture is the real patch shape (no =====, ends with \n), and the test fails on either the headers or the phantom row if they come back. 134 tests + typecheck pass. The workflow run on the new SHA needs your approval.

@hypnguyen1209 hypnguyen1209 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied exactly, and the fixture is now the real patch shape with the trailing newline, so the phantom-row case is covered. Re-verified on top of main (0.14.0): typecheck clean, 134 tests pass, CI green on 8af7444. Merging. Thanks!

@hypnguyen1209
hypnguyen1209 merged commit 6491cc9 into pifydev:main Oct 14, 2026
2 checks passed
hypnguyen1209 added a commit that referenced this pull request Oct 14, 2026
- Edit results render from details.patch (pi's unified patch with hunk
  headers) instead of details.diff, so side-by-side split, word-level
  emphasis and the old/new gutter finally work for edit; the patch's
  file headers and trailing newline are stripped so the result matches
  the pre-apply preview (#1 by @iamxeph).
- The read summary coerces string-typed offset/limit before computing
  the line range, so "1" + "2000" no longer reads as lines 1-11999, and
  a non-numeric value is left out of the range (#2 by @iamxeph).
@iamxeph
iamxeph deleted the fix/support-pi-edit-diff branch October 14, 2026 07:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants