vigiles 14.1.0 → 14.2.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.
- package/README.md +16 -13
- package/dist/adapters/claude-code/dialect.d.ts +1 -1
- package/dist/audit-report.template.html +14 -14
- package/dist/cli.js +6 -4
- package/dist/core/rule-catalog.d.ts +1 -1
- package/dist/core/rule-catalog.js +1 -1
- package/dist/instruction-sources.d.ts +1 -1
- package/dist/instruction-sources.js +1 -1
- package/dist/rule-inventory.js +151 -2
- package/dist/rule-routing.d.ts +16 -1
- package/dist/rule-routing.js +172 -138
- package/dist/rule-signals.d.ts +46 -0
- package/dist/rule-signals.js +49 -0
- package/dist/segment.d.ts +16 -5
- package/dist/segment.js +219 -179
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1535,7 +1535,7 @@ function computeRuleInventory(root, instructionFile) {
|
|
|
1535
1535
|
* instruction files PLUS nested subdirectory-memory (`src/CLAUDE.md`,
|
|
1536
1536
|
* `research/CLAUDE.md`, …), skipping fixture/demo/build/test dirs (`isFixturePath`)
|
|
1537
1537
|
* so a repo's real memory is read without the test-fixture noise. `.claude/` rule
|
|
1538
|
-
* sources remain a future source. research/rule-
|
|
1538
|
+
* sources remain a future source. research/rule-enforcer-multilang-design.md §0. */
|
|
1539
1539
|
function gatherInstructionFiles(root, instructionFile) {
|
|
1540
1540
|
const raw = [];
|
|
1541
1541
|
const collect = (rel) => {
|
|
@@ -1696,10 +1696,12 @@ function formatRuleMapSummary(routing) {
|
|
|
1696
1696
|
counts.meta;
|
|
1697
1697
|
if (confident === 0 && possible.length === 0 && skipped.length === 0)
|
|
1698
1698
|
return "";
|
|
1699
|
+
// Lane counts, rendered from the single-source LANE_META (glyph + label).
|
|
1700
|
+
const lane = (c) => `${rule_routing_js_1.LANE_META[c].glyph} ${String(counts[c])} ${rule_routing_js_1.LANE_META[c].label}`;
|
|
1699
1701
|
const lines = [
|
|
1700
|
-
"Rule map — how your prose rules could be enforced (heuristic, precision-first):",
|
|
1701
|
-
`
|
|
1702
|
-
(counts.meta > 0 ? ` ·
|
|
1702
|
+
"Rule map [experimental] — how your prose rules could be enforced (heuristic, precision-first; misses some rules):",
|
|
1703
|
+
` ${lane("reuse")} · ${lane("hook")} · ${lane("unrouted")} · ${lane("semantic")}` +
|
|
1704
|
+
(counts.meta > 0 ? ` · ${lane("meta")}` : ""),
|
|
1703
1705
|
];
|
|
1704
1706
|
if (possible.length > 0)
|
|
1705
1707
|
lines.push(` ? ${String(possible.length)} possible — rule-ish, but below the confidence bar (review these)`);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* / boundaries), of which ~140 are enabled — vs the old static map's ~23. That
|
|
9
9
|
* makes an architecture norm enforceable too (`boundaries/dependencies` is in the
|
|
10
10
|
* catalog), which a static map never captured. See
|
|
11
|
-
* `research/rule-
|
|
11
|
+
* `research/rule-enforcer-multilang-design.md` §0 (the spike this productizes).
|
|
12
12
|
*
|
|
13
13
|
* SAFETY — this EXECUTES the linter. Loading ESLint resolves the repo's real
|
|
14
14
|
* config (which can run plugin/config code), so `enumerateEslintCatalog` is an
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* / boundaries), of which ~140 are enabled — vs the old static map's ~23. That
|
|
10
10
|
* makes an architecture norm enforceable too (`boundaries/dependencies` is in the
|
|
11
11
|
* catalog), which a static map never captured. See
|
|
12
|
-
* `research/rule-
|
|
12
|
+
* `research/rule-enforcer-multilang-design.md` §0 (the spike this productizes).
|
|
13
13
|
*
|
|
14
14
|
* SAFETY — this EXECUTES the linter. Loading ESLint resolves the repo's real
|
|
15
15
|
* config (which can run plugin/config code), so `enumerateEslintCatalog` is an
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* NOISE that would flood the preview. `isFixturePath` is the precision-first
|
|
8
8
|
* discriminator (over-skip a legit `sample-service` before flooding with fixture
|
|
9
9
|
* rules). Pure + unit-tested; the fs discovery/glue lives in cli.ts
|
|
10
|
-
* (`gatherInstructionFiles`). See research/rule-
|
|
10
|
+
* (`gatherInstructionFiles`). See research/rule-enforcer-multilang-design.md §0.
|
|
11
11
|
*/
|
|
12
12
|
/** One instruction file gathered from disk, with its canonical (symlink-resolved)
|
|
13
13
|
* path so a mirror can be detected. */
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* NOISE that would flood the preview. `isFixturePath` is the precision-first
|
|
9
9
|
* discriminator (over-skip a legit `sample-service` before flooding with fixture
|
|
10
10
|
* rules). Pure + unit-tested; the fs discovery/glue lives in cli.ts
|
|
11
|
-
* (`gatherInstructionFiles`). See research/rule-
|
|
11
|
+
* (`gatherInstructionFiles`). See research/rule-enforcer-multilang-design.md §0.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
14
|
exports.dedupeInstructionFiles = dedupeInstructionFiles;
|
package/dist/rule-inventory.js
CHANGED
|
@@ -42,7 +42,7 @@ exports.buildRuleInventory = buildRuleInventory;
|
|
|
42
42
|
// the DYNAMIC available-rule catalog — enumerate the rules the repo's linter
|
|
43
43
|
// ACTUALLY has (spike: 702 for this repo vs ~23 here) and match prose against
|
|
44
44
|
// THAT, own-repo/consented since it executes the linter. Do NOT keep growing this
|
|
45
|
-
// by hand. See research/rule-
|
|
45
|
+
// by hand. See research/rule-enforcer-multilang-design.md §0.
|
|
46
46
|
exports.INTENT_MAP = [
|
|
47
47
|
{
|
|
48
48
|
intent: "no console.log / use the logger",
|
|
@@ -235,12 +235,93 @@ exports.INTENT_MAP = [
|
|
|
235
235
|
rule: "no-only-tests/no-only-tests",
|
|
236
236
|
configFix: '"no-only-tests/no-only-tests": "error"',
|
|
237
237
|
},
|
|
238
|
+
// --- Ruff (Python) — the modern default (allow-list, like ESLint). These are
|
|
239
|
+
// the NET-NEW intents Pylint does NOT already cover (imports-inside-functions,
|
|
240
|
+
// annotations, logger.exception, print, import-sorting). The SHARED Python
|
|
241
|
+
// intents (bare-except, broad-except, mutable-default, wildcard, global,
|
|
242
|
+
// too-many-*, line-length, unused, f-strings, docstrings) stay with the Pylint
|
|
243
|
+
// entries below — one intent, one linter, so keyword disjointness holds (the
|
|
244
|
+
// `rule-routing-dogfood` invariant test) and prose never double-routes. Whether
|
|
245
|
+
// shared Python NL prose should later PREFER Ruff over Pylint is an open
|
|
246
|
+
// ownership decision, deferred. ROUTE-ONLY like Pylint: config-state
|
|
247
|
+
// (buildRuleInventory) stays gated to eslint — Ruff's select/ignore config shape
|
|
248
|
+
// differs from the eslint rules-map, so the eslint-shaped check would MISLABEL;
|
|
249
|
+
// accurate Ruff enabled-state is the catalog/ConfigProbe follow-up
|
|
250
|
+
// (rule-enforcer-multilang-design.md §3). Every CODE was verified to exist
|
|
251
|
+
// against ruff 0.15.8 (`ruff rule <CODE>`); keywords are Python-diagnostic (no
|
|
252
|
+
// cross-language phrase that would mis-attribute the linter in a JS/TS doc).
|
|
253
|
+
{
|
|
254
|
+
intent: "no imports inside functions (Python)",
|
|
255
|
+
linter: "ruff",
|
|
256
|
+
keywords: [
|
|
257
|
+
"PLC0415",
|
|
258
|
+
"inline import",
|
|
259
|
+
"inline imports",
|
|
260
|
+
"imports inside functions",
|
|
261
|
+
"import inside a function",
|
|
262
|
+
"import inside functions",
|
|
263
|
+
],
|
|
264
|
+
rule: "PLC0415",
|
|
265
|
+
configFix: 'add "PLC0415" (import-outside-top-level) to [tool.ruff.lint] select',
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
// E402 flags module-level code BEFORE the imports; the "no imports inside
|
|
269
|
+
// functions" reading is PLC0415 above. Code-only keyword ON PURPOSE: the
|
|
270
|
+
// phrase "imports at the top" is ambiguous between the two AND collides with
|
|
271
|
+
// ESLint `import/first` in a JS doc, so it is NOT a keyword here (a missing
|
|
272
|
+
// route is recoverable; a wrong "enforceable" claim is not).
|
|
273
|
+
intent: "no module code before imports (Python)",
|
|
274
|
+
linter: "ruff",
|
|
275
|
+
keywords: ["E402"],
|
|
276
|
+
rule: "E402",
|
|
277
|
+
configFix: 'add "E402" (module-import-not-at-top-of-file) to [tool.ruff.lint] select',
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
intent: "require argument type annotations (Python)",
|
|
281
|
+
linter: "ruff",
|
|
282
|
+
keywords: [
|
|
283
|
+
"ANN001",
|
|
284
|
+
"type hints required",
|
|
285
|
+
"type hints are required",
|
|
286
|
+
"require type hints",
|
|
287
|
+
],
|
|
288
|
+
rule: "ANN001",
|
|
289
|
+
configFix: 'add "ANN001" (missing-type-function-argument) to [tool.ruff.lint] select',
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
intent: "require return type annotations (Python)",
|
|
293
|
+
linter: "ruff",
|
|
294
|
+
keywords: ["ANN201"],
|
|
295
|
+
rule: "ANN201",
|
|
296
|
+
configFix: 'add "ANN201" (missing-return-type-undocumented-public-function) to [tool.ruff.lint] select',
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
intent: "use logger.exception in handlers (Python)",
|
|
300
|
+
linter: "ruff",
|
|
301
|
+
keywords: ["TRY400", "logger.exception", "logging.exception"],
|
|
302
|
+
rule: "TRY400",
|
|
303
|
+
configFix: 'add "TRY400" (error-instead-of-exception) to [tool.ruff.lint] select',
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
intent: "no print statements — use logging (Python)",
|
|
307
|
+
linter: "ruff",
|
|
308
|
+
keywords: ["T201", "print statement", "print statements"],
|
|
309
|
+
rule: "T201",
|
|
310
|
+
configFix: 'add "T201" (print) to [tool.ruff.lint] select',
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
intent: "keep imports sorted (Python)",
|
|
314
|
+
linter: "ruff",
|
|
315
|
+
keywords: ["I001", "sorted imports", "isort"],
|
|
316
|
+
rule: "I001",
|
|
317
|
+
configFix: 'add "I001" (unsorted-imports) to [tool.ruff.lint] select',
|
|
318
|
+
},
|
|
238
319
|
// --- Pylint (Python) — routing basics. These feed classify() (routing → reuse);
|
|
239
320
|
// buildRuleInventory is gated to eslint (see below) because pylint is
|
|
240
321
|
// ON-BY-DEFAULT (deny-list), so the eslint-shaped config-state check would
|
|
241
322
|
// MISLABEL it (a symbol in `disable=` reads as "in-config", an absent one as
|
|
242
323
|
// "enable it"). Accurate pylint enabled-state needs the inverted-polarity
|
|
243
|
-
// ConfigProbe (research/rule-
|
|
324
|
+
// ConfigProbe (research/rule-enforcer-multilang-design.md §3), deferred —
|
|
244
325
|
// classify() needs NO enabled-state, so pylint prose still routes honestly.
|
|
245
326
|
// Keywords are code-shaped symbols + Python-UNAMBIGUOUS compounds (singular AND
|
|
246
327
|
// plural, since matchesWholeToken is boundary-exact); bare ambiguous words
|
|
@@ -351,6 +432,74 @@ exports.INTENT_MAP = [
|
|
|
351
432
|
rule: "unused-import",
|
|
352
433
|
configFix: "pylint enables unused-import (W0611) by default; keep it out of the disable list",
|
|
353
434
|
},
|
|
435
|
+
// --- Clippy (Rust) — routing basics (backlog #3: the 0%→ win for Rust
|
|
436
|
+
// rulebooks; codex/ghostty routed 0% purely because clippy was unmapped, yet
|
|
437
|
+
// codex literally says "avoid patterns that require `panic!`, `unreachable!`,
|
|
438
|
+
// or `.unwrap()`" and "make `match` exhaustive … avoid wildcard arms"). Every
|
|
439
|
+
// lint is a real clippy restriction lint. Route-only, like ruff/pylint —
|
|
440
|
+
// config-state stays eslint-gated (L639) because clippy's lint levels live in
|
|
441
|
+
// Cargo.toml `[lints.clippy]` / crate attrs, a different shape (accurate
|
|
442
|
+
// enabled-state is the ConfigProbe follow-up, rule-enforcer-multilang-design
|
|
443
|
+
// §3). Keywords are Rust-UNAMBIGUOUS — macro `!` forms, `.method` call forms,
|
|
444
|
+
// `clippy::` symbols, and Rust-only compounds ("wildcard arm(s)", NOT bare
|
|
445
|
+
// "wildcard" which collides with pylint `wildcard-import`) — so a Python/JS
|
|
446
|
+
// doc never mis-attributes clippy (the cross-language-FP test guards this).
|
|
447
|
+
{
|
|
448
|
+
intent: "no .unwrap() (Rust)",
|
|
449
|
+
linter: "clippy",
|
|
450
|
+
keywords: ["clippy::unwrap_used", "unwrap_used", ".unwrap"],
|
|
451
|
+
rule: "clippy::unwrap_used",
|
|
452
|
+
configFix: 'set unwrap_used = "warn" in [lints.clippy] (Cargo.toml), or #![warn(clippy::unwrap_used)]',
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
intent: "no .expect() (Rust)",
|
|
456
|
+
linter: "clippy",
|
|
457
|
+
keywords: ["clippy::expect_used", "expect_used", ".expect"],
|
|
458
|
+
rule: "clippy::expect_used",
|
|
459
|
+
configFix: 'set expect_used = "warn" in [lints.clippy] (Cargo.toml), or #![warn(clippy::expect_used)]',
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
intent: "no panic! (Rust)",
|
|
463
|
+
linter: "clippy",
|
|
464
|
+
keywords: ["clippy::panic", "panic!"],
|
|
465
|
+
rule: "clippy::panic",
|
|
466
|
+
configFix: 'set panic = "warn" in [lints.clippy] (Cargo.toml), or #![warn(clippy::panic)]',
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
intent: "no unreachable! (Rust)",
|
|
470
|
+
linter: "clippy",
|
|
471
|
+
keywords: ["clippy::unreachable", "unreachable!"],
|
|
472
|
+
rule: "clippy::unreachable",
|
|
473
|
+
configFix: 'set unreachable = "warn" in [lints.clippy] (Cargo.toml), or #![warn(clippy::unreachable)]',
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
intent: "no todo! (Rust)",
|
|
477
|
+
linter: "clippy",
|
|
478
|
+
keywords: ["clippy::todo", "todo!"],
|
|
479
|
+
rule: "clippy::todo",
|
|
480
|
+
configFix: 'set todo = "warn" in [lints.clippy] (Cargo.toml), or #![warn(clippy::todo)]',
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
intent: "no dbg! (Rust)",
|
|
484
|
+
linter: "clippy",
|
|
485
|
+
keywords: ["clippy::dbg_macro", "dbg!"],
|
|
486
|
+
rule: "clippy::dbg_macro",
|
|
487
|
+
configFix: 'set dbg_macro = "warn" in [lints.clippy] (Cargo.toml), or #![warn(clippy::dbg_macro)]',
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
intent: "exhaustive match — no wildcard enum arm (Rust)",
|
|
491
|
+
linter: "clippy",
|
|
492
|
+
keywords: [
|
|
493
|
+
"clippy::wildcard_enum_match_arm",
|
|
494
|
+
"wildcard_enum_match_arm",
|
|
495
|
+
"wildcard match arm",
|
|
496
|
+
"wildcard match arms",
|
|
497
|
+
"wildcard arm",
|
|
498
|
+
"wildcard arms",
|
|
499
|
+
],
|
|
500
|
+
rule: "clippy::wildcard_enum_match_arm",
|
|
501
|
+
configFix: 'set wildcard_enum_match_arm = "warn" in [lints.clippy] (Cargo.toml), or #![warn(clippy::wildcard_enum_match_arm)]',
|
|
502
|
+
},
|
|
354
503
|
];
|
|
355
504
|
/** Escape a keyword for use inside a RegExp. */
|
|
356
505
|
function escapeRe(s) {
|
package/dist/rule-routing.d.ts
CHANGED
|
@@ -41,6 +41,21 @@ import type { RuleCatalog } from "./core/rule-catalog.js";
|
|
|
41
41
|
/** How a routed rule would be enforced (a MECHANISM ladder, not a 1-10 score). */
|
|
42
42
|
export type RuleCategory = "reuse" | "hook" | "meta" | "semantic" | "unrouted";
|
|
43
43
|
export type RuleMechanism = "config-line" | "hook" | "prose" | "synthesize";
|
|
44
|
+
/**
|
|
45
|
+
* The user-facing presentation of each routing category — its glyph + lane
|
|
46
|
+
* label. The SINGLE SOURCE the terminal summary reads (and the HTML report
|
|
47
|
+
* mirrors), so the category-name → lane-label mapping lives in one place.
|
|
48
|
+
*
|
|
49
|
+
* NB the type name `unrouted` is a WIRE value (it appears in the versioned
|
|
50
|
+
* `AuditReport` JSON), which is why it isn't renamed to its lane label `custom`;
|
|
51
|
+
* this table is where the human-facing name is resolved. The category meanings
|
|
52
|
+
* are documented in the file header; the mapping is tabled in
|
|
53
|
+
* `research/rule-enforcer-design.md` §4.
|
|
54
|
+
*/
|
|
55
|
+
export declare const LANE_META: Record<RuleCategory, {
|
|
56
|
+
readonly glyph: string;
|
|
57
|
+
readonly label: string;
|
|
58
|
+
}>;
|
|
44
59
|
/** One segmented, deterministically-routed rule with provenance. */
|
|
45
60
|
export interface RoutedRule {
|
|
46
61
|
/** Normalized atomic rule text (from the segmenter). */
|
|
@@ -75,7 +90,7 @@ export interface RuleRouting {
|
|
|
75
90
|
/** The POSSIBLE tier — rule-ish bullets (medium confidence) that did NOT clear
|
|
76
91
|
* the bar, still classified so a human can review + promote them. Detection is
|
|
77
92
|
* precision-first, so this is where a declarative rule ("Every X must Y") that
|
|
78
|
-
* the confident tier misses shows up. See `research/rule-
|
|
93
|
+
* the confident tier misses shows up. See `research/rule-enforcer-design.md` §2. */
|
|
79
94
|
readonly possible: readonly RoutedRule[];
|
|
80
95
|
/** Bullets the segmenter decided were NOT rules, each with a reason — so the
|
|
81
96
|
* report is honest about what it set aside (§3). */
|