vigiles 14.13.2 → 14.13.4
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/.claude-plugin/marketplace.json +1 -1
- package/README.md +1 -1
- package/dist/adapters/claude-code/run-scripts.js +12 -0
- package/dist/core/agent-plugins.d.ts +44 -0
- package/dist/core/agent-plugins.js +68 -0
- package/dist/scan-files.js +18 -4
- package/dist/scan.js +25 -6
- package/package.json +2 -1
- package/plugin.json +14 -0
package/README.md
CHANGED
|
@@ -256,7 +256,7 @@ Targets Claude Code and Codex out of the box, or [your own harness](docs/authori
|
|
|
256
256
|
- **Reference** — [CLI](docs/cli.md) · [rules matrix](docs/verifying-instruction-files.md#the-validation-rules--the-full-matrix) · [testing API](docs/testing-api.md) · [full API](https://zernie.github.io/vigiles/api/)
|
|
257
257
|
- **Explanation** — [what it catches](docs/what-vigiles-catches.md) · [how it compares](docs/comparison.md) · [FAQ](docs/faq.md)
|
|
258
258
|
|
|
259
|
-
**Project** — [Stability](STABILITY.md) · [Related tools](docs/comparison.md#what-vigiles-composes-with)
|
|
259
|
+
**Project** — [Stability](STABILITY.md) · [Related tools](docs/comparison.md#what-vigiles-composes-with) · ships as an [Agent Plugins](https://agent-plugins.org) 1.0.0 plugin ([how to do the same](docs/for-plugin-authors.md#6-ship-it-in-the-portable-agent-plugins-format))
|
|
260
260
|
|
|
261
261
|
<!-- The "companion to [Feedback Loop Is All You Need](https://zernie.com/blog/feedback-loop-is-all-you-need)"
|
|
262
262
|
link is temporarily removed while AgenticDev paper #1 is under blind review: repo → blog is a
|
|
@@ -83,9 +83,21 @@ function discoverScripts(patterns, defaultGlob, cwd) {
|
|
|
83
83
|
found.add(p);
|
|
84
84
|
continue;
|
|
85
85
|
}
|
|
86
|
+
// 🔴 `dot: true`, because the harness for a Claude Code harness lives in `.claude/`.
|
|
87
|
+
// Without it, `vigiles test` and `vigiles eval` print "no files found" in a repository
|
|
88
|
+
// that has them, and `Tested` then measures visibility rather than coverage — the author
|
|
89
|
+
// reads "you have no tests" when the honest reading is "I looked in the wrong place".
|
|
90
|
+
// Observed 2026-08-07 on a repo with two harnesses under `.claude/`, both invisible; it
|
|
91
|
+
// stayed hidden because that repo's CI happened to pass explicit paths.
|
|
92
|
+
//
|
|
93
|
+
// This codebase already fixed the same defect elsewhere and missed it here:
|
|
94
|
+
// `test-coverage.ts` and `cli.ts` both pass `dot: true` with comments saying why, and
|
|
95
|
+
// `test-coverage.test.ts` records "glob without `dot:true` never found it and the surface
|
|
96
|
+
// looked untested". Coverage learned it; the runner did not.
|
|
86
97
|
for (const m of (0, glob_1.globSync)(p, {
|
|
87
98
|
cwd,
|
|
88
99
|
ignore: ["node_modules/**", "dist/**"],
|
|
100
|
+
dot: true,
|
|
89
101
|
})) {
|
|
90
102
|
found.add(m);
|
|
91
103
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Plugins — the vendor-neutral packaging standard (agent-plugins.org),
|
|
3
|
+
* v1.0.0. A plugin declares itself with a root `plugin.json` and puts its
|
|
4
|
+
* components at fixed paths: skills at `skills/<name>/SKILL.md`, MCP servers in
|
|
5
|
+
* a root `mcp.json`.
|
|
6
|
+
*
|
|
7
|
+
* WHY THIS IS NOT AN ADAPTER. The standard is a PACKAGING format, not a harness:
|
|
8
|
+
* it has no tool catalog, no hook events, no runtime — a plugin shipped this way
|
|
9
|
+
* still runs inside Claude Code or Codex. So it COMPLEMENTS a harness rather than
|
|
10
|
+
* replacing one, and a repo commonly carries both manifests side by side (vigiles
|
|
11
|
+
* itself does). Modelling it as a `HarnessAdapter` would mean inventing a fake
|
|
12
|
+
* `HarnessDialect` — exactly the half-wired port the conformance kit refuses.
|
|
13
|
+
*
|
|
14
|
+
* WHAT THIS MODULE IS FOR. The skills half already works for free: the standard's
|
|
15
|
+
* `skills/<name>/SKILL.md` is the layout every adapter already reads. The gap is
|
|
16
|
+
* MCP — the standard puts servers in a root `mcp.json`, which no harness layout
|
|
17
|
+
* names, so `mcp-config` / `mcp-tool-resolves` / `mcp-hook-target-resolves`
|
|
18
|
+
* would silently check nothing on a plugin laid out this way. This module tells
|
|
19
|
+
* the scanner when that file is in play.
|
|
20
|
+
*
|
|
21
|
+
* DETECTION IS BY `$schema`, NOT BY FILENAME. `plugin.json` and `mcp.json` are
|
|
22
|
+
* generic names other tools use too. A plugin is treated as conformant only when
|
|
23
|
+
* its root manifest pins an `agent-plugins.org` schema — then a sibling
|
|
24
|
+
* `mcp.json` is unambiguously the standard's, and is read even if it omits its
|
|
25
|
+
* own `$schema`. Pure (no `node:fs`): the caller injects the read, so the
|
|
26
|
+
* browser engine can back it with a file map.
|
|
27
|
+
*/
|
|
28
|
+
/** The standard's root manifest filename. */
|
|
29
|
+
export declare const AGENT_PLUGINS_MANIFEST = "plugin.json";
|
|
30
|
+
/** The standard's root MCP-server config filename. */
|
|
31
|
+
export declare const AGENT_PLUGINS_MCP_CONFIG = "mcp.json";
|
|
32
|
+
/**
|
|
33
|
+
* Does this manifest text declare an Agent Plugins plugin? True only when it
|
|
34
|
+
* parses and its `$schema` points at the standard — a `plugin.json` belonging to
|
|
35
|
+
* something else is not claimed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isAgentPluginsManifest(text: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* The extra MCP config files to read for this root — `[mcp.json]` when the root
|
|
40
|
+
* manifest declares an Agent Plugins plugin, `[]` otherwise. `readText` returns
|
|
41
|
+
* the file's contents, or `undefined` when it does not exist.
|
|
42
|
+
*/
|
|
43
|
+
export declare function agentPluginsMcpSources(readText: (file: string) => string | undefined): readonly string[];
|
|
44
|
+
//# sourceMappingURL=agent-plugins.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Agent Plugins — the vendor-neutral packaging standard (agent-plugins.org),
|
|
4
|
+
* v1.0.0. A plugin declares itself with a root `plugin.json` and puts its
|
|
5
|
+
* components at fixed paths: skills at `skills/<name>/SKILL.md`, MCP servers in
|
|
6
|
+
* a root `mcp.json`.
|
|
7
|
+
*
|
|
8
|
+
* WHY THIS IS NOT AN ADAPTER. The standard is a PACKAGING format, not a harness:
|
|
9
|
+
* it has no tool catalog, no hook events, no runtime — a plugin shipped this way
|
|
10
|
+
* still runs inside Claude Code or Codex. So it COMPLEMENTS a harness rather than
|
|
11
|
+
* replacing one, and a repo commonly carries both manifests side by side (vigiles
|
|
12
|
+
* itself does). Modelling it as a `HarnessAdapter` would mean inventing a fake
|
|
13
|
+
* `HarnessDialect` — exactly the half-wired port the conformance kit refuses.
|
|
14
|
+
*
|
|
15
|
+
* WHAT THIS MODULE IS FOR. The skills half already works for free: the standard's
|
|
16
|
+
* `skills/<name>/SKILL.md` is the layout every adapter already reads. The gap is
|
|
17
|
+
* MCP — the standard puts servers in a root `mcp.json`, which no harness layout
|
|
18
|
+
* names, so `mcp-config` / `mcp-tool-resolves` / `mcp-hook-target-resolves`
|
|
19
|
+
* would silently check nothing on a plugin laid out this way. This module tells
|
|
20
|
+
* the scanner when that file is in play.
|
|
21
|
+
*
|
|
22
|
+
* DETECTION IS BY `$schema`, NOT BY FILENAME. `plugin.json` and `mcp.json` are
|
|
23
|
+
* generic names other tools use too. A plugin is treated as conformant only when
|
|
24
|
+
* its root manifest pins an `agent-plugins.org` schema — then a sibling
|
|
25
|
+
* `mcp.json` is unambiguously the standard's, and is read even if it omits its
|
|
26
|
+
* own `$schema`. Pure (no `node:fs`): the caller injects the read, so the
|
|
27
|
+
* browser engine can back it with a file map.
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.AGENT_PLUGINS_MCP_CONFIG = exports.AGENT_PLUGINS_MANIFEST = void 0;
|
|
31
|
+
exports.isAgentPluginsManifest = isAgentPluginsManifest;
|
|
32
|
+
exports.agentPluginsMcpSources = agentPluginsMcpSources;
|
|
33
|
+
/** The standard's root manifest filename. */
|
|
34
|
+
exports.AGENT_PLUGINS_MANIFEST = "plugin.json";
|
|
35
|
+
/** The standard's root MCP-server config filename. */
|
|
36
|
+
exports.AGENT_PLUGINS_MCP_CONFIG = "mcp.json";
|
|
37
|
+
/** Canonical schema host — a manifest pinning one of these targets the standard. */
|
|
38
|
+
const SCHEMA_PREFIX = "https://agent-plugins.org/schemas/";
|
|
39
|
+
/**
|
|
40
|
+
* Does this manifest text declare an Agent Plugins plugin? True only when it
|
|
41
|
+
* parses and its `$schema` points at the standard — a `plugin.json` belonging to
|
|
42
|
+
* something else is not claimed.
|
|
43
|
+
*/
|
|
44
|
+
function isAgentPluginsManifest(text) {
|
|
45
|
+
let parsed;
|
|
46
|
+
try {
|
|
47
|
+
parsed = JSON.parse(text);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (parsed === null || typeof parsed !== "object")
|
|
53
|
+
return false;
|
|
54
|
+
const schema = parsed.$schema;
|
|
55
|
+
return typeof schema === "string" && schema.startsWith(SCHEMA_PREFIX);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The extra MCP config files to read for this root — `[mcp.json]` when the root
|
|
59
|
+
* manifest declares an Agent Plugins plugin, `[]` otherwise. `readText` returns
|
|
60
|
+
* the file's contents, or `undefined` when it does not exist.
|
|
61
|
+
*/
|
|
62
|
+
function agentPluginsMcpSources(readText) {
|
|
63
|
+
const manifest = readText(exports.AGENT_PLUGINS_MANIFEST);
|
|
64
|
+
if (manifest === undefined || !isAgentPluginsManifest(manifest))
|
|
65
|
+
return [];
|
|
66
|
+
return [exports.AGENT_PLUGINS_MCP_CONFIG];
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=agent-plugins.js.map
|
package/dist/scan-files.js
CHANGED
|
@@ -45,6 +45,7 @@ const dialect_js_1 = require("./adapters/claude-code/dialect.js");
|
|
|
45
45
|
const hook_normalize_js_1 = require("./core/hook-normalize.js");
|
|
46
46
|
const hook_events_js_1 = require("./core/hook-events.js");
|
|
47
47
|
const mcp_config_js_1 = require("./core/mcp-config.js");
|
|
48
|
+
const agent_plugins_js_1 = require("./core/agent-plugins.js");
|
|
48
49
|
const mcp_hook_js_1 = require("./core/mcp-hook.js");
|
|
49
50
|
const plugin_dir_layout_js_1 = require("./core/plugin-dir-layout.js");
|
|
50
51
|
const hook_block_ineffective_js_1 = require("./core/hook-block-ineffective.js");
|
|
@@ -424,8 +425,9 @@ function loadPluginFromFiles(files, layout, repoName) {
|
|
|
424
425
|
// ---------------------------------------------------------------------------
|
|
425
426
|
function collectMcpServers(files, layout) {
|
|
426
427
|
const servers = {};
|
|
428
|
+
const read = (file) => files[file];
|
|
427
429
|
const collect = (file) => {
|
|
428
|
-
const text =
|
|
430
|
+
const text = read(file);
|
|
429
431
|
if (text === undefined)
|
|
430
432
|
return;
|
|
431
433
|
try {
|
|
@@ -438,8 +440,16 @@ function collectMcpServers(files, layout) {
|
|
|
438
440
|
/* malformed JSON is the loader's concern, not this check's */
|
|
439
441
|
}
|
|
440
442
|
};
|
|
441
|
-
|
|
442
|
-
|
|
443
|
+
// Mirrors the fs-backed collector in scan.ts: the layout's own locations plus
|
|
444
|
+
// the Agent Plugins standard's root `mcp.json` when this repo ships that
|
|
445
|
+
// manifest (no harness layout names it, so the MCP checks would miss it).
|
|
446
|
+
for (const file of [
|
|
447
|
+
layout.mcpConfigFile,
|
|
448
|
+
layout.manifestPath,
|
|
449
|
+
...(0, agent_plugins_js_1.agentPluginsMcpSources)(read),
|
|
450
|
+
]) {
|
|
451
|
+
collect(file);
|
|
452
|
+
}
|
|
443
453
|
return servers;
|
|
444
454
|
}
|
|
445
455
|
// ---------------------------------------------------------------------------
|
|
@@ -502,7 +512,11 @@ function scanFiles(files, layout = layout_js_1.claudeCodeLayout, dialect = diale
|
|
|
502
512
|
inlineHooks: inline,
|
|
503
513
|
manualHookCount: manual,
|
|
504
514
|
commands: Object.keys(loaded.files).filter(cls.isCommand).length,
|
|
505
|
-
|
|
515
|
+
// A declared server set counts even when the loader emitted no warning —
|
|
516
|
+
// otherwise a plugin whose servers come from the Agent Plugins `mcp.json`
|
|
517
|
+
// reports "MCP servers: no" while the report lists an MCP finding.
|
|
518
|
+
mcp: loaded.warnings.some((w) => w.includes("MCP server")) ||
|
|
519
|
+
declaredServers.length > 0,
|
|
506
520
|
danglingRefs: danglingRefs(files, lay, repoName ?? (0, posix_path_js_1.basename)(exports.BROWSER_ROOT)),
|
|
507
521
|
hookEventIssues,
|
|
508
522
|
frontmatterIssues: remap((0, scan_core_js_1.frontmatterIssuesFor)(loaded.files, cls)),
|
package/dist/scan.js
CHANGED
|
@@ -41,6 +41,7 @@ const dialect_js_1 = require("./adapters/claude-code/dialect.js");
|
|
|
41
41
|
const plugin_loader_js_2 = require("./plugin-loader.js");
|
|
42
42
|
const hook_events_js_1 = require("./core/hook-events.js");
|
|
43
43
|
const mcp_config_js_1 = require("./core/mcp-config.js");
|
|
44
|
+
const agent_plugins_js_1 = require("./core/agent-plugins.js");
|
|
44
45
|
const hook_normalize_js_1 = require("./core/hook-normalize.js");
|
|
45
46
|
const mcp_js_1 = require("./core/mcp.js");
|
|
46
47
|
const mcp_contract_message_js_1 = require("./core/mcp-contract-message.js");
|
|
@@ -67,12 +68,16 @@ __exportStar(require("./scan-core.js"), exports);
|
|
|
67
68
|
*/
|
|
68
69
|
function collectMcpServers(root, layout) {
|
|
69
70
|
const servers = {};
|
|
70
|
-
const
|
|
71
|
+
const read = (file) => {
|
|
71
72
|
const p = (0, node_path_1.join)(root, file);
|
|
72
|
-
|
|
73
|
+
return (0, node_fs_1.existsSync)(p) ? (0, node_fs_1.readFileSync)(p, "utf-8") : undefined;
|
|
74
|
+
};
|
|
75
|
+
const collect = (file) => {
|
|
76
|
+
const text = read(file);
|
|
77
|
+
if (text === undefined)
|
|
73
78
|
return;
|
|
74
79
|
try {
|
|
75
|
-
const parsed = JSON.parse(
|
|
80
|
+
const parsed = JSON.parse(text);
|
|
76
81
|
if (parsed.mcpServers !== null && typeof parsed.mcpServers === "object") {
|
|
77
82
|
Object.assign(servers, parsed.mcpServers);
|
|
78
83
|
}
|
|
@@ -81,8 +86,18 @@ function collectMcpServers(root, layout) {
|
|
|
81
86
|
/* malformed JSON is the loader's concern, not this check's */
|
|
82
87
|
}
|
|
83
88
|
};
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
// The harness's own locations (from the layout — never a hard-coded literal),
|
|
90
|
+
// plus the Agent Plugins standard's root `mcp.json` when the repo ships that
|
|
91
|
+
// manifest. A plugin in the vendor-neutral format declares its servers there,
|
|
92
|
+
// which no harness layout names — without this the MCP checks would silently
|
|
93
|
+
// pass over it.
|
|
94
|
+
for (const file of [
|
|
95
|
+
layout.mcpConfigFile,
|
|
96
|
+
layout.manifestPath,
|
|
97
|
+
...(0, agent_plugins_js_1.agentPluginsMcpSources)(read),
|
|
98
|
+
]) {
|
|
99
|
+
collect(file);
|
|
100
|
+
}
|
|
86
101
|
return servers;
|
|
87
102
|
}
|
|
88
103
|
// Disk-backed IO the moved detectors take by injection (they never import
|
|
@@ -172,7 +187,11 @@ function scanPlugin(dir, layout, dialect = dialect_js_1.claudeCodeDialect, opts
|
|
|
172
187
|
inlineHooks: inline,
|
|
173
188
|
manualHookCount: manual,
|
|
174
189
|
commands: Object.keys(loaded.files).filter(cls.isCommand).length,
|
|
175
|
-
|
|
190
|
+
// A declared server set counts even when the loader emitted no warning —
|
|
191
|
+
// otherwise a plugin whose servers come from the Agent Plugins `mcp.json`
|
|
192
|
+
// reports "MCP servers: no" while the report lists an MCP finding.
|
|
193
|
+
mcp: loaded.warnings.some((w) => w.includes("MCP server")) ||
|
|
194
|
+
declaredServers.length > 0,
|
|
176
195
|
danglingRefs: (0, plugin_loader_js_2.danglingRefs)((0, node_path_1.resolve)(dir), lay),
|
|
177
196
|
hookEventIssues,
|
|
178
197
|
frontmatterIssues: remap((0, scan_core_js_1.frontmatterIssuesFor)(loaded.files, cls)),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vigiles",
|
|
3
|
-
"version": "14.13.
|
|
3
|
+
"version": "14.13.4",
|
|
4
4
|
"description": "Lint & test the harness your AI agent runs on — verify the references in your CLAUDE.md / AGENTS.md and test that your hooks and skills actually work.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"!dist/**/*.test.js",
|
|
66
66
|
"!dist/**/*.test.d.ts",
|
|
67
67
|
"action.yml",
|
|
68
|
+
"plugin.json",
|
|
68
69
|
".claude-plugin",
|
|
69
70
|
"hooks",
|
|
70
71
|
"skills",
|
package/plugin.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
|
+
"name": "vigiles",
|
|
4
|
+
"version": "4.0.1",
|
|
5
|
+
"description": "Verify CLAUDE.md/AGENTS.md references, compile typed specs, and test the agent harness",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "zernie",
|
|
8
|
+
"url": "https://github.com/zernie"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://vigiles.sh",
|
|
11
|
+
"repository": "https://github.com/zernie/vigiles",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"keywords": ["linting", "claude-md", "agents-md", "feedback-loops", "harness"]
|
|
14
|
+
}
|