@agents-inc/cli 0.45.0 → 0.47.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/CHANGELOG.md +27 -0
- package/dist/{chunk-V43QDMYQ.js → chunk-2RQYJFKA.js} +2 -2
- package/dist/{chunk-ABE55TEU.js → chunk-3APMMQUA.js} +11 -3
- package/dist/chunk-3APMMQUA.js.map +1 -0
- package/dist/{chunk-KWWLPPHF.js → chunk-B4QYXVPZ.js} +2 -2
- package/dist/{chunk-473YHDYQ.js → chunk-C4QI54PN.js} +147 -52
- package/dist/chunk-C4QI54PN.js.map +1 -0
- package/dist/{chunk-CLHBKFHU.js → chunk-CJFWO46A.js} +2 -2
- package/dist/{chunk-M2XPTQDT.js → chunk-DC333ZDC.js} +4 -4
- package/dist/{chunk-M3AGB4TR.js → chunk-DVW6ASTO.js} +4 -4
- package/dist/{chunk-ARET3NYO.js → chunk-EZ46ZTAQ.js} +3 -3
- package/dist/{chunk-VTDEENSP.js → chunk-FMQ3A7W4.js} +5 -5
- package/dist/{chunk-VTDEENSP.js.map → chunk-FMQ3A7W4.js.map} +1 -1
- package/dist/{chunk-FVN5PFFY.js → chunk-FTD5Z6QD.js} +5 -1
- package/dist/chunk-FTD5Z6QD.js.map +1 -0
- package/dist/{chunk-YQFU2KZ5.js → chunk-FXQYEXLS.js} +3 -3
- package/dist/{chunk-ZECXM7LP.js → chunk-GFDGYQ6M.js} +3553 -3497
- package/dist/chunk-GFDGYQ6M.js.map +1 -0
- package/dist/chunk-HLTJK3XB.js +71 -0
- package/dist/chunk-HLTJK3XB.js.map +1 -0
- package/dist/{chunk-KVHLKPYB.js → chunk-HPJP3HFD.js} +7 -7
- package/dist/{chunk-KZNPPUQG.js → chunk-INKJBMPJ.js} +5 -3
- package/dist/chunk-INKJBMPJ.js.map +1 -0
- package/dist/{chunk-SBWMSNS2.js → chunk-IRJADQM7.js} +2 -2
- package/dist/{chunk-SYGEV3KV.js → chunk-JTTTXGHX.js} +4 -4
- package/dist/{chunk-ELRGSZHZ.js → chunk-JZHIF3K7.js} +5 -5
- package/dist/{chunk-OALQWRLG.js → chunk-KQ27IDYL.js} +2 -2
- package/dist/{chunk-OLZBZAW4.js → chunk-LJ5E4GXC.js} +2 -2
- package/dist/{chunk-SRFNNOLC.js → chunk-LNA6M2IE.js} +2 -2
- package/dist/{chunk-GUIRWCKI.js → chunk-M4P5YJ45.js} +3 -3
- package/dist/{chunk-3S4GIO4B.js → chunk-MVEYK55V.js} +2 -2
- package/dist/{chunk-HKRLWERR.js → chunk-N5OCAAXY.js} +2 -2
- package/dist/{chunk-YRVTXSXP.js → chunk-NRCKIHND.js} +9 -15
- package/dist/chunk-NRCKIHND.js.map +1 -0
- package/dist/{chunk-DAVOSI4M.js → chunk-OEJDFGAF.js} +5 -5
- package/dist/{chunk-ENWMWIHP.js → chunk-QR2TM4OY.js} +5 -12
- package/dist/chunk-QR2TM4OY.js.map +1 -0
- package/dist/{chunk-FYNMNY4P.js → chunk-SPSGZWTZ.js} +25 -12
- package/dist/chunk-SPSGZWTZ.js.map +1 -0
- package/dist/{chunk-3JRWNWBF.js → chunk-TBB3THSL.js} +38 -4
- package/dist/chunk-TBB3THSL.js.map +1 -0
- package/dist/chunk-TWDVLTU6.js +132 -0
- package/dist/chunk-TWDVLTU6.js.map +1 -0
- package/dist/{chunk-KCYNTAAF.js → chunk-VAQJLHUW.js} +12 -10
- package/dist/chunk-VAQJLHUW.js.map +1 -0
- package/dist/{chunk-F3O5YHSI.js → chunk-VSZ5GDET.js} +2 -2
- package/dist/{chunk-Q5BSIARS.js → chunk-XTRPYUWK.js} +15 -15
- package/dist/chunk-XTRPYUWK.js.map +1 -0
- package/dist/{chunk-WWSKP5SR.js → chunk-YTRFL3MR.js} +70 -24
- package/dist/chunk-YTRFL3MR.js.map +1 -0
- package/dist/{chunk-NTPHCNJO.js → chunk-ZWAL2ZY7.js} +2 -2
- package/dist/commands/build/marketplace.js +12 -126
- package/dist/commands/build/marketplace.js.map +1 -1
- package/dist/commands/build/plugins.js +5 -5
- package/dist/commands/build/stack.js +5 -5
- package/dist/commands/compile.js +6 -6
- package/dist/commands/config/get.js +4 -4
- package/dist/commands/config/index.js +5 -5
- package/dist/commands/config/path.js +4 -4
- package/dist/commands/config/set-project.js +4 -4
- package/dist/commands/config/show.js +5 -5
- package/dist/commands/config/unset-project.js +4 -4
- package/dist/commands/diff.js +4 -4
- package/dist/commands/doctor.js +4 -4
- package/dist/commands/edit.js +29 -29
- package/dist/commands/eject.js +19 -23
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/import/skill.js +5 -5
- package/dist/commands/info.js +5 -5
- package/dist/commands/init.js +30 -29
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.js +4 -4
- package/dist/commands/new/agent.js +52 -38
- package/dist/commands/new/agent.js.map +1 -1
- package/dist/commands/new/marketplace.js +252 -0
- package/dist/commands/new/marketplace.js.map +1 -0
- package/dist/commands/new/skill.js +35 -16
- package/dist/commands/new/skill.js.map +1 -1
- package/dist/commands/outdated.js +4 -4
- package/dist/commands/search.js +7 -7
- package/dist/commands/uninstall.js +6 -6
- package/dist/commands/update.js +6 -6
- package/dist/commands/validate.js +229 -19
- package/dist/commands/validate.js.map +1 -1
- package/dist/components/skill-search/skill-search.js +3 -3
- package/dist/components/wizard/category-grid.js +2 -2
- package/dist/components/wizard/category-grid.test.js +122 -2
- package/dist/components/wizard/category-grid.test.js.map +1 -1
- package/dist/components/wizard/checkbox-grid.js +3 -3
- package/dist/components/wizard/checkbox-grid.test.js +3 -3
- package/dist/components/wizard/domain-selection.js +9 -8
- package/dist/components/wizard/help-modal.js +2 -2
- package/dist/components/wizard/menu-item.js +1 -1
- package/dist/components/wizard/search-modal.js +2 -2
- package/dist/components/wizard/search-modal.test.js +2 -2
- package/dist/components/wizard/section-progress.js +2 -2
- package/dist/components/wizard/section-progress.test.js +2 -2
- package/dist/components/wizard/selection-card.js +2 -2
- package/dist/components/wizard/source-grid.js +3 -3
- package/dist/components/wizard/source-grid.test.js +3 -3
- package/dist/components/wizard/stack-selection.js +8 -9
- package/dist/components/wizard/step-agents.js +8 -7
- package/dist/components/wizard/step-agents.test.js +25 -20
- package/dist/components/wizard/step-agents.test.js.map +1 -1
- package/dist/components/wizard/step-build.js +8 -8
- package/dist/components/wizard/step-build.test.js +78 -46
- package/dist/components/wizard/step-build.test.js.map +1 -1
- package/dist/components/wizard/step-confirm.js +4 -4
- package/dist/components/wizard/step-confirm.test.js +8 -8
- package/dist/components/wizard/step-refine.js +2 -2
- package/dist/components/wizard/step-refine.test.js +2 -2
- package/dist/components/wizard/step-settings.js +5 -5
- package/dist/components/wizard/step-settings.test.js +8 -8
- package/dist/components/wizard/step-sources.js +10 -10
- package/dist/components/wizard/step-sources.test.js +11 -11
- package/dist/components/wizard/step-stack.js +12 -12
- package/dist/components/wizard/step-stack.test.js +27 -15
- package/dist/components/wizard/step-stack.test.js.map +1 -1
- package/dist/components/wizard/view-title.js +2 -2
- package/dist/components/wizard/wizard-layout.js +8 -8
- package/dist/components/wizard/wizard-tabs.js +2 -2
- package/dist/components/wizard/wizard-tabs.test.js +2 -2
- package/dist/components/wizard/wizard.js +25 -25
- package/dist/hooks/init.js +3 -5
- package/dist/hooks/init.js.map +1 -1
- package/dist/{source-manager-HXFXBZJU.js → source-manager-Q34LTUVM.js} +4 -4
- package/dist/src/agents/meta/documentor/examples.md +35 -36
- package/dist/src/agents/meta/documentor/workflow.md +91 -105
- package/dist/stores/wizard-store.js +5 -5
- package/dist/stores/wizard-store.test.js +48 -6
- package/dist/stores/wizard-store.test.js.map +1 -1
- package/package.json +1 -1
- package/src/agents/meta/documentor/examples.md +35 -36
- package/src/agents/meta/documentor/workflow.md +91 -105
- package/src/schemas/agent.schema.json +6 -0
- package/src/schemas/metadata.schema.json +7 -41
- package/src/schemas/project-config.schema.json +8 -4
- package/src/schemas/skills-matrix.schema.json +11 -298
- package/src/schemas/stacks.schema.json +2 -4
- package/dist/chunk-3JRWNWBF.js.map +0 -1
- package/dist/chunk-473YHDYQ.js.map +0 -1
- package/dist/chunk-5BDYODWP.js +0 -45
- package/dist/chunk-5BDYODWP.js.map +0 -1
- package/dist/chunk-ABE55TEU.js.map +0 -1
- package/dist/chunk-ENWMWIHP.js.map +0 -1
- package/dist/chunk-FVN5PFFY.js.map +0 -1
- package/dist/chunk-FYNMNY4P.js.map +0 -1
- package/dist/chunk-KCYNTAAF.js.map +0 -1
- package/dist/chunk-KZNPPUQG.js.map +0 -1
- package/dist/chunk-Q5BSIARS.js.map +0 -1
- package/dist/chunk-WWSKP5SR.js.map +0 -1
- package/dist/chunk-YRVTXSXP.js.map +0 -1
- package/dist/chunk-ZECXM7LP.js.map +0 -1
- package/dist/cli/defaults/agent-mappings.yaml +0 -215
- /package/dist/{chunk-V43QDMYQ.js.map → chunk-2RQYJFKA.js.map} +0 -0
- /package/dist/{chunk-KWWLPPHF.js.map → chunk-B4QYXVPZ.js.map} +0 -0
- /package/dist/{chunk-CLHBKFHU.js.map → chunk-CJFWO46A.js.map} +0 -0
- /package/dist/{chunk-M2XPTQDT.js.map → chunk-DC333ZDC.js.map} +0 -0
- /package/dist/{chunk-M3AGB4TR.js.map → chunk-DVW6ASTO.js.map} +0 -0
- /package/dist/{chunk-ARET3NYO.js.map → chunk-EZ46ZTAQ.js.map} +0 -0
- /package/dist/{chunk-YQFU2KZ5.js.map → chunk-FXQYEXLS.js.map} +0 -0
- /package/dist/{chunk-KVHLKPYB.js.map → chunk-HPJP3HFD.js.map} +0 -0
- /package/dist/{chunk-SBWMSNS2.js.map → chunk-IRJADQM7.js.map} +0 -0
- /package/dist/{chunk-SYGEV3KV.js.map → chunk-JTTTXGHX.js.map} +0 -0
- /package/dist/{chunk-ELRGSZHZ.js.map → chunk-JZHIF3K7.js.map} +0 -0
- /package/dist/{chunk-OALQWRLG.js.map → chunk-KQ27IDYL.js.map} +0 -0
- /package/dist/{chunk-OLZBZAW4.js.map → chunk-LJ5E4GXC.js.map} +0 -0
- /package/dist/{chunk-SRFNNOLC.js.map → chunk-LNA6M2IE.js.map} +0 -0
- /package/dist/{chunk-GUIRWCKI.js.map → chunk-M4P5YJ45.js.map} +0 -0
- /package/dist/{chunk-3S4GIO4B.js.map → chunk-MVEYK55V.js.map} +0 -0
- /package/dist/{chunk-HKRLWERR.js.map → chunk-N5OCAAXY.js.map} +0 -0
- /package/dist/{chunk-DAVOSI4M.js.map → chunk-OEJDFGAF.js.map} +0 -0
- /package/dist/{chunk-F3O5YHSI.js.map → chunk-VSZ5GDET.js.map} +0 -0
- /package/dist/{chunk-NTPHCNJO.js.map → chunk-ZWAL2ZY7.js.map} +0 -0
- /package/dist/{source-manager-HXFXBZJU.js.map → source-manager-Q34LTUVM.js.map} +0 -0
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ERROR_MESSAGES
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-2RQYJFKA.js";
|
|
5
5
|
import {
|
|
6
6
|
BaseCommand,
|
|
7
7
|
EXIT_CODES
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-LNA6M2IE.js";
|
|
9
9
|
import {
|
|
10
|
+
checkMatrixHealth,
|
|
11
|
+
extractAllSkills,
|
|
10
12
|
extractFrontmatter,
|
|
13
|
+
loadProjectSourceConfig,
|
|
14
|
+
loadSkillsMatrix,
|
|
15
|
+
mergeMatrixWithSkills,
|
|
16
|
+
parseFrontmatter,
|
|
11
17
|
printPluginValidationResult,
|
|
12
18
|
validateAllPlugins,
|
|
13
19
|
validatePlugin
|
|
14
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-GFDGYQ6M.js";
|
|
15
21
|
import "../chunk-T4EXUIBY.js";
|
|
16
22
|
import {
|
|
23
|
+
SKILL_ID_PATTERN,
|
|
17
24
|
agentFrontmatterValidationSchema,
|
|
18
25
|
agentYamlGenerationSchema,
|
|
26
|
+
directoryExists,
|
|
19
27
|
fileExists,
|
|
20
28
|
getErrorMessage,
|
|
29
|
+
glob,
|
|
21
30
|
log,
|
|
22
31
|
metadataValidationSchema,
|
|
23
32
|
pluginManifestSchema,
|
|
@@ -26,13 +35,14 @@ import {
|
|
|
26
35
|
skillFrontmatterValidationSchema,
|
|
27
36
|
skillsMatrixConfigSchema,
|
|
28
37
|
stackConfigValidationSchema,
|
|
29
|
-
stacksConfigSchema
|
|
30
|
-
|
|
38
|
+
stacksConfigSchema,
|
|
39
|
+
verbose
|
|
40
|
+
} from "../chunk-C4QI54PN.js";
|
|
31
41
|
import {
|
|
32
42
|
CLAUDE_DIR,
|
|
33
43
|
CLAUDE_SRC_DIR,
|
|
34
44
|
STANDARD_FILES
|
|
35
|
-
} from "../chunk-
|
|
45
|
+
} from "../chunk-FTD5Z6QD.js";
|
|
36
46
|
import {
|
|
37
47
|
init_esm_shims
|
|
38
48
|
} from "../chunk-DHET7RCE.js";
|
|
@@ -40,7 +50,7 @@ import {
|
|
|
40
50
|
// src/cli/commands/validate.ts
|
|
41
51
|
init_esm_shims();
|
|
42
52
|
import { Args, Flags } from "@oclif/core";
|
|
43
|
-
import
|
|
53
|
+
import path3 from "path";
|
|
44
54
|
|
|
45
55
|
// src/cli/lib/schema-validator.ts
|
|
46
56
|
init_esm_shims();
|
|
@@ -135,11 +145,11 @@ var VALIDATION_TARGETS = [
|
|
|
135
145
|
];
|
|
136
146
|
function formatZodErrors(error) {
|
|
137
147
|
return error.issues.map((issue) => {
|
|
138
|
-
const
|
|
148
|
+
const path4 = issue.path.join(".");
|
|
139
149
|
if (issue.code === "unrecognized_keys") {
|
|
140
150
|
return `Unrecognized key: "${issue.keys.join('", "')}"`;
|
|
141
151
|
}
|
|
142
|
-
return
|
|
152
|
+
return path4 ? `${path4}: ${issue.message}` : issue.message;
|
|
143
153
|
});
|
|
144
154
|
}
|
|
145
155
|
async function validateFile(filePath, schema, extractor) {
|
|
@@ -246,15 +256,180 @@ function printValidationResults(result) {
|
|
|
246
256
|
}
|
|
247
257
|
}
|
|
248
258
|
|
|
259
|
+
// src/cli/lib/source-validator.ts
|
|
260
|
+
init_esm_shims();
|
|
261
|
+
import path2 from "path";
|
|
262
|
+
import { parse as parseYaml2 } from "yaml";
|
|
263
|
+
function isSnakeCase(key) {
|
|
264
|
+
return /[a-z]_[a-z]/.test(key);
|
|
265
|
+
}
|
|
266
|
+
async function validateSource(sourcePath) {
|
|
267
|
+
const issues = [];
|
|
268
|
+
const resolvedPath = path2.isAbsolute(sourcePath) ? sourcePath : path2.resolve(sourcePath);
|
|
269
|
+
if (!await directoryExists(resolvedPath)) {
|
|
270
|
+
issues.push({
|
|
271
|
+
severity: "error",
|
|
272
|
+
file: resolvedPath,
|
|
273
|
+
message: "Source directory does not exist"
|
|
274
|
+
});
|
|
275
|
+
return buildResult(issues, 0);
|
|
276
|
+
}
|
|
277
|
+
const sourceProjectConfig = await loadProjectSourceConfig(resolvedPath);
|
|
278
|
+
const skillsDirRelPath = sourceProjectConfig?.skillsDir ?? "src/skills";
|
|
279
|
+
const skillsDir = path2.join(resolvedPath, skillsDirRelPath);
|
|
280
|
+
if (!await directoryExists(skillsDir)) {
|
|
281
|
+
issues.push({
|
|
282
|
+
severity: "error",
|
|
283
|
+
file: skillsDir,
|
|
284
|
+
message: "Skills directory does not exist"
|
|
285
|
+
});
|
|
286
|
+
return buildResult(issues, 0);
|
|
287
|
+
}
|
|
288
|
+
const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir);
|
|
289
|
+
const metadataFiles = await glob(`**/${STANDARD_FILES.METADATA_YAML}`, skillsDir);
|
|
290
|
+
const skillMdDirs = new Set(skillMdFiles.map((f) => path2.dirname(f)));
|
|
291
|
+
const metadataDirs = new Set(metadataFiles.map((f) => path2.dirname(f)));
|
|
292
|
+
for (const dir of skillMdDirs) {
|
|
293
|
+
if (!metadataDirs.has(dir)) {
|
|
294
|
+
issues.push({
|
|
295
|
+
severity: "error",
|
|
296
|
+
file: path2.join(skillsDir, dir),
|
|
297
|
+
message: `Missing ${STANDARD_FILES.METADATA_YAML} \u2014 skill directory has ${STANDARD_FILES.SKILL_MD} but no metadata`
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
for (const dir of metadataDirs) {
|
|
302
|
+
if (!skillMdDirs.has(dir)) {
|
|
303
|
+
issues.push({
|
|
304
|
+
severity: "error",
|
|
305
|
+
file: path2.join(skillsDir, dir),
|
|
306
|
+
message: `Missing ${STANDARD_FILES.SKILL_MD} \u2014 skill directory has ${STANDARD_FILES.METADATA_YAML} but no SKILL.md`
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
let skillCount = 0;
|
|
311
|
+
for (const metadataFile of metadataFiles) {
|
|
312
|
+
const metadataPath = path2.join(skillsDir, metadataFile);
|
|
313
|
+
const skillDir = path2.dirname(metadataFile);
|
|
314
|
+
const skillMdPath = path2.join(skillsDir, skillDir, STANDARD_FILES.SKILL_MD);
|
|
315
|
+
if (!await fileExists(skillMdPath)) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
skillCount++;
|
|
319
|
+
const relPath = path2.join(skillsDirRelPath, metadataFile);
|
|
320
|
+
let rawMetadata;
|
|
321
|
+
try {
|
|
322
|
+
const metadataContent = await readFile(metadataPath);
|
|
323
|
+
rawMetadata = parseYaml2(metadataContent);
|
|
324
|
+
} catch (error) {
|
|
325
|
+
issues.push({
|
|
326
|
+
severity: "error",
|
|
327
|
+
file: relPath,
|
|
328
|
+
message: "Failed to parse YAML"
|
|
329
|
+
});
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (rawMetadata && typeof rawMetadata === "object" && !Array.isArray(rawMetadata)) {
|
|
333
|
+
for (const key of Object.keys(rawMetadata)) {
|
|
334
|
+
if (isSnakeCase(key)) {
|
|
335
|
+
issues.push({
|
|
336
|
+
severity: "error",
|
|
337
|
+
file: relPath,
|
|
338
|
+
message: `Key '${key}' uses snake_case \u2014 use camelCase instead`
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const result = metadataValidationSchema.safeParse(rawMetadata);
|
|
344
|
+
if (!result.success) {
|
|
345
|
+
for (const issue of result.error.issues) {
|
|
346
|
+
const fieldPath = issue.path.join(".");
|
|
347
|
+
issues.push({
|
|
348
|
+
severity: "error",
|
|
349
|
+
file: relPath,
|
|
350
|
+
message: `${fieldPath}: ${issue.message}`
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
const metadata = result.data;
|
|
356
|
+
const dirName = path2.basename(skillDir);
|
|
357
|
+
if (metadata.cliName !== dirName) {
|
|
358
|
+
issues.push({
|
|
359
|
+
severity: "warning",
|
|
360
|
+
file: relPath,
|
|
361
|
+
message: `cliName '${metadata.cliName}' does not match directory name '${dirName}'`
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
const skillMdContent = await readFile(skillMdPath);
|
|
365
|
+
const frontmatter = parseFrontmatter(skillMdContent, skillMdPath);
|
|
366
|
+
if (frontmatter) {
|
|
367
|
+
if (!SKILL_ID_PATTERN.test(frontmatter.name)) {
|
|
368
|
+
issues.push({
|
|
369
|
+
severity: "warning",
|
|
370
|
+
file: path2.join(skillsDirRelPath, skillDir, STANDARD_FILES.SKILL_MD),
|
|
371
|
+
message: `SKILL.md name '${frontmatter.name}' does not match expected skill ID pattern (domain-subcategory-name)`
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (metadata.category && !/^(web|api|cli|mobile|infra|meta|security|shared)-.+$/.test(metadata.category)) {
|
|
376
|
+
issues.push({
|
|
377
|
+
severity: "warning",
|
|
378
|
+
file: relPath,
|
|
379
|
+
message: `Category '${metadata.category}' does not follow domain-prefixed pattern (e.g., 'web-framework', 'api-database')`
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
const matrixRelPath = sourceProjectConfig?.matrixFile ?? "config/skills-matrix.yaml";
|
|
385
|
+
const matrixPath = path2.join(resolvedPath, matrixRelPath);
|
|
386
|
+
if (await fileExists(matrixPath)) {
|
|
387
|
+
const matrix = await loadSkillsMatrix(matrixPath);
|
|
388
|
+
const skills = await extractAllSkills(skillsDir);
|
|
389
|
+
const mergedMatrix = await mergeMatrixWithSkills(matrix, skills);
|
|
390
|
+
const healthIssues = checkMatrixHealth(mergedMatrix);
|
|
391
|
+
for (const healthIssue of healthIssues) {
|
|
392
|
+
issues.push({
|
|
393
|
+
severity: healthIssue.severity,
|
|
394
|
+
file: matrixRelPath,
|
|
395
|
+
message: healthIssue.details
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
} else {
|
|
399
|
+
verbose(`No matrix file at '${matrixPath}' \u2014 skipping cross-reference validation`);
|
|
400
|
+
}
|
|
401
|
+
} catch (error) {
|
|
402
|
+
issues.push({
|
|
403
|
+
severity: "warning",
|
|
404
|
+
file: "config/skills-matrix.yaml",
|
|
405
|
+
message: `Cross-reference validation skipped: failed to load matrix`
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
return buildResult(issues, skillCount);
|
|
409
|
+
}
|
|
410
|
+
function buildResult(issues, skillCount) {
|
|
411
|
+
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
412
|
+
const warningCount = issues.filter((i) => i.severity === "warning").length;
|
|
413
|
+
return { issues, skillCount, errorCount, warningCount };
|
|
414
|
+
}
|
|
415
|
+
|
|
249
416
|
// src/cli/commands/validate.ts
|
|
250
417
|
var Validate = class _Validate extends BaseCommand {
|
|
251
|
-
static summary = "Validate YAML files against schemas or validate
|
|
252
|
-
static description = "Validates skill/agent YAML files against JSON schemas,
|
|
418
|
+
static summary = "Validate YAML files against schemas, validate compiled plugins, or validate a skills source";
|
|
419
|
+
static description = "Validates skill/agent YAML files against JSON schemas, validates compiled plugin structure and content, or validates a skills source repository for metadata correctness. Without arguments, validates all YAML files in the current directory against their schemas. With --source, validates all skills in the source for schema compliance, cross-references, and conventions. With a path argument or --plugins flag, validates plugin(s) instead.";
|
|
253
420
|
static examples = [
|
|
254
421
|
{
|
|
255
422
|
description: "Validate all YAML schemas",
|
|
256
423
|
command: "<%= config.bin %> <%= command.id %>"
|
|
257
424
|
},
|
|
425
|
+
{
|
|
426
|
+
description: "Validate a skills source repository",
|
|
427
|
+
command: "<%= config.bin %> <%= command.id %> --source ."
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
description: "Validate a remote skills source",
|
|
431
|
+
command: "<%= config.bin %> <%= command.id %> --source github:acme-corp/skills"
|
|
432
|
+
},
|
|
258
433
|
{
|
|
259
434
|
description: "Validate a specific plugin",
|
|
260
435
|
command: "<%= config.bin %> <%= command.id %> ./path/to/plugin"
|
|
@@ -294,7 +469,9 @@ var Validate = class _Validate extends BaseCommand {
|
|
|
294
469
|
};
|
|
295
470
|
async run() {
|
|
296
471
|
const { args, flags } = await this.parse(_Validate);
|
|
297
|
-
if (
|
|
472
|
+
if (flags.source) {
|
|
473
|
+
await this.validateSkillsSource(flags.source);
|
|
474
|
+
} else if (args.path || flags.plugins) {
|
|
298
475
|
await this.validatePlugins(args.path, flags.verbose, flags.all);
|
|
299
476
|
} else {
|
|
300
477
|
await this.validateSchemas();
|
|
@@ -317,15 +494,15 @@ var Validate = class _Validate extends BaseCommand {
|
|
|
317
494
|
this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });
|
|
318
495
|
}
|
|
319
496
|
}
|
|
320
|
-
async validatePlugins(pluginPath,
|
|
321
|
-
const targetPath = pluginPath ?
|
|
497
|
+
async validatePlugins(pluginPath, verbose2, all) {
|
|
498
|
+
const targetPath = pluginPath ? path3.resolve(pluginPath) : process.cwd();
|
|
322
499
|
if (all) {
|
|
323
|
-
await this.validateAllPluginsInDirectory(targetPath,
|
|
500
|
+
await this.validateAllPluginsInDirectory(targetPath, verbose2);
|
|
324
501
|
} else {
|
|
325
|
-
await this.validateSinglePlugin(targetPath,
|
|
502
|
+
await this.validateSinglePlugin(targetPath, verbose2);
|
|
326
503
|
}
|
|
327
504
|
}
|
|
328
|
-
async validateAllPluginsInDirectory(targetPath,
|
|
505
|
+
async validateAllPluginsInDirectory(targetPath, verbose2) {
|
|
329
506
|
this.log("");
|
|
330
507
|
this.log(`Validating all plugins in: ${targetPath}`);
|
|
331
508
|
this.log("");
|
|
@@ -341,7 +518,7 @@ var Validate = class _Validate extends BaseCommand {
|
|
|
341
518
|
this.log(` Invalid: ${result.summary.invalid}`);
|
|
342
519
|
this.log(` With warnings: ${result.summary.withWarnings}`);
|
|
343
520
|
for (const { name, result: pluginResult } of result.results) {
|
|
344
|
-
printPluginValidationResult(name, pluginResult,
|
|
521
|
+
printPluginValidationResult(name, pluginResult, verbose2);
|
|
345
522
|
}
|
|
346
523
|
if (result.valid) {
|
|
347
524
|
this.log("");
|
|
@@ -364,7 +541,7 @@ var Validate = class _Validate extends BaseCommand {
|
|
|
364
541
|
const result = await validatePlugin(targetPath);
|
|
365
542
|
const summary = result.valid ? "Done: Plugin is valid" : "Done: Plugin has errors";
|
|
366
543
|
this.log(summary);
|
|
367
|
-
printPluginValidationResult(
|
|
544
|
+
printPluginValidationResult(path3.basename(targetPath), result, true);
|
|
368
545
|
if (result.valid && result.warnings.length === 0) {
|
|
369
546
|
this.log("");
|
|
370
547
|
this.logSuccess("Plugin validated successfully");
|
|
@@ -382,6 +559,39 @@ var Validate = class _Validate extends BaseCommand {
|
|
|
382
559
|
this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });
|
|
383
560
|
}
|
|
384
561
|
}
|
|
562
|
+
async validateSkillsSource(source) {
|
|
563
|
+
this.log("");
|
|
564
|
+
this.log(`Validating source: ${source}`);
|
|
565
|
+
this.log("");
|
|
566
|
+
try {
|
|
567
|
+
const result = await validateSource(source);
|
|
568
|
+
this.log(`Checked ${result.skillCount} skill(s)`);
|
|
569
|
+
this.log("");
|
|
570
|
+
for (const issue of result.issues) {
|
|
571
|
+
const prefix = issue.severity === "error" ? "ERROR" : "WARN";
|
|
572
|
+
this.log(` [${prefix}] ${issue.file}: ${issue.message}`);
|
|
573
|
+
}
|
|
574
|
+
if (result.issues.length > 0) {
|
|
575
|
+
this.log("");
|
|
576
|
+
}
|
|
577
|
+
this.log(`Result: ${result.errorCount} error(s), ${result.warningCount} warning(s)`);
|
|
578
|
+
if (result.errorCount > 0) {
|
|
579
|
+
this.log("");
|
|
580
|
+
this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });
|
|
581
|
+
} else if (result.warningCount > 0) {
|
|
582
|
+
this.log("");
|
|
583
|
+
this.logWarning("Source valid with warnings");
|
|
584
|
+
this.log("");
|
|
585
|
+
} else {
|
|
586
|
+
this.log("");
|
|
587
|
+
this.logSuccess("Source validated successfully");
|
|
588
|
+
this.log("");
|
|
589
|
+
}
|
|
590
|
+
} catch (error) {
|
|
591
|
+
const message = getErrorMessage(error);
|
|
592
|
+
this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });
|
|
593
|
+
}
|
|
594
|
+
}
|
|
385
595
|
};
|
|
386
596
|
export {
|
|
387
597
|
Validate as default
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/commands/validate.ts","../../src/cli/lib/schema-validator.ts"],"sourcesContent":["import { Args, Flags } from \"@oclif/core\";\nimport path from \"path\";\nimport { BaseCommand } from \"../base-command.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { EXIT_CODES } from \"../lib/exit-codes.js\";\nimport { ERROR_MESSAGES } from \"../utils/messages.js\";\nimport { validateAllSchemas, printValidationResults } from \"../lib/schema-validator.js\";\nimport {\n validatePlugin,\n validateAllPlugins,\n printPluginValidationResult,\n} from \"../lib/plugins/index.js\";\n\nexport default class Validate extends BaseCommand {\n static summary = \"Validate YAML files against schemas or validate compiled plugins\";\n static description =\n \"Validates skill/agent YAML files against JSON schemas, or validates compiled plugin structure and content. \" +\n \"Without arguments, validates all YAML files in the current directory against their schemas. \" +\n \"With a path argument or --plugins flag, validates plugin(s) instead.\";\n\n static examples = [\n {\n description: \"Validate all YAML schemas\",\n command: \"<%= config.bin %> <%= command.id %>\",\n },\n {\n description: \"Validate a specific plugin\",\n command: \"<%= config.bin %> <%= command.id %> ./path/to/plugin\",\n },\n {\n description: \"Validate all plugins in a directory\",\n command: \"<%= config.bin %> <%= command.id %> ./plugins --all\",\n },\n {\n description: \"Validate plugins with verbose output\",\n command: \"<%= config.bin %> <%= command.id %> --plugins --verbose\",\n },\n ];\n\n static args = {\n path: Args.string({\n description: \"Path to plugin or plugins directory to validate\",\n required: false,\n }),\n };\n\n static flags = {\n ...BaseCommand.baseFlags,\n verbose: Flags.boolean({\n char: \"v\",\n description: \"Enable verbose logging\",\n default: false,\n }),\n all: Flags.boolean({\n char: \"a\",\n description: \"Validate all plugins in directory\",\n default: false,\n }),\n plugins: Flags.boolean({\n char: \"p\",\n description: \"Validate plugins instead of schemas\",\n default: false,\n }),\n };\n\n async run(): Promise<void> {\n const { args, flags } = await this.parse(Validate);\n\n if (args.path || flags.plugins) {\n await this.validatePlugins(args.path, flags.verbose, flags.all);\n } else {\n await this.validateSchemas();\n }\n }\n\n private async validateSchemas(): Promise<void> {\n this.log(\"\");\n this.log(\"Validating all schemas\");\n this.log(\"\");\n\n try {\n const result = await validateAllSchemas();\n\n const summary = result.valid\n ? `Done: ${result.summary.validFiles}/${result.summary.totalFiles} files valid`\n : `Done: ${result.summary.invalidFiles} invalid files`;\n\n this.log(summary);\n printValidationResults(result);\n\n if (!result.valid) {\n this.exit(EXIT_CODES.ERROR);\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n\n private async validatePlugins(\n pluginPath: string | undefined,\n verbose: boolean,\n all: boolean,\n ): Promise<void> {\n const targetPath = pluginPath ? path.resolve(pluginPath) : process.cwd();\n\n if (all) {\n await this.validateAllPluginsInDirectory(targetPath, verbose);\n } else {\n await this.validateSinglePlugin(targetPath, verbose);\n }\n }\n\n private async validateAllPluginsInDirectory(targetPath: string, verbose: boolean): Promise<void> {\n this.log(\"\");\n this.log(`Validating all plugins in: ${targetPath}`);\n this.log(\"\");\n\n try {\n const result = await validateAllPlugins(targetPath);\n\n const summary = result.valid\n ? `Done: ${result.summary.valid}/${result.summary.total} plugins valid`\n : `Done: ${result.summary.invalid} invalid plugins`;\n\n this.log(summary);\n\n this.log(\"\");\n this.log(\" Plugin Validation Summary:\");\n this.log(\" -------------------------\");\n this.log(` Total plugins: ${result.summary.total}`);\n this.log(` Valid: ${result.summary.valid}`);\n this.log(` Invalid: ${result.summary.invalid}`);\n this.log(` With warnings: ${result.summary.withWarnings}`);\n\n for (const { name, result: pluginResult } of result.results) {\n printPluginValidationResult(name, pluginResult, verbose);\n }\n\n if (result.valid) {\n this.log(\"\");\n this.logSuccess(\"All plugins validated successfully\");\n this.log(\"\");\n } else {\n this.log(\"\");\n this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n\n private async validateSinglePlugin(targetPath: string, _verbose: boolean): Promise<void> {\n this.log(\"\");\n this.log(`Validating plugin: ${targetPath}`);\n this.log(\"\");\n\n try {\n const result = await validatePlugin(targetPath);\n\n const summary = result.valid ? \"Done: Plugin is valid\" : \"Done: Plugin has errors\";\n\n this.log(summary);\n\n printPluginValidationResult(path.basename(targetPath), result, true);\n\n if (result.valid && result.warnings.length === 0) {\n this.log(\"\");\n this.logSuccess(\"Plugin validated successfully\");\n this.log(\"\");\n } else if (result.valid) {\n this.log(\"\");\n this.logWarning(\"Plugin valid with warnings\");\n this.log(\"\");\n } else {\n this.log(\"\");\n this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n}\n","import { sumBy } from \"remeda\";\nimport { z } from \"zod\";\nimport path from \"path\";\nimport { getErrorMessage } from \"../utils/errors\";\nimport { readFile, fileExists } from \"../utils/fs\";\nimport { parse as parseYaml } from \"yaml\";\nimport fg from \"fast-glob\";\nimport { extractFrontmatter } from \"../utils/frontmatter\";\nimport { log } from \"../utils/logger\";\nimport {\n skillsMatrixConfigSchema,\n metadataValidationSchema,\n stackConfigValidationSchema,\n skillFrontmatterValidationSchema,\n agentFrontmatterValidationSchema,\n agentYamlGenerationSchema,\n stacksConfigSchema,\n projectSourceConfigSchema,\n pluginManifestSchema,\n} from \"./schemas\";\nimport { CLAUDE_DIR, CLAUDE_SRC_DIR, STANDARD_FILES } from \"../consts\";\n\ntype FileValidationError = {\n file: string;\n errors: string[];\n};\n\ntype SchemaValidationResult = {\n schemaName: string;\n valid: boolean;\n totalFiles: number;\n validFiles: number;\n invalidFiles: FileValidationError[];\n};\n\nexport type FullValidationResult = {\n valid: boolean;\n results: SchemaValidationResult[];\n summary: {\n totalSchemas: number;\n totalFiles: number;\n validFiles: number;\n invalidFiles: number;\n };\n};\n\ntype ContentExtractor = (content: string) => unknown | null;\n\ntype ValidationTarget = {\n name: string;\n schema: z.ZodType<unknown>;\n pattern: string;\n baseDir: string;\n extractor?: ContentExtractor;\n};\n\nconst VALIDATION_TARGETS: ValidationTarget[] = [\n {\n name: \"Skills Matrix\",\n schema: skillsMatrixConfigSchema,\n pattern: STANDARD_FILES.SKILLS_MATRIX_YAML,\n baseDir: \"src/config\",\n },\n {\n name: \"Skill Metadata\",\n schema: metadataValidationSchema,\n pattern: `**/${STANDARD_FILES.METADATA_YAML}`,\n baseDir: \"src/skills\",\n },\n {\n name: \"Stack Skill Metadata\",\n schema: metadataValidationSchema,\n pattern: `**/skills/**/${STANDARD_FILES.METADATA_YAML}`,\n baseDir: \"src/stacks\",\n },\n {\n name: \"Stack Config\",\n schema: stackConfigValidationSchema,\n pattern: `*/${STANDARD_FILES.CONFIG_YAML}`,\n baseDir: \"src/stacks\",\n },\n {\n name: \"Agent Definition\",\n schema: agentYamlGenerationSchema,\n pattern: `**/${STANDARD_FILES.AGENT_YAML}`,\n baseDir: \"src/agents\",\n },\n {\n name: \"Skill Frontmatter\",\n schema: skillFrontmatterValidationSchema,\n pattern: `**/${STANDARD_FILES.SKILL_MD}`,\n baseDir: \"src/skills\",\n extractor: extractFrontmatter,\n },\n {\n name: \"Stack Skill Frontmatter\",\n schema: skillFrontmatterValidationSchema,\n pattern: `**/skills/**/${STANDARD_FILES.SKILL_MD}`,\n baseDir: \"src/stacks\",\n extractor: extractFrontmatter,\n },\n {\n name: \"Stacks Config\",\n schema: stacksConfigSchema,\n pattern: \"stacks.yaml\",\n baseDir: \"config\",\n },\n {\n name: \"Project Source Config\",\n schema: projectSourceConfigSchema,\n pattern: STANDARD_FILES.CONFIG_YAML,\n baseDir: CLAUDE_SRC_DIR,\n },\n {\n name: \"Project Skill Metadata\",\n schema: metadataValidationSchema,\n pattern: `*/${STANDARD_FILES.METADATA_YAML}`,\n baseDir: `${CLAUDE_DIR}/skills`,\n },\n {\n name: \"Project Skill Frontmatter\",\n schema: skillFrontmatterValidationSchema,\n pattern: `*/${STANDARD_FILES.SKILL_MD}`,\n baseDir: `${CLAUDE_DIR}/skills`,\n extractor: extractFrontmatter,\n },\n {\n name: \"Project Agent Frontmatter\",\n schema: agentFrontmatterValidationSchema,\n pattern: \"*.md\",\n baseDir: `${CLAUDE_DIR}/agents`,\n extractor: extractFrontmatter,\n },\n {\n name: \"Plugin Manifest\",\n schema: pluginManifestSchema,\n pattern: `*/${STANDARD_FILES.PLUGIN_JSON}`,\n baseDir: `${CLAUDE_DIR}/plugins`,\n extractor: (content: string) => JSON.parse(content) as unknown,\n },\n];\n\nfunction formatZodErrors(error: z.ZodError): string[] {\n return error.issues.map((issue) => {\n const path = issue.path.join(\".\");\n if (issue.code === \"unrecognized_keys\") {\n return `Unrecognized key: \"${issue.keys.join('\", \"')}\"`;\n }\n return path ? `${path}: ${issue.message}` : issue.message;\n });\n}\n\nasync function validateFile(\n filePath: string,\n schema: z.ZodType<unknown>,\n extractor?: ContentExtractor,\n): Promise<{ valid: boolean; errors: string[] }> {\n try {\n if (!(await fileExists(filePath))) {\n return { valid: false, errors: [`File not found: ${filePath}`] };\n }\n\n const content = await readFile(filePath);\n\n let parsed: unknown;\n if (extractor) {\n parsed = extractor(content);\n if (parsed === null) {\n return {\n valid: false,\n errors: [\"Failed to extract content (no valid frontmatter found)\"],\n };\n }\n } else {\n parsed = parseYaml(content);\n }\n\n const result = schema.safeParse(parsed);\n\n if (result.success) {\n return { valid: true, errors: [] };\n }\n\n return { valid: false, errors: formatZodErrors(result.error) };\n } catch (error) {\n const message = getErrorMessage(error);\n return { valid: false, errors: [`Failed to parse content: ${message}`] };\n }\n}\n\nasync function validateTarget(\n target: ValidationTarget,\n rootDir: string = process.cwd(),\n): Promise<SchemaValidationResult> {\n const baseDir = path.join(rootDir, target.baseDir);\n const pattern = path.join(baseDir, target.pattern);\n const files = await fg(pattern, { absolute: true });\n\n const result: SchemaValidationResult = {\n schemaName: target.name,\n valid: true,\n totalFiles: files.length,\n validFiles: 0,\n invalidFiles: [],\n };\n\n if (files.length === 0) {\n return result;\n }\n\n for (const file of files) {\n const validation = await validateFile(file, target.schema, target.extractor);\n const relativePath = path.relative(rootDir, file);\n\n if (validation.valid) {\n result.validFiles++;\n } else {\n result.valid = false;\n result.invalidFiles.push({\n file: relativePath,\n errors: validation.errors,\n });\n }\n }\n\n return result;\n}\n\nexport async function validateAllSchemas(\n rootDir: string = process.cwd(),\n): Promise<FullValidationResult> {\n const results: SchemaValidationResult[] = [];\n\n for (const target of VALIDATION_TARGETS) {\n const result = await validateTarget(target, rootDir);\n results.push(result);\n }\n\n const summary = {\n totalSchemas: results.length,\n totalFiles: sumBy(results, (r) => r.totalFiles),\n validFiles: sumBy(results, (r) => r.validFiles),\n invalidFiles: sumBy(results, (r) => r.invalidFiles.length),\n };\n\n return {\n valid: results.every((r) => r.valid),\n results,\n summary,\n };\n}\n\nexport function printValidationResults(result: FullValidationResult): void {\n log(\"\\n Schema Validation Summary:\");\n log(\" ─────────────────────────\");\n log(` Total schemas checked: ${result.summary.totalSchemas}`);\n log(` Total files: ${result.summary.totalFiles}`);\n log(` Valid: ${result.summary.validFiles}`);\n log(` Invalid: ${result.summary.invalidFiles}`);\n\n for (const schemaResult of result.results) {\n if (schemaResult.totalFiles === 0) continue;\n\n const status = schemaResult.valid ? \"✓\" : \"✗\";\n log(\n `\\n ${status} ${schemaResult.schemaName}: ${schemaResult.validFiles}/${schemaResult.totalFiles} valid`,\n );\n\n if (schemaResult.invalidFiles.length > 0) {\n for (const file of schemaResult.invalidFiles) {\n log(`\\n ${file.file}:`);\n file.errors.forEach((e) => log(` - ${e}`));\n }\n }\n }\n\n if (result.valid) {\n log(\"\\n ✓ All schemas validated successfully\\n\");\n } else {\n log(\"\\n ✗ Validation failed\\n\");\n }\n}\n"],"mappings":"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","names":["path","path","path"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/commands/validate.ts","../../src/cli/lib/schema-validator.ts","../../src/cli/lib/source-validator.ts"],"sourcesContent":["import { Args, Flags } from \"@oclif/core\";\nimport path from \"path\";\nimport { BaseCommand } from \"../base-command.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { EXIT_CODES } from \"../lib/exit-codes.js\";\nimport { ERROR_MESSAGES } from \"../utils/messages.js\";\nimport { validateAllSchemas, printValidationResults } from \"../lib/schema-validator.js\";\nimport {\n validatePlugin,\n validateAllPlugins,\n printPluginValidationResult,\n} from \"../lib/plugins/index.js\";\nimport { validateSource } from \"../lib/source-validator.js\";\n\nexport default class Validate extends BaseCommand {\n static summary =\n \"Validate YAML files against schemas, validate compiled plugins, or validate a skills source\";\n static description =\n \"Validates skill/agent YAML files against JSON schemas, validates compiled plugin structure and content, \" +\n \"or validates a skills source repository for metadata correctness. \" +\n \"Without arguments, validates all YAML files in the current directory against their schemas. \" +\n \"With --source, validates all skills in the source for schema compliance, cross-references, and conventions. \" +\n \"With a path argument or --plugins flag, validates plugin(s) instead.\";\n\n static examples = [\n {\n description: \"Validate all YAML schemas\",\n command: \"<%= config.bin %> <%= command.id %>\",\n },\n {\n description: \"Validate a skills source repository\",\n command: \"<%= config.bin %> <%= command.id %> --source .\",\n },\n {\n description: \"Validate a remote skills source\",\n command: \"<%= config.bin %> <%= command.id %> --source github:acme-corp/skills\",\n },\n {\n description: \"Validate a specific plugin\",\n command: \"<%= config.bin %> <%= command.id %> ./path/to/plugin\",\n },\n {\n description: \"Validate all plugins in a directory\",\n command: \"<%= config.bin %> <%= command.id %> ./plugins --all\",\n },\n {\n description: \"Validate plugins with verbose output\",\n command: \"<%= config.bin %> <%= command.id %> --plugins --verbose\",\n },\n ];\n\n static args = {\n path: Args.string({\n description: \"Path to plugin or plugins directory to validate\",\n required: false,\n }),\n };\n\n static flags = {\n ...BaseCommand.baseFlags,\n verbose: Flags.boolean({\n char: \"v\",\n description: \"Enable verbose logging\",\n default: false,\n }),\n all: Flags.boolean({\n char: \"a\",\n description: \"Validate all plugins in directory\",\n default: false,\n }),\n plugins: Flags.boolean({\n char: \"p\",\n description: \"Validate plugins instead of schemas\",\n default: false,\n }),\n };\n\n async run(): Promise<void> {\n const { args, flags } = await this.parse(Validate);\n\n if (flags.source) {\n await this.validateSkillsSource(flags.source);\n } else if (args.path || flags.plugins) {\n await this.validatePlugins(args.path, flags.verbose, flags.all);\n } else {\n await this.validateSchemas();\n }\n }\n\n private async validateSchemas(): Promise<void> {\n this.log(\"\");\n this.log(\"Validating all schemas\");\n this.log(\"\");\n\n try {\n const result = await validateAllSchemas();\n\n const summary = result.valid\n ? `Done: ${result.summary.validFiles}/${result.summary.totalFiles} files valid`\n : `Done: ${result.summary.invalidFiles} invalid files`;\n\n this.log(summary);\n printValidationResults(result);\n\n if (!result.valid) {\n this.exit(EXIT_CODES.ERROR);\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n\n private async validatePlugins(\n pluginPath: string | undefined,\n verbose: boolean,\n all: boolean,\n ): Promise<void> {\n const targetPath = pluginPath ? path.resolve(pluginPath) : process.cwd();\n\n if (all) {\n await this.validateAllPluginsInDirectory(targetPath, verbose);\n } else {\n await this.validateSinglePlugin(targetPath, verbose);\n }\n }\n\n private async validateAllPluginsInDirectory(targetPath: string, verbose: boolean): Promise<void> {\n this.log(\"\");\n this.log(`Validating all plugins in: ${targetPath}`);\n this.log(\"\");\n\n try {\n const result = await validateAllPlugins(targetPath);\n\n const summary = result.valid\n ? `Done: ${result.summary.valid}/${result.summary.total} plugins valid`\n : `Done: ${result.summary.invalid} invalid plugins`;\n\n this.log(summary);\n\n this.log(\"\");\n this.log(\" Plugin Validation Summary:\");\n this.log(\" -------------------------\");\n this.log(` Total plugins: ${result.summary.total}`);\n this.log(` Valid: ${result.summary.valid}`);\n this.log(` Invalid: ${result.summary.invalid}`);\n this.log(` With warnings: ${result.summary.withWarnings}`);\n\n for (const { name, result: pluginResult } of result.results) {\n printPluginValidationResult(name, pluginResult, verbose);\n }\n\n if (result.valid) {\n this.log(\"\");\n this.logSuccess(\"All plugins validated successfully\");\n this.log(\"\");\n } else {\n this.log(\"\");\n this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n\n private async validateSinglePlugin(targetPath: string, _verbose: boolean): Promise<void> {\n this.log(\"\");\n this.log(`Validating plugin: ${targetPath}`);\n this.log(\"\");\n\n try {\n const result = await validatePlugin(targetPath);\n\n const summary = result.valid ? \"Done: Plugin is valid\" : \"Done: Plugin has errors\";\n\n this.log(summary);\n\n printPluginValidationResult(path.basename(targetPath), result, true);\n\n if (result.valid && result.warnings.length === 0) {\n this.log(\"\");\n this.logSuccess(\"Plugin validated successfully\");\n this.log(\"\");\n } else if (result.valid) {\n this.log(\"\");\n this.logWarning(\"Plugin valid with warnings\");\n this.log(\"\");\n } else {\n this.log(\"\");\n this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n\n private async validateSkillsSource(source: string): Promise<void> {\n this.log(\"\");\n this.log(`Validating source: ${source}`);\n this.log(\"\");\n\n try {\n const result = await validateSource(source);\n\n this.log(`Checked ${result.skillCount} skill(s)`);\n this.log(\"\");\n\n for (const issue of result.issues) {\n const prefix = issue.severity === \"error\" ? \"ERROR\" : \"WARN\";\n this.log(` [${prefix}] ${issue.file}: ${issue.message}`);\n }\n\n if (result.issues.length > 0) {\n this.log(\"\");\n }\n\n this.log(`Result: ${result.errorCount} error(s), ${result.warningCount} warning(s)`);\n\n if (result.errorCount > 0) {\n this.log(\"\");\n this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });\n } else if (result.warningCount > 0) {\n this.log(\"\");\n this.logWarning(\"Source valid with warnings\");\n this.log(\"\");\n } else {\n this.log(\"\");\n this.logSuccess(\"Source validated successfully\");\n this.log(\"\");\n }\n } catch (error) {\n const message = getErrorMessage(error);\n this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });\n }\n }\n}\n","import { sumBy } from \"remeda\";\nimport { z } from \"zod\";\nimport path from \"path\";\nimport { getErrorMessage } from \"../utils/errors\";\nimport { readFile, fileExists } from \"../utils/fs\";\nimport { parse as parseYaml } from \"yaml\";\nimport fg from \"fast-glob\";\nimport { extractFrontmatter } from \"../utils/frontmatter\";\nimport { log } from \"../utils/logger\";\nimport {\n skillsMatrixConfigSchema,\n metadataValidationSchema,\n stackConfigValidationSchema,\n skillFrontmatterValidationSchema,\n agentFrontmatterValidationSchema,\n agentYamlGenerationSchema,\n stacksConfigSchema,\n projectSourceConfigSchema,\n pluginManifestSchema,\n} from \"./schemas\";\nimport { CLAUDE_DIR, CLAUDE_SRC_DIR, STANDARD_FILES } from \"../consts\";\n\ntype FileValidationError = {\n file: string;\n errors: string[];\n};\n\ntype SchemaValidationResult = {\n schemaName: string;\n valid: boolean;\n totalFiles: number;\n validFiles: number;\n invalidFiles: FileValidationError[];\n};\n\nexport type FullValidationResult = {\n valid: boolean;\n results: SchemaValidationResult[];\n summary: {\n totalSchemas: number;\n totalFiles: number;\n validFiles: number;\n invalidFiles: number;\n };\n};\n\ntype ContentExtractor = (content: string) => unknown | null;\n\ntype ValidationTarget = {\n name: string;\n schema: z.ZodType<unknown>;\n pattern: string;\n baseDir: string;\n extractor?: ContentExtractor;\n};\n\nconst VALIDATION_TARGETS: ValidationTarget[] = [\n {\n name: \"Skills Matrix\",\n schema: skillsMatrixConfigSchema,\n pattern: STANDARD_FILES.SKILLS_MATRIX_YAML,\n baseDir: \"src/config\",\n },\n {\n name: \"Skill Metadata\",\n schema: metadataValidationSchema,\n pattern: `**/${STANDARD_FILES.METADATA_YAML}`,\n baseDir: \"src/skills\",\n },\n {\n name: \"Stack Skill Metadata\",\n schema: metadataValidationSchema,\n pattern: `**/skills/**/${STANDARD_FILES.METADATA_YAML}`,\n baseDir: \"src/stacks\",\n },\n {\n name: \"Stack Config\",\n schema: stackConfigValidationSchema,\n pattern: `*/${STANDARD_FILES.CONFIG_YAML}`,\n baseDir: \"src/stacks\",\n },\n {\n name: \"Agent Definition\",\n schema: agentYamlGenerationSchema,\n pattern: `**/${STANDARD_FILES.AGENT_YAML}`,\n baseDir: \"src/agents\",\n },\n {\n name: \"Skill Frontmatter\",\n schema: skillFrontmatterValidationSchema,\n pattern: `**/${STANDARD_FILES.SKILL_MD}`,\n baseDir: \"src/skills\",\n extractor: extractFrontmatter,\n },\n {\n name: \"Stack Skill Frontmatter\",\n schema: skillFrontmatterValidationSchema,\n pattern: `**/skills/**/${STANDARD_FILES.SKILL_MD}`,\n baseDir: \"src/stacks\",\n extractor: extractFrontmatter,\n },\n {\n name: \"Stacks Config\",\n schema: stacksConfigSchema,\n pattern: \"stacks.yaml\",\n baseDir: \"config\",\n },\n {\n name: \"Project Source Config\",\n schema: projectSourceConfigSchema,\n pattern: STANDARD_FILES.CONFIG_YAML,\n baseDir: CLAUDE_SRC_DIR,\n },\n {\n name: \"Project Skill Metadata\",\n schema: metadataValidationSchema,\n pattern: `*/${STANDARD_FILES.METADATA_YAML}`,\n baseDir: `${CLAUDE_DIR}/skills`,\n },\n {\n name: \"Project Skill Frontmatter\",\n schema: skillFrontmatterValidationSchema,\n pattern: `*/${STANDARD_FILES.SKILL_MD}`,\n baseDir: `${CLAUDE_DIR}/skills`,\n extractor: extractFrontmatter,\n },\n {\n name: \"Project Agent Frontmatter\",\n schema: agentFrontmatterValidationSchema,\n pattern: \"*.md\",\n baseDir: `${CLAUDE_DIR}/agents`,\n extractor: extractFrontmatter,\n },\n {\n name: \"Plugin Manifest\",\n schema: pluginManifestSchema,\n pattern: `*/${STANDARD_FILES.PLUGIN_JSON}`,\n baseDir: `${CLAUDE_DIR}/plugins`,\n extractor: (content: string) => JSON.parse(content) as unknown,\n },\n];\n\nfunction formatZodErrors(error: z.ZodError): string[] {\n return error.issues.map((issue) => {\n const path = issue.path.join(\".\");\n if (issue.code === \"unrecognized_keys\") {\n return `Unrecognized key: \"${issue.keys.join('\", \"')}\"`;\n }\n return path ? `${path}: ${issue.message}` : issue.message;\n });\n}\n\nasync function validateFile(\n filePath: string,\n schema: z.ZodType<unknown>,\n extractor?: ContentExtractor,\n): Promise<{ valid: boolean; errors: string[] }> {\n try {\n if (!(await fileExists(filePath))) {\n return { valid: false, errors: [`File not found: ${filePath}`] };\n }\n\n const content = await readFile(filePath);\n\n let parsed: unknown;\n if (extractor) {\n parsed = extractor(content);\n if (parsed === null) {\n return {\n valid: false,\n errors: [\"Failed to extract content (no valid frontmatter found)\"],\n };\n }\n } else {\n parsed = parseYaml(content);\n }\n\n const result = schema.safeParse(parsed);\n\n if (result.success) {\n return { valid: true, errors: [] };\n }\n\n return { valid: false, errors: formatZodErrors(result.error) };\n } catch (error) {\n const message = getErrorMessage(error);\n return { valid: false, errors: [`Failed to parse content: ${message}`] };\n }\n}\n\nasync function validateTarget(\n target: ValidationTarget,\n rootDir: string = process.cwd(),\n): Promise<SchemaValidationResult> {\n const baseDir = path.join(rootDir, target.baseDir);\n const pattern = path.join(baseDir, target.pattern);\n const files = await fg(pattern, { absolute: true });\n\n const result: SchemaValidationResult = {\n schemaName: target.name,\n valid: true,\n totalFiles: files.length,\n validFiles: 0,\n invalidFiles: [],\n };\n\n if (files.length === 0) {\n return result;\n }\n\n for (const file of files) {\n const validation = await validateFile(file, target.schema, target.extractor);\n const relativePath = path.relative(rootDir, file);\n\n if (validation.valid) {\n result.validFiles++;\n } else {\n result.valid = false;\n result.invalidFiles.push({\n file: relativePath,\n errors: validation.errors,\n });\n }\n }\n\n return result;\n}\n\nexport async function validateAllSchemas(\n rootDir: string = process.cwd(),\n): Promise<FullValidationResult> {\n const results: SchemaValidationResult[] = [];\n\n for (const target of VALIDATION_TARGETS) {\n const result = await validateTarget(target, rootDir);\n results.push(result);\n }\n\n const summary = {\n totalSchemas: results.length,\n totalFiles: sumBy(results, (r) => r.totalFiles),\n validFiles: sumBy(results, (r) => r.validFiles),\n invalidFiles: sumBy(results, (r) => r.invalidFiles.length),\n };\n\n return {\n valid: results.every((r) => r.valid),\n results,\n summary,\n };\n}\n\nexport function printValidationResults(result: FullValidationResult): void {\n log(\"\\n Schema Validation Summary:\");\n log(\" ─────────────────────────\");\n log(` Total schemas checked: ${result.summary.totalSchemas}`);\n log(` Total files: ${result.summary.totalFiles}`);\n log(` Valid: ${result.summary.validFiles}`);\n log(` Invalid: ${result.summary.invalidFiles}`);\n\n for (const schemaResult of result.results) {\n if (schemaResult.totalFiles === 0) continue;\n\n const status = schemaResult.valid ? \"✓\" : \"✗\";\n log(\n `\\n ${status} ${schemaResult.schemaName}: ${schemaResult.validFiles}/${schemaResult.totalFiles} valid`,\n );\n\n if (schemaResult.invalidFiles.length > 0) {\n for (const file of schemaResult.invalidFiles) {\n log(`\\n ${file.file}:`);\n file.errors.forEach((e) => log(` - ${e}`));\n }\n }\n }\n\n if (result.valid) {\n log(\"\\n ✓ All schemas validated successfully\\n\");\n } else {\n log(\"\\n ✗ Validation failed\\n\");\n }\n}\n","import path from \"path\";\nimport { parse as parseYaml } from \"yaml\";\nimport { glob, readFile, fileExists, directoryExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport { STANDARD_FILES } from \"../consts\";\nimport { metadataValidationSchema, formatZodErrors, SKILL_ID_PATTERN } from \"./schemas\";\nimport { parseFrontmatter } from \"./loading/loader\";\nimport { loadProjectSourceConfig } from \"./configuration\";\nimport {\n checkMatrixHealth,\n extractAllSkills,\n loadSkillsMatrix,\n mergeMatrixWithSkills,\n} from \"./matrix\";\n\nexport type SourceValidationIssue = {\n severity: \"error\" | \"warning\";\n file: string;\n message: string;\n};\n\nexport type SourceValidationResult = {\n issues: SourceValidationIssue[];\n skillCount: number;\n errorCount: number;\n warningCount: number;\n};\n\n/** Checks if a key uses snake_case (has underscore between lowercase letters) */\nfunction isSnakeCase(key: string): boolean {\n return /[a-z]_[a-z]/.test(key);\n}\n\n/**\n * Validates a skills source repository for metadata correctness.\n *\n * Checks:\n * 1. Every metadata.yaml against the strict validation schema\n * 2. cliName format and directory name consistency\n * 3. category values against known domain-prefixed patterns\n * 4. Cross-references resolve to existing skill IDs (via checkMatrixHealth)\n * 5. categoryExclusive presence when required\n * 6. camelCase key convention (no snake_case)\n * 7. Every skill directory has both SKILL.md and metadata.yaml\n */\nexport async function validateSource(sourcePath: string): Promise<SourceValidationResult> {\n const issues: SourceValidationIssue[] = [];\n\n const resolvedPath = path.isAbsolute(sourcePath) ? sourcePath : path.resolve(sourcePath);\n\n if (!(await directoryExists(resolvedPath))) {\n issues.push({\n severity: \"error\",\n file: resolvedPath,\n message: \"Source directory does not exist\",\n });\n return buildResult(issues, 0);\n }\n\n const sourceProjectConfig = await loadProjectSourceConfig(resolvedPath);\n const skillsDirRelPath = sourceProjectConfig?.skillsDir ?? \"src/skills\";\n const skillsDir = path.join(resolvedPath, skillsDirRelPath);\n\n if (!(await directoryExists(skillsDir))) {\n issues.push({\n severity: \"error\",\n file: skillsDir,\n message: \"Skills directory does not exist\",\n });\n return buildResult(issues, 0);\n }\n\n // Phase 1: Check every skill directory has both SKILL.md and metadata.yaml\n const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir);\n const metadataFiles = await glob(`**/${STANDARD_FILES.METADATA_YAML}`, skillsDir);\n\n const skillMdDirs = new Set(skillMdFiles.map((f) => path.dirname(f)));\n const metadataDirs = new Set(metadataFiles.map((f) => path.dirname(f)));\n\n // Dirs with SKILL.md but no metadata.yaml\n for (const dir of skillMdDirs) {\n if (!metadataDirs.has(dir)) {\n issues.push({\n severity: \"error\",\n file: path.join(skillsDir, dir),\n message: `Missing ${STANDARD_FILES.METADATA_YAML} — skill directory has ${STANDARD_FILES.SKILL_MD} but no metadata`,\n });\n }\n }\n\n // Dirs with metadata.yaml but no SKILL.md\n for (const dir of metadataDirs) {\n if (!skillMdDirs.has(dir)) {\n issues.push({\n severity: \"error\",\n file: path.join(skillsDir, dir),\n message: `Missing ${STANDARD_FILES.SKILL_MD} — skill directory has ${STANDARD_FILES.METADATA_YAML} but no SKILL.md`,\n });\n }\n }\n\n // Phase 2: Validate each metadata.yaml against strict schema and conventions\n let skillCount = 0;\n for (const metadataFile of metadataFiles) {\n const metadataPath = path.join(skillsDir, metadataFile);\n const skillDir = path.dirname(metadataFile);\n const skillMdPath = path.join(skillsDir, skillDir, STANDARD_FILES.SKILL_MD);\n\n if (!(await fileExists(skillMdPath))) {\n // Already reported above\n continue;\n }\n\n skillCount++;\n const relPath = path.join(skillsDirRelPath, metadataFile);\n\n // Read and parse metadata.yaml\n let rawMetadata: unknown;\n try {\n const metadataContent = await readFile(metadataPath);\n rawMetadata = parseYaml(metadataContent);\n } catch (error) {\n issues.push({\n severity: \"error\",\n file: relPath,\n message: \"Failed to parse YAML\",\n });\n continue;\n }\n\n // Check for snake_case keys\n if (rawMetadata && typeof rawMetadata === \"object\" && !Array.isArray(rawMetadata)) {\n for (const key of Object.keys(rawMetadata as Record<string, unknown>)) {\n if (isSnakeCase(key)) {\n issues.push({\n severity: \"error\",\n file: relPath,\n message: `Key '${key}' uses snake_case — use camelCase instead`,\n });\n }\n }\n }\n\n // Validate against strict metadata schema\n const result = metadataValidationSchema.safeParse(rawMetadata);\n if (!result.success) {\n for (const issue of result.error.issues) {\n const fieldPath = issue.path.join(\".\");\n issues.push({\n severity: \"error\",\n file: relPath,\n message: `${fieldPath}: ${issue.message}`,\n });\n }\n continue;\n }\n\n const metadata = result.data;\n\n // Check cliName matches directory name\n const dirName = path.basename(skillDir);\n if (metadata.cliName !== dirName) {\n issues.push({\n severity: \"warning\",\n file: relPath,\n message: `cliName '${metadata.cliName}' does not match directory name '${dirName}'`,\n });\n }\n\n // Parse SKILL.md frontmatter and check name matches cliName\n const skillMdContent = await readFile(skillMdPath);\n const frontmatter = parseFrontmatter(skillMdContent, skillMdPath);\n if (frontmatter) {\n if (!SKILL_ID_PATTERN.test(frontmatter.name)) {\n issues.push({\n severity: \"warning\",\n file: path.join(skillsDirRelPath, skillDir, STANDARD_FILES.SKILL_MD),\n message: `SKILL.md name '${frontmatter.name}' does not match expected skill ID pattern (domain-subcategory-name)`,\n });\n }\n }\n\n // Check category follows domain-prefixed pattern\n if (\n metadata.category &&\n !/^(web|api|cli|mobile|infra|meta|security|shared)-.+$/.test(metadata.category)\n ) {\n issues.push({\n severity: \"warning\",\n file: relPath,\n message: `Category '${metadata.category}' does not follow domain-prefixed pattern (e.g., 'web-framework', 'api-database')`,\n });\n }\n }\n\n // Phase 3: Cross-reference validation via matrix health check\n try {\n const matrixRelPath = sourceProjectConfig?.matrixFile ?? \"config/skills-matrix.yaml\";\n const matrixPath = path.join(resolvedPath, matrixRelPath);\n\n if (await fileExists(matrixPath)) {\n const matrix = await loadSkillsMatrix(matrixPath);\n const skills = await extractAllSkills(skillsDir);\n const mergedMatrix = await mergeMatrixWithSkills(matrix, skills);\n const healthIssues = checkMatrixHealth(mergedMatrix);\n\n for (const healthIssue of healthIssues) {\n issues.push({\n severity: healthIssue.severity,\n file: matrixRelPath,\n message: healthIssue.details,\n });\n }\n } else {\n verbose(`No matrix file at '${matrixPath}' — skipping cross-reference validation`);\n }\n } catch (error) {\n issues.push({\n severity: \"warning\",\n file: \"config/skills-matrix.yaml\",\n message: `Cross-reference validation skipped: failed to load matrix`,\n });\n }\n\n return buildResult(issues, skillCount);\n}\n\nfunction buildResult(issues: SourceValidationIssue[], skillCount: number): SourceValidationResult {\n const errorCount = issues.filter((i) => i.severity === \"error\").length;\n const warningCount = issues.filter((i) => i.severity === \"warning\").length;\n return { issues, skillCount, errorCount, warningCount };\n}\n"],"mappings":"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","names":["path","path","path","parseYaml","path","parseYaml","verbose","path"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
SkillSearch
|
|
4
|
-
} from "../../chunk-
|
|
5
|
-
import "../../chunk-
|
|
4
|
+
} from "../../chunk-EZ46ZTAQ.js";
|
|
5
|
+
import "../../chunk-N5OCAAXY.js";
|
|
6
6
|
import "../../chunk-U3IGFMCY.js";
|
|
7
|
-
import "../../chunk-
|
|
7
|
+
import "../../chunk-FTD5Z6QD.js";
|
|
8
8
|
import "../../chunk-DHET7RCE.js";
|
|
9
9
|
export {
|
|
10
10
|
SkillSearch
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
CategoryGrid
|
|
4
|
-
} from "../../chunk-
|
|
4
|
+
} from "../../chunk-3APMMQUA.js";
|
|
5
5
|
import "../../chunk-GG4BSB6S.js";
|
|
6
|
-
import "../../chunk-
|
|
6
|
+
import "../../chunk-FTD5Z6QD.js";
|
|
7
7
|
import "../../chunk-DHET7RCE.js";
|
|
8
8
|
export {
|
|
9
9
|
CategoryGrid
|
|
@@ -21,9 +21,9 @@ import {
|
|
|
21
21
|
} from "../../chunk-XY3XDVMI.js";
|
|
22
22
|
import {
|
|
23
23
|
CategoryGrid
|
|
24
|
-
} from "../../chunk-
|
|
24
|
+
} from "../../chunk-3APMMQUA.js";
|
|
25
25
|
import "../../chunk-GG4BSB6S.js";
|
|
26
|
-
import "../../chunk-
|
|
26
|
+
import "../../chunk-FTD5Z6QD.js";
|
|
27
27
|
import {
|
|
28
28
|
init_esm_shims
|
|
29
29
|
} from "../../chunk-DHET7RCE.js";
|
|
@@ -1068,5 +1068,125 @@ describe("CategoryGrid component", () => {
|
|
|
1068
1068
|
globalExpect(output).toContain("Category 3");
|
|
1069
1069
|
});
|
|
1070
1070
|
});
|
|
1071
|
+
describe("selection counter", () => {
|
|
1072
|
+
it("should show 'X of 1' for exclusive categories with no selection", () => {
|
|
1073
|
+
const categories = [
|
|
1074
|
+
createCategory(
|
|
1075
|
+
"web-framework",
|
|
1076
|
+
"Framework",
|
|
1077
|
+
[
|
|
1078
|
+
createOption("web-framework-react", "React"),
|
|
1079
|
+
createOption("web-framework-vue-composition-api", "Vue")
|
|
1080
|
+
],
|
|
1081
|
+
{ exclusive: true }
|
|
1082
|
+
)
|
|
1083
|
+
];
|
|
1084
|
+
const { lastFrame, unmount } = renderGrid({ categories });
|
|
1085
|
+
cleanup = unmount;
|
|
1086
|
+
const output = lastFrame();
|
|
1087
|
+
globalExpect(output).toContain("(0 of 1)");
|
|
1088
|
+
});
|
|
1089
|
+
it("should show '1 of 1' for exclusive categories with one selection", () => {
|
|
1090
|
+
const categories = [
|
|
1091
|
+
createCategory(
|
|
1092
|
+
"web-framework",
|
|
1093
|
+
"Framework",
|
|
1094
|
+
[
|
|
1095
|
+
createOption("web-framework-react", "React", { selected: true }),
|
|
1096
|
+
createOption("web-framework-vue-composition-api", "Vue")
|
|
1097
|
+
],
|
|
1098
|
+
{ exclusive: true }
|
|
1099
|
+
)
|
|
1100
|
+
];
|
|
1101
|
+
const { lastFrame, unmount } = renderGrid({ categories });
|
|
1102
|
+
cleanup = unmount;
|
|
1103
|
+
const output = lastFrame();
|
|
1104
|
+
globalExpect(output).toContain("(1 of 1)");
|
|
1105
|
+
});
|
|
1106
|
+
it("should show 'X selected' for non-exclusive categories", () => {
|
|
1107
|
+
const categories = [
|
|
1108
|
+
createCategory(
|
|
1109
|
+
"web-testing",
|
|
1110
|
+
"Testing",
|
|
1111
|
+
[
|
|
1112
|
+
createOption("web-testing-vitest", "Vitest", { selected: true }),
|
|
1113
|
+
createOption("web-testing-playwright-e2e", "Playwright", { selected: true }),
|
|
1114
|
+
createOption("web-testing-cypress-e2e", "Cypress")
|
|
1115
|
+
],
|
|
1116
|
+
{ exclusive: false }
|
|
1117
|
+
)
|
|
1118
|
+
];
|
|
1119
|
+
const { lastFrame, unmount } = renderGrid({ categories });
|
|
1120
|
+
cleanup = unmount;
|
|
1121
|
+
const output = lastFrame();
|
|
1122
|
+
globalExpect(output).toContain("(2 selected)");
|
|
1123
|
+
});
|
|
1124
|
+
it("should show '0 selected' for non-exclusive categories with no selection", () => {
|
|
1125
|
+
const categories = [
|
|
1126
|
+
createCategory(
|
|
1127
|
+
"web-testing",
|
|
1128
|
+
"Testing",
|
|
1129
|
+
[
|
|
1130
|
+
createOption("web-testing-vitest", "Vitest"),
|
|
1131
|
+
createOption("web-testing-playwright-e2e", "Playwright")
|
|
1132
|
+
],
|
|
1133
|
+
{ exclusive: false }
|
|
1134
|
+
)
|
|
1135
|
+
];
|
|
1136
|
+
const { lastFrame, unmount } = renderGrid({ categories });
|
|
1137
|
+
cleanup = unmount;
|
|
1138
|
+
const output = lastFrame();
|
|
1139
|
+
globalExpect(output).toContain("(0 selected)");
|
|
1140
|
+
});
|
|
1141
|
+
it("should hide counter in expert mode", () => {
|
|
1142
|
+
const categories = [
|
|
1143
|
+
createCategory(
|
|
1144
|
+
"web-framework",
|
|
1145
|
+
"Framework",
|
|
1146
|
+
[createOption("web-framework-react", "React", { selected: true })],
|
|
1147
|
+
{ exclusive: true }
|
|
1148
|
+
),
|
|
1149
|
+
createCategory(
|
|
1150
|
+
"web-testing",
|
|
1151
|
+
"Testing",
|
|
1152
|
+
[createOption("web-testing-vitest", "Vitest", { selected: true })],
|
|
1153
|
+
{ exclusive: false }
|
|
1154
|
+
)
|
|
1155
|
+
];
|
|
1156
|
+
const { lastFrame, unmount } = renderGrid({ categories, expertMode: true });
|
|
1157
|
+
cleanup = unmount;
|
|
1158
|
+
const output = lastFrame();
|
|
1159
|
+
globalExpect(output).not.toContain("of 1");
|
|
1160
|
+
globalExpect(output).not.toContain("selected");
|
|
1161
|
+
});
|
|
1162
|
+
it("should show correct counts for mixed exclusive and non-exclusive categories", () => {
|
|
1163
|
+
const categories = [
|
|
1164
|
+
createCategory(
|
|
1165
|
+
"web-framework",
|
|
1166
|
+
"Framework",
|
|
1167
|
+
[
|
|
1168
|
+
createOption("web-framework-react", "React", { selected: true }),
|
|
1169
|
+
createOption("web-framework-vue-composition-api", "Vue")
|
|
1170
|
+
],
|
|
1171
|
+
{ exclusive: true }
|
|
1172
|
+
),
|
|
1173
|
+
createCategory(
|
|
1174
|
+
"web-testing",
|
|
1175
|
+
"Testing",
|
|
1176
|
+
[
|
|
1177
|
+
createOption("web-testing-vitest", "Vitest", { selected: true }),
|
|
1178
|
+
createOption("web-testing-playwright-e2e", "Playwright", { selected: true }),
|
|
1179
|
+
createOption("web-testing-cypress-e2e", "Cypress")
|
|
1180
|
+
],
|
|
1181
|
+
{ exclusive: false }
|
|
1182
|
+
)
|
|
1183
|
+
];
|
|
1184
|
+
const { lastFrame, unmount } = renderGrid({ categories });
|
|
1185
|
+
cleanup = unmount;
|
|
1186
|
+
const output = lastFrame();
|
|
1187
|
+
globalExpect(output).toContain("(1 of 1)");
|
|
1188
|
+
globalExpect(output).toContain("(2 selected)");
|
|
1189
|
+
});
|
|
1190
|
+
});
|
|
1071
1191
|
});
|
|
1072
1192
|
//# sourceMappingURL=category-grid.test.js.map
|