/** * `cleo doctor projects` โ€” machine-wide integrity check of the project * registry (T12471 ยท ADR-094). * * The registry is keyed by project id, never by path. A path is a per-device * hint that goes stale when a project moves, is deleted, or is re-initialized * under a new id. This module walks every registry row, probes its path, scans * the directories projects live in for `.cleo/` declarations, and classifies * each row (see `NexusRegistryFindingKind`): * * - {@link inspectProjectRegistry} is read-only and returns every non-`ok` * row with its exact remedy. * - {@link applyProjectRegistryRepair} applies only the safe classes: a * `moved` row is rebound to the one path that declares its id, a * `missing` row's location is recorded as `missing`, and an alias row whose * truncated legacy key several projects claim is removed (T12589). No * registry row is deleted. The rows the changes touch are imaged before and * after, and both images are written to `nexus_audit_log` in the same * transaction as the changes. * - {@link rollbackProjectRegistryRepair} restores the before image, guarded * on the current rows still matching the after image. * * Split identities, ambiguous moves, temp rows and home/root rows are owner * decisions: they are reported with a remedy and never changed. * * Probes are bounded: at most `concurrency` filesystem reads at once, each * with a `timeoutMs` budget. Only ENOENT/ENOTDIR prove a path gone; EACCES, * EPERM or a timeout leave the row `unreadable` and untouched (#1606). * * @task T12471 * @epic T12468 * @see ADR-094 โ€” write-once portable project identity */ import type { NexusRegistryIntegrityReport, NexusRegistryRollbackResult } from '@cleocode/contracts'; /** Options shared by inspect and apply. */ export interface ProjectRegistryScanOptions { /** Extra directories to scan for `.cleo/`, beyond the registry-derived parents. */ roots?: readonly string[]; /** Scan depth below each root (default 2). */ maxDepth?: number; /** Filesystem reads in flight at once (default 16). */ concurrency?: number; /** Budget per filesystem probe in milliseconds (default 2000). */ timeoutMs?: number; /** Global CLEO home whose registry is checked (defaults to the current one). */ cleoHome?: string; } /** Error with a stable code, raised when a rollback is refused. */ export declare class RegistryRepairError extends Error { readonly code: 'E_NOT_FOUND' | 'E_ROLLBACK_CONFLICT'; /** @override */ readonly name = "RegistryRepairError"; /** * @param code - Stable error code (`E_NOT_FOUND`, `E_ROLLBACK_CONFLICT`). * @param message - Explanation with the remedy. */ constructor(code: 'E_NOT_FOUND' | 'E_ROLLBACK_CONFLICT', message: string); } /** * Whether a registry path is a temp row: under a temp directory while the * registry itself is persistent. A sandboxed registry (temp `CLEO_HOME`) keeps * its temp fixtures legitimately, as encounter registration does. * * @param projectPath - Registered path. * @param cleoHome - Home of the registry being checked. * @returns `true` for a temp row. * * @example * ```ts * isTempRegistryPath('/tmp/fixture', '/home/me/.local/share/cleo'); // true * ``` */ export declare function isTempRegistryPath(projectPath: string, cleoHome: string): boolean; /** * Whether a registry path is the home directory or a filesystem root: a * project registered there claims every directory below it. * * @param projectPath - Registered path. * @param home - Home directory (defaults to the current user's). * @returns `true` for a home or root row. * * @example * ```ts * isHomeOrRootPath('/'); // true * ``` */ export declare function isHomeOrRootPath(projectPath: string, home?: string): boolean; /** * Whether `path` lies in a trash directory. A project the owner trashed is * never a move target, and its registry row may be cleaned. * * @param path - Absolute path. * @returns `true` inside `.Trash`, `.Trashes`, `$RECYCLE.BIN` or `.local/share/Trash`. * * @example * ```ts * isTrashPath('/Users/me/.Trash/app'); // true * ``` */ export declare function isTrashPath(path: string): boolean; /** * Classify every registry row, read-only. * * @param opts - Extra scan roots, depth, concurrency, per-probe budget, registry home. * @returns Counts per kind and every non-`ok` row with its exact remedy. * * @example * ```ts * const report = await inspectProjectRegistry({ roots: ['/home/me/src'] }); * for (const f of report.findings) console.log(f.kind, f.projectPath, f.remedy); * ``` */ export declare function inspectProjectRegistry(opts?: ProjectRegistryScanOptions): Promise; /** * Inspect, then apply the safe repairs: rebind every `moved` row to the path * that declares its id, record every `missing` row's location as * `missing`, and remove every alias row whose key several projects claim * (T12589). One immediate transaction makes the changes and writes the * receipt (before and after images of every touched row, alias rows * included) to `nexus_audit_log`. No registry row is deleted, and nothing * outside the registry is written. A row that changed since inspection is * skipped. * * @param opts - Same options as {@link inspectProjectRegistry}. * @returns The report, with `dryRun: false` and a receipt when anything applied. * * @example * ```ts * const result = await applyProjectRegistryRepair(); * if (result.receipt) console.log(result.receipt.rollback); * ``` */ export declare function applyProjectRegistryRepair(opts?: ProjectRegistryScanOptions): Promise; /** * Restore the rows an applied repair changed. Refused when the receipt is * unknown, was already rolled back, or when any row in its scope changed * since the repair (the current rows must equal the receipt's after image) โ€” * a later edit is never overwritten. The rollback writes its own receipt. * * @param receiptId - `receiptId` from {@link applyProjectRegistryRepair}. * @param opts - Registry home. * @returns Rows restored per table and the rollback's own receipt id. * @throws RegistryRepairError (`E_NOT_FOUND`, `E_ROLLBACK_CONFLICT`). * * @example * ```ts * await rollbackProjectRegistryRepair(receipt.receiptId); * ``` */ export declare function rollbackProjectRegistryRepair(receiptId: string, opts?: Pick): Promise; /** * For each id, the paths on this device other than `exclude` that provably * hold it now: recorded locations of the id that still declare it, plus * directories found by scanning `roots`. Used by `nexus projects clean` to * refuse deleting a row whose project merely moved (T12471). * * @param ids - Project ids to look for, with the registered path to exclude. * @param opts - Scan roots and budgets; roots default to the parents of the given paths. * @returns Map from id to the paths that declare it (ids found nowhere are absent). * * @example * ```ts * const found = await locateProjectsElsewhere([{ projectId, projectPath }]); * ``` */ export declare function locateProjectsElsewhere(ids: ReadonlyArray<{ projectId: string; projectPath: string; }>, opts?: ProjectRegistryScanOptions): Promise>; //# sourceMappingURL=projects.d.ts.map