vigiles 5.1.0 → 5.2.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 +2 -2
- package/dist/adapters/claude-code/adapter.js +1 -0
- package/dist/adapters/claude-code/agent-runtime.d.ts +20 -6
- package/dist/adapters/claude-code/agent-runtime.js +51 -8
- package/dist/adapters/claude-code/dialect.js +19 -0
- package/dist/adapters/claude-code/effect-region.d.ts +9 -0
- package/dist/adapters/claude-code/effect-region.js +45 -0
- package/dist/adapters/claude-code/layout.js +3 -0
- package/dist/adapters/claude-code/skill-runtime.d.ts +25 -0
- package/dist/adapters/claude-code/skill-runtime.js +48 -0
- package/dist/adapters/codex/adapter.js +3 -0
- package/dist/adapters/codex/layout.js +3 -0
- package/dist/adapters/opencode/adapter.js +1 -0
- package/dist/adapters/opencode/layout.js +3 -0
- package/dist/check.d.ts +8 -0
- package/dist/check.js +27 -3
- package/dist/cli.js +323 -88
- package/dist/core/adapter.d.ts +10 -0
- package/dist/core/bash-effects.d.ts +41 -0
- package/dist/core/bash-effects.js +405 -0
- package/dist/core/compile.d.ts +3 -1
- package/dist/core/compile.js +162 -39
- package/dist/core/dialect.d.ts +10 -0
- package/dist/core/effects.d.ts +172 -0
- package/dist/core/effects.js +245 -0
- package/dist/core/layout.d.ts +6 -0
- package/dist/core/mcp-tool.d.ts +1 -1
- package/dist/core/orphans.js +21 -0
- package/dist/core/spec.d.ts +142 -3
- package/dist/core/spec.js +48 -0
- package/dist/core/tool-contract.d.ts +1 -1
- package/dist/core/types.d.ts +6 -6
- package/dist/core/validate.js +4 -4
- package/dist/harness-test.d.ts +7 -0
- package/dist/harness-test.js +19 -7
- package/dist/leaderboard.d.ts +2 -0
- package/dist/leaderboard.js +2 -0
- package/dist/optimize.d.ts +74 -0
- package/dist/optimize.js +94 -0
- package/dist/scaffold-test.d.ts +30 -0
- package/dist/scaffold-test.js +158 -0
- package/dist/scan.d.ts +40 -0
- package/dist/scan.js +91 -43
- package/dist/score-explainer.d.ts +69 -0
- package/dist/score-explainer.js +169 -0
- package/dist/test-coverage.d.ts +7 -0
- package/dist/test-coverage.js +39 -24
- package/package.json +2 -1
- package/skills/{migrate-to-spec → adopt-spec}/SKILL.md +4 -4
- package/skills/edit-spec/SKILL.md +1 -1
package/dist/test-coverage.js
CHANGED
|
@@ -29,6 +29,20 @@ exports.formatUntestedReport = formatUntestedReport;
|
|
|
29
29
|
const node_fs_1 = require("node:fs");
|
|
30
30
|
const node_path_1 = require("node:path");
|
|
31
31
|
const glob_1 = require("glob");
|
|
32
|
+
const layout_js_1 = require("./adapters/claude-code/layout.js");
|
|
33
|
+
/**
|
|
34
|
+
* The two on-disk locations a surface dir can occupy: the plugin-root form
|
|
35
|
+
* (`skills/…`) and the materialized form (`.claude/skills/…`) — derived from the
|
|
36
|
+
* layout so a non-Claude-Code harness (its own `materializeRoot` / surface dir)
|
|
37
|
+
* is discovered without hard-coding `.claude`. An empty `dir` (a harness lacking
|
|
38
|
+
* the surface, e.g. Codex subagents) yields no globs.
|
|
39
|
+
*/
|
|
40
|
+
function surfaceGlobs(dir, leaf, materializeRoot) {
|
|
41
|
+
if (!dir)
|
|
42
|
+
return [];
|
|
43
|
+
const matForm = materializeRoot ? `${materializeRoot}/${dir}` : dir;
|
|
44
|
+
return [...new Set([`${dir}/${leaf}`, `${matForm}/${leaf}`])];
|
|
45
|
+
}
|
|
32
46
|
// ---------------------------------------------------------------------------
|
|
33
47
|
// Internals
|
|
34
48
|
// ---------------------------------------------------------------------------
|
|
@@ -58,12 +72,9 @@ function read(path) {
|
|
|
58
72
|
return "";
|
|
59
73
|
}
|
|
60
74
|
}
|
|
61
|
-
function discoverSkills(basePath, ignore) {
|
|
75
|
+
function discoverSkills(basePath, ignore, layout) {
|
|
62
76
|
const out = [];
|
|
63
|
-
const found = (0, glob_1.globSync)(
|
|
64
|
-
cwd: basePath,
|
|
65
|
-
ignore,
|
|
66
|
-
});
|
|
77
|
+
const found = (0, glob_1.globSync)(surfaceGlobs(layout.skillDir, "*/SKILL.md", layout.materializeRoot), { cwd: basePath, ignore });
|
|
67
78
|
for (const path of found.sort()) {
|
|
68
79
|
const name = (0, node_path_1.basename)((0, node_path_1.dirname)(path));
|
|
69
80
|
const content = read((0, node_path_1.join)(basePath, path));
|
|
@@ -71,18 +82,15 @@ function discoverSkills(basePath, ignore) {
|
|
|
71
82
|
kind: "skill",
|
|
72
83
|
path,
|
|
73
84
|
name,
|
|
74
|
-
tokens: [
|
|
85
|
+
tokens: [`${layout.skillDir}/${name}`, `:${name}`],
|
|
75
86
|
ignored: content.includes(IGNORE_MARKER),
|
|
76
87
|
});
|
|
77
88
|
}
|
|
78
89
|
return out;
|
|
79
90
|
}
|
|
80
|
-
function discoverAgents(basePath, ignore) {
|
|
91
|
+
function discoverAgents(basePath, ignore, layout) {
|
|
81
92
|
const out = [];
|
|
82
|
-
const found = (0, glob_1.globSync)(
|
|
83
|
-
cwd: basePath,
|
|
84
|
-
ignore,
|
|
85
|
-
});
|
|
93
|
+
const found = (0, glob_1.globSync)(surfaceGlobs(layout.agentDir, "*.md", layout.materializeRoot), { cwd: basePath, ignore });
|
|
86
94
|
for (const path of found.sort()) {
|
|
87
95
|
if (path.endsWith(".spec.ts"))
|
|
88
96
|
continue;
|
|
@@ -100,7 +108,7 @@ function discoverAgents(basePath, ignore) {
|
|
|
100
108
|
return out;
|
|
101
109
|
}
|
|
102
110
|
/** Hook-script paths referenced from a manifest's `hooks` block (file hooks only). */
|
|
103
|
-
function hookScripts(basePath, manifest) {
|
|
111
|
+
function hookScripts(basePath, manifest, pluginRootToken) {
|
|
104
112
|
if (!(0, node_fs_1.existsSync)((0, node_path_1.join)(basePath, manifest)))
|
|
105
113
|
return [];
|
|
106
114
|
let hooks;
|
|
@@ -114,25 +122,31 @@ function hookScripts(basePath, manifest) {
|
|
|
114
122
|
if (hooks === undefined)
|
|
115
123
|
return [];
|
|
116
124
|
const text = JSON.stringify(hooks);
|
|
125
|
+
// "${CLAUDE_PLUGIN_ROOT}" → unbraced "$CLAUDE_PLUGIN_ROOT" — strip the harness's
|
|
126
|
+
// own token (both forms) so the path is checkable relative to the plugin root.
|
|
127
|
+
const unbraced = pluginRootToken.replace(/^\$\{(.+)\}$/, "$$$1");
|
|
117
128
|
const scripts = new Set();
|
|
118
129
|
for (const m of text.matchAll(SCRIPT_RE)) {
|
|
119
130
|
const rel = m[0]
|
|
120
|
-
.
|
|
121
|
-
.
|
|
131
|
+
.replaceAll(pluginRootToken, "")
|
|
132
|
+
.replaceAll(unbraced, "")
|
|
133
|
+
.replace(/^\/+/, "")
|
|
122
134
|
.replace(/^\.\//, "");
|
|
123
135
|
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(basePath, rel)))
|
|
124
136
|
scripts.add(rel);
|
|
125
137
|
}
|
|
126
138
|
return [...scripts];
|
|
127
139
|
}
|
|
128
|
-
function discoverHooks(basePath) {
|
|
140
|
+
function discoverHooks(basePath, layout) {
|
|
129
141
|
const scripts = new Set();
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
142
|
+
// The harness's manifest + settings (and a `.local` settings sibling, a CC
|
|
143
|
+
// convention that's harmless to probe elsewhere).
|
|
144
|
+
const localSettings = layout.settingsPath.replace(/(\.[^./]+)$/, ".local$1");
|
|
145
|
+
const manifests = [
|
|
146
|
+
...new Set([layout.manifestPath, layout.settingsPath, localSettings]),
|
|
147
|
+
];
|
|
148
|
+
for (const m of manifests) {
|
|
149
|
+
for (const s of hookScripts(basePath, m, layout.pluginRootToken))
|
|
136
150
|
scripts.add(s);
|
|
137
151
|
}
|
|
138
152
|
return [...scripts].sort().map((path) => ({
|
|
@@ -185,15 +199,16 @@ function isCovered(surface, tests) {
|
|
|
185
199
|
*/
|
|
186
200
|
function findUntestedSurfaces(options = {}) {
|
|
187
201
|
const basePath = options.basePath ?? process.cwd();
|
|
202
|
+
const layout = options.layout ?? layout_js_1.claudeCodeLayout;
|
|
188
203
|
const ignore = [...DEFAULT_IGNORE, ...(options.exclude ?? [])];
|
|
189
204
|
const globs = options.testGlobs ?? DEFAULT_TEST_GLOBS;
|
|
190
205
|
const surfaces = [];
|
|
191
206
|
if (options.skills !== false)
|
|
192
|
-
surfaces.push(...discoverSkills(basePath, ignore));
|
|
207
|
+
surfaces.push(...discoverSkills(basePath, ignore, layout));
|
|
193
208
|
if (options.agents !== false)
|
|
194
|
-
surfaces.push(...discoverAgents(basePath, ignore));
|
|
209
|
+
surfaces.push(...discoverAgents(basePath, ignore, layout));
|
|
195
210
|
if (options.hooks !== false)
|
|
196
|
-
surfaces.push(...discoverHooks(basePath));
|
|
211
|
+
surfaces.push(...discoverHooks(basePath, layout));
|
|
197
212
|
// Every skill/agent/hook is held to the requirement — only an explicit
|
|
198
213
|
// `vigiles:ignore-test` marker exempts a surface (a visible, deliberate skip).
|
|
199
214
|
const considered = surfaces.filter((s) => !s.ignored);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vigiles",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
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",
|
|
@@ -128,6 +128,7 @@
|
|
|
128
128
|
"glob": "^13.0.6",
|
|
129
129
|
"js-yaml": "^4.1.0",
|
|
130
130
|
"minimatch": "^10.0.1",
|
|
131
|
+
"mvdan-sh": "^0.10.1",
|
|
131
132
|
"ts-essentials": "^10.2.1",
|
|
132
133
|
"typescript": "^5.9.3"
|
|
133
134
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description:
|
|
2
|
+
name: adopt-spec
|
|
3
|
+
description: Adopt a typed .spec.ts for an existing hand-written CLAUDE.md — start from the file you already have, non-destructively
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
argument-hint: <path to CLAUDE.md, defaults to CLAUDE.md>
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Start a typed `CLAUDE.md.spec.ts` from an existing hand-written CLAUDE.md (or AGENTS.md). This is the non-destructive adoption path — you keep your existing instruction file as the starting point and get type safety going forward.
|
|
9
9
|
|
|
10
|
-
> **Don't need full TypeScript?** A typed spec is the deepest commitment level. If the user only wants verified rules without a build step, point them at markdown mode first: inline `<!-- vigiles:enforce ... -->` comments (Level 0) or a `vigiles:` YAML frontmatter block with `vigiles generate-schema` for editor autocomplete (Level 1). Both are verified by `vigiles lint` with the same engine as a spec. See `docs/markdown-mode.md`.
|
|
10
|
+
> **Don't need full TypeScript?** A typed spec is the deepest commitment level. If the user only wants verified rules without a build step, point them at markdown mode first: inline `<!-- vigiles:enforce ... -->` comments (Level 0) or a `vigiles:` YAML frontmatter block with `vigiles generate-schema` for editor autocomplete (Level 1). Both are verified by `vigiles lint` with the same engine as a spec. See `docs/markdown-mode.md`. Adopt a spec only when they want compiler-grade guarantees.
|
|
11
11
|
|
|
12
12
|
## Instructions
|
|
13
13
|
|
|
@@ -27,7 +27,7 @@ Look for spec files in the repo root:
|
|
|
27
27
|
- Any `*.spec.ts` matching instruction files
|
|
28
28
|
|
|
29
29
|
If no spec exists: if there's a hand-written `CLAUDE.md`, suggest the
|
|
30
|
-
`
|
|
30
|
+
`adopt-spec` skill; otherwise suggest `npx vigiles init` to scaffold one.
|
|
31
31
|
|
|
32
32
|
### Step 2: Read and Understand the Spec
|
|
33
33
|
|