export type WriteStatus = "new" | "unchanged" | "overwrite" | "merged" | "conflict" | "refused" | "skipped" /** FR-038 §8 — deleted because it was generated by a previous run, is no longer * generated, and was never edited by hand. Reported as a file outcome rather * than a warning because a deletion is exactly as consequential as a write, * and a run summary that lists writes but hides deletions is how a silent * deletion happens. */ | "removed"; /** * "overwrite" — default; three-way merge if .gen-state exists, else write-if- * different / first-time-existing flow. * "skip-existing" — never write over an existing file; status "skipped". * Useful for `meta gen --dry-run` style flows. */ export type MergeStrategy = "overwrite" | "skip-existing"; /** "default" — the standard three-way merge flow described in the file header. * "fresh" — opt-in via `meta gen --baseline=fresh`. When .gen-state is absent * but the file exists, OVERWRITE with fresh content and seed .gen-state from * the fresh content (caveat 3 escape hatch). */ export type BaselineMode = "default" | "fresh"; export interface DecideAndWriteOpts { strategy?: MergeStrategy; /** Absolute path to the .gen-state/ root for this project. When undefined, * we fall back to a process-isolated tmpdir — fine for tests but the CLI * always supplies the real project value via runGen(). */ genStateDir?: string; /** Path the snapshot is keyed by — usually the path relative to the * project root, but ANY stable identifier works. When undefined, derived * from `path` (so unit tests can call without supplying it). */ outputRelPath?: string; /** First-time-existing-file behavior. */ baseline?: BaselineMode; } export interface WriteResult { path: string; status: WriteStatus; /** Present when status is "conflict" or "refused" — human-readable reason, * naming what the user must do. A refusal nobody can act on gets the file * deleted by hand, which is the outcome refusing exists to prevent. */ conflictHint?: string; } /** * Every snapshot key recorded under `genStateDir` — in a runner-driven project, * the project-relative path of every file some previous `meta gen` wrote. * * Empty for a gen-state directory that does not exist yet, which is what makes a * first run, an ephemeral test run and `verify --codegen`'s throwaway root all * reconcile nothing. */ export declare function listGeneratedPaths(genStateDir: string): string[]; /** * sha-256 of `content`, hex — the same function that produces `.hashes.json`. * * Exported so a caller can ask the one question the manifest exists to answer * ("is this file byte-for-byte what we recorded writing?") without needing the * snapshot body, which is what makes the answer available on a fresh clone. */ export declare function contentHash(content: string): string; /** * The hash we recorded when we last wrote `relPath`, or undefined if we have no * record of ever writing it. * * This is the COMMITTED half of `.gen-state`. The snapshot bodies stay ignored — * they are a second full copy of all generated output — but a hash per path is * small enough to commit and review, and it is sufficient to distinguish "nobody * touched this" from "somebody edited this", which is the only distinction the * overwrite and delete decisions actually need. */ export declare function readGeneratedHash(genStateDir: string, relPath: string): string | undefined; /** * True when the file at `relPath` is byte-for-byte what we recorded writing. * * FAILS CLOSED: with no recorded hash we cannot prove anything, so the answer is * false. Both the write path and the orphan-delete path ask this one question of * this one piece of evidence — before this existed they answered the same * uncertainty in opposite directions inside a single feature, refusing to DELETE * a hand-edited file while silently OVERWRITING one. */ export declare function isPristineGenerated(genStateDir: string, relPath: string, current: string): boolean; /** * The snapshot of what we last wrote to `relPath`, or undefined when there is no * trustworthy one. * * HASH-CHECKED on purpose. A caller comparing this against the file on disk is * asking "is the output still exactly what I wrote?" — and a snapshot that fails * its own hash cannot answer that. Returning the stale text would let a * reconciling caller conclude "untouched" and delete a file it cannot vouch for, * so a tampered snapshot reads as absent and the caller fails closed. */ export declare function readGeneratedSnapshot(genStateDir: string, relPath: string): string | undefined; /** * Drop both halves of the record for paths this run no longer generates — each * snapshot file and its `.hashes.json` entry. * * BOTH halves, or the next run sees the path again in `listGeneratedPaths` and * re-decides an orphan that has already been dealt with. A no-op for a path that was * never generated. * * Batched deliberately: one manifest read and one write for the whole set. A per-path * variant re-reads, re-sorts and rewrites the entire manifest every call, so clearing k * orphans rewrote it k times — on a project with hundreds of generated files that makes * bookkeeping the dominant cost of `meta gen`, for an identical result. */ export declare function forgetGeneratedPaths(genStateDir: string, relPaths: Iterable): void; /** * The codegen engine version that last wrote this `.gen-state`, or undefined if it * was never stamped (a pre-#232 snapshot, or a fresh project). Informational only — * a separate reserved file from `.hashes.json`, it never participates in the * three-way merge decision. (#232) */ export declare function loadEngineVersion(genStateDir: string): string | undefined; /** Record the codegen engine version alongside the gen-state hashes (#232). */ export declare function saveEngineVersion(genStateDir: string, version: string): void; /** Thrown when git is unavailable. Surfaces as a clear CLI error rather than * a generic ENOENT halfway through a regen. */ export declare class GitMissingError extends Error { constructor(); } /** True when this project has a hash manifest at all — as opposed to a manifest * that simply has no entry for some path. * * The distinction drives the upgrade message: a project with NO manifest predates * the manifest being committed, so its refusals are one fixable configuration * problem rather than N independent hand edits, and it deserves one instruction * instead of a wall of per-file warnings. */ export declare function hasHashManifest(genStateDir: string): boolean; /** * What `decideAndWrite` WOULD do, touching nothing. Backs `meta gen --dry-run`. * * Exact for every outcome the hash manifest decides, because those are pure * comparisons — `classifyWrite` above is the single source for which one applies. * Deliberately COARSE in one place: with a snapshot body present the result depends * on whether `git merge-file` comes back clean or conflicted, which cannot be known * without performing the merge — so that case reports `overwrite`, meaning "this * file will be rewritten", which is true either way. * * The reason this exists as its own function rather than a flag on * `decideAndWrite`: a preview must be incapable of writing, and the cheapest way to * guarantee that is to give it no write in its body at all — mapping a `WriteCase` * to a status touches no file and no manifest. */ export declare function previewWriteStatus(path: string, content: string, optsOrStrategy?: DecideAndWriteOpts | MergeStrategy): WriteStatus; /** * The main entry point. Backward-compatible with the rc.11 signature: passing * a `MergeStrategy` string as the third argument continues to work; passing * an options object opts into three-way merge. */ export declare function decideAndWrite(path: string, content: string, optsOrStrategy?: DecideAndWriteOpts | MergeStrategy): WriteResult; //# sourceMappingURL=overwrite-policy.d.ts.map