import { spawn } from "node:child_process";
import { type LockfileRegenPlan, type PackageManager, LOCKFILE_REGEN_PLANS, detectPackageManagers, enginesForbiddenManagers, getLockfileRegenPlan } from "./package-manager-detect.js";
export { LOCKFILE_REGEN_PLANS, detectPackageManagers, enginesForbiddenManagers, getLockfileRegenPlan, };
export type { LockfileRegenPlan, PackageManager };
/**
 * Determine whether this Lisa invocation is running as a package-manager lifecycle
 * script (postinstall, prepare, etc.). Works across npm, bun, yarn, and pnpm since
 * all set npm_package_json while executing lifecycle scripts.
 * @returns true when running inside a package-manager lifecycle hook
 */
export declare function isRunningAsLifecycleScript(): boolean;
/**
 * Determine whether this Lisa invocation is itself the trampoline reconciliation
 * child. Used to short-circuit further trampoline scheduling.
 * @returns true when running inside the detached reconciliation child
 */
export declare function isRunningAsTrampoline(): boolean;
/**
 * Detect whether Lisa is running inside a CI environment.
 *
 * Returns false when running inside a Vitest or Jest test runner, even if
 * `CI=true` is set, because test runners are not package-manager processes
 * and the trampoline's parent-liveness check would never terminate.
 * @returns true when common CI env vars indicate we're in a CI runner AND we
 *   are not currently inside a test runner process
 */
export declare function isRunningInCI(): boolean;
/**
 * Decide whether Lisa should spawn a post-install reconciliation trampoline.
 *
 * Background: `bun add` (and similar mutations in other package managers) reads
 * package.json into memory at the start of the command and writes it back at the end,
 * overwriting any changes that postinstall scripts make to package.json. This breaks
 * Lisa's `force`/`defaults`/`merge` semantics for package.json fields like
 * resolutions/overrides/scripts when the project is updated via
 * `bun add -d @codyswann/lisa\@latest` (escaped to avoid JSDoc misparsing).
 *
 * The trampoline works around this by spawning a fully detached child process that
 * waits for the package manager to exit, then re-runs Lisa. The re-run happens after
 * the package manager has finished writing package.json, so Lisa's changes survive.
 *
 * We only schedule the trampoline when:
 * - Lisa is running as a lifecycle script (so the package-manager write race applies)
 * - Lisa is not already the trampoline child (no recursive scheduling)
 * - We're not in dry-run mode (no filesystem changes, nothing to reconcile)
 * @param dryRun - Whether Lisa is in dry-run mode
 * @returns true when reconciliation trampoline should be scheduled
 */
export declare function shouldSchedulePostinstallReconciliation(dryRun: boolean): boolean;
/**
 * Get the directory containing the currently running Lisa CLI entrypoint.
 * Walks up from the current module (utils) to dist/, then returns dist/.
 * This path is embedded in the trampoline shell script so it can re-invoke Lisa
 * without relying on cwd or a PATH lookup.
 *
 * Note: exported for testing but not part of the public API.
 * @param moduleUrl - import.meta.url of the caller (for ESM compatibility)
 * @returns Absolute path to the Lisa dist directory
 */
export declare function getLisaDistDir(moduleUrl: string): string;
/**
 * Hash a file's contents (sha256, hex-encoded). Returns null if the file
 * does not exist or cannot be read. Used to detect whether Lisa mutated
 * package.json during its apply so we only regenerate lockfiles when needed.
 * @param filePath - Absolute path to the file
 * @returns Hex-encoded sha256 hash, or null if the file is unavailable
 */
export declare function hashFile(filePath: string): string | null;
/**
 * Regenerate lockfiles in the current process (no trampoline, no detached child).
 *
 * This is the synchronous counterpart to the trampoline's `regenerateLockfiles`
 * closure. It runs in-process when Lisa is invoked manually (e.g.,
 * `node node_modules/@codyswann/lisa/dist/index.js --yes --skip-git-check .`
 * after `npm install -D`), where no parent package-manager process is racing
 * to rewrite package.json. In that path the trampoline is never scheduled, so
 * without an in-process regen the lockfile drifts from package.json and the
 * next `npm ci` (or `bun install --frozen-lockfile`) fails.
 *
 * Best-effort: failures (missing PM binary, transient network issues) are
 * intentionally swallowed so a missing global PM does not cascade into an
 * apply failure. The caller should already have verified that the project's
 * primary package manager is on PATH before invoking Lisa manually.
 * @param projectDir - Absolute path to the project directory
 * @param spawnFn - Optional spawn implementation; defaults to node:child_process spawn. Tests pass a vi.fn() spy here as a DI seam.
 * @returns Promise that resolves once all detected lockfiles have been regenerated
 */
export declare function regenerateLockfilesInProcess(projectDir: string, spawnFn?: typeof spawn): Promise<void>;
/**
 * Spawn a reconciliation child process that waits for the parent package manager
 * to exit, then re-runs Lisa to reconcile package.json.
 *
 * The child is always spawned fully detached (independent process group, stdio
 * ignored, unref'd) so the parent package manager can exit normally. The
 * trampoline child then detects the parent exiting and re-runs Lisa after the
 * package manager has finished writing package.json.
 *
 * A blocking (non-detached) CI variant was previously attempted but created a
 * circular deadlock (PM waits for Lisa, Lisa waits for the child, the child
 * polls `parentPid` waiting for the PM) that only resolves after the 120 s
 * `MAX_WAIT_MS` timeout — without running reconciliation. Always detaching
 * avoids this deadlock and ensures reconciliation actually runs.
 * @param projectDir - Absolute path to the project directory Lisa will reconcile
 * @param lisaDistDir - Absolute path to Lisa's dist directory (where index.js lives)
 * @param parentPid - PID of the package-manager process to wait on (usually process.ppid)
 * @param spawnFn - Optional spawn implementation; defaults to node:child_process spawn. Tests pass a vi.fn() spy here as a dependency-injection seam, avoiding the unreliable vi.doMock-on-builtins pattern that breaks under v8 coverage in CI runners.
 * @returns Promise that resolves immediately after spawning the detached child
 */
export declare function scheduleReconciliationChild(projectDir: string, lisaDistDir: string, parentPid: number, spawnFn?: typeof spawn): Promise<void>;
//# sourceMappingURL=postinstall-trampoline.d.ts.map