/** * Worktree janitor — low-level helpers. * * A command runner that never throws, a parser for `git worktree list * --porcelain`, path helpers, and the process-cwd probe used to tell whether * anything is still working inside a worktree. Kept apart from the service so * each piece can be tested on its own. * * @module services/worktree/worktree-janitor.git */ /** Result of one external command. `code` is null when it could not start or timed out. */ export interface CommandResult { /** Exit code, or null when the process did not exit normally */ code: number | null; /** Captured stdout */ stdout: string; /** Captured stderr (or the spawn error message) */ stderr: string; } /** Options for {@link runCommand}. */ export interface RunCommandOptions { /** Working directory */ cwd?: string; /** Timeout in ms (default: GIT_TIMEOUT_MS) */ timeoutMs?: number; /** Extra environment variables merged over process.env */ env?: Record; /** Text written to the command's stdin (then closed) */ input?: string; } /** * Run a command without a shell and never throw. * * Git and gh are told never to prompt (no credential dialogs, no pager), so a * periodic background job cannot hang on input, and git takes no optional * locks (a status never rewrites the index). * * @param file - Executable (e.g. `git`, `gh`, or an absolute path) * @param args - Arguments * @param options - cwd / timeout / env * @returns Exit code and output; `code` is null on spawn failure or timeout * * @example * ```typescript * const r = await runCommand('git', ['-C', repo, 'status', '--porcelain']); * if (r.code === 0 && r.stdout === '') console.log('clean'); * ``` */ export declare function runCommand(file: string, args: string[], options?: RunCommandOptions): Promise; /** One entry of `git worktree list --porcelain`. */ export interface PorcelainWorktree { /** Absolute worktree path as git reports it */ path: string; /** HEAD commit, or null (bare) */ head: string | null; /** Short branch name (`refs/heads/` stripped), or null when detached */ branch: string | null; /** True for the bare entry */ bare: boolean; /** True when HEAD is detached */ detached: boolean; /** True when the worktree is locked (`git worktree lock`) */ locked: boolean; /** Lock reason, when given */ lockReason?: string; /** True when git reports the directory as gone (`prunable`) */ prunable: boolean; /** True for the first entry — the main worktree */ isMain: boolean; } /** * Parse `git worktree list --porcelain` output. * * Entries are separated by blank lines; the first entry is the main worktree. * * @param output - Raw porcelain output * @returns Parsed entries in git's order * * @example * ```typescript * parsePorcelainWorktrees('worktree /r\nHEAD abc\nbranch refs/heads/main\n'); * // [{ path: '/r', head: 'abc', branch: 'main', isMain: true, ... }] * ``` */ export declare function parsePorcelainWorktrees(output: string): PorcelainWorktree[]; /** * Real path of `p`, or its resolved form when it does not exist. * * @param p - Path * @returns Canonical path for comparisons */ export declare function canonicalPath(p: string): string; /** * Whether `child` is `parent` or inside it (both canonicalised first). * * @param child - Candidate path * @param parent - Directory * @returns True when child === parent or child is under parent * * @example * ```typescript * isPathInside('/r/.claude/worktrees/a/src', '/r/.claude/worktrees/a'); // true * isPathInside('/r/.claude/worktrees/ab', '/r/.claude/worktrees/a'); // false * ``` */ export declare function isPathInside(child: string, parent: string): boolean; /** * Latest modification time (ms) of a worktree: the directory itself plus its * private git dir's `index`, `HEAD` and `logs/HEAD` — every commit, checkout, * stage or reset touches one of those. Uncommitted edits are caught by the * dirty check instead. * * @param worktreePath - Worktree root * @param gitDir - Its private git dir (`.git/worktrees/`), when known * @returns Newest mtime in ms, or null when nothing could be stat'ed */ export declare function latestWorktreeMtimeMs(worktreePath: string, gitDir: string | null): number | null; /** * Current working directories of every process this user can see. * * macOS: `lsof -a -d cwd -u -Fn`. Linux: `/proc//cwd`. Returns * null when the probe cannot run, so the caller can treat "unknown" as busy. * * @returns Absolute cwd paths (deduplicated), or null when unavailable */ export declare function listProcessCwds(): Promise; /** * Whether the kill switch disables the janitor. * * @param env - Environment (defaults to process.env) * @returns True when `CREWLY_WORKTREE_JANITOR` is `0`/`off`/`false`/`no` */ export declare function isJanitorDisabled(env?: NodeJS.ProcessEnv): boolean; /** * Yield to the event loop every YIELD_EVERY_ENTRIES calls, so a long * filesystem walk never holds up Slack, relay polls or PTY I/O. */ export declare class EventLoopYielder { private count; /** * Count one unit of work; every YIELD_EVERY_ENTRIES units, wait one * `setImmediate` turn. * * @returns Resolves when the caller may continue */ tick(): Promise; } /** * Git dir a `.git` file points to (`gitdir: `), resolved against `dir`. * * @param dir - Directory holding the `.git` file * @returns Absolute git dir, or null when there is no readable `.git` file */ export declare function readGitFilePointer(dir: string): Promise; /** * Main worktree of the repo a linked worktree belongs to, read from the * worktree's `.git` file (`gitdir:
/.git/worktrees/`). Returns * null for anything else: a `.git` directory (a normal clone), a submodule * (`gitdir: …/.git/modules/…`), or an unreadable file. * * @param dir - Directory that may be a linked worktree * @returns Absolute path of the main worktree, or null * * @example * ```typescript * // /tmp/visa-cm-wt/.git contains "gitdir: /src/ce-core/.git/worktrees/visa-cm-wt" * await mainRepoOfLinkedWorktree('/tmp/visa-cm-wt'); // '/src/ce-core' * ``` */ export declare function mainRepoOfLinkedWorktree(dir: string): Promise; /** A git checkout found on disk. */ export interface FoundRepo { /** Directory holding `.git` */ path: string; /** `dir` for a `.git` directory (clone / main worktree), `file` for a `.git` file (linked worktree or submodule) */ kind: 'dir' | 'file'; } /** * Find git checkouts under `root` without following symlinks. A directory * with a `.git` directory or file is a checkout and is not descended into; * `node_modules` and `.git` are never descended into. Fully async; yields to * the event loop every few directories. * * @param root - Directory to search (itself included, at depth 0) * @param maxDepth - Deepest level searched (root = 0) * @param maxDirs - Stop after visiting this many directories * @returns Checkouts found, in walk order */ export declare function findGitCheckouts(root: string, maxDepth: number, maxDirs?: number): Promise; /** * Main worktrees of every repo that owns a linked worktree somewhere under * the given roots (e.g. `/tmp/claude-501/visa-cm-wt` → `~/src/ce-core`), so * repos that are not registered projects still get their temp worktrees * cleaned. Visits at most DISCOVERY_MAX_DIRS directories per root. * * @param roots - Temp roots to scan (missing roots are skipped) * @returns De-duplicated main-worktree paths */ export declare function discoverLinkedWorktreeRepos(roots: readonly string[]): Promise; /** * Newest mtime (ms) of `dir` and everything within `depth` levels below it, * using lstat (symlinks are not followed). Async; yields every few entries. * * @param dir - Directory * @param depth - Levels below `dir` to include (0 = only `dir`) * @returns Newest mtime in ms, or null when `dir` cannot be stat'ed */ export declare function latestTreeMtimeMs(dir: string, depth: number): Promise; /** * Disk usage of a path in bytes (`du -sk` in a child process, so the event * loop is never blocked; `du` does not follow symlinks). * * @param p - File or directory * @returns Bytes, or null when `du` failed or timed out */ export declare function diskUsageBytes(p: string): Promise; /** * Human-readable size (`1.8 GB`, `512 MB`). * * @param bytes - Size in bytes * @returns Short string with one decimal for GB */ export declare function formatBytes(bytes: number): string; //# sourceMappingURL=worktree-janitor.git.d.ts.map