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 CHANGED
@@ -26,12 +26,11 @@ const { skills, report } = await loadSkills({
26
26
 
27
27
  `skills` includes loaded skill payloads:
28
28
 
29
- - `meta`: parsed frontmatter object
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 { LoadedSkill, LoadSkillsConfig, IgnoredDuplicateSkill, LoadSkillsPathError, LoadSkillsPathReport, LoadSkillsReport, LoadSkillsResult, SkillScript, SkillScriptType, SkillState, SkillWarning, SkillWarningCode, } from "./types.js";
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
@@ -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,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,UAAU,EACV,YAAY,EACZ,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
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"}
@@ -1 +1 @@
1
- {"version":3,"file":"loadSkills.d.ts","sourceRoot":"","sources":["../src/loadSkills.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,EAIjB,MAAM,YAAY,CAAC;AAOpB,wBAAsB,UAAU,CAC9B,MAAM,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,gBAAgB,CAAC,CAoG3B"}
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"}
@@ -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, validateLargeReferences, } from "./validateSkill.js";
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 results = [];
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 largeReferenceWarnings = validateLargeReferences(resourceScan.referenceContents);
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
- const resolvedName = typeof skill.meta.name === "string" && skill.meta.name.trim() !== ""
41
- ? skill.meta.name.trim()
42
- : path.basename(skill.skillPath);
43
- if (typeof skill.meta.name === "string" && skill.meta.name.trim() !== "") {
44
- const normalizedName = skill.meta.name.trim().toLowerCase();
45
- const existing = includedByName.get(normalizedName);
46
- if (existing) {
47
- const ignoredEntry = {
48
- skillName: resolvedName,
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
- results.push(skill);
72
- reportItem.skillNames.push(resolvedName);
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: results,
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
- const content = await readFile(filePath, "utf8");
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" | "reference_large_without_toc" | "resource_read_error";
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 interface LoadedSkill {
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: LoadedSkill[];
61
+ skills: Record<string, Skill>;
51
62
  report: LoadSkillsReport;
52
63
  }
53
64
  export interface ParseSkillResult {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAE7C,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,6BAA6B,GAC7B,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,WAAW,WAAW;IAC1B,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,KAAK,EAAE,UAAU,CAAC;IAClB,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;CAC5D;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,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"}
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"}
@@ -1,8 +1,11 @@
1
- import type { LoadedSkill, SkillWarning } from "./types.js";
2
- export declare function applyValidationRules(skill: LoadedSkill): LoadedSkill;
3
- export declare function validateLargeReferences(references: Array<{
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
- lineCount: number;
7
- }>): SkillWarning[];
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,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAc5D,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CA8CpE;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GACtE,YAAY,EAAE,CAqBhB"}
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"}
@@ -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(skill) {
13
- const warnings = [...skill.warnings];
14
- const metaName = skill.meta.name;
15
- const metaDescription = skill.meta.description;
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(skill.content);
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 state = warnings.some((warning) => INVALID_WARNING_CODES.has(warning.code))
40
- ? "invalid"
41
- : "valid";
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
- ...skill,
44
- warnings: dedupeWarnings(warnings),
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 = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "load-skills",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Load and validate agent skills from configurable paths.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",