/**
 * Project identity — decouple project scope from filesystem path.
 *
 * Two artifacts work together:
 *
 *   1. **Local ID card** at `<project-root>/.opensquid/project.json` —
 *      stable identity that travels with the project across moves /
 *      renames. Contains `id` (human-friendly), `uuid` (machine-stable),
 *      `created_at`.
 *
 *   2. **Global registry** at `~/.opensquid/projects.json` — index of
 *      all known projects by uuid. Tracks `last_seen_path`,
 *      `last_seen_at`, `status` (active|deleted) so we can detect
 *      moves and prune deletes.
 *
 * State machine on `resolveProject(cwd)`:
 *
 *   - card present, registry agrees on path → **known**
 *   - card present, registry has different path for this uuid → **moved**
 *     (registry's old path may or may not still exist; we update both)
 *   - card absent → **new** (caller decides whether to auto-create)
 *
 * `pruneDeleted()` sweeps the registry: for each active entry whose
 * `last_seen_path` no longer exists on disk, flip status to "deleted".
 */
export interface ProjectCard {
    /** Schema version; bump when the file shape changes. */
    version: 1;
    /** Human-friendly id (default: git basename of project root). */
    id: string;
    /** Machine-stable uuid that survives moves + id renames. */
    uuid: string;
    /** ISO timestamp the card was first written. */
    created_at: string;
}
export type ProjectStatus = "active" | "deleted";
export interface RegistryEntry {
    id: string;
    last_seen_path: string;
    last_seen_at: string;
    created_at: string;
    status: ProjectStatus;
}
export interface Registry {
    version: 1;
    /** Map of uuid → registry entry. */
    projects: Record<string, RegistryEntry>;
}
export type ResolvedProject = {
    kind: "known";
    card: ProjectCard;
    cardPath: string;
} | {
    kind: "moved";
    card: ProjectCard;
    cardPath: string;
    from_path: string;
} | {
    kind: "new";
    project_root: string | null;
    suggested_id: string;
};
/** Path to the registry file. */
export declare function registryPath(dataRoot?: string): string;
/** Path the ID card WOULD live at for a given project root. */
export declare function cardPathForRoot(projectRoot: string): string;
/**
 * Walk up from `cwd` looking for a `.opensquid/project.json` card.
 * Stops at the filesystem root. Returns the card + the path the
 * card was found at, or null if no card exists in any ancestor.
 */
export declare function findProjectCard(cwd: string): Promise<{
    card: ProjectCard;
    cardPath: string;
} | null>;
/**
 * Write a project card at `<projectRoot>/.opensquid/project.json`.
 * Refuses to overwrite an existing card unless `force` is set —
 * overwriting is a manual operation, not an accidental one.
 */
export declare function writeProjectCard(projectRoot: string, card: ProjectCard, options?: {
    force?: boolean;
}): Promise<{
    cardPath: string;
}>;
export declare function loadRegistry(dataRoot?: string): Promise<Registry>;
export declare function saveRegistry(reg: Registry, dataRoot?: string): Promise<void>;
/**
 * Determine the project state for a given cwd.
 *
 * - Walks up from cwd for a card.
 * - Cross-references with the registry.
 * - Returns the state without mutating either file.
 *
 * `applyResolution` is the side-effect step: it updates the registry
 * based on the resolved state (and creates a card for `new` IF the
 * caller asks it to).
 */
export declare function resolveProject(cwd: string, options?: {
    dataRoot?: string;
}): Promise<ResolvedProject>;
/**
 * Side-effecting companion to `resolveProject`. Updates the registry
 * (creating the card if asked for `new`). Idempotent.
 *
 * - `known`: bump `last_seen_at` only.
 * - `moved`: update `last_seen_path` + `last_seen_at`.
 * - `new`: if `autoCreate` is true, create a card at `cwd` + register
 *   it. Otherwise return null.
 */
export declare function applyResolution(cwd: string, resolved: ResolvedProject, options?: {
    dataRoot?: string;
    autoCreate?: boolean;
    id?: string;
}): Promise<ProjectCard | null>;
/**
 * Mark registry entries as "deleted" when their `last_seen_path` no
 * longer exists on disk. Does NOT actually remove the entry — kept
 * for historical reference and so the user can see what they had.
 *
 * Returns the count of entries that flipped from active → deleted.
 */
export declare function pruneDeleted(dataRoot?: string): Promise<{
    swept: number;
    removed_ids: string[];
}>;
//# sourceMappingURL=project.d.ts.map