vigiles 31.0.0 → 32.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/run-scripts.d.ts +5 -3
- package/dist/adapters/claude-code/run-scripts.js +5 -3
- package/dist/cli-main.d.ts +8 -0
- package/dist/cli-main.js +297 -192
- package/dist/core/coverage.d.ts +4 -3
- package/dist/core/coverage.js +4 -3
- package/dist/core/doc-refs.d.ts +3 -1
- package/dist/core/doc-refs.js +2 -1
- package/dist/core/frame.d.ts +90 -0
- package/dist/core/frame.js +58 -0
- package/dist/core/glob-ignore.d.ts +28 -0
- package/dist/core/glob-ignore.js +42 -0
- package/dist/core/orphans.d.ts +5 -3
- package/dist/core/orphans.js +5 -4
- package/dist/eval.d.ts +50 -11
- package/dist/eval.js +371 -152
- package/dist/exclude.d.ts +3 -5
- package/dist/exclude.js +3 -3
- package/dist/scan.d.ts +3 -3
- package/dist/scan.js +8 -7
- package/dist/test-coverage.d.ts +20 -3
- package/dist/test-coverage.js +40 -14
- package/package.json +3 -2
package/dist/scan.js
CHANGED
|
@@ -230,13 +230,14 @@ function scanPlugin(dir, layout, dialect, opts = {}) {
|
|
|
230
230
|
const coverage = (0, test_coverage_js_1.findUntestedSurfaces)({
|
|
231
231
|
basePath: dir,
|
|
232
232
|
layout: lay,
|
|
233
|
-
// The SAME `.vigilesrc.json#exclude
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
|
|
233
|
+
// The SAME `.vigilesrc.json#exclude`. Untested-surface discovery is a
|
|
234
|
+
// second walk over the same trees, so leaving it out would have excluded a
|
|
235
|
+
// skill from the GRADE while still naming it in "Untested surfaces: 1" — a
|
|
236
|
+
// report contradicting itself about whether the file exists. Passed WHOLE,
|
|
237
|
+
// not as a pattern list: `dir` is often not the repo root (`audit plugins/p`),
|
|
238
|
+
// and a root-relative list globbed from here matched nothing it named —
|
|
239
|
+
// `inventory.skills: 1` beside `untested: 2` (#281, D7).
|
|
240
|
+
excludes: opts.excludes,
|
|
240
241
|
});
|
|
241
242
|
const caveats = (0, test_coverage_js_1.coverageCaveats)(coverage);
|
|
242
243
|
return {
|
package/dist/test-coverage.d.ts
CHANGED
|
@@ -60,6 +60,8 @@
|
|
|
60
60
|
* magnitude in cost without saying which. {@link UntestedReport} therefore carries
|
|
61
61
|
* a per-tier {@link CoverageTier} alongside the (unchanged) union fields.
|
|
62
62
|
*/
|
|
63
|
+
import { type ExcludeSet } from "./exclude.js";
|
|
64
|
+
import type { RepoPath } from "./core/frame.js";
|
|
63
65
|
import { type PluginLayout } from "./core/layout.js";
|
|
64
66
|
import { type CoverageEvidence, type EvidenceCounts } from "./coverage-evidence.js";
|
|
65
67
|
export type SurfaceKind = "skill" | "agent" | "hook";
|
|
@@ -185,8 +187,14 @@ export interface UntestedReport {
|
|
|
185
187
|
readonly evals: CoverageTier;
|
|
186
188
|
}
|
|
187
189
|
export interface TestCoverageOptions {
|
|
188
|
-
/**
|
|
190
|
+
/** The bundle whose surfaces are discovered. Defaults to `process.cwd()`. */
|
|
189
191
|
readonly basePath?: string;
|
|
192
|
+
/**
|
|
193
|
+
* The directory `include`, `exclude`, the coverage artifact and every REPORTED
|
|
194
|
+
* path are relative to — where `.vigilesrc.json` lives. Defaults to `basePath`.
|
|
195
|
+
* Differs from it for a nested bundle under `bundles: "all"` (#281).
|
|
196
|
+
*/
|
|
197
|
+
readonly root?: string;
|
|
190
198
|
/** Scan skills under `skills/` and `.claude/skills/`. Default true. */
|
|
191
199
|
readonly skills?: boolean;
|
|
192
200
|
/** Scan subagents under `agents/` and `.claude/agents/`. Default true. */
|
|
@@ -210,8 +218,17 @@ export interface TestCoverageOptions {
|
|
|
210
218
|
* `mjs` was silently ignored on any TypeScript-shaped repo.
|
|
211
219
|
*/
|
|
212
220
|
readonly testExtension?: string;
|
|
213
|
-
/**
|
|
221
|
+
/**
|
|
222
|
+
* The rule's own `exclude` globs, relative to `root` (added to
|
|
223
|
+
* node_modules/dist/.git/.vigiles). NARROWS on top of {@link excludes}.
|
|
224
|
+
*/
|
|
214
225
|
readonly exclude?: readonly string[];
|
|
226
|
+
/**
|
|
227
|
+
* The repo-wide `.vigilesrc.json#exclude`. It carries its own root, so it is
|
|
228
|
+
* applied correctly whichever directory is being discovered — a string list
|
|
229
|
+
* here was correct only when that directory WAS the repo root (#281).
|
|
230
|
+
*/
|
|
231
|
+
readonly excludes?: ExcludeSet;
|
|
215
232
|
/**
|
|
216
233
|
* Harness layout — where skills/agents live, the plugin-root token, the
|
|
217
234
|
* manifest/settings paths. Defaults to Claude Code; a non-CC adapter passes its
|
|
@@ -266,7 +283,7 @@ export declare function coverageEvidenceCounts(report: UntestedReport): Evidence
|
|
|
266
283
|
* Returns `null` when the edited file isn't a skill/agent surface, or when it is
|
|
267
284
|
* covered on both tiers. Never throws — a nudge must not disrupt an edit.
|
|
268
285
|
*/
|
|
269
|
-
export declare function skillTestNudge(filePath:
|
|
286
|
+
export declare function skillTestNudge(filePath: RepoPath, options: TestCoverageOptions): string | null;
|
|
270
287
|
/**
|
|
271
288
|
* What the PAID eval tier measures for a surface of this kind — or `null` when
|
|
272
289
|
* it measures nothing for it.
|
package/dist/test-coverage.js
CHANGED
|
@@ -71,10 +71,13 @@ exports.coverageCaveats = coverageCaveats;
|
|
|
71
71
|
exports.formatUntestedReport = formatUntestedReport;
|
|
72
72
|
const node_fs_1 = require("node:fs");
|
|
73
73
|
const node_path_1 = require("node:path");
|
|
74
|
+
const minimatch_1 = require("minimatch");
|
|
74
75
|
const test_file_ext_js_1 = require("./core/test-file-ext.js");
|
|
75
76
|
const assert_never_js_1 = require("./core/assert-never.js");
|
|
76
77
|
const ts_runner_caps_js_1 = require("./ts-runner-caps.js");
|
|
77
78
|
const glob_1 = require("glob");
|
|
79
|
+
const glob_ignore_js_1 = require("./core/glob-ignore.js");
|
|
80
|
+
const exclude_js_1 = require("./exclude.js");
|
|
78
81
|
const layout_js_1 = require("./core/layout.js");
|
|
79
82
|
const coverage_evidence_js_1 = require("./coverage-evidence.js");
|
|
80
83
|
const coverage_artifact_js_1 = require("./coverage-artifact.js");
|
|
@@ -427,27 +430,47 @@ function tierOf(considered, tests, index, tier, globs) {
|
|
|
427
430
|
*/
|
|
428
431
|
function findUntestedSurfaces(options) {
|
|
429
432
|
const basePath = options.basePath ?? process.cwd();
|
|
433
|
+
const root = options.root ?? basePath;
|
|
430
434
|
const { layout } = options;
|
|
431
|
-
const
|
|
435
|
+
const ruleIgnore = [...DEFAULT_IGNORE, ...(options.exclude ?? [])];
|
|
436
|
+
const ignore = (0, glob_ignore_js_1.withIgnored)(ruleIgnore, options.excludes?.globIgnore);
|
|
432
437
|
const globs = options.include ?? DEFAULT_TEST_GLOBS;
|
|
433
|
-
|
|
438
|
+
// #281: surfaces are FOUND under the bundle and then re-expressed from `root`,
|
|
439
|
+
// so every later comparison (colocation, `{surface}` globs, the run index,
|
|
440
|
+
// exclude, the printed path) happens in ONE frame.
|
|
441
|
+
const prefix = (0, node_path_1.relative)(root, basePath).split(node_path_1.sep).join("/");
|
|
442
|
+
const toRoot = (s) => prefix === "" ? s : { ...s, path: `${prefix}/${s.path}` };
|
|
443
|
+
const repoExcluded = (0, exclude_js_1.excludedBy)(options.excludes);
|
|
444
|
+
const excluded = (p) => ruleIgnore.some((g) => (0, minimatch_1.minimatch)(p, g, { dot: true })) ||
|
|
445
|
+
repoExcluded((0, node_path_1.join)(root, p));
|
|
446
|
+
// Discovery globs from the BUNDLE, so it gets only the frame-free floor; the
|
|
447
|
+
// rule's patterns are root-relative and the repo's carry their own root, so
|
|
448
|
+
// both are applied below, to the root-relative path.
|
|
449
|
+
const floor = [...DEFAULT_IGNORE];
|
|
450
|
+
const found = [];
|
|
434
451
|
if (options.skills !== false)
|
|
435
|
-
|
|
452
|
+
found.push(...discoverSkills(basePath, floor, layout));
|
|
436
453
|
if (options.agents !== false)
|
|
437
|
-
|
|
454
|
+
found.push(...discoverAgents(basePath, floor, layout));
|
|
438
455
|
if (options.hooks !== false)
|
|
439
|
-
|
|
456
|
+
found.push(...discoverHooks(basePath, layout));
|
|
457
|
+
// Hooks were never subject to `ignore` (a compiled hook lives under the
|
|
458
|
+
// default-ignored `.vigiles/`); keep that, and re-apply exclude to the kinds
|
|
459
|
+
// whose discovery glob used it — now from `root`, where the patterns live.
|
|
460
|
+
const surfaces = found
|
|
461
|
+
.map(toRoot)
|
|
462
|
+
.filter((s) => s.kind === "hook" || !excluded(s.path));
|
|
440
463
|
// Every skill/agent/hook is held to the requirement — only an explicit
|
|
441
464
|
// `vigiles:ignore-test` marker exempts a surface (a visible, deliberate skip).
|
|
442
465
|
const considered = surfaces.filter((s) => !s.ignored);
|
|
443
466
|
const exempt = surfaces.length - considered.length;
|
|
444
|
-
const tests = discoverTests(
|
|
467
|
+
const tests = discoverTests(root, globs, ignore);
|
|
445
468
|
const split = partitionTests(tests);
|
|
446
469
|
// The run record, if there is one. NO artifact ⇒ an empty index ⇒ every
|
|
447
470
|
// decision below falls through to colocation, byte-for-byte as before: a fresh
|
|
448
471
|
// clone and someone else's repo must not get one extra nudge from this tier.
|
|
449
|
-
const runIndex = (0, coverage_artifact_js_1.indexRuns)((0, coverage_artifact_js_1.readCoverageArtifact)(
|
|
450
|
-
const abs = (0, node_path_1.join)(
|
|
472
|
+
const runIndex = (0, coverage_artifact_js_1.indexRuns)((0, coverage_artifact_js_1.readCoverageArtifact)(root), (p) => {
|
|
473
|
+
const abs = (0, node_path_1.join)(root, p);
|
|
451
474
|
return (0, node_fs_1.existsSync)(abs) ? (0, coverage_artifact_js_1.surfaceSha)(read(abs)) : null;
|
|
452
475
|
},
|
|
453
476
|
// The SCRIPT that did the exercising has to still be here too — a deleted or
|
|
@@ -455,7 +478,7 @@ function findUntestedSurfaces(options) {
|
|
|
455
478
|
// record is permanent, unfalsifiable coverage. `canonicalScript` first: one
|
|
456
479
|
// file has several legitimate spellings (`x.mjs`, `./x.mjs`, absolute), and
|
|
457
480
|
// the artifact records whichever one was typed.
|
|
458
|
-
(by) => (0, node_fs_1.existsSync)((0, node_path_1.join)(
|
|
481
|
+
(by) => (0, node_fs_1.existsSync)((0, node_path_1.join)(root, (0, coverage_artifact_js_1.canonicalScript)(by, root))));
|
|
459
482
|
const union = tierOf(considered, tests, runIndex, undefined, globs);
|
|
460
483
|
return {
|
|
461
484
|
total: considered.length,
|
|
@@ -477,8 +500,8 @@ function findUntestedSurfaces(options) {
|
|
|
477
500
|
}),
|
|
478
501
|
legacyCoversFiles: tests
|
|
479
502
|
.map((t) => t.path)
|
|
480
|
-
.filter((path) => read((0, node_path_1.join)(
|
|
481
|
-
retiredTestNames: retiredTestNamesFor(
|
|
503
|
+
.filter((path) => read((0, node_path_1.join)(root, path)).includes(LEGACY_COVERS)),
|
|
504
|
+
retiredTestNames: retiredTestNamesFor(root, union.untested),
|
|
482
505
|
decisions: union.decisions,
|
|
483
506
|
harness: tierOf(considered, split.harness, runIndex, "harness", globs),
|
|
484
507
|
evals: tierOf(considered, split.evals, runIndex, "eval", globs),
|
|
@@ -549,9 +572,12 @@ function skillTestNudge(filePath, options) {
|
|
|
549
572
|
catch {
|
|
550
573
|
return null; // a broken scan must never surface as a broken edit
|
|
551
574
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
575
|
+
// 🔴 EQUALITY, IN ONE FRAME (#281, D6). This used to accept any path ENDING in
|
|
576
|
+
// a surface's path, so editing `plugins/p/skills/x/SKILL.md` matched the root
|
|
577
|
+
// bundle's `skills/x/SKILL.md` and reported THAT skill's coverage as the
|
|
578
|
+
// edited one's. The caller now hands a `RepoPath` and the report's paths are
|
|
579
|
+
// relative to the same `root`, so a suffix has nothing left to rescue.
|
|
580
|
+
const isTarget = (s) => s.path === filePath;
|
|
555
581
|
const untested = report.untested.find(isTarget);
|
|
556
582
|
if (untested)
|
|
557
583
|
return (`vigiles: you edited ${untested.path}, and nothing measures whether it ` +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vigiles",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "32.0.0",
|
|
4
4
|
"description": "Audit, test and measure the harness your AI agent runs on — grade your CLAUDE.md / AGENTS.md, skills, subagents and hooks, run them against a scripted model, and measure whether they actually fire.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"@typescript-eslint/eslint-plugin": "^8.58.0",
|
|
110
110
|
"@typescript-eslint/parser": "^8.58.0",
|
|
111
111
|
"@vitest/coverage-v8": "^4.1.8",
|
|
112
|
-
"conventional-changelog-conventionalcommits": "
|
|
112
|
+
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
113
113
|
"eslint": "^10.1.0",
|
|
114
114
|
"eslint-import-resolver-typescript": "^4.4.5",
|
|
115
115
|
"eslint-plugin-boundaries": "^6.0.2",
|
|
@@ -118,6 +118,7 @@
|
|
|
118
118
|
"jest": "^30.4.2",
|
|
119
119
|
"picomatch": "^4.0.4",
|
|
120
120
|
"prettier": "^3.8.1",
|
|
121
|
+
"semantic-release": "25.0.9",
|
|
121
122
|
"tsx": "^4.21.0",
|
|
122
123
|
"typedoc": "^0.28.19",
|
|
123
124
|
"typescript": "^5.9.3",
|