@vibe-agent-toolkit/agent-skills 0.1.41-rc.7 → 0.1.41
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/dist/content-type-routing.d.ts +18 -1
- package/dist/content-type-routing.d.ts.map +1 -1
- package/dist/content-type-routing.js +17 -0
- package/dist/content-type-routing.js.map +1 -1
- package/dist/evidence/build-evidence.d.ts +38 -0
- package/dist/evidence/build-evidence.d.ts.map +1 -0
- package/dist/evidence/build-evidence.js +60 -0
- package/dist/evidence/build-evidence.js.map +1 -0
- package/dist/evidence/index.d.ts +1 -0
- package/dist/evidence/index.d.ts.map +1 -1
- package/dist/evidence/index.js +1 -0
- package/dist/evidence/index.js.map +1 -1
- package/dist/files-config.d.ts +225 -31
- package/dist/files-config.d.ts.map +1 -1
- package/dist/files-config.js +445 -123
- package/dist/files-config.js.map +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/dist/post-build-checks.d.ts +10 -1
- package/dist/post-build-checks.d.ts.map +1 -1
- package/dist/post-build-checks.js +55 -6
- package/dist/post-build-checks.js.map +1 -1
- package/dist/schemas/index.d.ts +3 -2
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +2 -2
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/installed-plugins-registry.d.ts +137 -63
- package/dist/schemas/installed-plugins-registry.d.ts.map +1 -1
- package/dist/schemas/installed-plugins-registry.js +94 -6
- package/dist/schemas/installed-plugins-registry.js.map +1 -1
- package/dist/schemas/known-marketplaces-registry.d.ts +165 -100
- package/dist/schemas/known-marketplaces-registry.d.ts.map +1 -1
- package/dist/schemas/known-marketplaces-registry.js +108 -21
- package/dist/schemas/known-marketplaces-registry.js.map +1 -1
- package/dist/schemas/registry-drift.d.ts +53 -0
- package/dist/schemas/registry-drift.d.ts.map +1 -0
- package/dist/schemas/registry-drift.js +50 -0
- package/dist/schemas/registry-drift.js.map +1 -0
- package/dist/skill-packager.d.ts +75 -5
- package/dist/skill-packager.d.ts.map +1 -1
- package/dist/skill-packager.js +304 -50
- package/dist/skill-packager.js.map +1 -1
- package/dist/skill-source/git-clone.d.ts.map +1 -1
- package/dist/skill-source/git-clone.js +32 -11
- package/dist/skill-source/git-clone.js.map +1 -1
- package/dist/skill-test/eval-inputs.js +2 -2
- package/dist/skill-test/eval-inputs.js.map +1 -1
- package/dist/skill-test/eval-lint.js +2 -2
- package/dist/skill-test/eval-lint.js.map +1 -1
- package/dist/skill-test/grader-prompt.js +2 -2
- package/dist/skill-test/grader-prompt.js.map +1 -1
- package/dist/test-input.d.ts +59 -24
- package/dist/test-input.d.ts.map +1 -1
- package/dist/test-input.js +77 -16
- package/dist/test-input.js.map +1 -1
- package/dist/validators/agent-instruction-presence.d.ts +41 -0
- package/dist/validators/agent-instruction-presence.d.ts.map +1 -0
- package/dist/validators/agent-instruction-presence.js +79 -0
- package/dist/validators/agent-instruction-presence.js.map +1 -0
- package/dist/validators/bundled-resource-link-detection.d.ts +20 -3
- package/dist/validators/bundled-resource-link-detection.d.ts.map +1 -1
- package/dist/validators/bundled-resource-link-detection.js +78 -34
- package/dist/validators/bundled-resource-link-detection.js.map +1 -1
- package/dist/validators/compat-detectors.d.ts +9 -4
- package/dist/validators/compat-detectors.d.ts.map +1 -1
- package/dist/validators/compat-detectors.js +18 -30
- package/dist/validators/compat-detectors.js.map +1 -1
- package/dist/validators/index.d.ts +1 -0
- package/dist/validators/index.d.ts.map +1 -1
- package/dist/validators/index.js +1 -0
- package/dist/validators/index.js.map +1 -1
- package/dist/validators/packaging-validator.d.ts +68 -10
- package/dist/validators/packaging-validator.d.ts.map +1 -1
- package/dist/validators/packaging-validator.js +51 -10
- package/dist/validators/packaging-validator.js.map +1 -1
- package/dist/validators/registry-validator.d.ts.map +1 -1
- package/dist/validators/registry-validator.js +28 -7
- package/dist/validators/registry-validator.js.map +1 -1
- package/dist/validators/rule-engine/rule-context.d.ts +4 -2
- package/dist/validators/rule-engine/rule-context.d.ts.map +1 -1
- package/dist/validators/rule-engine/rule-context.js.map +1 -1
- package/dist/validators/rule-engine/rule-engine.d.ts.map +1 -1
- package/dist/validators/rule-engine/rule-engine.js +15 -3
- package/dist/validators/rule-engine/rule-engine.js.map +1 -1
- package/dist/validators/skill-validator.d.ts.map +1 -1
- package/dist/validators/skill-validator.js +16 -10
- package/dist/validators/skill-validator.js.map +1 -1
- package/dist/validators/source-only-codes.d.ts.map +1 -1
- package/dist/validators/source-only-codes.js +1 -0
- package/dist/validators/source-only-codes.js.map +1 -1
- package/dist/validators/validation-rules.d.ts +135 -17
- package/dist/validators/validation-rules.d.ts.map +1 -1
- package/dist/validators/validation-rules.js +192 -38
- package/dist/validators/validation-rules.js.map +1 -1
- package/dist/validators/walker-to-issues.d.ts.map +1 -1
- package/dist/validators/walker-to-issues.js +20 -7
- package/dist/validators/walker-to-issues.js.map +1 -1
- package/dist/walk-link-graph.d.ts +10 -1
- package/dist/walk-link-graph.d.ts.map +1 -1
- package/dist/walk-link-graph.js +124 -39
- package/dist/walk-link-graph.js.map +1 -1
- package/package.json +5 -5
package/dist/test-input.js
CHANGED
|
@@ -15,6 +15,13 @@
|
|
|
15
15
|
* — because "we stopped shipping it" and "the executor can't read it" must not
|
|
16
16
|
* be the same guarantee.)
|
|
17
17
|
*
|
|
18
|
+
* The rule is PROJECT-WIDE, not per-skill: a file that ANY skill declares as its eval
|
|
19
|
+
* suite is test input, and never ships in ANY skill's bundle. Skill A's docs are free
|
|
20
|
+
* to cite skill B's suite as a worked example — that citation is a reference, not a
|
|
21
|
+
* reason to package B's answer key inside A. The invariant "the executor's filesystem
|
|
22
|
+
* contains no answer key" therefore covers every declared suite in the project, not
|
|
23
|
+
* only the subject's own.
|
|
24
|
+
*
|
|
18
25
|
* This module is the ONE definition of where a skill's test input lives, shared by
|
|
19
26
|
* every packaging lane, so no lane can answer the question differently.
|
|
20
27
|
*/
|
|
@@ -34,8 +41,14 @@ function hasConventionalSuite(skillDir) {
|
|
|
34
41
|
// eslint-disable-next-line security/detect-non-literal-fs-filename -- skillDir is VAT's own resolved skill directory
|
|
35
42
|
return existsSync(safePath.resolve(skillDir, DEFAULT_EVALS_SUBPATH));
|
|
36
43
|
}
|
|
44
|
+
/** True when `candidate` is inside (or equal to) `dir`. Both are absolute. */
|
|
45
|
+
function isInside(candidate, dir) {
|
|
46
|
+
const c = safePath.resolve(candidate);
|
|
47
|
+
const d = safePath.resolve(dir);
|
|
48
|
+
return c === d || c.startsWith(`${d}/`);
|
|
49
|
+
}
|
|
37
50
|
/**
|
|
38
|
-
* Absolute directories holding
|
|
51
|
+
* Absolute directories holding ONE skill's declared test input.
|
|
39
52
|
*
|
|
40
53
|
* The unit is the DIRECTORY containing the suite file (it also holds the suite's
|
|
41
54
|
* `fixtures/`), except when the suite sits at the skill root — where the "directory"
|
|
@@ -56,7 +69,7 @@ function hasConventionalSuite(skillDir) {
|
|
|
56
69
|
* root `evals/` holding no `evals.json`. Guessing from the name alone would silently
|
|
57
70
|
* drop an author's unrelated directory out of their own bundle.
|
|
58
71
|
*/
|
|
59
|
-
|
|
72
|
+
function declaredTestInputDirs(config, skillDir) {
|
|
60
73
|
if (config.test === undefined && !hasConventionalSuite(skillDir))
|
|
61
74
|
return [];
|
|
62
75
|
const subpath = config.test?.evals ?? DEFAULT_EVALS_SUBPATH;
|
|
@@ -67,6 +80,55 @@ export function resolveTestInputDirs(config, skillDir) {
|
|
|
67
80
|
return [];
|
|
68
81
|
return [safePath.resolve(skillDir, parent)];
|
|
69
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Absolute directories holding declared test input, from the point of view of the
|
|
85
|
+
* skill being packaged: its OWN suite, plus EVERY other suite the project declares.
|
|
86
|
+
*
|
|
87
|
+
* The cross-skill half is not defensive breadth — it is the same rule applied to the
|
|
88
|
+
* same kind of file. A doc citing another skill's eval suite as a worked example is an
|
|
89
|
+
* entirely reasonable doc, and the link walker follows those citations. Keyed only to
|
|
90
|
+
* the subject, this function called another skill's answer key "ordinary content", so
|
|
91
|
+
* the walker bundled it — with no exclusion and no `PACKAGED_TEST_INPUT` receipt,
|
|
92
|
+
* because nothing under the subject's config named that path.
|
|
93
|
+
*
|
|
94
|
+
* Observed ACTIVE on a 90-skill adopter (2026-07-30, `vat skills build`): a built
|
|
95
|
+
* bundle contained a file byte-identical to a DIFFERENT skill's real eval suite,
|
|
96
|
+
* carried under that bundle's own subtree. The answer key shipped. A basename
|
|
97
|
+
* collision between two such suites does not save anyone — a FILENAME_COLLISION is
|
|
98
|
+
* REPORTED, not thrown, and the copy still happens.
|
|
99
|
+
*
|
|
100
|
+
* Nothing else in the pipeline sees it. `LINK_TO_SKILL_DEFINITION` covers only another
|
|
101
|
+
* skill's SKILL.md. And the two mechanisms in THIS module that exist to catch a
|
|
102
|
+
* shipped suite — the `PACKAGED_TEST_INPUT` receipt and the `checkPackagedTestInput`
|
|
103
|
+
* backstop — are both fed the dirs this function returns, so while those dirs were the
|
|
104
|
+
* subject's alone, both were structurally blind to another skill's suite. That is why
|
|
105
|
+
* the fix has to be a project-level INPUT here rather than another check downstream.
|
|
106
|
+
*
|
|
107
|
+
* `projectSkills` may include the skill being packaged; dirs are deduplicated, and the
|
|
108
|
+
* subject's own come first. It is REQUIRED, with no default: a defaulted parameter made
|
|
109
|
+
* every pre-existing caller silently keep the per-skill behaviour, so the rule above
|
|
110
|
+
* was written down and shipped as a no-op. A lane that genuinely cannot enumerate the
|
|
111
|
+
* project's skills passes `[]` at the call site, where the narrowing is visible and has
|
|
112
|
+
* to be justified in a comment — it can no longer happen by omission.
|
|
113
|
+
*
|
|
114
|
+
* SCOPE OF THE CROSS-SKILL HALF — another skill's suite dir travels to this build only
|
|
115
|
+
* when it lies INSIDE that skill's own directory. A `test.evals` pointing at a SHARED
|
|
116
|
+
* directory already strips that whole directory from the declaring skill's bundle
|
|
117
|
+
* (issue #166); honouring it project-wide would turn one skill's over-broad
|
|
118
|
+
* declaration into a project-wide strip of a directory no single skill owns. The
|
|
119
|
+
* declaring skill's own build is unchanged — the `config`/`skillDir` pair above is
|
|
120
|
+
* still taken at face value — so this narrows nothing that worked before.
|
|
121
|
+
*/
|
|
122
|
+
export function resolveTestInputDirs(config, skillDir, projectSkills) {
|
|
123
|
+
const dirs = new Set(declaredTestInputDirs(config, skillDir));
|
|
124
|
+
for (const skill of projectSkills) {
|
|
125
|
+
for (const dir of declaredTestInputDirs(skill.config, skill.skillDir)) {
|
|
126
|
+
if (isInside(dir, skill.skillDir))
|
|
127
|
+
dirs.add(dir);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return [...dirs];
|
|
131
|
+
}
|
|
70
132
|
/**
|
|
71
133
|
* The test-input dirs VAT actually excludes links for: those inside the project
|
|
72
134
|
* root. A dir outside it yields no exclude rule — the walker already refuses to
|
|
@@ -103,12 +165,6 @@ export function testInputExcludeRules(testInputDirs, projectRoot) {
|
|
|
103
165
|
}
|
|
104
166
|
return patterns.length === 0 ? [] : [{ patterns }];
|
|
105
167
|
}
|
|
106
|
-
/** True when `candidate` is inside (or equal to) `dir`. Both are absolute. */
|
|
107
|
-
function isInside(candidate, dir) {
|
|
108
|
-
const c = safePath.resolve(candidate);
|
|
109
|
-
const d = safePath.resolve(dir);
|
|
110
|
-
return c === d || c.startsWith(`${d}/`);
|
|
111
|
-
}
|
|
112
168
|
/**
|
|
113
169
|
* Split `files:` entries into those that may be packaged and those that point into
|
|
114
170
|
* declared test input and therefore must not be.
|
|
@@ -139,8 +195,8 @@ export function partitionTestInputFileEntries(filesConfig, projectRoot, testInpu
|
|
|
139
195
|
return { kept, dropped };
|
|
140
196
|
}
|
|
141
197
|
/**
|
|
142
|
-
* The `files:` entries that will ACTUALLY be packaged for a skill
|
|
143
|
-
*
|
|
198
|
+
* The `files:` entries that will ACTUALLY be packaged for a skill:
|
|
199
|
+
* {@link resolveTestInputDirs} + the `kept` half of
|
|
144
200
|
* {@link partitionTestInputFileEntries}.
|
|
145
201
|
*
|
|
146
202
|
* Every lane that models "which `dest` paths will exist after the build" must build
|
|
@@ -151,16 +207,20 @@ export function partitionTestInputFileEntries(filesConfig, projectRoot, testInpu
|
|
|
151
207
|
* and fails. Same input, opposite verdicts, in two commands that are supposed to
|
|
152
208
|
* agree. The packager is the authority on what ships; this is how the read-only
|
|
153
209
|
* lanes borrow its answer instead of guessing.
|
|
210
|
+
*
|
|
211
|
+
* `projectSkills` is forwarded to {@link resolveTestInputDirs}, so a `files:` entry
|
|
212
|
+
* pointing at ANOTHER skill's declared suite is dropped here too. Required for the
|
|
213
|
+
* same reason it is required there — see that function's note on the no-op default.
|
|
154
214
|
*/
|
|
155
|
-
export function packagedFileEntries(config, skillDir, projectRoot) {
|
|
156
|
-
return partitionTestInputFileEntries(config.files ?? [], projectRoot, resolveTestInputDirs(config, skillDir)).kept;
|
|
215
|
+
export function packagedFileEntries(config, skillDir, projectRoot, projectSkills) {
|
|
216
|
+
return partitionTestInputFileEntries(config.files ?? [], projectRoot, resolveTestInputDirs(config, skillDir, projectSkills)).kept;
|
|
157
217
|
}
|
|
158
218
|
/** A `PACKAGED_TEST_INPUT` receipt per dropped `files:` entry — a warning, not a failure. */
|
|
159
219
|
export function testInputFileEntryIssues(dropped) {
|
|
160
220
|
return dropped.map((entry) => materializeIssue('PACKAGED_TEST_INPUT', {
|
|
161
221
|
location: toForwardSlash(entry.dest),
|
|
162
222
|
message: `files: entry "${toForwardSlash(entry.source)} -> ${toForwardSlash(entry.dest)}" points into ` +
|
|
163
|
-
`
|
|
223
|
+
`declared test input (some skill's test.evals) and was NOT packaged — test input, ` +
|
|
164
224
|
`including the expected_output answer key, never ships to consumers.`,
|
|
165
225
|
}));
|
|
166
226
|
}
|
|
@@ -200,9 +260,10 @@ export function testInputLinkIssues(excluded, testInputDirs, projectRoot, locati
|
|
|
200
260
|
seen.add(location);
|
|
201
261
|
issues.push(materializeIssue('PACKAGED_TEST_INPUT', {
|
|
202
262
|
location,
|
|
203
|
-
message: `Link "${ref.linkHref ?? location}" points into
|
|
204
|
-
`
|
|
205
|
-
`answer key, never ships to consumers — so the link is
|
|
263
|
+
message: `Link "${ref.linkHref ?? location}" points into declared test input (some skill's ` +
|
|
264
|
+
`test.evals — its own or another skill's). The target is NOT packaged — test input, ` +
|
|
265
|
+
`including the expected_output answer key, never ships to consumers — so the link is ` +
|
|
266
|
+
`removed from the packaged output.`,
|
|
206
267
|
}));
|
|
207
268
|
}
|
|
208
269
|
return issues;
|
package/dist/test-input.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-input.js","sourceRoot":"","sources":["../src/test-input.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"test-input.js","sourceRoot":"","sources":["../src/test-input.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presence-side detection for repo-internal agent-instruction files.
|
|
3
|
+
*
|
|
4
|
+
* `LINK_TO_AGENT_INSTRUCTION_FILE` catches the *reference* — a packaged doc
|
|
5
|
+
* linking a `CLAUDE.md` — and the walker then keeps that file out of the
|
|
6
|
+
* bundle. It cannot catch the other half: a file that arrives in the
|
|
7
|
+
* distributed artifact without any link at all. The plugin lane tree-copies its
|
|
8
|
+
* `source:` directory verbatim, so a `CLAUDE.md` sitting beside `plugin.json`
|
|
9
|
+
* ships to every consumer, and the orphan check does not object because plugin
|
|
10
|
+
* artifacts are exempt from skill reachability rules by design.
|
|
11
|
+
*
|
|
12
|
+
* This detector reads the built tree directly, so it is blind to how the file
|
|
13
|
+
* got there. That is the point: link-following, `files:` globs, and verbatim
|
|
14
|
+
* tree-copies are three routes into a bundle, and only the first is visible to
|
|
15
|
+
* the walker.
|
|
16
|
+
*/
|
|
17
|
+
import { type ValidationIssue } from '@vibe-agent-toolkit/agent-schema';
|
|
18
|
+
/**
|
|
19
|
+
* Report every repo-internal agent-instruction file present in a distributed
|
|
20
|
+
* tree.
|
|
21
|
+
*
|
|
22
|
+
* @param rootDir Absolute path to the tree to scan (a packaged skill output
|
|
23
|
+
* directory, or a plugin directory about to be published).
|
|
24
|
+
* @param locationRoot Anchor base for reported locations. Pass the run's stated
|
|
25
|
+
* root, not `rootDir`, when the two differ — a location the reader cannot
|
|
26
|
+
* open is worse than no location.
|
|
27
|
+
* @param declaredDests Skill-output-relative dests named by EXPLICIT (non-glob)
|
|
28
|
+
* `files:` entries — {@link explicitFilesConfigDests}. Such a dest is NOT
|
|
29
|
+
* reported: naming a file in config is an unambiguous instruction to ship it,
|
|
30
|
+
* and this finding's remediation is "remove the file", i.e. undo what the
|
|
31
|
+
* config sanctioned. Callers that cannot know the config (an installed
|
|
32
|
+
* third-party bundle, a plugin tree with no `files:` block) pass `[]` — the
|
|
33
|
+
* honest answer, since intent is genuinely unknowable there.
|
|
34
|
+
*
|
|
35
|
+
* REQUIRED, not defaulted: every caller must state its answer, or a lane that
|
|
36
|
+
* silently inherits `[]` re-opens the contradiction for the population it
|
|
37
|
+
* governs. Matching is EXACT membership, never a prefix test — a directory-ish
|
|
38
|
+
* dest must not launder its whole subtree (see {@link explicitFilesConfigDests}).
|
|
39
|
+
*/
|
|
40
|
+
export declare function detectPackagedAgentInstructionFiles(rootDir: string, locationRoot: string, declaredDests: readonly string[]): ValidationIssue[];
|
|
41
|
+
//# sourceMappingURL=agent-instruction-presence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-instruction-presence.d.ts","sourceRoot":"","sources":["../../src/validators/agent-instruction-presence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAcxE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,eAAe,EAAE,CA8BnB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presence-side detection for repo-internal agent-instruction files.
|
|
3
|
+
*
|
|
4
|
+
* `LINK_TO_AGENT_INSTRUCTION_FILE` catches the *reference* — a packaged doc
|
|
5
|
+
* linking a `CLAUDE.md` — and the walker then keeps that file out of the
|
|
6
|
+
* bundle. It cannot catch the other half: a file that arrives in the
|
|
7
|
+
* distributed artifact without any link at all. The plugin lane tree-copies its
|
|
8
|
+
* `source:` directory verbatim, so a `CLAUDE.md` sitting beside `plugin.json`
|
|
9
|
+
* ships to every consumer, and the orphan check does not object because plugin
|
|
10
|
+
* artifacts are exempt from skill reachability rules by design.
|
|
11
|
+
*
|
|
12
|
+
* This detector reads the built tree directly, so it is blind to how the file
|
|
13
|
+
* got there. That is the point: link-following, `files:` globs, and verbatim
|
|
14
|
+
* tree-copies are three routes into a bundle, and only the first is visible to
|
|
15
|
+
* the walker.
|
|
16
|
+
*/
|
|
17
|
+
import { existsSync } from 'node:fs';
|
|
18
|
+
import { crawlDirectorySync, issueLocation, safePath } from '@vibe-agent-toolkit/utils';
|
|
19
|
+
import { normalizeRelPath } from '../files-config.js';
|
|
20
|
+
import { materializeIssue } from './rule-engine/index.js';
|
|
21
|
+
import { AGENT_INSTRUCTION_FILE_PATTERNS, toAnyDepthGlobs } from './validation-rules.js';
|
|
22
|
+
/**
|
|
23
|
+
* Match each basename at the tree root AND at any depth — the root is the case
|
|
24
|
+
* that matters most (a `CLAUDE.md` beside `plugin.json` is the observed defect).
|
|
25
|
+
*/
|
|
26
|
+
const INCLUDE_GLOBS = toAnyDepthGlobs(AGENT_INSTRUCTION_FILE_PATTERNS);
|
|
27
|
+
/**
|
|
28
|
+
* Report every repo-internal agent-instruction file present in a distributed
|
|
29
|
+
* tree.
|
|
30
|
+
*
|
|
31
|
+
* @param rootDir Absolute path to the tree to scan (a packaged skill output
|
|
32
|
+
* directory, or a plugin directory about to be published).
|
|
33
|
+
* @param locationRoot Anchor base for reported locations. Pass the run's stated
|
|
34
|
+
* root, not `rootDir`, when the two differ — a location the reader cannot
|
|
35
|
+
* open is worse than no location.
|
|
36
|
+
* @param declaredDests Skill-output-relative dests named by EXPLICIT (non-glob)
|
|
37
|
+
* `files:` entries — {@link explicitFilesConfigDests}. Such a dest is NOT
|
|
38
|
+
* reported: naming a file in config is an unambiguous instruction to ship it,
|
|
39
|
+
* and this finding's remediation is "remove the file", i.e. undo what the
|
|
40
|
+
* config sanctioned. Callers that cannot know the config (an installed
|
|
41
|
+
* third-party bundle, a plugin tree with no `files:` block) pass `[]` — the
|
|
42
|
+
* honest answer, since intent is genuinely unknowable there.
|
|
43
|
+
*
|
|
44
|
+
* REQUIRED, not defaulted: every caller must state its answer, or a lane that
|
|
45
|
+
* silently inherits `[]` re-opens the contradiction for the population it
|
|
46
|
+
* governs. Matching is EXACT membership, never a prefix test — a directory-ish
|
|
47
|
+
* dest must not launder its whole subtree (see {@link explicitFilesConfigDests}).
|
|
48
|
+
*/
|
|
49
|
+
export function detectPackagedAgentInstructionFiles(rootDir, locationRoot, declaredDests) {
|
|
50
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- rootDir is a validated build-output path
|
|
51
|
+
if (!existsSync(rootDir))
|
|
52
|
+
return [];
|
|
53
|
+
const declared = new Set(declaredDests.map((d) => normalizeRelPath(d)));
|
|
54
|
+
// `respectGitignore: false` and an empty `exclude` are both load-bearing: the
|
|
55
|
+
// subject is BUILT output, which normally lives under a gitignored `dist/`
|
|
56
|
+
// that the crawler's defaults would skip entirely — the scan would pass by
|
|
57
|
+
// scanning nothing.
|
|
58
|
+
const files = crawlDirectorySync({
|
|
59
|
+
baseDir: rootDir,
|
|
60
|
+
include: INCLUDE_GLOBS,
|
|
61
|
+
exclude: [],
|
|
62
|
+
absolute: true,
|
|
63
|
+
filesOnly: true,
|
|
64
|
+
respectGitignore: false,
|
|
65
|
+
});
|
|
66
|
+
const issues = [];
|
|
67
|
+
for (const file of files) {
|
|
68
|
+
// The declaration is expressed relative to the SCANNED tree (a `files:` dest is
|
|
69
|
+
// skill-output-relative), while the reported location is relative to
|
|
70
|
+
// `locationRoot` — the two roots differ whenever a batching caller anchors
|
|
71
|
+
// elsewhere, so the exemption test must use rootDir and never the location.
|
|
72
|
+
if (declared.has(normalizeRelPath(safePath.relative(rootDir, file))))
|
|
73
|
+
continue;
|
|
74
|
+
const location = issueLocation(file, locationRoot);
|
|
75
|
+
issues.push(materializeIssue('PACKAGED_AGENT_INSTRUCTION_FILE', { location, detail: location }));
|
|
76
|
+
}
|
|
77
|
+
return issues;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=agent-instruction-presence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-instruction-presence.js","sourceRoot":"","sources":["../../src/validators/agent-instruction-presence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGrC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAExF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,+BAA+B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEzF;;;GAGG;AACH,MAAM,aAAa,GAAG,eAAe,CAAC,+BAA+B,CAAC,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,mCAAmC,CACjD,OAAe,EACf,YAAoB,EACpB,aAAgC;IAEhC,+GAA+G;IAC/G,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAExE,8EAA8E;IAC9E,2EAA2E;IAC3E,2EAA2E;IAC3E,oBAAoB;IACpB,MAAM,KAAK,GAAG,kBAAkB,CAAC;QAC/B,OAAO,EAAE,OAAO;QAChB,OAAO,EAAE,aAAa;QACtB,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,IAAI;QACd,SAAS,EAAE,IAAI;QACf,gBAAgB,EAAE,KAAK;KACxB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,gFAAgF;QAChF,qEAAqE;QACrE,2EAA2E;QAC3E,4EAA4E;QAC5E,IAAI,QAAQ,CAAC,GAAG,CAAC,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;YAAE,SAAS;QAC/E,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QACnD,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,iCAAiC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;IACnG,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -2,13 +2,30 @@
|
|
|
2
2
|
* Bundled-resource link detector.
|
|
3
3
|
*
|
|
4
4
|
* Emits SKILL_REFERENCES_BUT_NO_LINKS at info severity when a skill
|
|
5
|
-
* directory contains
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* directory contains a bundled subdirectory (the content-type routing
|
|
6
|
+
* categories plus the claude-web `references/` layout) holding files that
|
|
7
|
+
* neither SKILL.md's body nor any transitively linked file points at.
|
|
8
8
|
*
|
|
9
9
|
* Plugin-dev's "Mistake 4: Missing Resource References" — bundled assets
|
|
10
10
|
* the body never links to are dead weight in the install. The author
|
|
11
11
|
* intended progressive disclosure but forgot to wire up the references.
|
|
12
|
+
*
|
|
13
|
+
* Two properties are load-bearing:
|
|
14
|
+
*
|
|
15
|
+
* 1. **The vocabulary is derived, never re-listed.** A hardcoded list drifts
|
|
16
|
+
* from `content-type-routing.ts` — which routes `.md` and every unknown
|
|
17
|
+
* extension to `resources/` — and a subdirectory missing from the list
|
|
18
|
+
* makes the detector structurally unable to fire on it.
|
|
19
|
+
*
|
|
20
|
+
* 2. **Coverage is per file, not per directory.** A single live mention must
|
|
21
|
+
* not mask dead siblings: the real `command-development` skill ships 7
|
|
22
|
+
* files under `references/` and mentions only 2 of them, and a
|
|
23
|
+
* directory-granular check reported nothing at all.
|
|
24
|
+
*
|
|
25
|
+
* A file counts as referenced when its skill-relative path appears anywhere
|
|
26
|
+
* in the body — markdown link target, inline code span, or bare prose alike —
|
|
27
|
+
* or when link traversal reached it. Fenced code blocks are deliberately NOT
|
|
28
|
+
* parsed out: that approach was tried and rejected as too fragile.
|
|
12
29
|
*/
|
|
13
30
|
import { type ValidationIssue } from '@vibe-agent-toolkit/agent-schema';
|
|
14
31
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundled-resource-link-detection.d.ts","sourceRoot":"","sources":["../../src/validators/bundled-resource-link-detection.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"bundled-resource-link-detection.d.ts","sourceRoot":"","sources":["../../src/validators/bundled-resource-link-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAOH,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,kCAAkC,CAAC;AA4EvF;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC/C,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,YAAY,EAAE,MAAM,GACnB,eAAe,EAAE,CAgCnB"}
|
|
@@ -2,20 +2,50 @@
|
|
|
2
2
|
* Bundled-resource link detector.
|
|
3
3
|
*
|
|
4
4
|
* Emits SKILL_REFERENCES_BUT_NO_LINKS at info severity when a skill
|
|
5
|
-
* directory contains
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* directory contains a bundled subdirectory (the content-type routing
|
|
6
|
+
* categories plus the claude-web `references/` layout) holding files that
|
|
7
|
+
* neither SKILL.md's body nor any transitively linked file points at.
|
|
8
8
|
*
|
|
9
9
|
* Plugin-dev's "Mistake 4: Missing Resource References" — bundled assets
|
|
10
10
|
* the body never links to are dead weight in the install. The author
|
|
11
11
|
* intended progressive disclosure but forgot to wire up the references.
|
|
12
|
+
*
|
|
13
|
+
* Two properties are load-bearing:
|
|
14
|
+
*
|
|
15
|
+
* 1. **The vocabulary is derived, never re-listed.** A hardcoded list drifts
|
|
16
|
+
* from `content-type-routing.ts` — which routes `.md` and every unknown
|
|
17
|
+
* extension to `resources/` — and a subdirectory missing from the list
|
|
18
|
+
* makes the detector structurally unable to fire on it.
|
|
19
|
+
*
|
|
20
|
+
* 2. **Coverage is per file, not per directory.** A single live mention must
|
|
21
|
+
* not mask dead siblings: the real `command-development` skill ships 7
|
|
22
|
+
* files under `references/` and mentions only 2 of them, and a
|
|
23
|
+
* directory-granular check reported nothing at all.
|
|
24
|
+
*
|
|
25
|
+
* A file counts as referenced when its skill-relative path appears anywhere
|
|
26
|
+
* in the body — markdown link target, inline code span, or bare prose alike —
|
|
27
|
+
* or when link traversal reached it. Fenced code blocks are deliberately NOT
|
|
28
|
+
* parsed out: that approach was tried and rejected as too fragile.
|
|
12
29
|
*/
|
|
13
30
|
/* eslint-disable security/detect-non-literal-fs-filename -- skill paths validated upstream */
|
|
14
31
|
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
15
32
|
import { dirname } from 'node:path';
|
|
16
33
|
import { CODE_REGISTRY } from '@vibe-agent-toolkit/agent-schema';
|
|
17
34
|
import { issueLocation, safePath } from '@vibe-agent-toolkit/utils';
|
|
18
|
-
|
|
35
|
+
import { CLAUDE_WEB_REFERENCES_SUBDIR, TARGET_SUBDIR_CATEGORIES } from '../content-type-routing.js';
|
|
36
|
+
/**
|
|
37
|
+
* Subdirectories a packaged skill may bundle resources into.
|
|
38
|
+
*
|
|
39
|
+
* Derived from the content-type routing categories (the single source of
|
|
40
|
+
* truth for extension→subdirectory mapping) plus the claude-web-specific
|
|
41
|
+
* `references/` layout name. De-duplicated so promoting `references` to a
|
|
42
|
+
* routing category later cannot produce a doubled finding.
|
|
43
|
+
*/
|
|
44
|
+
const BUNDLED_SUBDIRS = [
|
|
45
|
+
...new Set([...TARGET_SUBDIR_CATEGORIES, CLAUDE_WEB_REFERENCES_SUBDIR]),
|
|
46
|
+
];
|
|
47
|
+
/** How many dead file names the message spells out before summarising. */
|
|
48
|
+
const MAX_LISTED_FILES = 5;
|
|
19
49
|
/** Read SKILL.md content (best-effort) — empty string if unreadable. */
|
|
20
50
|
function readSkillContent(skillPath) {
|
|
21
51
|
try {
|
|
@@ -25,40 +55,51 @@ function readSkillContent(skillPath) {
|
|
|
25
55
|
return '';
|
|
26
56
|
}
|
|
27
57
|
}
|
|
28
|
-
/**
|
|
29
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Every file under `dir`, recursively, as paths relative to `baseDir`
|
|
60
|
+
* (forward-slash normalised by safePath). Empty when `dir` is absent,
|
|
61
|
+
* unreadable, or contains no files.
|
|
62
|
+
*/
|
|
63
|
+
function listFilesRelative(dir, baseDir) {
|
|
30
64
|
if (!existsSync(dir))
|
|
31
|
-
return
|
|
65
|
+
return [];
|
|
66
|
+
const found = [];
|
|
32
67
|
try {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
if (entry.isFile())
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
68
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
69
|
+
const child = safePath.join(dir, entry.name);
|
|
70
|
+
if (entry.isFile()) {
|
|
71
|
+
found.push(safePath.relative(baseDir, child));
|
|
72
|
+
}
|
|
73
|
+
else if (entry.isDirectory()) {
|
|
74
|
+
found.push(...listFilesRelative(child, baseDir));
|
|
39
75
|
}
|
|
40
76
|
}
|
|
41
77
|
}
|
|
42
78
|
catch {
|
|
43
|
-
return
|
|
79
|
+
return [];
|
|
44
80
|
}
|
|
45
|
-
return
|
|
81
|
+
return found;
|
|
46
82
|
}
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Body points at this file.
|
|
85
|
+
*
|
|
86
|
+
* Substring match on the skill-relative path, so `[x](references/a.md)`,
|
|
87
|
+
* `(./references/a.md)`, `` `references/a.md` `` and a bare prose
|
|
88
|
+
* `references/a.md` all count. Deliberately liberal: at info severity a
|
|
89
|
+
* missed finding beats a wrong one.
|
|
90
|
+
*/
|
|
91
|
+
function bodyMentionsFile(body, relPath) {
|
|
92
|
+
return body.includes(relPath);
|
|
93
|
+
}
|
|
94
|
+
/** Link traversal reached this exact file. */
|
|
95
|
+
function linkedFilesCoverFile(linkedFiles, absPath) {
|
|
96
|
+
return linkedFiles.has(safePath.resolve(absPath));
|
|
54
97
|
}
|
|
55
|
-
/**
|
|
56
|
-
function
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return !rel.startsWith('..') && rel.length > 0;
|
|
61
|
-
});
|
|
98
|
+
/** Human-readable file list, truncated so the message stays scannable. */
|
|
99
|
+
function summariseFiles(relPaths) {
|
|
100
|
+
const shown = relPaths.slice(0, MAX_LISTED_FILES).join(', ');
|
|
101
|
+
const hidden = relPaths.length - MAX_LISTED_FILES;
|
|
102
|
+
return hidden > 0 ? `${shown} (and ${hidden} more)` : shown;
|
|
62
103
|
}
|
|
63
104
|
/**
|
|
64
105
|
* @param skillPath Absolute path to SKILL.md
|
|
@@ -70,19 +111,22 @@ export function detectBundledResourceWithoutLinks(skillPath, skillDir, linkedFil
|
|
|
70
111
|
const issues = [];
|
|
71
112
|
const body = readSkillContent(skillPath);
|
|
72
113
|
const baseDir = skillDir.length > 0 ? skillDir : dirname(skillPath);
|
|
114
|
+
const linked = new Set(linkedFiles.map((f) => safePath.resolve(f)));
|
|
73
115
|
for (const sub of BUNDLED_SUBDIRS) {
|
|
74
116
|
const subPath = safePath.join(baseDir, sub);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (bodyMentionsSubdir(body, sub))
|
|
117
|
+
const bundled = listFilesRelative(subPath, baseDir);
|
|
118
|
+
if (bundled.length === 0)
|
|
78
119
|
continue;
|
|
79
|
-
|
|
120
|
+
const unreferenced = bundled.filter((rel) => !bodyMentionsFile(body, rel) && !linkedFilesCoverFile(linked, safePath.join(baseDir, rel)));
|
|
121
|
+
if (unreferenced.length === 0)
|
|
80
122
|
continue;
|
|
81
123
|
const entry = CODE_REGISTRY.SKILL_REFERENCES_BUT_NO_LINKS;
|
|
82
124
|
issues.push({
|
|
83
125
|
severity: entry.defaultSeverity,
|
|
84
126
|
code: 'SKILL_REFERENCES_BUT_NO_LINKS',
|
|
85
|
-
message: `Skill directory contains "${sub}/"
|
|
127
|
+
message: `Skill directory contains "${sub}/" with ${unreferenced.length} of ${bundled.length} ` +
|
|
128
|
+
`file(s) that nothing in SKILL.md or its linked files points at: ` +
|
|
129
|
+
`${summariseFiles(unreferenced)}.`,
|
|
86
130
|
location: issueLocation(subPath, locationRoot),
|
|
87
131
|
fix: entry.fix,
|
|
88
132
|
reference: entry.reference,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundled-resource-link-detection.js","sourceRoot":"","sources":["../../src/validators/bundled-resource-link-detection.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"bundled-resource-link-detection.js","sourceRoot":"","sources":["../../src/validators/bundled-resource-link-detection.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,14 +15,19 @@ export interface DetectorOutput {
|
|
|
15
15
|
evidence: EvidenceRecord[];
|
|
16
16
|
observations: Observation[];
|
|
17
17
|
}
|
|
18
|
-
export declare function collectLocalShellEvidence(content: string, filePath: string): EvidenceRecord[];
|
|
19
|
-
export declare function collectExternalCLIEvidence(content: string, filePath: string): EvidenceRecord[];
|
|
20
|
-
export declare function collectBrowserAuthEvidence(content: string, filePath: string): EvidenceRecord[];
|
|
18
|
+
export declare function collectLocalShellEvidence(content: string, filePath: string, locationRoot: string): EvidenceRecord[];
|
|
19
|
+
export declare function collectExternalCLIEvidence(content: string, filePath: string, locationRoot: string): EvidenceRecord[];
|
|
20
|
+
export declare function collectBrowserAuthEvidence(content: string, filePath: string, locationRoot: string): EvidenceRecord[];
|
|
21
21
|
export declare function deriveObservations(evidence: readonly EvidenceRecord[]): Observation[];
|
|
22
22
|
/**
|
|
23
23
|
* Convert a capability Observation into a ValidationIssue using the
|
|
24
24
|
* default severity, fix, and reference from CODE_REGISTRY.
|
|
25
25
|
*/
|
|
26
26
|
export declare function observationToIssue(obs: Observation, location: string): ValidationIssue;
|
|
27
|
-
|
|
27
|
+
/**
|
|
28
|
+
* @param locationRoot - The ONE base every emitted evidence `location.file` is
|
|
29
|
+
* expressed relative to. Required, so a producer cannot emit an absolute
|
|
30
|
+
* location by omission.
|
|
31
|
+
*/
|
|
32
|
+
export declare function runCompatDetectors(content: string, filePath: string, locationRoot: string): DetectorOutput;
|
|
28
33
|
//# sourceMappingURL=compat-detectors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compat-detectors.d.ts","sourceRoot":"","sources":["../../src/validators/compat-detectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAiC,KAAK,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAEvG,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"compat-detectors.d.ts","sourceRoot":"","sources":["../../src/validators/compat-detectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAiC,KAAK,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAEvG,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA2BxE,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AA6CD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,cAAc,EAAE,CAkDlB;AASD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,cAAc,EAAE,CA6BlB;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,cAAc,EAAE,CAgBlB;AAQD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAAG,WAAW,EAAE,CAKrF;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,CAWtF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,cAAc,CAQhB"}
|