/** * Structured worktree enumeration with status classification (T9546). * * Exposes {@link listWorktrees} — the SDK primitive behind the * `cleo worktree list` CLI command and the `worktree.list` dispatch operation. * * For each worktree returned by `git worktree list --porcelain`, the function * resolves: * - `taskId` (regex match against the branch name `task/T####`). * - `owningTaskStatus` (read from the project cleo.db tasks SSoT via `getDb`). * - `lastActivity` (newest commit on the branch, falling back to dir mtime). * - `isLocked` (porcelain `locked` line). * - `isMerged` (`git merge-base --is-ancestor main`, exit-0 ⇒ merged). * - `isStale` (no commits in >N days AND (task done/cancelled OR merged)). * - `owningAgent` (best-effort from `.git/worktree.json` if present). * - `statusCategory` — one of `locked|orphan|merged|stale|active`, resolved * by precedence in {@link classifyStatus}. * - `source` — origin of the worktree: `cleo-spawn`, `claude-agent`, `manual`, * or `adopted`. Set by consulting the sentinel index at * `/.cleo/worktrees.json` (T9804). * * Multi-source listing (T9804): After enumerating git-native worktrees the * function reads the sentinel index and appends any entries whose `path` is * NOT already present in the porcelain output. This ensures that worktrees * created by Claude Code Agent `isolation:worktree` (and subsequently adopted * via `cleo worktree adopt`) appear in `cleo worktree list` alongside the * canonical CLEO-spawned worktrees. * * All git invocations are bounded by an explicit 60-second supervisor * timeout, matching the {@link runGitWithLockRetry} discipline used elsewhere * in `packages/core/src/release/engine-ops.ts`. We intentionally do NOT use * `runGitWithLockRetry` directly here — these read-only queries never touch * `.git/index.lock`, so the retry/backoff dance would be pure overhead. * * @task T9546 * @task T9804 — multi-source listing (sentinel index union) * @adr ADR-068 — DB chokepoint (openCleoDb) * @adr ADR-062 — git merge-no-ff integration semantics */ import { type EngineResult, type ListWorktreesOpts, type ListWorktreesResult, type WorktreeStatusCategory } from '@cleocode/contracts'; /** * One row of porcelain output from `git worktree list --porcelain`. * * @internal */ interface PorcelainEntry { path: string; branch: string; locked: boolean; } /** * List all worktrees attached to the given project root, classify each one * by activity / merge / lock / orphan state, and return a structured envelope. * * @param opts - Listing options including project root, filter, and stale-days threshold. * @returns EngineResult containing a {@link ListWorktreesResult} on success. * * @example * ```ts * const result = await listWorktrees({ projectRoot: process.cwd() }); * if (result.success) { * for (const wt of result.data.worktrees) { * console.log(wt.path, wt.statusCategory); * } * } * ``` */ export declare function listWorktrees(opts?: ListWorktreesOpts): Promise>; /** * Parse `git worktree list --porcelain` into structured rows. * * The porcelain grammar is documented at * https://git-scm.com/docs/git-worktree#_porcelain_format — each record is * separated by a blank line and may contain `worktree `, `HEAD `, * `branch refs/heads/` and `locked []` lines. * * @param projectRoot - Absolute path to the project root. * @returns Parsed porcelain rows, one per worktree. * @internal Exported for tests only. */ export declare function enumerateWorktrees(projectRoot: string): PorcelainEntry[]; /** * Extract a task ID from a branch name following the `task/T####` convention. * * @param branch - Git branch name. * @returns The task ID string, or null if the branch does not match the convention. * @internal Exported for tests only. */ export declare function taskIdFromBranch(branch: string): string | null; /** * Get the timestamp (ms since epoch) of the newest commit on the given branch. * * Uses `git log -1 --format=%cI` and returns null if the branch resolution or * the log invocation fails — callers fall back to the worktree directory mtime. * * @param branch - Branch name. * @param projectRoot - Project root for the git invocation. * @returns ms since epoch, or null on failure. * @internal Exported for tests only. */ export declare function getLastCommitTimestampMs(branch: string, projectRoot: string): number | null; /** * Test whether the given branch is reachable from the canonical upstream branch. * * Implementation: `git merge-base --is-ancestor
` exits 0 if * is an ancestor of
(i.e. has been merged or fast-forwarded * into it). Any other exit code is interpreted as "not merged". * * @param branch - Branch name to test. * @param projectRoot - Project root. * @param mainBranch - Upstream branch to test reachability against. * @returns true iff branch is reachable from main. * @internal Exported for tests only. */ export declare function branchIsMergedToMain(branch: string, projectRoot: string, mainBranch?: string): boolean; /** * Apply orphan/merged/stale/locked precedence to produce the single * mutually-exclusive `statusCategory` field. See the {@link WorktreeStatusCategory} * docstring for the resolution order. * * Primary-worktree guard: the canonical project checkout is always classified * as `active`, regardless of merge state. The primary worktree's branch is * trivially an ancestor of itself (`main` is reachable from `main`), so the * naive ancestry check would label it `merged` and make it a prune candidate. * * @param flags - The set of pre-computed boolean classifiers. * @returns The resolved status category. * @internal Exported for tests only. */ export declare function classifyStatus(flags: { isPrimary?: boolean; isLocked: boolean; isOrphan: boolean; isMerged: boolean; isStale: boolean; }): WorktreeStatusCategory; /** * Load the `status` column for each of the given task IDs through the * ADR-068 DB chokepoint. * * Returns a map (taskId → status) for IDs that resolved; missing IDs are * absent from the map (callers treat absent as `null` for downstream * orphan detection). * * @param taskIds - Task IDs to look up. * @param projectRoot - Project root for DB resolution. * @returns Map of taskId → status. * @internal Exported for tests only. */ export declare function loadOwningTaskStatuses(taskIds: string[], projectRoot: string): Promise>; /** * Best-effort read of the owning agent identifier from a per-worktree metadata * file (`/.git/worktree.json`). Returns null if the file is absent or * malformed. * * @param worktreePath - Absolute path to the worktree directory. * @returns The owningAgent string, or null. * @internal Exported for tests only. */ export declare function readOwningAgent(worktreePath: string): string | null; /** * Resolve the absolute path to the repository's `.git` common directory. * * For the primary worktree this is `/.git`. For linked worktrees * this is also the primary's `.git` (the common dir is shared across all * worktrees — each linked worktree only owns its admin subdirectory under * `.git/worktrees//`). * * Returns null on failure — callers degrade gracefully to dir-mtime for the * `createdAt` field. * * @param projectRoot - Project root for the git invocation. * @returns Absolute path to the `.git` common directory, or null. * @internal Exported for tests only. */ export declare function resolveGitCommonDir(projectRoot: string): string | null; /** * Resolve the `createdAt` ISO timestamp for a worktree entry (T9546 AC4). * * Resolution order: * 1. Per-worktree admin file `/worktrees//HEAD`. * Git writes this exactly once at `git worktree add` time; its mtime is the * most reliable proxy for worktree creation time. * 2. For the primary worktree (which has no per-worktree admin dir), the * mtime of the `.git` common dir itself — set when the repo was created * or cloned. * 3. Sentinel index `adoptedAt`, when the entry was registered via * `cleo worktree adopt`. * 4. Fallback — `mtime` of the worktree directory. * * @internal Exported for tests only. */ export declare function resolveCreatedAt(input: { worktreePath: string; gitCommonDir: string | null; primaryWorktreePath: string | null; sentinelAdoptedAt: string | undefined; }): string; /** * Resolve the canonical primary worktree path — i.e. the directory containing * the repository's `.git` directory (not a `.git` file pointing into * `.git/worktrees/`). * * Implementation: `git rev-parse --path-format=absolute --git-common-dir` * returns the absolute path to `.git` regardless of which worktree the * command is invoked from. The primary worktree is the parent of that path. * * Falls back to null on failure — callers treat missing detection as "do * nothing special" (no primary guard applied). This is safe: every worktree * still gets classified, just the same way the old code did. * * @param projectRoot - Project root for the git invocation. * @returns Absolute path to the primary worktree, or null on failure. * @internal Exported for tests only. */ export declare function resolvePrimaryWorktreePath(projectRoot: string): string | null; /** * Test whether the given worktree path is the canonical primary worktree. * * Compares both raw and `realpath`-resolved variants to handle the common * case where the project root is symlinked or contains symlinked ancestors. * * @param worktreePath - Path from `git worktree list --porcelain`. * @param primaryPath - Output of {@link resolvePrimaryWorktreePath}, or null. * @returns true iff the two paths resolve to the same canonical location. * @internal Exported for tests only. */ export declare function isPrimaryWorktree(worktreePath: string, primaryPath: string | null): boolean; export {}; //# sourceMappingURL=list.d.ts.map