@skill-graph/cli 0.5.6 → 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.
Files changed (90) hide show
  1. package/CHANGELOG.md +27 -3
  2. package/README.md +103 -31
  3. package/SKILL_GRAPH.md +2 -2
  4. package/bin/skill-graph.js +150 -10
  5. package/docs/ADOPTION.md +1 -1
  6. package/docs/PRIMER.md +6 -5
  7. package/docs/QUICKSTART-30MIN.md +2 -2
  8. package/docs/SKILL_AUDIT_CHECKLIST.md +1 -1
  9. package/docs/SKILL_METADATA_PROTOCOL.md +2 -2
  10. package/docs/_archived/marketplace-publication-priority-2026-05-18.md +1 -1
  11. package/docs/_drafts/0.5.8-release-prep.md +164 -0
  12. package/docs/adr/0009-sibling-repo-deprecation.md +48 -0
  13. package/docs/diagrams/skill-graph-ecosystem.mmd +17 -0
  14. package/docs/field-reference.generated.md +22 -2
  15. package/docs/field-reference.md +53 -2
  16. package/docs/images/skill-graph-ecosystem.svg +1 -0
  17. package/docs/manifest-field-mapping.md +3 -3
  18. package/docs/marketplace-publication-queue.generated.md +2 -2
  19. package/docs/plans/scripts-roadmap.md +2 -2
  20. package/docs/positioning.md +88 -0
  21. package/docs/research/skill-comprehension-eval-research.md +5 -5
  22. package/docs/research/skill-demand-gap-roadmap-2026-05-16.md +215 -0
  23. package/docs/status.generated.md +48 -0
  24. package/examples/audits/context-graph/findings.md +59 -0
  25. package/examples/audits/context-graph/scorecard.md +22 -0
  26. package/examples/audits/context-graph/verdict.md +33 -0
  27. package/examples/evals/a11y.json +45 -13
  28. package/examples/evals/api-design.json +18 -5
  29. package/examples/evals/code-review.json +18 -5
  30. package/examples/evals/data-modeling.json +18 -5
  31. package/examples/evals/database-migration.json +18 -5
  32. package/examples/evals/debugging.json +37 -11
  33. package/examples/evals/dependency-architecture.json +18 -5
  34. package/examples/evals/design-system-architecture.json +18 -5
  35. package/examples/evals/error-tracking.json +18 -5
  36. package/examples/evals/event-contract-design.json +18 -5
  37. package/examples/evals/form-ux-architecture.json +18 -5
  38. package/examples/evals/framework-fit-analysis.json +18 -5
  39. package/examples/evals/graph-audit.json +55 -13
  40. package/examples/evals/information-architecture.json +18 -5
  41. package/examples/evals/interaction-feedback.json +18 -5
  42. package/examples/evals/interaction-patterns.json +18 -5
  43. package/examples/evals/layout-composition.json +18 -5
  44. package/examples/evals/lint-overlay.json +38 -11
  45. package/examples/evals/microcopy.json +18 -5
  46. package/examples/evals/observability-modeling.json +18 -5
  47. package/examples/evals/pattern-recognition.json +32 -9
  48. package/examples/evals/performance-engineering.json +18 -5
  49. package/examples/evals/refactor.json +41 -12
  50. package/examples/evals/semiotics.json +18 -5
  51. package/examples/evals/skill-infrastructure.json +32 -9
  52. package/examples/evals/skill-router.json +42 -13
  53. package/examples/evals/system-interface-contracts.json +18 -5
  54. package/examples/evals/task-analysis.json +18 -5
  55. package/examples/evals/testing-strategy.json +36 -11
  56. package/examples/evals/type-safety.json +251 -66
  57. package/examples/evals/visual-design-foundations.json +18 -5
  58. package/examples/evals/webhook-integration.json +18 -5
  59. package/examples/fixture-skills/README.md +47 -0
  60. package/examples/fixture-skills/comprehension-full/SKILL.md +79 -0
  61. package/examples/fixture-skills/minimal-capability/SKILL.md +51 -0
  62. package/examples/fixture-skills/with-grounding/SKILL.md +78 -0
  63. package/examples/fixture-skills/with-relations/SKILL.md +87 -0
  64. package/examples/skills.manifest.sample.json +1722 -446
  65. package/marketplace/README.md +1 -1
  66. package/marketplace/skills/a11y/SKILL.md +1 -1
  67. package/marketplace/skills/best-practice/SKILL.md +211 -0
  68. package/marketplace/skills/context-graph/SKILL.md +1 -1
  69. package/marketplace/skills/debugging/SKILL.md +1 -1
  70. package/marketplace/skills/graph-audit/SKILL.md +3 -1
  71. package/marketplace/skills/postgres-rls/SKILL.md +284 -0
  72. package/marketplace/skills/refactor/SKILL.md +1 -1
  73. package/marketplace/skills/skill-infrastructure/SKILL.md +2 -0
  74. package/marketplace/skills/skill-router/SKILL.md +3 -1
  75. package/marketplace/skills/testing-strategy/SKILL.md +1 -1
  76. package/package.json +3 -1
  77. package/schemas/manifest.schema.json +8 -0
  78. package/schemas/manifest.v6.schema.json +8 -0
  79. package/schemas/skill.context.jsonld +5 -0
  80. package/schemas/skill.schema.json +27 -0
  81. package/scripts/__tests__/test-marketplace-export.js +6 -2
  82. package/scripts/__tests__/test-v3-1-alias-contract.js +3 -3
  83. package/scripts/build-status-doc.js +177 -0
  84. package/scripts/check-doc-drift.js +224 -0
  85. package/scripts/check-markdown-links.js +34 -4
  86. package/scripts/check-mirror-freeze.js +270 -0
  87. package/scripts/export-marketplace-skills.js +35 -6
  88. package/scripts/lib/audit-prompt-builder.js +3 -3
  89. package/scripts/lib/parse-frontmatter.js +2 -2
  90. 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 entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
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
- * model CLI (e.g. `claude -p`) for each dimension, collects the structured
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 model provider — the grader CLI is resolved by the caller.
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 model call is made (e.g. grounding for scope: portable).
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 Anthropic
348
- // Claude Code's frontmatter contract. The normalizer lifts those back to
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([
@@ -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 model CLI for each
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. Default: `claude -p`.
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: DEFAULT_GRADER_CLI,
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, e.g. `claude -p` or `codex exec`.
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 [--grader-cli <cmd>] [--grader-timeout <ms>]]');
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