vigiles 38.2.0 → 39.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/dist/adapters/claude-code/event-capability.js +24 -8
- package/dist/adapters/claude-code/hook-protocol.js +2 -1
- package/dist/adapters/codex/hook-protocol.js +2 -1
- package/dist/cli-flag-check.d.ts +36 -0
- package/dist/cli-flag-check.js +72 -1
- package/dist/cli-main.d.ts +2 -2
- package/dist/cli-main.js +548 -379
- package/dist/core/bash-effects.d.ts +0 -57
- package/dist/core/bash-effects.js +0 -147
- package/dist/core/compile-generator.d.ts +6 -6
- package/dist/core/compile-generator.js +4 -4
- package/dist/core/compile.d.ts +72 -8
- package/dist/core/compile.js +276 -133
- package/dist/core/config-schema.d.ts +40 -10
- package/dist/core/config-schema.js +139 -16
- package/dist/core/coverage.d.ts +16 -0
- package/dist/core/coverage.js +22 -0
- package/dist/core/doc-refs.d.ts +3 -2
- package/dist/core/doc-refs.js +2 -2
- package/dist/core/enforce-marker.d.ts +28 -0
- package/dist/core/enforce-marker.js +34 -0
- package/dist/core/eslint-inventory.d.ts +96 -0
- package/dist/core/eslint-inventory.js +251 -0
- package/dist/core/event-capability.d.ts +5 -1
- package/dist/core/frame.d.ts +43 -2
- package/dist/core/frame.js +85 -2
- package/dist/core/generate-schema.d.ts +3 -2
- package/dist/core/generate-schema.js +2 -2
- package/dist/core/generate-types.d.ts +11 -2
- package/dist/core/generate-types.js +12 -5
- package/dist/core/hook-command.d.ts +172 -0
- package/dist/core/hook-command.js +277 -0
- package/dist/core/hook-delivery.d.ts +133 -0
- package/dist/core/hook-delivery.js +150 -0
- package/dist/core/hook-paths.d.ts +14 -0
- package/dist/core/hook-paths.js +22 -0
- package/dist/core/hook-program.d.ts +4 -4
- package/dist/core/hook-program.js +13 -25
- package/dist/core/hook-state.d.ts +1 -1
- package/dist/core/hook-state.js +1 -1
- package/dist/core/instruction-remedies.d.ts +35 -0
- package/dist/core/instruction-remedies.js +70 -1
- package/dist/core/integrity-header.d.ts +54 -0
- package/dist/core/integrity-header.js +90 -0
- package/dist/core/integrity.d.ts +1 -25
- package/dist/core/integrity.js +16 -74
- package/dist/core/linter-adapter.d.ts +27 -3
- package/dist/core/linters.d.ts +8 -1
- package/dist/core/linters.js +116 -81
- package/dist/core/orphans.d.ts +3 -5
- package/dist/core/orphans.js +9 -10
- package/dist/core/presets.d.ts +19 -7
- package/dist/core/presets.js +19 -6
- package/dist/core/rule-catalog.d.ts +11 -12
- package/dist/core/rule-catalog.js +25 -94
- package/dist/core/rule-meta.d.ts +12 -0
- package/dist/core/rule-meta.js +47 -10
- package/dist/core/source-refs.d.ts +3 -23
- package/dist/core/source-refs.js +3 -27
- package/dist/core/test-utils.d.ts +13 -0
- package/dist/core/test-utils.js +40 -1
- package/dist/core/types.d.ts +18 -26
- package/dist/core/validate.d.ts +1 -2
- package/dist/core/validate.js +60 -47
- package/dist/coverage-evidence.js +2 -3
- package/dist/deprecated-key-files.d.ts +6 -0
- package/dist/deprecated-key-files.js +7 -6
- package/dist/exclude.d.ts +3 -1
- package/dist/exclude.js +9 -33
- package/dist/harness-assert.d.ts +18 -0
- package/dist/harness-assert.js +38 -0
- package/dist/hook-check-install.js +15 -6
- package/dist/hook-check.d.ts +1 -3
- package/dist/hook-check.js +3 -5
- package/dist/hook-install.d.ts +7 -41
- package/dist/hook-install.js +29 -96
- package/dist/hook-runtime.js +2 -1
- package/dist/load-hook.d.ts +3 -2
- package/dist/load-hook.js +28 -6
- package/dist/overrides.d.ts +25 -0
- package/dist/overrides.js +51 -0
- package/dist/plugin-loader.d.ts +23 -0
- package/dist/plugin-loader.js +42 -12
- package/dist/root-globs.d.ts +9 -0
- package/dist/root-globs.js +48 -0
- package/dist/rule-routing.js +6 -3
- package/dist/run-hook.d.ts +26 -2
- package/dist/run-hook.js +48 -12
- package/dist/scan-core.d.ts +96 -20
- package/dist/scan-core.js +171 -176
- package/dist/scan-files.d.ts +0 -8
- package/dist/scan-files.js +78 -20
- package/dist/scan.d.ts +12 -12
- package/dist/scan.js +33 -10
- package/dist/setup-plan.d.ts +15 -9
- package/dist/setup-plan.js +26 -18
- package/dist/test-coverage.d.ts +3 -8
- package/dist/test-coverage.js +6 -6
- package/dist/test.d.ts +1 -1
- package/dist/vigilesrc.schema.json +1329 -118
- package/package.json +1 -1
- package/skills/adopt-spec/SKILL.md +2 -1
package/dist/core/integrity.js
CHANGED
|
@@ -19,81 +19,26 @@
|
|
|
19
19
|
* → CI runs `vigiles compile` then `git diff --exit-code`
|
|
20
20
|
*/
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.REQUIRE_INSTRUCTIONS_SPEC_DISABLE = void 0;
|
|
23
|
-
exports.splitFrontmatter = splitFrontmatter;
|
|
24
|
-
exports.findIntegrityHeader = findIntegrityHeader;
|
|
22
|
+
exports.instructionSpecState = exports.REQUIRE_INSTRUCTIONS_SPEC_DISABLE = exports.findIntegrityHeader = exports.splitFrontmatter = void 0;
|
|
25
23
|
exports.placeIntegrityHeader = placeIntegrityHeader;
|
|
26
24
|
exports.checkIntegrity = checkIntegrity;
|
|
27
25
|
exports.parseIntegrityHeader = parseIntegrityHeader;
|
|
28
26
|
exports.ejectMarkdown = ejectMarkdown;
|
|
29
27
|
const hash_js_1 = require("./hash.js");
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* Measured 2026-08-17 on five real skills: after compiling, a reader anchored to `^---` finds NO
|
|
41
|
-
* frontmatter at all. The header was the ENTIRE delta — body byte-identical, section order
|
|
42
|
-
* untouched — so one misplaced line was the whole reason a compiled skill could not be adopted.
|
|
43
|
-
*
|
|
44
|
-
* The header now goes AFTER the frontmatter when there is one. Readers must ask this module where
|
|
45
|
-
* the header is rather than anchoring their own regex at `^`: four call sites had independently
|
|
46
|
-
* encoded "first line", and that duplication is what let the invariant drift unnoticed.
|
|
47
|
-
*/
|
|
48
|
-
const FRONTMATTER_RE = /^---\r?\n[\s\S]*?\r?\n---[ \t]*\r?\n/;
|
|
49
|
-
/** Same header, matched where it sits below a frontmatter block (blank line allowed). */
|
|
50
|
-
const HASH_LINE_IN_BODY_RE = /^\s*<!-- vigiles:sha256:([a-f0-9]+) compiled from (.+) -->\r?\n\r?\n?/;
|
|
51
|
-
/**
|
|
52
|
-
* Split off a leading frontmatter block. `head` is `""` when the file has none, so a caller can
|
|
53
|
-
* concatenate `head + body` unconditionally and get the original content back.
|
|
54
|
-
*/
|
|
55
|
-
function splitFrontmatter(content) {
|
|
56
|
-
const m = FRONTMATTER_RE.exec(content);
|
|
57
|
-
return m
|
|
58
|
-
? { head: m[0], body: content.slice(m[0].length) }
|
|
59
|
-
: { head: "", body: content };
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Locate the integrity header wherever it legitimately sits — before the frontmatter (files
|
|
63
|
-
* compiled before 2026-08-17) or after it (files compiled since). Returns the hash, the spec
|
|
64
|
-
* path, and the content with the header removed and everything else intact.
|
|
65
|
-
*
|
|
66
|
-
* This is the ONLY place that knows where the header can be. Matching `/^<!-- vigiles:sha256/`
|
|
67
|
-
* by hand elsewhere is the bug this function exists to make unnecessary.
|
|
68
|
-
*/
|
|
69
|
-
function findIntegrityHeader(content) {
|
|
70
|
-
const { head, body } = splitFrontmatter(content);
|
|
71
|
-
// After the frontmatter — the current placement. The leading `\s*` is load-bearing: the header
|
|
72
|
-
// is written one blank line below the closing `---` for readability, so an anchor at position 0
|
|
73
|
-
// of the body misses it. A test caught exactly that before it shipped.
|
|
74
|
-
const inBody = body.match(HASH_LINE_IN_BODY_RE);
|
|
75
|
-
if (inBody) {
|
|
76
|
-
return {
|
|
77
|
-
hash: inBody[1],
|
|
78
|
-
specFile: inBody[2],
|
|
79
|
-
withoutHeader: head + body.replace(HASH_LINE_IN_BODY_RE, ""),
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
// Or at the very top, which is what a file compiled before the fix looks like. Such a file has
|
|
83
|
-
// no leading frontmatter by definition — the header displaced it — so `head` is empty here.
|
|
84
|
-
const atTop = content.match(HASH_LINE_RE);
|
|
85
|
-
if (!atTop)
|
|
86
|
-
return null;
|
|
87
|
-
return {
|
|
88
|
-
hash: atTop[1],
|
|
89
|
-
specFile: atTop[2],
|
|
90
|
-
withoutHeader: content.replace(HASH_LINE_RE, ""),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
28
|
+
// The header parser and the hand-written predicate live in a node-free leaf so
|
|
29
|
+
// the browser scan twin (`scan-files.ts`) can ask them without pulling
|
|
30
|
+
// `node:crypto` in through `./hash.js`; re-exported so every existing import
|
|
31
|
+
// from this module is unchanged.
|
|
32
|
+
const integrity_header_js_1 = require("./integrity-header.js");
|
|
33
|
+
var integrity_header_js_2 = require("./integrity-header.js");
|
|
34
|
+
Object.defineProperty(exports, "splitFrontmatter", { enumerable: true, get: function () { return integrity_header_js_2.splitFrontmatter; } });
|
|
35
|
+
Object.defineProperty(exports, "findIntegrityHeader", { enumerable: true, get: function () { return integrity_header_js_2.findIntegrityHeader; } });
|
|
36
|
+
Object.defineProperty(exports, "REQUIRE_INSTRUCTIONS_SPEC_DISABLE", { enumerable: true, get: function () { return integrity_header_js_2.REQUIRE_INSTRUCTIONS_SPEC_DISABLE; } });
|
|
37
|
+
Object.defineProperty(exports, "instructionSpecState", { enumerable: true, get: function () { return integrity_header_js_2.instructionSpecState; } });
|
|
93
38
|
/** Render the header in its correct position for this content. */
|
|
94
39
|
function placeIntegrityHeader(content, hash, specFile) {
|
|
95
40
|
const stamp = `<!-- vigiles:sha256:${hash} compiled from ${specFile} -->`;
|
|
96
|
-
const { head, body } = splitFrontmatter(content);
|
|
41
|
+
const { head, body } = (0, integrity_header_js_1.splitFrontmatter)(content);
|
|
97
42
|
return head ? `${head}\n${stamp}\n\n${body}` : `${stamp}\n\n${body}`;
|
|
98
43
|
}
|
|
99
44
|
/**
|
|
@@ -101,7 +46,7 @@ function placeIntegrityHeader(content, hash, specFile) {
|
|
|
101
46
|
* Files without a hash header are treated as hand-written (intact).
|
|
102
47
|
*/
|
|
103
48
|
function checkIntegrity(content) {
|
|
104
|
-
const found = findIntegrityHeader(content);
|
|
49
|
+
const found = (0, integrity_header_js_1.findIntegrityHeader)(content);
|
|
105
50
|
if (!found) {
|
|
106
51
|
return { intact: true, reason: "No hash header (hand-written file)" };
|
|
107
52
|
}
|
|
@@ -115,16 +60,13 @@ function checkIntegrity(content) {
|
|
|
115
60
|
}
|
|
116
61
|
return { intact: true };
|
|
117
62
|
}
|
|
118
|
-
/** The marker that tells `require-instructions-spec` a file is intentionally
|
|
119
|
-
* hand-owned (no `.spec.ts` expected). */
|
|
120
|
-
exports.REQUIRE_INSTRUCTIONS_SPEC_DISABLE = "<!-- vigiles-disable require-instructions-spec -->";
|
|
121
63
|
/**
|
|
122
64
|
* Parse the `vigiles:sha256 … compiled from <spec>` integrity header, if the
|
|
123
65
|
* file carries one. Returns the referenced spec path and the body below the
|
|
124
66
|
* header; `null` when the file is plain markdown (no header).
|
|
125
67
|
*/
|
|
126
68
|
function parseIntegrityHeader(content) {
|
|
127
|
-
const found = findIntegrityHeader(content);
|
|
69
|
+
const found = (0, integrity_header_js_1.findIntegrityHeader)(content);
|
|
128
70
|
if (!found)
|
|
129
71
|
return null;
|
|
130
72
|
return { specFile: found.specFile, body: found.withoutHeader };
|
|
@@ -152,9 +94,9 @@ function ejectMarkdown(content) {
|
|
|
152
94
|
if (/^---\r?\n/.test(parsed.body)) {
|
|
153
95
|
return { markdown: parsed.body, specFile: parsed.specFile };
|
|
154
96
|
}
|
|
155
|
-
const markdown = parsed.body.startsWith(
|
|
97
|
+
const markdown = parsed.body.startsWith(integrity_header_js_1.REQUIRE_INSTRUCTIONS_SPEC_DISABLE)
|
|
156
98
|
? parsed.body
|
|
157
|
-
: `${
|
|
99
|
+
: `${integrity_header_js_1.REQUIRE_INSTRUCTIONS_SPEC_DISABLE}\n\n${parsed.body}`;
|
|
158
100
|
return { markdown, specFile: parsed.specFile };
|
|
159
101
|
}
|
|
160
102
|
//# sourceMappingURL=integrity.js.map
|
|
@@ -17,14 +17,38 @@
|
|
|
17
17
|
* `generate-types.ts` both depend on it without a cycle.
|
|
18
18
|
*/
|
|
19
19
|
import type { BuiltinLinter } from "./spec.js";
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The config-enabled state of a linter rule in a project.
|
|
22
|
+
*
|
|
23
|
+
* `unconfigured` and `unknown` are different facts and must not share a word:
|
|
24
|
+
* `unconfigured` = the WHOLE config was read and no block sets the rule (ESLint
|
|
25
|
+
* treats that as off); `unknown` = vigiles could not read it, or read only part
|
|
26
|
+
* of it. Only a linter that can see every block of its config returns
|
|
27
|
+
* `unconfigured` (today: ESLint flat config); the rest keep `unknown`.
|
|
28
|
+
*/
|
|
29
|
+
export type ConfigEnabledStatus = "enabled" | "disabled" | "unconfigured" | "unknown";
|
|
22
30
|
/** A linter's enabled rules discovered from project config, for generate-types. */
|
|
23
31
|
export interface DiscoveredRules {
|
|
24
32
|
linter: string;
|
|
25
33
|
rules: string[];
|
|
26
34
|
via: string;
|
|
27
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* What asking a linter for its enabled rules produced. `none` (the linter is not
|
|
38
|
+
* set up here, or switches nothing on) and `unreadable` (it is set up, and its
|
|
39
|
+
* config could not be read) are different facts: the second is a problem the
|
|
40
|
+
* user has to hear about, never an empty list.
|
|
41
|
+
*/
|
|
42
|
+
export type Discovery = {
|
|
43
|
+
readonly kind: "found";
|
|
44
|
+
readonly rules: DiscoveredRules;
|
|
45
|
+
} | {
|
|
46
|
+
readonly kind: "none";
|
|
47
|
+
} | {
|
|
48
|
+
readonly kind: "unreadable";
|
|
49
|
+
readonly linter: string;
|
|
50
|
+
readonly reason: string;
|
|
51
|
+
};
|
|
28
52
|
/**
|
|
29
53
|
* A linter's capabilities — the variance that used to be IMPLICIT in which map
|
|
30
54
|
* had an entry, now DECLARED (mirrors `AdapterCapabilities`). The conformance
|
|
@@ -72,6 +96,6 @@ export interface LinterAdapter {
|
|
|
72
96
|
/** All known rule names, for closest-match suggestions; absent when unlistable. */
|
|
73
97
|
enumerateRules?(basePath: string): Set<string>;
|
|
74
98
|
/** Enabled rules discovered from project config, for generate-types. */
|
|
75
|
-
discoverEnabled?(basePath: string):
|
|
99
|
+
discoverEnabled?(basePath: string): Discovery;
|
|
76
100
|
}
|
|
77
101
|
//# sourceMappingURL=linter-adapter.d.ts.map
|
package/dist/core/linters.d.ts
CHANGED
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
* competitive positioning outright. Describe the mechanism; let a reader compare.
|
|
21
21
|
*/
|
|
22
22
|
import { editDistance } from "./edit-distance.js";
|
|
23
|
-
export type { ConfigEnabledStatus, DiscoveredRules, LinterAdapter, LinterCapabilities, } from "./linter-adapter.js";
|
|
23
|
+
export type { ConfigEnabledStatus, DiscoveredRules, Discovery, LinterAdapter, LinterCapabilities, } from "./linter-adapter.js";
|
|
24
24
|
import type { ConfigEnabledStatus, LinterAdapter } from "./linter-adapter.js";
|
|
25
|
+
import { type EslintInventory } from "./eslint-inventory.js";
|
|
25
26
|
import type { BuiltinLinter } from "./spec.js";
|
|
26
27
|
export interface LinterCheckResult {
|
|
27
28
|
exists: boolean;
|
|
@@ -121,6 +122,12 @@ export declare function golangciEnabledStatusFromOutput(output: string, name: st
|
|
|
121
122
|
* @internal
|
|
122
123
|
*/
|
|
123
124
|
export declare function parseGolangciEnabledLinters(output: string): string[];
|
|
125
|
+
/**
|
|
126
|
+
* The repo's ESLint inventory — the ONE answer `enforce()` verification,
|
|
127
|
+
* `generate types` and the routing catalog read. One child process per
|
|
128
|
+
* project per run, however many references ask.
|
|
129
|
+
*/
|
|
130
|
+
export declare function eslintInventory(basePath: string): EslintInventory;
|
|
124
131
|
export { editDistance };
|
|
125
132
|
/** @internal */ export declare function clearCedarCache(): void;
|
|
126
133
|
export declare const LINTERS: Record<BuiltinLinter, LinterAdapter>;
|
package/dist/core/linters.js
CHANGED
|
@@ -33,6 +33,7 @@ exports.checkstyleEnabledStatus = checkstyleEnabledStatus;
|
|
|
33
33
|
exports.golangciOutputListsLinter = golangciOutputListsLinter;
|
|
34
34
|
exports.golangciEnabledStatusFromOutput = golangciEnabledStatusFromOutput;
|
|
35
35
|
exports.parseGolangciEnabledLinters = parseGolangciEnabledLinters;
|
|
36
|
+
exports.eslintInventory = eslintInventory;
|
|
36
37
|
exports.clearCedarCache = clearCedarCache;
|
|
37
38
|
exports.checkLinterRule = checkLinterRule;
|
|
38
39
|
const node_fs_1 = require("node:fs");
|
|
@@ -71,6 +72,7 @@ function augmentToolPath() {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
augmentToolPath();
|
|
75
|
+
const eslint_inventory_js_1 = require("./eslint-inventory.js");
|
|
74
76
|
const tmp_root_js_1 = require("./tmp-root.js");
|
|
75
77
|
// ---------------------------------------------------------------------------
|
|
76
78
|
// Parsing enforcement references
|
|
@@ -574,18 +576,28 @@ function golangciCheckExists(ruleName) {
|
|
|
574
576
|
throw new Error(`Unknown golangci-lint linter: ${ruleName}`);
|
|
575
577
|
}
|
|
576
578
|
}
|
|
577
|
-
|
|
579
|
+
/**
|
|
580
|
+
* Memoize a per-project load by `basePath`; a load that throws is cached as
|
|
581
|
+
* null, so a missing tool is asked once per run, not once per reference.
|
|
582
|
+
*/
|
|
583
|
+
function memoByBasePath(load) {
|
|
578
584
|
const cache = new Map();
|
|
579
|
-
return (
|
|
585
|
+
return (basePath) => {
|
|
580
586
|
if (!cache.has(basePath)) {
|
|
581
587
|
try {
|
|
582
|
-
cache.set(basePath,
|
|
588
|
+
cache.set(basePath, load(basePath));
|
|
583
589
|
}
|
|
584
590
|
catch {
|
|
585
591
|
cache.set(basePath, null);
|
|
586
592
|
}
|
|
587
593
|
}
|
|
588
|
-
|
|
594
|
+
return cache.get(basePath) ?? null;
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
function createCachedChecker(loadConfigFn) {
|
|
598
|
+
const loader = memoByBasePath(loadConfigFn);
|
|
599
|
+
return (ruleName, basePath) => {
|
|
600
|
+
const config = loader(basePath);
|
|
589
601
|
if (!config)
|
|
590
602
|
return "unknown";
|
|
591
603
|
return config(ruleName);
|
|
@@ -594,42 +606,58 @@ function createCachedChecker(loadConfigFn) {
|
|
|
594
606
|
// Each config-enabled checker is referenced directly by its linter's adapter in
|
|
595
607
|
// the LINTERS registry (no name-keyed map). eslint/stylelint are shared by the
|
|
596
608
|
// node-api adapters; the rest by their cli adapter.
|
|
597
|
-
|
|
609
|
+
// ---------------------------------------------------------------------------
|
|
610
|
+
// ESLint: the enabled state and the registered plugins come from the one
|
|
611
|
+
// inventory (`eslint-inventory.ts`), which `generate types` reads as well.
|
|
612
|
+
// ---------------------------------------------------------------------------
|
|
613
|
+
/** Ask ESLint, in a child process at `basePath` (see `eslint-inventory.ts`). */
|
|
614
|
+
function probeEslint(basePath) {
|
|
615
|
+
const files = (0, glob_1.globSync)(eslint_inventory_js_1.PROJECT_FILES_GLOB.pattern, {
|
|
616
|
+
cwd: basePath,
|
|
617
|
+
dot: true,
|
|
618
|
+
nodir: true,
|
|
619
|
+
ignore: [...eslint_inventory_js_1.PROJECT_FILES_GLOB.ignore],
|
|
620
|
+
});
|
|
598
621
|
try {
|
|
599
|
-
|
|
600
|
-
const { loadESLint } = require("eslint");
|
|
601
|
-
(async () => {
|
|
602
|
-
try {
|
|
603
|
-
const ESLint = await loadESLint();
|
|
604
|
-
const eslint = new ESLint({ cwd: ${JSON.stringify(basePath)} });
|
|
605
|
-
const config = await eslint.calculateConfigForFile("dummy.js");
|
|
606
|
-
console.log(JSON.stringify(config.rules || {}));
|
|
607
|
-
} catch(e) {
|
|
608
|
-
console.log("{}");
|
|
609
|
-
}
|
|
610
|
-
})();
|
|
611
|
-
`;
|
|
612
|
-
const output = (0, node_child_process_1.execSync)(`node -e '${script.replace(/'/g, "'\\''")}'`, {
|
|
622
|
+
return (0, eslint_inventory_js_1.parseEslintInventory)((0, node_child_process_1.execFileSync)("node", ["-e", eslint_inventory_js_1.ESLINT_PROBE_SCRIPT], {
|
|
613
623
|
encoding: "utf-8",
|
|
614
624
|
cwd: basePath,
|
|
625
|
+
input: JSON.stringify((0, eslint_inventory_js_1.representativePaths)(files)),
|
|
615
626
|
stdio: ["pipe", "pipe", "pipe"],
|
|
616
|
-
timeout:
|
|
617
|
-
});
|
|
618
|
-
const rules = JSON.parse(output.trim() || "{}");
|
|
619
|
-
return (ruleName) => {
|
|
620
|
-
if (!(ruleName in rules))
|
|
621
|
-
return "unknown";
|
|
622
|
-
const setting = rules[ruleName];
|
|
623
|
-
const severity = Array.isArray(setting) ? setting[0] : setting;
|
|
624
|
-
if (severity === 0 || severity === "off")
|
|
625
|
-
return "disabled";
|
|
626
|
-
return "enabled";
|
|
627
|
-
};
|
|
627
|
+
timeout: 60000,
|
|
628
|
+
}));
|
|
628
629
|
}
|
|
629
|
-
catch {
|
|
630
|
-
|
|
630
|
+
catch (e) {
|
|
631
|
+
// The child itself died (a timeout, or a crash the probe could not catch):
|
|
632
|
+
// the config could not be read.
|
|
633
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
634
|
+
return { kind: "failed", error: msg.split("\n")[0] ?? msg };
|
|
631
635
|
}
|
|
632
|
-
}
|
|
636
|
+
}
|
|
637
|
+
const probeEslintCached = memoByBasePath(probeEslint);
|
|
638
|
+
/**
|
|
639
|
+
* The repo's ESLint inventory — the ONE answer `enforce()` verification,
|
|
640
|
+
* `generate types` and the routing catalog read. One child process per
|
|
641
|
+
* project per run, however many references ask.
|
|
642
|
+
*/
|
|
643
|
+
function eslintInventory(basePath) {
|
|
644
|
+
return (probeEslintCached((0, node_path_1.resolve)(basePath)) ?? {
|
|
645
|
+
kind: "failed",
|
|
646
|
+
error: "the ESLint probe could not run",
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
/** Enabled state of an ESLint rule id across every block of the config. */
|
|
650
|
+
function eslintConfigEnabled(ruleName, basePath) {
|
|
651
|
+
return (0, eslint_inventory_js_1.eslintRuleState)(eslintInventory(basePath), ruleName);
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Does the plugin a config REGISTERS under `ruleId`'s namespace define the rule?
|
|
655
|
+
* null when the config registers no plugin under that namespace (the caller then
|
|
656
|
+
* falls back to the npm package guess).
|
|
657
|
+
*/
|
|
658
|
+
function eslintConfigPluginHasRule(ruleId, basePath) {
|
|
659
|
+
return (0, eslint_inventory_js_1.registeredPluginHasRule)(eslintInventory(basePath), ruleId);
|
|
660
|
+
}
|
|
633
661
|
const stylelintConfigEnabled = createCachedChecker((basePath) => {
|
|
634
662
|
try {
|
|
635
663
|
const script = `
|
|
@@ -914,19 +942,35 @@ function closestRuleNames(target, candidates, limit = 3, maxDistance = 4) {
|
|
|
914
942
|
}
|
|
915
943
|
}
|
|
916
944
|
function isEslintPluginRule(ruleName, basePath) {
|
|
945
|
+
// The plugin object the config registers wins over a package-name guess: it
|
|
946
|
+
// is what ESLint runs, and an inline plugin has no package at all (#351).
|
|
947
|
+
const inConfig = eslintConfigPluginHasRule(ruleName, basePath);
|
|
948
|
+
if (inConfig !== null)
|
|
949
|
+
return inConfig;
|
|
917
950
|
const pluginPrefix = ruleName.substring(0, ruleName.indexOf("/"));
|
|
918
951
|
const pluginRuleName = ruleName.substring(ruleName.indexOf("/") + 1);
|
|
919
952
|
const pluginRules = resolveEslintPluginRules(pluginPrefix, basePath);
|
|
920
953
|
return pluginRules?.has(pluginRuleName) === true;
|
|
921
954
|
}
|
|
922
955
|
/** @internal */ function tryScopedPlugin(ctx) {
|
|
923
|
-
const
|
|
924
|
-
|
|
956
|
+
const fullId = `${ctx.linterName}/${ctx.ruleName}`;
|
|
957
|
+
// A built-in linter's name (`ruff`, `clippy`, …) is never an ESLint plugin
|
|
958
|
+
// namespace worth spawning ESLint for; it goes on to its own CLI check.
|
|
959
|
+
const inConfig = getLinterAdapter(ctx.linterName) === undefined
|
|
960
|
+
? eslintConfigPluginHasRule(fullId, ctx.basePath)
|
|
961
|
+
: null;
|
|
962
|
+
// The plugin the config REGISTERS wins over an npm package-name guess (#351).
|
|
963
|
+
const exists = inConfig ??
|
|
964
|
+
resolveEslintPluginRules(ctx.linterName, ctx.basePath)?.has(ctx.ruleName);
|
|
965
|
+
if (exists === undefined)
|
|
925
966
|
return null;
|
|
926
|
-
if (!
|
|
927
|
-
|
|
967
|
+
if (!exists) {
|
|
968
|
+
const where = inConfig === null
|
|
969
|
+
? ""
|
|
970
|
+
: " (the plugin your ESLint config registers under that name)";
|
|
971
|
+
return makeResult(ctx, false, "unknown", `Rule "${ctx.ruleName}" not found in ${ctx.linterName}${where}`);
|
|
928
972
|
}
|
|
929
|
-
const enabled = checkConfigEnabled("eslint",
|
|
973
|
+
const enabled = checkConfigEnabled("eslint", fullId, ctx.basePath, ctx.catalogOnly);
|
|
930
974
|
return makeResult(ctx, true, enabled);
|
|
931
975
|
}
|
|
932
976
|
// ---------------------------------------------------------------------------
|
|
@@ -1250,40 +1294,26 @@ function hasGolangciConfig(basePath) {
|
|
|
1250
1294
|
// ---------------------------------------------------------------------------
|
|
1251
1295
|
// Linter rule discovery
|
|
1252
1296
|
// ---------------------------------------------------------------------------
|
|
1297
|
+
/** `generate types`' view of the inventory: the rules the config switches on. */
|
|
1253
1298
|
function discoverEslintRules(basePath) {
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
.
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1299
|
+
const inv = eslintInventory(basePath);
|
|
1300
|
+
switch (inv.kind) {
|
|
1301
|
+
case "not-installed":
|
|
1302
|
+
case "no-config":
|
|
1303
|
+
case "empty":
|
|
1304
|
+
return { kind: "none" };
|
|
1305
|
+
case "failed":
|
|
1306
|
+
return { kind: "unreadable", linter: "eslint", reason: inv.error };
|
|
1307
|
+
case "loaded": {
|
|
1308
|
+
const plugins = [...inv.view.plugins.keys()].sort();
|
|
1309
|
+
const via = plugins.length > 0
|
|
1310
|
+
? `flat config (v9+/v10), plugins: ${plugins.join(", ")}`
|
|
1311
|
+
: "flat config (v9+/v10)";
|
|
1312
|
+
return {
|
|
1313
|
+
kind: "found",
|
|
1314
|
+
rules: { linter: "eslint", rules: [...(0, eslint_inventory_js_1.enabledRuleIds)(inv.view)], via },
|
|
1315
|
+
};
|
|
1271
1316
|
}
|
|
1272
|
-
})();
|
|
1273
|
-
`;
|
|
1274
|
-
const output = (0, node_child_process_1.execSync)(`node -e '${script.replace(/'/g, "'\\''")}'`, {
|
|
1275
|
-
encoding: "utf-8",
|
|
1276
|
-
cwd: basePath,
|
|
1277
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1278
|
-
timeout: 15000,
|
|
1279
|
-
});
|
|
1280
|
-
const rules = JSON.parse(output.trim() || "[]");
|
|
1281
|
-
if (rules.length === 0)
|
|
1282
|
-
return null;
|
|
1283
|
-
return { linter: "eslint", rules, via: "flat config (v9+/v10)" };
|
|
1284
|
-
}
|
|
1285
|
-
catch {
|
|
1286
|
-
return null;
|
|
1287
1317
|
}
|
|
1288
1318
|
}
|
|
1289
1319
|
function discoverStylelintRules(basePath) {
|
|
@@ -1549,6 +1579,11 @@ function discoverGolangciLintRules(basePath) {
|
|
|
1549
1579
|
return null;
|
|
1550
1580
|
}
|
|
1551
1581
|
}
|
|
1582
|
+
/** A discoverer that answers "found" or nothing; it has no unreadable state. */
|
|
1583
|
+
const foundOrNone = (discover) => (basePath) => {
|
|
1584
|
+
const rules = discover(basePath);
|
|
1585
|
+
return rules ? { kind: "found", rules } : { kind: "none" };
|
|
1586
|
+
};
|
|
1552
1587
|
const cliAdapter = (name, cliTool, probeExists, configEnabled, discover, enumerate) => ({
|
|
1553
1588
|
name,
|
|
1554
1589
|
capabilities: {
|
|
@@ -1583,15 +1618,15 @@ const nodeApiAdapter = (name, resolver, configEnabled, discover) => ({
|
|
|
1583
1618
|
});
|
|
1584
1619
|
exports.LINTERS = {
|
|
1585
1620
|
eslint: nodeApiAdapter("eslint", eslintResolver, eslintConfigEnabled, discoverEslintRules),
|
|
1586
|
-
stylelint: nodeApiAdapter("stylelint", stylelintResolver, stylelintConfigEnabled, discoverStylelintRules),
|
|
1587
|
-
ruff: cliAdapter("ruff", "ruff", ruffCheckExists, ruffConfigEnabled, discoverRuffRules, enumerateRuffRules),
|
|
1588
|
-
clippy: cliAdapter("clippy", "cargo", clippyCheckExists, clippyConfigEnabled, discoverClippyRules, enumerateClippyRules),
|
|
1589
|
-
pylint: cliAdapter("pylint", "pylint", pylintCheckExists, pylintConfigEnabled, discoverPylintRules, enumeratePylintRules),
|
|
1590
|
-
rubocop: cliAdapter("rubocop", "rubocop", rubocopCheckExists, rubocopConfigEnabled, discoverRubocopRules, enumerateRubocopRules),
|
|
1591
|
-
detekt: cliAdapter("detekt", "detekt", detektCheckExists, detektEnabledStatus, discoverDetektRules, enumerateDetektRules),
|
|
1592
|
-
ktlint: cliAdapter("ktlint", "ktlint", ktlintCheckExists, ktlintEnabledStatus, discoverKtlintRules),
|
|
1593
|
-
checkstyle: cliAdapter("checkstyle", "checkstyle", checkstyleCheckExists, checkstyleEnabledStatus, discoverCheckstyleRules),
|
|
1594
|
-
"golangci-lint": cliAdapter("golangci-lint", "golangci-lint", golangciCheckExists, golangciConfigEnabled, discoverGolangciLintRules, enumerateGolangciRules),
|
|
1621
|
+
stylelint: nodeApiAdapter("stylelint", stylelintResolver, stylelintConfigEnabled, foundOrNone(discoverStylelintRules)),
|
|
1622
|
+
ruff: cliAdapter("ruff", "ruff", ruffCheckExists, ruffConfigEnabled, foundOrNone(discoverRuffRules), enumerateRuffRules),
|
|
1623
|
+
clippy: cliAdapter("clippy", "cargo", clippyCheckExists, clippyConfigEnabled, foundOrNone(discoverClippyRules), enumerateClippyRules),
|
|
1624
|
+
pylint: cliAdapter("pylint", "pylint", pylintCheckExists, pylintConfigEnabled, foundOrNone(discoverPylintRules), enumeratePylintRules),
|
|
1625
|
+
rubocop: cliAdapter("rubocop", "rubocop", rubocopCheckExists, rubocopConfigEnabled, foundOrNone(discoverRubocopRules), enumerateRubocopRules),
|
|
1626
|
+
detekt: cliAdapter("detekt", "detekt", detektCheckExists, detektEnabledStatus, foundOrNone(discoverDetektRules), enumerateDetektRules),
|
|
1627
|
+
ktlint: cliAdapter("ktlint", "ktlint", ktlintCheckExists, ktlintEnabledStatus, foundOrNone(discoverKtlintRules)),
|
|
1628
|
+
checkstyle: cliAdapter("checkstyle", "checkstyle", checkstyleCheckExists, checkstyleEnabledStatus, foundOrNone(discoverCheckstyleRules)),
|
|
1629
|
+
"golangci-lint": cliAdapter("golangci-lint", "golangci-lint", golangciCheckExists, golangciConfigEnabled, foundOrNone(discoverGolangciLintRules), enumerateGolangciRules),
|
|
1595
1630
|
cedar: {
|
|
1596
1631
|
name: "cedar",
|
|
1597
1632
|
capabilities: {
|
|
@@ -1602,7 +1637,7 @@ exports.LINTERS = {
|
|
|
1602
1637
|
generateTypes: true,
|
|
1603
1638
|
},
|
|
1604
1639
|
checkExists: (rule, basePath, customDirs) => getCedarPolicies(basePath, customDirs).has(rule),
|
|
1605
|
-
discoverEnabled: discoverCedarPolicies,
|
|
1640
|
+
discoverEnabled: foundOrNone(discoverCedarPolicies),
|
|
1606
1641
|
},
|
|
1607
1642
|
};
|
|
1608
1643
|
// ---------------------------------------------------------------------------
|
package/dist/core/orphans.d.ts
CHANGED
|
@@ -24,8 +24,8 @@ export interface OrphanReport {
|
|
|
24
24
|
readonly orphans: readonly string[];
|
|
25
25
|
}
|
|
26
26
|
export interface FindOrphansOptions {
|
|
27
|
-
/** Repository root.
|
|
28
|
-
readonly basePath
|
|
27
|
+
/** Repository root. Required: no cwd fallback. */
|
|
28
|
+
readonly basePath: string;
|
|
29
29
|
/**
|
|
30
30
|
* Glob patterns of `.md` files to scan. Defaults to `["docs/**\/*.md"]`
|
|
31
31
|
* (`docs/` is the near-universal convention; a vigiles-specific dir like
|
|
@@ -33,8 +33,6 @@ export interface FindOrphansOptions {
|
|
|
33
33
|
* or to your project's doc globs (e.g. `["wiki/**\/*.md"]`) to override.
|
|
34
34
|
*/
|
|
35
35
|
readonly include?: readonly string[];
|
|
36
|
-
/** Glob patterns to exclude within the include scope (orphan CANDIDACY only). */
|
|
37
|
-
readonly exclude?: readonly string[];
|
|
38
36
|
/**
|
|
39
37
|
* The repo-wide `.vigilesrc.json#exclude` (`ExcludeSet.globIgnore`,
|
|
40
38
|
* src/exclude.ts — correct from any glob cwd; a plain list is relative to
|
|
@@ -65,7 +63,7 @@ export interface FindOrphansOptions {
|
|
|
65
63
|
* `.vigilesrc.json` → `orphans.include` (whose presence also opts the repo
|
|
66
64
|
* into the scan — see the CLI gate in `vigiles lint`).
|
|
67
65
|
*/
|
|
68
|
-
export declare function findOrphanDocs(options
|
|
66
|
+
export declare function findOrphanDocs(options: FindOrphansOptions): OrphanReport;
|
|
69
67
|
/** Format an orphan report as human-readable text. */
|
|
70
68
|
export declare function formatOrphanReport(report: OrphanReport): string;
|
|
71
69
|
//# sourceMappingURL=orphans.d.ts.map
|
package/dist/core/orphans.js
CHANGED
|
@@ -152,17 +152,16 @@ function refTargets(sourcePath, ref) {
|
|
|
152
152
|
* `.vigilesrc.json` → `orphans.include` (whose presence also opts the repo
|
|
153
153
|
* into the scan — see the CLI gate in `vigiles lint`).
|
|
154
154
|
*/
|
|
155
|
-
function findOrphanDocs(options
|
|
156
|
-
const basePath = options.basePath
|
|
155
|
+
function findOrphanDocs(options) {
|
|
156
|
+
const basePath = options.basePath;
|
|
157
157
|
const include = options.include ?? DEFAULT_INCLUDE;
|
|
158
|
-
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
// of the orphan list can still reference others. Union, never override.
|
|
158
|
+
// `repoExclude` is the repo-wide `.vigilesrc.json#exclude` — a vendored
|
|
159
|
+
// corpus is neither an orphan CANDIDATE nor a SOURCE of references (a link
|
|
160
|
+
// from inside it must not keep a doc alive) (#192). Keeping a doc out of the
|
|
161
|
+
// orphan list while it still references others is `overrides` with
|
|
162
|
+
// `orphan-docs` off, applied to the report by the caller.
|
|
164
163
|
const repoExclude = options.repoExclude;
|
|
165
|
-
const ignore = (0, glob_ignore_js_1.withIgnored)(
|
|
164
|
+
const ignore = (0, glob_ignore_js_1.withIgnored)(DEFAULT_IGNORE, repoExclude);
|
|
166
165
|
const layouts = options.layouts ?? [];
|
|
167
166
|
const allDocs = collectDocs(basePath, include, ignore, layouts);
|
|
168
167
|
const allMarkdown = (0, glob_1.globSync)("**/*.md", {
|
|
@@ -217,7 +216,7 @@ function formatOrphanReport(report) {
|
|
|
217
216
|
];
|
|
218
217
|
for (const o of report.orphans)
|
|
219
218
|
lines.push(` ${o}`);
|
|
220
|
-
lines.push(" Fix: link each from another .md (README, an instruction file, or a doc).", " To silence: add `<!-- vigiles-disable orphan-docs -->` to the doc, or", "
|
|
219
|
+
lines.push(" Fix: link each from another .md (README, an instruction file, or a doc).", " To silence: add `<!-- vigiles-disable orphan-docs -->` to the doc, or", " turn `orphan-docs` off for its folder in .vigilesrc.json → `overrides` (or narrow `orphans.include`).");
|
|
221
220
|
return lines.join("\n");
|
|
222
221
|
}
|
|
223
222
|
//# sourceMappingURL=orphans.js.map
|
package/dist/core/presets.d.ts
CHANGED
|
@@ -22,15 +22,27 @@ import type { RulesConfig } from "./types.js";
|
|
|
22
22
|
/**
|
|
23
23
|
* The rules `vigiles:recommended` raises to `error` — the FP-safe structural
|
|
24
24
|
* group: each one fires only on a genuine defect (a never-available or typo'd
|
|
25
|
-
* tool, a subagent missing `name`/`description`, a typo'd hook event, a
|
|
26
|
-
*
|
|
27
|
-
* well-formed harness stays green.
|
|
25
|
+
* tool, a subagent missing `name`/`description`, a typo'd hook event, a file a
|
|
26
|
+
* compiled hook needs gone missing, a broken MCP reference, two skills that
|
|
27
|
+
* collide in the selector), so a well-formed harness stays green.
|
|
28
28
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* `hand-written-hook` is NOT here: its only cure is a compiled hook, written
|
|
30
|
+
* with the `experimental_define*` API, and a preset must not push users onto an
|
|
31
|
+
* experimental API. It joins when compiled hooks leave experimental (the exit
|
|
32
|
+
* criteria in docs/experimental.md, "When the prefix comes off").
|
|
33
|
+
*
|
|
34
|
+
* Also gates `require-instructions-spec`: a hand-written CLAUDE.md / AGENTS.md
|
|
35
|
+
* is a file whose references nothing re-verifies on change. It is the one rule
|
|
36
|
+
* here a clean repo can fail before any work is done, so its finding names all
|
|
37
|
+
* three ways out in order — adopt it (`adopt-spec` / `init --target`), exclude
|
|
38
|
+
* a folder that is not yours, or turn it off for folders that stay hand-written
|
|
39
|
+
* (`overrides`) — and `init` adopts every instruction file it finds, so a repo
|
|
40
|
+
* set up by `init` starts green.
|
|
41
|
+
*
|
|
42
|
+
* Deliberately EXCLUDES the `untested-*` workflow rules: they stay an explicit
|
|
43
|
+
* opt-in (`init --strict`).
|
|
32
44
|
*/
|
|
33
|
-
export declare const RECOMMENDED_RULES: readonly ["subagent-tool-contract", "subagent-frontmatter", "hook-events", "hook-script-exists", "mcp-config", "mcp-tool-resolves", "mcp-hook-target-resolves", "disallowed-tools-contract", "description-overlap", "compile-budget"];
|
|
45
|
+
export declare const RECOMMENDED_RULES: readonly ["subagent-tool-contract", "subagent-frontmatter", "hook-events", "hook-script-exists", "mcp-config", "mcp-tool-resolves", "mcp-hook-target-resolves", "disallowed-tools-contract", "description-overlap", "compile-budget", "require-instructions-spec"];
|
|
34
46
|
/** The preset names `extends` accepts. One today; the tuple is the enum. */
|
|
35
47
|
export declare const PRESET_NAMES: readonly ["vigiles:recommended"];
|
|
36
48
|
/** A preset name `extends` accepts. */
|
package/dist/core/presets.js
CHANGED
|
@@ -6,13 +6,25 @@ exports.presetRules = presetRules;
|
|
|
6
6
|
/**
|
|
7
7
|
* The rules `vigiles:recommended` raises to `error` — the FP-safe structural
|
|
8
8
|
* group: each one fires only on a genuine defect (a never-available or typo'd
|
|
9
|
-
* tool, a subagent missing `name`/`description`, a typo'd hook event, a
|
|
10
|
-
*
|
|
11
|
-
* well-formed harness stays green.
|
|
9
|
+
* tool, a subagent missing `name`/`description`, a typo'd hook event, a file a
|
|
10
|
+
* compiled hook needs gone missing, a broken MCP reference, two skills that
|
|
11
|
+
* collide in the selector), so a well-formed harness stays green.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* `hand-written-hook` is NOT here: its only cure is a compiled hook, written
|
|
14
|
+
* with the `experimental_define*` API, and a preset must not push users onto an
|
|
15
|
+
* experimental API. It joins when compiled hooks leave experimental (the exit
|
|
16
|
+
* criteria in docs/experimental.md, "When the prefix comes off").
|
|
17
|
+
*
|
|
18
|
+
* Also gates `require-instructions-spec`: a hand-written CLAUDE.md / AGENTS.md
|
|
19
|
+
* is a file whose references nothing re-verifies on change. It is the one rule
|
|
20
|
+
* here a clean repo can fail before any work is done, so its finding names all
|
|
21
|
+
* three ways out in order — adopt it (`adopt-spec` / `init --target`), exclude
|
|
22
|
+
* a folder that is not yours, or turn it off for folders that stay hand-written
|
|
23
|
+
* (`overrides`) — and `init` adopts every instruction file it finds, so a repo
|
|
24
|
+
* set up by `init` starts green.
|
|
25
|
+
*
|
|
26
|
+
* Deliberately EXCLUDES the `untested-*` workflow rules: they stay an explicit
|
|
27
|
+
* opt-in (`init --strict`).
|
|
16
28
|
*/
|
|
17
29
|
exports.RECOMMENDED_RULES = [
|
|
18
30
|
"subagent-tool-contract",
|
|
@@ -25,6 +37,7 @@ exports.RECOMMENDED_RULES = [
|
|
|
25
37
|
"disallowed-tools-contract",
|
|
26
38
|
"description-overlap",
|
|
27
39
|
"compile-budget",
|
|
40
|
+
"require-instructions-spec",
|
|
28
41
|
];
|
|
29
42
|
/** The preset names `extends` accepts. One today; the tuple is the enum. */
|
|
30
43
|
exports.PRESET_NAMES = ["vigiles:recommended"];
|