/** * Rule-inventory — the deterministic, foreign-safe teaser surface of the * `audit` rule-compile tier (design: `research/audit-rule-compile-tier.md`). * * Finds prose rules in a `CLAUDE.md` / `AGENTS.md` that map to an off-the-shelf * lint rule, and whether that rule already appears in the repo's lint config. * The remedy for a documented-but-unconfigured intent is a one-line config * change, not synthesis — so this is a cheap, high-value nudge. * * NO model. NO config execution (textual grep only — never resolves/executes * `eslint.config.js`, which would be the RCE path). HIGH PRECISION by * construction: only rule-name / code-token-shaped keywords are matched, and * only as whole tokens. Bare prose words are excluded on purpose — a raw * keyword match (`token`, `!`, `await`, `secret`, `silently`, …) sprays false * positives on real instruction files (measured: 107 raw hits over 4 real * CLAUDE.md files, ~all garbage). The model-driven analysis (extract → classify * → compile → gate → run) lives in the OPT-IN tier, not here. * * MULTI-LINTER by shape, ESLint-first by data. The matcher is linter-agnostic; * each mapping is keyed by linter, so adding Ruff / Clippy / Pylint / RuboCop / * Stylelint is additive DATA (a curation task), not a refactor. The OPT-IN tier * should resolve "is this rule enabled" via vigiles's existing multi-linter * `checkLinterRule` engine (which execs the config) — kept out of this * exec-free, foreign-safe surface on purpose. */ /** Linters vigiles's cross-reference engine already understands. */ export type LinterName = "eslint" | "ruff" | "clippy" | "pylint" | "rubocop" | "stylelint"; /** One prose→rule mapping for a single linter. `keywords` are rule-name/token-shaped. */ export interface IntentMapping { readonly intent: string; readonly linter: LinterName; /** Whole-token, code-shaped triggers only (no bare English words). */ readonly keywords: readonly string[]; /** The off-the-shelf rule that enforces this intent. */ readonly rule: string; /** The one-line config change that turns it on. */ readonly configFix: string; /** True if a `recommended` preset typically enables this rule (so a bare * recommended-extends is evidence it may already be on). */ readonly inRecommended?: boolean; } /** * Curated from agent-rules-compiler's `rule-map.json`, keeping ONLY the * specific (rule-name / code-token) keywords and dropping every bare-word * trigger the FP measurement flagged (`token`, `secret`, `password`, `await`, * `!`, `aria`, `silently`, `prefix`, `complexity`, `barrel`, `cycle`, …). * * ESLint-only today — Ruff/Clippy/Pylint/RuboCop/Stylelint entries append here * with their own `linter` + rule-name keywords, no code change. */ export declare const INTENT_MAP: readonly IntentMapping[]; /** * Whether the mapped rule is visible in the lint config text (textual grep — * imperfect, labelled). `contradiction` is the sharpest state: the harness * documents the rule as a norm, yet the config EXPLICITLY sets it to off/0 — * the docs and the config disagree. */ export type ConfigState = "in-config" | "not-in-config" | "preset-maybe" | "contradiction"; /** One documented-intent → off-the-shelf-rule finding. */ export interface RuleInventoryItem { readonly intent: string; readonly linter: LinterName; /** The rule-name/token that matched in the instruction file. */ readonly matched: string; /** The off-the-shelf rule that enforces it. */ readonly rule: string; /** Whether `rule` appears anywhere in the provided config text. */ readonly configState: ConfigState; /** The one-line config change to enforce it (shown when not in config). */ readonly configFix: string; } /** Options for {@link buildRuleInventory}. */ export interface RuleInventoryOptions { /** * Restrict to the repo's detected linter(s). When omitted, all linters are * considered — safe because the keywords are rule-name-specific, but a caller * that knows the repo is Python-only can pass `["ruff"]` to avoid a stray * cross-language rule-name collision. */ readonly linters?: readonly LinterName[]; } /** * A keyword matches only as a WHOLE token: bounded by start/end or a * non-`[\w/@.-]` character on each side (so `no-console` matches in * `` `no-console` `` and `enforce no-console;` but `no-console-x` does not, * and prose containing the substring elsewhere never trips it). * * The TRAILING boundary is a lookahead, not a consuming class, so a keyword at * SENTENCE END ("No wildcard imports.") matches: a `.` is allowed unless it * CONTINUES a code token (`.log` in `console.log`), which still blocks a partial * match. `(?![\w/@-])` rejects a word/`/`/`@`/`-` continuation; `(?!\.[\w/@-])` * rejects a dotted continuation but permits a trailing sentence `.`. */ export declare function matchesWholeToken(text: string, keyword: string): boolean; export declare function buildRuleInventory(instructionText: string, configText: string, options?: RuleInventoryOptions): RuleInventoryItem[]; //# sourceMappingURL=rule-inventory.d.ts.map