import type { ResolvedConfig } from "../types.js"; import type { SignalStrength } from "./registry.js"; /** One place a change could belong, named concretely enough to open. */ export interface SourceLocation { /** Absolute path. Whoever reads this has to open it. */ path: string; /** What lives here, in a few words. */ what: string; } export interface DetectedKit { /** Registry id, or the package name for a workspace-local kit. */ id: string; name: string; /** How the kit was found, worst-to-best: inferred < marker < dependency < declared. */ via: SignalStrength; /** What proved it: a dependency name, a marker file, a config declaration. */ evidence: string[]; /** * True when the kit's source is inside this repository and therefore the * repository's to edit. The single most consequential field in the file: it * decides whether a defect in a kit component is fixed here or upstream. */ editable: boolean; /** Absolute path to the kit's own package root, when it is in this repo. */ packageRoot: string | null; /** Where its components live, when that is knowable. */ componentRoots: string[]; /** Import specifier prefixes that mean "this came from the kit". */ importPrefixes: string[]; /** * Component names the kit exposes, read from the kit itself. * * Empty means the kit could not be read from here, NOT that it exports * nothing. Everything downstream treats the two differently: an unknown kit * falls back to suspecting by name, and a known one is allowed to say that a * component it does not list is a gap rather than a duplicate. */ exports: string[]; docs?: string; } export interface TokenLayer { id: string; name: string; /** Absolute paths of the config or token files found. */ files: string[]; } /** One component the app built itself that the kit appears to already provide. */ export interface HandRoll { /** Absolute path of the file that hand-rolls it. */ path: string; /** Repo-relative, for titles and fingerprints that must survive a move of the checkout. */ relPath: string; /** The exported component's name, when one could be read. */ symbol: string | null; /** Which primitive elements gave it away. */ elements: string[]; /** The kit export it most likely duplicates, when one is a plausible match. */ candidate: string | null; /** Line number of the declaration, 1-based, for the finding to point at. */ line: number; /** * Which oracle found it. * * This is not bookkeeping. A finding is closed by re-running the thing that * filed it, and the two oracles here do not see the same defects: the scan * cannot see a hand-roll in a file that imports the kit, and the skill is a * model call that is not spent on every `verify-fix`. Ruling a skill-found * finding by re-running the scan would clear it the first time anybody asked, * without anything having been fixed. */ foundBy: "scan" | "skill"; /** The skill's own account of what this component is, when a skill found it. */ note?: string; } export interface DesignInventory { /** Schema version, so a stale cache is discarded rather than misread. */ schema: 2; /** When it was built. */ at: string; /** The project this describes, for the same reason the backlog carries it. */ project: string; /** Empty when the project has no design system: a real and common answer. */ kits: DetectedKit[]; tokens: TokenLayer[]; /** Directories holding the application's own screens and features. */ appRoots: string[]; /** Suspected hand-rolled duplicates of kit components. */ handRolls: HandRoll[]; /** How many import statements resolved to a kit, against how many were UI-ish. */ adoption: { fromKit: number; total: number; } | null; /** Anything the scan could not settle, said plainly rather than guessed at. */ notes: string[]; } /** The primary kit: the one a fix is aimed at when there is a choice. */ export declare function primaryKit(inv: DesignInventory): DetectedKit | null; export declare function hasDesignSystem(inv: DesignInventory | null): boolean; export declare function inventoryPath(resolved: ResolvedConfig): string; export declare function saveInventory(resolved: ResolvedConfig, inv: DesignInventory): Promise; /** * The cached inventory, or null when there is none or it is from an older * schema. A stale cache is dropped rather than migrated: it is rebuilt from the * repository in under a second, so carrying migration code for it would cost * more than it saves. */ export declare function loadInventory(resolved: ResolvedConfig): Promise;