import { TreeKind } from './tree-recovery'; /** * The "this branch is already merged, start fresh" text, shared by the TWO guards that detect the * state from the same cached signal (`MainSyncStatus.branchAlreadyMerged`): * * - feature-branch-guard blocks Write/Edit → {@link MergedBranchMessage.forEdits} * - read-stale-guard blocks Read → {@link MergedBranchMessage.forReads} * * One source of truth on purpose: the recovery steps are instructions the AI follows LITERALLY, so * two drifting copies would mean two different behaviors for the same repo state. Only the * "what is still allowed" tail differs, because the two guards block different tools. * * The steps themselves come from {@link TreeRecovery}, which renders them in the flavour of the tree * we are actually standing in — a merged LINKED WORKTREE is told to open a new worktree and remove * this dead one, a merged branch in the primary clone is told to `git checkout -b … origin/main`. * * ONE VOICE on `git checkout main`: only the WORKTREE flavour says never to run it (there it fatals — * main is checked out in the primary clone). In the primary clone it is a perfectly good move and the * allowance list below says so explicitly. The two used to disagree inside a single message — the * header forbade it while the allowance list permitted "git checkout ", and `main` is an * other-branch — and an agent that resolved the contradiction in favour of the prohibition concluded * its only exit was creating a branch, which the branch cap then refused. */ export declare class MergedBranchMessage { private readonly treeRoot; private readonly recovery; /** * `treeRoot` is the tree the guard judged — pass it and every prescribed command comes out as * `cd && …`. That form is the only one that is correct across tool calls: the harness * RESETS a cwd that left the workspace, so an agent in a linked worktree is back in the primary * clone by the time it runs the cure, and a bare `git checkout -b` would branch the WRONG tree. */ constructor(treeRoot?: string); /** * The ONE allowance list, shared by every guard that blocks while this state is up. * * Each guard used to print its own view of the world: this one's narrow bash allowlist, and * read-stale-guard's "EVERY Bash command". Both statements were true of their own guard and false * of the session — on a merged branch BOTH fire, so the agent was told simultaneously that all * Bash runs and that most Bash is blocked. One list, printed by both. */ private allowances; private common; forEdits(branch: string, mergedPr: string, kind?: TreeKind, worktreePath?: string): string; /** * The Bash variant. merged-branch-bash-guard DEFAULT-DENIES Bash on a merged branch, so the message * has to spell out the narrow allowlist — otherwise an agent reads "blocked" and believes it is * wedged. The cure commands it lists are exactly the ones the allowlist lets through (including the * `| tail`/`; echo` shaping an agent reflexively appends), so following this message can never hit * the guard again. */ forBash(branch: string, mergedPr: string, kind?: TreeKind, worktreePath?: string): string; /** * The Read variant. Says WHY a read (not an edit) is blocked — reading this branch feeds the AI a * pre-merge snapshot of the codebase and every plan built on it is built on code main has already * moved past — and spells out the escape valves so the agent never believes it is stuck. */ forReads(branch: string, mergedPr: string, kind?: TreeKind, worktreePath?: string): string; }