Diff rendering
Pipeline overview
EditToolsorFileToolscompute diffs and statistics withdiff.- Tool responses include
diffandstatsobjects. ToolResultPresenterformats 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,
}
}
computeDiffproduces a unified diff string consumed by the terminal presenter.DiffStatssummarises 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: falseand include a recommendation. FileTools.writeFileperforms identical gating. WhenincludeDiffisfalseor there are no changes it omits bothdiffandstats:
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)
}
renderDiffBlockapplies syntax colouring and indentation to the unified diff.displayChangeStatsprints+added,-removed, and total change counts.- Similar helpers exist for
writeFilepreviews 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/computeStatsto provide consistent UI.
Source
src/utils/diff.tssrc/tools/EditTools.tssrc/tools/FileTools.tssrc/chat/ui/presenters/DiffRenderer.tssrc/chat/ui/ToolResultPresenter.ts