Skip to main content

Diff rendering

Pipeline overview​

  1. EditTools or FileTools compute diffs and statistics with diff.
  2. Tool responses include diff and stats objects.
  3. ToolResultPresenter formats the diff block and change metrics.

Diff utilities​

src/utils/diff.ts
// Generate unified diff format
export const computeDiff = (original: string, updated: string, filePath: string): string =>
Diff.createPatch(filePath, original, updated)

// Calculate change statistics
export const computeStats = (original: string, updated: string): DiffStats => {
const parts = Diff.diffLines(original, updated)
let added = 0
let removed = 0

for (const part of parts) {
if (part.added) added += part.count ?? 0
else if (part.removed) removed += part.count ?? 0
}

return {
linesAdded: added,
linesRemoved: removed,
totalChanges: added + removed,
}
}
  • computeDiff produces a unified diff string consumed by the terminal presenter.
  • DiffStats summarises change size for gating logic and UI display.

Tool responses​

src/tools/EditTools.ts
const stats = computeStats(content, updated)
const hasChanges = stats.totalChanges > 0
const diff = hasChanges ? computeDiff(content, updated, filePath) : undefined

return {
success: true,
path: relPath,
size: updated.length,
diff, // only present when changes detected
stats: hasChanges ? stats : undefined,
validation,
}
  • Preview responses reuse the same gating logic but set success: false and include a recommendation.
  • FileTools.writeFile performs identical gating. When includeDiff is false or there are no changes it omits both diff and stats:
src/tools/FileTools.ts
if (includeDiff) {
const computedStats = computeStats(original, content)
const hasChanges = computedStats.totalChanges > 0
stats = hasChanges ? computedStats : undefined
diff = hasChanges ? computeDiff(original, content, relPath) : undefined
}

This avoids noisy empty patches and keeps responses compact while still providing rich context when modifications occur.

Terminal presentation​

src/chat/ui/ToolResultPresenter.ts
export const displayEditDiff = (result: ToolResult, toolName: string): void => {
if (toolName !== 'editFile' || !result?.diff) return

console.log(
'\n' +
UI.Colors.BORDER(' ') +
UI.Colors.TOOL(`${UI.Icons.CHANGES} `) +
UI.Colors.MUTED('Changes'),
)
renderDiffBlock(result.diff)
displayChangeStats(result.stats as DiffStats)
}
  • renderDiffBlock applies syntax colouring and indentation to the unified diff.
  • displayChangeStats prints +added, -removed, and total change counts.
  • Similar helpers exist for writeFile previews and tool validations.

Rendering helper​

src/chat/ui/presenters/DiffRenderer.ts
export const renderDiffBlock = (diff: string) => {
const lines = diff.split('\n')
for (const line of lines) {
if (line.startsWith('+')) console.log(UI.Colors.DIFF_ADD(` ${line}`))
else if (line.startsWith('-')) console.log(UI.Colors.DIFF_REMOVE(` ${line}`))
else console.log(UI.Colors.DIFF_CONTEXT(` ${line}`))
}
}

The renderer colours additions/removals and keeps context lines muted so the diff stays legible in the terminal.

Usage tips​

  • Use previews (previewEdit) to show diffs without touching the filesystem.
  • Watch the stats to decide whether to prompt the assistant for confirmation.
  • Custom tools can reuse computeDiff/computeStats to provide consistent UI.

Source​

  • src/utils/diff.ts
  • src/tools/EditTools.ts
  • src/tools/FileTools.ts
  • src/chat/ui/presenters/DiffRenderer.ts
  • src/chat/ui/ToolResultPresenter.ts