vigiles 7.0.0 → 9.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/README.md +207 -88
- package/dist/adoptability.d.ts +55 -0
- package/dist/adoptability.js +196 -0
- package/dist/audit-html.d.ts +20 -0
- package/dist/audit-html.js +61 -0
- package/dist/audit-prompts.d.ts +46 -0
- package/dist/audit-prompts.js +90 -0
- package/dist/audit-report.d.ts +70 -0
- package/dist/audit-report.js +51 -0
- package/dist/audit-report.template.html +110 -0
- package/dist/audit-score.d.ts +44 -0
- package/dist/audit-score.js +221 -0
- package/dist/cli-commands.d.ts +1 -1
- package/dist/cli-commands.js +3 -7
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +749 -180
- package/dist/core/adopt.d.ts +65 -0
- package/dist/core/adopt.js +199 -0
- package/dist/core/compose.d.ts +1 -1
- package/dist/core/compose.js +1 -1
- package/dist/core/evolve.d.ts +4 -0
- package/dist/core/evolve.js +4 -0
- package/dist/core/frontmatter.d.ts +8 -7
- package/dist/core/frontmatter.js +8 -7
- package/dist/core/generate-harness.d.ts +1 -1
- package/dist/core/generate-harness.js +3 -3
- package/dist/core/generate-schema.js +1 -1
- package/dist/core/inline.d.ts +6 -6
- package/dist/core/inline.js +17 -7
- package/dist/core/integrity.d.ts +31 -0
- package/dist/core/integrity.js +45 -0
- package/dist/core/orphans.js +1 -1
- package/dist/core/spec.d.ts +40 -2
- package/dist/core/spec.js +16 -1
- package/dist/core/types.d.ts +42 -6
- package/dist/core/validate.js +26 -26
- package/dist/dialect-drift.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +1 -1
- package/dist/guardrail-check.d.ts +1 -1
- package/dist/guardrail-check.js +1 -1
- package/dist/optimize.d.ts +12 -5
- package/dist/optimize.js +27 -5
- package/dist/scan-behavioral.d.ts +8 -2
- package/dist/scan-behavioral.js +6 -4
- package/dist/scan-trigger-suggest.d.ts +91 -0
- package/dist/scan-trigger-suggest.js +103 -0
- package/dist/scan.d.ts +53 -12
- package/dist/scan.js +92 -16
- package/dist/score-explainer.d.ts +1 -1
- package/dist/setup-plan.d.ts +59 -1
- package/dist/setup-plan.js +103 -5
- package/hooks/post-edit.sh +1 -1
- package/package.json +4 -2
- package/skills/adopt-spec/SKILL.md +7 -7
- package/skills/linter-docs/eslint.md +1 -1
- package/skills/strengthen/SKILL.md +1 -1
- package/skills/test-harness/SKILL.md +1 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Category scoring for `vigiles audit` — the Lighthouse rings.
|
|
3
|
+
*
|
|
4
|
+
* A single structural-health number (the leaderboard's `scoreReport`) ranks
|
|
5
|
+
* plugins, but it hides WHERE a harness is weak. This buckets the SAME
|
|
6
|
+
* deterministic findings into four categories — Truthfulness, Triggering,
|
|
7
|
+
* Structure, Tested — each a 0–100 ring, with a weighted overall. Same
|
|
8
|
+
* detectors, no re-detection (one-detector-no-drift); all deterministic, no
|
|
9
|
+
* execution. (Safety — "do your hooks actually block?" — is NOT an `audit` ring:
|
|
10
|
+
* it requires executing your hooks, which needs cross-platform confinement
|
|
11
|
+
* that isn't shipped yet, so it lives in the `vigiles/testing` API via
|
|
12
|
+
* `guardrail-check`/`assertBlocksDisasters`, where you opt in explicitly.)
|
|
13
|
+
*
|
|
14
|
+
* A category that can't be assessed scores `null` (n/a) and is EXCLUDED from the
|
|
15
|
+
* overall — never a false 0. Pure over the `ScanReport`, so it's fully testable.
|
|
16
|
+
*/
|
|
17
|
+
import { type PluginScore } from "./leaderboard.js";
|
|
18
|
+
import type { ScanReport } from "./scan.js";
|
|
19
|
+
export type CategoryKey = "Truthfulness" | "Triggering" | "Structure" | "Tested";
|
|
20
|
+
export interface CategoryScore {
|
|
21
|
+
readonly key: CategoryKey;
|
|
22
|
+
/** 0–100, or `null` when the category isn't assessable (n/a — excluded from overall). */
|
|
23
|
+
readonly score: number | null;
|
|
24
|
+
/** Relative weight in the overall (equal by default — tune later). */
|
|
25
|
+
readonly weight: number;
|
|
26
|
+
/** Human-readable deductions / notes, worst first; empty when clean. */
|
|
27
|
+
readonly findings: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
export interface AuditScore {
|
|
30
|
+
/** Weighted average over the ASSESSABLE categories (n/a excluded). 0 when empty. */
|
|
31
|
+
readonly overall: number;
|
|
32
|
+
readonly grade: PluginScore["grade"];
|
|
33
|
+
readonly categories: readonly CategoryScore[];
|
|
34
|
+
/** No loadable surface at all — overall 0, every category n/a. */
|
|
35
|
+
readonly empty: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Bucket a scan report into the four deterministic Lighthouse categories with a
|
|
39
|
+
* weighted overall. n/a categories are excluded from the overall, never scored 0.
|
|
40
|
+
*/
|
|
41
|
+
export declare function auditScore(report: ScanReport): AuditScore;
|
|
42
|
+
/** Render the category rings + the weighted overall for the terminal. */
|
|
43
|
+
export declare function formatAuditScore(s: AuditScore): string;
|
|
44
|
+
//# sourceMappingURL=audit-score.d.ts.map
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.auditScore = auditScore;
|
|
4
|
+
exports.formatAuditScore = formatAuditScore;
|
|
5
|
+
/**
|
|
6
|
+
* Category scoring for `vigiles audit` — the Lighthouse rings.
|
|
7
|
+
*
|
|
8
|
+
* A single structural-health number (the leaderboard's `scoreReport`) ranks
|
|
9
|
+
* plugins, but it hides WHERE a harness is weak. This buckets the SAME
|
|
10
|
+
* deterministic findings into four categories — Truthfulness, Triggering,
|
|
11
|
+
* Structure, Tested — each a 0–100 ring, with a weighted overall. Same
|
|
12
|
+
* detectors, no re-detection (one-detector-no-drift); all deterministic, no
|
|
13
|
+
* execution. (Safety — "do your hooks actually block?" — is NOT an `audit` ring:
|
|
14
|
+
* it requires executing your hooks, which needs cross-platform confinement
|
|
15
|
+
* that isn't shipped yet, so it lives in the `vigiles/testing` API via
|
|
16
|
+
* `guardrail-check`/`assertBlocksDisasters`, where you opt in explicitly.)
|
|
17
|
+
*
|
|
18
|
+
* A category that can't be assessed scores `null` (n/a) and is EXCLUDED from the
|
|
19
|
+
* overall — never a false 0. Pure over the `ScanReport`, so it's fully testable.
|
|
20
|
+
*/
|
|
21
|
+
const leaderboard_js_1 = require("./leaderboard.js");
|
|
22
|
+
// Per-item penalties — mirror the leaderboard's weights so the category view and
|
|
23
|
+
// the single health number stay consistent (broken-at-runtime costs most).
|
|
24
|
+
const W_MISSING_HOOK = 15;
|
|
25
|
+
const W_NO_DESCRIPTION = 10;
|
|
26
|
+
const W_DANGLING_REF = 8;
|
|
27
|
+
const W_OVERLAP = 8; // a description collision → the wrong skill fires
|
|
28
|
+
const W_NO_CONTRACT = 5;
|
|
29
|
+
const W_UNTESTED = 3;
|
|
30
|
+
/** Apply deductions to a 100 base, clamped to [0,100], collecting non-zero labels. */
|
|
31
|
+
function scoreFrom(deductions) {
|
|
32
|
+
let penalty = 0;
|
|
33
|
+
const findings = [];
|
|
34
|
+
for (const d of deductions) {
|
|
35
|
+
if (d.n <= 0)
|
|
36
|
+
continue;
|
|
37
|
+
penalty += d.n * d.weight;
|
|
38
|
+
findings.push({ n: d.n, text: `${String(d.n)} ${d.label}` });
|
|
39
|
+
}
|
|
40
|
+
findings.sort((a, b) => b.n - a.n);
|
|
41
|
+
return {
|
|
42
|
+
score: Math.max(0, 100 - penalty),
|
|
43
|
+
findings: findings.map((f) => f.text),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function truthfulness(r) {
|
|
47
|
+
const missingHooks = r.hooks.filter((h) => h.status === "missing").length;
|
|
48
|
+
const { score, findings } = scoreFrom([
|
|
49
|
+
{
|
|
50
|
+
n: r.danglingRefs.length,
|
|
51
|
+
weight: W_DANGLING_REF,
|
|
52
|
+
label: "broken intra-plugin reference(s)",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
n: missingHooks,
|
|
56
|
+
weight: W_MISSING_HOOK,
|
|
57
|
+
label: "hook script(s) missing (never run)",
|
|
58
|
+
},
|
|
59
|
+
]);
|
|
60
|
+
return { key: "Truthfulness", score, weight: 1, findings };
|
|
61
|
+
}
|
|
62
|
+
function triggering(r) {
|
|
63
|
+
const noDesc = r.skills.filter((s) => !s.hasDescription).length;
|
|
64
|
+
const { score, findings } = scoreFrom([
|
|
65
|
+
{
|
|
66
|
+
n: noDesc,
|
|
67
|
+
weight: W_NO_DESCRIPTION,
|
|
68
|
+
label: "skill(s) with no usable description (can't trigger)",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
n: r.descriptionOverlaps.length,
|
|
72
|
+
weight: W_OVERLAP,
|
|
73
|
+
label: "near-identical skill description(s) (wrong one fires)",
|
|
74
|
+
},
|
|
75
|
+
]);
|
|
76
|
+
return { key: "Triggering", score, weight: 1, findings };
|
|
77
|
+
}
|
|
78
|
+
function structure(r) {
|
|
79
|
+
const noContract = r.agents.filter((a) => a.tools === null).length;
|
|
80
|
+
const deadTools = r.agents.reduce((n, a) => n + a.toolIssues.length, 0);
|
|
81
|
+
const deadMcpTools = r.agents.reduce((n, a) => n + a.mcpToolIssues.length, 0);
|
|
82
|
+
const deadDisallowed = r.agents.reduce((n, a) => n + a.disallowedToolIssues.length, 0);
|
|
83
|
+
const { score, findings } = scoreFrom([
|
|
84
|
+
{
|
|
85
|
+
n: deadTools,
|
|
86
|
+
weight: W_DANGLING_REF,
|
|
87
|
+
label: "agent tool(s) that don't exist (typo / never-available)",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
n: deadMcpTools,
|
|
91
|
+
weight: W_DANGLING_REF,
|
|
92
|
+
label: "agent MCP tool(s) whose server isn't declared",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
n: r.hookEventIssues.length,
|
|
96
|
+
weight: W_MISSING_HOOK,
|
|
97
|
+
label: "hook(s) on an unknown event (never fire)",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
n: r.mcpIssues.length,
|
|
101
|
+
weight: W_DANGLING_REF,
|
|
102
|
+
label: "MCP server(s) that can't start (no command/url)",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
n: r.mcpHookIssues.length,
|
|
106
|
+
weight: W_DANGLING_REF,
|
|
107
|
+
label: "mcp_tool hook(s) incomplete / undeclared server",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
n: r.frontmatterIssues.length,
|
|
111
|
+
weight: W_NO_DESCRIPTION,
|
|
112
|
+
label: "surface(s) missing required frontmatter",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
n: r.frontmatterValueIssues.length,
|
|
116
|
+
weight: W_NO_CONTRACT,
|
|
117
|
+
label: "agent(s) with an invalid model/color (silent fallback)",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
n: deadDisallowed,
|
|
121
|
+
weight: W_NO_CONTRACT,
|
|
122
|
+
label: "disallowedTools typo(s) that block nothing",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
n: noContract,
|
|
126
|
+
weight: W_NO_CONTRACT,
|
|
127
|
+
label: "agent(s) inherit all tools (no contract)",
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
return { key: "Structure", score, weight: 1, findings };
|
|
131
|
+
}
|
|
132
|
+
function tested(r) {
|
|
133
|
+
const { score, findings } = scoreFrom([
|
|
134
|
+
{ n: r.untested, weight: W_UNTESTED, label: "untested surface(s)" },
|
|
135
|
+
]);
|
|
136
|
+
return { key: "Tested", score, weight: 1, findings };
|
|
137
|
+
}
|
|
138
|
+
function isEmptyMachine(r) {
|
|
139
|
+
const surfaces = r.skills.length +
|
|
140
|
+
r.agents.length +
|
|
141
|
+
r.hooks.length +
|
|
142
|
+
r.inlineHooks +
|
|
143
|
+
r.commands;
|
|
144
|
+
// An instruction-only repo (just a CLAUDE.md/AGENTS.md, no plugin surface) is
|
|
145
|
+
// NOT empty — the scan records `instructions` precisely so it isn't graded
|
|
146
|
+
// F/0 "no loadable surface". Only a dir with NO instruction file AND no
|
|
147
|
+
// surface is the empty machine.
|
|
148
|
+
return surfaces === 0 && !r.mcp && !r.instructions;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Bucket a scan report into the four deterministic Lighthouse categories with a
|
|
152
|
+
* weighted overall. n/a categories are excluded from the overall, never scored 0.
|
|
153
|
+
*/
|
|
154
|
+
function auditScore(report) {
|
|
155
|
+
if (isEmptyMachine(report)) {
|
|
156
|
+
const categories = [
|
|
157
|
+
"Truthfulness",
|
|
158
|
+
"Triggering",
|
|
159
|
+
"Structure",
|
|
160
|
+
"Tested",
|
|
161
|
+
];
|
|
162
|
+
return {
|
|
163
|
+
overall: 0,
|
|
164
|
+
grade: (0, leaderboard_js_1.gradeFor)(0),
|
|
165
|
+
categories: categories.map((key) => ({
|
|
166
|
+
key,
|
|
167
|
+
score: null,
|
|
168
|
+
weight: 1,
|
|
169
|
+
findings: ["no loadable plugin surface"],
|
|
170
|
+
})),
|
|
171
|
+
empty: true,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const categories = [
|
|
175
|
+
truthfulness(report),
|
|
176
|
+
triggering(report),
|
|
177
|
+
structure(report),
|
|
178
|
+
tested(report),
|
|
179
|
+
];
|
|
180
|
+
const assessable = categories.filter((c) => c.score !== null);
|
|
181
|
+
const totalWeight = assessable.reduce((s, c) => s + c.weight, 0);
|
|
182
|
+
const overall = totalWeight === 0
|
|
183
|
+
? 0
|
|
184
|
+
: Math.round(assessable.reduce((s, c) => s + c.score * c.weight, 0) / totalWeight);
|
|
185
|
+
return { overall, grade: (0, leaderboard_js_1.gradeFor)(overall), categories, empty: false };
|
|
186
|
+
}
|
|
187
|
+
// A 22-cell bar gauge ("ring" in the terminal; the real rings are the HTML).
|
|
188
|
+
const BAR_CELLS = 22;
|
|
189
|
+
/** A glyph that signals the band at a glance (green/amber/red, no ANSI needed). */
|
|
190
|
+
function bandGlyph(score) {
|
|
191
|
+
if (score === null)
|
|
192
|
+
return "○";
|
|
193
|
+
if (score >= 90)
|
|
194
|
+
return "●";
|
|
195
|
+
if (score >= 70)
|
|
196
|
+
return "◑";
|
|
197
|
+
return "✗";
|
|
198
|
+
}
|
|
199
|
+
function bar(score) {
|
|
200
|
+
if (score === null)
|
|
201
|
+
return "n/a";
|
|
202
|
+
const filled = Math.round((score / 100) * BAR_CELLS);
|
|
203
|
+
return "█".repeat(filled) + "░".repeat(BAR_CELLS - filled);
|
|
204
|
+
}
|
|
205
|
+
/** Render the category rings + the weighted overall for the terminal. */
|
|
206
|
+
function formatAuditScore(s) {
|
|
207
|
+
const lines = ["Harness audit", ""];
|
|
208
|
+
for (const c of s.categories) {
|
|
209
|
+
const glyph = bandGlyph(c.score);
|
|
210
|
+
const label = c.key.padEnd(13);
|
|
211
|
+
const num = (c.score === null ? "n/a" : String(c.score)).padStart(4);
|
|
212
|
+
lines.push(` ${glyph} ${label} ${num} ${bar(c.score)}`);
|
|
213
|
+
if (c.findings.length > 0) {
|
|
214
|
+
lines.push(` └ ${c.findings.join("; ")}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
lines.push("");
|
|
218
|
+
lines.push(`Harness health: ${s.grade} (${String(s.overall)}/100)`);
|
|
219
|
+
return lines.join("\n");
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=audit-score.js.map
|
package/dist/cli-commands.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* recognizes exactly these, so this list can't silently drift from the code.
|
|
12
12
|
*/
|
|
13
13
|
/** Human-facing verbs (printed in help; typed by a human/agent/CI). */
|
|
14
|
-
export declare const VERBS: readonly ["init", "compile", "
|
|
14
|
+
export declare const VERBS: readonly ["init", "compile", "eject", "lint", "test", "eval", "audit", "scaffold-test", "generate", "hook-runtime"];
|
|
15
15
|
/** Runtime entrypoint kinds under `vigiles hook-runtime <kind>` (emitted, not typed). */
|
|
16
16
|
export declare const HOOK_RUNTIME_KINDS: readonly ["run-program", "agent", "agent-start", "agent-done", "skill", "skill-tool", "skill-start", "skill-done", "run-skill", "intercept-tool", "guard", "action", "refs", "effect-enter", "effect-exit"];
|
|
17
17
|
export type Verb = (typeof VERBS)[number];
|
package/dist/cli-commands.js
CHANGED
|
@@ -17,17 +17,13 @@ exports.HOOK_RUNTIME_KINDS = exports.VERBS = void 0;
|
|
|
17
17
|
exports.VERBS = [
|
|
18
18
|
"init",
|
|
19
19
|
"compile",
|
|
20
|
+
"eject",
|
|
20
21
|
"lint",
|
|
21
|
-
"refs",
|
|
22
22
|
"test",
|
|
23
23
|
"eval",
|
|
24
|
-
"
|
|
25
|
-
"measure",
|
|
26
|
-
"explain",
|
|
24
|
+
"audit",
|
|
27
25
|
"scaffold-test",
|
|
28
|
-
"generate
|
|
29
|
-
"generate-schema",
|
|
30
|
-
"generate-harness",
|
|
26
|
+
"generate",
|
|
31
27
|
"hook-runtime",
|
|
32
28
|
];
|
|
33
29
|
/** Runtime entrypoint kinds under `vigiles hook-runtime <kind>` (emitted, not typed). */
|
package/dist/cli.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* vigiles init — scaffold a spec from scratch
|
|
7
7
|
* vigiles compile — compile .spec.ts → .md with linter verification
|
|
8
8
|
* vigiles lint — verify hashes, report coverage, detect duplicates
|
|
9
|
-
* vigiles generate
|
|
9
|
+
* vigiles generate types — emit .d.ts with types from project state
|
|
10
10
|
*/
|
|
11
11
|
export {};
|
|
12
12
|
//# sourceMappingURL=cli.d.ts.map
|