vigiles 12.8.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.
- package/README.md +10 -4
- package/dist/audit-report.d.ts +10 -4
- package/dist/audit-report.js +7 -2
- package/dist/audit-report.template.html +28 -28
- package/dist/cli.js +105 -28
- package/dist/core/orphans.d.ts +16 -6
- package/dist/core/orphans.js +45 -19
- package/dist/core/rule-catalog.d.ts +56 -0
- package/dist/core/rule-catalog.js +146 -0
- package/dist/core/rule-meta.js +2 -2
- package/dist/core/types.d.ts +10 -4
- package/dist/eval.d.ts +13 -1
- package/dist/eval.js +13 -1
- package/dist/instruction-sources.d.ts +39 -0
- package/dist/instruction-sources.js +71 -0
- package/dist/rule-inventory.d.ts +6 -0
- package/dist/rule-inventory.js +170 -1
- package/dist/rule-routing.d.ts +25 -2
- package/dist/rule-routing.js +337 -26
- package/dist/segment.d.ts +1 -1
- package/dist/segment.js +151 -17
- package/package.json +2 -2
- package/skills/linter-docs/clippy.md +1 -1
- package/skills/linter-docs/eslint.md +1 -1
- package/skills/linter-docs/pylint.md +1 -1
- package/skills/linter-docs/rubocop.md +1 -1
- package/skills/linter-docs/ruff.md +1 -1
- package/skills/linter-docs/stylelint.md +1 -1
- package/skills/strengthen/SKILL.md +4 -4
package/dist/segment.js
CHANGED
|
@@ -11,10 +11,108 @@
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.segmentInstructions = segmentInstructions;
|
|
13
13
|
// --- Heuristic vocabulary --------------------------------------------------
|
|
14
|
-
/** Imperative/prohibitive head the candidate must START with (form cue).
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
/** Imperative/prohibitive head the candidate must START with (form cue). The
|
|
15
|
+
* deontic verbs (require/disallow/forbid/ban/enforce) are common rule leads —
|
|
16
|
+
* "Require `curly` braces", "Disallow `var`" — so they belong here. NB "no" is
|
|
17
|
+
* `no\s` (the prohibition word + whitespace) NOT the old `no\s+\S`, which — via
|
|
18
|
+
* the shared trailing `\b` — only matched when the word after "No " began at a
|
|
19
|
+
* boundary, so "No bare except" / "No default exports" silently failed the form
|
|
20
|
+
* cue. Bare `no` + the shared `\b` (checked right after "no", a boundary before
|
|
21
|
+
* a space OR a backtick) matches "No bare" AND "No `any`", while "Note"/"Nowhere"
|
|
22
|
+
* (no boundary after "no") are still rejected. */
|
|
23
|
+
const FORM_HEAD = /^(?:use|avoid|prefer|never|always|don'?t|do not|no|must|should|keep|run|write|add|remove|only|require|requires?|disallow|forbid|ban|enforce)\b/i;
|
|
24
|
+
/**
|
|
25
|
+
* Rule-ish heading gate for prose-under-heading candidacy. Word-bounded so the
|
|
26
|
+
* `do` alternate can't match inside `Documentation`/`Adoption`/`Download` (the
|
|
27
|
+
* measured bug). Accept-heading vocabulary grounded in the OSS-corpus survey
|
|
28
|
+
* (`## Coding Standards`/`## Code Style`/`## Naming`/`## Good practices`/
|
|
29
|
+
* `## Error Handling` are the real code-norm sections).
|
|
30
|
+
*/
|
|
31
|
+
const RULE_HEADING = /\b(?:rules?|conventions?|code[ -]?style|style|guidelines?|standards?|naming|good practices?|error handling|do'?s?\s*(?:and|&|\/)\s*don'?ts?|don'?ts?|never|always|must|require)\b/i;
|
|
32
|
+
/**
|
|
33
|
+
* Anti-context heading: a section whose content is overwhelmingly index /
|
|
34
|
+
* command / setup / narrative, not enforceable norms (the corpus's #1
|
|
35
|
+
* false-positive source). Content under one of these is rejected outright —
|
|
36
|
+
* UNLESS the heading is also rule-ish (`## Testing conventions` keeps its
|
|
37
|
+
* bullets), so the accept signal wins a tie.
|
|
38
|
+
*/
|
|
39
|
+
const ANTI_HEADING = /\b(?:commands?|setup|install(?:ation)?|usage|getting started|quick ?start|examples?|key files|(?:code)?base structure|project structure|repository structure|architecture|overview|directory|layout|environment|commits?|pull requests?|testing|scripts?|dependencies|roadmap|changelog|table of contents|where to look)\b/i;
|
|
40
|
+
/**
|
|
41
|
+
* INDEX-SMELL veto: a bullet whose content is a code span followed by a
|
|
42
|
+
* separator + description (`` `src/x.ts` — Type system ``, `` `npm test` — run ``)
|
|
43
|
+
* is a keyFiles/command INDEX entry, never a rule. The single highest-value
|
|
44
|
+
* rejection signal (the corpus's dominant false positive).
|
|
45
|
+
*/
|
|
46
|
+
const INDEX_SMELL = /^`[^`]+`\s*[:—–-]\s/;
|
|
47
|
+
/**
|
|
48
|
+
* Broader index/reference-entry shapes the backtick INDEX_SMELL misses — from
|
|
49
|
+
* real-corpus leakage: a path MAPPING (`next-dev.ts → next-dev-server.ts`), a
|
|
50
|
+
* bullet LED by a file path + em-dash (`packages/x/ — Session replay`), or a
|
|
51
|
+
* `Label: <path>` pointer (`Skill file: .agents/skills/…`). Paths in these are
|
|
52
|
+
* backtick-wrapped, so we test a backtick-stripped shadow. The path DISCRIMINATOR
|
|
53
|
+
* — a file extension (`.ts`) or a trailing slash — is what keeps a rule id
|
|
54
|
+
* (`@scope/no-explicit-any`, no extension) from being mistaken for a path, so a
|
|
55
|
+
* rule-naming bullet is never rejected as an index entry.
|
|
56
|
+
*/
|
|
57
|
+
const INDEX_ARROW = /[\w./@-]*\.[a-z]{1,6}\b\s*(?:→|->|=>)/;
|
|
58
|
+
// A multi-segment slash PATH before an arrow is a path-mapping/index row
|
|
59
|
+
// (`node_modules/@astrojs/react/… → packages/…`) — ≥2 slashes keeps it path-
|
|
60
|
+
// specific so a prose "A → B" isn't caught.
|
|
61
|
+
const INDEX_ARROW_PATH = /[\w@.-]+(?:\/[\w@.*-]+){2,}\s*(?:→|->|=>)/;
|
|
62
|
+
const INDEX_LABEL_PATH = /^[A-Za-z][\w ]{0,24}:\s+\.?[\w@-]*\/[\w@./-]*(?:\.[a-z0-9]{1,6}\b|\/)/;
|
|
63
|
+
const INDEX_PATH_LED = /^[\w@.-]+\/[\w@./-]*(?:\.[a-z0-9]{1,6}\b|\/)/;
|
|
64
|
+
function looksLikeIndexEntry(t) {
|
|
65
|
+
if (INDEX_SMELL.test(t))
|
|
66
|
+
return true;
|
|
67
|
+
const bare = t.replace(/`/g, " ").trim();
|
|
68
|
+
if (INDEX_ARROW.test(bare) || INDEX_ARROW_PATH.test(bare))
|
|
69
|
+
return true;
|
|
70
|
+
if (INDEX_LABEL_PATH.test(bare))
|
|
71
|
+
return true;
|
|
72
|
+
return INDEX_PATH_LED.test(bare) && /\s[—–]\s/.test(bare);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* DESCRIPTION-LED reject: a segment that DESCRIBES a code entity rather than
|
|
76
|
+
* instructing about it. It leads with a backticked identifier/path, then a
|
|
77
|
+
* copula / code-KIND noun / descriptive verb ("`Foo` class in `x` executes …",
|
|
78
|
+
* "`bar` is the loader", "`apps/x` handles …"). A real rule leads with a VERB
|
|
79
|
+
* ("Use `Foo`", "Never `bar`") — never the code span itself — so a code-span
|
|
80
|
+
* lead-in followed by a descriptive word is an architecture/index sentence, the
|
|
81
|
+
* dogfood's #1 segmenter false positive (39% of the "hard" bucket was this
|
|
82
|
+
* kind of noise). High-precision: only when the descriptive word IMMEDIATELY
|
|
83
|
+
* follows the leading code span.
|
|
84
|
+
*/
|
|
85
|
+
const DESCRIPTION_LED = /^`[^`]+`\s+(?:is|are|was|were|lives?|live|contains?|holds?|handles?|executes?|provides?|represents?|maps?|points?|implements?|exports?|defines?|wraps?|stores?|returns?|the|a|an|class|function|module|component|file|package|hook|utility|helper|type|interface|enum|constant|method|directory|folder|dir)\b/i;
|
|
86
|
+
// A deontic predicate makes a code-span-led sentence a RULE, not a description
|
|
87
|
+
// ("`const` is preferred over `let`", "`AbstractBase` class must be extended") —
|
|
88
|
+
// so the description reject must NOT fire. Guards the copula/kind-noun ambiguity.
|
|
89
|
+
const RULE_PREDICATE = /\b(?:must|should|shall|never|always|require|avoid|prefer|banned|forbidden|prohibited|allowed|disallowed|deprecated|discouraged|mandatory|do not|don'?t|only|instead)\b/i;
|
|
90
|
+
function looksLikeDescription(t) {
|
|
91
|
+
const s = t.trim();
|
|
92
|
+
return DESCRIPTION_LED.test(s) && !RULE_PREDICATE.test(s);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* RULE-NAME cue: a backticked token that is SHAPED like an off-the-shelf lint
|
|
96
|
+
* rule — a scoped/plugin rule (`@typescript-eslint/consistent-type-imports`,
|
|
97
|
+
* `import/no-cycle`) or a ≥3-segment kebab id (`no-floating-promises`). Requiring
|
|
98
|
+
* the backticks kills prose false positives (`up-to-date`, `state-of-the-art`,
|
|
99
|
+
* a file path). Naming a rule is a STRONG signal a bullet is an enforceable rule
|
|
100
|
+
* even when it has no imperative verb — the corpus's rule-naming bullets
|
|
101
|
+
* ("No floating promises (`@ts.../no-floating-promises`)") otherwise score
|
|
102
|
+
* "medium" and get dropped by the high-only default.
|
|
103
|
+
*/
|
|
104
|
+
const RULE_NAME_IN_CODE = /`[^`]*(?:@[a-z][\w-]*\/[a-z][\w-]*|[a-z][\w-]*\/[a-z][\w-]*-[\w-]+|(?:no|prefer|require|consistent|max|min|func|id|sort|valid|padding|dot|array|object)-[a-z][a-z0-9-]+)[^`]*`/i;
|
|
105
|
+
/**
|
|
106
|
+
* Leading markdown decoration a rule may be wrapped in — emphasis (`**bold**`),
|
|
107
|
+
* blockquote, checkbox, or a status emoji. Stripped on a SHADOW string before
|
|
108
|
+
* the imperative-head test so `- **Never** …` / `✅ Use const` still read as
|
|
109
|
+
* imperative. Provenance (exactQuote/offsets) is unaffected — only the form cue
|
|
110
|
+
* sees the stripped text.
|
|
111
|
+
*/
|
|
112
|
+
const LEAD_DECORATION = /^(?:\s+|>+|\*+|_+|~+|\[[ xX]\]\s*|[✅❌☑✔✖✗⚠ℹ])+/u;
|
|
113
|
+
function stripLeadDecoration(s) {
|
|
114
|
+
return s.replace(LEAD_DECORATION, "").trimStart();
|
|
115
|
+
}
|
|
18
116
|
/** Declarative subjects — these signal a statement, not an instruction. */
|
|
19
117
|
const DECLARATION = /^(?:this|these|those|it|we|our|there)\b/i;
|
|
20
118
|
/** Line consisting only of a bare URL. */
|
|
@@ -101,14 +199,9 @@ const VERBS = new Set([
|
|
|
101
199
|
"filters",
|
|
102
200
|
"merge",
|
|
103
201
|
"merges",
|
|
104
|
-
|
|
105
|
-
"
|
|
106
|
-
|
|
107
|
-
"have",
|
|
108
|
-
"has",
|
|
109
|
-
"may",
|
|
110
|
-
"should",
|
|
111
|
-
"must",
|
|
202
|
+
// Copulas/modals (be/is/are/have/has/may/should/must) are deliberately NOT
|
|
203
|
+
// here: as "shape" verbs they made the cue near-vacuous (almost any English
|
|
204
|
+
// sentence passed). Deontic modals still live in FORM_HEAD (the form cue).
|
|
112
205
|
"pin",
|
|
113
206
|
"pins",
|
|
114
207
|
"lint",
|
|
@@ -157,6 +250,13 @@ const VERBS = new Set([
|
|
|
157
250
|
"squash",
|
|
158
251
|
"enforce",
|
|
159
252
|
"enforces",
|
|
253
|
+
"regenerate",
|
|
254
|
+
"regenerates",
|
|
255
|
+
"regen",
|
|
256
|
+
"rebuild",
|
|
257
|
+
"rebuilds",
|
|
258
|
+
"generate",
|
|
259
|
+
"generates",
|
|
160
260
|
"define",
|
|
161
261
|
"defines",
|
|
162
262
|
"declare",
|
|
@@ -218,13 +318,31 @@ function isLinkOnly(text) {
|
|
|
218
318
|
*/
|
|
219
319
|
function gate(text, isBullet, underRuleHeading) {
|
|
220
320
|
const t = text.trim();
|
|
221
|
-
|
|
321
|
+
// Reject an index/command/reference entry outright (`` `path` — description ``,
|
|
322
|
+
// `a.ts → b.ts`, `dir/x — …`, `Label: path`) — the corpus's dominant false
|
|
323
|
+
// positive. No cue count can rescue it.
|
|
324
|
+
if (looksLikeIndexEntry(t))
|
|
325
|
+
return null;
|
|
326
|
+
// Reject a DESCRIPTION-led sentence (`` `Foo` class in `x` executes … ``) — an
|
|
327
|
+
// architecture/index sentence, not a rule (the dogfood's #1 false positive).
|
|
328
|
+
if (looksLikeDescription(t))
|
|
329
|
+
return null;
|
|
222
330
|
const context = isBullet || underRuleHeading;
|
|
331
|
+
// RULE-NAME cue: a bullet/section line that NAMES an off-the-shelf rule is a
|
|
332
|
+
// strong signal it's enforceable, even without an imperative verb — promote it
|
|
333
|
+
// to high so the high-only default doesn't drop it (recovers rule-naming
|
|
334
|
+
// bullets like "No floating promises (`@ts.../no-floating-promises`)").
|
|
335
|
+
if (context && RULE_NAME_IN_CODE.test(t))
|
|
336
|
+
return "high";
|
|
337
|
+
// The form/declaration cues see the text with leading decoration stripped, so
|
|
338
|
+
// `- **Never** …` reads as imperative and `**We** …` still reads declarative.
|
|
339
|
+
const head = stripLeadDecoration(t);
|
|
340
|
+
const form = FORM_HEAD.test(head);
|
|
223
341
|
const shape = t.length >= 15 &&
|
|
224
342
|
t.length <= 300 &&
|
|
225
343
|
hasVerbish(t) &&
|
|
226
344
|
!isLinkOnly(t) &&
|
|
227
|
-
!DECLARATION.test(
|
|
345
|
+
!DECLARATION.test(head);
|
|
228
346
|
const cues = (form ? 1 : 0) + (context ? 1 : 0) + (shape ? 1 : 0);
|
|
229
347
|
if (cues >= 3)
|
|
230
348
|
return "high";
|
|
@@ -304,7 +422,9 @@ function emitFromSpan(src, lineOffsets, file, span, confidence) {
|
|
|
304
422
|
};
|
|
305
423
|
}
|
|
306
424
|
// --- Scanner ---------------------------------------------------------------
|
|
307
|
-
|
|
425
|
+
// Ordered (`1.`/`1)`) and emoji (`✅`/`❌`) bullets count as list items too —
|
|
426
|
+
// the native `-*+` class missed them, so shouted/numbered rules were invisible.
|
|
427
|
+
const LIST_ITEM = /^(\s*)([-*+]|\d+[.)]|[✅❌☑✔✖✗])(\s+)(.*)$/u;
|
|
308
428
|
const HEADING = /^(#{1,6})\s+(.*)$/;
|
|
309
429
|
const FENCE = /^\s*(```|~~~)/;
|
|
310
430
|
const TABLE_LINE = /^\s*\|/;
|
|
@@ -315,12 +435,13 @@ const TABLE_LINE = /^\s*\|/;
|
|
|
315
435
|
* candidacy. Candidate units are (a) list items with attached continuation
|
|
316
436
|
* lines and (b) sentences of paragraphs under a rule-ish heading.
|
|
317
437
|
*/
|
|
318
|
-
function segmentInstructions(markdown, file) {
|
|
438
|
+
function segmentInstructions(markdown, file, skipLines) {
|
|
319
439
|
const lines = markdown.split("\n");
|
|
320
440
|
const lineOffsets = computeLineOffsets(lines);
|
|
321
441
|
const out = [];
|
|
322
442
|
let inFence = false;
|
|
323
443
|
let currentHeadingIsRuleish = false;
|
|
444
|
+
let currentHeadingIsAntiContext = false;
|
|
324
445
|
let i = 0;
|
|
325
446
|
const lineSpan = (a, b) => ({
|
|
326
447
|
start: lineOffsets[a],
|
|
@@ -342,6 +463,10 @@ function segmentInstructions(markdown, file) {
|
|
|
342
463
|
const h = HEADING.exec(line);
|
|
343
464
|
if (h) {
|
|
344
465
|
currentHeadingIsRuleish = RULE_HEADING.test(h[2]);
|
|
466
|
+
// Anti-context only when it is NOT also rule-ish, so an accept word wins a
|
|
467
|
+
// tie (`## Testing conventions` keeps its bullets; `## Testing` drops them).
|
|
468
|
+
currentHeadingIsAntiContext =
|
|
469
|
+
ANTI_HEADING.test(h[2]) && !currentHeadingIsRuleish;
|
|
345
470
|
i++;
|
|
346
471
|
continue;
|
|
347
472
|
}
|
|
@@ -350,6 +475,13 @@ function segmentInstructions(markdown, file) {
|
|
|
350
475
|
i++;
|
|
351
476
|
continue;
|
|
352
477
|
}
|
|
478
|
+
// A line already CONSUMED by the structured-marker pre-pass (a marked
|
|
479
|
+
// section's body) is not re-segmented — this is the span-consumption that
|
|
480
|
+
// stops a marked rule being double-counted by the heuristic. (1-based.)
|
|
481
|
+
if (skipLines?.has(i + 1)) {
|
|
482
|
+
i++;
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
353
485
|
// List items (with attached continuation lines).
|
|
354
486
|
const li = LIST_ITEM.exec(line);
|
|
355
487
|
if (li) {
|
|
@@ -382,7 +514,9 @@ function segmentInstructions(markdown, file) {
|
|
|
382
514
|
const contentSpan = { start: contentStart, end: contentEnd };
|
|
383
515
|
const wholeText = normalize(markdown.slice(contentStart, contentEnd));
|
|
384
516
|
const conf = gate(wholeText, true, currentHeadingIsRuleish);
|
|
385
|
-
|
|
517
|
+
// Reject bullets under an anti-context heading (Commands/Setup/Key Files/
|
|
518
|
+
// Architecture/…) — the corpus's dominant false-positive locus.
|
|
519
|
+
if (conf !== null && !currentHeadingIsAntiContext) {
|
|
386
520
|
// Only attempt splitting for single-line items (keeps offsets exact).
|
|
387
521
|
const spans = multiLine
|
|
388
522
|
? [trimSpan(markdown, contentSpan)]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vigiles",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "14.0.0",
|
|
4
4
|
"description": "Lint & test the harness your AI agent runs on — verify the references in your CLAUDE.md / AGENTS.md and test that your hooks and skills actually work.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -90,6 +90,7 @@
|
|
|
90
90
|
},
|
|
91
91
|
"devDependencies": {
|
|
92
92
|
"@eslint/js": "^10.0.1",
|
|
93
|
+
"@jackchuka/mdschema": "^0.12.8",
|
|
93
94
|
"@microsoft/api-extractor": "^7.58.9",
|
|
94
95
|
"@types/js-yaml": "^4.0.9",
|
|
95
96
|
"@types/minimatch": "^5.1.2",
|
|
@@ -127,7 +128,6 @@
|
|
|
127
128
|
"@ast-grep/lang-rust": "^0.0.7",
|
|
128
129
|
"@ast-grep/napi": "^0.43.0",
|
|
129
130
|
"@iarna/toml": "^2.2.5",
|
|
130
|
-
"@jackchuka/mdschema": "^0.12.8",
|
|
131
131
|
"ci-info": "^4.4.0",
|
|
132
132
|
"cosmiconfig": "^9.0.1",
|
|
133
133
|
"glob": "^13.0.6",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Clippy — Reference
|
|
2
2
|
|
|
3
|
-
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and
|
|
3
|
+
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and the planned rule-synthesis skill (write custom lints, not yet shipped).
|
|
4
4
|
|
|
5
5
|
## Check Existing Lints First
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ESLint — Reference
|
|
2
2
|
|
|
3
|
-
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and
|
|
3
|
+
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and the planned rule-synthesis skill (write custom rules, not yet shipped).
|
|
4
4
|
|
|
5
5
|
## Check Existing Plugins First
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Pylint — Reference
|
|
2
2
|
|
|
3
|
-
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and
|
|
3
|
+
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and the planned rule-synthesis skill (write custom checkers, not yet shipped).
|
|
4
4
|
|
|
5
5
|
## Check Existing Plugins First
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# RuboCop — Reference
|
|
2
2
|
|
|
3
|
-
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and
|
|
3
|
+
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and the planned rule-synthesis skill (write custom cops, not yet shipped).
|
|
4
4
|
|
|
5
5
|
## Check Existing Gems First
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Ruff — Reference
|
|
2
2
|
|
|
3
|
-
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and
|
|
3
|
+
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and the planned rule-synthesis skill (write custom rules, not yet shipped).
|
|
4
4
|
|
|
5
5
|
## Check Existing Rules First
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Stylelint — Reference
|
|
2
2
|
|
|
3
|
-
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and
|
|
3
|
+
Shared linter reference for vigiles skills. Used by `strengthen` (find existing rules) and the planned rule-synthesis skill (write custom rules, not yet shipped).
|
|
4
4
|
|
|
5
5
|
## Check Existing Plugins First
|
|
6
6
|
|
|
@@ -142,12 +142,12 @@ Group the output into tiers:
|
|
|
142
142
|
→ enforce("eslint/sonarjs/cognitive-complexity", "Keep functions simple")
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
**Tier 4: No match** (stays as guidance
|
|
145
|
+
**Tier 4: No match** (stays as guidance — candidate for a future rule-synthesis skill)
|
|
146
146
|
|
|
147
147
|
```
|
|
148
148
|
"research-first": guidance("Google unfamiliar APIs first.")
|
|
149
|
-
→ No linter rule can enforce this. Stays as guidance.
|
|
150
|
-
→
|
|
149
|
+
→ No linter rule can enforce this. Stays as guidance for now.
|
|
150
|
+
→ (Custom-rule synthesis is planned but not yet shipped.)
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
### Step 6: Apply Changes
|
|
@@ -168,4 +168,4 @@ Group the output into tiers:
|
|
|
168
168
|
4. Run `npm run build && npx vigiles compile` to verify
|
|
169
169
|
5. If compilation fails, report the error and revert
|
|
170
170
|
|
|
171
|
-
**For Tier 4 (no match):**
|
|
171
|
+
**For Tier 4 (no match):** Tell the user these rules stay as guidance — custom-rule synthesis is a planned skill, not yet available.
|