/** * NEXUS project registry - cross-project registration and management. * * SQLite-backed via nexus.db (Drizzle ORM). The global project registry * is stored in ~/.cleo/nexus.db in the project_registry table. * * Legacy JSON backend (projects-registry.json) is migrated on first init * via migrate-json-to-sqlite.ts. * * @task T5366 * @epic T4540 */ import type { NexusInitParams, NexusListParams, NexusPermissionSetParams, NexusProjectCandidate, NexusProjectsFleetParams, NexusProjectsFleetResult, NexusProjectsStatusParams, NexusProjectsStatusResult, NexusReconcileParams, NexusRegisterParams, NexusShowParams, NexusSyncParams, NexusUnregisterParams } from '@cleocode/contracts'; import { type EngineResult } from '../engine-result.js'; import { CleoError } from '../errors.js'; import { paginate } from '../pagination.js'; import { resetNexusDbState } from '../store/nexus-sqlite.js'; import type { ProjectRegistryRow } from '../store/schema/nexus-schema.js'; export type NexusPermissionLevel = 'read' | 'write' | 'execute'; export type NexusHealthStatus = 'unknown' | 'healthy' | 'degraded' | 'unreachable'; /** Per-project code intelligence statistics stored in stats_json. */ export interface NexusProjectStats { nodeCount: number; relationCount: number; fileCount: number; } /** Domain representation of a registered Nexus project. */ export interface NexusProject { hash: string; projectId: string; path: string; name: string; registeredAt: string; lastSeen: string; healthStatus: NexusHealthStatus; healthLastCheck: string | null; permissions: NexusPermissionLevel; lastSync: string; taskCount: number; labels: string[]; /** Absolute path to the project's live store holding brain tables (`.cleo/cleo.db`). Null if not yet populated. */ brainDbPath: string | null; /** Absolute path to the project's live store holding task tables (`.cleo/cleo.db`). Null if not yet populated. */ tasksDbPath: string | null; /** ISO 8601 timestamp of the last code intelligence index run. Null if never indexed. */ lastIndexed: string | null; /** Code intelligence stats from the last index run. */ stats: NexusProjectStats; /** ISO 8601 instant of the last health check, sync or git probe (T12512); null if never. */ lastProbedAt?: string | null; /** ISO 8601 instant of the last real CLI use inside the project (T12512); null if never. */ lastOpenedAt?: string | null; /** * The project lives on another machine (a row a cloud vault restore brought): * `path` is a placeholder, not a location here, so nothing may probe or open * it (T13006). Absent for a project on this machine. */ remote?: true; } /** Legacy registry file shape (pre-SQLite). Retained for migration compatibility. */ export interface NexusRegistryFile { $schema?: string; schemaVersion: string; lastUpdated: string; projects: Record; } /** Get path to the NEXUS home directory (cache, etc.). */ export declare function getNexusHome(): string; /** Get path to the NEXUS cache directory. */ export declare function getNexusCacheDir(): string; /** * Get path to the legacy projects registry JSON file. * @deprecated Use nexus.db via getNexusDb() instead. Retained for JSON-to-SQLite migration. */ export declare function getRegistryPath(): string; /** * Read all projects from nexus.db and return as a NexusRegistryFile. * Compatibility wrapper for consumers that expect the legacy JSON shape. * * @throws {NexusRegistryReadError} When the registry cannot be read — a * broken registry never looks empty (T12512). */ export declare function readRegistry(): Promise; /** * Read the global registry. * * @deprecated {@link readRegistry} now throws a typed error itself (T12512). * @throws {NexusRegistryReadError} When the registry cannot be read. */ export declare function readRegistryRequired(): Promise; /** * Initialize the NEXUS directory structure and nexus.db. * Idempotent -- safe to call multiple times. * Migrates legacy JSON registry on first run if present. */ export declare function nexusInit(_projectRoot?: string, _params?: NexusInitParams): Promise; /** * Register or refresh the same immutable project in the global registry. * @param _projectRoot - Calling context; the explicit params.path owns the registration. * @param params - Target path and optional requested name and permission. * @returns The canonical path fingerprint of the registered project. * @remarks Metadata is read before a synchronous ownership recheck and atomic * registry/alias transaction. Encounter-time store initialization and audit logging * are separate operations. Ownership is the immutable project id alone (ADR-094 · * T12469): the same id at a new path moves the row to that checkout and records a * location; a different id at a registered path registers beside the old row. * @example * ```ts * const hash = await nexusRegister(projectRoot, { path: projectRoot, name: 'app', permission: 'read' }); * ``` */ export declare function nexusRegister(_projectRoot: string, params: NexusRegisterParams): Promise; /** @deprecated Use `nexusRegister(projectRoot, params)` — ADR-057 D1 */ export declare function nexusRegister(projectPath: string, name?: string, permissions?: NexusPermissionLevel): Promise; /** * Unregister a project from the global registry. */ export declare function nexusUnregister(_projectRoot: string, params: NexusUnregisterParams): Promise; /** @deprecated Use `nexusUnregister(projectRoot, params)` — ADR-057 D1 */ export declare function nexusUnregister(nameOrHash: string): Promise; /** * List all registered projects. * * @throws {NexusRegistryReadError} When the registry cannot be read — never * an empty list that hides the failure (T12512). */ export declare function nexusList(_projectRoot?: string, _params?: NexusListParams): Promise; /** Registry rows by recorded health, for `cleo nexus status` (T14011). */ export interface NexusRegistryHealthSummary { /** * Registry rows, excluding only vault placeholders (rows a cloud restore * brought from another machine). Rows with no host recorded are counted even * when they belong to another machine. */ total: number; /** Rows per recorded health status. */ byStatus: Record; /** * Rows recorded as unreachable whose project lives on this machine (or whose * host is unknown): candidates for `projects clean --orphans`. */ unreachable: number; /** * Rows whose project lives on another machine (T14013): recorded only on * other devices, or with no record and a path root absent here. Never * candidates for removal from this machine. */ otherHost: number; /** The dry-run command that classifies them; present when `unreachable > 0`. */ cleanCommand?: string; } /** * Count registry rows by their recorded health. Reads only: nothing is * probed or removed; `projects clean --orphans --dry-run` proves which rows * are gone before anything is deleted. * * @returns Counts per status, and the dry-run command when rows are unreachable. * @throws {NexusRegistryReadError} When the registry cannot be read. * @example * ```ts * const { unreachable, cleanCommand } = await summarizeRegistryHealth(); * ``` */ export declare function summarizeRegistryHealth(): Promise; /** * A project NAME matched more than one registry row (T12510). Names are not * unique — two checkouts or two unrelated projects can share one — so the * lookup refuses to pick one and lists every candidate id instead. * * @example * ```ts * try { await nexusGetProject('', { name: 'api' }); } * catch (e) { if (e instanceof NexusProjectAmbiguityError) console.error(e.candidates); } * ``` */ export declare class NexusProjectAmbiguityError extends CleoError { /** Stable machine-readable error code. */ readonly codeName = "E_NEXUS_PROJECT_AMBIGUOUS"; /** Every matching project, most recently active first (`projectLastActivity`). */ readonly candidates: NexusProjectCandidate[]; /** * @param name - The ambiguous project name, or alias key (T12589). * @param rows - Every registry row that matches it. * @param field - What `name` is: a project name, or an alias key that more * than one project claims. */ constructor(name: string, rows: ReadonlyArray, field?: 'name' | 'alias'); } /** * Look up a portable project id or an unambiguous recorded legacy alias. * Names and path hashes are not project ids and are not accepted here. * * @param projectRoot - Calling checkout (consistent with the registry API). * @param projectId - Portable id, or a recorded alias retained for compatibility. * @returns The registered project, or null when the id is not registered. * @throws {NexusProjectAmbiguityError} When several projects claim an alias. * @throws {NexusRegistryReadError} When registry access fails. */ export declare function nexusGetProjectById(_projectRoot: string, projectId: string): Promise; /** * Bind a project-scoped graph query to its checkout's portable identity. * An override may name that identity or its unique legacy alias, never another * project: the graph store cannot be selected by relabeling its counts. * * @param projectRoot - Checkout whose project-scoped graph will be queried. * @param requestedId - Optional explicit portable id or recorded legacy alias. * @returns The current checkout's canonical id. * @throws {CleoError} On missing identity or a foreign/unresolved override. */ export declare function resolveNexusQueryProjectId(projectRoot: string, requestedId?: string): Promise; /** * Get a project by name or hash. * Returns null if not found. */ export declare function nexusGetProject(_projectRoot: string, params: NexusShowParams): Promise; /** @deprecated Use `nexusGetProject(projectRoot, params)` — ADR-057 D1 */ export declare function nexusGetProject(nameOrHash: string): Promise; /** * Check if a project exists in the registry. */ export declare function nexusProjectExists(nameOrHash: string): Promise; /** * Sync project metadata (task count, labels) for a registered project. */ export declare function nexusSync(_projectRoot: string, params: NexusSyncParams): Promise; /** @deprecated Use `nexusSync(projectRoot, params)` — ADR-057 D1 */ export declare function nexusSync(nameOrHash: string): Promise; /** * Sync all registered projects. A project that lives on another machine (a * cloud vault placeholder row) is skipped: it has no store here (T13006). * @returns Counts of synced and failed projects. */ export declare function nexusSyncAll(): Promise<{ synced: number; failed: number; }>; /** * Update code intelligence index stats for a registered project. * * Called after a successful `cleo nexus analyze` run to record the * latest node/relation/file counts and the indexed timestamp. * * @param projectPath - Absolute path to the project root. * @param stats - Results from the pipeline run. * @task T622 */ export declare function nexusUpdateIndexStats(projectPath: string, stats: NexusProjectStats): Promise; /** * Update a project's permission level in the registry. * Used by permissions.ts to avoid direct JSON file writes. */ export declare function nexusSetPermission(_projectRoot: string, params: NexusPermissionSetParams): Promise; /** @deprecated Use `nexusSetPermission(projectRoot, params)` — ADR-057 D1 */ export declare function nexusSetPermission(nameOrHash: string, permission: NexusPermissionLevel): Promise; /** * Reconcile the current project's identity with the global nexus registry. * * 3-scenario policy, keyed by the immutable project id alone (ADR-094 · T12469): * 1. projectId in registry + path matches → update lastSeen, return {status:'ok'} * 2. projectId in registry + path changed → update path+hash, return {status:'path_updated'} * 3. projectId not in registry → auto-register, return {status:'auto_registered'} * * The former scenario 4 (a row at this path hash under another id → identity * conflict) is gone: a path is a location, not an identity. That row is a * stale location of another project, recorded as `superseded` in * `nexus_project_locations` when this project is recorded. * * @task T5368 */ export declare function nexusReconcile(projectRoot: string, params?: NexusReconcileParams): Promise<{ status: 'ok' | 'path_updated' | 'auto_registered' | 'candidate'; oldPath?: string; newPath?: string; }>; /** * Update a project's registry entry after a filesystem move. @task T11024 */ export declare function nexusMoveProject(projectId: string, newPath: string): Promise; /** * Update a project's name in the registry after a rename. @task T11024 */ export declare function nexusRenameProject(projectId: string, newName: string): Promise; /** * Reset the nexus database singleton state. * Re-exported from nexus-sqlite for test convenience. */ export { resetNexusDbState }; /** * Convert a caught error to an EngineResult failure, keeping the code, exit * code, fix and details of a typed registry error (`E_NEXUS_REGISTRY_READ`, * `E_NEXUS_DEVICE_NOT_FOUND`, `E_NEXUS_PROJECT_AMBIGUOUS`, `E_PROJECT_MOVED`) * instead of collapsing it to `E_INTERNAL` (T12512). Every nexus engine * wrapper that reads the registry uses this. * * @param error - The caught value. * @param fallbackMsg - Message used when `error` is not an `Error`. * @returns A failed EngineResult. * @example * ```ts * try { return engineSuccess(await nexusList()); } * catch (error) { return nexusCaughtToEngineError(error, 'Failed to list projects'); } * ``` * @task T12512 */ export declare function nexusCaughtToEngineError(error: unknown, fallbackMsg: string): EngineResult; /** * Get nexus status (initialized, project count, last updated). * * @task T1569 */ export declare function nexusStatus(): Promise>; /** * List all registered projects with pagination. * * @task T1569 */ export declare function nexusListProjects(limit?: number, offset?: number): Promise>; count: number; total: number; filtered: number; page: ReturnType['page']; }>>; /** * Show a single project by name. * * @task T1569 */ export declare function nexusShowProject(name: string): Promise>>>; /** * Initialize the nexus. * * @task T1569 */ export declare function nexusInitialize(): Promise>; /** * Register a project in the nexus. * * @task T1569 */ export declare function nexusRegisterProject(path: string, name?: string, permission?: NexusPermissionLevel): Promise>; /** * Unregister a project from the nexus. * * @task T1569 */ export declare function nexusUnregisterProject(name: string): Promise>; /** * Sync a specific project or all projects. * * @task T1569 */ export declare function nexusSyncProject(name?: string): Promise>; /** * Reconcile the nexus registry with the filesystem. * * @task T1569 */ export declare function nexusReconcileProject(projectRoot: string, params?: NexusReconcileParams): Promise>>>; /** * List all projects in the global nexus registry (Phase 2 dispatch op). * * @task T1569 */ export declare function nexusProjectsList(): Promise>; /** * Probe and record the git state of every project location on this device * (`nexus.projects.status`, T12511). Bounded concurrency, a per-location * timeout, no network unless `fetch` — see `nexus/git-state.ts`. * * @param _projectRoot - Unused: the probe covers every location on this device * (uniform ADR-057 signature). * @param params - Fetch, concurrency, timeout and staleness. * @returns Fresh rows for this device plus recorded rows of other devices. * @task T12511 */ export declare function nexusProjectsStatus(_projectRoot: string, params: NexusProjectsStatusParams): Promise>; /** * Fleet view (`nexus.projects.fleet`, T12513): every project, where it lives * on each device, and its last recorded git state, paged with counts first. * Read-only: it never runs git — see `nexus/fleet-status.ts`. * * @param _projectRoot - Unused: the fleet spans every registered project * (uniform ADR-057 signature). * @param params - Filters, staleness window and paging. * @returns The fleet view, or a typed error (`E_NEXUS_REGISTRY_READ`, * `E_NEXUS_DEVICE_NOT_FOUND`) — never an empty page that hides a failure. * @task T12513 */ export declare function nexusProjectsFleet(_projectRoot: string, params: NexusProjectsFleetParams): Promise>; /** * Register a project in the global nexus registry (Phase 2 dispatch op). * * @param repoPath - Absolute path to the project directory. * @param name - Custom project name (optional). * @task T1569 */ export declare function nexusProjectsRegister(repoPath: string, name?: string): Promise>; /** * Remove a project from the global nexus registry by name or hash (Phase 2 dispatch op). * * @param nameOrHash - Project name or hash to remove. * @task T1569 */ export declare function nexusProjectsRemove(nameOrHash: string): Promise>; //# sourceMappingURL=registry.d.ts.map