framework
Version:
The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.
77 lines • 3.9 kB
TypeScript
import { type RemoveResult, type WorktreeRow } from './worktrees.js';
/** One worktree this sweep removed. */
export interface RemovedWorktree {
/** The agent id, which is also the worktree's directory name. */
agentId: string;
}
/** A worktree the sweep tried to reclaim and could not, and why. */
export interface FailedRemoval {
agentId: string;
error: string;
}
/** What {@link removeMergedWorktrees} did. */
export interface MergedSweepResult {
removed: RemovedWorktree[];
/** Worktrees it tried and could not reclaim — most often a branch it could not push. */
failed: FailedRemoval[];
}
/** Injectable seams so the sweep is unit-testable off disk. */
export interface MergedSweepDeps {
/** The worktrees on disk (default {@link listProjectWorktrees}). */
worktrees?: (cwd: string) => Promise<WorktreeRow[]>;
/** Removes one worktree (default {@link removeProjectWorktree}). */
remove?: (cwd: string, agentId: string) => Promise<RemoveResult>;
/** Whether the repo has a remote at all (default {@link repoHasRemote}). */
hasRemote?: (cwd: string) => Promise<boolean>;
/** Agent ids whose checkouts the daemon is still responsible for; see {@link MergedSweepOptions.busy}. */
busy?: ReadonlySet<string>;
}
/**
* Reclaim every retained worktree in `cwd` whose work can reach the remote (E5).
*
* The decision is entirely {@link removeProjectWorktree}'s — commit what is pending, push the
* branch, remove only once the remote has it — so the automatic path and the manual one (the
* dashboard's Remove button) are one behaviour rather than two that can disagree. This adds the
* loop, the one thing it must never touch (a live agent's checkout, where its agent is working), and
* the agent lock.
*
* The lock is load-bearing: an agent's meta flips to `done` a beat before its teardown finishes
* archiving, so a sweep landing in that window would remove the checkout out from under the
* archive — which then recreates the directory it was reading from, and the removal silently
* un-happens. Every other actor on a checkout already takes this lock.
*/
export declare function removeMergedWorktrees(cwd: string, deps?: MergedSweepDeps): Promise<MergedSweepResult>;
/** A running sweep, in the shape the daemon's other background services use. */
export interface MergedWorktreeSweep {
/** Run one sweep now, awaiting it. Exposed for tests and for a caller that wants it on demand. */
tick: () => Promise<void>;
stop: () => void;
}
/** What {@link startMergedWorktreeSweep} needs from the daemon. */
export interface MergedSweepOptions {
/** The registered projects to sweep. */
projects: () => Promise<readonly {
path: string;
}[]>;
log: (message: string) => void;
/**
* The agents the daemon is still responsible for — spawning, running, or mid-retirement — whose
* checkouts this must not touch.
*
* "Not live" on disk is not "the daemon is finished with it": an agent's meta flips to `done` a
* beat before its teardown archives the history and reclaims the checkout, and a sweep landing
* in that window races the teardown for the same directory. Absent means nothing is busy, which
* is right for a caller that spawns no agents.
*/
busy?: () => ReadonlySet<string>;
/** The per-project sweep (default {@link removeMergedWorktrees}). */
sweep?: (cwd: string) => Promise<MergedSweepResult>;
}
/**
* Sweep every registered project's reclaimable worktrees (#1036), one turn per call.
*
* Says what it removed rather than removing it silently: a checkout vanishing from under someone
* with no line explaining why reads as a bug, even when the work behind it is safe.
*/
export declare function startMergedWorktreeSweep(opts: MergedSweepOptions): MergedWorktreeSweep;
//# sourceMappingURL=merged-worktrees.d.ts.map