/** * Per-tool load-group token model for `aih report`. * * `scanContextBloat` sums EVERY bootloader (CLAUDE.md + AGENTS.md + GEMINI.md + * …) — but no single tool loads all of them: Claude loads only CLAUDE.md, Cursor * only its `.mdc`, Copilot only its instructions file. The real per-turn cost is * the heaviest single tool's bootloader bundle, not the union. This models that. * * Design lifted from @razroo/isolint's `cost.js groupByTool` (MIT) — but where * isolint GUESSES the file→tool map with regexes, aih reads the authoritative map * it already writes (`CLI_BOOTLOADERS` in bootstrap-ai/canon.ts), so the worst-case * is exact, not estimated. The `ai-coding/**` canon tree is loaded on demand via a * pointer (not auto-injected every turn), so it is reported as a separate bucket. */ import { type Cli } from "../internals/clis.js"; import { type ContextFile, type ScanOptions } from "./bloat.js"; /** One always-loaded-per-turn footprint: the bootloader files a set of tools share. */ export interface LoadGroup { /** Tools that load the same bootloader set (e.g. codex+opencode+zed+kimi → AGENTS.md). */ clis: Cli[]; /** The group's declared bootloader files (whether or not they exist on disk). */ bootloaderPaths: string[]; /** Human label, e.g. "codex, opencode, zed, kimi → AGENTS.md". */ label: string; /** The group's bootloader files that EXIST on disk, sorted by path. */ files: ContextFile[]; tokens: number; bytes: number; /** Present iff ≥1 bootloader file exists. Absent groups are excluded from worst-case. */ present: boolean; } export interface LoadGroupModel { /** All groups, sorted: present first, then by tokens desc, then label. */ groups: LoadGroup[]; /** The heaviest PRESENT group — the real per-turn worst case. */ worst: LoadGroup | null; /** `worst.tokens`, or 0 when nothing is present. */ worstTokens: number; budgetTokens: number; /** worstTokens > budgetTokens — the gate input (NOT the summed total). */ overBudget: boolean; /** Canon tree + non-bootloader files loaded on demand via pointer (informational). */ onDemandFiles: ContextFile[]; onDemandTokens: number; } /** * Build the per-tool load-group model: each tool group's existing bootloader * footprint, the heaviest present group (worst case), and the on-demand canon * bucket. Pure: reads file sizes only (never contents), no network, no spawn. */ export declare function scanLoadGroups(root: string, contextDir: string, budgetTokens: number, opts?: ScanOptions): LoadGroupModel;