load-skills 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/loadSkills.d.ts.map +1 -1
- package/dist/loadSkills.js +53 -50
- package/dist/types.d.ts +16 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/validateSkill.d.ts +9 -6
- package/dist/validateSkill.d.ts.map +1 -1
- package/dist/validateSkill.js +16 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,12 +26,11 @@ const { skills, report } = await loadSkills({
|
|
|
26
26
|
|
|
27
27
|
`skills` includes loaded skill payloads:
|
|
28
28
|
|
|
29
|
-
- `
|
|
29
|
+
- shape: `Record<string, Skill>` (keyed by skill name)
|
|
30
|
+
- `meta`: typed frontmatter object with required `name` and `description`
|
|
30
31
|
- `content`: `SKILL.md` content without frontmatter
|
|
31
32
|
- `references`: absolute file paths from `references/`
|
|
32
33
|
- `scripts`: script descriptors from `scripts/` as `{ path, type }`
|
|
33
|
-
- `state`: `"valid"` or `"invalid"`
|
|
34
|
-
- `warnings`: warning objects with machine-readable `code`
|
|
35
34
|
|
|
36
35
|
`report` includes one entry per configured path:
|
|
37
36
|
|
|
@@ -42,6 +41,7 @@ const { skills, report } = await loadSkills({
|
|
|
42
41
|
- `skillNames`: included skill names from that path
|
|
43
42
|
- `error` (optional): `path_not_found` or `path_not_directory`
|
|
44
43
|
- `ignoredDuplicates`: map of skipped duplicate skills (first-find-wins), keyed by kept skill name
|
|
44
|
+
- `invalidSkills`: invalid skill entries moved out of `skills`; each item includes `warnings`
|
|
45
45
|
|
|
46
46
|
## Path Priority
|
|
47
47
|
|
|
@@ -49,6 +49,8 @@ const { skills, report } = await loadSkills({
|
|
|
49
49
|
|
|
50
50
|
## Warning codes
|
|
51
51
|
|
|
52
|
+
These warnings appear on `report.invalidSkills[*].warnings`.
|
|
53
|
+
|
|
52
54
|
- `missing_frontmatter`: `SKILL.md` is missing a valid `--- ... ---` frontmatter block.
|
|
53
55
|
- `invalid_yaml_frontmatter`: Frontmatter exists but YAML parsing failed or did not produce an object.
|
|
54
56
|
- `missing_required_meta_name`: Frontmatter is missing required `name`.
|
|
@@ -56,5 +58,4 @@ const { skills, report } = await loadSkills({
|
|
|
56
58
|
- `invalid_meta_name`: `name` is present but not a non-empty string.
|
|
57
59
|
- `invalid_meta_description`: `description` is present but not a non-empty string.
|
|
58
60
|
- `skill_md_content_size_limit_exceeded`: `SKILL.md` body exceeds 500 lines.
|
|
59
|
-
- `reference_large_without_toc`: A `references/` file exceeds 300 lines without a table-of-contents marker.
|
|
60
61
|
- `resource_read_error`: A resource file or directory could not be read during scanning.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { loadSkills } from "./loadSkills.js";
|
|
2
|
-
export type {
|
|
2
|
+
export type { IgnoredDuplicateSkill, InvalidSkill, LoadSkillsConfig, LoadSkillsPathError, LoadSkillsPathReport, LoadSkillsReport, LoadSkillsResult, Skill, SkillMeta, SkillScript, SkillScriptType, SkillWarning, SkillWarningCode, } from "./types.js";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EACV,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EACV,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,EACL,SAAS,EACT,WAAW,EACX,eAAe,EACf,YAAY,EACZ,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
package/dist/loadSkills.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadSkills.d.ts","sourceRoot":"","sources":["../src/loadSkills.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"loadSkills.d.ts","sourceRoot":"","sources":["../src/loadSkills.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,EAKjB,MAAM,YAAY,CAAC;AAIpB,wBAAsB,UAAU,CAC9B,MAAM,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,gBAAgB,CAAC,CA2G3B"}
|
package/dist/loadSkills.js
CHANGED
|
@@ -4,11 +4,12 @@ import path from "node:path";
|
|
|
4
4
|
import { discoverSkillFiles } from "./discovery.js";
|
|
5
5
|
import { parseSkillDocument } from "./parseSkill.js";
|
|
6
6
|
import { getErrorMessage, pathExists } from "./utils.js";
|
|
7
|
-
import { applyValidationRules
|
|
7
|
+
import { applyValidationRules } from "./validateSkill.js";
|
|
8
8
|
export async function loadSkills(config) {
|
|
9
9
|
const resolvedConfig = config ?? {};
|
|
10
10
|
const discovery = await discoverSkillFiles(resolvedConfig);
|
|
11
|
-
const
|
|
11
|
+
const skills = {};
|
|
12
|
+
const invalidSkills = [];
|
|
12
13
|
const reportPaths = discovery.report.map((entry) => ({ ...entry }));
|
|
13
14
|
const ignoredDuplicates = {};
|
|
14
15
|
const includedByName = new Map();
|
|
@@ -21,65 +22,74 @@ export async function loadSkills(config) {
|
|
|
21
22
|
skillWarnings.push(...parsed.warnings);
|
|
22
23
|
const resourceScan = await collectResources(skillPath);
|
|
23
24
|
skillWarnings.push(...resourceScan.warnings);
|
|
24
|
-
const
|
|
25
|
-
skillWarnings.push(...largeReferenceWarnings);
|
|
26
|
-
const skill = applyValidationRules({
|
|
25
|
+
const validated = applyValidationRules({
|
|
27
26
|
meta: parsed.meta,
|
|
28
27
|
content: parsed.content,
|
|
29
|
-
references: resourceScan.references,
|
|
30
|
-
scripts: resourceScan.scripts,
|
|
31
|
-
state: "valid",
|
|
32
28
|
warnings: skillWarnings,
|
|
33
|
-
skillPath,
|
|
34
|
-
skillFilePath,
|
|
35
29
|
});
|
|
36
30
|
const reportItem = reportPaths[discovered.pathReportIndex];
|
|
37
31
|
if (!reportItem) {
|
|
38
32
|
continue;
|
|
39
33
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
normalizedSkillName: normalizedName,
|
|
50
|
-
ignoredSkillPath: skill.skillPath,
|
|
51
|
-
ignoredSkillFilePath: skill.skillFilePath,
|
|
52
|
-
ignoredFromInputPath: discovered.inputPath,
|
|
53
|
-
keptSkillPath: existing.skillPath,
|
|
54
|
-
keptSkillFilePath: existing.skillFilePath,
|
|
55
|
-
keptFromInputPath: existing.inputPath,
|
|
56
|
-
};
|
|
57
|
-
const key = existing.skillName;
|
|
58
|
-
ignoredDuplicates[key] = [
|
|
59
|
-
...(ignoredDuplicates[key] ?? []),
|
|
60
|
-
ignoredEntry,
|
|
61
|
-
];
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
includedByName.set(normalizedName, {
|
|
65
|
-
skillName: resolvedName,
|
|
66
|
-
skillPath: skill.skillPath,
|
|
67
|
-
skillFilePath: skill.skillFilePath,
|
|
68
|
-
inputPath: discovered.inputPath,
|
|
34
|
+
if (!validated.isValid || !validated.meta) {
|
|
35
|
+
invalidSkills.push({
|
|
36
|
+
meta: parsed.meta,
|
|
37
|
+
content: parsed.content,
|
|
38
|
+
references: resourceScan.references,
|
|
39
|
+
scripts: resourceScan.scripts,
|
|
40
|
+
warnings: validated.warnings,
|
|
41
|
+
skillPath,
|
|
42
|
+
skillFilePath,
|
|
69
43
|
});
|
|
44
|
+
continue;
|
|
70
45
|
}
|
|
71
|
-
|
|
72
|
-
|
|
46
|
+
const skill = {
|
|
47
|
+
meta: validated.meta,
|
|
48
|
+
content: parsed.content,
|
|
49
|
+
references: resourceScan.references,
|
|
50
|
+
scripts: resourceScan.scripts,
|
|
51
|
+
skillPath,
|
|
52
|
+
skillFilePath,
|
|
53
|
+
};
|
|
54
|
+
const normalizedName = skill.meta.name.trim().toLowerCase();
|
|
55
|
+
const existing = includedByName.get(normalizedName);
|
|
56
|
+
if (existing) {
|
|
57
|
+
const ignoredEntry = {
|
|
58
|
+
skillName: skill.meta.name,
|
|
59
|
+
normalizedSkillName: normalizedName,
|
|
60
|
+
ignoredSkillPath: skill.skillPath,
|
|
61
|
+
ignoredSkillFilePath: skill.skillFilePath,
|
|
62
|
+
ignoredFromInputPath: discovered.inputPath,
|
|
63
|
+
keptSkillPath: existing.skillPath,
|
|
64
|
+
keptSkillFilePath: existing.skillFilePath,
|
|
65
|
+
keptFromInputPath: existing.inputPath,
|
|
66
|
+
};
|
|
67
|
+
const key = existing.skillName;
|
|
68
|
+
ignoredDuplicates[key] = [
|
|
69
|
+
...(ignoredDuplicates[key] ?? []),
|
|
70
|
+
ignoredEntry,
|
|
71
|
+
];
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
includedByName.set(normalizedName, {
|
|
75
|
+
skillName: skill.meta.name,
|
|
76
|
+
skillPath: skill.skillPath,
|
|
77
|
+
skillFilePath: skill.skillFilePath,
|
|
78
|
+
inputPath: discovered.inputPath,
|
|
79
|
+
});
|
|
80
|
+
skills[skill.meta.name] = skill;
|
|
81
|
+
reportItem.skillNames.push(skill.meta.name);
|
|
73
82
|
reportItem.count += 1;
|
|
74
83
|
}
|
|
75
84
|
for (const reportItem of reportPaths) {
|
|
76
85
|
reportItem.skillNames.sort((a, b) => a.localeCompare(b));
|
|
77
86
|
}
|
|
78
87
|
return {
|
|
79
|
-
skills
|
|
88
|
+
skills,
|
|
80
89
|
report: {
|
|
81
90
|
paths: reportPaths,
|
|
82
91
|
ignoredDuplicates,
|
|
92
|
+
invalidSkills,
|
|
83
93
|
},
|
|
84
94
|
};
|
|
85
95
|
}
|
|
@@ -89,15 +99,9 @@ async function collectResources(skillPath) {
|
|
|
89
99
|
const warnings = [];
|
|
90
100
|
const referenceFiles = await listFilesRecursively(referencesRoot, warnings);
|
|
91
101
|
const scriptFiles = await listFilesRecursively(scriptsRoot, warnings);
|
|
92
|
-
const referenceContents = [];
|
|
93
102
|
for (const filePath of referenceFiles) {
|
|
94
103
|
try {
|
|
95
|
-
|
|
96
|
-
referenceContents.push({
|
|
97
|
-
path: filePath,
|
|
98
|
-
content,
|
|
99
|
-
lineCount: content.length === 0 ? 0 : content.split(/\r?\n/).length,
|
|
100
|
-
});
|
|
104
|
+
await readFile(filePath, "utf8");
|
|
101
105
|
}
|
|
102
106
|
catch (error) {
|
|
103
107
|
warnings.push({
|
|
@@ -115,7 +119,6 @@ async function collectResources(skillPath) {
|
|
|
115
119
|
}
|
|
116
120
|
return {
|
|
117
121
|
references: referenceFiles.sort((a, b) => a.localeCompare(b)),
|
|
118
|
-
referenceContents,
|
|
119
122
|
scripts: scripts.sort((a, b) => a.path.localeCompare(b.path)),
|
|
120
123
|
warnings,
|
|
121
124
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export type SkillState = "valid" | "invalid";
|
|
2
1
|
export type SkillScriptType = "javascript" | "typescript" | "python" | "shell" | "ruby" | "other";
|
|
3
|
-
export type SkillWarningCode = "missing_frontmatter" | "invalid_yaml_frontmatter" | "missing_required_meta_name" | "missing_required_meta_description" | "invalid_meta_name" | "invalid_meta_description" | "skill_md_content_size_limit_exceeded" | "
|
|
2
|
+
export type SkillWarningCode = "missing_frontmatter" | "invalid_yaml_frontmatter" | "missing_required_meta_name" | "missing_required_meta_description" | "invalid_meta_name" | "invalid_meta_description" | "skill_md_content_size_limit_exceeded" | "resource_read_error";
|
|
4
3
|
export interface SkillWarning {
|
|
5
4
|
code: SkillWarningCode;
|
|
6
5
|
message: string;
|
|
@@ -9,12 +8,23 @@ export interface SkillScript {
|
|
|
9
8
|
path: string;
|
|
10
9
|
type: SkillScriptType;
|
|
11
10
|
}
|
|
12
|
-
export
|
|
11
|
+
export type SkillMeta = Record<string, unknown> & {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
export interface Skill {
|
|
16
|
+
meta: SkillMeta;
|
|
17
|
+
content: string;
|
|
18
|
+
references: string[];
|
|
19
|
+
scripts: SkillScript[];
|
|
20
|
+
skillPath: string;
|
|
21
|
+
skillFilePath: string;
|
|
22
|
+
}
|
|
23
|
+
export interface InvalidSkill {
|
|
13
24
|
meta: Record<string, unknown>;
|
|
14
25
|
content: string;
|
|
15
26
|
references: string[];
|
|
16
27
|
scripts: SkillScript[];
|
|
17
|
-
state: SkillState;
|
|
18
28
|
warnings: SkillWarning[];
|
|
19
29
|
skillPath: string;
|
|
20
30
|
skillFilePath: string;
|
|
@@ -45,9 +55,10 @@ export interface IgnoredDuplicateSkill {
|
|
|
45
55
|
export interface LoadSkillsReport {
|
|
46
56
|
paths: LoadSkillsPathReport[];
|
|
47
57
|
ignoredDuplicates: Record<string, IgnoredDuplicateSkill[]>;
|
|
58
|
+
invalidSkills: InvalidSkill[];
|
|
48
59
|
}
|
|
49
60
|
export interface LoadSkillsResult {
|
|
50
|
-
skills:
|
|
61
|
+
skills: Record<string, Skill>;
|
|
51
62
|
report: LoadSkillsReport;
|
|
52
63
|
}
|
|
53
64
|
export interface ParseSkillResult {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,YAAY,GACZ,YAAY,GACZ,QAAQ,GACR,OAAO,GACP,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,gBAAgB,GACxB,qBAAqB,GACrB,0BAA0B,GAC1B,4BAA4B,GAC5B,mCAAmC,GACnC,mBAAmB,GACnB,0BAA0B,GAC1B,sCAAsC,GACtC,qBAAqB,CAAC;AAE1B,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,GAAG,oBAAoB,CAAC;AAE1E,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,mBAAmB,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAC3D,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC9B,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B"}
|
package/dist/validateSkill.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function applyValidationRules(
|
|
3
|
-
|
|
4
|
-
path: string;
|
|
1
|
+
import type { SkillMeta, SkillWarning } from "./types.js";
|
|
2
|
+
export declare function applyValidationRules(input: {
|
|
3
|
+
meta: Record<string, unknown>;
|
|
5
4
|
content: string;
|
|
6
|
-
|
|
7
|
-
}
|
|
5
|
+
warnings: SkillWarning[];
|
|
6
|
+
}): {
|
|
7
|
+
warnings: SkillWarning[];
|
|
8
|
+
isValid: boolean;
|
|
9
|
+
meta?: SkillMeta;
|
|
10
|
+
};
|
|
8
11
|
//# sourceMappingURL=validateSkill.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateSkill.d.ts","sourceRoot":"","sources":["../src/validateSkill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"validateSkill.d.ts","sourceRoot":"","sources":["../src/validateSkill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAa1D,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B,GAAG;IACF,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB,CAoDA"}
|
package/dist/validateSkill.js
CHANGED
|
@@ -6,13 +6,12 @@ const INVALID_WARNING_CODES = new Set([
|
|
|
6
6
|
"invalid_meta_name",
|
|
7
7
|
"invalid_meta_description",
|
|
8
8
|
"skill_md_content_size_limit_exceeded",
|
|
9
|
-
"reference_large_without_toc",
|
|
10
9
|
"resource_read_error",
|
|
11
10
|
]);
|
|
12
|
-
export function applyValidationRules(
|
|
13
|
-
const warnings = [...
|
|
14
|
-
const metaName =
|
|
15
|
-
const metaDescription =
|
|
11
|
+
export function applyValidationRules(input) {
|
|
12
|
+
const warnings = [...input.warnings];
|
|
13
|
+
const metaName = input.meta.name;
|
|
14
|
+
const metaDescription = input.meta.description;
|
|
16
15
|
if (typeof metaName !== "string" || metaName.trim() === "") {
|
|
17
16
|
warnings.push({
|
|
18
17
|
code: typeof metaName === "undefined"
|
|
@@ -29,39 +28,27 @@ export function applyValidationRules(skill) {
|
|
|
29
28
|
message: "Frontmatter field `description` is required and must be a non-empty string.",
|
|
30
29
|
});
|
|
31
30
|
}
|
|
32
|
-
const contentLineCount = getLineCount(
|
|
31
|
+
const contentLineCount = getLineCount(input.content);
|
|
33
32
|
if (contentLineCount > 500) {
|
|
34
33
|
warnings.push({
|
|
35
34
|
code: "skill_md_content_size_limit_exceeded",
|
|
36
35
|
message: `SKILL.md body exceeds the recommended 500-line limit (${contentLineCount} lines).`,
|
|
37
36
|
});
|
|
38
37
|
}
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
const dedupedWarnings = dedupeWarnings(warnings);
|
|
39
|
+
const isValid = !dedupedWarnings.some((warning) => INVALID_WARNING_CODES.has(warning.code));
|
|
40
|
+
if (isValid) {
|
|
41
|
+
return {
|
|
42
|
+
warnings: dedupedWarnings,
|
|
43
|
+
isValid: true,
|
|
44
|
+
meta: input.meta,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
42
47
|
return {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
state,
|
|
48
|
+
warnings: dedupedWarnings,
|
|
49
|
+
isValid: false,
|
|
46
50
|
};
|
|
47
51
|
}
|
|
48
|
-
export function validateLargeReferences(references) {
|
|
49
|
-
const warnings = [];
|
|
50
|
-
for (const reference of references) {
|
|
51
|
-
if (reference.lineCount <= 300) {
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
const normalized = reference.content.toLowerCase();
|
|
55
|
-
const hasTocMarker = normalized.includes("table of contents") || normalized.includes("[toc]");
|
|
56
|
-
if (!hasTocMarker) {
|
|
57
|
-
warnings.push({
|
|
58
|
-
code: "reference_large_without_toc",
|
|
59
|
-
message: `Large reference file is missing a table of contents hint: ${reference.path}`,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return warnings;
|
|
64
|
-
}
|
|
65
52
|
function dedupeWarnings(warnings) {
|
|
66
53
|
const keys = new Set();
|
|
67
54
|
const deduped = [];
|