export type GitLogFn = (text: string, level?: "stdout" | "stderr") => void; /** IMP-914 per-project agent git automation level. */ export type GitPolicy = "none" | "commit" | "push" | "pr"; /** * Resolve the effective git policy for a project (IMP-914). The cloud * setting wins whenever it is a valid value; older projects without one * fall back to the legacy local-config flags (`openPr: false` meant "no * PR, still push") and finally to the historical full behavior. */ export declare function resolveGitPolicy(projectAgentGitPolicy: string | null | undefined, legacy?: { openPr?: boolean; }): GitPolicy; export interface PrepareRunGitOptions { taskId: string; /** Agent display id used for the commit identity (worker convention: id slice). */ agentName: string; /** Project id - reads per-project git options from the bridge config. */ projectId?: string | null; /** Readable branch naming: / (falls back to tasks/). */ projectSlug?: string; taskTitle?: string; /** * Explicit branch name for the run (IMP-743). When set it wins over the * branchForTask() derivation so the hub-assigned name (worktree OR shared * folder) is used verbatim - this is what group chainMode relies on. */ branchName?: string; /** * chainMode base branch: when creating a FRESH branch, start it here so the * task builds on the previous group task's work. Existing task branches * (retries) win over this. Ignored when the branch already exists. */ baseBranch?: string | null; /** * Independent task (ungrouped / first-of-chain): when no chain * `baseBranch` is given, start the fresh branch from the repository's * default branch (freshly fetched) instead of whatever HEAD happens to be. * This keeps sibling tasks in a shared folder from inheriting each other's * commits - the shared-folder equivalent of a worktree cut from `main`. * Ignored when `baseBranch` is set or the branch already exists. */ independentBase?: boolean; /** * IMP-914 per-project git automation level. 'none' skips the whole * pipeline (no branch, no commit); other values branch normally and are * enforced again in finalizeRunGit. */ policy?: GitPolicy; /** * true = give the run its own branch (plain runs + build mode); * false = legacy behavior, work on whatever branch is checked out * (orchestrator workers - the backend owns those branches/worktrees). */ branch: boolean; /** * When true, allow commits directly on the default/main branch. * Default false — agents should never commit to main; only worktrees * should produce commits. Set by the --allow-main-commit flag on * standalone run commands. */ allowMainBranchCommit?: boolean; log: GitLogFn; } export interface PreparedGit { active: boolean; /** Why git was downgraded ('not-a-git-repo' / 'disabled') - empty when active. */ skipReason?: string; /** Branch the run works on (undefined = current branch / fresh repo). */ branch?: string; /** Short sha the branch was created from (when branching happened). */ createdFrom?: string; /** True when the working directory is on the default branch (main/master). */ onDefaultBranch?: boolean; /** Per-repo preparation results (populated when multiple repos are found). */ repos?: Array<{ gitRoot: string; active: boolean; branch?: string; createdFrom?: string; onDefaultBranch?: boolean; }>; } /** * Branch name for a task run: readable slug /. Falls * back to `tasks/` when project/title are unknown (legacy callers). */ export declare function branchForTask(taskId: string, ctx?: { projectSlug?: string; taskTitle?: string; }): string; /** * Walk up from `startDir` looking for a `.git` entry (file or directory). * Returns the directory containing `.git`, or null if none found within * `maxLevels` parent directories (default 5). This mirrors what `git * rev-parse` does internally but without shelling out. * * When no `.git` is found walking UP, we also check one level DOWN into * common child directories (frontend/, backend/, src/, packages/, apps/). * This handles monorepo layouts where the linked folder is the parent of * multiple independent git repos (e.g. `TeamShare/` containing * `TeamShare/frontend/.git`, `TeamShare/teamshare-backend/.git`). */ export declare function findGitRoot(startDir: string, maxLevels?: number): string | null; /** * Find ALL git repositories under `startDir`. Used by the multi-repo * finalize path to commit changes across independent repos (e.g., * `TeamShare/` containing `frontend/.git` and `teamshare-backend/.git`). * * If `startDir` itself is a git repo, returns `[startDir]` (single-repo * fast path). Otherwise recursively scans children up to `maxDepth` levels. */ export declare function findAllGitRoots(startDir: string, maxDepth?: number): string[]; /** * Detect whether the current HEAD is on the default branch (main/master). * Used to prevent agents from committing directly to main — only worktrees * should produce commits. Returns the current branch and the detected * default branch name for reporting. */ export declare function isOnDefaultBranch(gitRoot: string): { onDefault: boolean; currentBranch: string; defaultBranch: string; }; /** * Pre-run phase under an exclusive repo lock (IMP-950). Branch checkouts * are invisible-but-destructive to concurrent sessions in the same tree - * serialize them. Falls back to the unlocked path on lock timeout (with a * report note), never throws. */ export declare function prepareRunGitAsync(cwd: string, opts: PrepareRunGitOptions & { lockWaitMs?: number; }): Promise; /** * Legacy synchronous entry point - kept for callers outside session flows. * No arbitration; prefer {@link prepareRunGitAsync}. */ export declare function prepareRunGit(cwd: string, opts: PrepareRunGitOptions): PreparedGit; export interface FinalizeRunGitOptions { taskId: string; taskTitle: string; agentName: string; projectId?: string | null; /** Readable branch naming: /. Used by multi-repo finalize to create branches lazily. */ projectSlug?: string; /** Explicit hub-assigned branch name (IMP-743); wins over branchForTask(). */ branchName?: string; /** chainMode base branch: start a FRESH branch here (existing branch wins). */ baseBranch?: string | null; /** Independent task (ungrouped / first-of-chain): start the fresh branch from the repo's default branch (see PrepareRunGitOptions). */ independentBase?: boolean; /** Open a pull request after a successful push (needs a token). */ openPr: boolean; /** IMP-914 per-project git automation level (undefined = legacy flags). */ policy?: GitPolicy; /** * When true, allow commits directly on the default/main branch. * Default false — agents should never commit to main. */ allowMainBranchCommit?: boolean; /** * true (default) = create the task branch in finalize for repos with * changes (run/build/orchestrator shared folder); false = legacy, work on * whatever branch is checked out (callers that own branch layout, e.g. a * backend-managed worktree). Controls lazy branch creation. */ createBranch?: boolean; log: GitLogFn; } export interface FinalizedGit { /** One-liner for run reports ("" when nothing happened). */ gitLine: string; /** Human-readable report lines appended to the closing comment. */ lines: string[]; branch?: string; sha?: string; pushed?: boolean; prUrl?: string; prNumber?: number; changedFiles?: number; } /** * Post-run phase under an exclusive repo lock (IMP-950): commit/push while * no other session can switch branches or rebuild the tree underneath. * A busy workspace degrades the report - it never throws and never loses * the run's success. */ export declare function finalizeRunGit(cwd: string, prepared: PreparedGit, opts: FinalizeRunGitOptions & { lockWaitMs?: number; }): Promise; /** * Safety-net commit: if the working tree has uncommitted changes after a * harness failure (or any early exit), commit them to the task branch and * push so the next run continues from a clean tree instead of stashing and * losing the agent's progress. * * Never throws. Returns a FinalizedGit with report lines. */ export declare function commitRemainingChanges(cwd: string, prepared: PreparedGit, opts: FinalizeRunGitOptions): Promise;