/** * The subtractive half of `/learn`: which lines in a context file describe * things that no longer exist? * * The mining pipeline can only ever propose additions. Nothing in it moves the * always-loaded token surface down, so a context file accumulates: a rule * naming a deleted workflow, a command that was removed, a file that moved two * refactors ago. Those lines cost tokens on every request forever and are worse * than useless, because the agent believes them. * * This is deliberately deterministic — no model call, no cache, no state. It * reads the context files already in force, pulls out the referents they name * in backticks, and asks the filesystem. That makes it instant and free, which * is what lets it be the half you run most often. * * Precision is bought with exclusions rather than cleverness, because a noisy * audit is one nobody reads. Three rules do most of the work: * * - **Only path-like referents with a separator.** A bare `auth.json` could be * anywhere or nowhere; `docs/providers.md` is a claim about this repo. * - **Resolve against every package root, not just the repo root.** A monorepo * names `src/cli/args.ts` relative to the package being discussed, and * checking only the repo root reports the entire contributing guide as stale. * - **Skip lines that assert absence.** "these are all gone", "e.g. * `bedrock-utils.ts`", "create `foo.ts`" legitimately name files that do not * exist. Deciding this in general is a judgement call; a short vocabulary of * assertive forms catches the cases that occur in practice. * * What is left is a short list where a wrong entry costs one glance and a right * one costs a line of always-loaded context. That asymmetry is the reason the * remaining false positives are acceptable and silent misses are not. */ /** One referent that did not resolve, with the line that claimed it. */ export interface StaleReference { /** Context file the claim lives in. */ file: string; /** 1-based line number. */ line: number; /** The line, trimmed — what the reader would delete or fix. */ lineText: string; /** The referent that could not be found. */ referent: string; kind: "path" | "script"; /** Rough token cost of the line, so the value of deleting it is visible. */ tokens: number; } /** Why a referent was not checked. Reported as counts so the audit's reach is visible. */ export interface AuditSkips { /** Contains a placeholder or a glob, e.g. an angle-bracket stand-in or a star. */ placeholder: number; /** Home-relative or absolute: a runtime location, not a repo artifact. */ runtime: number; /** A URL, or a git ref like `origin/main`. */ external: number; /** No path separator, so the claim is not about a specific location. */ ambiguous: number; /** The line asserts the referent is absent, optional, or to be created. */ assertsAbsence: number; } export interface AuditReport { /** Context files audited, with their recurring cost. */ files: Array<{ path: string; tokens: number; }>; /** Context files skipped because they live outside the working tree. */ skippedFiles: string[]; /** Referents actually resolved against the filesystem. */ checked: number; skipped: AuditSkips; stale: StaleReference[]; /** Directories referents were resolved against, nearest first. */ roots: string[]; } /** * Directories a relative referent may be resolved against. * * The repo root alone is not enough: a monorepo's contributing notes name * `src/cli/args.ts` meaning "inside the package under discussion", and resolving * that only from the root reports every such line as stale. Every directory * holding a `package.json` is therefore a root, nearest-shallowest first. */ export declare function resolutionRoots(base: string): string[]; type Classification = { kind: "path"; } | { kind: "script"; script: string; } | { kind: "skip"; reason: keyof AuditSkips; }; /** * Decide whether a backticked token is a checkable claim about this repo. * * Ordering matters: the skip reasons are reported as counts, and a token that * matches several should be attributed to the most specific one, so a * placeholder is a placeholder rather than "ambiguous". */ export declare function classifyReferent(token: string): Classification; /** * The project a referent is resolved against: the nearest ancestor holding a * `.git`, or `cwd` when there is none. * * Not `cwd` itself. Context files are collected by walking up from `cwd`, so in * a monorepo the repo's `AGENTS.md` sits *above* the package you are working * in — and running from a package root is the normal case, not the exception. * Anchoring on `cwd` meant the file with all the claims in it was declared "not * in this working tree" and skipped, so the audit passed by checking nothing. */ export declare function findProjectRoot(cwd: string): string; export interface AuditOptions { cwd: string; /** Context files in force, as loaded for the system prompt. */ files: Array<{ path: string; tokens?: number; }>; } /** * Check every referent named by the repo-scope context files. * * Files outside the project are listed but not audited: a rule in * `~/.agents/AGENTS.md` naming `src/index.ts` is a claim about whichever repo * it was written for, and resolving it here would report another project's * rules as broken. * * File contents are re-read from disk rather than taken from the loader, which * truncates oversized files for the prompt — auditing the truncation would * silently stop checking exactly the files most likely to have gone stale. */ export declare function auditContextFiles(options: AuditOptions): AuditReport; /** Total recurring cost of the lines the audit flagged. */ export declare function staleTokens(report: AuditReport): number; export {}; //# sourceMappingURL=audit.d.ts.map