vigiles 37.0.0 → 38.1.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/audit-report.d.ts +8 -0
- package/dist/audit-report.js +12 -3
- package/dist/cli-main.d.ts +9 -0
- package/dist/cli-main.js +123 -25
- package/dist/core/CLAUDE.md.spec.js +0 -4
- package/dist/core/compile.d.ts +36 -6
- package/dist/core/compile.js +189 -54
- package/dist/core/config-schema.d.ts +10 -0
- package/dist/core/config-schema.js +16 -0
- package/dist/core/keyfiles-source.d.ts +9 -0
- package/dist/core/keyfiles-source.js +88 -0
- package/dist/core/keyfiles.d.ts +64 -0
- package/dist/core/keyfiles.js +93 -0
- package/dist/core/markdown.d.ts +17 -0
- package/dist/core/markdown.js +57 -0
- package/dist/core/orphans.js +1 -1
- package/dist/core/presets.d.ts +1 -1
- package/dist/core/presets.js +1 -0
- package/dist/core/rule-meta.js +21 -0
- package/dist/core/session.d.ts +7 -0
- package/dist/core/session.js +8 -0
- package/dist/core/spec.d.ts +59 -3
- package/dist/core/spec.js +16 -2
- package/dist/core/types.d.ts +34 -0
- package/dist/deprecated-key-files.d.ts +35 -0
- package/dist/deprecated-key-files.js +62 -0
- package/dist/research-index.d.ts +6 -6
- package/dist/research-index.js +6 -6
- package/dist/scan.d.ts +21 -0
- package/dist/scan.js +29 -2
- package/dist/setup-plan.d.ts +1 -1
- package/dist/setup-plan.js +3 -0
- package/dist/vigilesrc.schema.json +130 -0
- package/hooks/pre-edit.sh +1 -1
- package/package.json +1 -1
- package/skills/adopt-spec/SKILL.md +7 -12
- package/skills/edit-spec/SKILL.md +32 -25
package/dist/core/compile.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DEFAULT_MAX_SECTION_CHARS = exports.DEFAULT_MAX_ENTRY_CHARS = exports.DEFAULT_MAX_SECTION_LINES = void 0;
|
|
6
|
+
exports.DEFAULT_MAX_RULES_CHARS = exports.DEFAULT_MAX_SECTION_CHARS = exports.DEFAULT_MAX_ENTRY_CHARS = exports.DEFAULT_MAX_SECTION_LINES = void 0;
|
|
7
7
|
exports.computeHash = computeHash;
|
|
8
8
|
exports.addHash = addHash;
|
|
9
9
|
exports.seal = seal;
|
|
@@ -35,6 +35,7 @@ const integrity_js_1 = require("./integrity.js");
|
|
|
35
35
|
const markdown_js_1 = require("./markdown.js");
|
|
36
36
|
const symbols_js_1 = require("./symbols.js");
|
|
37
37
|
const skill_normalize_js_1 = require("./skill-normalize.js");
|
|
38
|
+
const keyfiles_js_1 = require("./keyfiles.js");
|
|
38
39
|
const linters_js_1 = require("./linters.js");
|
|
39
40
|
const tool_contract_js_1 = require("./tool-contract.js");
|
|
40
41
|
const effects_js_1 = require("./effects.js");
|
|
@@ -299,6 +300,38 @@ function renderFragment(fragment) {
|
|
|
299
300
|
// ---------------------------------------------------------------------------
|
|
300
301
|
// Compile CLAUDE.md spec → markdown
|
|
301
302
|
// ---------------------------------------------------------------------------
|
|
303
|
+
/** `[Details](path#anchor)` for a rule's long text. */
|
|
304
|
+
function detailsLink(details) {
|
|
305
|
+
const anchor = details.anchor === undefined ? "" : `#${details.anchor}`;
|
|
306
|
+
return `[Details](${details.file.path}${anchor})`;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* The first sentence of `text`: up to the first `.`, `!` or `?` that closes a
|
|
310
|
+
* word (no space before it) and is followed by the end of the text or by
|
|
311
|
+
* whitespace and a capital, digit, quote, backtick or bracket. So a mark inside
|
|
312
|
+
* the sentence — "instead of ! assertions", "e.g. into helpers" — does not end
|
|
313
|
+
* it. A `why` with no sentence end is returned whole — truncating mid-sentence
|
|
314
|
+
* would render a claim nobody wrote.
|
|
315
|
+
*/
|
|
316
|
+
function firstSentence(text) {
|
|
317
|
+
const m = /^[\s\S]*?\S[.!?](?=$|\s+[A-Z0-9"'`(*[])/.exec(text.trim());
|
|
318
|
+
return m ? m[0] : text.trim();
|
|
319
|
+
}
|
|
320
|
+
/** The `**Why:**` line(s) of an enforce rule under its render policy. */
|
|
321
|
+
function enforceWhy(rule) {
|
|
322
|
+
const link = rule.details ? ` ${detailsLink(rule.details)}` : "";
|
|
323
|
+
const render = rule.render ?? "full";
|
|
324
|
+
switch (render) {
|
|
325
|
+
case "full":
|
|
326
|
+
return [`**Why:** ${rule.why}${link}`];
|
|
327
|
+
case "line":
|
|
328
|
+
return [`**Why:** ${firstSentence(rule.why)}${link}`];
|
|
329
|
+
case "id":
|
|
330
|
+
return rule.details ? [detailsLink(rule.details)] : [];
|
|
331
|
+
default:
|
|
332
|
+
return (0, hash_js_1.assertNever)(render);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
302
335
|
function compileRule(id, rule) {
|
|
303
336
|
const title = id
|
|
304
337
|
.replace(/[-_]/g, " ")
|
|
@@ -309,10 +342,16 @@ function compileRule(id, rule) {
|
|
|
309
342
|
`### ${title}`,
|
|
310
343
|
"",
|
|
311
344
|
`**Enforced by:** \`${rule.linterRule}\``,
|
|
312
|
-
|
|
345
|
+
...enforceWhy(rule),
|
|
313
346
|
].join("\n");
|
|
314
|
-
case "guidance":
|
|
315
|
-
|
|
347
|
+
case "guidance": {
|
|
348
|
+
const link = rule.details ? ` ${detailsLink(rule.details)}` : "";
|
|
349
|
+
return [
|
|
350
|
+
`### ${title}`,
|
|
351
|
+
"",
|
|
352
|
+
`**Guidance only** — ${rule.text}${link}`,
|
|
353
|
+
].join("\n");
|
|
354
|
+
}
|
|
316
355
|
case "guard": {
|
|
317
356
|
const patterns = Array.isArray(rule.watch)
|
|
318
357
|
? rule.watch.join("`, `")
|
|
@@ -364,49 +403,99 @@ exports.DEFAULT_MAX_ENTRY_CHARS = 200;
|
|
|
364
403
|
*/
|
|
365
404
|
exports.DEFAULT_MAX_SECTION_CHARS = 15000;
|
|
366
405
|
/**
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
406
|
+
* The Rules section, as one budget. Under `compile-budget: "error"` only.
|
|
407
|
+
*
|
|
408
|
+
* WHY THE SECTION AND NOT EACH RULE. A per-rule length cap was built and
|
|
409
|
+
* removed within an hour: it opened on 40 of 53 rules, because a rule's length
|
|
410
|
+
* is not where the cost is — the SUM is, and it is loaded on every request.
|
|
411
|
+
* 20,000 characters is half of Claude Code's 40,000-character instruction
|
|
412
|
+
* budget, which leaves the other half for everything else the file carries.
|
|
371
413
|
*/
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
414
|
+
exports.DEFAULT_MAX_RULES_CHARS = 20000;
|
|
415
|
+
/**
|
|
416
|
+
* The thresholds this compile measures against.
|
|
417
|
+
*
|
|
418
|
+
* The Rules total has no default at `"warn"`: a repo that does not extend
|
|
419
|
+
* `vigiles:recommended` and leaves the rule at its default sees exactly the
|
|
420
|
+
* warnings `compile` printed before the rule had a name. At `"error"` (what the
|
|
421
|
+
* preset sets) every budget applies with its default.
|
|
422
|
+
*
|
|
423
|
+
* 🔴 WHAT THIS RULE DOES NOT MEASURE, ON PURPOSE — each has one owner already:
|
|
424
|
+
* - the whole always-loaded size, in the harness's unit (Claude Code chars,
|
|
425
|
+
* Codex bytes) — `instruction-weight` and its `max`, which sums the whole
|
|
426
|
+
* chain, so moving text into a rules file does not fool it;
|
|
427
|
+
* - `keyFiles` — deprecated (`deprecated-key-files`); budgeting a section the
|
|
428
|
+
* reader is told to delete would be a second, contradicting instruction.
|
|
429
|
+
*/
|
|
430
|
+
function resolveBudgetLimits(options, severity) {
|
|
431
|
+
const strict = severity === "error";
|
|
432
|
+
return {
|
|
433
|
+
entryChars: options.maxEntryChars ?? exports.DEFAULT_MAX_ENTRY_CHARS,
|
|
434
|
+
sectionChars: options.maxSectionChars ?? exports.DEFAULT_MAX_SECTION_CHARS,
|
|
435
|
+
rulesChars: options.maxRulesChars ?? (strict ? exports.DEFAULT_MAX_RULES_CHARS : 0),
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
const n = (x) => x.toLocaleString("en-US");
|
|
439
|
+
/**
|
|
440
|
+
* Budget findings for `commands` entries, section size and the Rules total.
|
|
441
|
+
* Severity is applied by the caller: the same finding is a warning at the
|
|
442
|
+
* rule's default and an error under `vigiles:recommended`.
|
|
443
|
+
*/
|
|
444
|
+
function checkContentBudgets(input, specFile, limits) {
|
|
445
|
+
return [
|
|
446
|
+
...entryBudget("commands", input.spec.commands, specFile, limits),
|
|
447
|
+
...sectionBudget(input.spec.sections, specFile, limits),
|
|
448
|
+
...rulesBudget(input, specFile, limits),
|
|
449
|
+
];
|
|
450
|
+
}
|
|
451
|
+
/** One finding per `commands` description over the entry budget. */
|
|
452
|
+
function entryBudget(field, map, specFile, limits) {
|
|
453
|
+
if (!map || limits.entryChars <= 0)
|
|
454
|
+
return [];
|
|
455
|
+
return Object.entries(map)
|
|
456
|
+
.filter(([, description]) => description.length > limits.entryChars)
|
|
457
|
+
.map(([key, description]) => ({
|
|
458
|
+
type: "entry-too-long",
|
|
459
|
+
path: specFile,
|
|
460
|
+
message: `${field}[${JSON.stringify(key)}] description is ${String(description.length)} ` +
|
|
461
|
+
`characters (budget ${String(limits.entryChars)}). An entry is a pointer — say what the ` +
|
|
462
|
+
`command does in one line and move the explanation into the script's own header or ` +
|
|
463
|
+
`--help, which is read when someone runs it rather than on every request.`,
|
|
464
|
+
}));
|
|
465
|
+
}
|
|
466
|
+
/** One finding per plain-prose section over the section budget. */
|
|
467
|
+
function sectionBudget(sections, specFile, limits) {
|
|
468
|
+
if (!sections || limits.sectionChars <= 0)
|
|
469
|
+
return [];
|
|
470
|
+
// Fragment-valued sections are assembled later; only plain prose is
|
|
471
|
+
// measurable here, and it is the shape that grows.
|
|
472
|
+
return Object.entries(sections)
|
|
473
|
+
.filter((pair) => typeof pair[1] === "string" && pair[1].length > limits.sectionChars)
|
|
474
|
+
.map(([name, body]) => ({
|
|
475
|
+
type: "section-too-large",
|
|
476
|
+
path: specFile,
|
|
477
|
+
message: `section ${JSON.stringify(name)} is ${String(body.length)} characters ` +
|
|
478
|
+
`(budget ${String(limits.sectionChars)}). The line guard cannot see this — long lines are ` +
|
|
479
|
+
`the normal shape of compiled prose — and every character is loaded on every request.`,
|
|
480
|
+
}));
|
|
481
|
+
}
|
|
482
|
+
/** The whole Rules section, as one sum. */
|
|
483
|
+
function rulesBudget(input, specFile, limits) {
|
|
484
|
+
const rulesChars = input.renderedRules.length;
|
|
485
|
+
if (limits.rulesChars <= 0 || rulesChars <= limits.rulesChars)
|
|
486
|
+
return [];
|
|
487
|
+
return [
|
|
488
|
+
{
|
|
489
|
+
type: "rules-too-large",
|
|
490
|
+
path: specFile,
|
|
491
|
+
message: `the Rules section is ${n(rulesChars)} characters for ` +
|
|
492
|
+
`${String(Object.keys(input.spec.rules).length)} rules ` +
|
|
493
|
+
`(budget ${n(limits.rulesChars)}). Render a rule shorter instead of deleting it: ` +
|
|
494
|
+
`enforce(rule, why, { render: "id" }) when a linter already holds it, or ` +
|
|
495
|
+
`guidance(statement, { details: file(...) }) with the long text in that file — ` +
|
|
496
|
+
`the rule stays in the spec, where lint and audit read it.`,
|
|
497
|
+
},
|
|
377
498
|
];
|
|
378
|
-
for (const [field, map] of entries) {
|
|
379
|
-
if (!map || maxEntryChars <= 0)
|
|
380
|
-
continue;
|
|
381
|
-
for (const [key, description] of Object.entries(map)) {
|
|
382
|
-
if (description.length <= maxEntryChars)
|
|
383
|
-
continue;
|
|
384
|
-
warns.push({
|
|
385
|
-
type: "entry-too-long",
|
|
386
|
-
path: specFile,
|
|
387
|
-
message: `${field}[${JSON.stringify(key)}] description is ${String(description.length)} ` +
|
|
388
|
-
`characters (budget ${String(maxEntryChars)}). An entry is a pointer — say what the ` +
|
|
389
|
-
`file is for in one line and move the explanation into its own header, which is read ` +
|
|
390
|
-
`when someone opens it rather than on every request.`,
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
if (spec.sections && maxSectionChars > 0) {
|
|
395
|
-
for (const [name, body] of Object.entries(spec.sections)) {
|
|
396
|
-
// Fragment-valued sections are assembled later; only plain prose is
|
|
397
|
-
// measurable here, and it is the shape that grows.
|
|
398
|
-
if (typeof body !== "string" || body.length <= maxSectionChars)
|
|
399
|
-
continue;
|
|
400
|
-
warns.push({
|
|
401
|
-
type: "section-too-large",
|
|
402
|
-
path: specFile,
|
|
403
|
-
message: `section ${JSON.stringify(name)} is ${String(body.length)} characters ` +
|
|
404
|
-
`(budget ${String(maxSectionChars)}). The line guard cannot see this — long lines are ` +
|
|
405
|
-
`the normal shape of compiled prose — and every character is loaded on every request.`,
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
return warns;
|
|
410
499
|
}
|
|
411
500
|
// A key-files entry is a POINTER, not an essay: the prose about why a file is
|
|
412
501
|
// shaped the way it is belongs in that file's own header, where it is read when
|
|
@@ -476,12 +565,12 @@ async function compileSectionsSection(spec, basePath, maxSectionLines) {
|
|
|
476
565
|
return { lines, errors };
|
|
477
566
|
}
|
|
478
567
|
function compileKeyFilesSection(spec, basePath) {
|
|
479
|
-
|
|
568
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- keyFiles is deprecated, not removed: it still compiles until the next major
|
|
569
|
+
const keyFiles = spec.keyFiles;
|
|
570
|
+
if (!keyFiles)
|
|
480
571
|
return { lines: [], errors: [] };
|
|
481
|
-
const lines = ["## Key Files", ""];
|
|
482
572
|
const errors = [];
|
|
483
|
-
for (const [filePath, desc] of Object.entries(
|
|
484
|
-
lines.push(`- \`${filePath}\` — ${desc}`);
|
|
573
|
+
for (const [filePath, desc] of Object.entries(keyFiles)) {
|
|
485
574
|
const err = validateFileRef(filePath, basePath);
|
|
486
575
|
if (err)
|
|
487
576
|
errors.push(err);
|
|
@@ -492,7 +581,9 @@ function compileKeyFilesSection(spec, basePath) {
|
|
|
492
581
|
});
|
|
493
582
|
}
|
|
494
583
|
}
|
|
495
|
-
|
|
584
|
+
// The one renderer the `deprecated-key-files` rule measures with, so the
|
|
585
|
+
// size a finding reports is the size of what this emits.
|
|
586
|
+
return { lines: [(0, keyfiles_js_1.renderKeyFiles)(keyFiles)], errors };
|
|
496
587
|
}
|
|
497
588
|
function compileCommandsSection(spec, basePath) {
|
|
498
589
|
if (!spec.commands)
|
|
@@ -524,6 +615,29 @@ function shouldVerifyRule(rule, options) {
|
|
|
524
615
|
return false;
|
|
525
616
|
return true;
|
|
526
617
|
}
|
|
618
|
+
/**
|
|
619
|
+
* A rule's `details` must land somewhere: the file exists (the same check as
|
|
620
|
+
* every `file()`), and its anchor, if any, is a heading or an explicit id in
|
|
621
|
+
* that file. A short rule whose long text 404s is the rule deleted, with a
|
|
622
|
+
* link standing where it was.
|
|
623
|
+
*/
|
|
624
|
+
function validateRuleDetails(details, basePath) {
|
|
625
|
+
const missing = validateFileRef(details.file.path, basePath);
|
|
626
|
+
if (missing)
|
|
627
|
+
return [missing];
|
|
628
|
+
if (details.anchor === undefined)
|
|
629
|
+
return [];
|
|
630
|
+
const text = (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(basePath, details.file.path), "utf-8");
|
|
631
|
+
if ((0, markdown_js_1.markdownAnchors)(text).has(details.anchor))
|
|
632
|
+
return [];
|
|
633
|
+
return [
|
|
634
|
+
{
|
|
635
|
+
type: "stale-ref",
|
|
636
|
+
message: `Anchor "#${details.anchor}" not found in "${details.file.path}" (no heading slugs to it and no <a id> defines it)`,
|
|
637
|
+
path: `${details.file.path}#${details.anchor}`,
|
|
638
|
+
},
|
|
639
|
+
];
|
|
640
|
+
}
|
|
527
641
|
function compileRulesSection(spec, basePath, options) {
|
|
528
642
|
const ruleEntries = Object.entries(spec.rules);
|
|
529
643
|
if (ruleEntries.length === 0) {
|
|
@@ -564,7 +678,14 @@ function compileRulesSection(spec, basePath, options) {
|
|
|
564
678
|
}
|
|
565
679
|
}
|
|
566
680
|
}
|
|
567
|
-
|
|
681
|
+
const detailsErrors = ruleEntries.flatMap(([, rule]) => rule._kind !== "guard" && rule.details
|
|
682
|
+
? validateRuleDetails(rule.details, basePath)
|
|
683
|
+
: []);
|
|
684
|
+
return {
|
|
685
|
+
lines: [ruleLines.join("\n")],
|
|
686
|
+
errors: [...errors, ...detailsErrors],
|
|
687
|
+
linterResults,
|
|
688
|
+
};
|
|
568
689
|
}
|
|
569
690
|
// ---------------------------------------------------------------------------
|
|
570
691
|
// compileClaude
|
|
@@ -622,7 +743,9 @@ async function compileClaude(spec, options = {}) {
|
|
|
622
743
|
errors.push(...prose.errors, ...keyFiles.errors, ...commands.errors, ...rules.errors);
|
|
623
744
|
const body = sections.join("\n\n") + "\n";
|
|
624
745
|
const tokens = estimateTokens(body);
|
|
625
|
-
// Per-spec maxTokens takes precedence, then compile options
|
|
746
|
+
// Per-spec maxTokens takes precedence, then compile options. An explicit
|
|
747
|
+
// ceiling is an error at any `compile-budget` severity, as it always was:
|
|
748
|
+
// the author typed a number and asked for it to hold.
|
|
626
749
|
const maxTokens = spec.maxTokens ?? options.maxTokens;
|
|
627
750
|
if (maxTokens && tokens > maxTokens) {
|
|
628
751
|
errors.push({
|
|
@@ -632,10 +755,22 @@ async function compileClaude(spec, options = {}) {
|
|
|
632
755
|
}
|
|
633
756
|
const markdown = addHash(body, specFile);
|
|
634
757
|
const allTargets = Array.isArray(targets) ? targets : [targets];
|
|
758
|
+
const severity = options.budgetSeverity ?? "warn";
|
|
759
|
+
const budgetFindings = severity === false
|
|
760
|
+
? []
|
|
761
|
+
: [
|
|
762
|
+
...checkContentBudgets({
|
|
763
|
+
spec,
|
|
764
|
+
renderedRules: rules.lines.join("\n\n"),
|
|
765
|
+
}, specFile, resolveBudgetLimits(options, severity)),
|
|
766
|
+
];
|
|
635
767
|
return {
|
|
636
768
|
markdown,
|
|
637
|
-
|
|
638
|
-
|
|
769
|
+
// The SAME objects as `budgetFindings`, so `lint` can tell them apart by
|
|
770
|
+
// identity.
|
|
771
|
+
errors: severity === "error" ? [...errors, ...budgetFindings] : errors,
|
|
772
|
+
warnings: severity === "warn" ? budgetFindings : [],
|
|
773
|
+
budgetFindings,
|
|
639
774
|
linterResults: rules.linterResults,
|
|
640
775
|
tokens,
|
|
641
776
|
targets: allTargets,
|
|
@@ -130,6 +130,15 @@ export declare const vigilesConfigSchema: z.ZodObject<{
|
|
|
130
130
|
"disallowed-tools-contract": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
|
|
131
131
|
"description-overlap": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
|
|
132
132
|
"skill-description-budget": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
|
|
133
|
+
"compile-budget": z.ZodDefault<z.ZodUnion<readonly [z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>, z.ZodTuple<[z.ZodLiteral<"warn">, z.ZodObject<{
|
|
134
|
+
maxEntryChars: z.ZodOptional<z.ZodNumber>;
|
|
135
|
+
maxSectionChars: z.ZodOptional<z.ZodNumber>;
|
|
136
|
+
maxRulesChars: z.ZodOptional<z.ZodNumber>;
|
|
137
|
+
}, z.core.$strict>], null>, z.ZodTuple<[z.ZodLiteral<"error">, z.ZodObject<{
|
|
138
|
+
maxEntryChars: z.ZodOptional<z.ZodNumber>;
|
|
139
|
+
maxSectionChars: z.ZodOptional<z.ZodNumber>;
|
|
140
|
+
maxRulesChars: z.ZodOptional<z.ZodNumber>;
|
|
141
|
+
}, z.core.$strict>], null>]>>;
|
|
133
142
|
"frontmatter-valid": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
|
|
134
143
|
"mcp-hook-target-resolves": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
|
|
135
144
|
"lethal-trifecta": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
|
|
@@ -141,6 +150,7 @@ export declare const vigilesConfigSchema: z.ZodObject<{
|
|
|
141
150
|
"hook-matcher": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
|
|
142
151
|
"doc-refs": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
|
|
143
152
|
"instruction-weight": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
|
|
153
|
+
"deprecated-key-files": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
|
|
144
154
|
}, z.core.$strict>>;
|
|
145
155
|
files: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
146
156
|
maxRules: z.ZodOptional<z.ZodNumber>;
|
|
@@ -122,6 +122,20 @@ const testCoverageConfig = zod_1.z
|
|
|
122
122
|
testExtension: zod_1.z.string().optional(),
|
|
123
123
|
})
|
|
124
124
|
.strict();
|
|
125
|
+
/**
|
|
126
|
+
* Thresholds for `compile-budget`, in characters. Each one left out keeps its
|
|
127
|
+
* built-in default; `0` turns that one budget off.
|
|
128
|
+
*/
|
|
129
|
+
const compileBudgetThresholds = zod_1.z
|
|
130
|
+
.object({
|
|
131
|
+
/** One `commands` description. */
|
|
132
|
+
maxEntryChars: zod_1.z.number().optional(),
|
|
133
|
+
/** One named prose section. */
|
|
134
|
+
maxSectionChars: zod_1.z.number().optional(),
|
|
135
|
+
/** The whole rendered Rules section. */
|
|
136
|
+
maxRulesChars: zod_1.z.number().optional(),
|
|
137
|
+
})
|
|
138
|
+
.strict();
|
|
125
139
|
/**
|
|
126
140
|
* Every validation rule, with its shipped default severity.
|
|
127
141
|
*
|
|
@@ -156,6 +170,7 @@ const rulesSchema = zod_1.z
|
|
|
156
170
|
"disallowed-tools-contract": severitySchema.default("warn"),
|
|
157
171
|
"description-overlap": severitySchema.default("warn"),
|
|
158
172
|
"skill-description-budget": severitySchema.default("warn"),
|
|
173
|
+
"compile-budget": withOptions(compileBudgetThresholds).default("warn"),
|
|
159
174
|
"frontmatter-valid": severitySchema.default("warn"),
|
|
160
175
|
"mcp-hook-target-resolves": severitySchema.default("warn"),
|
|
161
176
|
"lethal-trifecta": severitySchema.default("warn"),
|
|
@@ -167,6 +182,7 @@ const rulesSchema = zod_1.z
|
|
|
167
182
|
"hook-matcher": severitySchema.default("warn"),
|
|
168
183
|
"doc-refs": severitySchema.default(false),
|
|
169
184
|
"instruction-weight": severitySchema.default("error"),
|
|
185
|
+
"deprecated-key-files": severitySchema.default("warn"),
|
|
170
186
|
})
|
|
171
187
|
.strict();
|
|
172
188
|
/** The rule names, for the "did you mean" on an unknown one. */
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type KeyFilesUsage } from "./keyfiles.js";
|
|
2
|
+
/**
|
|
3
|
+
* The `keyFiles` usage in one spec's source, or `null` when the spec does not
|
|
4
|
+
* use the field. The first `keyFiles:` property in any object literal counts —
|
|
5
|
+
* an instruction spec has one builder call, and nothing else in a spec is
|
|
6
|
+
* called that.
|
|
7
|
+
*/
|
|
8
|
+
export declare function findKeyFilesInSpecSource(source: string): KeyFilesUsage | null;
|
|
9
|
+
//# sourceMappingURL=keyfiles-source.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.findKeyFilesInSpecSource = findKeyFilesInSpecSource;
|
|
7
|
+
/**
|
|
8
|
+
* Reads `keyFiles` out of an instruction spec's SOURCE, without running it.
|
|
9
|
+
*
|
|
10
|
+
* `lint` and `audit` both report the deprecated field (`deprecated-key-files`),
|
|
11
|
+
* and neither may import a spec: `audit` is a side-effect-free read, and a spec
|
|
12
|
+
* is the user's program. So the source is PARSED (TypeScript syntax tree) and
|
|
13
|
+
* the field is read off the tree — a `keyFiles` inside a comment or a string is
|
|
14
|
+
* not usage, and a text search could not tell the difference.
|
|
15
|
+
*
|
|
16
|
+
* Pure: the file walk that feeds it is `src/deprecated-key-files.ts`.
|
|
17
|
+
*/
|
|
18
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
19
|
+
const keyfiles_js_1 = require("./keyfiles.js");
|
|
20
|
+
/** Field used, size not knowable from the syntax alone. */
|
|
21
|
+
const UNMEASURED = (entries) => ({
|
|
22
|
+
entries,
|
|
23
|
+
chars: 0,
|
|
24
|
+
measured: false,
|
|
25
|
+
});
|
|
26
|
+
/** A literal key or value: `"a"`, `'a'`, `` `a` `` (and, for a key only, `a`). */
|
|
27
|
+
function literalText(node) {
|
|
28
|
+
return typescript_1.default.isStringLiteral(node) || typescript_1.default.isNoSubstitutionTemplateLiteral(node)
|
|
29
|
+
? node.text
|
|
30
|
+
: undefined;
|
|
31
|
+
}
|
|
32
|
+
/** One `path: "description"` pair, or `undefined` when either side is not literal. */
|
|
33
|
+
function literalEntry(prop) {
|
|
34
|
+
if (!typescript_1.default.isPropertyAssignment(prop))
|
|
35
|
+
return undefined;
|
|
36
|
+
const key = typescript_1.default.isIdentifier(prop.name)
|
|
37
|
+
? prop.name.text
|
|
38
|
+
: literalText(prop.name);
|
|
39
|
+
const value = literalText(prop.initializer);
|
|
40
|
+
return key === undefined || value === undefined ? undefined : [key, value];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read a `{ path: "description" }` object literal. A computed key or value, or a
|
|
44
|
+
* spread, makes the map unmeasurable: its pairs are still counted (each one
|
|
45
|
+
* compiles to a line), its size is not guessed.
|
|
46
|
+
*/
|
|
47
|
+
function measureLiteral(obj) {
|
|
48
|
+
const pairs = obj.properties.filter(typescript_1.default.isPropertyAssignment);
|
|
49
|
+
const literal = obj.properties
|
|
50
|
+
.map(literalEntry)
|
|
51
|
+
.filter((e) => e !== undefined);
|
|
52
|
+
return literal.length === obj.properties.length
|
|
53
|
+
? (0, keyfiles_js_1.measureKeyFiles)(Object.fromEntries(literal))
|
|
54
|
+
: UNMEASURED(pairs.length);
|
|
55
|
+
}
|
|
56
|
+
/** The usage a node declares, if it is a `keyFiles` property. */
|
|
57
|
+
function usageAt(node) {
|
|
58
|
+
if (typescript_1.default.isShorthandPropertyAssignment(node))
|
|
59
|
+
return node.name.text === "keyFiles" ? UNMEASURED(0) : undefined;
|
|
60
|
+
if (!typescript_1.default.isPropertyAssignment(node))
|
|
61
|
+
return undefined;
|
|
62
|
+
const name = typescript_1.default.isIdentifier(node.name)
|
|
63
|
+
? node.name.text
|
|
64
|
+
: literalText(node.name);
|
|
65
|
+
if (name !== "keyFiles")
|
|
66
|
+
return undefined;
|
|
67
|
+
return typescript_1.default.isObjectLiteralExpression(node.initializer)
|
|
68
|
+
? measureLiteral(node.initializer)
|
|
69
|
+
: UNMEASURED(0);
|
|
70
|
+
}
|
|
71
|
+
/** Depth-first: the first `keyFiles` property under `node`. */
|
|
72
|
+
function firstUsage(node) {
|
|
73
|
+
return (usageAt(node) ??
|
|
74
|
+
typescript_1.default.forEachChild(node, (child) => firstUsage(child)) ??
|
|
75
|
+
undefined);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The `keyFiles` usage in one spec's source, or `null` when the spec does not
|
|
79
|
+
* use the field. The first `keyFiles:` property in any object literal counts —
|
|
80
|
+
* an instruction spec has one builder call, and nothing else in a spec is
|
|
81
|
+
* called that.
|
|
82
|
+
*/
|
|
83
|
+
function findKeyFilesInSpecSource(source) {
|
|
84
|
+
const sf = typescript_1.default.createSourceFile("spec.ts", source, typescript_1.default.ScriptTarget.Latest,
|
|
85
|
+
/* setParentNodes */ false, typescript_1.default.ScriptKind.TS);
|
|
86
|
+
return firstUsage(sf) ?? null;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=keyfiles-source.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `keyFiles` — DEPRECATED, removed in the major after the one that ships this
|
|
3
|
+
* notice (the one-major alias window in `core/spec.ts`).
|
|
4
|
+
*
|
|
5
|
+
* WHY. The field compiles a flat "## Key Files" path → description list into
|
|
6
|
+
* the instruction file the harness loads on EVERY request. Measured on this
|
|
7
|
+
* repository's own root spec (2026-10-11): 230 entries, 45,467 characters —
|
|
8
|
+
* 29% of a 157,872-character file, paid on every request, to tell an agent where files are
|
|
9
|
+
* when it finds them itself with Glob/Grep. Anthropic's own guidance lists
|
|
10
|
+
* "File-by-file descriptions of the codebase" under what to EXCLUDE from
|
|
11
|
+
* `CLAUDE.md` (https://code.claude.com/docs/en/best-practices).
|
|
12
|
+
*
|
|
13
|
+
* THE REPLACEMENT, decided per entry, in this order:
|
|
14
|
+
* - the file's own header, the docs or the path already say it → delete it;
|
|
15
|
+
* - a fact about that one file, written nowhere else → that file's header
|
|
16
|
+
* comment, where a reader meets it on opening the file;
|
|
17
|
+
* - a rule an agent must follow while working in one folder → that folder's
|
|
18
|
+
* nested instruction file (`<folder>/CLAUDE.md` from its own spec), which
|
|
19
|
+
* loads only when the agent works there;
|
|
20
|
+
* - only when nothing else would lead an agent to the file → a one-line
|
|
21
|
+
* `file()` pointer where the decision that needs it is written (verified
|
|
22
|
+
* exactly like a `keyFiles` path, by `validateFileRef`).
|
|
23
|
+
*
|
|
24
|
+
* This module is pure: the renderer `compile` uses, the measurement the
|
|
25
|
+
* `deprecated-key-files` rule reports, and its message — one wording for lint
|
|
26
|
+
* and audit.
|
|
27
|
+
*/
|
|
28
|
+
/** Where the migration is written up. Printed in every finding. */
|
|
29
|
+
export declare const KEYFILES_DOCS_URL = "https://github.com/zernie/vigiles/blob/main/docs/rules/deprecated-key-files.md";
|
|
30
|
+
/**
|
|
31
|
+
* The compiled "## Key Files" section — the ONE renderer, so the size a
|
|
32
|
+
* finding reports is the size of what actually loads.
|
|
33
|
+
*/
|
|
34
|
+
export declare function renderKeyFiles(keyFiles: Readonly<Record<string, string>>): string;
|
|
35
|
+
/**
|
|
36
|
+
* What a spec's `keyFiles` costs. `measured: false` when the map is not a
|
|
37
|
+
* literal (a variable, a computed key or value, a spread) — then the field is
|
|
38
|
+
* known to be USED and its size is not guessed.
|
|
39
|
+
*/
|
|
40
|
+
export interface KeyFilesUsage {
|
|
41
|
+
readonly entries: number;
|
|
42
|
+
readonly chars: number;
|
|
43
|
+
readonly measured: boolean;
|
|
44
|
+
}
|
|
45
|
+
/** Measure a map the way it compiles. */
|
|
46
|
+
export declare function measureKeyFiles(keyFiles: Readonly<Record<string, string>>): KeyFilesUsage;
|
|
47
|
+
/** The cost half of a finding, e.g. `230 entries, 45,467 characters compiled into …`. */
|
|
48
|
+
export declare function keyFilesCost(u: KeyFilesUsage): string;
|
|
49
|
+
/**
|
|
50
|
+
* The instruction file a spec compiles to — `pkg/AGENTS.md.spec.ts` →
|
|
51
|
+
* `AGENTS.md` — so the fix names the nested file in the reader's own harness
|
|
52
|
+
* instead of assuming one.
|
|
53
|
+
*/
|
|
54
|
+
export declare function instructionFileOfSpec(specPath: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* The fix, one sentence — shared by the lint message and the audit line. The
|
|
57
|
+
* four places an entry can go, in the order to try them; the full recipe is
|
|
58
|
+
* the migration guide. `targets` are the instruction files the reported specs
|
|
59
|
+
* compile to (`CLAUDE.md`, `AGENTS.md`), so the nested file is named for real.
|
|
60
|
+
*/
|
|
61
|
+
export declare function keyFilesFix(targets: readonly string[]): string;
|
|
62
|
+
/** The whole finding: what it costs, then where each entry goes and where it is written up. */
|
|
63
|
+
export declare function keyFilesDeprecationMessage(u: KeyFilesUsage, target: string): string;
|
|
64
|
+
//# sourceMappingURL=keyfiles.d.ts.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `keyFiles` — DEPRECATED, removed in the major after the one that ships this
|
|
4
|
+
* notice (the one-major alias window in `core/spec.ts`).
|
|
5
|
+
*
|
|
6
|
+
* WHY. The field compiles a flat "## Key Files" path → description list into
|
|
7
|
+
* the instruction file the harness loads on EVERY request. Measured on this
|
|
8
|
+
* repository's own root spec (2026-10-11): 230 entries, 45,467 characters —
|
|
9
|
+
* 29% of a 157,872-character file, paid on every request, to tell an agent where files are
|
|
10
|
+
* when it finds them itself with Glob/Grep. Anthropic's own guidance lists
|
|
11
|
+
* "File-by-file descriptions of the codebase" under what to EXCLUDE from
|
|
12
|
+
* `CLAUDE.md` (https://code.claude.com/docs/en/best-practices).
|
|
13
|
+
*
|
|
14
|
+
* THE REPLACEMENT, decided per entry, in this order:
|
|
15
|
+
* - the file's own header, the docs or the path already say it → delete it;
|
|
16
|
+
* - a fact about that one file, written nowhere else → that file's header
|
|
17
|
+
* comment, where a reader meets it on opening the file;
|
|
18
|
+
* - a rule an agent must follow while working in one folder → that folder's
|
|
19
|
+
* nested instruction file (`<folder>/CLAUDE.md` from its own spec), which
|
|
20
|
+
* loads only when the agent works there;
|
|
21
|
+
* - only when nothing else would lead an agent to the file → a one-line
|
|
22
|
+
* `file()` pointer where the decision that needs it is written (verified
|
|
23
|
+
* exactly like a `keyFiles` path, by `validateFileRef`).
|
|
24
|
+
*
|
|
25
|
+
* This module is pure: the renderer `compile` uses, the measurement the
|
|
26
|
+
* `deprecated-key-files` rule reports, and its message — one wording for lint
|
|
27
|
+
* and audit.
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.KEYFILES_DOCS_URL = void 0;
|
|
31
|
+
exports.renderKeyFiles = renderKeyFiles;
|
|
32
|
+
exports.measureKeyFiles = measureKeyFiles;
|
|
33
|
+
exports.keyFilesCost = keyFilesCost;
|
|
34
|
+
exports.instructionFileOfSpec = instructionFileOfSpec;
|
|
35
|
+
exports.keyFilesFix = keyFilesFix;
|
|
36
|
+
exports.keyFilesDeprecationMessage = keyFilesDeprecationMessage;
|
|
37
|
+
/** Where the migration is written up. Printed in every finding. */
|
|
38
|
+
exports.KEYFILES_DOCS_URL = "https://github.com/zernie/vigiles/blob/main/docs/rules/deprecated-key-files.md";
|
|
39
|
+
/**
|
|
40
|
+
* The compiled "## Key Files" section — the ONE renderer, so the size a
|
|
41
|
+
* finding reports is the size of what actually loads.
|
|
42
|
+
*/
|
|
43
|
+
function renderKeyFiles(keyFiles) {
|
|
44
|
+
return [
|
|
45
|
+
"## Key Files",
|
|
46
|
+
"",
|
|
47
|
+
...Object.entries(keyFiles).map(([path, desc]) => `- \`${path}\` — ${desc}`),
|
|
48
|
+
].join("\n");
|
|
49
|
+
}
|
|
50
|
+
/** Measure a map the way it compiles. */
|
|
51
|
+
function measureKeyFiles(keyFiles) {
|
|
52
|
+
return {
|
|
53
|
+
entries: Object.keys(keyFiles).length,
|
|
54
|
+
chars: renderKeyFiles(keyFiles).length,
|
|
55
|
+
measured: true,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const grouped = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
59
|
+
/** The cost half of a finding, e.g. `230 entries, 45,467 characters compiled into …`. */
|
|
60
|
+
function keyFilesCost(u) {
|
|
61
|
+
if (!u.measured)
|
|
62
|
+
return "not a literal map — size not measured";
|
|
63
|
+
const noun = u.entries === 1 ? "entry" : "entries";
|
|
64
|
+
return `${String(u.entries)} ${noun}, ${grouped(u.chars)} characters compiled into the instruction file`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The instruction file a spec compiles to — `pkg/AGENTS.md.spec.ts` →
|
|
68
|
+
* `AGENTS.md` — so the fix names the nested file in the reader's own harness
|
|
69
|
+
* instead of assuming one.
|
|
70
|
+
*/
|
|
71
|
+
function instructionFileOfSpec(specPath) {
|
|
72
|
+
return specPath.replace(/^.*[\\/]/, "").replace(/\.spec\.ts$/, "");
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The fix, one sentence — shared by the lint message and the audit line. The
|
|
76
|
+
* four places an entry can go, in the order to try them; the full recipe is
|
|
77
|
+
* the migration guide. `targets` are the instruction files the reported specs
|
|
78
|
+
* compile to (`CLAUDE.md`, `AGENTS.md`), so the nested file is named for real.
|
|
79
|
+
*/
|
|
80
|
+
function keyFilesFix(targets) {
|
|
81
|
+
// In the order given — the detector sorts findings by path — once each.
|
|
82
|
+
const nested = Array.from(new Set(targets), (t) => `<folder>/${t}`).join(" or ");
|
|
83
|
+
return ("For each entry: delete it if the file or the docs already say it · " +
|
|
84
|
+
"move a fact about one file into that file's header comment · " +
|
|
85
|
+
`move a rule for one folder into ${nested} · ` +
|
|
86
|
+
"keep a one-line file() pointer only if nothing else leads there");
|
|
87
|
+
}
|
|
88
|
+
/** The whole finding: what it costs, then where each entry goes and where it is written up. */
|
|
89
|
+
function keyFilesDeprecationMessage(u, target) {
|
|
90
|
+
return (`\`keyFiles\` is deprecated and goes away in the next major (${keyFilesCost(u)}). ` +
|
|
91
|
+
`${keyFilesFix([target])} (guide: ${exports.KEYFILES_DOCS_URL})`);
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=keyfiles.js.map
|