feat: show aggregated +/− line counts in FileChangesPanel header#11618
Conversation
There was a problem hiding this comment.
PR Review: #11618 -- Aggregated +/- line counts in FileChangesPanel header
Is this PR sound?
No -- this PR has significant scope creep and several issues that need to be addressed before merging.
The linked issue (#11615) requests one thing: "The File Changes panel header should show total lines added and removed across all changed files." The aggregated totals feature is well-implemented and accounts for roughly 30% of this PR. However, the remaining 70% introduces an entirely separate feature -- merged diff computation -- that reads the current file from disk at expand time and recomputes a single unified diff using createTwoFilesPatch. This second feature is never mentioned in the issue, was not requested, and introduces new backend infrastructure (readFileContent message handler, fileContent response type, originalContent plumbing through tools).
Findings
P0 (Critical) -- Path Traversal in readFileContent Handler
The new readFileContent handler in webviewMessageHandler.ts reads arbitrary files from the filesystem:
const absPath = path.isAbsolute(relPath) ? relPath : path.join(getCurrentCwd(), relPath)
const content = await fs.readFile(absPath, "utf-8")There is no path validation or sandboxing. A crafted message from the webview could read any file the extension process has access to (e.g., ../../.ssh/id_rsa, /etc/passwd). While the webview content is extension-controlled, this still expands the attack surface unnecessarily. Other existing handlers like openFile have the same pattern, but they open files in the editor (user-visible). Silently returning file contents to the webview is a different risk profile. At minimum, this needs workspace-boundary validation.
P1 (High) -- Scope Creep: Merged Diff Feature Bundled With Header Totals
This PR bundles two distinct features:
- Aggregated +/- totals in the header (what the issue asks for)
- Merged diff display using
originalContent+ live file reads (not requested)
The merged diff feature touches 5 files beyond what the totals feature requires:
- Adds
originalContenttoClineSayTooltype - Adds
originalContentpassthrough inApplyDiffTool.tsandApplyPatchTool.ts - Adds
readFileContent/fileContentmessage types toExtensionMessageandWebviewMessage - Adds
readFileContenthandler inwebviewMessageHandler.ts - Adds file-fetching state management and
createTwoFilesPatchlogic inFileChangesPanel.tsx - Adds
originalContenttoFileChangeEntryinterface
These should be separate PRs. The merged diff feature needs its own issue, design discussion, and review cycle.
P1 (High) -- Inconsistent originalContent Coverage Across Tools
originalContent is only added to ApplyDiffTool and ApplyPatchTool, but not to WriteToFileTool, SearchReplaceTool, EditTool, or EditFileTool -- all of which also emit tool: "appliedDiff" messages. This means the merged diff feature will silently fall back to concatenated diffs for edits made by those tools, creating an inconsistent and confusing user experience. Users will see merged diffs for some file edits and concatenated diffs for others with no indication of why.
P1 (High) -- No Tests for New Backend or Merged Diff Logic
- The
readFileContenthandler has zero test coverage. This is a new message type that reads from the filesystem. - The merged diff computation in
FileChangesPanel.tsx(thecreateTwoFilesPatchpath, theuseEffectfor requesting file content, the message listener) has no test coverage. - The only new tests cover the aggregated totals feature, which is the simpler part of the PR.
P2 (Medium) -- Duplicated Path Normalization
The lookupPath logic (path.startsWith("./") ? path.slice(2) : path) appears in two separate locations within FileChangesPanel.tsx (the useEffect for requesting content and the render function). This should be extracted to a helper to avoid drift between the two.
P2 (Medium) -- Missing Error Handling for createTwoFilesPatch
If createTwoFilesPatch throws (e.g., due to encoding issues or unexpected content), the entire FileChangesPanel render will crash. This call should be wrapped in a try/catch that falls back to the concatenated diff display.
P3 (Low) -- hasMergedDiff Condition May Suppress Valid Empty-File Diffs
The condition finalContent !== "" means if a file was edited down to empty content, the merged diff will not render -- it will fall back to concatenated diffs. This is a minor edge case but worth noting.
Recommendation
Split this PR into two:
- PR A (header totals): The
totalStatsmemo, the header rendering with+N/-N, and the corresponding tests. This is clean, well-tested, and directly addresses #11615. - PR B (merged diff display): The
originalContentplumbing,readFileContenthandler,fileContentmessage type, andcreateTwoFilesPatchlogic. This needs its own issue, consistent coverage across all edit tools, path validation, error handling, and test coverage.
hannesrudolph
left a comment
There was a problem hiding this comment.
Hannes: Disregard the scope creep / feature split feedback above, the merged diff addition is acceptable to bundle here. However, please fix the P0 path traversal vulnerability in readFileContent before we merge!
…-platform compatibility The isPathOutsideWorkspace mock used hardcoded Unix-style path comparisons (/mock/workspace with forward slashes), which fails on Windows where path.resolve() produces paths with drive letters (C:\mock\workspace\...). Replace manual string normalization with path.resolve() and path.sep so the mock behaves correctly on both Windows and Unix.
hannesrudolph
left a comment
There was a problem hiding this comment.
Tests are fully green.
Related GitHub Issue
Closes: #11615
Roo Code Task Context (Optional)
Description
This PR adds aggregated line counts (+/−) in the FileChangesPanel header. The panel header now shows total lines added and removed across all changed files so users can see the scope of changes at a glance.
Implementation:
totalStatsmemo inFileChangesPanel.tsx: SumsdiffStats.addedanddiffStats.removedfrom all file-change entries.+N(green) and-N(red) in theCollapsibleTrigger, right-aligned, using existing chart color tokens.aria-labelon the totals container;data-testidfor tests.Test Procedure
pnpm exec vitest run -- FileChangesPanelfromwebview-ui/.+Xand-Ynext to the file count whendiffStatsare available.diffStatsare present, totals are hidden; file count still displays.Pre-Submission Checklist
FileChangesPanel.spec.tsxincludes aggregated totals test.Screenshots / Videos
Documentation Updates
Additional Notes
diffStatsfrom the backend; edits without stats do not contribute to the header.Get in Touch
Discord: santy2509
Start a new Roo Code Cloud session on this branch