UNPKG

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.

168 lines 9.62 kB
import { type GitRunner } from '../project.js'; /** * Git-worktree lifecycle for concurrent agents (#453/#735): give each agent its own * checkout so N runs on one repo never fight over the working tree. Pure plumbing * over the existing {@link GitRunner} seam; no daemon wiring, no concurrency, no * dashboard changes (those are the sibling #453 slices). This module only knows * how to add, list, remove, and prune worktrees. */ /** The path an agent's worktree gets (#1580): `<repo>/.the-framework/branches/<run branch name>`. */ export declare function worktreePath(repo: string, agentId: string): string; export { AGENT_BRANCH_PREFIX, LEGACY_AGENT_BRANCH_PREFIX, agentBranchName, legacyAgentBranchName, } from '../branch-names.js'; /** One entry parsed from `git worktree list --porcelain`. */ export interface WorktreeInfo { /** Absolute worktree path (the main checkout included). */ path: string; /** The checked-out commit. */ head: string; /** The checked-out branch (short name), or absent when detached. */ branch?: string; } /** Inputs to {@link addWorktree}. The caller owns branch naming (#736). */ export interface AddWorktreeOptions { agentId: string; /** The branch to create for the agent. */ branch: string; /** Base ref to branch from; defaults to the repo's current HEAD. */ base?: string; } /** The worktree {@link addWorktree} created. */ export interface AddedWorktree { path: string; branch: string; } /** * Create a worktree for an agent on a fresh branch: `git worktree add -b <branch> * <path> [base]`. Git makes the leaf dir (and any missing parents) itself. The * `agentId` is validated as path-safe first so a caller can never traverse out of * `.the-framework/branches/`. Rejects on any git failure (a caller that wants a * run needs its checkout, so failure must surface, not be swallowed). */ export declare function addWorktree(repo: string, opts: AddWorktreeOptions, agent?: GitRunner): Promise<AddedWorktree>; /** * Check an *existing* branch out into an agent's worktree (#762): `git worktree add <path> <branch>`, * no `-b`. Continuing an agent puts it back on the branch its work is already on, rather than * branching again from HEAD and stranding what it did last time. * * Rejects on git failure, like {@link addWorktree}: a continued agent needs its checkout. */ export declare function attachWorktree(repo: string, opts: { agentId: string; branch: string; }, agent?: GitRunner): Promise<AddedWorktree>; /** * Every worktree registered for the repo (the main checkout included). Forgiving: * a non-repo / git failure yields `[]` so a reconcile scan never throws. */ export declare function listWorktrees(repo: string, agent?: GitRunner): Promise<WorktreeInfo[]>; /** * Parse `git worktree list --porcelain`: blank-line-separated records, each with * a `worktree <path>` line, a `HEAD <sha>` line, and either `branch refs/heads/...` * or `detached`. Extra attributes (bare/locked/prunable) are ignored. Exported so * the parsing is unit-testable without a real repo. */ export declare function parseWorktreeList(porcelain: string): WorktreeInfo[]; /** * Commit whatever the agent left behind, on the agent's own branch (#786). * * An agent that edits and stops without committing is behaving as instructed: the * system prompt has it commit *pre-existing* changes before it starts, never its own * work at the end. Removing that checkout would destroy the diff (the work was never * staged, so it is not recoverable from git afterwards), so teardown commits it first * and the branch outlives the worktree. * * Returns whether the checkout is safe to remove: true when it was already clean or * the work is now committed, false when the commit failed (no git identity, a hook * refusing it). False means keep the checkout, which is the safe direction. * * Retries before giving up (#1376): the daemon's conversation committer works in the same * checkout and is busiest exactly when this runs (session end), so a first attempt can lose * an `index.lock` race. That transient loss is how a session's real work got judged * "committed nothing" by the handoff while the teardown's identical commit, seconds later, * succeeded. A short wait outlasts the committer's hold; a persistent failure (identity, * hooks) still comes back false. */ export declare function commitPendingWork(path: string, agent?: GitRunner, retry?: { attempts?: number; delayMs?: number; }): Promise<boolean>; /** * Remove an agent's worktree. Tolerant of an already-gone / never-registered path so * teardown stays idempotent (the agent child is detached; the daemon only holds its pid). * * Plain removal first: it refuses a checkout git considers unclean, which after * {@link commitPendingWork} means a state we did not anticipate. Falling back to * `--force` keeps teardown working (an ignored build artifact must not strand a * worktree forever), but it says so, because forcing past unknown state is exactly * how uncommitted work got deleted in the first place. */ export declare function removeWorktree(repo: string, path: string, agent?: GitRunner): Promise<void>; /** * The branch checked out at `path`, or `undefined` when detached / not a repo. * Forgiving, like {@link listWorktrees}: callers use it to decide, not to fail. */ export declare function currentBranch(path: string, agent?: GitRunner): Promise<string | undefined>; /** * Rename an agent's branch once the agent names the session (#736): the worktree is * created on `tf-agent-<agentId>` before a name exists, and this puts the * readable `tf-<sessionName>` on it. * * Only renames when `path` is still on `from`. The #326 system prompt currently * tells the agent to create and check out its own `tf-<name>` branch, * and until that step is dropped there (the prompt ships verbatim from the issue, * so it is not ours to edit) the agent may already have moved off `from` — in * which case it named the branch itself and there is nothing to rename. Returns * whether it renamed, and never throws: an agent must not die over a branch name. */ export declare function renameAgentBranch(path: string, from: string, to: string, agent?: GitRunner): Promise<boolean>; /** * `git worktree prune`: drop administrative entries for worktree dirs a crash left * behind. Never removes a live worktree, so it is always safe. Forgiving. */ export declare function pruneWorktrees(repo: string, agent?: GitRunner): Promise<void>; /** Runs `du`, resolving its stdout. Injectable so the size read can be tested without a real tree. */ export type SizeRunner = (path: string) => Promise<string>; /** A {@link SizeRunner} over `du -sk`: one process, and it does not follow the symlinked deps (#736). */ export declare function nodeSizeRunner(): SizeRunner; /** * A worktree's size on disk in bytes, or undefined when it cannot be read (#798). Best-effort by * design: this only ever labels a "remove this" button, so a missing number costs nothing while a * throw or a hang would cost the panel it sits in. `du` is absent on Windows, which reads as * unknown like any other failure. */ export declare function worktreeSize(path: string, agent?: SizeRunner): Promise<number | undefined>; /** * Whether a branch is on the remote, with the local tip already there (E5). * * The one predicate the whole retention story is built on: nothing local is ever the last copy of * work, so anything the remote has may be deleted and anything it does not have stays. It replaced * three interacting rules — a clean finish removes the checkout, a failure keeps it, a merged * branch reclaims it later — each of which asked *what state did this session end in* rather than * *is this recoverable*. * * `git rev-parse` of the remote-tracking ref, then a merge-base check: the ref existing is not * enough, because a branch pushed and then committed to again has a tip the remote has never seen. * Reads only local refs (no fetch), so it is cheap enough to ask on every teardown — the remote ref * is written by the push this is checking for, which is what makes that sound. * * Anything unreadable answers `false`. A repo with no remote configured therefore keeps every * checkout, which is the honest outcome: there is nowhere for the work to be recoverable from. */ export declare function branchPushed(repo: string, branch: string, agent?: GitRunner): Promise<boolean>; /** * Whether the checkout has nothing uncommitted — the read half of {@link commitPendingWork}, for * a decision that must not commit on the way to its answer: removing a publish-nothing session's * checkout requires a clean tree, and grabbing someone's half-typed edits as a commit to find * that out would be the intrusion the question exists to avoid. Throws when git cannot answer, * so the caller keeps the checkout rather than guessing. */ export declare function worktreeClean(path: string, agent?: GitRunner): Promise<boolean>; /** * Whether the repo has any remote configured at all. What the sweep asks once per project: with * no remote, {@link branchPushed} is false for every checkout and the push cannot land, so the * whole per-checkout probe-and-push cycle is doomed before it starts — and that answer cannot * change between two rows of the same sweep. Anything unreadable answers `false`, like * {@link branchPushed}: keeping a checkout is the safe direction. */ export declare function repoHasRemote(repo: string, agent?: GitRunner): Promise<boolean>; //# sourceMappingURL=worktree.d.ts.map