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,20 @@
1
+ import type { AuditReport } from "./audit-report.js";
2
+ /**
3
+ * Candidate locations for the built template, relative to this module (`__dirname`
4
+ * is the compiled `dist/` at runtime, or `src/` under vitest). CommonJS output, so
5
+ * we use `__dirname`, not `import.meta`.
6
+ */
7
+ export declare function templatePath(): string | null;
8
+ /**
9
+ * Inject the report JSON into a template by replacing the quoted placeholder
10
+ * string with the JSON object literal. Pure — the testable core. Throws if the
11
+ * template is missing the placeholder.
12
+ */
13
+ export declare function injectReportData(template: string, report: AuditReport): string;
14
+ /**
15
+ * Render the self-contained HTML report (React template + injected data). Throws
16
+ * if the template hasn't been built — the caller (writeAuditHtml) catches that and
17
+ * skips the HTML, since the JSON + terminal report don't depend on it.
18
+ */
19
+ export declare function renderAuditHtml(report: AuditReport): string;
20
+ //# sourceMappingURL=audit-html.d.ts.map
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.templatePath = templatePath;
4
+ exports.injectReportData = injectReportData;
5
+ exports.renderAuditHtml = renderAuditHtml;
6
+ /**
7
+ * The shareable HTML audit report — the prebuilt **Vite + React + shadcn** template
8
+ * (`report/`, built to one self-contained file at `dist/audit-report.template.html`)
9
+ * with the {@link AuditReport} JSON injected. The React app runs in the reader's
10
+ * browser, so the CLI stays runtime-dependency-light and the output is still a
11
+ * single offline file. There is ONE renderer (pure shadcn/Tailwind) — no inline-CSS
12
+ * fallback; the build guarantees the template exists, and if it somehow doesn't the
13
+ * caller skips the HTML (the JSON + terminal report still work).
14
+ *
15
+ * `<`/`>`/`&` are escaped on injection so report text can never break out of the
16
+ * `<script>`. `injectReportData` is the pure, testable core.
17
+ */
18
+ const node_fs_1 = require("node:fs");
19
+ const node_path_1 = require("node:path");
20
+ const PLACEHOLDER = "__VIGILES_DATA_PLACEHOLDER__";
21
+ /**
22
+ * Candidate locations for the built template, relative to this module (`__dirname`
23
+ * is the compiled `dist/` at runtime, or `src/` under vitest). CommonJS output, so
24
+ * we use `__dirname`, not `import.meta`.
25
+ */
26
+ function templatePath() {
27
+ const candidates = [
28
+ (0, node_path_1.resolve)(__dirname, "audit-report.template.html"), // dist/ (shipped)
29
+ (0, node_path_1.resolve)(__dirname, "..", "dist", "audit-report.template.html"), // src/ under vitest
30
+ ];
31
+ return candidates.find((p) => (0, node_fs_1.existsSync)(p)) ?? null;
32
+ }
33
+ /** Escape `<`, `>`, `&` so report text can never break out of the `<script>`. */
34
+ function escapeForScript(json) {
35
+ return json.replace(/[<>&]/g, (ch) => "\\u00" + ch.charCodeAt(0).toString(16).padStart(2, "0"));
36
+ }
37
+ /**
38
+ * Inject the report JSON into a template by replacing the quoted placeholder
39
+ * string with the JSON object literal. Pure — the testable core. Throws if the
40
+ * template is missing the placeholder.
41
+ */
42
+ function injectReportData(template, report) {
43
+ const re = new RegExp(`(["'])${PLACEHOLDER}\\1`);
44
+ if (!re.test(template)) {
45
+ throw new Error("audit report template is missing the data placeholder");
46
+ }
47
+ return template.replace(re, escapeForScript(JSON.stringify(report)));
48
+ }
49
+ /**
50
+ * Render the self-contained HTML report (React template + injected data). Throws
51
+ * if the template hasn't been built — the caller (writeAuditHtml) catches that and
52
+ * skips the HTML, since the JSON + terminal report don't depend on it.
53
+ */
54
+ function renderAuditHtml(report) {
55
+ const p = templatePath();
56
+ if (!p) {
57
+ throw new Error("audit report template not built — run `npm run build` (builds report/), or use --json / --no-html");
58
+ }
59
+ return injectReportData((0, node_fs_1.readFileSync)(p, "utf-8"), report);
60
+ }
61
+ //# sourceMappingURL=audit-html.js.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Auto-generated trigger probes for `vigiles audit`'s model trigger tier.
3
+ *
4
+ * The trigger-rate eval needs a per-skill prompt set (does the description FIRE?
5
+ * — recall + precision). Authoring that set by hand was the friction that made
6
+ * the eval un-wowable. The trigger tier removes it: derive a small, DIVERSE probe
7
+ * set from each skill's own description — zero setup. `--prompts=<file>` still
8
+ * overrides for a rigorous, curated benchmark.
9
+ *
10
+ * Deterministic by design (no model needed to AUTHOR the probes — the model is
11
+ * spent RUNNING them). The trick that clears the diversity gate: extract a SHORT
12
+ * topic from the description (so the shared text stays small relative to the
13
+ * frame) and wrap it in lexically-distant frames. Measured min pairwise NCD
14
+ * ~0.27 across short/long descriptions — comfortably above {@link AUTO_MIN_DISTANCE}.
15
+ */
16
+ import type { TriggerPromptSet } from "./scan-behavioral.js";
17
+ export interface PromptSkill {
18
+ readonly name: string;
19
+ readonly description: string;
20
+ }
21
+ /** How many recall probes we generate per skill (each a distinct frame). */
22
+ export declare const AUTO_RECALL_COUNT = 6;
23
+ /**
24
+ * The diversity floor for AUTO probes — relaxed from the default 0.3 because a
25
+ * templated-but-varied machine probe legitimately shares a topic phrase (the
26
+ * generator's measured min is ~0.27). Still well above 0 → genuine copy-paste
27
+ * is caught; the gate's "vary the phrasing" advice is for hand-authored sets.
28
+ */
29
+ export declare const AUTO_MIN_DISTANCE = 0.2;
30
+ /**
31
+ * Extract a short, action-shaped topic from a description: drop boilerplate
32
+ * lead-ins ("A skill that…", "Use this skill to…"), take the first clause, cap
33
+ * at 8 words. Capping is load-bearing — a long verbatim topic makes the frames
34
+ * too similar (NCD collapses below the gate).
35
+ */
36
+ export declare function topicOf(description: string): string;
37
+ /** Recall probes for one skill: distinct frames around its extracted topic. */
38
+ export declare function recallPrompts(description: string, count?: number): string[];
39
+ /**
40
+ * Build a {@link TriggerPromptSet} from skill descriptions — zero-setup trigger
41
+ * probes. Each skill gets `recallPrompts` derived from its description plus the
42
+ * shared irrelevant bank for precision. Skills with an empty description are
43
+ * skipped (nothing to derive a topic from).
44
+ */
45
+ export declare function autoTriggerPrompts(skills: readonly PromptSkill[], count?: number): TriggerPromptSet;
46
+ //# sourceMappingURL=audit-prompts.d.ts.map
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AUTO_MIN_DISTANCE = exports.AUTO_RECALL_COUNT = void 0;
4
+ exports.topicOf = topicOf;
5
+ exports.recallPrompts = recallPrompts;
6
+ exports.autoTriggerPrompts = autoTriggerPrompts;
7
+ /** How many recall probes we generate per skill (each a distinct frame). */
8
+ exports.AUTO_RECALL_COUNT = 6;
9
+ /**
10
+ * The diversity floor for AUTO probes — relaxed from the default 0.3 because a
11
+ * templated-but-varied machine probe legitimately shares a topic phrase (the
12
+ * generator's measured min is ~0.27). Still well above 0 → genuine copy-paste
13
+ * is caught; the gate's "vary the phrasing" advice is for hand-authored sets.
14
+ */
15
+ exports.AUTO_MIN_DISTANCE = 0.2;
16
+ // Lexically-distant frames around a short topic. Order matters: the first N are
17
+ // used, and they're arranged so any prefix stays diverse (verified in the test).
18
+ const RECALL_FRAMES = [
19
+ (t) => `I need help to ${t} in my project right now.`,
20
+ (t) => `How do I ${t}? Walk me through the steps.`,
21
+ (t) => `Please ${t} before I open this pull request.`,
22
+ (t) => `What's the recommended way to ${t} on a large team?`,
23
+ (t) => `Can you take a look and ${t} for me?`,
24
+ (t) => `My task today: ${t} across the whole repo.`,
25
+ (t) => `Is there a tool that will ${t} automatically?`,
26
+ (t) => `Give me a checklist to ${t} thoroughly.`,
27
+ ];
28
+ // Unrelated requests for the precision arm — varied, clearly off-topic, so a
29
+ // well-scoped skill should NOT fire on them (a too-broad description that hijacks
30
+ // these fails precision). Generic on purpose, distant from any one skill's topic.
31
+ // Count is load-bearing: the auto trigger tier applies the diversity gate's
32
+ // `minPrompts` floor (= AUTO_RECALL_COUNT) to BOTH arms, so the bank must hold at
33
+ // least AUTO_RECALL_COUNT entries or the precision arm fails preflight and every
34
+ // skill reports "unmeasured" instead of running.
35
+ const IRRELEVANT_BANK = [
36
+ "What's the weather forecast for Tokyo this weekend?",
37
+ "Summarize the plot of Hamlet in two sentences.",
38
+ "Convert 100 US dollars to euros at today's rate.",
39
+ "Recommend a good pasta recipe for dinner tonight.",
40
+ "Who won the most Olympic gold medals in swimming?",
41
+ "Explain how photosynthesis works in plants.",
42
+ "Suggest a weekend hiking trail near Seattle.",
43
+ ];
44
+ // Boilerplate lead-in words that carry no topical signal (skill descriptions
45
+ // open with a verb — "Reviews…", "Generate…" — so stripping these from the front
46
+ // never eats the real action). Applied iteratively until a content word remains.
47
+ const LEAD_WORD = /^(a|an|the|this|use|skill|agent|tool|command|helper|that|which|to|for|when|invoked?|invoke|used?|helps?|you|with)\b[\s,:-]*/i;
48
+ /**
49
+ * Extract a short, action-shaped topic from a description: drop boilerplate
50
+ * lead-ins ("A skill that…", "Use this skill to…"), take the first clause, cap
51
+ * at 8 words. Capping is load-bearing — a long verbatim topic makes the frames
52
+ * too similar (NCD collapses below the gate).
53
+ */
54
+ function topicOf(description) {
55
+ let t = description.trim().toLowerCase();
56
+ let prev = "";
57
+ while (t !== prev) {
58
+ prev = t;
59
+ t = t.replace(LEAD_WORD, "");
60
+ }
61
+ const firstClause = t.split(/[.,;:!?]/)[0].trim();
62
+ const words = firstClause.split(/\s+/).filter(Boolean).slice(0, 8);
63
+ const topic = words.join(" ");
64
+ // Fall back to the raw (capped, lowercased) description if stripping left nothing.
65
+ return (topic || description.trim().toLowerCase().split(/\s+/).slice(0, 8).join(" "));
66
+ }
67
+ /** Recall probes for one skill: distinct frames around its extracted topic. */
68
+ function recallPrompts(description, count = exports.AUTO_RECALL_COUNT) {
69
+ const topic = topicOf(description);
70
+ return RECALL_FRAMES.slice(0, count).map((frame) => frame(topic));
71
+ }
72
+ /**
73
+ * Build a {@link TriggerPromptSet} from skill descriptions — zero-setup trigger
74
+ * probes. Each skill gets `recallPrompts` derived from its description plus the
75
+ * shared irrelevant bank for precision. Skills with an empty description are
76
+ * skipped (nothing to derive a topic from).
77
+ */
78
+ function autoTriggerPrompts(skills, count = exports.AUTO_RECALL_COUNT) {
79
+ const set = {};
80
+ for (const s of skills) {
81
+ if (!s.description.trim())
82
+ continue;
83
+ set[s.name] = {
84
+ prompts: recallPrompts(s.description, count),
85
+ irrelevant: [...IRRELEVANT_BANK],
86
+ };
87
+ }
88
+ return set;
89
+ }
90
+ //# sourceMappingURL=audit-prompts.js.map
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The `AuditReport` — the versioned JSON contract that IS the audit's product
3
+ * boundary. Everything renders FROM it: the local self-contained HTML report,
4
+ * `audit --json` for CI, and (later) an upload to a hosted dashboard. Because it's
5
+ * the wire format between the CLI and anything downstream, it is VERSIONED
6
+ * (`meta.schemaVersion`) and stable — additive changes only within a version.
7
+ *
8
+ * Pure: `buildAuditReport` assembles the report from the same deterministic pieces
9
+ * the terminal output uses (`auditScore` + `optimize` + the scan inventory) — no
10
+ * re-detection (one-detector-no-drift), no model, no clock (a `generatedAt`
11
+ * timestamp is attached by the CLI at write time, never by this pure builder, so
12
+ * the embedded-in-HTML form stays deterministic).
13
+ */
14
+ import { type AuditScore } from "./audit-score.js";
15
+ import { type Recommendation } from "./optimize.js";
16
+ import type { AdoptabilityResult } from "./adoptability.js";
17
+ import type { ScanReport } from "./scan.js";
18
+ /** The current schema version. Bump only on a BREAKING change to the shape. */
19
+ export declare const AUDIT_SCHEMA_VERSION = 1;
20
+ export interface AuditReportMeta {
21
+ /** Wire-format version — consumers gate on this. */
22
+ readonly schemaVersion: typeof AUDIT_SCHEMA_VERSION;
23
+ readonly tool: "vigiles";
24
+ /** The vigiles version that produced the report. */
25
+ readonly vigilesVersion: string;
26
+ /** The detected/selected harness (`claude-code`, `codex`, …). */
27
+ readonly harness: string;
28
+ /** The audited directory. */
29
+ readonly dir: string;
30
+ /** ISO-8601 produced-at stamp — set by the CLI at write time (NOT the pure builder). */
31
+ readonly generatedAt?: string;
32
+ }
33
+ /** What the harness ships — the "inventory" surface counts. */
34
+ export interface AuditInventory {
35
+ readonly skills: number;
36
+ readonly agents: number;
37
+ readonly hooks: number;
38
+ readonly commands: number;
39
+ readonly mcp: boolean;
40
+ readonly untested: number;
41
+ }
42
+ /**
43
+ * The full audit, as the dashboard / CI / HTML all consume it. Self-describing
44
+ * and versioned; additive-only within a `schemaVersion`.
45
+ */
46
+ export interface AuditReport {
47
+ readonly meta: AuditReportMeta;
48
+ /** The four deterministic category rings + the weighted overall + grade. */
49
+ readonly score: AuditScore;
50
+ /** The deterministic, ranked fixes (the inline recommendations). */
51
+ readonly recommendations: readonly Recommendation[];
52
+ readonly inventory: AuditInventory;
53
+ /**
54
+ * The adoption preview — "what would vigiles catch in your repo?" Present only
55
+ * when the model-gated tier ran (behind consent); a deterministic read omits it.
56
+ * Additive/optional, so the schema version is unchanged.
57
+ */
58
+ readonly adoptability?: AdoptabilityResult;
59
+ }
60
+ export interface BuildAuditReportOptions {
61
+ readonly harness: string;
62
+ readonly vigilesVersion: string;
63
+ }
64
+ /**
65
+ * Assemble the versioned {@link AuditReport} from a scan report — pure, no clock.
66
+ * The CLI attaches `meta.generatedAt` when it writes the JSON artifact; the
67
+ * HTML-embedded form omits it so the rendered file stays deterministic.
68
+ */
69
+ export declare function buildAuditReport(report: ScanReport, opts: BuildAuditReportOptions): AuditReport;
70
+ //# sourceMappingURL=audit-report.d.ts.map
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AUDIT_SCHEMA_VERSION = void 0;
4
+ exports.buildAuditReport = buildAuditReport;
5
+ /**
6
+ * The `AuditReport` — the versioned JSON contract that IS the audit's product
7
+ * boundary. Everything renders FROM it: the local self-contained HTML report,
8
+ * `audit --json` for CI, and (later) an upload to a hosted dashboard. Because it's
9
+ * the wire format between the CLI and anything downstream, it is VERSIONED
10
+ * (`meta.schemaVersion`) and stable — additive changes only within a version.
11
+ *
12
+ * Pure: `buildAuditReport` assembles the report from the same deterministic pieces
13
+ * the terminal output uses (`auditScore` + `optimize` + the scan inventory) — no
14
+ * re-detection (one-detector-no-drift), no model, no clock (a `generatedAt`
15
+ * timestamp is attached by the CLI at write time, never by this pure builder, so
16
+ * the embedded-in-HTML form stays deterministic).
17
+ */
18
+ const audit_score_js_1 = require("./audit-score.js");
19
+ const optimize_js_1 = require("./optimize.js");
20
+ /** The current schema version. Bump only on a BREAKING change to the shape. */
21
+ exports.AUDIT_SCHEMA_VERSION = 1;
22
+ /**
23
+ * Assemble the versioned {@link AuditReport} from a scan report — pure, no clock.
24
+ * The CLI attaches `meta.generatedAt` when it writes the JSON artifact; the
25
+ * HTML-embedded form omits it so the rendered file stays deterministic.
26
+ */
27
+ function buildAuditReport(report, opts) {
28
+ return {
29
+ meta: {
30
+ schemaVersion: exports.AUDIT_SCHEMA_VERSION,
31
+ tool: "vigiles",
32
+ vigilesVersion: opts.vigilesVersion,
33
+ harness: opts.harness,
34
+ dir: report.dir,
35
+ },
36
+ score: (0, audit_score_js_1.auditScore)(report),
37
+ recommendations: (0, optimize_js_1.optimize)(report).recommendations,
38
+ inventory: {
39
+ skills: report.skills.length,
40
+ agents: report.agents.length,
41
+ // All hooks, file-backed + inline — matches formatScanReport and the
42
+ // emptiness/scoring count, so a JSON/HTML "What it ships" never reports 0
43
+ // hooks for an inline-hook-only harness.
44
+ hooks: report.hooks.length + report.inlineHooks,
45
+ commands: report.commands,
46
+ mcp: report.mcp,
47
+ untested: report.untested,
48
+ },
49
+ };
50
+ }
51
+ //# sourceMappingURL=audit-report.js.map