/**
 * Per-project apply lockfile.
 *
 * Prevents two `wizard apply` invocations from running concurrently
 * against the same install dir. Without this guard, an outer agent
 * (Claude Code, Cursor, etc.) that spawns `wizard apply` and forgets
 * to wait on the first invocation can race a second one — the inner
 * Claude SDK agents stomp each other's edits and the user gets a
 * file with track() calls inserted twice (or worse, conflicting
 * shapes). Real-world transcript: an agent backgrounded `wizard
 * --agent` 5 times before realizing the first was still running.
 *
 * Design:
 *   - File at `<runDir>/apply.lock` carries `{ pid, startedAt, planId }`.
 *   - `acquireApplyLock` writes atomically and returns either
 *     `{ ok: true, release }` or `{ ok: false, holder: ... }` so the
 *     caller can refuse with a clear message.
 *   - Stale locks (PID no longer running, or older than 30 minutes)
 *     are auto-cleared. `wizard apply` taking >30 minutes is itself
 *     a bug; treating the lock as stale is safer than wedging forever.
 *   - `release()` removes the file. Best-effort: if it fails (file
 *     gone, permission), the next acquire will treat it as stale
 *     and recover.
 *
 * Pure-ish — does fs I/O but no UI imports, so the module can be
 * exercised in isolation by unit tests with a custom installDir.
 */
/** Max age (ms) before we consider a lockfile stale and steal it. */
export declare const STALE_LOCK_THRESHOLD_MS: number;
export interface ApplyLockHolder {
    pid: number;
    startedAt: string;
    planId: string;
}
export type AcquireResult = {
    ok: true;
    release: () => void;
} | {
    ok: false;
    holder: ApplyLockHolder;
    reason: 'in_progress';
};
/**
 * Try to acquire the apply lock for `installDir`. If another wizard
 * apply is in progress, returns `{ ok: false, holder }` so the caller
 * can refuse with a clear message and a `kill <pid>` hint.
 *
 * Stale locks (PID gone, or older than `STALE_LOCK_THRESHOLD_MS`) are
 * cleared and re-acquired in the same call.
 */
export declare function acquireApplyLock(installDir: string, planId: string, now?: Date): AcquireResult;
/**
 * A lock is stale if either:
 *   - the PID is no longer running on this machine, OR
 *   - the lock is older than `STALE_LOCK_THRESHOLD_MS` (a wizard apply
 *     should never run that long; treating it as stale is safer than
 *     wedging forever).
 *
 * `process.kill(pid, 0)` is the standard liveness probe — sends signal
 * 0 (no-op) and throws ESRCH if the pid is gone, EPERM if we can't
 * signal it (rare, but treat as alive — same-machine + same-user).
 *
 * Exported for unit tests.
 */
export declare function isStale(holder: ApplyLockHolder, now?: Date): boolean;
