/** * SSoT config registry — `resolveCleoConfig` cascade resolver implementing the * ConfigManifest contract from `@cleocode/contracts`. * * Precedence chain (higher wins): * - `0` — defaults / metadata baseline (NOT part of the merge chain) * - `10` — `~/.cleo/config.json` (global) * - `20` — `/.cleo/config.json` (project) * * Metadata files (`project-info.json`, `project-context.json`) live on a * separate channel and are surfaced through dedicated loaders — they are NEVER * merged into the resolved CleoConfig. * * This module is pure JSON file IO. It does NOT open SQLite — the * `DB Open Guard` SSoT contract (ADR-068) is preserved. * * Path resolution is delegated to `@cleocode/paths` (`getCleoHome`) per the * Paths SSoT (T9802) — XDG / env-paths logic is NEVER hand-rolled here. * * @task T9878 * @saga T9855 * @adr 076 */ /** * Merged JSON shape of `.cleo/config.json`. * * Intentionally generic — the cascade resolver operates structurally and * defers shape interpretation to the consumer. For typed reads, layer a Zod * schema on top via {@link validateConfig} or the manifest's `schema` field. * * @public */ export type MergedConfig = { [key: string]: unknown; }; /** * Read-only metadata channel for `.cleo/project-info.json`. * * @public */ export type ProjectInfo = Record; /** * Read-only metadata channel for `.cleo/project-context.json`. * * @public */ export type ProjectContext = Record; /** * Scope selector for {@link resolveCleoConfig} and {@link getConfigValue}. * * - `'global'` — return ONLY the global file's contents (or `{}` if missing) * - `'project'` — return ONLY the project file's contents (or `{}` if missing) * - `'merged'` — return the deep-merge of `{ ...defaults, ...global, ...project }` * * @public */ export type ResolveScope = 'global' | 'project' | 'merged'; /** * Scope selector for {@link validateConfig}. * * @public */ export type ValidateScope = 'global' | 'project'; /** * Scope selector for {@link checkDrift}. * * The `'metadata'` value runs drift checks against the two metadata-channel * entries (`project-info`, `project-context`) and aggregates the worst case. * * @public */ export type DriftScope = 'global' | 'project' | 'metadata'; /** * Result of {@link validateConfig}. * * @public */ export interface ValidateResult { /** `true` IFF every gate passed (or no schema was configured). */ ok: boolean; /** Human-readable issues. Empty when `ok === true`. */ issues: string[]; } /** * Result of {@link checkDrift}. * * @public */ export interface DriftResult { /** `true` IFF drift was detected. */ drift: boolean; /** Optional human-readable reason for the drift verdict. */ reason?: string; } /** * Resolve the CleoConfig cascade for a project. * * Precedence (higher wins): defaults (0) → global (10) → project (20). * * @param opts.scope - Which cascade slice to return. `'merged'` applies the * full precedence chain; `'global'` and `'project'` return their respective * files unmodified (or `{}` when missing). * @param opts.projectRoot - Absolute path to the project root (the directory * containing `.cleo/`). * @returns A merged `MergedConfig` for `'merged'` scope, or the raw file * contents for `'global'` / `'project'` scopes. * @throws When a present file contains malformed JSON; missing files are * silently treated as `{}`. * * @example * ```typescript * const cfg = await resolveCleoConfig({ scope: 'merged', projectRoot: '/my/proj' }); * ``` * * @public */ export declare function resolveCleoConfig(opts: { scope: ResolveScope; projectRoot: string; }): Promise; /** * Load `/.cleo/project-info.json`. * * Metadata channel — NEVER merged into the cascade. Returns `null` when the * file is absent. * * @public */ export declare function loadProjectInfo(projectRoot: string): Promise; /** * Load `/.cleo/project-context.json`. * * Metadata channel — NEVER merged into the cascade. Returns `null` when the * file is absent. * * @public */ export declare function loadProjectContext(projectRoot: string): Promise; /** * Read a single value out of the resolved cascade by `.`-separated key path. * * @param key - Dot-separated key (e.g. `'release.branchModel'`). * @param opts.scope - Cascade slice to read from. Defaults to `'merged'`. * @param opts.projectRoot - Absolute path to the project root. * @returns The resolved value, or `undefined` when the key is absent. * * @public */ export declare function getConfigValue(key: string, opts: { scope?: ResolveScope; projectRoot: string; }): Promise; /** * Flatten a resolved config object into its dot-notation leaf keys. * * Recurses into plain objects; arrays and primitives are treated as leaf * values. Used by `config.list` to surface a discoverable, copy-pasteable key * list alongside the full resolved object. * * @param obj - A resolved config object (e.g. from {@link resolveCleoConfig}). * @returns Sorted dot-notation key paths to every leaf value. * * @example * ```typescript * flattenConfigKeys({ a: { b: 1 }, c: [2] }); // → ['a.b', 'c'] * ``` * * @public */ export declare function flattenConfigKeys(obj: Record): string[]; /** * Result of {@link unsetConfigValue}. * * @public */ export interface UnsetResult { /** The dot-notation key that was targeted. */ key: string; /** Scope the key was removed from. */ scope: 'project' | 'global'; /** `true` IFF a value was actually deleted (idempotent: `false` when absent). */ removed: boolean; } /** * Remove a single `.`-separated key from a scoped config file and persist the * result. Pure JSON file IO — does NOT open SQLite (ADR-068 preserved). * * Idempotent: when the key is already absent the file is left untouched and * `{ removed: false }` is returned. When the key exists it is deleted (pruning * the now-empty parent objects is intentionally NOT done — operators may rely on * a present-but-empty namespace), the file is rewritten, and `{ removed: true }` * is returned. * * @param key - Dot-separated key to remove (e.g. `'release.branchModel'`). * @param opts.projectRoot - Absolute path to the project root. * @param opts.global - When `true`, target the global `~/.cleo/config.json` * instead of the project file. * @returns The {@link UnsetResult} describing the outcome. * @throws When the present file contains malformed JSON. * * @public */ export declare function unsetConfigValue(key: string, opts: { projectRoot: string; global?: boolean; }): Promise; /** * Validate a scoped config file against its manifest entry's `schema`. * * When the entry has no schema attached (`schema === null` or `undefined`), * validation is a no-op and `{ ok: true, issues: [] }` is returned. * * @public */ export declare function validateConfig(scope: ValidateScope, projectRoot: string): Promise; /** * Apply the manifest's drift-detection strategy to a scoped file. * * - `'global'` / `'project'` — runs the configured strategy against the * matching cascade entry. * - `'metadata'` — runs strategies for BOTH metadata entries and returns the * first drift hit (worst-case semantics). * * Strategy map: * - `'schema-validate'` → delegate to {@link validateConfig} * - `'staleness-gate'` → compare `detectedAt` against * {@link PROJECT_CONTEXT_STALENESS_MS} * - `'value-diff'` → noop for now; returns `{ drift: false }` * - `'none'` → noop; returns `{ drift: false }` * * @public */ export declare function checkDrift(scope: DriftScope, projectRoot: string): Promise; //# sourceMappingURL=registry.d.ts.map