Skip to content

feat(vscode): restore multi-project section and drag-and-drop support - #12803

Merged
marius-kilocode merged 3 commits into
mainfrom
plan-multi-repo-section-drag-and-drop
Aug 3, 2026
Merged

feat(vscode): restore multi-project section and drag-and-drop support#12803
marius-kilocode merged 3 commits into
mainfrom
plan-multi-repo-section-drag-and-drop

Conversation

@marius-kilocode

Copy link
Copy Markdown
Collaborator

Summary

Restore Agent Manager sections and worktree drag-and-drop when multiple projects are shown, with ordering and section moves scoped to the owning project.

Multi-project Agent Manager with sections and drag-and-drop

Changes

  • Extract shared worktree ordering/grouping logic used by both single and multi-project modes
  • Add per-project drag-and-drop providers with scoped identifiers
  • Preserve live run statuses when state payloads omit them
  • Match single-project label resolution using ordered session titles
  • Make section auto-rename tracking request-scoped to prevent stale renames
  • Expand multi-project Storybook story with persisted ordering, sections, and grouped worktrees
  • Add project-store isolation and ordering tests

Testing

  • All 3,665 unit tests pass
  • Focused Agent Manager tests: 62 passed
  • Verified with extension compile, knip, and kilocode_change checks

Restore Agent Manager sections and worktree drag-and-drop when multiple
projects are shown, with ordering and section moves scoped to the owning
project.

- Extract shared worktree ordering/grouping logic used by both single and
  multi-project modes
- Add per-project drag-and-drop providers with scoped identifiers
- Preserve live run statuses when state payloads omit them
- Match single-project label resolution using ordered session titles
- Make section auto-rename tracking request-scoped to prevent stale
  renames
- Expand multi-project Storybook story with persisted ordering, sections,
  and grouped worktrees
- Add project-store isolation and ordering tests
const state = this.getStateManager()
if (state) {
state.setWorktreeOrder(m.order)
this.pushState()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: The added pushState() on every drag end may not be needed, and it is not free

The multi-project body now applies the new order to its own store optimistically (ProjectSidebarBody.tsx:156-162), so unlike setSessionsCollapsed (which documents why it must round-trip) nothing here depends on the echoed state. Two side effects worth weighing:

  • pushState() calls pushProjectSessions(), which re-lists sessions for the project root and every worktree directory whenever the 2s freshness window has lapsed — i.e. a backend round trip per worktree on each drop.
  • The pushed payload includes tabOrder, and store.applyState() overwrites the store's tab order with it. The persisted order has terminal/review tab ids stripped (AgentManagerApp.tsx:506-508), so a worktree drag end can snap those tabs back to the tail of the tab bar. That already happens for renameWorktree/section ops, but this adds a new, much more frequent trigger.

If the push is only there so a background project's normalized order lands in the webview, the store already holds an equivalent order; consider dropping it (or gating on whether the normalization actually changed anything — WorktreeStateManager.setWorktreeOrder discards the changed result that moveSection already uses).


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

const runs: Record<string, RunStatus> = {}
for (const item of state.runStatuses ?? []) runs[item.worktreeId] = item
setRunStatuses(runs)
if (state.runStatuses) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Preserved run statuses are never reconciled against the worktree list

Guarding on state.runStatuses is the right fix for background payloads (pushState omits runStatuses for non-active projects), but it also means a status can now outlive the worktree it belongs to: a background project's payload never resends the list, so a run status for a deleted worktree stays in the map forever and keeps runs()[id]?.state === "running" truthy for a recreated/reused id. The busy map right below already prunes against state.worktrees; consider doing the same here, e.g. filtering the retained statuses by ids instead of leaving them untouched.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

return
}
if (section && sections().some((item) => item.id === section)) {
post({ type: "agentManager.moveToSection", worktreeIds: [from], sectionId: section })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: The section-drop branch skips the origin rewind that the other exit paths perform

By the time the pointer reaches a section header, onDragOver has already rewritten store.worktreeOrder for every worktree it passed over. Both invalid-drop branches rewind with store.setWorktreeOrder(origin), but this branch neither persists the new order nor rewinds it, so local order stays diverged from persisted until the moveToSection round-trip push arrives. It does self-heal today (the section handler always pushes state, and the payload always carries worktreeOrder), so this is cosmetic — but the asymmetry is easy to misread as intentional.

While here: origin is snapshotted at drag start, so if an agentManager.state push lands mid-drag, the rewind paths restore the pre-drag order and discard the newer persisted one until the next push.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

const detector = sectionAwareDetector(sectionIds, home)
const dragIds = createMemo(() => sorted().map((wt) => scope("worktree", wt.id)))

const onDragStart = (event: DragEvent) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: These three handlers are a near-copy of SidebarBody.tsx:258-286

The only real differences are the scoped ids and the origin rewind. Since the PR already extracted sortWorktrees to keep single- and multi-project ordering in sync, it would be consistent to extract the drag handlers too (a small factory taking scope/parse, the sorted list accessor, and the persist callback), so the two sidebars cannot drift apart the next time drag behaviour changes. Not a blocker.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.


it("keeps multi-version siblings adjacent at the first group position", () => {
const all = [wt("a", { groupId: "g" }), wt("b"), wt("c", { groupId: "g" })]
expect(sortWorktrees(all, ["b", "c", "a"]).map((item) => item.id)).toEqual(["b", "c", "a"])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: This case does not actually exercise the grouping branch

With order = ["b", "c", "a"] the expected result is identical to plain applyTabOrder output, so the test passes even if the group-adjacency logic is removed. A case where the two differ would pin the behaviour the test name describes, e.g. sortWorktrees(all, ["a", "b", "c"]) should yield ["a", "c", "b"] — the g sibling pulled up next to its group start.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 6 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 5
Issue Details (click to expand)

WARNING

File Line Issue
image.png - Stray 114 KB PNG committed at the repo root, unreferenced and unrelated to the change. Not inline-commentable (binary), so reported here only — it looks like an accidental git add and should be dropped from the branch.

SUGGESTION

File Line Issue
packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts 740 Added pushState() on every drag end re-lists project sessions and re-broadcasts tabOrder (which has terminal/review ids stripped), while the webview store already applied the order optimistically.
packages/kilo-vscode/webview-ui/agent-manager/project/store.ts 81 Retained run statuses are never pruned against state.worktrees, so a status can outlive its worktree now that background payloads omit the list.
packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx 178 The section-drop branch skips the origin rewind the other exit paths do; also origin is a drag-start snapshot that can discard a mid-drag state push.
packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx 144 Drag handlers are a near-copy of SidebarBody.tsx:258-286; extracting them would keep single- and multi-project behaviour in sync.
packages/kilo-vscode/tests/unit/section-helpers.test.ts 124 The multi-version adjacency case passes without the grouping branch; the chosen order matches plain applyTabOrder output.
Incremental review note

Since the previous review (9819c1c3), the branch merged origin/main (a112823f) and updated visual-regression baselines (1ca7d690). Merge resolutions in the 6 files touched on both sides were re-verified: the PR's changes are cleanly preserved on top of main's changes (store/mode props coexist on ProjectList, sortWorktrees extraction intact, scoped setWorktreeOrder routing intact). All 6 prior findings were re-verified against current HEAD and remain unresolved; no new issues found in the increment.

Files Reviewed (14 files)
  • .changeset/bright-project-dnd.md
  • image.png - 1 issue
  • packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts - 1 issue
  • packages/kilo-vscode/src/agent-manager/types.ts
  • packages/kilo-vscode/tests/unit/project-store.test.ts
  • packages/kilo-vscode/tests/unit/section-helpers.test.ts - 1 issue
  • packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/ProjectList.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx - 2 issues
  • packages/kilo-vscode/webview-ui/agent-manager/SectionHeader.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/project/store.ts - 1 issue
  • packages/kilo-vscode/webview-ui/agent-manager/section-helpers.ts
  • packages/kilo-vscode/webview-ui/src/stories/agent-manager.stories.tsx
  • packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts

Fix these issues in Kilo Cloud

@marius-kilocode
marius-kilocode merged commit e83ab0d into main Aug 3, 2026
32 of 33 checks passed
@marius-kilocode
marius-kilocode deleted the plan-multi-repo-section-drag-and-drop branch August 3, 2026 15:15
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
…on-drag-and-drop

feat(vscode): restore multi-project section and drag-and-drop support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants