refactor(agent-manager): extract worktree importer (#8725)

* refactor(agent-manager): extract worktree importer

* test(agent-manager): keep provider line cap
This commit is contained in:
Marius
2026-04-10 13:37:00 +02:00
committed by GitHub
parent 358263c5a1
commit 05474353fd
3 changed files with 382 additions and 356 deletions
@@ -12,7 +12,7 @@ import { GitStatsPoller, type WorktreePresenceResult } from "./GitStatsPoller"
import { PRStatusBridge } from "./pr-status-bridge"
import { GitOps } from "./GitOps"
import { versionedName } from "./branch-name"
import { normalizePath, classifyWorktreeError } from "./git-import"
import { classifyWorktreeError } from "./git-import"
import { SetupScriptService } from "./SetupScriptService"
import { SetupScriptRunner } from "./SetupScriptRunner"
import { copyEnvFiles } from "./env-copy"
@@ -22,6 +22,7 @@ import { executeVscodeTask } from "./task-runner"
import { forkSession } from "./fork-session"
import { continueInWorktree } from "./continue-in-worktree"
import { WorktreeDiffController } from "./worktree-diff-controller"
import { WorktreeImporter } from "./worktree-importer"
import { buildKeybindingMap } from "./format-keybinding"
import { resolveVersionModels, buildInitialMessages, type CreatedVersion } from "./multi-version"
@@ -46,9 +47,9 @@ export class AgentManagerProvider implements Disposable {
private worktrees: WorktreeManager | undefined
private state: WorktreeStateManager | undefined
private setupScript: SetupScriptService | undefined
private importer: WorktreeImporter
private terminalManager: SessionTerminalManager
private stateReady: Promise<void> | undefined
private importing = false
private statsPoller: GitStatsPoller
private prBridge!: PRStatusBridge
private gitOps: GitOps
@@ -70,6 +71,17 @@ export class AgentManagerProvider implements Disposable {
(msg) => this.outputChannel.appendLine(`[SessionTerminal] ${msg}`),
createTerminalHost(),
)
this.importer = new WorktreeImporter({
manager: () => this.getWorktreeManager(),
state: () => this.getStateManager(),
post: (msg) => this.postToWebview(msg),
push: () => this.pushState(),
setup: (dir, branch, id) => this.runSetupScriptForWorktree(dir, branch, id),
session: (dir, branch, id) => this.createSessionInWorktree(dir, branch, id),
register: (sid, dir) => this.registerWorktreeSession(sid, dir),
ready: (sid, result, id) => this.notifyWorktreeReady(sid, result, id),
log: (...args) => this.log(...args),
})
const semaphore = new Semaphore(3)
this.gitOps = new GitOps({ log: (...args) => this.log(...args), semaphore })
this.diffs = new WorktreeDiffController({
@@ -285,10 +297,12 @@ export class AgentManagerProvider implements Disposable {
const persist = m.type === "agentManager.persistSession"
void this.stateReady?.then(() => {
const state = this.getStateManager()
if (state)
persist
? !state.getSession(m.sessionId) && state.addSession(m.sessionId, null)
: state.removeSession(m.sessionId)
if (!state) return
if (persist) {
if (!state.getSession(m.sessionId)) state.addSession(m.sessionId, null)
return
}
state.removeSession(m.sessionId)
})
return null
}
@@ -384,11 +398,6 @@ export class AgentManagerProvider implements Disposable {
this.onRequestState()
return null
}
if (m.type === "agentManager.requestBranches") {
void this.onRequestBranches()
return null
}
if (m.type === "agentManager.setTabOrder") {
this.state?.setTabOrder(m.key, m.order)
return null
@@ -414,24 +423,28 @@ export class AgentManagerProvider implements Disposable {
}
private onImportMessage(m: AgentManagerInMessage): Record<string, unknown> | null | undefined {
if (m.type === "agentManager.requestBranches") {
void this.importer.branches()
return null
}
if (m.type === "agentManager.requestExternalWorktrees") {
void this.onRequestExternalWorktrees()
void this.importer.external()
return null
}
if (m.type === "agentManager.importFromBranch") {
void this.onImportFromBranch(m.branch)
void this.importer.branch(m.branch)
return null
}
if (m.type === "agentManager.importFromPR") {
void this.onImportFromPR(m.url)
void this.importer.pr(m.url)
return null
}
if (m.type === "agentManager.importExternalWorktree") {
void this.onImportExternalWorktree(m.path, m.branch)
void this.importer.path(m.path, m.branch)
return null
}
if (m.type === "agentManager.importAllExternalWorktrees") {
void this.onImportAllExternalWorktrees()
void this.importer.all()
return null
}
}
@@ -1064,344 +1077,6 @@ export class AgentManagerProvider implements Disposable {
return null
}
// ---------------------------------------------------------------------------
// Import
// ---------------------------------------------------------------------------
private async onRequestBranches(): Promise<void> {
const manager = this.getWorktreeManager()
if (!manager) {
this.postToWebview({ type: "agentManager.branches", branches: [], defaultBranch: "main" })
return
}
try {
const result = await manager.listBranches()
const checkedOut = await manager.checkedOutBranches()
// Include isCheckedOut flag on each branch — let the webview decide how to filter
const branches = result.branches.map((b) => ({
...b,
isCheckedOut: checkedOut.has(b.name),
}))
// Validate configured default branch still exists
const state = this.getStateManager()
const configured = state?.getDefaultBaseBranch()
if (configured && !branches.some((b) => b.name === configured)) {
this.clearStaleDefaultBaseBranch(state!, configured)
}
this.postToWebview({
type: "agentManager.branches",
branches,
defaultBranch: result.defaultBranch,
})
} catch (error) {
this.log(`Failed to list branches: ${error}`)
this.postToWebview({ type: "agentManager.branches", branches: [], defaultBranch: "main" })
}
}
private async onRequestExternalWorktrees(): Promise<void> {
const manager = this.getWorktreeManager()
const state = this.getStateManager()
if (!manager || !state) {
this.postToWebview({ type: "agentManager.externalWorktrees", worktrees: [] })
return
}
try {
const managedPaths = new Set(state.getWorktrees().map((wt) => wt.path))
const worktrees = await manager.listExternalWorktrees(managedPaths)
this.postToWebview({ type: "agentManager.externalWorktrees", worktrees })
} catch (error) {
this.log(`Failed to list external worktrees: ${error}`)
this.postToWebview({ type: "agentManager.externalWorktrees", worktrees: [] })
}
}
private async onImportFromBranch(branch: string): Promise<void> {
const manager = this.getWorktreeManager()
const state = this.getStateManager()
if (!manager || !state) {
this.postToWebview({ type: "agentManager.importResult", success: false, message: "Not a git repository" })
return
}
if (this.importing) {
this.postToWebview({
type: "agentManager.importResult",
success: false,
message: "Another import is already in progress",
})
return
}
this.importing = true
try {
this.postToWebview({
type: "agentManager.worktreeSetup",
status: "creating",
message: "Creating worktree from branch...",
})
const result = await manager.createWorktree({ existingBranch: branch })
const worktree = state.addWorktree({
branch: result.branch,
path: result.path,
parentBranch: result.parentBranch,
remote: result.remote,
})
this.pushState()
try {
this.postToWebview({
type: "agentManager.worktreeSetup",
status: "creating",
message: "Running setup script...",
branch: result.branch,
worktreeId: worktree.id,
})
await this.runSetupScriptForWorktree(result.path, result.branch, worktree.id)
const session = await this.createSessionInWorktree(result.path, result.branch, worktree.id)
if (!session) throw new Error("Failed to create session")
state.addSession(session.id, worktree.id)
this.registerWorktreeSession(session.id, result.path)
this.notifyWorktreeReady(session.id, result, worktree.id)
this.postToWebview({ type: "agentManager.importResult", success: true, message: `Opened branch ${branch}` })
this.log(`Imported branch ${branch} as worktree ${worktree.id}`)
} catch (inner) {
state.removeWorktree(worktree.id)
await manager.removeWorktree(result.path)
this.pushState()
throw inner
}
} catch (error) {
const raw = error instanceof Error ? error.message : String(error)
const msg =
raw.includes("already used by worktree") || raw.includes("already checked out")
? `Branch "${branch}" is already checked out in another worktree`
: raw
const code = classifyWorktreeError(msg)
this.postToWebview({ type: "agentManager.worktreeSetup", status: "error", message: msg, errorCode: code })
this.postToWebview({ type: "agentManager.importResult", success: false, message: msg, errorCode: code })
} finally {
this.importing = false
}
}
private async onImportFromPR(url: string): Promise<void> {
const manager = this.getWorktreeManager()
const state = this.getStateManager()
if (!manager || !state) {
this.postToWebview({ type: "agentManager.importResult", success: false, message: "Not a git repository" })
return
}
if (this.importing) {
this.postToWebview({
type: "agentManager.importResult",
success: false,
message: "Another import is already in progress",
})
return
}
this.importing = true
try {
this.postToWebview({ type: "agentManager.worktreeSetup", status: "creating", message: "Resolving PR..." })
const result = await manager.createFromPR(url)
const worktree = state.addWorktree({
branch: result.branch,
path: result.path,
parentBranch: result.parentBranch,
remote: result.remote,
})
this.pushState()
try {
this.postToWebview({
type: "agentManager.worktreeSetup",
status: "creating",
message: "Setting up worktree...",
branch: result.branch,
worktreeId: worktree.id,
})
await this.runSetupScriptForWorktree(result.path, result.branch, worktree.id)
const session = await this.createSessionInWorktree(result.path, result.branch, worktree.id)
if (!session) throw new Error("Failed to create session")
state.addSession(session.id, worktree.id)
this.registerWorktreeSession(session.id, result.path)
this.notifyWorktreeReady(session.id, result, worktree.id)
this.postToWebview({
type: "agentManager.importResult",
success: true,
message: `Opened PR branch ${result.branch}`,
})
this.log(`Imported PR ${url} as worktree ${worktree.id}`)
} catch (inner) {
state.removeWorktree(worktree.id)
await manager.removeWorktree(result.path)
this.pushState()
throw inner
}
} catch (error) {
const raw = error instanceof Error ? error.message : String(error)
const msg =
raw.includes("already used by worktree") || raw.includes("already checked out")
? "This PR's branch is already checked out in another worktree"
: raw
const code = classifyWorktreeError(msg)
this.postToWebview({ type: "agentManager.worktreeSetup", status: "error", message: msg, errorCode: code })
this.postToWebview({ type: "agentManager.importResult", success: false, message: msg, errorCode: code })
} finally {
this.importing = false
}
}
private async onImportExternalWorktree(wtPath: string, branch: string): Promise<void> {
const state = this.getStateManager()
const manager = this.getWorktreeManager()
if (!state || !manager) {
this.postToWebview({ type: "agentManager.importResult", success: false, message: "State not initialized" })
return
}
if (this.importing) {
this.postToWebview({
type: "agentManager.importResult",
success: false,
message: "Another import is already in progress",
})
return
}
this.importing = true
let worktree: ReturnType<typeof state.addWorktree> | undefined
try {
const externals = await manager.listExternalWorktrees(new Set(state.getWorktrees().map((wt) => wt.path)))
if (!externals.some((e) => normalizePath(e.path) === normalizePath(wtPath))) {
this.postToWebview({
type: "agentManager.importResult",
success: false,
message: "Path is not a valid worktree for this repository",
})
return
}
const base = await manager.resolveBaseBranch()
worktree = state.addWorktree({ branch, path: wtPath, parentBranch: base.branch, remote: base.remote })
this.pushState()
const session = await this.createSessionInWorktree(wtPath, branch, worktree.id)
if (!session) {
state.removeWorktree(worktree.id)
this.pushState()
this.postToWebview({ type: "agentManager.importResult", success: false, message: "Failed to create session" })
return
}
state.addSession(session.id, worktree.id)
this.registerWorktreeSession(session.id, wtPath)
this.pushState()
this.postToWebview({
type: "agentManager.worktreeSetup",
status: "ready",
message: "Worktree imported",
sessionId: session.id,
branch,
worktreeId: worktree.id,
})
this.postToWebview({
type: "agentManager.sessionMeta",
sessionId: session.id,
mode: "worktree",
branch,
path: wtPath,
parentBranch: base.branch,
})
this.postToWebview({ type: "agentManager.importResult", success: true, message: `Imported ${branch}` })
this.log(`Imported external worktree ${wtPath} (${branch})`)
} catch (error) {
if (worktree) {
state.removeWorktree(worktree.id)
this.pushState()
}
const msg = error instanceof Error ? error.message : String(error)
this.postToWebview({ type: "agentManager.importResult", success: false, message: msg })
} finally {
this.importing = false
}
}
private async onImportAllExternalWorktrees(): Promise<void> {
if (this.importing) {
this.postToWebview({
type: "agentManager.importResult",
success: false,
message: "Another import is already in progress",
})
return
}
const manager = this.getWorktreeManager()
const state = this.getStateManager()
if (!manager || !state) {
this.postToWebview({ type: "agentManager.importResult", success: false, message: "Not a git repository" })
return
}
this.importing = true
try {
const managedPaths = new Set(state.getWorktrees().map((wt) => wt.path))
const externals = await manager.listExternalWorktrees(managedPaths)
if (externals.length === 0) {
this.postToWebview({
type: "agentManager.importResult",
success: true,
message: "No external worktrees to import",
})
return
}
let imported = 0
const base = await manager.resolveBaseBranch()
for (const ext of externals) {
try {
const worktree = state.addWorktree({
branch: ext.branch,
path: ext.path,
parentBranch: base.branch,
remote: base.remote,
})
const session = await this.createSessionInWorktree(ext.path, ext.branch, worktree.id)
if (session) {
state.addSession(session.id, worktree.id)
this.registerWorktreeSession(session.id, ext.path)
imported++
} else {
state.removeWorktree(worktree.id)
}
} catch (error) {
this.log(`Failed to import external worktree ${ext.path}: ${error}`)
}
}
this.pushState()
this.postToWebview({
type: "agentManager.importResult",
success: true,
message: `Imported ${imported} worktree${imported !== 1 ? "s" : ""}`,
})
this.log(`Imported ${imported}/${externals.length} external worktrees`)
} catch (error) {
const msg = error instanceof Error ? error.message : String(error)
this.postToWebview({ type: "agentManager.importResult", success: false, message: msg })
} finally {
this.importing = false
}
}
// ---------------------------------------------------------------------------
// Keybindings
// ---------------------------------------------------------------------------
@@ -0,0 +1,336 @@
import type { Session } from "@kilocode/sdk/v2/client"
import type { AgentManagerOutMessage } from "./types"
import type { WorktreeManager, CreateWorktreeResult } from "./WorktreeManager"
import type { WorktreeStateManager } from "./WorktreeStateManager"
import { classifyWorktreeError, normalizePath } from "./git-import"
type Worktree = ReturnType<WorktreeStateManager["addWorktree"]>
export interface WorktreeImporterHost {
manager(): WorktreeManager | undefined
state(): WorktreeStateManager | undefined
post(msg: AgentManagerOutMessage): void
push(): void
setup(path: string, branch?: string, worktreeId?: string): Promise<void>
session(path: string, branch: string, worktreeId?: string): Promise<Session | null>
register(sessionId: string, directory: string): void
ready(sessionId: string, result: CreateWorktreeResult, worktreeId?: string): void
log(...args: unknown[]): void
}
export class WorktreeImporter {
private importing = false
constructor(private readonly host: WorktreeImporterHost) {}
async branches(): Promise<void> {
const manager = this.host.manager()
if (!manager) {
this.host.post({ type: "agentManager.branches", branches: [], defaultBranch: "main" })
return
}
try {
const result = await manager.listBranches()
const checked = await manager.checkedOutBranches()
const branches = result.branches.map((branch) => ({
...branch,
isCheckedOut: checked.has(branch.name),
}))
const state = this.host.state()
const configured = state?.getDefaultBaseBranch()
if (state && configured && !branches.some((branch) => branch.name === configured)) {
this.host.log(`Default base branch "${configured}" no longer exists, clearing`)
state.setDefaultBaseBranch(undefined)
this.host.push()
}
this.host.post({
type: "agentManager.branches",
branches,
defaultBranch: result.defaultBranch,
})
} catch (error) {
this.host.log(`Failed to list branches: ${error}`)
this.host.post({ type: "agentManager.branches", branches: [], defaultBranch: "main" })
}
}
async external(): Promise<void> {
const manager = this.host.manager()
const state = this.host.state()
if (!manager || !state) {
this.host.post({ type: "agentManager.externalWorktrees", worktrees: [] })
return
}
try {
const paths = new Set(state.getWorktrees().map((worktree) => worktree.path))
const worktrees = await manager.listExternalWorktrees(paths)
this.host.post({ type: "agentManager.externalWorktrees", worktrees })
} catch (error) {
this.host.log(`Failed to list external worktrees: ${error}`)
this.host.post({ type: "agentManager.externalWorktrees", worktrees: [] })
}
}
async branch(branch: string): Promise<void> {
const manager = this.host.manager()
const state = this.host.state()
if (!manager || !state) {
this.host.post({ type: "agentManager.importResult", success: false, message: "Not a git repository" })
return
}
if (this.busy()) return
this.importing = true
try {
this.host.post({
type: "agentManager.worktreeSetup",
status: "creating",
message: "Creating worktree from branch...",
})
const result = await manager.createWorktree({ existingBranch: branch })
const worktree = state.addWorktree({
branch: result.branch,
path: result.path,
parentBranch: result.parentBranch,
remote: result.remote,
})
this.host.push()
try {
this.host.post({
type: "agentManager.worktreeSetup",
status: "creating",
message: "Running setup script...",
branch: result.branch,
worktreeId: worktree.id,
})
await this.host.setup(result.path, result.branch, worktree.id)
const session = await this.host.session(result.path, result.branch, worktree.id)
if (!session) throw new Error("Failed to create session")
state.addSession(session.id, worktree.id)
this.host.register(session.id, result.path)
this.host.ready(session.id, result, worktree.id)
this.host.post({ type: "agentManager.importResult", success: true, message: `Opened branch ${branch}` })
this.host.log(`Imported branch ${branch} as worktree ${worktree.id}`)
} catch (error) {
state.removeWorktree(worktree.id)
await manager.removeWorktree(result.path)
this.host.push()
throw error
}
} catch (error) {
this.importError(error, `Branch "${branch}" is already checked out in another worktree`)
} finally {
this.importing = false
}
}
async pr(url: string): Promise<void> {
const manager = this.host.manager()
const state = this.host.state()
if (!manager || !state) {
this.host.post({ type: "agentManager.importResult", success: false, message: "Not a git repository" })
return
}
if (this.busy()) return
this.importing = true
try {
this.host.post({ type: "agentManager.worktreeSetup", status: "creating", message: "Resolving PR..." })
const result = await manager.createFromPR(url)
const worktree = state.addWorktree({
branch: result.branch,
path: result.path,
parentBranch: result.parentBranch,
remote: result.remote,
})
this.host.push()
try {
this.host.post({
type: "agentManager.worktreeSetup",
status: "creating",
message: "Setting up worktree...",
branch: result.branch,
worktreeId: worktree.id,
})
await this.host.setup(result.path, result.branch, worktree.id)
const session = await this.host.session(result.path, result.branch, worktree.id)
if (!session) throw new Error("Failed to create session")
state.addSession(session.id, worktree.id)
this.host.register(session.id, result.path)
this.host.ready(session.id, result, worktree.id)
this.host.post({
type: "agentManager.importResult",
success: true,
message: `Opened PR branch ${result.branch}`,
})
this.host.log(`Imported PR ${url} as worktree ${worktree.id}`)
} catch (error) {
state.removeWorktree(worktree.id)
await manager.removeWorktree(result.path)
this.host.push()
throw error
}
} catch (error) {
this.importError(error, "This PR's branch is already checked out in another worktree")
} finally {
this.importing = false
}
}
async path(path: string, branch: string): Promise<void> {
const state = this.host.state()
const manager = this.host.manager()
if (!state || !manager) {
this.host.post({ type: "agentManager.importResult", success: false, message: "State not initialized" })
return
}
if (this.busy()) return
this.importing = true
let worktree: Worktree | undefined
try {
const paths = new Set(state.getWorktrees().map((worktree) => worktree.path))
const externals = await manager.listExternalWorktrees(paths)
if (!externals.some((worktree) => normalizePath(worktree.path) === normalizePath(path))) {
this.host.post({
type: "agentManager.importResult",
success: false,
message: "Path is not a valid worktree for this repository",
})
return
}
const base = await manager.resolveBaseBranch()
worktree = state.addWorktree({ branch, path, parentBranch: base.branch, remote: base.remote })
this.host.push()
const session = await this.host.session(path, branch, worktree.id)
if (!session) {
state.removeWorktree(worktree.id)
this.host.push()
this.host.post({ type: "agentManager.importResult", success: false, message: "Failed to create session" })
return
}
state.addSession(session.id, worktree.id)
this.host.register(session.id, path)
this.host.push()
this.host.post({
type: "agentManager.worktreeSetup",
status: "ready",
message: "Worktree imported",
sessionId: session.id,
branch,
worktreeId: worktree.id,
})
this.host.post({
type: "agentManager.sessionMeta",
sessionId: session.id,
mode: "worktree",
branch,
path,
parentBranch: base.branch,
})
this.host.post({ type: "agentManager.importResult", success: true, message: `Imported ${branch}` })
this.host.log(`Imported external worktree ${path} (${branch})`)
} catch (error) {
if (worktree) {
state.removeWorktree(worktree.id)
this.host.push()
}
const message = error instanceof Error ? error.message : String(error)
this.host.post({ type: "agentManager.importResult", success: false, message })
} finally {
this.importing = false
}
}
async all(): Promise<void> {
if (this.busy()) return
const manager = this.host.manager()
const state = this.host.state()
if (!manager || !state) {
this.host.post({ type: "agentManager.importResult", success: false, message: "Not a git repository" })
return
}
this.importing = true
try {
const paths = new Set(state.getWorktrees().map((worktree) => worktree.path))
const externals = await manager.listExternalWorktrees(paths)
if (externals.length === 0) {
this.host.post({
type: "agentManager.importResult",
success: true,
message: "No external worktrees to import",
})
return
}
const imported: string[] = []
const base = await manager.resolveBaseBranch()
for (const external of externals) {
try {
const worktree = state.addWorktree({
branch: external.branch,
path: external.path,
parentBranch: base.branch,
remote: base.remote,
})
const session = await this.host.session(external.path, external.branch, worktree.id)
if (session) {
state.addSession(session.id, worktree.id)
this.host.register(session.id, external.path)
imported.push(worktree.id)
continue
}
state.removeWorktree(worktree.id)
} catch (error) {
this.host.log(`Failed to import external worktree ${external.path}: ${error}`)
}
}
this.host.push()
this.host.post({
type: "agentManager.importResult",
success: true,
message: `Imported ${imported.length} worktree${imported.length !== 1 ? "s" : ""}`,
})
this.host.log(`Imported ${imported.length}/${externals.length} external worktrees`)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
this.host.post({ type: "agentManager.importResult", success: false, message })
} finally {
this.importing = false
}
}
private busy(): boolean {
if (!this.importing) return false
this.host.post({
type: "agentManager.importResult",
success: false,
message: "Another import is already in progress",
})
return true
}
private importError(error: unknown, duplicate: string): void {
const raw = error instanceof Error ? error.message : String(error)
const message = raw.includes("already used by worktree") || raw.includes("already checked out") ? duplicate : raw
const code = classifyWorktreeError(message)
this.host.post({ type: "agentManager.worktreeSetup", status: "error", message, errorCode: code })
this.host.post({ type: "agentManager.importResult", success: false, message, errorCode: code })
}
}
@@ -37,6 +37,7 @@ const TSX_FILES = [
const TSX_FILE = TSX_FILES[0]!
const PROVIDER_FILE = path.join(ROOT, "src/agent-manager/AgentManagerProvider.ts")
const DIFF_CONTROLLER_FILE = path.join(ROOT, "src/agent-manager/worktree-diff-controller.ts")
const IMPORTER_FILE = path.join(ROOT, "src/agent-manager/worktree-importer.ts")
const SETUP_SCRIPT_RUNNER_FILE = path.join(ROOT, "src/agent-manager/SetupScriptRunner.ts")
function readAllCss(): string {
@@ -177,6 +178,10 @@ describe("Agent Manager Provider — onMessage routing", () => {
return fs.readFileSync(DIFF_CONTROLLER_FILE, "utf-8")
}
function importer(): string {
return fs.readFileSync(IMPORTER_FILE, "utf-8")
}
// -- onMessage dispatches all expected message types -----------------------
it("provider routing handles all documented agentManager.* message types", () => {
@@ -220,6 +225,7 @@ describe("Agent Manager Provider — onMessage routing", () => {
const text = body("onMessage")
expect(text).toContain("onWorktreeMessage")
expect(text).toContain("onSessionMessage")
expect(text).toContain("onImportMessage")
expect(text).toContain("onDiffMessage")
expect(text).not.toContain("agentManager.requestState")
})
@@ -314,7 +320,6 @@ describe("Agent Manager Provider — onMessage routing", () => {
* loading skeletons forever.
*/
it("requestState handler calls pushEmptyState when this.state is falsy", () => {
// onStateMessage delegates to onRequestState; verify the actual handler
const text = body("onRequestState")
expect(text, "must call pushEmptyState when state is absent").toContain("pushEmptyState")
expect(text, "must guard on this.state being falsy").toMatch(/!this\.state/)
@@ -335,6 +340,16 @@ describe("Agent Manager Provider — onMessage routing", () => {
expect(text).toContain("shouldStopDiffPolling")
expect(providerText).toContain("this.diffs")
})
it("worktree import behavior lives in the cohesive importer", () => {
const text = importer()
const providerText = body("onImportMessage")
expect(text).toContain("class WorktreeImporter")
expect(text).toContain("createFromPR")
expect(text).toContain("listExternalWorktrees")
expect(text).toContain("createWorktree")
expect(providerText).toContain("this.importer")
})
})
// ---------------------------------------------------------------------------
@@ -559,7 +574,7 @@ const VSCODE_ALLOWED: Record<string, { note: string }> = {
const MAX_LINES: Record<string, { maxLines: number; note: string }> = {
"AgentManagerProvider.ts": {
maxLines: 2000,
note: "worktree diff orchestration lives in WorktreeDiffController; lower this after the next cohesive extraction",
note: "diff and import workflows are extracted into cohesive domain services; extract more orchestration next",
},
}