@skill-graph/cli 0.5.7 → 0.5.8
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/CHANGELOG.md +27 -3
- package/README.md +40 -14
- package/SKILL_GRAPH.md +2 -2
- package/bin/skill-graph.js +118 -2
- package/docs/ADOPTION.md +1 -1
- package/docs/PRIMER.md +6 -5
- package/docs/QUICKSTART-30MIN.md +2 -2
- package/docs/SKILL_AUDIT_CHECKLIST.md +1 -1
- package/docs/SKILL_METADATA_PROTOCOL.md +2 -2
- package/docs/_archived/marketplace-publication-priority-2026-05-18.md +1 -1
- package/docs/_drafts/0.5.8-release-prep.md +164 -0
- package/docs/field-reference.generated.md +1 -1
- package/docs/field-reference.md +2 -2
- package/docs/manifest-field-mapping.md +3 -3
- package/docs/marketplace-publication-queue.generated.md +2 -2
- package/docs/plans/scripts-roadmap.md +2 -2
- package/docs/positioning.md +88 -0
- package/docs/research/skill-comprehension-eval-research.md +5 -5
- package/docs/research/skill-demand-gap-roadmap-2026-05-16.md +215 -0
- package/docs/status.generated.md +48 -0
- package/examples/audits/context-graph/findings.md +59 -0
- package/examples/audits/context-graph/scorecard.md +22 -0
- package/examples/audits/context-graph/verdict.md +33 -0
- package/examples/evals/a11y.json +45 -13
- package/examples/evals/api-design.json +18 -5
- package/examples/evals/code-review.json +18 -5
- package/examples/evals/data-modeling.json +18 -5
- package/examples/evals/database-migration.json +18 -5
- package/examples/evals/debugging.json +37 -11
- package/examples/evals/dependency-architecture.json +18 -5
- package/examples/evals/design-system-architecture.json +18 -5
- package/examples/evals/error-tracking.json +18 -5
- package/examples/evals/event-contract-design.json +18 -5
- package/examples/evals/form-ux-architecture.json +18 -5
- package/examples/evals/framework-fit-analysis.json +18 -5
- package/examples/evals/graph-audit.json +55 -13
- package/examples/evals/information-architecture.json +18 -5
- package/examples/evals/interaction-feedback.json +18 -5
- package/examples/evals/interaction-patterns.json +18 -5
- package/examples/evals/layout-composition.json +18 -5
- package/examples/evals/lint-overlay.json +38 -11
- package/examples/evals/microcopy.json +18 -5
- package/examples/evals/observability-modeling.json +18 -5
- package/examples/evals/pattern-recognition.json +32 -9
- package/examples/evals/performance-engineering.json +18 -5
- package/examples/evals/refactor.json +41 -12
- package/examples/evals/semiotics.json +18 -5
- package/examples/evals/skill-infrastructure.json +32 -9
- package/examples/evals/skill-router.json +42 -13
- package/examples/evals/system-interface-contracts.json +18 -5
- package/examples/evals/task-analysis.json +18 -5
- package/examples/evals/testing-strategy.json +36 -11
- package/examples/evals/type-safety.json +251 -66
- package/examples/evals/visual-design-foundations.json +18 -5
- package/examples/evals/webhook-integration.json +18 -5
- package/examples/fixture-skills/README.md +47 -0
- package/examples/fixture-skills/comprehension-full/SKILL.md +79 -0
- package/examples/fixture-skills/minimal-capability/SKILL.md +51 -0
- package/examples/fixture-skills/with-grounding/SKILL.md +78 -0
- package/examples/fixture-skills/with-relations/SKILL.md +87 -0
- package/examples/skills.manifest.sample.json +1722 -446
- package/marketplace/README.md +1 -1
- package/marketplace/skills/a11y/SKILL.md +1 -1
- package/marketplace/skills/best-practice/SKILL.md +211 -0
- package/marketplace/skills/context-graph/SKILL.md +1 -1
- package/marketplace/skills/debugging/SKILL.md +1 -1
- package/marketplace/skills/graph-audit/SKILL.md +3 -1
- package/marketplace/skills/postgres-rls/SKILL.md +284 -0
- package/marketplace/skills/refactor/SKILL.md +1 -1
- package/marketplace/skills/skill-infrastructure/SKILL.md +2 -0
- package/marketplace/skills/skill-router/SKILL.md +3 -1
- package/marketplace/skills/testing-strategy/SKILL.md +1 -1
- package/package.json +3 -1
- package/scripts/__tests__/test-marketplace-export.js +6 -2
- package/scripts/__tests__/test-v3-1-alias-contract.js +3 -3
- package/scripts/build-status-doc.js +177 -0
- package/scripts/check-doc-drift.js +224 -0
- package/scripts/check-markdown-links.js +34 -4
- package/scripts/check-mirror-freeze.js +270 -0
- package/scripts/export-marketplace-skills.js +35 -6
- package/scripts/lib/audit-prompt-builder.js +3 -3
- package/scripts/lib/parse-frontmatter.js +2 -2
- package/scripts/skill-audit.js +7 -9
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Mirror-freeze linter.
|
|
4
|
+
*
|
|
5
|
+
* Scans the two docs-only deprecation mirror repos (`skill-metadata-protocol`,
|
|
6
|
+
* `skill-audit-loop`) for content that contradicts their post-ADR-0009
|
|
7
|
+
* docs-only status: active-package version claims, references to source
|
|
8
|
+
* directories that no longer exist there (src/, evals/, lib/schemas/), or
|
|
9
|
+
* live-contribution language that would mislead users into filing issues or
|
|
10
|
+
* PRs against an inert repo.
|
|
11
|
+
*
|
|
12
|
+
* The script is read-only. It exits 1 when active claims are found in the
|
|
13
|
+
* mirrors, 0 when the mirrors are clean.
|
|
14
|
+
*
|
|
15
|
+
* Mirrors are resolved as sibling clones of this repo by default:
|
|
16
|
+
* ../skill-metadata-protocol
|
|
17
|
+
* ../skill-audit-loop
|
|
18
|
+
*
|
|
19
|
+
* Override with SKILL_GRAPH_MIRRORS="path1,path2" or --mirror=path flags.
|
|
20
|
+
*
|
|
21
|
+
* Allowlist (lines / files are not reported):
|
|
22
|
+
* - `CHANGELOG.md` at any depth — release notes legitimately cite the
|
|
23
|
+
* packages and source paths that shipped in past releases.
|
|
24
|
+
* - Any line inside a fenced code block.
|
|
25
|
+
* - Any heading line (#, ##, ...) — these often quote retired names.
|
|
26
|
+
* - Any line containing a historical-framing token anywhere in the line:
|
|
27
|
+
* retired, deprecated, historical, was, previously, formerly, legacy,
|
|
28
|
+
* snapshot, archived, "this mirror", "docs-only", "preserved for", etc.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
'use strict';
|
|
32
|
+
|
|
33
|
+
const fs = require('fs');
|
|
34
|
+
const path = require('path');
|
|
35
|
+
const { workspaceRoot } = require('./lib/roots');
|
|
36
|
+
|
|
37
|
+
const REPO_ROOT = workspaceRoot();
|
|
38
|
+
const PARENT = path.dirname(REPO_ROOT);
|
|
39
|
+
|
|
40
|
+
const DEFAULT_MIRRORS = [
|
|
41
|
+
path.join(PARENT, 'skill-metadata-protocol'),
|
|
42
|
+
path.join(PARENT, 'skill-audit-loop'),
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const IGNORED_DIRS = new Set(['.git', 'node_modules', '.artifacts', '.roundtable', 'docs/images']);
|
|
46
|
+
|
|
47
|
+
// Active-claim patterns — match references that imply this mirror is an active
|
|
48
|
+
// implementation/source surface.
|
|
49
|
+
const ACTIVE_CLAIM_PATTERNS = [
|
|
50
|
+
{
|
|
51
|
+
id: 'pkg-protocol',
|
|
52
|
+
regex: /@skill-graph\/protocol/g,
|
|
53
|
+
description: '`@skill-graph/protocol` package mention (retired post-ADR-0009)',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'pkg-audit',
|
|
57
|
+
regex: /@skill-graph\/audit\b/g,
|
|
58
|
+
description: '`@skill-graph/audit` package mention (retired post-ADR-0009)',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'src-dir',
|
|
62
|
+
regex: /(?:^|\s|`)src\/[A-Za-z0-9_./-]+\.js\b/g,
|
|
63
|
+
description: 'reference to src/*.js (mirrors have no src/)',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'lib-schemas',
|
|
67
|
+
regex: /lib\/schemas\//g,
|
|
68
|
+
description: 'reference to lib/schemas/ (canonical path is schemas/)',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'local-evals',
|
|
72
|
+
regex: /(?:^|\s|`)evals\/[A-Za-z0-9_./-]+/g,
|
|
73
|
+
description: 'reference to local evals/ (eval fixtures live in skill-graph)',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'local-schemas',
|
|
77
|
+
regex: /(?:^|\s|`)schemas\/skill\.v\d+\.schema\.json/g,
|
|
78
|
+
description: 'reference to local schemas/skill.v*.schema.json (mirrors have no schemas/)',
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
// Historical-framing tokens. When any of these appear earlier in the same line
|
|
83
|
+
// (or the immediately preceding sentence-ish window), we treat the active-claim
|
|
84
|
+
// match as historical context and skip it.
|
|
85
|
+
const HISTORICAL_TOKENS = [
|
|
86
|
+
'retired',
|
|
87
|
+
'deprecated',
|
|
88
|
+
'historical',
|
|
89
|
+
'historically',
|
|
90
|
+
'previously',
|
|
91
|
+
'formerly',
|
|
92
|
+
'legacy',
|
|
93
|
+
'snapshot',
|
|
94
|
+
'archived',
|
|
95
|
+
'no longer',
|
|
96
|
+
'has moved',
|
|
97
|
+
'have moved',
|
|
98
|
+
'moved to',
|
|
99
|
+
'docs-only',
|
|
100
|
+
'docs only',
|
|
101
|
+
'was the',
|
|
102
|
+
'were the',
|
|
103
|
+
'used to',
|
|
104
|
+
'preserved for',
|
|
105
|
+
'frozen',
|
|
106
|
+
'mirror only',
|
|
107
|
+
'this mirror',
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
function hasHistoricalFraming(line) {
|
|
111
|
+
const lower = line.toLowerCase();
|
|
112
|
+
return HISTORICAL_TOKENS.some(tok => lower.includes(tok));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function collectFiles(dir, out = []) {
|
|
116
|
+
if (!fs.existsSync(dir)) return out;
|
|
117
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
118
|
+
const abs = path.join(dir, entry.name);
|
|
119
|
+
if (entry.isDirectory()) {
|
|
120
|
+
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
121
|
+
// Skip nested ignored paths like docs/images
|
|
122
|
+
const rel = path.relative(dir, abs);
|
|
123
|
+
if (IGNORED_DIRS.has(rel)) continue;
|
|
124
|
+
collectFiles(abs, out);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (!entry.isFile()) continue;
|
|
128
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
129
|
+
if (['.md', '.yml', '.yaml', '.json'].includes(ext)) out.push(abs);
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// File-level banner detection: a file that opens with an explicit historical-
|
|
135
|
+
// snapshot or frozen-mirror banner in its first ~12 lines is treated as a
|
|
136
|
+
// preserved artifact and skipped entirely. Detected by combining "historical"
|
|
137
|
+
// or "frozen" or "snapshot" or "deprecation mirror" with explicit dating or
|
|
138
|
+
// the word "snapshot"/"frozen" in a blockquote.
|
|
139
|
+
const FILE_BANNER_HINTS = [
|
|
140
|
+
/historical\s+(?:v\d+\s+)?snapshot/i,
|
|
141
|
+
/frozen\s+snapshot/i,
|
|
142
|
+
/deprecation\s+mirror/i,
|
|
143
|
+
/docs-only\s+(?:deprecation\s+)?mirror/i,
|
|
144
|
+
/this\s+(?:repository|repo|file)\s+is\s+a?\s*(?:docs-only|deprecation|historical|frozen|deprecated)/i,
|
|
145
|
+
/(?:do\s+not\s+execute|preserved\s+for\s+(?:the\s+)?historical\s+record)/i,
|
|
146
|
+
/superseded\s+by/i,
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
function hasFileBanner(text) {
|
|
150
|
+
const head = text.split(/\r?\n/).slice(0, 12).join('\n');
|
|
151
|
+
return FILE_BANNER_HINTS.some(re => re.test(head));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function scanFile(absPath, mirrorRoot) {
|
|
155
|
+
// File-level allowlist: CHANGELOG.md release notes legitimately cite
|
|
156
|
+
// packages and source paths from past releases.
|
|
157
|
+
if (path.basename(absPath) === 'CHANGELOG.md') return [];
|
|
158
|
+
|
|
159
|
+
const text = fs.readFileSync(absPath, 'utf8');
|
|
160
|
+
if (hasFileBanner(text)) return [];
|
|
161
|
+
const lines = text.split(/\r?\n/);
|
|
162
|
+
let inFence = false;
|
|
163
|
+
const hits = [];
|
|
164
|
+
for (let i = 0; i < lines.length; i++) {
|
|
165
|
+
const line = lines[i];
|
|
166
|
+
if (/^\s*```/.test(line)) { inFence = !inFence; continue; }
|
|
167
|
+
if (inFence) continue;
|
|
168
|
+
if (/^\s*#{1,6}\s/.test(line)) continue; // skip headings
|
|
169
|
+
if (hasHistoricalFraming(line)) continue; // skip historically-framed lines
|
|
170
|
+
for (const p of ACTIVE_CLAIM_PATTERNS) {
|
|
171
|
+
// Reset regex state for each line scan.
|
|
172
|
+
p.regex.lastIndex = 0;
|
|
173
|
+
let m;
|
|
174
|
+
while ((m = p.regex.exec(line)) !== null) {
|
|
175
|
+
hits.push({
|
|
176
|
+
file: path.relative(mirrorRoot, absPath),
|
|
177
|
+
mirror: path.basename(mirrorRoot),
|
|
178
|
+
line: i + 1,
|
|
179
|
+
column: m.index + 1,
|
|
180
|
+
pattern: p.id,
|
|
181
|
+
description: p.description,
|
|
182
|
+
match: m[0],
|
|
183
|
+
snippet: line.trim().slice(0, 240),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return hits;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function resolveMirrors(argv) {
|
|
192
|
+
const flagMirrors = argv
|
|
193
|
+
.filter(a => a.startsWith('--mirror='))
|
|
194
|
+
.map(a => path.resolve(a.slice('--mirror='.length)));
|
|
195
|
+
if (flagMirrors.length > 0) return flagMirrors;
|
|
196
|
+
if (process.env.SKILL_GRAPH_MIRRORS) {
|
|
197
|
+
return process.env.SKILL_GRAPH_MIRRORS
|
|
198
|
+
.split(',')
|
|
199
|
+
.map(p => p.trim())
|
|
200
|
+
.filter(Boolean)
|
|
201
|
+
.map(p => path.resolve(p));
|
|
202
|
+
}
|
|
203
|
+
return DEFAULT_MIRRORS;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function main() {
|
|
207
|
+
const argv = process.argv.slice(2);
|
|
208
|
+
const json = argv.includes('--json');
|
|
209
|
+
const quiet = argv.includes('--quiet');
|
|
210
|
+
const mirrors = resolveMirrors(argv);
|
|
211
|
+
|
|
212
|
+
const allHits = [];
|
|
213
|
+
const missingMirrors = [];
|
|
214
|
+
let filesScanned = 0;
|
|
215
|
+
for (const mirror of mirrors) {
|
|
216
|
+
if (!fs.existsSync(mirror)) {
|
|
217
|
+
missingMirrors.push(mirror);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const files = collectFiles(mirror);
|
|
221
|
+
filesScanned += files.length;
|
|
222
|
+
for (const f of files) allHits.push(...scanFile(f, mirror));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (json) {
|
|
226
|
+
process.stdout.write(JSON.stringify({
|
|
227
|
+
mirrors,
|
|
228
|
+
missing_mirrors: missingMirrors,
|
|
229
|
+
files_scanned: filesScanned,
|
|
230
|
+
hits: allHits,
|
|
231
|
+
}, null, 2) + '\n');
|
|
232
|
+
process.exit(allHits.length > 0 ? 1 : 0);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!quiet && missingMirrors.length > 0) {
|
|
236
|
+
for (const m of missingMirrors) {
|
|
237
|
+
process.stderr.write(`WARN mirror not found: ${m}\n`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (!quiet) {
|
|
241
|
+
for (const h of allHits) {
|
|
242
|
+
process.stderr.write(
|
|
243
|
+
`${h.mirror}/${h.file}:${h.line}:${h.column} [${h.pattern}] ${h.match} — ${h.description}\n ${h.snippet}\n`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (allHits.length > 0) {
|
|
249
|
+
process.stderr.write(
|
|
250
|
+
`FAIL mirror freeze: ${allHits.length} active claim(s) across ${mirrors.length} mirror(s). Mirrors should not advertise active source / packages — add historical framing or move content to skill-graph.\n`
|
|
251
|
+
);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
process.stdout.write(
|
|
255
|
+
`OK mirror freeze: ${filesScanned} file(s) scanned across ${mirrors.length} mirror(s); no active-source/package claims found.\n`
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
module.exports = {
|
|
260
|
+
ACTIVE_CLAIM_PATTERNS,
|
|
261
|
+
FILE_BANNER_HINTS,
|
|
262
|
+
HISTORICAL_TOKENS,
|
|
263
|
+
collectFiles,
|
|
264
|
+
hasFileBanner,
|
|
265
|
+
hasHistoricalFraming,
|
|
266
|
+
resolveMirrors,
|
|
267
|
+
scanFile,
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
if (require.main === module) main();
|
|
@@ -188,20 +188,49 @@ function canonicalSourcePath(skillMd) {
|
|
|
188
188
|
return path.relative(SKILLS_LIBRARY_REPO_ROOT, skillMd).split(path.sep).join('/');
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Recursively collect SKILL.md paths from a root directory.
|
|
193
|
+
*
|
|
194
|
+
* After the M1 category restructure (jacob-balslev/skills 42552d1), the
|
|
195
|
+
* skills library uses a nested layout: <root>/<category>/[<domain>/]<name>/SKILL.md
|
|
196
|
+
* rather than the old flat <root>/<name>/SKILL.md. This walker mirrors the
|
|
197
|
+
* same recursive pattern used by skill-lint.js so both tools agree on which
|
|
198
|
+
* skill files are canonical.
|
|
199
|
+
*
|
|
200
|
+
* Stops descending as soon as it finds a SKILL.md in a directory (that
|
|
201
|
+
* directory is a skill, not a container). Skips _underscore and .dot dirs.
|
|
202
|
+
*
|
|
203
|
+
* @param {string} dir - Directory to search.
|
|
204
|
+
* @param {number} depth - Current recursion depth (capped at 3).
|
|
205
|
+
* @returns {string[]} Absolute paths to every discovered SKILL.md.
|
|
206
|
+
*/
|
|
207
|
+
function walkForSkillMd(dir, depth = 0) {
|
|
208
|
+
const results = [];
|
|
209
|
+
if (!fs.existsSync(dir) || depth > 3) return results;
|
|
210
|
+
for (const name of fs.readdirSync(dir)) {
|
|
211
|
+
if (name.startsWith('_') || name.startsWith('.')) continue;
|
|
212
|
+
const entryPath = path.join(dir, name);
|
|
213
|
+
if (!fs.statSync(entryPath).isDirectory()) continue;
|
|
214
|
+
const skillMd = path.join(entryPath, 'SKILL.md');
|
|
215
|
+
if (fs.existsSync(skillMd)) {
|
|
216
|
+
results.push(skillMd);
|
|
217
|
+
} else {
|
|
218
|
+
// Not a skill folder — recurse into it as a category/domain container.
|
|
219
|
+
results.push(...walkForSkillMd(entryPath, depth + 1));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return results;
|
|
223
|
+
}
|
|
224
|
+
|
|
191
225
|
function collectCanonicalSkills(sourceDir = DEFAULT_SOURCE_DIR) {
|
|
192
226
|
const skills = [];
|
|
193
|
-
for (const
|
|
194
|
-
if (!entry.isDirectory()) continue;
|
|
195
|
-
const skillDir = path.join(sourceDir, entry.name);
|
|
196
|
-
const skillMd = path.join(skillDir, 'SKILL.md');
|
|
197
|
-
if (!fs.existsSync(skillMd)) continue;
|
|
227
|
+
for (const skillMd of walkForSkillMd(sourceDir)) {
|
|
198
228
|
const text = fs.readFileSync(skillMd, 'utf8');
|
|
199
229
|
const fm = parseFrontmatter(text);
|
|
200
230
|
if (!fm) {
|
|
201
231
|
throw new Error(`Source skill has no parseable frontmatter: ${repoRelative(skillMd)}`);
|
|
202
232
|
}
|
|
203
233
|
skills.push({
|
|
204
|
-
dirName: entry.name,
|
|
205
234
|
sourcePath: skillMd,
|
|
206
235
|
// sourceRelPath: skill-graph-repo-relative path — used for link rewriting (filesystem context).
|
|
207
236
|
sourceRelPath: repoRelative(skillMd),
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Builds the context and prompts for the seven scorecard dimensions defined in
|
|
7
7
|
* SKILL_AUDIT_CHECKLIST.md. The audit runner calls an external
|
|
8
|
-
*
|
|
8
|
+
* grader CLI for each dimension, collects the structured
|
|
9
9
|
* verdicts, and merges them into findings.md / verdict.md / scorecard.md.
|
|
10
10
|
*
|
|
11
11
|
* This file is self-contained. It only uses Node built-ins and does not depend
|
|
12
|
-
* on any specific
|
|
12
|
+
* on any specific provider — the grader CLI is resolved by the caller.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
const fs = require('fs');
|
|
@@ -32,7 +32,7 @@ const { parseFrontmatter } = require('./parse-frontmatter');
|
|
|
32
32
|
*
|
|
33
33
|
* `appliesWhen` is an optional predicate that takes the parsed frontmatter
|
|
34
34
|
* and returns true/false. If it returns false, the dimension is graded as
|
|
35
|
-
* N/A and no
|
|
35
|
+
* N/A and no grader call is made (e.g. grounding for scope: portable).
|
|
36
36
|
*/
|
|
37
37
|
const DIMENSIONS = [
|
|
38
38
|
{
|
|
@@ -344,8 +344,8 @@ function parseFrontmatter(text) {
|
|
|
344
344
|
|
|
345
345
|
// Set of base SKILL.md fields that stay at the top level in the marketplace
|
|
346
346
|
// export shape. Anything else found under `metadata:` is a Skill Graph
|
|
347
|
-
// extension field that the export pipeline relocated to satisfy
|
|
348
|
-
//
|
|
347
|
+
// extension field that the export pipeline relocated to satisfy the plain
|
|
348
|
+
// Agent Skills frontmatter contract. The normalizer lifts those back to
|
|
349
349
|
// top-level so downstream consumers (lint, manifest, route, drift) can read
|
|
350
350
|
// the canonical shape regardless of which format the file is in.
|
|
351
351
|
const SKILL_MD_BASE_FIELDS = new Set([
|
package/scripts/skill-audit.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* deterministic. This is the original behavior and is unchanged.
|
|
10
10
|
*
|
|
11
11
|
* 2. Graded mode (--graded) — on top of the stub, runs a prompt-driven
|
|
12
|
-
* seven-dimension review by calling an external
|
|
12
|
+
* seven-dimension review by calling an external grader CLI for each
|
|
13
13
|
* dimension. Writes structured PASS / PASS WITH FIXES / FAIL verdicts with
|
|
14
14
|
* evidence quotes into findings.md / verdict.md / scorecard.md, replacing
|
|
15
15
|
* the human-TODO placeholders. Requires a grader CLI to be on PATH.
|
|
@@ -18,16 +18,14 @@
|
|
|
18
18
|
* node scripts/skill-audit.js <skill-name>
|
|
19
19
|
* node scripts/skill-audit.js <skill-name> --audit-root <path>
|
|
20
20
|
* node scripts/skill-audit.js <skill-name> --force
|
|
21
|
-
* node scripts/skill-audit.js <skill-name> --graded
|
|
22
|
-
* node scripts/skill-audit.js <skill-name> --graded --grader-cli "claude -p"
|
|
23
|
-
* node scripts/skill-audit.js <skill-name> --graded --grader-cli "codex exec"
|
|
21
|
+
* node scripts/skill-audit.js <skill-name> --graded --grader-cli "<command>"
|
|
24
22
|
*
|
|
25
23
|
* Flags:
|
|
26
24
|
* --audit-root <path> Output directory root (default: examples/audits/).
|
|
27
25
|
* --force Overwrite existing artifacts.
|
|
28
26
|
* --graded Enable the prompt-driven grader pass.
|
|
29
27
|
* --grader-cli <cmd> Shell command to invoke the grader. The prompt is
|
|
30
|
-
* piped to stdin; stdout is parsed.
|
|
28
|
+
* piped to stdin; stdout is parsed. Required with --graded.
|
|
31
29
|
* --grader-timeout <ms> Per-dimension timeout in milliseconds. Default: 120000.
|
|
32
30
|
*
|
|
33
31
|
* Produces under <audit-root>/<skill-name>/:
|
|
@@ -61,7 +59,6 @@ const {
|
|
|
61
59
|
// CLI argument parsing
|
|
62
60
|
// ---------------------------------------------------------------------------
|
|
63
61
|
|
|
64
|
-
const DEFAULT_GRADER_CLI = 'claude -p';
|
|
65
62
|
const DEFAULT_GRADER_TIMEOUT = 120_000;
|
|
66
63
|
|
|
67
64
|
function parseArgs(argv) {
|
|
@@ -71,7 +68,7 @@ function parseArgs(argv) {
|
|
|
71
68
|
auditRoot: path.join(REPO_ROOT, 'examples', 'audits'),
|
|
72
69
|
force: false,
|
|
73
70
|
graded: false,
|
|
74
|
-
graderCli:
|
|
71
|
+
graderCli: null,
|
|
75
72
|
graderTimeout: DEFAULT_GRADER_TIMEOUT,
|
|
76
73
|
errors: [],
|
|
77
74
|
};
|
|
@@ -106,6 +103,7 @@ function parseArgs(argv) {
|
|
|
106
103
|
}
|
|
107
104
|
|
|
108
105
|
if (!result.skillName) result.errors.push('missing required argument: <skill-name>');
|
|
106
|
+
if (result.graded && !result.graderCli) result.errors.push('--graded requires an explicit --grader-cli <cmd>');
|
|
109
107
|
|
|
110
108
|
return result;
|
|
111
109
|
}
|
|
@@ -225,7 +223,7 @@ function inferFix(d) {
|
|
|
225
223
|
* provider. The prompt is piped to stdin to avoid shell-escaping issues with
|
|
226
224
|
* large multi-line prompts containing quotes and backticks.
|
|
227
225
|
*
|
|
228
|
-
* @param {string} graderCli Shell-style command
|
|
226
|
+
* @param {string} graderCli Shell-style command that reads the prompt from stdin.
|
|
229
227
|
* @param {string} prompt Full prompt text.
|
|
230
228
|
* @param {number} timeoutMs Per-call timeout.
|
|
231
229
|
* @returns {{ ok: boolean, stdout: string, stderr: string, exitCode: number, error: string|null }}
|
|
@@ -806,7 +804,7 @@ function main() {
|
|
|
806
804
|
|
|
807
805
|
if (opts.errors.length > 0) {
|
|
808
806
|
for (const e of opts.errors) console.error(`error: ${e}`);
|
|
809
|
-
console.error('\nUsage: node scripts/skill-audit.js <skill-name> [--audit-root <path>] [--force] [--graded
|
|
807
|
+
console.error('\nUsage: node scripts/skill-audit.js <skill-name> [--audit-root <path>] [--force] [--graded --grader-cli <cmd> [--grader-timeout <ms>]]');
|
|
810
808
|
process.exit(1);
|
|
811
809
|
}
|
|
812
810
|
|