/** * Managed markdown blocks delimited by HTML comments — the mechanism behind the * `ai-coding/` canon bootloaders. A bootloader is hand-written tool-specific * content PLUS one generated block fenced by: * * * …generated… * * * Regenerating replaces only the fenced region, so a human's edits outside the * block survive and re-running with the same body is byte-identical (idempotent). * This is the markdown analogue of {@link upsertManagedBlock} in envfile.ts. */ export interface ManagedBlock { /** Stable marker id, e.g. "ai-canonical:shared". */ marker: string; /** Parenthetical note on the BEGIN line (e.g. the generated-from source path). */ note: string; /** The block body (markdown), without the surrounding markers. */ body: string; } export declare function beginLine(marker: string, note: string): string; export declare function endLine(marker: string): string; /** * Upsert a managed block into `existing` (`undefined` = the file does not exist * yet). Replaces the fenced region if present, appends it if the file exists * without the markers, or creates the file as `preamble` + block when `existing` * is undefined. Everything outside the markers is preserved verbatim, and the * file's existing EOL style (CRLF vs LF) is kept. Deterministic for a given body. */ export declare function mergeManagedBlock(existing: string | undefined, block: ManagedBlock, preamble: string): string; /** * Remove a managed block (and the blank lines hugging it) from `existing`, leaving * everything OUTSIDE the fence verbatim and preserving the file's EOL style. The * inverse of {@link mergeManagedBlock}: `aih prune` uses it to SUBTRACT aih's * canonical block from a co-owned bootloader when the CLI is dropped, so the * tool-specific preamble and any human edits survive. Returns `existing` unchanged * when the marker is absent (no-op), and `""` when the block was the file's entire * content. It never deletes the file — the caller writes the stripped remainder in * place (the bootloader has no reliable "pure-aih remainder" signal, so we keep it). */ export declare function stripManagedBlock(existing: string, marker: string): string; /** * Extract a managed block's body (trimmed) from `text`, or `undefined` if the * markers are absent. Used by the drift check: compare the on-disk body to the * freshly generated one and fail if they differ. */ export declare function extractManagedBlock(text: string, marker: string): string | undefined; /** * True when `text` OPENS a managed block for `marker` — the BEGIN line alone, with no * matching END required. It recognizes exactly the same BEGIN line * {@link extractManagedBlock} does; it just asks the weaker question. * * {@link extractManagedBlock} needs a well-formed pair because it returns a body. A * caller about to OVERWRITE a file needs the opposite bias: a bootloader whose END line * was truncated or hand-deleted is still owned by `marker`, and a file someone already * damaged is the one you least want silently replaced. Ownership checks guarding a * destructive write use this and fail closed; anything that reads the body keeps using * {@link extractManagedBlock}. */ export declare function hasManagedBlockStart(text: string, marker: string): boolean; /** The sub-marker that fences a human "project extension" inside a managed block. */ export declare const PROJECT_EXTENSION_MARKER = "project-extension"; /** * Carve the human "project extension" out of an on-disk managed-block body — the * core of `aih adopt`'s non-destructive reconcile. A brownfield bootloader (e.g. * eicp) folded project-specific guidance INTO the shared block; regenerating the * block from the canonical source would silently delete it. This isolates exactly * those human lines so the caller can re-home them to a project-owned file BEFORE * the block is regenerated clean. * * Two strategies (the decided "diff-inferred now + sub-marker going forward"): * 1. **Sub-marker (precise)** — if `onDisk` fences a region with * `…`, that * region's content IS the extension, verbatim and order-preserving. * 2. **Diff-inferred (legacy)** — otherwise, the extension is the set of on-disk * lines whose trimmed form is absent from `canonical`, kept in on-disk order * with their original text. Whitespace-only and pure-structure lines that also * appear in canonical are dropped, so a reordering alone yields no false extension. * * Returns the extension as a trimmed markdown string, or `""` when there is none * (i.e. the on-disk body is canonical — already adopted). */ export declare function splitManagedBody(onDisk: string, canonical: string): string;