import { type GitResult, type GitSyncOptions } from './git.js'; import { type NodeMeta, type ManagedWorktree } from './canvas/index.js'; export interface WorktreeCommandResult { ok: boolean; stdout: string; stderr: string; } export declare class WorktreeError extends Error { code: string; next: string; detail?: string; constructor(code: string, message: string, next: string, detail?: string); } export declare function managedWorktreesRoot(): string; export declare function managedWorktreePath(nodeId: string): string; export declare function hasOpenManagedWorktree(node: NodeMeta | null | undefined): boolean; export declare function openManagedWorktreeForNode(nodeId: string): ManagedWorktree | null; export declare function rollbackManagedWorktree(wt: ManagedWorktree): void; export declare function createManagedWorktree(cwd: string, nodeId: string, baseRef?: string): ManagedWorktree; export declare function isRebaseInProgress(cwd: string): boolean; export interface CloseManagedWorktreeResult { node_id: string; branch: string; worktree_path: string; landed_sha: string; /** False when the landing happened but the worktree checkout could not be * removed automatically afterward — the close still counts as done (state is * closed, push-final is unblocked); `worktree_remove_error` carries the * manual cleanup note. */ worktree_removed: boolean; worktree_remove_error?: string; /** False when the landing happened but the local branch could not be deleted * afterward — same non-fatal treatment as worktree_removed. */ branch_deleted: boolean; branch_delete_error?: string; } export declare function closeManagedWorktree(nodeId: string): CloseManagedWorktreeResult; type WorktreeCleanupRunner = (args: string[], cwd: string, options: GitSyncOptions) => GitResult; /** Test seam for the bounded post-land cleanup regression. */ export declare function setWorktreeCleanupRunnerForTest(runner?: WorktreeCleanupRunner): void; interface CleanEmptyProof { /** The branch's HEAD SHA at the moment this was proven clean-and-contained * — the exact value the destructive delete must be conditioned on. */ branchSha: string; /** The base branch tip the proof was made against; re-resolved and * re-checked immediately before the destructive delete, since the proof * and the delete are two different points in time. */ baseSha: string; } /** Fix B's shared "nothing to land" test — the SAME clean-worktree definition * `close`'s own dirty check now naturally satisfies once `.git/info/exclude` * hides crtr's own `.crouter/config.json`/`state.json` bookkeeping (Fix A), * plus one more requirement close doesn't need: the branch tip must already * be a git-ancestor of (or equal to) the CURRENT tip of its recorded base * branch — i.e. landing it would be a pure no-op. Any ambiguity (missing * path, mid-rebase, wrong branch, an unresolvable base, or a real ancestry * divergence) returns null: this function only ever proves "safe to drop" * when a live git check establishes it, never on a heuristic guess, so real * unlanded work always falls through to the existing explicit-close guard. * This proof is NOT itself the safety boundary — it only decides whether * auto-drop is even attempted. `autoDropCleanManagedWorktreeLocked` below * re-proves containment against the LIVE base immediately before the * destructive delete, refuses outright if the checkout cannot be removed, * and conditions the delete itself on BOTH `branchSha` and that same live * base SHA via `bestEffortDeleteBranchWithBaseProof`'s atomic multi-ref * transaction — those checks, not this one, are what close the TOCTOU gap. * Exported (alongside * `autoDropCleanManagedWorktreeLocked`) purely so regression tests can * inject a race in the window between proving and cleanup; production code * must always go through `autoDropCleanManagedWorktree`. */ export declare function proveCleanEmptyManagedWorktree(wt: ManagedWorktree): CleanEmptyProof | null; export interface AutoDroppedWorktree { node_id: string; branch: string; worktree_path: string; worktree_removed: boolean; worktree_remove_error?: string; branch_deleted: boolean; branch_delete_error?: string; } /** Exported (see `proveCleanEmptyManagedWorktree`'s doc) so regression tests * can call it directly with a proof captured before injecting a race. * Returns null — persisting NO state change — when the immediate base * re-verification, the checkout removal, or the atomic base+branch delete * refuses: the caller (`autoDropCleanManagedWorktree`, and `push final` * above it) then falls through to the existing `open_managed_worktree` * guard exactly as if this worktree had never looked droppable. In every * refusal case the branch, and every commit reachable from it, is * unconditionally preserved — that is the actual safety property this * function guarantees. A checkout-removal refusal in particular must be * treated as a full refusal, not a partial success: if the checkout cannot * be removed (e.g. a file reappeared after the clean predicate ran), the * branch is still checked out somewhere, so this function must never reach * the ref-deletion transaction or mutate managed-worktree state — both the * branch and the live checkout stay fully recoverable and the caller's * `open_managed_worktree` guard fires. */ export declare function autoDropCleanManagedWorktreeLocked(nodeId: string, wt: ManagedWorktree, proof: CleanEmptyProof): AutoDroppedWorktree | null; /** `push final`'s escape hatch (#327, #333): a managed worktree that has * nothing to land is auto-dropped instead of blocking on an explicit * `node worktree close` — one the caller may have been told NOT to run * because a manager already landed the same change another way (#327), or * that never had anything to close in the first place (#333, a read-only * review node). Returns null and changes nothing when the worktree is not * PROVABLY empty-and-clean by `proveCleanEmptyManagedWorktree`, OR when a * concurrent mutation invalidates that proof before the destructive delete * (see `autoDropCleanManagedWorktreeLocked`); the caller's existing * `open_managed_worktree` guard then still applies unchanged — real * commits, real uncommitted changes, and a lost race all require the * explicit path. */ export declare function autoDropCleanManagedWorktree(nodeId: string): AutoDroppedWorktree | null; export {};