/** The umbrella package every consumer pins. One `catalog:` entry pins its children in lockstep. */ export declare const UMBRELLA_PACKAGE = "@webpieces/nx-webpieces-rules"; /** * ONE tree's answer to "which @webpieces does this tree declare, and which does it have installed". * Data-only (per CLAUDE.md, classes for data). `null` means "could not be determined" — which is NOT * the same as disagreeing, and every caller must treat it as "no opinion", never as skew. */ export declare class TreeVersions { readonly root: string; /** The catalog pin in this tree's `pnpm-workspace.yaml`. Tracked in git, so it is per-branch. */ readonly pinned: string | null; /** The version actually installed under this tree's own `node_modules`, when it has one. */ readonly installed: string | null; constructor(root: string, /** The catalog pin in this tree's `pnpm-workspace.yaml`. Tracked in git, so it is per-branch. */ pinned: string | null, /** The version actually installed under this tree's own `node_modules`, when it has one. */ installed: string | null); } /** * The 3-or-4 webpieces versions in play when a worktree is involved, and whether they agree. * * THREE always — main pin, worktree pin, main install — and a FOURTH when the worktree has its own * `node_modules`, which happens the moment anyone runs `pnpm add ` in it. That fourth is the * uncommon one, and it is NOT optional to check: nx, vitest and the eslint plugin all run IN that tree * and load THAT copy. (It does not decide who *judges* the tree — with the guard hooks registered * absolutely, the judging binary is always the main tree's — but it decides who *builds, lints and * tests* it, and nothing else looks at it.) */ export declare class VersionQuartet { readonly main: TreeVersions; readonly worktree: TreeVersions; constructor(main: TreeVersions, worktree: TreeVersions); /** Every version that was actually readable, deduped — the set that must have exactly one member. */ get distinct(): readonly string[]; /** * True when every version we could read agrees. * * FAILS OPEN on purpose: if nothing could be read (`distinct` is empty) this is `true`. A guard that * cannot measure must not block — the repo's worst incidents are guards that fired on a state they * could not diagnose, leaving an agent with no reachable cure. */ get inSync(): boolean; } /** * Reads the webpieces versions a tree declares and installs. * * WHY A DEDICATED READER rather than reusing the sh drift guard's scraping: that one compares a pin to * an install WITHIN ONE TREE, which is a different question. This is the CROSS-TREE generalisation, and * it is the only thing that can catch the case the absolute-registration design deliberately accepts — * a worktree being judged by the main tree's release while its own manifest asks for another. * * Every read is best-effort and returns `null` rather than throwing: this runs on the hook's BLOCKING * path, so an unreadable file must degrade to "no opinion", never to a fault. */ export declare class WebpiecesVersions { private readonly byRoot; /** Both trees' versions, ready to compare. */ quartet(mainRoot: string, worktreeRoot: string): VersionQuartet; /** One tree's declared + installed versions, memoized. */ forTree(root: string): TreeVersions; /** * The catalog pin, scraped from `pnpm-workspace.yaml`. * * Deliberately a narrow scraper rather than a YAML parser: this module must stay dependency-free (it * loads on the hook path, where a broken tree is exactly the case that matters). A RANGE (`^`, `~`, * `workspace:*`) is returned as null, not as a version: a range cannot be compared for equality, and * treating it as skew would block every consumer who pins loosely. * * ANCHORS AND ALIASES ARE NOT OPTIONAL TO SUPPORT. The scraper originally assumed one shape — * `'@webpieces/nx-webpieces-rules': 0.4.616` — and a consumer repo that keeps the whole `@webpieces` * family in lockstep the obvious way writes the version ONCE and aliases it: * * catalog: * '@webpieces/core-context': &wp 0.4.634 * '@webpieces/nx-webpieces-rules': *wp * * There the umbrella's own value is `*wp`, which does not start with a digit, so the pin read as * null and the whole TRINARY compare silently degraded to installed-vs-installed — the guard's third * leg gone with no error, on exactly the repos that pin most carefully. So both halves are resolved * here: a `&name` anchor DEFINED on the umbrella's line is stepped over, and a `*name` alias is * looked up against the anchor definition anywhere in the file. */ private readPin; /** One catalog value — literal, `&anchor literal`, or `*alias` — reduced to a plain version or null. */ private resolveValue; /** The version under this tree's OWN node_modules, or null when it has none (the normal worktree). */ private readInstalled; private readText; /** * Every OTHER linked worktree of this repo, so a block can name the ones that are ALSO skewed. * * A skew is never a two-tree problem: if worktree A is aligned and B is not, the agents working in B * are already mis-governed and nothing has told them. Best-effort — an empty answer means "could not * enumerate", and callers must never read that as "there are no other worktrees". */ otherWorktrees(mainRoot: string, exclude: string): readonly string[]; }