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/audit-report.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type { PluginScore } from "./score-core.js";
|
|
|
21
21
|
import type { RuleInventoryItem } from "./rule-inventory.js";
|
|
22
22
|
import type { RuleRouting } from "./rule-routing.js";
|
|
23
23
|
import type { EvidenceCounts } from "./coverage-evidence.js";
|
|
24
|
+
import type { DeprecatedKeyFilesFinding } from "./deprecated-key-files.js";
|
|
24
25
|
/**
|
|
25
26
|
* The current schema version. Bump only on a BREAKING change to the shape.
|
|
26
27
|
* v2 (2026-07-15): the rule-map `mechanism` enum value for an unrouted rule
|
|
@@ -131,6 +132,13 @@ export interface AuditReport {
|
|
|
131
132
|
* version unchanged.
|
|
132
133
|
*/
|
|
133
134
|
readonly brokenReferences?: readonly string[];
|
|
135
|
+
/**
|
|
136
|
+
* Instruction specs still using the deprecated `keyFiles` (the
|
|
137
|
+
* `deprecated-key-files` rule's findings: path, entry count, compiled
|
|
138
|
+
* characters). Advisory — never scored. Present only when at least one spec
|
|
139
|
+
* uses the field. Additive/optional — schema version unchanged.
|
|
140
|
+
*/
|
|
141
|
+
readonly deprecatedKeyFiles?: readonly DeprecatedKeyFilesFinding[];
|
|
134
142
|
/**
|
|
135
143
|
* The adoption preview — "what would vigiles catch in your repo?" Present only
|
|
136
144
|
* when the model-gated tier ran (behind consent); a deterministic read omits it.
|
package/dist/audit-report.js
CHANGED
|
@@ -74,6 +74,17 @@ function inventoryOf(report) {
|
|
|
74
74
|
function outputStylesField(n) {
|
|
75
75
|
return n === undefined || n === 0 ? {} : { outputStyles: n };
|
|
76
76
|
}
|
|
77
|
+
/** The scan's concrete finding lists, each present only when non-empty. */
|
|
78
|
+
function scanFindingLists(report) {
|
|
79
|
+
return {
|
|
80
|
+
...(report.danglingRefs.length
|
|
81
|
+
? { brokenReferences: report.danglingRefs }
|
|
82
|
+
: {}),
|
|
83
|
+
...(report.deprecatedKeyFiles
|
|
84
|
+
? { deprecatedKeyFiles: report.deprecatedKeyFiles }
|
|
85
|
+
: {}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
77
88
|
/**
|
|
78
89
|
* Assemble the versioned {@link AuditReport} from a scan report — pure, no clock.
|
|
79
90
|
* The CLI attaches `meta.generatedAt` when it writes the JSON artifact; the
|
|
@@ -97,9 +108,7 @@ function buildAuditReport(report, opts) {
|
|
|
97
108
|
verdict,
|
|
98
109
|
recommendations,
|
|
99
110
|
inventory: inventoryOf(report),
|
|
100
|
-
...(report
|
|
101
|
-
? { brokenReferences: report.danglingRefs }
|
|
102
|
-
: {}),
|
|
111
|
+
...scanFindingLists(report),
|
|
103
112
|
...(adoptable ? { adoptable } : {}),
|
|
104
113
|
...(opts.observations ? { observations: opts.observations } : {}),
|
|
105
114
|
...(opts.rulesInventory && opts.rulesInventory.length
|
package/dist/cli-main.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* vigiles CLI — verify your agent harness is real, and prove it works.
|
|
3
3
|
*
|
|
4
|
+
* This is the VERB barrel: what each verb does is defined here and nowhere
|
|
5
|
+
* else. `src/cli.ts` loads it lazily for everything that is not a hook
|
|
6
|
+
* decision (those go to `src/hook-runtime.ts`), so a top-level import added
|
|
7
|
+
* here is paid by every verb but never by a gated tool call.
|
|
8
|
+
*
|
|
4
9
|
* The verbs and their one-liners live in ONE place, `COMMAND_HELP` + `HELP_GROUPS`
|
|
5
10
|
* near the bottom of this file, and `--help` prints from that table. A second list
|
|
6
11
|
* here would be a copy that rots — this docblock WAS that copy: it named four
|
|
@@ -24,6 +29,8 @@ interface LintReport {
|
|
|
24
29
|
frontmatterRules: number;
|
|
25
30
|
specRefIssues: number;
|
|
26
31
|
specRefErrors: number;
|
|
32
|
+
/** `compile-budget` findings at "error" (reported by `lint` only then). */
|
|
33
|
+
compileBudgetErrors: number;
|
|
27
34
|
duplicatePairs: number;
|
|
28
35
|
/** Severity of `duplicate-instructions` — carried so the exit code can tier it. */
|
|
29
36
|
duplicateSeverity: RuleSeverity;
|
|
@@ -79,6 +86,8 @@ interface LintReport {
|
|
|
79
86
|
hookMatcherErrors: number;
|
|
80
87
|
instructionWeightIssues: number;
|
|
81
88
|
instructionWeightErrors: number;
|
|
89
|
+
deprecatedKeyFilesIssues: number;
|
|
90
|
+
deprecatedKeyFilesErrors: number;
|
|
82
91
|
docRefErrors: number;
|
|
83
92
|
symbolRefErrors: number;
|
|
84
93
|
mcpRefErrors: number;
|
package/dist/cli-main.js
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* vigiles CLI — verify your agent harness is real, and prove it works.
|
|
4
4
|
*
|
|
5
|
+
* This is the VERB barrel: what each verb does is defined here and nowhere
|
|
6
|
+
* else. `src/cli.ts` loads it lazily for everything that is not a hook
|
|
7
|
+
* decision (those go to `src/hook-runtime.ts`), so a top-level import added
|
|
8
|
+
* here is paid by every verb but never by a gated tool call.
|
|
9
|
+
*
|
|
5
10
|
* The verbs and their one-liners live in ONE place, `COMMAND_HELP` + `HELP_GROUPS`
|
|
6
11
|
* near the bottom of this file, and `--help` prints from that table. A second list
|
|
7
12
|
* here would be a copy that rots — this docblock WAS that copy: it named four
|
|
@@ -22,6 +27,7 @@ const repo_path_js_1 = require("./core/repo-path.js");
|
|
|
22
27
|
const node_child_process_1 = require("node:child_process");
|
|
23
28
|
const glob_1 = require("glob");
|
|
24
29
|
const exclude_js_1 = require("./exclude.js");
|
|
30
|
+
const glob_ignore_js_1 = require("./core/glob-ignore.js");
|
|
25
31
|
const generate_types_js_1 = require("./core/generate-types.js");
|
|
26
32
|
const generate_harness_js_1 = require("./core/generate-harness.js");
|
|
27
33
|
const capability_diff_js_1 = require("./core/capability-diff.js");
|
|
@@ -96,6 +102,7 @@ const adopt_js_1 = require("./core/adopt.js");
|
|
|
96
102
|
const coverage_js_1 = require("./core/coverage.js");
|
|
97
103
|
const orphans_js_1 = require("./core/orphans.js");
|
|
98
104
|
const doc_refs_js_1 = require("./core/doc-refs.js");
|
|
105
|
+
const deprecated_key_files_js_1 = require("./deprecated-key-files.js");
|
|
99
106
|
const hook_runtime_js_1 = require("./hook-runtime.js");
|
|
100
107
|
// ---------------------------------------------------------------------------
|
|
101
108
|
// Constants
|
|
@@ -126,6 +133,32 @@ function findSpecs(excludes, pattern) {
|
|
|
126
133
|
cwd: process.cwd(),
|
|
127
134
|
});
|
|
128
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* The `deprecated-key-files` detector's I/O: every instruction spec under
|
|
138
|
+
* `root` (the glob `compile` uses), read from disk. Lint and audit both call
|
|
139
|
+
* this, so the two cannot disagree. `node_modules` is never read — someone
|
|
140
|
+
* else's spec is not this repository's to migrate.
|
|
141
|
+
*/
|
|
142
|
+
function specsUsingKeyFiles(root, ignore) {
|
|
143
|
+
return (0, deprecated_key_files_js_1.findDeprecatedKeyFiles)({
|
|
144
|
+
specPaths: (0, glob_1.globSync)("**/*.md.spec.ts", {
|
|
145
|
+
cwd: root,
|
|
146
|
+
dot: true,
|
|
147
|
+
nodir: true,
|
|
148
|
+
posix: true,
|
|
149
|
+
ignore: (0, glob_ignore_js_1.withIgnored)(["**/node_modules/**"], ignore),
|
|
150
|
+
}),
|
|
151
|
+
read: (path) => (0, node_fs_1.readFileSync)((0, node_path_1.join)(root, path), "utf-8"),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/** `audit --single` names ONE harness: more than one directory is a contradiction (exit 2). */
|
|
155
|
+
function refuseSingleOverMany(single, dirs) {
|
|
156
|
+
if (!single || dirs.length <= 1)
|
|
157
|
+
return;
|
|
158
|
+
console.error(`--single audits ONE directory as one harness, but ${String(dirs.length)} were given. ` +
|
|
159
|
+
`Drop --single for a leaderboard, or pass a single directory.`);
|
|
160
|
+
process.exit(2);
|
|
161
|
+
}
|
|
129
162
|
/**
|
|
130
163
|
* Why the last `loadSpec()` returned null.
|
|
131
164
|
*
|
|
@@ -353,6 +386,7 @@ async function compileClaudeToFile(spec, specPath, config, dialect) {
|
|
|
353
386
|
maxSectionLines: config.maxSectionLines,
|
|
354
387
|
catalogOnly: config.catalogOnly,
|
|
355
388
|
linters: config.linters,
|
|
389
|
+
...compileBudgetOptions(config),
|
|
356
390
|
});
|
|
357
391
|
const primaryOutput = specPath.replace(/\.spec\.ts$/, "");
|
|
358
392
|
// Budget findings print BEFORE the pass/fail line and never change the exit
|
|
@@ -985,6 +1019,7 @@ function lintExitCode(report) {
|
|
|
985
1019
|
report.hookBlockErrors > 0 ||
|
|
986
1020
|
report.hookMatcherErrors > 0 ||
|
|
987
1021
|
report.instructionWeightErrors > 0 ||
|
|
1022
|
+
report.deprecatedKeyFilesErrors > 0 ||
|
|
988
1023
|
report.symbolRefErrors > 0 ||
|
|
989
1024
|
report.mcpRefErrors > 0 ||
|
|
990
1025
|
// `doc-refs` is opt-in and this counter is only non-zero when the user set
|
|
@@ -992,7 +1027,8 @@ function lintExitCode(report) {
|
|
|
992
1027
|
// error — it used to sit at exit 1 because it fired unasked and could not be
|
|
993
1028
|
// turned off.
|
|
994
1029
|
report.docRefErrors > 0 ||
|
|
995
|
-
report.specRefErrors > 0
|
|
1030
|
+
report.specRefErrors > 0 ||
|
|
1031
|
+
report.compileBudgetErrors > 0)
|
|
996
1032
|
return 2;
|
|
997
1033
|
// Tierable now: a `warn` orphan/duplicate finding is reported and does NOT
|
|
998
1034
|
// change the exit code. Both used to feed the exit directly, which is what
|
|
@@ -1363,9 +1399,13 @@ function overBundles(fn, run, bundles) {
|
|
|
1363
1399
|
*/
|
|
1364
1400
|
async function checkSpecRefs(excludes, config, silent, dialect) {
|
|
1365
1401
|
const sev = (0, types_js_1.ruleSeverity)(config?.rules?.["spec-refs"]) ?? "error";
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1402
|
+
// `compile-budget` is reported here only at "error": at its default it is a
|
|
1403
|
+
// compile-time warning, and `lint` printed nothing about size before the rule
|
|
1404
|
+
// had a name — the default output does not change.
|
|
1405
|
+
const budgetGate = (0, types_js_1.ruleSeverity)(config?.rules?.["compile-budget"]) === "error";
|
|
1406
|
+
if (!sev && !budgetGate)
|
|
1407
|
+
return { issues: 0, errors: 0, budgetErrors: 0 };
|
|
1408
|
+
const derived = [];
|
|
1369
1409
|
let knownAgents;
|
|
1370
1410
|
for (const specPath of findSpecs(excludes)) {
|
|
1371
1411
|
const target = specPath.replace(/\.spec\.ts$/, "");
|
|
@@ -1379,9 +1419,11 @@ async function checkSpecRefs(excludes, config, silent, dialect) {
|
|
|
1379
1419
|
// collected lazily so a repo with no railway spec never pays for the walk.
|
|
1380
1420
|
if (spec._specType === "railway")
|
|
1381
1421
|
knownAgents ??= await collectAgentNames(excludes);
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1422
|
+
derived.push({
|
|
1423
|
+
target,
|
|
1424
|
+
specPath,
|
|
1425
|
+
findings: await specCompileErrors(spec, specPath, dialect, config, knownAgents ?? []),
|
|
1426
|
+
});
|
|
1385
1427
|
}
|
|
1386
1428
|
catch {
|
|
1387
1429
|
// A spec that will not load is `compile`'s finding, not this one's —
|
|
@@ -1389,6 +1431,9 @@ async function checkSpecRefs(excludes, config, silent, dialect) {
|
|
|
1389
1431
|
continue;
|
|
1390
1432
|
}
|
|
1391
1433
|
}
|
|
1434
|
+
const messages = (pick) => derived.flatMap((d) => pick(d.findings).map((e) => `${d.target}: ${e.message} (from ${d.specPath})`));
|
|
1435
|
+
const found = sev ? messages((f) => f.errors) : [];
|
|
1436
|
+
const overBudget = messages((f) => f.budget);
|
|
1392
1437
|
if (found.length > 0 && !silent) {
|
|
1393
1438
|
console.log("\nSpec reference check:\n");
|
|
1394
1439
|
for (const msg of found) {
|
|
@@ -1396,7 +1441,18 @@ async function checkSpecRefs(excludes, config, silent, dialect) {
|
|
|
1396
1441
|
ghAnnotate(sev === "error" ? "error" : "warning", msg);
|
|
1397
1442
|
}
|
|
1398
1443
|
}
|
|
1399
|
-
|
|
1444
|
+
if (overBudget.length > 0 && !silent) {
|
|
1445
|
+
console.log("\nCompile budget check (compile-budget):\n");
|
|
1446
|
+
overBudget.forEach((msg) => {
|
|
1447
|
+
console.log(` ✗ ${msg}`);
|
|
1448
|
+
ghAnnotate("error", `compile-budget: ${msg}`);
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
return {
|
|
1452
|
+
issues: found.length,
|
|
1453
|
+
errors: sev === "error" ? found.length : 0,
|
|
1454
|
+
budgetErrors: overBudget.length,
|
|
1455
|
+
};
|
|
1400
1456
|
}
|
|
1401
1457
|
/**
|
|
1402
1458
|
* The ONE spec-type dispatcher, in the only shape both directions can share:
|
|
@@ -1420,8 +1476,8 @@ async function checkSpecRefs(excludes, config, silent, dialect) {
|
|
|
1420
1476
|
async function specCompileErrors(spec, specPath, dialect, config, knownAgents) {
|
|
1421
1477
|
const basePath = process.cwd();
|
|
1422
1478
|
switch (spec._specType) {
|
|
1423
|
-
case "claude":
|
|
1424
|
-
|
|
1479
|
+
case "claude": {
|
|
1480
|
+
const r = await (0, compile_js_1.compileClaude)(spec, {
|
|
1425
1481
|
basePath,
|
|
1426
1482
|
specFile: specPath,
|
|
1427
1483
|
dialect,
|
|
@@ -1430,19 +1486,52 @@ async function specCompileErrors(spec, specPath, dialect, config, knownAgents) {
|
|
|
1430
1486
|
maxSectionLines: config?.maxSectionLines,
|
|
1431
1487
|
catalogOnly: config?.catalogOnly,
|
|
1432
1488
|
linters: config?.linters,
|
|
1433
|
-
|
|
1489
|
+
...compileBudgetOptions(config),
|
|
1490
|
+
});
|
|
1491
|
+
// A size finding is `compile-budget`'s, not a dead reference: split by
|
|
1492
|
+
// IDENTITY against the compiler's own list, never by the type's name.
|
|
1493
|
+
return {
|
|
1494
|
+
errors: r.errors.filter((e) => !r.budgetFindings.includes(e)),
|
|
1495
|
+
budget: r.errors.filter((e) => r.budgetFindings.includes(e)),
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1434
1498
|
case "skill":
|
|
1435
|
-
return
|
|
1499
|
+
return {
|
|
1500
|
+
errors: (await (0, compile_js_1.compileSkill)(spec, { basePath, specFile: specPath, dialect })).errors,
|
|
1501
|
+
budget: [],
|
|
1502
|
+
};
|
|
1436
1503
|
case "agent":
|
|
1437
|
-
return
|
|
1504
|
+
return {
|
|
1505
|
+
errors: (await (0, compile_js_1.compileAgent)(spec, { basePath, specFile: specPath, dialect })).errors,
|
|
1506
|
+
budget: [],
|
|
1507
|
+
};
|
|
1438
1508
|
case "railway":
|
|
1439
|
-
return
|
|
1509
|
+
return {
|
|
1510
|
+
errors: (0, compile_js_1.compileRailway)(spec, { specFile: specPath, knownAgents })
|
|
1511
|
+
.errors,
|
|
1512
|
+
budget: [],
|
|
1513
|
+
};
|
|
1440
1514
|
default:
|
|
1441
1515
|
// A `pipeline` spec compiles through its underlying railway, so it has no
|
|
1442
1516
|
// artifact of its own to re-derive refs for.
|
|
1443
|
-
return [];
|
|
1517
|
+
return { errors: [], budget: [] };
|
|
1444
1518
|
}
|
|
1445
1519
|
}
|
|
1520
|
+
/**
|
|
1521
|
+
* The `compile-budget` rule, as `compileClaude` options: its severity and any
|
|
1522
|
+
* thresholds the config set. The ONE translation, so `compile` (which writes)
|
|
1523
|
+
* and `lint` (which re-derives) can never measure against different numbers.
|
|
1524
|
+
*/
|
|
1525
|
+
function compileBudgetOptions(config) {
|
|
1526
|
+
const rule = config?.rules?.["compile-budget"];
|
|
1527
|
+
// No config at all (a programmatic caller, a missing file) is the default.
|
|
1528
|
+
if (rule === undefined)
|
|
1529
|
+
return { budgetSeverity: "warn" };
|
|
1530
|
+
return {
|
|
1531
|
+
budgetSeverity: (0, types_js_1.ruleSeverity)(rule),
|
|
1532
|
+
...(0, types_js_1.ruleOptions)(rule),
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1446
1535
|
/**
|
|
1447
1536
|
* Write a compiled artifact. Accepts ONLY a {@link StampedMarkdown}, so a body
|
|
1448
1537
|
* that failed to compile cannot reach the disk — there is no stamp to pass.
|
|
@@ -1698,6 +1787,17 @@ async function runLint(restArgs, flags, excludes, config) {
|
|
|
1698
1787
|
ghAnnotate(level, message);
|
|
1699
1788
|
},
|
|
1700
1789
|
});
|
|
1790
|
+
// 7w. Deprecated-key-files — an instruction spec still using `keyFiles`. Once
|
|
1791
|
+
// over the repository, like the ratchet: a nested bundle's spec would
|
|
1792
|
+
// otherwise be reported again by every bundle that encloses it.
|
|
1793
|
+
const deprecatedKeyFiles = (0, deprecated_key_files_js_1.checkDeprecatedKeyFiles)({
|
|
1794
|
+
severity: (0, types_js_1.ruleSeverity)(config?.rules?.["deprecated-key-files"]),
|
|
1795
|
+
find: () => specsUsingKeyFiles(process.cwd(), excludes.globIgnore),
|
|
1796
|
+
silent,
|
|
1797
|
+
annotate: (level, message, path) => {
|
|
1798
|
+
ghAnnotate(level, message, frame.repo((0, node_path_1.resolve)(process.cwd(), path)));
|
|
1799
|
+
},
|
|
1800
|
+
});
|
|
1701
1801
|
// 8. Validate vigiles builder calls inside markdown code blocks — the
|
|
1702
1802
|
// `doc-refs` rule, DEFAULT OFF. Illustrative blocks opt out via
|
|
1703
1803
|
// `<!-- vigiles:ignore -->` (single block) or `<!-- vigiles:ignore-file -->`
|
|
@@ -1775,6 +1875,7 @@ async function runLint(restArgs, flags, excludes, config) {
|
|
|
1775
1875
|
frontmatterRules,
|
|
1776
1876
|
specRefIssues: specRefs.issues,
|
|
1777
1877
|
specRefErrors: specRefs.errors,
|
|
1878
|
+
compileBudgetErrors: specRefs.budgetErrors,
|
|
1778
1879
|
duplicatePairs: dups.pairCount,
|
|
1779
1880
|
duplicateSeverity: (0, types_js_1.ruleSeverity)(config?.rules?.["duplicate-rules"]) ?? "warn",
|
|
1780
1881
|
coverageEnabled: coverage.enabled,
|
|
@@ -1828,6 +1929,8 @@ async function runLint(restArgs, flags, excludes, config) {
|
|
|
1828
1929
|
hookMatcherErrors: hookMatcher.errors,
|
|
1829
1930
|
instructionWeightIssues: instructionWeight.issues,
|
|
1830
1931
|
instructionWeightErrors: instructionWeight.errors,
|
|
1932
|
+
deprecatedKeyFilesIssues: deprecatedKeyFiles.issues,
|
|
1933
|
+
deprecatedKeyFilesErrors: deprecatedKeyFiles.errors,
|
|
1831
1934
|
// Only the "error" tier gates. At "warn" the findings are printed and
|
|
1832
1935
|
// annotated, and the exit code is untouched — same contract as every other
|
|
1833
1936
|
// opt-in rule here.
|
|
@@ -2460,6 +2563,9 @@ export default instructionFile({${targetLine}
|
|
|
2460
2563
|
// Prose sections become ## headings in the compiled output.
|
|
2461
2564
|
// Do not add # or ## headers inside sections.
|
|
2462
2565
|
// positioning: "What this project does and why.",
|
|
2566
|
+
// A file a decision depends on: name it where the decision is, with
|
|
2567
|
+
// file() inside a prose section — verified to exist at compile time. No
|
|
2568
|
+
// file map: the agent finds files itself, and a map loads on every request.
|
|
2463
2569
|
|
|
2464
2570
|
// This section is included in the compiled output to help agents
|
|
2465
2571
|
// understand how to work with specs. Remove it once your team is familiar.
|
|
@@ -2472,11 +2578,6 @@ export default instructionFile({${targetLine}
|
|
|
2472
2578
|
// "npm test": "Run all tests",
|
|
2473
2579
|
},
|
|
2474
2580
|
|
|
2475
|
-
keyFiles: {
|
|
2476
|
-
// File paths are verified to exist at compile time.
|
|
2477
|
-
// "src/index.ts": "Main entry point",
|
|
2478
|
-
},
|
|
2479
|
-
|
|
2480
2581
|
rules: {
|
|
2481
2582
|
// enforce() — backed by a linter rule, verified to exist AND be enabled:
|
|
2482
2583
|
// "no-console": enforce("eslint/no-console", "Use structured logger."),
|
|
@@ -7242,11 +7343,7 @@ async function main() {
|
|
|
7242
7343
|
// contradiction. Refuse it (exit 2 = "could not do what you asked") rather
|
|
7243
7344
|
// than auditing the first and dropping the rest — silently honouring half
|
|
7244
7345
|
// an argument list is the same defect class as the ignored `--out` above.
|
|
7245
|
-
|
|
7246
|
-
console.error(`--single audits ONE directory as one harness, but ${String(dirs.length)} were given. ` +
|
|
7247
|
-
`Drop --single for a leaderboard, or pass a single directory.`);
|
|
7248
|
-
process.exit(2);
|
|
7249
|
-
}
|
|
7346
|
+
refuseSingleOverMany(single, dirs);
|
|
7250
7347
|
const targets = !single && market && market.onDisk.length > 0
|
|
7251
7348
|
? [...market.onDisk]
|
|
7252
7349
|
: dirs;
|
|
@@ -7336,6 +7433,7 @@ async function main() {
|
|
|
7336
7433
|
// (#281): cwd, unless the audited dir is somebody else's repository.
|
|
7337
7434
|
const frame = (0, frame_js_1.frameFor)(process.cwd(), root);
|
|
7338
7435
|
const report = (0, scan_js_1.scanPlugin)(targets[0], adapter.layout, adapter.dialect, {
|
|
7436
|
+
deprecatedKeyFiles: specsUsingKeyFiles(root, excludes.globIgnore),
|
|
7339
7437
|
sharedDirs: config.sharedDirs,
|
|
7340
7438
|
sharedDirsRoot: frame.root,
|
|
7341
7439
|
// `.vigilesrc.json#exclude` reaches surface DISCOVERY, not just the
|
|
@@ -15,10 +15,6 @@ exports.default = (0, spec_js_1.instructionFile)({
|
|
|
15
15
|
sections: {
|
|
16
16
|
scope: `Working in \`src/core/\`? This is the harness-AGNOSTIC domain (spec, compile, linters, the lint/audit detectors). The root \`CLAUDE.md\` holds the full positioning + rule set — read it first. Two invariants live closest to this code: the core must not import an adapter (\`core ⊄ adapter\`, eslint-enforced) and must not hard-code a Claude Code literal (read it from the injected layout/dialect). This file adds the rule for ADDING or CHANGING a detector.`,
|
|
17
17
|
},
|
|
18
|
-
keyFiles: {
|
|
19
|
-
"src/core/rule-meta.ts": "The RuleMeta registry — every rule's decidability bucket + severity + detector, the single source the detector-meta rule enforces.",
|
|
20
|
-
"src/core/types.ts": "RulesConfig — the rule-name keys the registry is keyed on.",
|
|
21
|
-
},
|
|
22
18
|
rules: {
|
|
23
19
|
"detector-meta": (0, spec_js_1.guidance)("A deterministic DETECTOR here is one half of a RULE — and a rule is not done until it is DECLARED. Three things move together (sibling of one-detector-no-drift + rules-docs-in-sync): (1) the pure detector function (shared by `lint` AND `audit`, never reimplemented per surface; read the layout/dialect, never a CC literal); (2) its entry in `src/core/rule-meta.ts` — the `Record<RuleName, RuleMeta>` won't typecheck without it — declaring its DECIDABILITY BUCKET (structural-closed = a type could prevent it / external-decidable = needs the world, error-capable / heuristic-behavioral = warn-or-measure-only), surface, defaultSeverity, the detector name, and any upstreamPrevention; (3) its `docs/rules/<name>.md` (the coverage test binds the registry to the docs by an EXACT set match, so a missing meta or doc fails CI). The bucket is the CEILING, not a preference — a heuristic proxy may NEVER default to `error` (it cries wolf); a structural/external fact MAY, once proven FP-safe. Before writing a new detector, CLASSIFY the defect into a bucket — that decides whether it can ever gate. The full model + the prose behind the buckets is the root `lint-rule-calibration` rule and `research/enforcement-model.md`."),
|
|
24
20
|
},
|
package/dist/core/compile.d.ts
CHANGED
|
@@ -56,7 +56,7 @@ export declare function verifyHash(content: string): {
|
|
|
56
56
|
*/
|
|
57
57
|
/** @internal */ export declare function estimateTokens(text: string): number;
|
|
58
58
|
export interface CompileError {
|
|
59
|
-
type: "stale-file" | "stale-command" | "stale-ref" | "invalid-rule" | "budget-exceeded" | "section-too-long" | "section-has-header" | "reserved-section-key" | "spec-name-mismatch" | "unknown-tool" | "invalid-railway" | "purity-violation" | "output-without-fork" | "effect-in-skill" | "inline-code-too-long" | "entry-too-long" | "section-too-large";
|
|
59
|
+
type: "stale-file" | "stale-command" | "stale-ref" | "invalid-rule" | "budget-exceeded" | "section-too-long" | "section-has-header" | "reserved-section-key" | "spec-name-mismatch" | "unknown-tool" | "invalid-railway" | "purity-violation" | "output-without-fork" | "effect-in-skill" | "inline-code-too-long" | "entry-too-long" | "section-too-large" | "rules-too-large";
|
|
60
60
|
message: string;
|
|
61
61
|
path?: string;
|
|
62
62
|
}
|
|
@@ -70,12 +70,21 @@ export interface CompileClaudeResult {
|
|
|
70
70
|
markdown: string;
|
|
71
71
|
errors: CompileError[];
|
|
72
72
|
/**
|
|
73
|
-
* Budget findings
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* channel discipline as
|
|
73
|
+
* Budget findings at the rule's DEFAULT severity (`compile-budget: "warn"`).
|
|
74
|
+
* An over-long `keyFiles` description or a bloated section does not break
|
|
75
|
+
* the harness, it makes every request more expensive, so by default it must
|
|
76
|
+
* not stop a faithful adoption from compiling. Same channel discipline as
|
|
77
|
+
* `checkInlineCode`. Empty when the rule is at `"error"` (the findings are in
|
|
78
|
+
* `errors` then) or off.
|
|
77
79
|
*/
|
|
78
80
|
warnings: CompileError[];
|
|
81
|
+
/**
|
|
82
|
+
* Every budget finding, whatever the severity — the SAME objects that were
|
|
83
|
+
* placed in `warnings` or `errors`. `lint` re-derives a spec's references
|
|
84
|
+
* through this compiler and must not report a size finding as a dead
|
|
85
|
+
* reference, so it tells the two apart by identity here, never by type name.
|
|
86
|
+
*/
|
|
87
|
+
budgetFindings: CompileError[];
|
|
79
88
|
linterResults: LinterCheckResult[];
|
|
80
89
|
/** Estimated token count of compiled output (~4 chars/token). */
|
|
81
90
|
tokens: number;
|
|
@@ -83,10 +92,21 @@ export interface CompileClaudeResult {
|
|
|
83
92
|
targets: string[];
|
|
84
93
|
}
|
|
85
94
|
export interface CompileClaudeOptions {
|
|
86
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* The `compile-budget` rule's severity. `"warn"` (the default) reports the
|
|
97
|
+
* per-entry and per-section budgets as warnings and evaluates the Rules total
|
|
98
|
+
* only when a threshold for it is passed; `"error"` (what
|
|
99
|
+
* `vigiles:recommended` sets) evaluates every budget, with defaults, as an
|
|
100
|
+
* error; `false` reports none (an explicit `maxTokens` still fails, as it
|
|
101
|
+
* always has — a separate check).
|
|
102
|
+
*/
|
|
103
|
+
budgetSeverity?: "warn" | "error" | false;
|
|
104
|
+
/** Per-entry `commands` character budget (0 disables). */
|
|
87
105
|
maxEntryChars?: number;
|
|
88
106
|
/** Per-section character budget, beside the line budget (0 disables). */
|
|
89
107
|
maxSectionChars?: number;
|
|
108
|
+
/** Character budget for the whole rendered Rules section (0 disables). */
|
|
109
|
+
maxRulesChars?: number;
|
|
90
110
|
basePath?: string;
|
|
91
111
|
specFile?: string;
|
|
92
112
|
/** Injected harness dialect; its instructionTargets[0] is the default target. */
|
|
@@ -136,6 +156,16 @@ export declare const DEFAULT_MAX_ENTRY_CHARS = 200;
|
|
|
136
156
|
* cost — characters do, because that is what the harness loads.
|
|
137
157
|
*/
|
|
138
158
|
export declare const DEFAULT_MAX_SECTION_CHARS = 15000;
|
|
159
|
+
/**
|
|
160
|
+
* The Rules section, as one budget. Under `compile-budget: "error"` only.
|
|
161
|
+
*
|
|
162
|
+
* WHY THE SECTION AND NOT EACH RULE. A per-rule length cap was built and
|
|
163
|
+
* removed within an hour: it opened on 40 of 53 rules, because a rule's length
|
|
164
|
+
* is not where the cost is — the SUM is, and it is loaded on every request.
|
|
165
|
+
* 20,000 characters is half of Claude Code's 40,000-character instruction
|
|
166
|
+
* budget, which leaves the other half for everything else the file carries.
|
|
167
|
+
*/
|
|
168
|
+
export declare const DEFAULT_MAX_RULES_CHARS = 20000;
|
|
139
169
|
/**
|
|
140
170
|
* Compile a ClaudeSpec into markdown.
|
|
141
171
|
*
|