From d0dd908fb0499283a6155f1af0bc88e69bb4c394 Mon Sep 17 00:00:00 2001 From: Josh Lambert Date: Thu, 6 Aug 2026 09:49:22 -0400 Subject: [PATCH] fix(agent-manager): extract pending-create below the app size cap PR #12931 grew AgentManagerApp.tsx to 2808 lines, over its 2800 max-lines ESLint cap. Main's push workflows path-filter the lint job so the breakage is latent there, but every kilo-vscode PR fails merge CI. Extract the cross-project pending-create controller into pending-create.ts instead of raising the cap. --- ...agent-manager-new-worktree-project.test.ts | 6 ++-- .../agent-manager/AgentManagerApp.tsx | 33 +++++++----------- .../agent-manager/pending-create.ts | 34 +++++++++++++++++++ 3 files changed, 50 insertions(+), 23 deletions(-) create mode 100644 packages/kilo-vscode/webview-ui/agent-manager/pending-create.ts diff --git a/packages/kilo-vscode/tests/unit/agent-manager-new-worktree-project.test.ts b/packages/kilo-vscode/tests/unit/agent-manager-new-worktree-project.test.ts index 35e439bc87..c11368c699 100644 --- a/packages/kilo-vscode/tests/unit/agent-manager-new-worktree-project.test.ts +++ b/packages/kilo-vscode/tests/unit/agent-manager-new-worktree-project.test.ts @@ -5,6 +5,7 @@ import { join } from "node:path" const root = join(__dirname, "..", "..") const dialog = readFileSync(join(root, "webview-ui", "agent-manager", "NewWorktreeDialog.tsx"), "utf8") const app = readFileSync(join(root, "webview-ui", "agent-manager", "AgentManagerApp.tsx"), "utf8") +const pending = readFileSync(join(root, "webview-ui", "agent-manager", "pending-create.ts"), "utf8") const importer = readFileSync(join(root, "src", "agent-manager", "worktree-importer.ts"), "utf8") const css = readFileSync(join(root, "webview-ui", "agent-manager", "agent-manager.css"), "utf8") @@ -20,9 +21,10 @@ describe("Agent Manager New Worktree project targeting", () => { }) it("does not replace a pending cross-project activation", () => { - expect(app).toContain("if (pendingCreate()) return") + expect(pending).toContain("if (pending()) return") + expect(app).toContain("usePendingCreate(activeProjectId") expect(app).toContain('msg.type === "agentManager.importResult"') - expect(app).toContain("!msg.success && pendingCreate()?.projectId === msg.projectId") + expect(app).toContain("!msg.success) creation.abandon(msg.projectId)") }) it("tags branch and import responses with their owning project", () => { diff --git a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx index 615ebb350d..bff0706d2b 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx @@ -27,7 +27,6 @@ import type { AgentManagerWorktreeDiffLoadingMessage, AgentManagerWorktreeDiffNoticeMessage, AgentManagerDiffBranchesMessage, - AgentManagerImportResultMessage, AgentManagerApplyWorktreeDiffResultMessage, AgentManagerWorktreeStatsMessage, AgentManagerLocalStatsMessage, @@ -181,6 +180,7 @@ import { clampPanelWidth, maxPanelWidth, minPanelWidth } from "./side-panel-layo import { buildShortcutCategories } from "./shortcuts" import { tracker } from "./telemetry" import { createChatFocus, hasQuestionOption } from "./focus" +import { usePendingCreate } from "./pending-create" import "./agent-manager.css" import "./agent-manager-review.css" import { cycleAgent as cycle } from "../src/context/session-agent" @@ -268,12 +268,12 @@ const AgentManagerContent: Component = () => { const [currentProjectId, setCurrentProjectId] = createSignal() const [projectStates, setProjectStates] = createSignal>({}) const activeProjectId = () => projectList().find((p) => p.active)?.id ?? currentProjectId() - const [pendingCreate, setPendingCreate] = createSignal<{ projectId: string }>() - const scheduleCreate = (projectId: string) => { - if (projectId === activeProjectId()) return - if (pendingCreate()) return - setPendingCreate({ projectId }) - } + const creation = usePendingCreate(activeProjectId, (projectId, worktreeId) => + vscode.postMessage({ + type: "agentManager.activateSelection", + target: { projectId, kind: "worktree", worktreeId }, + }), + ) const isActivePayload = (pid: string | undefined) => projectList().length === 0 || pid === undefined || pid === activeProjectId() @@ -1394,15 +1394,7 @@ const AgentManagerContent: Component = () => { if (msg.type === "agentManager.worktreeSetup") { const ev = msg as AgentManagerWorktreeSetupMessage - const pending = pendingCreate() - if (ev.status === "ready" && ev.projectId && pending?.projectId === ev.projectId && ev.worktreeId) { - setPendingCreate(undefined) - vscode.postMessage({ - type: "agentManager.activateSelection", - target: { projectId: ev.projectId, kind: "worktree", worktreeId: ev.worktreeId }, - }) - } - if (ev.status === "error" && pending?.projectId === ev.projectId) setPendingCreate(undefined) + creation.setup(ev) const store = ev.projectId ? registry.ensure(ev.projectId) : registry.active() const updateBusy: Setter> = (value) => store.setBusy(value) if (ev.status === "ready" || ev.status === "error") { @@ -1444,8 +1436,7 @@ const AgentManagerContent: Component = () => { } } - if (msg.type === "agentManager.importResult" && !msg.success && pendingCreate()?.projectId === msg.projectId) - setPendingCreate(undefined) + if (msg.type === "agentManager.importResult" && !msg.success) creation.abandon(msg.projectId) if (msg.type === "agentManager.sessionAdded") { const ev = msg as { type: string; sessionId: string; worktreeId: string } @@ -1488,7 +1479,7 @@ const AgentManagerContent: Component = () => { // When a multi-version progress update arrives, mark newly created worktrees as loading if ((msg as { type: string }).type === "agentManager.multiVersionProgress") { const ev = msg as unknown as AgentManagerMultiVersionProgressMessage - if (ev.status === "done" && pendingCreate()?.projectId === ev.projectId) setPendingCreate(undefined) + if (ev.status === "done") creation.abandon(ev.projectId) if (ev.status === "done" && ev.groupId) { // Clear busy state for all worktrees in this group const store = ev.projectId ? registry.ensure(ev.projectId) : registry.active() @@ -1914,7 +1905,7 @@ const AgentManagerContent: Component = () => { projects={multiProject() ? projectList : undefined} activeProjectId={activeProjectId()} defaultBase={defaultBase} - onCreate={scheduleCreate} + onCreate={creation.schedule} /> )) } @@ -2393,7 +2384,7 @@ const AgentManagerContent: Component = () => { currentSessionID={session.currentSessionID} mode={mode} defaultBase={defaultBase} - onCreate={scheduleCreate} + onCreate={creation.schedule} bindings={kb()} t={t} onSearchRef={(ref) => (sidebarSearchMenu = ref)} diff --git a/packages/kilo-vscode/webview-ui/agent-manager/pending-create.ts b/packages/kilo-vscode/webview-ui/agent-manager/pending-create.ts new file mode 100644 index 0000000000..4918e8f3fe --- /dev/null +++ b/packages/kilo-vscode/webview-ui/agent-manager/pending-create.ts @@ -0,0 +1,34 @@ +import { createSignal } from "solid-js" + +/** + * Tracks a cross-project worktree creation so the target project is activated + * once its worktree is ready, and abandons the pending activation when the + * creation fails or completes through another flow. + */ +export function usePendingCreate( + active: () => string | undefined, + activate: (projectId: string, worktreeId: string) => void, +) { + const [pending, setPending] = createSignal<{ projectId: string }>() + + const schedule = (projectId: string) => { + if (projectId === active()) return + if (pending()) return + setPending({ projectId }) + } + + const abandon = (projectId?: string) => { + if (pending()?.projectId === projectId) setPending(undefined) + } + + const setup = (ev: { status: string; projectId?: string; worktreeId?: string }) => { + if (pending()?.projectId !== ev.projectId) return + if (ev.status === "ready" && ev.projectId && ev.worktreeId) { + setPending(undefined) + activate(ev.projectId, ev.worktreeId) + } + if (ev.status === "error") setPending(undefined) + } + + return { schedule, abandon, setup } +}