vigiles 13.0.0 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,6 +37,12 @@ exports.buildRuleInventory = buildRuleInventory;
37
37
  * ESLint-only today — Ruff/Clippy/Pylint/RuboCop/Stylelint entries append here
38
38
  * with their own `linter` + rule-name keywords, no code change.
39
39
  */
40
+ // SCOPE (2026-07-14): this hand-curated list is a small high-precision FAST-PATH
41
+ // (alias enrichment for the most common rules), NOT the strategy. The strategy is
42
+ // the DYNAMIC available-rule catalog — enumerate the rules the repo's linter
43
+ // ACTUALLY has (spike: 702 for this repo vs ~23 here) and match prose against
44
+ // THAT, own-repo/consented since it executes the linter. Do NOT keep growing this
45
+ // by hand. See research/rule-compiler-multilang-design.md §0.
40
46
  exports.INTENT_MAP = [
41
47
  {
42
48
  intent: "no console.log / use the logger",
@@ -196,6 +202,155 @@ exports.INTENT_MAP = [
196
202
  rule: "no-warning-comments",
197
203
  configFix: '"no-warning-comments": ["error", {"terms": ["todo", "fixme"], "location": "anywhere"}]',
198
204
  },
205
+ // Grounded in the OSS-corpus sweep — rules real AGENTS.md files actually NAME
206
+ // (cloudflare/workers-sdk names all three inline). Keyword set is rule-name /
207
+ // code-shaped only, so it fires when the doc names the rule, never on prose.
208
+ {
209
+ intent: "require curly braces for control flow",
210
+ linter: "eslint",
211
+ keywords: ["curly", "curly braces"],
212
+ rule: "curly",
213
+ configFix: '"curly": ["error", "all"]',
214
+ },
215
+ {
216
+ intent: "use import type for type-only imports",
217
+ linter: "eslint",
218
+ keywords: [
219
+ "consistent-type-imports",
220
+ "@typescript-eslint/consistent-type-imports",
221
+ ],
222
+ rule: "@typescript-eslint/consistent-type-imports",
223
+ configFix: '"@typescript-eslint/consistent-type-imports": "error"',
224
+ },
225
+ {
226
+ intent: "no focused / .only tests committed",
227
+ linter: "eslint",
228
+ keywords: [
229
+ "no-only-tests",
230
+ "no-focused-tests",
231
+ "describe.only",
232
+ "it.only",
233
+ "test.only",
234
+ ],
235
+ rule: "no-only-tests/no-only-tests",
236
+ configFix: '"no-only-tests/no-only-tests": "error"',
237
+ },
238
+ // --- Pylint (Python) — routing basics. These feed classify() (routing → reuse);
239
+ // buildRuleInventory is gated to eslint (see below) because pylint is
240
+ // ON-BY-DEFAULT (deny-list), so the eslint-shaped config-state check would
241
+ // MISLABEL it (a symbol in `disable=` reads as "in-config", an absent one as
242
+ // "enable it"). Accurate pylint enabled-state needs the inverted-polarity
243
+ // ConfigProbe (research/rule-compiler-multilang-design.md §3), deferred —
244
+ // classify() needs NO enabled-state, so pylint prose still routes honestly.
245
+ // Keywords are code-shaped symbols + Python-UNAMBIGUOUS compounds (singular AND
246
+ // plural, since matchesWholeToken is boundary-exact); bare ambiguous words
247
+ // (`snake_case` — Rust/Ruby too, `import *` — JS `import * as`, "unused imports"
248
+ // — collides with eslint) are deliberately EXCLUDED to avoid cross-language FPs.
249
+ {
250
+ intent: "no bare except (Python)",
251
+ linter: "pylint",
252
+ keywords: ["bare-except", "bare except", "W0702"],
253
+ rule: "bare-except",
254
+ configFix: "pylint enables bare-except (W0702) by default; keep it out of the disable list",
255
+ },
256
+ {
257
+ intent: "no broad exception catch (Python)",
258
+ linter: "pylint",
259
+ keywords: [
260
+ "broad-exception-caught",
261
+ "broad except",
262
+ "broad exception",
263
+ "W0718",
264
+ ],
265
+ rule: "broad-exception-caught",
266
+ configFix: "pylint enables broad-exception-caught (W0718) by default; keep it out of the disable list",
267
+ },
268
+ {
269
+ intent: "require docstrings (Python)",
270
+ linter: "pylint",
271
+ // Bare "docstring"/"docstrings" removed — it over-fires on docstring
272
+ // CONTENT/STYLE rules (the dogfood caught langchain's "docstring warnings" /
273
+ // "backticks in docstrings"). Presence ("add docstrings", "docstrings for
274
+ // each") is handled by the PATTERN_RULE_MAP docstring-presence pattern in
275
+ // rule-routing.ts; only the rule SYMBOL matches here.
276
+ keywords: ["missing-docstring", "missing-function-docstring", "C0116"],
277
+ rule: "missing-function-docstring",
278
+ configFix: "pylint enables missing-function-docstring (C0116) by default; keep it out of the disable list",
279
+ },
280
+ {
281
+ intent: "no mutable default arguments (Python)",
282
+ linter: "pylint",
283
+ keywords: [
284
+ "dangerous-default-value",
285
+ "mutable default",
286
+ "mutable default argument",
287
+ "mutable default arguments",
288
+ "W0102",
289
+ ],
290
+ rule: "dangerous-default-value",
291
+ configFix: "pylint enables dangerous-default-value (W0102) by default; keep it out of the disable list",
292
+ },
293
+ {
294
+ intent: "prefer f-strings (Python)",
295
+ linter: "pylint",
296
+ keywords: ["f-string", "f-strings", "consider-using-f-string", "C0209"],
297
+ rule: "consider-using-f-string",
298
+ configFix: "pylint enables consider-using-f-string (C0209) by default; keep it out of the disable list",
299
+ },
300
+ {
301
+ intent: "consistent naming (Python)",
302
+ linter: "pylint",
303
+ keywords: ["invalid-name", "C0103"],
304
+ rule: "invalid-name",
305
+ configFix: "pylint enables invalid-name (C0103) by default; set naming-style in [tool.pylint], keep it out of disable",
306
+ },
307
+ {
308
+ intent: "limit function arguments (Python)",
309
+ linter: "pylint",
310
+ keywords: ["too-many-arguments", "R0913"],
311
+ rule: "too-many-arguments",
312
+ configFix: "pylint enables too-many-arguments (R0913) by default; set max-args in [tool.pylint]",
313
+ },
314
+ {
315
+ intent: "limit function length (Python)",
316
+ linter: "pylint",
317
+ keywords: ["too-many-statements", "R0915"],
318
+ rule: "too-many-statements",
319
+ configFix: "pylint enables too-many-statements (R0915) by default; set max-statements in [tool.pylint]",
320
+ },
321
+ {
322
+ intent: "no wildcard imports (Python)",
323
+ linter: "pylint",
324
+ keywords: [
325
+ "wildcard-import",
326
+ "wildcard import",
327
+ "wildcard imports",
328
+ "W0401",
329
+ ],
330
+ rule: "wildcard-import",
331
+ configFix: "pylint enables wildcard-import (W0401) by default; keep it out of the disable list",
332
+ },
333
+ {
334
+ intent: "no global statement (Python)",
335
+ linter: "pylint",
336
+ keywords: ["global-statement", "global statement", "W0603"],
337
+ rule: "global-statement",
338
+ configFix: "pylint enables global-statement (W0603) by default; keep it out of the disable list",
339
+ },
340
+ {
341
+ intent: "max line length (Python)",
342
+ linter: "pylint",
343
+ keywords: ["line-too-long", "C0301"],
344
+ rule: "line-too-long",
345
+ configFix: "pylint enables line-too-long (C0301) by default; set max-line-length in [tool.pylint]",
346
+ },
347
+ {
348
+ intent: "no unused imports (Python)",
349
+ linter: "pylint",
350
+ keywords: ["unused-import", "W0611"],
351
+ rule: "unused-import",
352
+ configFix: "pylint enables unused-import (W0611) by default; keep it out of the disable list",
353
+ },
199
354
  ];
200
355
  /** Escape a keyword for use inside a RegExp. */
201
356
  function escapeRe(s) {
@@ -206,9 +361,15 @@ function escapeRe(s) {
206
361
  * non-`[\w/@.-]` character on each side (so `no-console` matches in
207
362
  * `` `no-console` `` and `enforce no-console;` but `no-console-x` does not,
208
363
  * and prose containing the substring elsewhere never trips it).
364
+ *
365
+ * The TRAILING boundary is a lookahead, not a consuming class, so a keyword at
366
+ * SENTENCE END ("No wildcard imports.") matches: a `.` is allowed unless it
367
+ * CONTINUES a code token (`.log` in `console.log`), which still blocks a partial
368
+ * match. `(?![\w/@-])` rejects a word/`/`/`@`/`-` continuation; `(?!\.[\w/@-])`
369
+ * rejects a dotted continuation but permits a trailing sentence `.`.
209
370
  */
210
371
  function matchesWholeToken(text, keyword) {
211
- const re = new RegExp(`(^|[^\\w/@.-])${escapeRe(keyword)}([^\\w/@.-]|$)`, "i");
372
+ const re = new RegExp(`(^|[^\\w/@.-])${escapeRe(keyword)}(?![\\w/@-])(?!\\.[\\w/@-])`, "i");
212
373
  return re.test(text);
213
374
  }
214
375
  /**
@@ -292,6 +453,14 @@ function buildRuleInventory(instructionText, configText, options = {}) {
292
453
  for (const m of exports.INTENT_MAP) {
293
454
  if (linters && !linters.includes(m.linter))
294
455
  continue;
456
+ // ROUTE-ONLY for non-eslint linters: the config-state check below
457
+ // (ruleSetOff/ruleInConfig) is eslint-config-shaped and would MISLABEL a
458
+ // pylint rule, which is ON-BY-DEFAULT (a symbol in `disable=` reads as
459
+ // "in-config"; an absent one as "enable it" — both inverted). The routing
460
+ // preview (classify) still reuses these; accurate pylint enabled-state waits
461
+ // on the inverted-polarity ConfigProbe (design doc §3). Don't cry wolf.
462
+ if (m.linter !== "eslint")
463
+ continue;
295
464
  const matched = m.keywords.find((kw) => matchesWholeToken(instructionText, kw));
296
465
  if (!matched)
297
466
  continue;
@@ -1,7 +1,8 @@
1
1
  import { type LinterName } from "./rule-inventory.js";
2
+ import type { RuleCatalog } from "./core/rule-catalog.js";
2
3
  /** How a routed rule would be enforced (a MECHANISM ladder, not a 1-10 score). */
3
- export type RuleCategory = "reuse" | "hook" | "semantic" | "unrouted";
4
- export type RuleMechanism = "config-line" | "hook" | "prose" | "compile";
4
+ export type RuleCategory = "reuse" | "hook" | "meta" | "semantic" | "unrouted";
5
+ export type RuleMechanism = "config-line" | "hook" | "prose" | "synthesize";
5
6
  /** One segmented, deterministically-routed rule with provenance. */
6
7
  export interface RoutedRule {
7
8
  /** Normalized atomic rule text (from the segmenter). */
@@ -19,6 +20,13 @@ export interface RoutedRule {
19
20
  readonly rule?: string;
20
21
  /** reuse only: the linter that rule belongs to. */
21
22
  readonly linter?: LinterName;
23
+ /** reuse via the DYNAMIC catalog only: whether the rule is currently enabled in
24
+ * the repo's config (a disabled match is the "documented but OFF" nudge). */
25
+ readonly enabled?: boolean;
26
+ /** How this rule was found: `"marker"` = an explicit `**Enforced by:**` /
27
+ * `**Guard:**` / `**Guidance only**` marker (definitive, zero-heuristic — a
28
+ * compiled/marked doc); `"heuristic"` = the Tier-A segmenter. Absent ⇒ heuristic. */
29
+ readonly source?: "marker" | "heuristic";
22
30
  }
23
31
  export interface RuleRouting {
24
32
  /** How many atomic rules were routed (after the confidence filter). */
@@ -36,6 +44,14 @@ export interface RouteOptions {
36
44
  * `"medium"` to include both.
37
45
  */
38
46
  readonly minConfidence?: "high" | "medium";
47
+ /**
48
+ * The repo's DYNAMIC available-rule catalog (from `enumerateEslintCatalog`).
49
+ * When provided (OWN-REPO / consented — it executes the linter), a bullet that
50
+ * NAMES any of the repo's real rules routes to `reuse` with its enabled state —
51
+ * not just the ~23 static `INTENT_MAP` aliases. Absent = foreign-safe default
52
+ * (static map only, no execution).
53
+ */
54
+ readonly availableRules?: RuleCatalog;
39
55
  }
40
56
  /**
41
57
  * Segment the instruction file and route every atomic rule deterministically.
@@ -43,4 +59,11 @@ export interface RouteOptions {
43
59
  * source path for provenance). Returns per-category counts + the routed rules.
44
60
  */
45
61
  export declare function routeRules(instructionText: string, file?: string, options?: RouteOptions): RuleRouting;
62
+ /**
63
+ * Merge per-file routings into one. Each instruction source is routed SEPARATELY
64
+ * (so every rule keeps its OWN file path + line numbers — concatenating first
65
+ * would corrupt the provenance the preview promises), then folded here: rules
66
+ * concatenated, counts + segmented summed. Pure. `[]` → an empty routing.
67
+ */
68
+ export declare function mergeRoutings(routings: readonly RuleRouting[]): RuleRouting;
46
69
  //# sourceMappingURL=rule-routing.d.ts.map