vigiles 17.0.2 โ 18.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/audit-score.js +34 -1
- package/dist/cli.d.ts +7 -6
- package/dist/cli.js +107 -42
- package/package.json +1 -1
- package/skills/test-harness/SKILL.md +14 -0
package/dist/audit-score.js
CHANGED
|
@@ -303,12 +303,45 @@ function tested(r) {
|
|
|
303
303
|
"your own test setup detected โ vigiles-native skill coverage is optional",
|
|
304
304
|
]
|
|
305
305
|
: findings;
|
|
306
|
+
// ๐ด COVERED BY PLACEMENT ALONE โ the execution tier, finally said out loud.
|
|
307
|
+
//
|
|
308
|
+
// `colocated` evidence means a test file NAMED after the surface sits BESIDE it. That is
|
|
309
|
+
// a claim about the filesystem, and the provenance line already admits it ("this says the
|
|
310
|
+
// file EXISTS, not that it ran"). Nobody reads a provenance line. When `executed` is zero
|
|
311
|
+
// across the whole corpus, EVERY surface counted here rests on a filename, and the number
|
|
312
|
+
// above reads as health it has not earned.
|
|
313
|
+
//
|
|
314
|
+
// Measured in a real consumer 2026-08-18: 26 colocated, 0 executed โ while its CI invoked
|
|
315
|
+
// exactly those harnesses in a job that never installed the `claude` CLI, so each one
|
|
316
|
+
// called `skip()` and the step reported success. Coverage looked fine the entire time.
|
|
317
|
+
//
|
|
318
|
+
// Deliberately NOT "your CI does not run these": that needs parsing CI config, and the
|
|
319
|
+
// grep-shaped version accuses a healthy repo, because real workflows invoke harnesses by
|
|
320
|
+
// GLOB and name no file. This says only what the run records say, so it cannot be wrong
|
|
321
|
+
// about a repo it has not looked at.
|
|
322
|
+
//
|
|
323
|
+
// โ ๏ธ KNOWN LIMIT, stated rather than hidden: the threshold is CORPUS-WIDE, so ONE recorded
|
|
324
|
+
// run anywhere silences it for every surface. That is what the data supports โ the evidence
|
|
325
|
+
// this receives is an aggregate tally, not a per-surface verdict โ and the same consumer
|
|
326
|
+
// demonstrates the cost: it read 0 executed / 26 colocated in the morning and 16 / 26 by
|
|
327
|
+
// evening, after which twenty-six surfaces resting on a filename would no longer be named.
|
|
328
|
+
// Sharpening this means carrying evidence per surface, which is a change to the producer,
|
|
329
|
+
// not to this sentence. Until then it catches the state that actually shipped (a corpus
|
|
330
|
+
// where the tier is entirely absent) and stays quiet the moment the tier exists at all.
|
|
331
|
+
const ev = r.coverageEvidence;
|
|
332
|
+
const placementOnly = ev && ev.executed === 0 && ev.colocated > 0
|
|
333
|
+
? [
|
|
334
|
+
...contextualized,
|
|
335
|
+
`${String(ev.colocated)} surface(s) counted as covered by PLACEMENT only โ ` +
|
|
336
|
+
`no run on record ever exercised one`,
|
|
337
|
+
]
|
|
338
|
+
: contextualized;
|
|
306
339
|
return {
|
|
307
340
|
key: "Tested",
|
|
308
341
|
score,
|
|
309
342
|
weight: 1,
|
|
310
343
|
advisory: true,
|
|
311
|
-
findings:
|
|
344
|
+
findings: placementOnly,
|
|
312
345
|
};
|
|
313
346
|
}
|
|
314
347
|
/**
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* vigiles CLI โ
|
|
3
|
+
* vigiles CLI โ verify your agent harness is real, and prove it works.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* The verbs and their one-liners live in ONE place, `COMMAND_HELP` + `HELP_GROUPS`
|
|
6
|
+
* near the bottom of this file, and `--help` prints from that table. A second list
|
|
7
|
+
* here would be a copy that rots โ this docblock WAS that copy: it named four
|
|
8
|
+
* commands and omitted `audit`, `test`, `eval` and `eject`, four of the eight, and
|
|
9
|
+
* `self-command-refs.test.ts` did not catch it because it guards against refs to
|
|
10
|
+
* REMOVED commands, not against a list that merely stops growing.
|
|
10
11
|
*/
|
|
11
12
|
export {};
|
|
12
13
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
* vigiles CLI โ
|
|
4
|
+
* vigiles CLI โ verify your agent harness is real, and prove it works.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* The verbs and their one-liners live in ONE place, `COMMAND_HELP` + `HELP_GROUPS`
|
|
7
|
+
* near the bottom of this file, and `--help` prints from that table. A second list
|
|
8
|
+
* here would be a copy that rots โ this docblock WAS that copy: it named four
|
|
9
|
+
* commands and omitted `audit`, `test`, `eval` and `eject`, four of the eight, and
|
|
10
|
+
* `self-command-refs.test.ts` did not catch it because it guards against refs to
|
|
11
|
+
* REMOVED commands, not against a list that merely stops growing.
|
|
11
12
|
*/
|
|
12
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
14
|
const node_fs_1 = require("node:fs");
|
|
@@ -4291,6 +4292,32 @@ async function handleRunScripts(kind, args, restArgs) {
|
|
|
4291
4292
|
process.exit(1);
|
|
4292
4293
|
}
|
|
4293
4294
|
if (files.length === 0) {
|
|
4295
|
+
// ๐ด ASKING FOR SOMETHING AND GETTING NOTHING IS A FAILURE; FINDING NOTHING IS NOT.
|
|
4296
|
+
// The two cases were collapsed into one silent exit 0, and the collapse cost a real
|
|
4297
|
+
// repository three days of green CI verifying zero files: a named step ran
|
|
4298
|
+
// `vigiles test .claude/pipeline/skills.harness.mjs` after that file had been split
|
|
4299
|
+
// into one-per-skill, printed "No **/*.harness.* files found" and passed, right next
|
|
4300
|
+
// to a step that was red for the same root cause.
|
|
4301
|
+
//
|
|
4302
|
+
// They are different states. A POSITIONAL argument is a claim that something is there โ
|
|
4303
|
+
// when nothing matches it, the path is stale, the glob is wrong, or the run never
|
|
4304
|
+
// reached its target, and every one of those is a defect. Bare discovery finding
|
|
4305
|
+
// nothing is just an empty repository, which is a legitimate place to stand and must
|
|
4306
|
+
// stay quiet.
|
|
4307
|
+
//
|
|
4308
|
+
// This is the default the field settled on: Jest and Vitest FAIL on no tests found and
|
|
4309
|
+
// make you opt in with `--passWithNoTests`; pytest exits 5. `--min=0` remains the
|
|
4310
|
+
// explicit opt-out here, so no new flag is introduced by this change.
|
|
4311
|
+
// `minFlag`, not `minRequired`: 0 is both the DEFAULT and the explicit opt-out, so the
|
|
4312
|
+
// VALUE cannot tell them apart โ only the flag's presence can. (Caught by a control:
|
|
4313
|
+
// the first version read `minRequired === 0` and made `--min=0` do nothing.)
|
|
4314
|
+
if (restArgs.length > 0 && minFlag === undefined) {
|
|
4315
|
+
console.error(`โ vigiles ${kind}: ${String(restArgs.length)} target(s) given and NOTHING matched โ ` +
|
|
4316
|
+
`${restArgs.join(", ")}\n` +
|
|
4317
|
+
` Nothing ran. A stale path, a wrong glob, or a moved file all look like this.\n` +
|
|
4318
|
+
` If an empty match is expected here, say so with --min=0.`);
|
|
4319
|
+
process.exit(1);
|
|
4320
|
+
}
|
|
4294
4321
|
console.log(`No ${defaultGlob} files found.`);
|
|
4295
4322
|
return;
|
|
4296
4323
|
}
|
|
@@ -4380,35 +4407,42 @@ function capabilitiesOfReport(report, dialect) {
|
|
|
4380
4407
|
}
|
|
4381
4408
|
const COMMAND_HELP = {
|
|
4382
4409
|
init: {
|
|
4383
|
-
usage: " vigiles init [flags]
|
|
4410
|
+
usage: " vigiles init [flags] Set up this repo โ specs, plugin, and CI.",
|
|
4384
4411
|
},
|
|
4385
|
-
compile: { usage: " vigiles compile [files...]
|
|
4412
|
+
compile: { usage: " vigiles compile [files...] Compile .spec.ts โ .md" },
|
|
4386
4413
|
eject: {
|
|
4387
|
-
usage: " vigiles eject [file]
|
|
4414
|
+
usage: " vigiles eject [file] Hand a compiled file back as plain markdown.",
|
|
4388
4415
|
},
|
|
4389
4416
|
lint: {
|
|
4390
|
-
usage: " vigiles lint
|
|
4417
|
+
usage: " vigiles lint [files...] Gate it in CI. The same checks โ but a finding fails the build.",
|
|
4391
4418
|
},
|
|
4392
4419
|
audit: {
|
|
4393
|
-
|
|
4420
|
+
// "Reports everything, fails nothing" states always-exit-0 as the FEATURE it is. The line
|
|
4421
|
+
// it replaces had to end with "NOT a CI step โ use `vigiles lint` in CI", and a help text
|
|
4422
|
+
// that must say what a command ISN'T is a description that failed. Deleting that sentence
|
|
4423
|
+
// was the checkable success criterion for this rewrite.
|
|
4424
|
+
usage: " vigiles audit [dir...] Grade it on your machine. Reports everything, fails nothing.",
|
|
4394
4425
|
detail: [
|
|
4395
|
-
"
|
|
4396
|
-
"
|
|
4397
|
-
"
|
|
4426
|
+
" 2+ dirs โ a leaderboard. Writes vigiles-report.html + .json (auto-gitignored).",
|
|
4427
|
+
" The executing checks (run your hooks ยท live MCP ยท do skills fire?) run only",
|
|
4428
|
+
" interactively โ audit asks once and remembers; automation uses the testing API.",
|
|
4398
4429
|
],
|
|
4399
4430
|
},
|
|
4400
4431
|
test: {
|
|
4401
|
-
|
|
4432
|
+
// "Free, no API key" is the CONSEQUENCE; "deterministic" was the mechanism, and a reader
|
|
4433
|
+
// deciding whether to put this in CI needs the cost, not the implementation.
|
|
4434
|
+
usage: " vigiles test [files...] Against a scripted stand-in model. Free, no API key โ every commit.",
|
|
4402
4435
|
},
|
|
4403
4436
|
eval: {
|
|
4404
|
-
usage: " vigiles eval
|
|
4437
|
+
usage: " vigiles eval [files...] Against a real model. Spends your subscription โ on demand.",
|
|
4405
4438
|
detail: [
|
|
4406
|
-
"
|
|
4407
|
-
"
|
|
4439
|
+
" --update records each named eval's result to a committed lock (run it locally).",
|
|
4440
|
+
" --check verifies those committed results against current inputs with NO model โ",
|
|
4441
|
+
" the CI-safe half.",
|
|
4408
4442
|
],
|
|
4409
4443
|
},
|
|
4410
4444
|
generate: {
|
|
4411
|
-
usage: " vigiles generate <kind>
|
|
4445
|
+
usage: " vigiles generate <kind> Emit a dev-toolchain artifact: types ยท schema ยท harness",
|
|
4412
4446
|
detail: [
|
|
4413
4447
|
" vigiles generate <kind> --check Verify the generated file is up to date",
|
|
4414
4448
|
],
|
|
@@ -4418,19 +4452,39 @@ const COMMAND_HELP = {
|
|
|
4418
4452
|
},
|
|
4419
4453
|
};
|
|
4420
4454
|
/** Display order of the human-facing verbs in the banner's "Commands:" block. */
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4455
|
+
/**
|
|
4456
|
+
* The top-level help, as GROUPS. One table, so the printer cannot drift from the
|
|
4457
|
+
* grouping and a new verb cannot quietly land outside both.
|
|
4458
|
+
*/
|
|
4459
|
+
const HELP_GROUPS = [
|
|
4460
|
+
{
|
|
4461
|
+
heading: "Set up and manage your specs:",
|
|
4462
|
+
verbs: ["init", "compile", "eject"],
|
|
4463
|
+
},
|
|
4464
|
+
{
|
|
4465
|
+
heading: "Check your harness (reads your files โ nothing is executed):",
|
|
4466
|
+
verbs: ["audit", "lint"],
|
|
4467
|
+
},
|
|
4468
|
+
{
|
|
4469
|
+
heading: "Run your harness (drives it and watches what happens):",
|
|
4470
|
+
verbs: ["test", "eval"],
|
|
4471
|
+
},
|
|
4429
4472
|
];
|
|
4430
|
-
|
|
4473
|
+
/**
|
|
4474
|
+
* One command's line. `detail` is the per-flag prose and appears ONLY in
|
|
4475
|
+
* `vigiles <verb> --help`, never in the top-level list.
|
|
4476
|
+
*
|
|
4477
|
+
* That split is the second half of this rewrite. `audit`'s entry used to carry four
|
|
4478
|
+
* wrapped lines naming nine flags inline, and that single entry was most of the felt
|
|
4479
|
+
* crowding in a CLI whose verb count (8) is the smallest of every comparable tool
|
|
4480
|
+
* measured โ vitest ships 8 verbs and 164 flags, cargo 48 verbs, git 166. None of them
|
|
4481
|
+
* thinned their surface by removing verbs; they tiered the help. This does the same.
|
|
4482
|
+
*/
|
|
4483
|
+
function printHelpEntry(v, opts = {}) {
|
|
4431
4484
|
console.log(COMMAND_HELP[v].usage);
|
|
4432
|
-
|
|
4433
|
-
|
|
4485
|
+
if (opts.detail)
|
|
4486
|
+
for (const line of COMMAND_HELP[v].detail ?? [])
|
|
4487
|
+
console.log(line);
|
|
4434
4488
|
}
|
|
4435
4489
|
/**
|
|
4436
4490
|
* The loud "there is nothing here to audit" block. Deliberately says WHAT was
|
|
@@ -4470,27 +4524,38 @@ function formatNothingToAudit(root, harness, market) {
|
|
|
4470
4524
|
}
|
|
4471
4525
|
/** `vigiles <verb> --help` โ that verb's entry plus its complete flag list. */
|
|
4472
4526
|
function printCommandHelp(command) {
|
|
4473
|
-
printHelpEntry(command);
|
|
4527
|
+
printHelpEntry(command, { detail: true });
|
|
4474
4528
|
const flags = (0, cli_flag_check_js_1.knownFlagsFor)(command);
|
|
4475
4529
|
console.log("");
|
|
4476
4530
|
console.log(`Flags: ${[...flags].sort().join(" ")}`);
|
|
4477
4531
|
console.log("(`vigiles --help` lists every command.)");
|
|
4478
4532
|
}
|
|
4533
|
+
/**
|
|
4534
|
+
* The top-level help, grouped. The grouping is load-bearing, not cosmetic: four verbs
|
|
4535
|
+
* (`audit`, `lint`, `test`, `eval`) all read as "check my stuff", and a flat list left the
|
|
4536
|
+
* reader to work out the difference from four independent sentences. The headings state the
|
|
4537
|
+
* shared trait, which frees each verb's own line to state only what makes it different, so
|
|
4538
|
+
* the four form a 2x2 that survives one pass:
|
|
4539
|
+
*
|
|
4540
|
+
* no consequence has a consequence
|
|
4541
|
+
* read the files audit (fails nothing) lint (fails the build)
|
|
4542
|
+
* run the harness test (free) eval (spends money)
|
|
4543
|
+
*/
|
|
4479
4544
|
function printUsage(command) {
|
|
4480
|
-
console.log("vigiles โ
|
|
4481
|
-
console.log("");
|
|
4482
|
-
console.log("Commands:");
|
|
4483
|
-
for (const v of HELP_ORDER)
|
|
4484
|
-
printHelpEntry(v);
|
|
4485
|
-
console.log("");
|
|
4486
|
-
console.log("Examples:");
|
|
4487
|
-
console.log(" vigiles init Auto-detect project, create specs, wire CI");
|
|
4488
|
-
console.log(" vigiles compile Compile all .spec.ts files");
|
|
4489
|
-
console.log(" vigiles lint Verify references, hashes, coverage + suggestions");
|
|
4545
|
+
console.log("vigiles โ verify your agent harness is real, and prove it works");
|
|
4490
4546
|
console.log("");
|
|
4547
|
+
for (const g of HELP_GROUPS) {
|
|
4548
|
+
console.log(g.heading);
|
|
4549
|
+
for (const v of g.verbs)
|
|
4550
|
+
printHelpEntry(v);
|
|
4551
|
+
console.log("");
|
|
4552
|
+
}
|
|
4491
4553
|
console.log("Plumbing:");
|
|
4492
4554
|
printHelpEntry("generate");
|
|
4493
|
-
console.log(" vigiles --version
|
|
4555
|
+
console.log(" vigiles --version Print the version number");
|
|
4556
|
+
console.log("");
|
|
4557
|
+
console.log("Flags live in `vigiles <command> --help`.");
|
|
4558
|
+
console.log("New here? Start with `vigiles audit .`");
|
|
4494
4559
|
if (command && command !== "--help") {
|
|
4495
4560
|
console.log(`\nUnknown command: "${command}"`);
|
|
4496
4561
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vigiles",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "18.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",
|
|
@@ -326,6 +326,20 @@ We do **not** show "% of your subscription" โ Anthropic doesn't expose a plan'
|
|
|
326
326
|
quota, so any percentage would be invented. Tokens + API-equivalent `$` + the
|
|
327
327
|
billed-to line is the honest, complete picture. Keep the user's cost visible, always.
|
|
328
328
|
|
|
329
|
+
## CI โ don't hand-write the steps
|
|
330
|
+
|
|
331
|
+
These tiers belong in CI, and there is a published Action for it. Run `vigiles init`: it
|
|
332
|
+
writes `.github/workflows/vigiles.yml`, wiring the Action (`zernie/vigiles@v1`) for the jobs
|
|
333
|
+
that can use it plus a plain `npx vigiles test` job for this tier โ that one needs
|
|
334
|
+
repo-local `node_modules`, which the Action does not install, so it stays hand-rolled on
|
|
335
|
+
purpose.
|
|
336
|
+
|
|
337
|
+
If the repo already has a workflow, the Action's inputs are documented in
|
|
338
|
+
[docs/github-action.md](../../docs/github-action.md). Read them there rather than guessing:
|
|
339
|
+
the input list is defined in `action.yml`, and a copy of it here would be a second source of
|
|
340
|
+
truth that goes stale without anything noticing โ which is exactly what happened to this
|
|
341
|
+
file's own sibling docs and to a consumer's CI comment, both measured on 2026-08-18.
|
|
342
|
+
|
|
329
343
|
## Step 5 โ Lock the eval so CI stays honest (you do this automatically)
|
|
330
344
|
|
|
331
345
|
Real-model evals run on the user's subscription โ locally, never in CI. So **as
|