/** A Herdr-linked checkout owned by one Fresh Subagent attempt. */ export interface ManagedSubagentWorktree { workspaceId: string; path: string; branch: string; /** Commit used as the worktree branch base. */ baseCommit: string; } export interface GitWorktreeSnapshot { head: string; dirty: boolean; } export interface CreatedHerdrWorktree { surface: string; workspaceId: string; path: string; } /** Internal adapter for Git inspection and Herdr worktree lifecycle commands. */ export interface SubagentWorktreeTransport { inspect(cwd: string): GitWorktreeSnapshot; create(input: { cwd: string; branch: string; baseCommit: string; label: string; env?: Record; }): CreatedHerdrWorktree; remove(workspaceId: string): void; close(surface: string): void; } export interface WorktreeStartupRequest { name: string; operationId: string; cwd: string; env?: Record; } export interface CreatedSubagentWorktree { surface: string; worktree: ManagedSubagentWorktree; } export interface SubagentSurfaceCleanup { status: "removed" | "retained"; warning?: string; } function errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } function isUncPath(value: string): boolean { return value.startsWith("\\\\") || /^\/\/[^/]+\/[^/]+/.test(value); } /** Generate a unique, Git-safe branch name for one Fresh attempt. */ export function buildSubagentWorktreeBranch(name: string, operationId: string): string { const slug = name .normalize("NFKD") .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-|-$/g, "") .slice(0, 48) || "task"; const identity = operationId .toLowerCase() .replace(/[^a-z0-9-]/g, "-") .replace(/-+/g, "-") .replace(/^-|-$/g, ""); if (!identity) throw new Error("A subagent worktree requires a valid operation identity."); return `pi-subagent/${slug}-${identity}`; } /** Create a Herdr worktree only from a clean Git checkout and its current HEAD. */ export function createSubagentWorktree( request: WorktreeStartupRequest, transport: SubagentWorktreeTransport, ): CreatedSubagentWorktree { if (isUncPath(request.cwd)) { throw new Error("UNC paths are not supported for Herdr subagent worktrees."); } const source = transport.inspect(request.cwd); if (source.dirty) { throw new Error( "The source repository must be clean before creating a subagent worktree; commit or remove staged, unstaged, and untracked changes first.", ); } if (!source.head) { throw new Error("A subagent worktree requires a Git repository with a HEAD commit."); } const branch = buildSubagentWorktreeBranch(request.name, request.operationId); const created = transport.create({ cwd: request.cwd, branch, baseCommit: source.head, label: request.name, ...(request.env !== undefined ? { env: request.env } : {}), }); if (!created.surface || !created.workspaceId || !created.path) { throw new Error("Herdr worktree create returned incomplete workspace metadata."); } if (isUncPath(created.path)) { try { transport.remove(created.workspaceId); } catch { // Do not replace the unsupported-path error with best-effort cleanup. } throw new Error("Herdr created a worktree at an unsupported UNC path."); } return { surface: created.surface, worktree: { workspaceId: created.workspaceId, path: created.path, branch, baseCommit: source.head, }, }; } /** * Reclaim a completed Subagent surface without discarding work. Empty worktrees * are removed by Herdr; worktrees with commits or working-tree changes remain * available for review and are detached from their pane. */ export function reclaimSubagentSurface( surface: string, worktree: ManagedSubagentWorktree | undefined, transport: Pick, ): SubagentSurfaceCleanup | undefined { if (!worktree) { transport.close(surface); return undefined; } let snapshot: GitWorktreeSnapshot; try { snapshot = transport.inspect(worktree.path); } catch (error) { return closeAndRetain(surface, transport, errorMessage(error)); } if (snapshot.dirty || snapshot.head !== worktree.baseCommit) { return closeAndRetain(surface, transport); } try { transport.remove(worktree.workspaceId); return { status: "removed" }; } catch (error) { return closeAndRetain(surface, transport, errorMessage(error)); } } function closeAndRetain( surface: string, transport: Pick, warning?: string, ): SubagentSurfaceCleanup { try { transport.close(surface); } catch (error) { const closeError = errorMessage(error); warning = warning ? `${warning}; pane close failed: ${closeError}` : `Pane close failed: ${closeError}`; } return { status: "retained", ...(warning ? { warning } : {}) }; }