vigiles 9.1.0 → 10.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 CHANGED
@@ -6,18 +6,28 @@
6
6
  SPINE = CONCEPT 5 (proof/demo-led). Lead with REAL, screenshotable catches on
7
7
  plugins people actually ship, THEN explain the mechanism. The proofs are not
8
8
  illustrative — every block traces to a real dogfood run captured in
9
- research/dogfood/. HYBRID proof source (decided 2026-06-28): Proofs 1-2 are
10
- COMMUNITY catches, anonymized (a missing SKILL.md, an AskUserQuestion-never-
11
- available tool) — official plugins don't have those silent-failure bugs. Proofs
12
- 3-4 are OFFICIAL + NAMED: pr-review-toolkit (malformed YAML + unrestricted review
13
- agents) and the all-37 Anthropic-official leaderboard (A→F, LSP stubs excluded).
14
- NEVER replace a real catch with a fabricated one.
9
+ research/dogfood/. TWO COMMUNITY catches, anonymized (a skill-description collision
10
+ wrong-skill-fires (claude-flow, Triggering F), an AskUserQuestion-never-available
11
+ tool) — both real GRADED defects that REPRODUCE on current main. NEVER replace a
12
+ real catch with a fabricated one. (Proof 1 was a missing-SKILL.md/Truthfulness
13
+ catch, swapped 2026-06-28: its source (superpowers) is clean on current main and NO
14
+ reproducible dead-file-ref exists in popular OSS those are an adopt+strengthen
15
+ payoff, see research/oss-audit-render-findings.md.)
16
+
17
+ WHY ONLY TWO (decided 2026-06-28): the earlier Proofs 3-4 leaned on
18
+ pr-review-toolkit's "review agents inherit all tools" as an official-plugin
19
+ defect. But inherit-all (a subagent with no `tools:` line) is now ADVISORY, not a
20
+ graded penalty — omitting the tool contract is a near-universal, legitimate
21
+ authoring style (an OSS sweep of 122 plugins found 109 whose only finding was
22
+ this), so penalizing it cried wolf. With that change the official plugins are all
23
+ a clean A, so a "even Anthropic has bugs" proof would be dishonest — Proofs 3-4
24
+ were DROPPED rather than reframed. The leaderboard feature still exists; it just
25
+ isn't a headline proof.
15
26
 
16
27
  DON'T SHAME OSS: community catches are real but ANONYMIZED in public copy (no
17
28
  obra/superpowers, madappgang by name) — real names live only in research/dogfood/.
18
- Punch UP at official/vendor plugins (Anthropic's own are NAMED); never name a
19
- volunteer's repo to show its bug. Proof 3/4 don't show pr-review-toolkit's audit-
20
- RING score (88) — only the leaderboard score (52 F) — so the two never clash.
29
+ If an official/vendor proof returns, punch UP (name Anthropic's own); never name a
30
+ volunteer's repo to show its bug.
21
31
 
22
32
  1. LEAD WITH BENEFITS / the reader's CONCRETE PAIN, never an apology, caveat, or
23
33
  competitor. A bolded lead-in is the first thing read — make it the hook/win.
@@ -34,11 +44,14 @@
34
44
  Push depth into docs/ and LINK it.
35
45
  6. NO INTERNAL VOCABULARY (moat / measurement-authority / flywheel) and NO
36
46
  research/ links — name the user benefit.
37
- 7. ASSETS: the hero vigiles-audit.png must be REFRESHED from a real run
38
- (research/dogfood/audit-superpowers.html) without the dialect drift banner
39
- before launch. (vigiles-demo.gif was removed from Proof 1 it rendered as a
40
- frozen half-typed terminal and was redundant with the code block; if a lint
41
- demo returns, it belongs in the Lint section with a non-frozen asset.)
47
+ 7. ASSETS: the hero vigiles-audit.png is a REAL current report (a community
48
+ plugin rendered as "my-plugin" to anonymize) A 92 with four rings and an
49
+ inline subagent-tool-contract fix; no dialect-drift banner (HTML report is
50
+ terminal-banner-free by design). Re-render via headless Chromium on the React
51
+ report if the UI changes. (vigiles-demo.gif was removed
52
+ from Proof 1 — it rendered as a frozen half-typed terminal and was redundant
53
+ with the code block; if a lint demo returns, it belongs in the Lint section
54
+ with a non-frozen asset.)
42
55
  -->
43
56
 
44
57
  <p align="center">
@@ -60,8 +73,8 @@
60
73
  ---
61
74
 
62
75
  **You installed a bunch of plugins and wrote a few skills — but do they actually work?**
63
- A skill that never fires, a subagent wired to a tool that doesn't exist, a CLAUDE.md
64
- full of dead references — your harness fails **silently**, and you find out mid-task.
76
+ A skill that never fires, two skills the agent can't tell apart, a subagent wired to
77
+ a tool that doesn't exist — your harness fails **silently**, and you find out mid-task.
65
78
 
66
79
  **It's a library with no tests.** One command runs them — no key, no config, safe on
67
80
  any repo:
@@ -75,27 +88,26 @@ Here's what it found on real, popular plugins. ↓
75
88
  ## The report
76
89
 
77
90
  <p align="center">
78
- <img src="vigiles-audit.png" width="760" alt="vigiles audit report: an overall score with four category rings — Truthfulness, Triggering, Structure, Tested — and fix cards" />
91
+ <img src="vigiles-audit.png" width="760" alt="vigiles audit report: an overall A (92/100) score with four category rings — Truthfulness, Triggering, Structure, Tested — and an inline fix card for a subagent declaring a tool that doesn't exist" />
79
92
  </p>
80
- <!-- REFRESH before launch from research/dogfood/audit-superpowers.html (a real catch, no drift banner). -->
81
93
 
82
94
  Four deterministic rings, **each finding's fix inline**, and a shareable HTML report.
83
95
  Like Lighthouse, `audit` is a **local report you run on your machine** — safe on any
84
96
  repo (even one wired to prod), identical on every OS. **Not a CI step** (CI uses
85
97
  `lint`). **[Audit a harness →](docs/for-plugin-authors.md)**
86
98
 
87
- ## Proof 1 — your CLAUDE.md is lying to your agent
99
+ ## Proof 1 — two skills your agent can't tell apart
88
100
 
89
101
  ```text
90
- Truthfulness 92
91
- skills/using-debugging/SKILL.md (referenced but MISSING)
102
+ Triggering 0
103
+ 45 near-identical skill descriptions — the selector can't tell them apart,
104
+ so the wrong one fires (e.g. "agent-coder" ↔ "agent-tester", 83% alike)
92
105
  ```
93
106
 
94
- A real, widely-installed plugin its instructions send the agent to a skill file
95
- that **isn't there**. Valid markdown, but not _true_, and your agent trusts it anyway.
96
-
97
- File paths, scripts, code symbols — plus linter rules across **7 linters** (ESLint,
98
- Ruff, Clippy + four more): each one **exists _and_ is enabled**. **[Full guide →](docs/verifying-instruction-files.md)**
107
+ A real, popular plugin ships **45 skill pairs** described so similarly the model
108
+ can't reliably pick between them so it fires the **wrong** skill. Valid markdown;
109
+ the selector chooses by description, and near-identical text collides.
110
+ **[How triggering works →](docs/measuring-skills.md)**
99
111
 
100
112
  ## Proof 2 — a tool your subagent silently can't call
101
113
 
@@ -107,39 +119,15 @@ Ruff, Clippy + four more): each one **exists _and_ is enabled**. **[Full guide
107
119
  A real upstream subagent declares a tool the harness **silently drops**, so it loses
108
120
  a capability it thinks it has. vigiles flags it _and_ hands you the one-line fix —
109
121
  **free, no model.** That's the difference from a markdown linter: it checks your
110
- harness against **reality**, not style.
111
-
112
- ## Proof 3 even the official plugins
113
-
114
- Not cherry-picked community repos — **Anthropic's own official `pr-review-toolkit`**:
115
-
116
- ```text
117
- ✗ silent-failure-hunter.md — frontmatter isn't valid YAML (won't parse)
118
- ⚠ 6 review agents inherit ALL tools — a code-reviewer with Write + Bash
119
- ```
120
-
121
- A subagent whose YAML doesn't parse, and six review agents holding far more power than
122
- a reviewer needs — real findings on a first-party plugin. _(We name Anthropic's own;
123
- the community plugins above stay anonymous — punch up, don't shame volunteers.)_
124
-
125
- ## Proof 4 — rank a whole marketplace
126
-
127
- `audit` ranks a folder of plugins by what's actually **broken** — no key. Run across
128
- **all of Anthropic's official plugins**, it flags exactly **one** real outlier — no
129
- false-positive noise (untested surfaces are advisory):
130
-
131
- ```text
132
- # score grade plugin
133
- 1 100 A code-review ← 24 of 25 come back a clean A
134
- 25 70 C pr-review-toolkit — 6 agents inherit all tools (a reviewer with Write + Bash)
135
- ```
136
-
137
- <sub>Real scores on Anthropic's own marketplace — a fair tool flags the one real issue, not noise (empty LSP stubs excluded).</sub>
122
+ harness against **reality**, not style — every file path, script, code symbol, and
123
+ linter rule across **7 catalogs** (ESLint, Ruff, Clippy + four more), each verified
124
+ to **exist _and_ be enabled**. **[Full guide →](docs/verifying-instruction-files.md)**
138
125
 
139
- **[Plugin-author guide →](docs/for-plugin-authors.md)**
126
+ Two real catches, both **free and model-less** — and audit ranks a whole
127
+ marketplace the same way. **[Audit a marketplace →](docs/for-plugin-authors.md)**
140
128
 
141
- > **And it grades itself: 100/100, A, all four rings green.** vigiles runs `audit` on
142
- > its own harness in CI. We eat what we cook.
129
+ > **And it grades itself: 100/100, A, all four rings green** and CI gates every
130
+ > commit with `lint` + `test`. We eat what we cook.
143
131
 
144
132
  ## How it does it — three instruments
145
133
 
@@ -152,9 +140,7 @@ Every path, script, symbol & linter rule resolved against reality (the catches
152
140
  above). **You don't write any of it** — `npx vigiles init` **adopts your existing
153
141
  CLAUDE.md _and every skill and subagent_ into verified specs**, non-destructively
154
142
  (untouched until you `compile`; `eject` reverses). After that, plain-English asks
155
- edit them for you. Prefer zero new files? Plain markdown + one inline
156
- `<!-- vigiles:enforce -->` comment lints too — no TypeScript.
157
- **[How →](docs/verifying-instruction-files.md)**
143
+ edit them for you. **[How →](docs/verifying-instruction-files.md)**
158
144
 
159
145
  ### 🧪 Test — does the harness actually do its job?
160
146
 
@@ -234,9 +220,7 @@ JS **or** TS (`*.harness.{mjs,ts}`) — run with `npx vigiles test`.
234
220
  ## FAQ
235
221
 
236
222
  - **Isn't this just a markdown linter?** No — it checks whether your instruction file is _true_ (every path/script/symbol/rule exists and is enabled), then tests and measures your harness. A style linter can't do any of that.
237
- - **Do I have to write TypeScript?** No — your agent writes the spec (`init` adopts your CLAUDE.md into one). Prefer zero new files? Plain markdown lints too. Deeper compiler-grade guarantees are gradual and opt-in, like TS's `strict`.
238
- - **Does it overwrite my files?** No. `init` adopts an existing CLAUDE.md _non-destructively_ — untouched until you `compile`, and `eject` reverses it.
239
- - **Need an API key?** No for almost everything (free, every commit). Real-model evals run on your Claude Pro/Max subscription — $0 metered tokens.
223
+ - **Do I have to write TypeScript?** No — your agent writes the spec (`init` adopts your CLAUDE.md into one), or plain markdown lints with zero new files. Compiler-grade guarantees are opt-in, like TS's `strict`.
240
224
  - **Non-JS repo?** `npx vigiles lint` verifies your CLAUDE.md with no install (Ruff/Clippy/Pylint/… too).
241
225
 
242
226
  **[Full FAQ →](docs/faq.md)**
@@ -5,16 +5,27 @@ import type { AuditReport } from "./audit-report.js";
5
5
  * we use `__dirname`, not `import.meta`.
6
6
  */
7
7
  export declare function templatePath(): string | null;
8
+ /**
9
+ * Live-server info injected into the report when it's served by `audit --serve`
10
+ * (not a static file): the per-run token the report must send on every adopt
11
+ * POST. Absent for a plain static report — the buttons fall back to copy-command.
12
+ */
13
+ export interface ServeInfo {
14
+ readonly token: string;
15
+ }
8
16
  /**
9
17
  * Inject the report JSON into a template by replacing the quoted placeholder
10
18
  * string with the JSON object literal. Pure — the testable core. Throws if the
11
- * template is missing the placeholder.
19
+ * template is missing the placeholder. When `serve` is given (the `--serve`
20
+ * path), also sets `window.__VIGILES_SERVE__` so the report's adopt buttons POST
21
+ * to the local server instead of copying the command.
12
22
  */
13
- export declare function injectReportData(template: string, report: AuditReport): string;
23
+ export declare function injectReportData(template: string, report: AuditReport, serve?: ServeInfo): string;
14
24
  /**
15
25
  * Render the self-contained HTML report (React template + injected data). Throws
16
26
  * 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.
27
+ * skips the HTML, since the JSON + terminal report don't depend on it. Pass
28
+ * `serve` to render the live (`--serve`) variant whose buttons POST.
18
29
  */
19
- export declare function renderAuditHtml(report: AuditReport): string;
30
+ export declare function renderAuditHtml(report: AuditReport, serve?: ServeInfo): string;
20
31
  //# sourceMappingURL=audit-html.d.ts.map
@@ -37,25 +37,34 @@ function escapeForScript(json) {
37
37
  /**
38
38
  * Inject the report JSON into a template by replacing the quoted placeholder
39
39
  * string with the JSON object literal. Pure — the testable core. Throws if the
40
- * template is missing the placeholder.
40
+ * template is missing the placeholder. When `serve` is given (the `--serve`
41
+ * path), also sets `window.__VIGILES_SERVE__` so the report's adopt buttons POST
42
+ * to the local server instead of copying the command.
41
43
  */
42
- function injectReportData(template, report) {
44
+ function injectReportData(template, report, serve) {
43
45
  const re = new RegExp(`(["'])${PLACEHOLDER}\\1`);
44
46
  if (!re.test(template)) {
45
47
  throw new Error("audit report template is missing the data placeholder");
46
48
  }
47
- return template.replace(re, escapeForScript(JSON.stringify(report)));
49
+ let out = template.replace(re, escapeForScript(JSON.stringify(report)));
50
+ if (serve) {
51
+ // Prepend the serve global onto the same statement that carries the data, so
52
+ // it's set before the app bundle reads it. The token is hex, but escape anyway.
53
+ out = out.replace("window.__VIGILES_DATA__", `window.__VIGILES_SERVE__=${escapeForScript(JSON.stringify(serve))};window.__VIGILES_DATA__`);
54
+ }
55
+ return out;
48
56
  }
49
57
  /**
50
58
  * Render the self-contained HTML report (React template + injected data). Throws
51
59
  * 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.
60
+ * skips the HTML, since the JSON + terminal report don't depend on it. Pass
61
+ * `serve` to render the live (`--serve`) variant whose buttons POST.
53
62
  */
54
- function renderAuditHtml(report) {
63
+ function renderAuditHtml(report, serve) {
55
64
  const p = templatePath();
56
65
  if (!p) {
57
66
  throw new Error("audit report template not built — run `npm run build` (builds report/), or use --json / --no-html");
58
67
  }
59
- return injectReportData((0, node_fs_1.readFileSync)(p, "utf-8"), report);
68
+ return injectReportData((0, node_fs_1.readFileSync)(p, "utf-8"), report, serve);
60
69
  }
61
70
  //# sourceMappingURL=audit-html.js.map
@@ -14,13 +14,22 @@
14
14
  import { type AuditScore } from "./audit-score.js";
15
15
  import { type Recommendation } from "./optimize.js";
16
16
  import type { AdoptabilityResult } from "./adoptability.js";
17
- import type { ScanReport } from "./scan.js";
17
+ import type { ScanReport, MarketplaceInfo } from "./scan.js";
18
+ import type { PluginScore } from "./leaderboard.js";
18
19
  /** The current schema version. Bump only on a BREAKING change to the shape. */
19
20
  export declare const AUDIT_SCHEMA_VERSION = 1;
20
21
  export interface AuditReportMeta {
21
22
  /** Wire-format version — consumers gate on this. */
22
23
  readonly schemaVersion: typeof AUDIT_SCHEMA_VERSION;
23
24
  readonly tool: "vigiles";
25
+ /**
26
+ * Discriminates the three `audit --json` shapes a consumer may receive:
27
+ * `audit` (one plugin → {@link AuditReport}), `leaderboard` (a marketplace /
28
+ * multiple dirs → {@link LeaderboardReport}), `marketplace` (a curated,
29
+ * all-external marketplace → {@link MarketplaceReport}). Always present so the
30
+ * JSON is self-describing.
31
+ */
32
+ readonly kind: "audit";
24
33
  /** The vigiles version that produced the report. */
25
34
  readonly vigilesVersion: string;
26
35
  /** The detected/selected harness (`claude-code`, `codex`, …). */
@@ -104,4 +113,51 @@ export interface BuildAuditReportOptions {
104
113
  * HTML-embedded form omits it so the rendered file stays deterministic.
105
114
  */
106
115
  export declare function buildAuditReport(report: ScanReport, opts: BuildAuditReportOptions): AuditReport;
116
+ /**
117
+ * The versioned envelope for a `audit --json` run over MULTIPLE plugins (a
118
+ * marketplace expanded into its members, or several dirs) — the leaderboard.
119
+ * Shares the same `meta.schemaVersion`/`tool`/`kind` self-description as
120
+ * {@link AuditReport} so every `audit --json` shape is a versioned object, never
121
+ * a bare array. `kind:"leaderboard"` is the discriminant; `plugins` carries the
122
+ * ranked per-plugin scores.
123
+ */
124
+ export interface LeaderboardReport {
125
+ readonly meta: {
126
+ readonly schemaVersion: typeof AUDIT_SCHEMA_VERSION;
127
+ readonly tool: "vigiles";
128
+ readonly kind: "leaderboard";
129
+ readonly vigilesVersion: string;
130
+ /** The marketplace / parent dir that was expanded and ranked. */
131
+ readonly dir: string;
132
+ readonly generatedAt?: string;
133
+ };
134
+ readonly plugins: readonly PluginScore[];
135
+ }
136
+ /** Assemble the versioned {@link LeaderboardReport} — pure, no clock. */
137
+ export declare function buildLeaderboardReport(plugins: readonly PluginScore[], opts: {
138
+ vigilesVersion: string;
139
+ dir: string;
140
+ }): LeaderboardReport;
141
+ /**
142
+ * The versioned envelope for a `audit --json` run on a CURATED marketplace whose
143
+ * members are all external (git/url, nothing on disk to scan). Wraps the
144
+ * {@link MarketplaceInfo} inventory so this path, too, emits a versioned object
145
+ * rather than a raw, unversioned struct. `kind:"marketplace"` is the discriminant.
146
+ */
147
+ export interface MarketplaceReport {
148
+ readonly meta: {
149
+ readonly schemaVersion: typeof AUDIT_SCHEMA_VERSION;
150
+ readonly tool: "vigiles";
151
+ readonly kind: "marketplace";
152
+ readonly vigilesVersion: string;
153
+ readonly dir: string;
154
+ readonly generatedAt?: string;
155
+ };
156
+ readonly marketplace: MarketplaceInfo;
157
+ }
158
+ /** Assemble the versioned {@link MarketplaceReport} — pure, no clock. */
159
+ export declare function buildMarketplaceReport(marketplace: MarketplaceInfo, opts: {
160
+ vigilesVersion: string;
161
+ dir: string;
162
+ }): MarketplaceReport;
107
163
  //# sourceMappingURL=audit-report.d.ts.map
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AUDIT_SCHEMA_VERSION = void 0;
4
4
  exports.buildAuditReport = buildAuditReport;
5
+ exports.buildLeaderboardReport = buildLeaderboardReport;
6
+ exports.buildMarketplaceReport = buildMarketplaceReport;
5
7
  /**
6
8
  * The `AuditReport` — the versioned JSON contract that IS the audit's product
7
9
  * boundary. Everything renders FROM it: the local self-contained HTML report,
@@ -49,6 +51,7 @@ function buildAuditReport(report, opts) {
49
51
  meta: {
50
52
  schemaVersion: exports.AUDIT_SCHEMA_VERSION,
51
53
  tool: "vigiles",
54
+ kind: "audit",
52
55
  vigilesVersion: opts.vigilesVersion,
53
56
  harness: opts.harness,
54
57
  dir: report.dir,
@@ -69,4 +72,30 @@ function buildAuditReport(report, opts) {
69
72
  ...(adoptable ? { adoptable } : {}),
70
73
  };
71
74
  }
75
+ /** Assemble the versioned {@link LeaderboardReport} — pure, no clock. */
76
+ function buildLeaderboardReport(plugins, opts) {
77
+ return {
78
+ meta: {
79
+ schemaVersion: exports.AUDIT_SCHEMA_VERSION,
80
+ tool: "vigiles",
81
+ kind: "leaderboard",
82
+ vigilesVersion: opts.vigilesVersion,
83
+ dir: opts.dir,
84
+ },
85
+ plugins,
86
+ };
87
+ }
88
+ /** Assemble the versioned {@link MarketplaceReport} — pure, no clock. */
89
+ function buildMarketplaceReport(marketplace, opts) {
90
+ return {
91
+ meta: {
92
+ schemaVersion: exports.AUDIT_SCHEMA_VERSION,
93
+ tool: "vigiles",
94
+ kind: "marketplace",
95
+ vigilesVersion: opts.vigilesVersion,
96
+ dir: opts.dir,
97
+ },
98
+ marketplace,
99
+ };
100
+ }
72
101
  //# sourceMappingURL=audit-report.js.map