export interface MigrationEntry { version: string; date: string; notes: string; regenerate?: string[]; fileOps?: FileOp[]; breaking?: boolean; manual?: string[]; } export interface FileOp { op: "mkdir" | "copy" | "delete" | "substitute-placeholder"; path: string; src?: string; } export interface MigrationsJson { [key: string]: MigrationEntry; } export interface FailedCategory { version: string; category: string; reason: string; } export interface MigrationResult { applied: Array<{ fromVersion: string; toVersion: string; categories: string[]; }>; skippedBreaking: Array<{ fromVersion: string; toVersion: string; reason: string; }>; manualSteps: Array<{ fromVersion: string; toVersion: string; steps: string[]; }>; dryRun: boolean; schemasRefreshed: string[]; forgeRootUpdated: boolean; failedCategories: FailedCategory[]; } export interface RunMigrationsOptions { /** Absolute path to the dist/forge-payload/ bundle root */ bundleRoot: string; /** Absolute path to the project root (contains .forge/) */ projectRoot: string; /** Version the user was running before the upgrade */ fromVersion: string; /** Version the user just upgraded to */ toVersion: string; /** When true, log actions without writing any files */ dryRun?: boolean; } interface EntryWithKey { key: string; entry: MigrationEntry; } /** Write-descriptor: source path + destination path for a file copy/write operation */ interface WriteDescriptor { src: string; dest: string; content?: string; } /** * Compare two version strings using semver integer-component comparison. * Returns: negative if a < b, 0 if a === b, positive if a > b. * * Unlike string comparison, this correctly handles 0.9.x vs 0.10.x boundaries. * Strips leading "v" prefix (matches parseTriple() behavior in forge-update-command.ts:118). * Falls back to localeCompare for invalid/non-semver inputs. */ export declare function semverCompare(a: string, b: string): number; /** * Filter migrations.json entries using [fromVersion, toVersion) semantics on keys. * * - fromVersion entry IS included: it represents the transition AWAY from that version. * - toVersion entry is EXCLUDED: it would be a further transition past the target. * - Results are sorted ascending by semver (oldest first). * * No first-run special case — this same filter applies for both first-run * (empty ledger) and subsequent runs. The idempotency ledger handles re-run protection. */ export declare function filterMigrationEntries(migrations: MigrationsJson, fromVersion: string, toVersion: string): EntryWithKey[]; /** * Resolve a migration category string to one or more WriteDescriptors. * Pure function — does not write files; appends to the provided writes array. * * ENOENT trap rule: if source doesn't exist, skip silently (never throw on ENOENT). * Non-ENOENT IO errors propagate. * * Path-traversal defense: all output paths are validated against * path.join(projectRoot, '.forge') before being added to writes. */ export declare function resolveCategory(category: string, bundleRoot: string, projectRoot: string, writes: WriteDescriptor[]): void; /** * Execute all migration entries between [fromVersion, toVersion) from the * bundled migrations.json against the project's .forge/ directory. * * Design constraints: * - No UI context (no ctx.ui calls). * - No event emission (caller is responsible). * - Pure deterministic engine: reads from bundleRoot, writes to projectRoot/.forge/. * - Idempotent: already-applied versions (from .forge/applied-migrations.json) are skipped. * * Forward-compat: entries with non-empty fileOps[] use fileOps; otherwise regenerate. * NOTE: 0 of 158 current entries use fileOps — the executor is dead code on day 1. * Once a real fileOps entry lands, an integration test against that entry MUST be added. */ export declare function runMigrations(opts: RunMigrationsOptions): Promise; export declare const __test__: { semverCompare: typeof semverCompare; filterMigrationEntries: typeof filterMigrationEntries; resolveCategory: typeof resolveCategory; }; export {};