/** * The pure planner (spec 04 §3-§6) — the dry-run = real-run engine. * * `plan.ts` performs ZERO filesystem writes and ZERO network calls. It MAY read destination bytes * (via `sha256File`) and the prior manifest (already read by 05 and passed in via `PlanContext`). * It diffs three facts per bundle-relative path — source hash (S), destination hash (D), and the * manifest-recorded hash (M) — and emits a `PlannedAction`. The CLI hands the SAME object to * `apply()` on a real run, so dry-run and real run can never drift. Zero runtime dependencies. */ import type { AgentId, FileActionKind, Mode, PlannedAction, Result, Scope, InstallManifest } from "./types.js"; import { type LocatedSource } from "./source.js"; import { type ResolvedPlacement } from "./placements.js"; /** * Everything the pure planner needs to diff source ⇆ destination ⇆ manifest for ONE agent * (spec 04 §4). Built by cli.ts (07); the planner reads these and writes nothing. */ export interface PlanContext { /** The agent being planned. */ readonly agent: AgentId; /** Active scope; copied onto the plan. */ readonly scope: Scope; /** Resolved materialization mode. MUST already account for Windows (see `resolveMode`). */ readonly mode: Mode; /** Absolute path of the `feature-forge/` namespace dir to be governed. */ readonly destination: string; /** The located, integrity-checked source bundle (03), or `null` (absent/invalid bundle). */ readonly source: LocatedSource | null; /** The prior manifest for this destination, or `null` if none exists (fresh install). */ readonly priorManifest: InstallManifest | null; /** `--force`: overwrite `skip-modified` destinations instead of skipping. */ readonly force: boolean; /** The pinned rauf coordinate to surface on the plan (06); the planner only echoes it. */ readonly raufPin?: string | null; /** * Resolved secondary placements for this agent (A4b), or absent/empty when it has none. Supplied by * cli.ts (which holds the scope roots); the planner diffs each against its destination and the prior * manifest's matching placement inventory. */ readonly placements?: ResolvedPlacement[]; } /** * Classify one bundle-relative path (spec 04 §6 table). PURE: hashes are read, nothing is written. * * @param relpath bundle-relative POSIX path (informational; not used in the decision) * @param srcHash sha256 of the source file (S) * @param destHash sha256 of the destination file, or undefined if absent (D) * @param manifestHash sha256 recorded for this path in the prior manifest, or undefined (M) * @param force whether --force promotes skip-modified → overwrite */ export declare function classifyFile(relpath: string, srcHash: string, destHash: string | undefined, manifestHash: string | undefined, force: boolean): FileActionKind; /** * PURE. Compute the install plan for one agent (spec 04 §4.1). Writes nothing. Returns * err(SOURCE_MISSING/SOURCE_INVALID) when `ctx.source` is null. */ export declare function planInstall(ctx: PlanContext): Result; /** * PURE. Compute the update/reconcile plan for one agent (spec 04 §4.2): identical to planInstall * for create/overwrite/unchanged/skip-modified, PLUS manifest-scoped orphan removal — any path in * `priorManifest.files` the current source no longer contains becomes `remove`. With no prior * manifest, behaves exactly like planInstall (first install). */ export declare function planUpdate(ctx: PlanContext): Result; /** * Convenience dispatcher used by cli.ts (07). Routes install/update to the typed planner; * `uninstall` delegates to `planUninstall` (manifest-driven, from 05) — an absent prior manifest * yields an empty-files plan (no-op). */ export declare function plan(subcommand: "install" | "update" | "uninstall", ctx: PlanContext): Result; /** * Resolve the effective materialization mode (spec 04, REQ-FLAG-03, D8). `--symlink` requests * symlink, but Windows ALWAYS copies. Pure; `windows` is injectable for tests. */ export declare function resolveMode(wantSymlink: boolean, windows?: boolean): Mode;