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.
Files changed (58) hide show
  1. package/README.md +207 -88
  2. package/dist/adoptability.d.ts +55 -0
  3. package/dist/adoptability.js +196 -0
  4. package/dist/audit-html.d.ts +20 -0
  5. package/dist/audit-html.js +61 -0
  6. package/dist/audit-prompts.d.ts +46 -0
  7. package/dist/audit-prompts.js +90 -0
  8. package/dist/audit-report.d.ts +70 -0
  9. package/dist/audit-report.js +51 -0
  10. package/dist/audit-report.template.html +110 -0
  11. package/dist/audit-score.d.ts +44 -0
  12. package/dist/audit-score.js +221 -0
  13. package/dist/cli-commands.d.ts +1 -1
  14. package/dist/cli-commands.js +3 -7
  15. package/dist/cli.d.ts +1 -1
  16. package/dist/cli.js +749 -180
  17. package/dist/core/adopt.d.ts +65 -0
  18. package/dist/core/adopt.js +199 -0
  19. package/dist/core/compose.d.ts +1 -1
  20. package/dist/core/compose.js +1 -1
  21. package/dist/core/evolve.d.ts +4 -0
  22. package/dist/core/evolve.js +4 -0
  23. package/dist/core/frontmatter.d.ts +8 -7
  24. package/dist/core/frontmatter.js +8 -7
  25. package/dist/core/generate-harness.d.ts +1 -1
  26. package/dist/core/generate-harness.js +3 -3
  27. package/dist/core/generate-schema.js +1 -1
  28. package/dist/core/inline.d.ts +6 -6
  29. package/dist/core/inline.js +17 -7
  30. package/dist/core/integrity.d.ts +31 -0
  31. package/dist/core/integrity.js +45 -0
  32. package/dist/core/orphans.js +1 -1
  33. package/dist/core/spec.d.ts +40 -2
  34. package/dist/core/spec.js +16 -1
  35. package/dist/core/types.d.ts +42 -6
  36. package/dist/core/validate.js +26 -26
  37. package/dist/dialect-drift.js +1 -1
  38. package/dist/eval.d.ts +1 -1
  39. package/dist/eval.js +1 -1
  40. package/dist/guardrail-check.d.ts +1 -1
  41. package/dist/guardrail-check.js +1 -1
  42. package/dist/optimize.d.ts +12 -5
  43. package/dist/optimize.js +27 -5
  44. package/dist/scan-behavioral.d.ts +8 -2
  45. package/dist/scan-behavioral.js +6 -4
  46. package/dist/scan-trigger-suggest.d.ts +91 -0
  47. package/dist/scan-trigger-suggest.js +103 -0
  48. package/dist/scan.d.ts +53 -12
  49. package/dist/scan.js +92 -16
  50. package/dist/score-explainer.d.ts +1 -1
  51. package/dist/setup-plan.d.ts +59 -1
  52. package/dist/setup-plan.js +103 -5
  53. package/hooks/post-edit.sh +1 -1
  54. package/package.json +4 -2
  55. package/skills/adopt-spec/SKILL.md +7 -7
  56. package/skills/linter-docs/eslint.md +1 -1
  57. package/skills/strengthen/SKILL.md +1 -1
  58. 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
@@ -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", "lint", "refs", "test", "eval", "scan", "measure", "explain", "scaffold-test", "generate-types", "generate-schema", "generate-harness", "hook-runtime"];
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];
@@ -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
- "scan",
25
- "measure",
26
- "explain",
24
+ "audit",
27
25
  "scaffold-test",
28
- "generate-types",
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-types — emit .d.ts with types from project state
9
+ * vigiles generate types — emit .d.ts with types from project state
10
10
  */
11
11
  export {};
12
12
  //# sourceMappingURL=cli.d.ts.map