Render colored inline diffs for file-edit tool calls#83
Merged
Conversation
When the agent ran replace_in_file or write_file the user saw just "⎿ 1 line in 12ms" and had no idea what actually changed. Now the display shows a Claude-Code-style inline diff: an "Added N, removed M" summary followed by colored unified-diff hunks (red minus, green plus, cyan hunk header, dim metadata), so the change is visible at a glance. Implementation: - New R/diff-render.R::compute_unified_diff(old, new, path) shells out to `diff -u`. Returns NULL when the inputs are identical (display silently skips), an all-green payload for new files (old == ""), and a fallback summary when `diff` isn't on PATH so the tool call still succeeds. - New ok_with_diff(text, diff) in R/utils.R is a thin builder; ok() itself is untouched so the diff-extension doesn't bleed into the package's general tool-result contract. - tool_replace_in_file passes its existing `original`/`updated` strings; tool_write_file reads prior content before the write and diffs against the post-write content (taking append mode into account). - turn.R threads the diff payload through outcome_text -> event; observer_progress() renders via render_tool_diff() when present. - inst/bin/corteza tool_handler does the same render. - colorize_diff() in R/cli-colors.R is the shared line painter; also wired into the /diff slash command so its output is colored too. - LLM-facing result text is unchanged. The model already knows what it asked for; the diff is purely for the human reading the terminal. 27 new tinytests cover identical / new file / emptied / multi-hunk / missing trailing newline / `diff`-not-on-PATH fallback.
Codex flagged that compute_unified_diff() was unbounded: a 1000-line new file dumped 1003 lines into chat and CLI scrollback (and shipped the same volume across the callr worker boundary). Cap the lines vector with a "[diff truncated: N more lines]" marker so the user can see the size of the change without drowning in it. Summary counts still reflect the full diff so "Added 1000 lines" stays accurate. The new max_lines / max_chars params default to 200 / 20000 — tighter than the 300 / 60000 used by /diff because inline tool diffs live in chat scrollback rather than a one-shot command output. Both budgets are tunable; pass Inf to disable.
Contributor
Author
|
Addressed in d0bf67c: |
…numbers Codex flagged that the CLI script and corteza::chat() were making separate decisions about color support and inline-diff layout, which is exactly how the "CLI is colored but chat() isn't" drift just hit the user. Centralize the shared bits before the gap widens. Color policy is now sourced from R/cli-colors.R only: - ansi_supported() learns NO_COLOR / FORCE_COLOR / RSTUDIO so RStudio's R console (which is not a tty) and override env vars both work. - inst/bin/corteza deletes its private .ansi_supported() and inline 16-entry color list; both surfaces call corteza:::ansi_colors(). Inline diffs rendered through the same renderer everywhere: - render_tool_diff() now drops the redundant `--- /path` and `+++ /path` lines (the path is already in the tool-call title) and the `@@` hunk headers. It parses each hunk header for starting line numbers, walks the body, and emits one row per line as `NNNN +|-| content` with red on removals, green on additions, default on context. Truncation marker passes through dim. - Tool labels: replace_in_file -> "Update", write_file -> "Write" to match Claude Code phrasing and match the rendered diff context. This is the scoped step Codex suggested; a full console_ui() module that owns every cat() in the CLI is a follow-up, filed separately.
Contributor
Author
|
Addressed codex's drift concern in fc9788a — scoped centralization:
Full |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When the agent ran
replace_in_fileorwrite_filethe user saw just⎿ 1 line in 12msand had no idea what actually changed. Now the display shows a Claude-Code-style inline diff: anAdded N, removed Msummary followed by colored unified-diff hunks (red minus, green plus, cyan hunk header, dim metadata).Implementation
R/diff-render.R::compute_unified_diff(old, new, path)shells out todiff -u. ReturnsNULLwhen inputs are identical (display silently skips), an all-green payload for new files, and a fallback size summary whendiffisn't on PATH so the tool call still succeeds.ok_with_diff(text, diff)is a thin builder;ok()itself is untouched so the diff extension doesn't bleed into the package's general tool-result contract (per codex's earlier review).tool_replace_in_filepasses its existingoriginal/updatedstrings — no special substring math;tool_write_filereads prior content before the write and diffs against the post-write content (handlingappendmode).turn.Rthreads the diff payload through events;observer_progress()and the CLItool_handlerboth render viarender_tool_diff()when present.colorize_diff()inR/cli-colors.Ris the shared line painter; also wired into the/diffslash command so its output is colored too.Test plan
test_diff_render.R. Full suite: 1590/1590 pass.corteza::chat()→ ask the agent to edit a file → confirm inline colored diff appears.~/bin/cortezasame./diffslash command now colored.Updated <path> (1 replacement)/Wrote N bytes to <path>.