refactor(jetbrains): consolidate worktree change summaries into ChangesPanel - #13583
refactor(jetbrains): consolidate worktree change summaries into ChangesPanel#1358310 commits merged into
Conversation
… on worktree rows
Agent Manager worktree rows showed no +/- badge for branches that were
fully pushed, because stats() diffed against the worktree's own
@{upstream} instead of the repo's base branch. Once a branch is pushed,
merge-base(HEAD, upstream) equals HEAD, so the diff reported nothing.
stats() now resolves the base branch the same way the branch-diff editor
does (origin/HEAD, then origin/main/master, then local main/master,
falling back to the main worktree's branch), diffs committed work only
(trailing HEAD on the numstat call excludes the working tree), and
reports which base ref it used.
Uncommitted work moves to a new dirty() RPC and gets its own muted badge
next to the PR diff, since it has a different baseline (HEAD) and
refresh cadence than the PR-parity number.
Keep PR badges beside the diff statistics while exposing generic clickable badge slots and model-based action dispatch. Let Agent Manager own PR semantics instead of the shared list and metrics components.
Unify base and local Git comparisons behind shared backend/frontend boundaries instead of overlapping ad-hoc implementations: - Extract reusable comparison logic into backend/diff/GitComparison.kt and add a committed-only branchDiff plus a new localDiff endpoint. - Consolidate WorktreeStatsView and BranchChangesBadge into a single ChangesPanel with Compact/Full modes, reused across the Agent Manager worktree list, session toolbar, and worktree editor header. - Centralize diff editor opening/naming in KiloDiffEditorKind/Content so every surface produces identical params for the same comparison. - Migrate AgentManagerPanel, ActiveListRenderer, BranchDock, WorktreePrHeaderView, and WorktreeSessionEditorPanel onto the new component and RPC surface, removing redundant presentation code. Consolidates the three prior changesets covering PR badge placement, worktree PR diff counts, and the uncommitted changes badge into one release note.
# Conflicts: # packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/agentManager/worktree/WorktreePrHeaderView.kt # packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/agentManager/worktree/WorktreeSessionEditorPanel.kt # packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt # packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/agentManager/worktree/WorktreePrHeaderViewTest.kt
Remove the "Uncommitted" and "vs <base>" text labels from the worktree session editor header's full ChangesPanel groups, leaving only the file count and +/- badges. The distinguishing context is still available via each group's tooltip, so the header stays compact without redundant text.
Add a vertical separator between the changes summary and the trailing action buttons (Run, Open, Terminal) in the worktree editor header, shown only when both sides have visible content. Fix HoverIcon's labelled variant (used by the Open and Build/Run buttons) to size itself from its actual icon+text content instead of falling through to DarculaButtonUI's default preferred size, which enforces a 72x24 dialog-button minimum. That minimum was inflating short labels like "Open" into much wider buttons than the icon-only Terminal button, which already bypassed it.
Revert the labelled-button sizing change: Build/Run and Open go back to the platform's preferred size, so their insets match what they were. Instead give the icon-only Terminal button the labelled buttons' margin and toolbar insets, plus a new HoverIcon.match mode that squares it to the height the look-and-feel gives a button. Deriving that height from the LAF (rather than a fixed token) keeps it level with its labelled siblings under both Darcula and the plain test LAF. Add a leading separator to the header toolbar so the cluster reads as separator, standard padding, changes summary, separator, actions. Both separators stay hidden until an action exists, and the padding is a Stack gap rather than a filler so it collapses with the hidden separator -- leaving dock hosts with no trailing actions unchanged.
The separator before the changes summary read as clutter next to the PR title. Replace it with standard left padding on the toolbar row itself, which keeps the summary spaced off the title without drawing a rule. Padding now lives in the row's border instead of a Stack gap so it no longer depends on a separator being visible to apply.
| } | ||
|
|
||
| private fun loadDirty() { | ||
| val dir = project.basePath ?: return |
There was a problem hiding this comment.
WARNING: loadDirty() sends the synthetic frontend basePath to the backend
loadStats() and loadPr() resolve project.kiloRoot() inside the coroutine because, in split mode, project.basePath is a JetBrains Client path that does not exist on the host (ProjectRoot documents this). loadDirty() still uses basePath, so backend dirty() hits sync() → missing directory and returns an empty list. Uncommitted header counts stay at 0 in remote/split mode while committed stats still work.
kiloRoot() is suspend, so the fix is to mirror loadStats(): resolve it inside cs.launch, not by substituting on this line.
Tests miss this because fakeRoot(..., project.basePath!!) makes kiloRoot == basePath, and FakeWorktreeRpcApi.dirty() ignores the directory argument.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| val revs = if (to == null) listOf(from) else listOf(from, to) | ||
| val opts = listOf( | ||
| "--literal-pathspecs", "-c", "core.quotepath=false", "diff", "--relative", "--no-color", | ||
| "--no-ext-diff", "--no-textconv", "--find-renames", "--rename-empty", |
There was a problem hiding this comment.
WARNING: --rename-empty breaks comparisons on common Git versions
Every git diff (base and local, including stats()/dirty()) passes --rename-empty. That option is recent; typical distro and Xcode Git (2.34–2.43) reject unknown options with a non-zero exit. CmdOut.checked() then throws, so badges and diff editors go empty. --find-renames already covers normal rename detection — drop --rename-empty rather than failing all comparisons for empty-file renames.
| "--no-ext-diff", "--no-textconv", "--find-renames", "--rename-empty", | |
| "--no-ext-diff", "--no-textconv", "--find-renames", |
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
Code Review SummaryStatus: 7 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (59 files)
Fix these issues in Kilo Cloud Previous Review Summary (commit e0be1a7)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit e0be1a7)Status: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Files Reviewed (47 files)
Reviewed by grok-4.6 · Input: 249.3K · Output: 30.1K · Cached: 1M Review guidance: REVIEW.md from base branch |
Port VS Code's worktree setup script (.kilo/setup-script[.sh|.ps1|.cmd| .bat]) to the JetBrains plugin: - Resolve the script per-platform with disjoint POSIX/Windows candidate lists, and create it from VS Code's own templates when opened and missing (backend/rpc/SetupScriptTemplate.kt). - Run it in a terminal tab dedicated to the worktree (distinct from the Terminal button's tab), with WORKTREE_PATH/REPO_PATH set via envVariables() so the sent command needs no shell-specific env-prefix syntax. - Fire automatically, non-blocking, when a worktree is created (covers create, import, and move-to-worktree); silent no-op when no script exists. - Add Open/Create + Run to the worktree row menu (hidden on the main row); Open/Create only, not Run, in the tool-window settings popup. Also shorten several worktree row action labels and add "Open Uncommitted Changes" (KiloDiffComparison.LOCAL) alongside the existing base-diff action: - Open Pull Request in Browser -> Open in Browser - Open Diff Against Base Branch -> Open Diff Against Base - Open/Run Worktree Setup Script -> Open/Run Worktree Setup - Delete Worktree -> Delete
| internal fun setupScriptCommand(script: SetupScriptTargetDto): String = when (script.kind) { | ||
| SetupScriptKind.POSIX -> "sh ${quotePosix(script.path)}" | ||
| SetupScriptKind.POWERSHELL -> "powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ${quoteWindows(script.path)}" | ||
| SetupScriptKind.CMD -> "cmd.exe /d /s /c ${quoteWindows(script.path)}" |
There was a problem hiding this comment.
WARNING: cmd.exe /s /c strips the wrapping quotes, so paths with spaces break
/s makes cmd drop the first and last quote of the /c remainder. cmd.exe /d /s /c "C:\\Users\\Jane Doe\\repo\\.kilo\\setup-script.cmd" therefore becomes the unquoted command C:\Users\Jane Doe\repo\.kilo\setup-script.cmd, which cmd splits on spaces. VS Code avoids this by spawning argv; here the string is typed into a shell. Extra quotes after /c (""path"") are the usual /s fix so one quoted pair remains after the strip.
| SetupScriptKind.CMD -> "cmd.exe /d /s /c ${quoteWindows(script.path)}" | |
| SetupScriptKind.CMD -> "cmd.exe /d /s /c \"${quoteWindows(script.path)}\"" |
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| existing.content.manager?.setSelectedContent(existing.content, true) | ||
| } | ||
| ToolWindowManager.getInstance(project).getToolWindow(TerminalToolWindowFactory.TOOL_WINDOW_ID)?.activate(null) | ||
| tab.view.createSendTextBuilder().shouldExecute().send(setupScriptCommand(script)) |
There was a problem hiding this comment.
WARNING: Reused setup tabs still send() the command into a live PTY
openTerminal() reuses a tagged tab and returns without typing. This path reuses the dedicated setup tab, then always shouldExecute().send(...). A second Run, or Run while auto-run is still going, dumps the command into a busy shell (stdin of npm install, a pager, a partial line). workingDirectory / envVariables are applied only at tab creation, so a reused tab the user cd'd away from also runs in the wrong directory. Create a new tab (as GhAuth does) or focus-and-return when the tagged tab already exists.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| fun openSetupScript(directory: String, done: (Boolean) -> Unit) { | ||
| cs.launch { | ||
| val ok = try { | ||
| call { this.openSetupScript(directory) } |
There was a problem hiding this comment.
WARNING: Creating the setup script never updates the setupScript cache
OpenSetupScriptAction and canRunSetup only call refreshSetupScriptTarget when the map entry is missing. After the first lookup stores exists = false, openSetupScript writes the file but leaves the cache stale. The row menu then stays on "Create Worktree Setup" and canRunSetup keeps Run hidden until restart. On success, refresh the cache (or always re-fetch on menu update, like workspace() does for local/global config).
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| val target = project ?: return | ||
| service<KiloWorkspaceService>().ifSetupScriptExists(controller.directory) { script -> | ||
| Telemetry.send("Worktree Setup Script Run", mapOf("surface" to "auto")) | ||
| runWorktreeSetupScript(target, script, created.path, controller.directory) |
There was a problem hiding this comment.
WARNING: Setup runs without copying root .env files, unlike VS Code
VS Code runSetupScriptForWorktree always copyEnvFiles from the main repo into the worktree before invoking the script. The ported template (SetupScriptTemplate) tells users that copy already happened and that the script should not redo root .env. JetBrains git worktree add does not copy gitignored env files, and neither auto-run nor runSetup does either. Scripts written against that template (or shared with VS Code) will not see .env in the worktree. This needs a host-side copy (backend RPC in split mode) before both auto and manual runs.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| "@kilocode/kilo-jetbrains": minor | ||
| --- | ||
|
|
||
| Run the worktree setup script (`.kilo/setup-script`) in a terminal when a new worktree is created, and add actions to open, create, or run it from the worktree row menu and the settings menu. |
There was a problem hiding this comment.
SUGGESTION: Release note mentions a settings-menu action that is not wired
Kilo.OpenSetupScript / Kilo.Worktree.RunSetupScript are only in Kilo.Worktree.RowMenu. Tests assert Kilo.OpenSetupScript is not in Kilo.SettingsGroup. Users reading the changelog will look in the wrong menu.
| Run the worktree setup script (`.kilo/setup-script`) in a terminal when a new worktree is created, and add actions to open, create, or run it from the worktree row menu and the settings menu. | |
| Run the worktree setup script (`.kilo/setup-script`) in a terminal when a new worktree is created, and add actions to open, create, or run it from the worktree row menu. |
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
Issue
Fixes #
Context
Agent Manager's worktree rows, session toolbar, and worktree editor header had three overlapping implementations of "changes" presentation (
WorktreeStatsView,BranchChangesBadge, and ad hoc diff-opening logic), each with its own comparison semantics: the editor header mixed committed and local changes into one diff and disabled rename detection, so its numbers didn't match the worktree list's totals.This PR unifies base (committed, merge-base→HEAD) and local (HEAD→working tree) comparisons behind one backend pipeline and one retained Swing component, then polishes the resulting worktree editor header toolbar based on visual review.
Implementation
backend/diff/GitComparison.kt.KiloWorkspaceRpcApi.branchDiffis now committed-only; addedlocalDifffor the working-tree side. Both feedKiloWorktreeRpcApiImpl.stats()/dirty()so list totals and the editor header always agree.WorktreeStatsViewandBranchChangesBadgeintoui/ChangesPanel.ktwithCompact/Fullmodes, reused by the Agent Manager worktree list (via a thinActiveListhit-cell adapter), the session toolbar, and the worktree editor header (WorktreePrHeaderView/PrHeaderView).KiloDiffEditorKind/KiloDiffEditorContentso every surface (badge click, context-menu "Compare to Base", worktree header) produces identical params and reuses the same tab for a given comparison.main: this branch was rebased against a concurrent Run Configuration feature (WorktreeRunControl,runheader slot) landed onmain; resolved by keeping both the newChangesPanel-based header and the Run control button, and fixing a test that asserted the old text-only Terminal button.<base>" text captions from the full header (the tooltip already carries that context); fixedOpen/Build-Runvs.Terminalbutton-size inconsistency —HoverIcon's icon-only path used a fixed 24px square while labelled buttons went throughDarculaButtonUI's LAF sizing, so they never matched; added an opt-inHoverIcon.matchmode that squares an icon-only button to the LAF-computed labelled-button height instead of a hardcoded constant (verified this needed to come from the LAF, not a fixed token, because the height differs between Darcula and the plain test LAF); added then removed a leading toolbar separator per follow-up feedback, replacing it with a plain left inset on the toolbar row.Screenshots / Video
No exported before/after image assets from this session — the header changes were iterated live against screenshots pasted directly into chat by the reviewer (captions removed, button paddings equalized, separator placement adjusted), each confirmed before proceeding to the next tweak. See the "How to Test" steps below to reproduce the current state.
How to Test
Manual/local verification
./gradlew typecheckand./gradlew testfrompackages/kilo-jetbrains/after every commit in this branch; latest run: 3400+ frontend/backend tests green, zero failures.WorktreeStatsView/BranchChangesBadgeclasses (grep -rlacrosspackages/kilo-jetbrains).HoverIcon.kt's labelled-button path against the pre-change baseline to confirmOpen/Build-Runsizing is byte-identical to before (only the icon-onlyTerminalpath changed).Reviewer test steps
Changes vs base branch (<branch>).<base>" text labels (just counts + tooltips), a separator only appears between the changes summary and the action buttons when both sides have content, andBuild/Run,Open, and the icon-onlyTerminalbutton are the same height with matching padding.Blocked checks and substitute verification
./gradlew :shared:detekt :backend:detekt :frontend:detektfails, but with the same weighted-issue counts onmainbefore this branch's changes (confirmed viagit stash+ rerun) and none of the flagged files are touched by this diff — pre-existing baseline noise, not a regression.Checklist
Get in Touch