/** * adoption-scanner — Measure Eddie adoption across the Brad-Frost-Web GitHub * org, so coverage (what exists) can be distinguished from adoption (what's * used). See #1021. * * DURABILITY: adoption is measured from the GitHub organization, NOT from a * local ~/Sites checkout. A dependency read tied to one personal machine is * fragile — it changes with every new laptop, clone, or stale working copy, * and silently misses projects that aren't checked out. The org is the durable * source of truth: every product lives there regardless of what's on disk. * * METHOD: enumerate the org's active repositories, then for each one walk its * git tree for every package.json (root AND nested, so monorepos like * we-are-here are covered) and detect a `@brad-frost-web/eddie-*` dependency. * We use package.json manifests rather than the dependency-graph SBOM because * the SBOM endpoint 404s on most private org repos (dependency graph isn't * enabled there), whereas the contents API works uniformly. Declared versions * are compared to the latest published versions to surface drift. * * The output is runtime state (it reflects the org at scan time), written to * .eddie-brain/adoption.json and exempt from the CI freshness gate. * * Requires the `gh` CLI, authenticated with read access to the org. */ export interface RepoDrift { declared: string; latest: string; upToDate: boolean; } export interface RepoAdoption { name: string; visibility: 'public' | 'private' | 'internal'; archived: boolean; url: string; defaultBranch: string; pushedAt: string; /** Manifest paths where an Eddie dependency was found. */ manifests: string[]; /** Full Eddie package → declared version range (merged across manifests). */ eddieDependencies: Record; /** Short surfaces used: tokens, components, icons, recipes, charts. */ packages: string[]; /** Whether every declared Eddie dep satisfies the latest published version. */ upToDate: boolean; /** Per-package drift detail. */ drift: Record; } export interface AdoptionSnapshot { scannedAt: string; source: string; org: string; latestVersions: Record; totals: { reposInOrg: number; activeRepos: number; reposAdopting: number; usingTokens: number; usingComponents: number; usingIcons: number; usingRecipes: number; usingCharts: number; upToDate: number; drifted: number; }; adopters: RepoAdoption[]; nonAdopters: string[]; skipped: { name: string; reason: string; }[]; } /** Compare two semver-ish versions, ignoring range prefixes/pre-release tags. */ export declare function semverGte(declared: string, latest: string): boolean; /** * Scan the GitHub org for Eddie adoption and build a repo-level snapshot. * `latestVersions` maps short package name (tokens/components/icons/recipes/ * charts) → latest published version, used for drift detection. */ export declare function scanAdoption(latestVersions: Record, options?: { org?: string; includeArchived?: boolean; onProgress?: (msg: string) => void; }): AdoptionSnapshot; /** Fraction of the previous count below which a total counts as collapsed. */ export declare const SHRINK_RATIO = 0.5; /** * Previous totals below this are too small for a ratio to mean anything * (3 → 1 is noise). A fall to zero trips regardless of this floor. */ export declare const SHRINK_MIN_PREVIOUS = 4; export interface SnapshotTotalsCompared { reposInOrg: number; reposAdopting: number; } export interface SnapshotShrink { /** * True when a total collapsed: it fell below SHRINK_RATIO of a previous value * of at least SHRINK_MIN_PREVIOUS, or it fell to zero from anything above zero. */ shrunk: boolean; /** One human-readable line per tripped total; empty when not shrunk. */ reasons: string[]; previous: SnapshotTotalsCompared | null; next: SnapshotTotalsCompared; } /** * Compare a freshly scanned snapshot against the one on disk. Pure: the caller * decides what to do with the verdict (see applyAdoptionSnapshot). A missing or * unreadable previous snapshot never trips the guard — there is nothing * trustworthy to shrink from. */ export declare function detectSnapshotShrink(previous: Pick | null | undefined, next: Pick): SnapshotShrink; export interface ApplySnapshotResult { /** Whether adoption.json was (re)written this run. */ written: boolean; shrink: SnapshotShrink; /** * True when a file existed at outPath but held no readable snapshot (corrupt, * or a legacy shape without totals) — the guard had nothing to compare against. */ previousUnreadable: boolean; } /** * Write a scanned snapshot to disk unless it collapsed relative to the one * already there. This is the effect layer for `eddie-brain adoption`: nothing * else in the CLI writes adoption.json, so a refusal here leaves the file * byte-for-byte untouched. `force` writes anyway; the result still carries the * verdict so the caller can say so. */ export declare function applyAdoptionSnapshot(outPath: string, snapshot: AdoptionSnapshot, options?: { force?: boolean; }): ApplySnapshotResult; //# sourceMappingURL=adoption-scanner.d.ts.map