claude-skills-cli 0.0.21 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/{add-hook.cmd-B6iZtoPi.js → add-hook.cmd-JWw5UqA3.js} +3 -3
  2. package/dist/{add-hook.cmd-B6iZtoPi.js.map → add-hook.cmd-JWw5UqA3.js.map} +1 -1
  3. package/dist/{frontmatter-validator-DO686mla.js → dependency-validator-CQJ1hCoU.js} +159 -3
  4. package/dist/dependency-validator-CQJ1hCoU.js.map +1 -0
  5. package/dist/{doctor.cmd-CkNw6ine.js → doctor.cmd-DJpHLDCV.js} +42 -19
  6. package/dist/doctor.cmd-DJpHLDCV.js.map +1 -0
  7. package/dist/index.js +7 -7
  8. package/dist/{init.cmd-BoeuCgQP.js → init.cmd-BdqImX8b.js} +3 -3
  9. package/dist/{init.cmd-BoeuCgQP.js.map → init.cmd-BdqImX8b.js.map} +1 -1
  10. package/dist/{install.cmd-CH7yZ92g.js → install.cmd-BaP8k9d2.js} +2 -2
  11. package/dist/{install.cmd-CH7yZ92g.js.map → install.cmd-BaP8k9d2.js.map} +1 -1
  12. package/dist/{output-Dz8fk6Gu.js → output-DiffPD2u.js} +4 -2
  13. package/dist/output-DiffPD2u.js.map +1 -0
  14. package/dist/{package.cmd-CwGRHdEq.js → package.cmd-BYhkheya.js} +3 -3
  15. package/dist/{package.cmd-CwGRHdEq.js.map → package.cmd-BYhkheya.js.map} +1 -1
  16. package/dist/{stats.cmd-D1ujNiDO.js → stats.cmd-Dd46qjoV.js} +37 -4
  17. package/dist/stats.cmd-Dd46qjoV.js.map +1 -0
  18. package/dist/{templates-BQTgkXfH.js → templates-fyteNbD0.js} +5 -1
  19. package/dist/templates-fyteNbD0.js.map +1 -0
  20. package/dist/{validate.cmd-CDUJDKGs.js → validate.cmd-BxF4HNsu.js} +3 -3
  21. package/dist/{validate.cmd-CDUJDKGs.js.map → validate.cmd-BxF4HNsu.js.map} +1 -1
  22. package/dist/{validator-DV5zeeel.js → validator-Dp5x-OjP.js} +11 -3
  23. package/dist/{validator-DV5zeeel.js.map → validator-Dp5x-OjP.js.map} +1 -1
  24. package/package.json +1 -1
  25. package/dist/doctor.cmd-CkNw6ine.js.map +0 -1
  26. package/dist/frontmatter-validator-DO686mla.js.map +0 -1
  27. package/dist/output-Dz8fk6Gu.js.map +0 -1
  28. package/dist/stats.cmd-D1ujNiDO.js.map +0 -1
  29. package/dist/templates-BQTgkXfH.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"package.cmd-CwGRHdEq.js","names":[],"sources":["../src/commands/package.ts","../src/commands/package.cmd.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync, statSync } from 'node:fs';\nimport { basename, join, resolve } from 'node:path';\nimport { SkillValidator } from '../core/validator.js';\nimport type { PackageOptions } from '../types.js';\nimport { ensure_dir } from '../utils/fs.js';\nimport {\n\terror,\n\tpackage_,\n\tsearch,\n\tsuccess,\n\tupload,\n\twarning,\n} from '../utils/output.js';\n\nfunction validate_skill(skill_path: string): boolean {\n\tsearch('Validating skill...');\n\n\tconst validator = new SkillValidator(skill_path);\n\tconst result = validator.validate_all();\n\n\tif (result.errors.length > 0) {\n\t\tconsole.log('\\n❌ Errors:');\n\t\tfor (const err of result.errors) {\n\t\t\tconsole.log(` ${String(err)}`);\n\t\t}\n\t}\n\n\tif (result.warnings.length > 0) {\n\t\tconsole.log('\\n⚠️ Warnings:');\n\t\tfor (const warn of result.warnings) {\n\t\t\tconsole.log(` ${warn}`);\n\t\t}\n\t}\n\n\tif (result.is_valid) {\n\t\tsuccess('Skill is valid!');\n\t\tconsole.log('');\n\t\treturn true;\n\t} else {\n\t\terror('Validation failed. Fix errors before packaging.');\n\t\treturn false;\n\t}\n}\n\nfunction package_skill(\n\tskill_path: string,\n\toutput_dir: string,\n): string {\n\tconst skill_name = basename(skill_path);\n\tconst output_file = resolve(output_dir, `${skill_name}.zip`);\n\n\tpackage_(`Packaging skill: ${skill_name}`);\n\n\tensure_dir(output_dir);\n\n\t// Remove existing zip if present\n\tif (existsSync(output_file)) {\n\t\texecSync(`rm ${output_file}`);\n\t}\n\n\t// Use system zip command — available on all target platforms\n\tconst parent_dir = resolve(skill_path, '..');\n\texecSync(\n\t\t`cd \"${parent_dir}\" && zip -r \"${output_file}\" \"${skill_name}\" -x '${skill_name}/.*' '${skill_name}/*.swp' '${skill_name}/*~' '${skill_name}/.DS_Store'`,\n\t);\n\n\treturn output_file;\n}\n\nexport async function package_command(\n\toptions: PackageOptions,\n): Promise<void> {\n\tconst { skill_path, output, skip_validation } = options;\n\n\t// Validate path\n\tif (!existsSync(skill_path)) {\n\t\terror(`Skill directory does not exist: ${skill_path}`);\n\t\tprocess.exit(1);\n\t}\n\n\tconst stats = statSync(skill_path);\n\tif (!stats.isDirectory()) {\n\t\terror(`Path is not a directory: ${skill_path}`);\n\t\tprocess.exit(1);\n\t}\n\n\t// Check for SKILL.md\n\tif (!existsSync(join(skill_path, 'SKILL.md'))) {\n\t\terror(`SKILL.md not found in ${skill_path}`);\n\t\tprocess.exit(1);\n\t}\n\n\t// Validate skill\n\tif (!skip_validation) {\n\t\tif (!validate_skill(skill_path)) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Package skill\n\ttry {\n\t\tconst output_dir = output || 'dist';\n\t\tconst output_file = package_skill(skill_path, output_dir);\n\n\t\t// Print success\n\t\tconst file_stats = statSync(output_file);\n\t\tconst size_kb = file_stats.size / 1024;\n\n\t\tconsole.log('');\n\t\tsuccess('Skill packaged successfully!');\n\t\tconsole.log(` File: ${output_file}`);\n\t\tconsole.log(` Size: ${size_kb.toFixed(1)} KB`);\n\t\tconsole.log('');\n\t\twarning(\n\t\t\t'Note: ZIP packaging is not an official Claude Code distribution method.',\n\t\t);\n\t\tconsole.log(\n\t\t\t' Official distribution uses the plugin system (/plugin install)',\n\t\t);\n\t\tconsole.log(' or direct file placement in .claude/skills/.');\n\t\tconsole.log(' See: https://code.claude.com/docs/en/plugins');\n\t\tconsole.log('');\n\t\tupload(\n\t\t\t'Upload to Claude.ai: Settings > Features > Skills > Upload',\n\t\t);\n\t} catch (err) {\n\t\terror(`Failed to package skill: ${String(err)}`);\n\t\tprocess.exit(1);\n\t}\n}\n","import { defineCommand } from 'citty';\nimport { package_command } from './package.js';\n\nexport default defineCommand({\n\tmeta: { name: 'package', description: 'Package a skill to zip' },\n\targs: {\n\t\tskill_path: {\n\t\t\ttype: 'positional',\n\t\t\tdescription: 'Path to skill directory',\n\t\t\trequired: true,\n\t\t},\n\t\toutput: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Output path for zip file',\n\t\t},\n\t\t'skip-validation': {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Skip validation before packaging',\n\t\t},\n\t},\n\tasync run({ args }) {\n\t\tawait package_command({\n\t\t\tskill_path: args.skill_path,\n\t\t\toutput: args.output,\n\t\t\tskip_validation: args['skip-validation'],\n\t\t});\n\t},\n});\n"],"mappings":";;;;;;;;AAeA,SAAS,eAAe,YAA6B;AACpD,QAAO,sBAAsB;CAG7B,MAAM,SADY,IAAI,eAAe,WAAW,CACvB,cAAc;AAEvC,KAAI,OAAO,OAAO,SAAS,GAAG;AAC7B,UAAQ,IAAI,cAAc;AAC1B,OAAK,MAAM,OAAO,OAAO,OACxB,SAAQ,IAAI,KAAK,OAAO,IAAI,GAAG;;AAIjC,KAAI,OAAO,SAAS,SAAS,GAAG;AAC/B,UAAQ,IAAI,kBAAkB;AAC9B,OAAK,MAAM,QAAQ,OAAO,SACzB,SAAQ,IAAI,KAAK,OAAO;;AAI1B,KAAI,OAAO,UAAU;AACpB,UAAQ,kBAAkB;AAC1B,UAAQ,IAAI,GAAG;AACf,SAAO;QACD;AACN,QAAM,kDAAkD;AACxD,SAAO;;;AAIT,SAAS,cACR,YACA,YACS;CACT,MAAM,aAAa,SAAS,WAAW;CACvC,MAAM,cAAc,QAAQ,YAAY,GAAG,WAAW,MAAM;AAE5D,UAAS,oBAAoB,aAAa;AAE1C,YAAW,WAAW;AAGtB,KAAI,WAAW,YAAY,CAC1B,UAAS,MAAM,cAAc;AAK9B,UACC,OAFkB,QAAQ,YAAY,KAAK,CAEzB,eAAe,YAAY,KAAK,WAAW,QAAQ,WAAW,QAAQ,WAAW,WAAW,WAAW,QAAQ,WAAW,aAC5I;AAED,QAAO;;AAGR,eAAsB,gBACrB,SACgB;CAChB,MAAM,EAAE,YAAY,QAAQ,oBAAoB;AAGhD,KAAI,CAAC,WAAW,WAAW,EAAE;AAC5B,QAAM,mCAAmC,aAAa;AACtD,UAAQ,KAAK,EAAE;;AAIhB,KAAI,CADU,SAAS,WAAW,CACvB,aAAa,EAAE;AACzB,QAAM,4BAA4B,aAAa;AAC/C,UAAQ,KAAK,EAAE;;AAIhB,KAAI,CAAC,WAAW,KAAK,YAAY,WAAW,CAAC,EAAE;AAC9C,QAAM,yBAAyB,aAAa;AAC5C,UAAQ,KAAK,EAAE;;AAIhB,KAAI,CAAC;MACA,CAAC,eAAe,WAAW,CAC9B,SAAQ,KAAK,EAAE;;AAKjB,KAAI;EAEH,MAAM,cAAc,cAAc,YADf,UAAU,OAC4B;EAIzD,MAAM,UADa,SAAS,YAAY,CACb,OAAO;AAElC,UAAQ,IAAI,GAAG;AACf,UAAQ,+BAA+B;AACvC,UAAQ,IAAI,YAAY,cAAc;AACtC,UAAQ,IAAI,YAAY,QAAQ,QAAQ,EAAE,CAAC,KAAK;AAChD,UAAQ,IAAI,GAAG;AACf,UACC,0EACA;AACD,UAAQ,IACP,oEACA;AACD,UAAQ,IAAI,kDAAkD;AAC9D,UAAQ,IAAI,kDAAkD;AAC9D,UAAQ,IAAI,GAAG;AACf,SACC,6DACA;UACO,KAAK;AACb,QAAM,4BAA4B,OAAO,IAAI,GAAG;AAChD,UAAQ,KAAK,EAAE;;;;;AC7HjB,IAAA,sBAAe,cAAc;CAC5B,MAAM;EAAE,MAAM;EAAW,aAAa;EAA0B;CAChE,MAAM;EACL,YAAY;GACX,MAAM;GACN,aAAa;GACb,UAAU;GACV;EACD,QAAQ;GACP,MAAM;GACN,aAAa;GACb;EACD,mBAAmB;GAClB,MAAM;GACN,aAAa;GACb;EACD;CACD,MAAM,IAAI,EAAE,QAAQ;AACnB,QAAM,gBAAgB;GACrB,YAAY,KAAK;GACjB,QAAQ,KAAK;GACb,iBAAiB,KAAK;GACtB,CAAC;;CAEH,CAAC"}
1
+ {"version":3,"file":"package.cmd-BYhkheya.js","names":[],"sources":["../src/commands/package.ts","../src/commands/package.cmd.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync, statSync } from 'node:fs';\nimport { basename, join, resolve } from 'node:path';\nimport { SkillValidator } from '../core/validator.js';\nimport type { PackageOptions } from '../types.js';\nimport { ensure_dir } from '../utils/fs.js';\nimport {\n\terror,\n\tpackage_,\n\tsearch,\n\tsuccess,\n\tupload,\n\twarning,\n} from '../utils/output.js';\n\nfunction validate_skill(skill_path: string): boolean {\n\tsearch('Validating skill...');\n\n\tconst validator = new SkillValidator(skill_path);\n\tconst result = validator.validate_all();\n\n\tif (result.errors.length > 0) {\n\t\tconsole.log('\\n❌ Errors:');\n\t\tfor (const err of result.errors) {\n\t\t\tconsole.log(` ${String(err)}`);\n\t\t}\n\t}\n\n\tif (result.warnings.length > 0) {\n\t\tconsole.log('\\n⚠️ Warnings:');\n\t\tfor (const warn of result.warnings) {\n\t\t\tconsole.log(` ${warn}`);\n\t\t}\n\t}\n\n\tif (result.is_valid) {\n\t\tsuccess('Skill is valid!');\n\t\tconsole.log('');\n\t\treturn true;\n\t} else {\n\t\terror('Validation failed. Fix errors before packaging.');\n\t\treturn false;\n\t}\n}\n\nfunction package_skill(\n\tskill_path: string,\n\toutput_dir: string,\n): string {\n\tconst skill_name = basename(skill_path);\n\tconst output_file = resolve(output_dir, `${skill_name}.zip`);\n\n\tpackage_(`Packaging skill: ${skill_name}`);\n\n\tensure_dir(output_dir);\n\n\t// Remove existing zip if present\n\tif (existsSync(output_file)) {\n\t\texecSync(`rm ${output_file}`);\n\t}\n\n\t// Use system zip command — available on all target platforms\n\tconst parent_dir = resolve(skill_path, '..');\n\texecSync(\n\t\t`cd \"${parent_dir}\" && zip -r \"${output_file}\" \"${skill_name}\" -x '${skill_name}/.*' '${skill_name}/*.swp' '${skill_name}/*~' '${skill_name}/.DS_Store'`,\n\t);\n\n\treturn output_file;\n}\n\nexport async function package_command(\n\toptions: PackageOptions,\n): Promise<void> {\n\tconst { skill_path, output, skip_validation } = options;\n\n\t// Validate path\n\tif (!existsSync(skill_path)) {\n\t\terror(`Skill directory does not exist: ${skill_path}`);\n\t\tprocess.exit(1);\n\t}\n\n\tconst stats = statSync(skill_path);\n\tif (!stats.isDirectory()) {\n\t\terror(`Path is not a directory: ${skill_path}`);\n\t\tprocess.exit(1);\n\t}\n\n\t// Check for SKILL.md\n\tif (!existsSync(join(skill_path, 'SKILL.md'))) {\n\t\terror(`SKILL.md not found in ${skill_path}`);\n\t\tprocess.exit(1);\n\t}\n\n\t// Validate skill\n\tif (!skip_validation) {\n\t\tif (!validate_skill(skill_path)) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Package skill\n\ttry {\n\t\tconst output_dir = output || 'dist';\n\t\tconst output_file = package_skill(skill_path, output_dir);\n\n\t\t// Print success\n\t\tconst file_stats = statSync(output_file);\n\t\tconst size_kb = file_stats.size / 1024;\n\n\t\tconsole.log('');\n\t\tsuccess('Skill packaged successfully!');\n\t\tconsole.log(` File: ${output_file}`);\n\t\tconsole.log(` Size: ${size_kb.toFixed(1)} KB`);\n\t\tconsole.log('');\n\t\twarning(\n\t\t\t'Note: ZIP packaging is not an official Claude Code distribution method.',\n\t\t);\n\t\tconsole.log(\n\t\t\t' Official distribution uses the plugin system (/plugin install)',\n\t\t);\n\t\tconsole.log(' or direct file placement in .claude/skills/.');\n\t\tconsole.log(' See: https://code.claude.com/docs/en/plugins');\n\t\tconsole.log('');\n\t\tupload(\n\t\t\t'Upload to Claude.ai: Settings > Features > Skills > Upload',\n\t\t);\n\t} catch (err) {\n\t\terror(`Failed to package skill: ${String(err)}`);\n\t\tprocess.exit(1);\n\t}\n}\n","import { defineCommand } from 'citty';\nimport { package_command } from './package.js';\n\nexport default defineCommand({\n\tmeta: { name: 'package', description: 'Package a skill to zip' },\n\targs: {\n\t\tskill_path: {\n\t\t\ttype: 'positional',\n\t\t\tdescription: 'Path to skill directory',\n\t\t\trequired: true,\n\t\t},\n\t\toutput: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Output path for zip file',\n\t\t},\n\t\t'skip-validation': {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Skip validation before packaging',\n\t\t},\n\t},\n\tasync run({ args }) {\n\t\tawait package_command({\n\t\t\tskill_path: args.skill_path,\n\t\t\toutput: args.output,\n\t\t\tskip_validation: args['skip-validation'],\n\t\t});\n\t},\n});\n"],"mappings":";;;;;;;;AAeA,SAAS,eAAe,YAA6B;AACpD,QAAO,sBAAsB;CAG7B,MAAM,SADY,IAAI,eAAe,WAAW,CACvB,cAAc;AAEvC,KAAI,OAAO,OAAO,SAAS,GAAG;AAC7B,UAAQ,IAAI,cAAc;AAC1B,OAAK,MAAM,OAAO,OAAO,OACxB,SAAQ,IAAI,KAAK,OAAO,IAAI,GAAG;;AAIjC,KAAI,OAAO,SAAS,SAAS,GAAG;AAC/B,UAAQ,IAAI,kBAAkB;AAC9B,OAAK,MAAM,QAAQ,OAAO,SACzB,SAAQ,IAAI,KAAK,OAAO;;AAI1B,KAAI,OAAO,UAAU;AACpB,UAAQ,kBAAkB;AAC1B,UAAQ,IAAI,GAAG;AACf,SAAO;QACD;AACN,QAAM,kDAAkD;AACxD,SAAO;;;AAIT,SAAS,cACR,YACA,YACS;CACT,MAAM,aAAa,SAAS,WAAW;CACvC,MAAM,cAAc,QAAQ,YAAY,GAAG,WAAW,MAAM;AAE5D,UAAS,oBAAoB,aAAa;AAE1C,YAAW,WAAW;AAGtB,KAAI,WAAW,YAAY,CAC1B,UAAS,MAAM,cAAc;AAK9B,UACC,OAFkB,QAAQ,YAAY,KAAK,CAEzB,eAAe,YAAY,KAAK,WAAW,QAAQ,WAAW,QAAQ,WAAW,WAAW,WAAW,QAAQ,WAAW,aAC5I;AAED,QAAO;;AAGR,eAAsB,gBACrB,SACgB;CAChB,MAAM,EAAE,YAAY,QAAQ,oBAAoB;AAGhD,KAAI,CAAC,WAAW,WAAW,EAAE;AAC5B,QAAM,mCAAmC,aAAa;AACtD,UAAQ,KAAK,EAAE;;AAIhB,KAAI,CADU,SAAS,WAAW,CACvB,aAAa,EAAE;AACzB,QAAM,4BAA4B,aAAa;AAC/C,UAAQ,KAAK,EAAE;;AAIhB,KAAI,CAAC,WAAW,KAAK,YAAY,WAAW,CAAC,EAAE;AAC9C,QAAM,yBAAyB,aAAa;AAC5C,UAAQ,KAAK,EAAE;;AAIhB,KAAI,CAAC;MACA,CAAC,eAAe,WAAW,CAC9B,SAAQ,KAAK,EAAE;;AAKjB,KAAI;EAEH,MAAM,cAAc,cAAc,YADf,UAAU,OAC4B;EAIzD,MAAM,UADa,SAAS,YAAY,CACb,OAAO;AAElC,UAAQ,IAAI,GAAG;AACf,UAAQ,+BAA+B;AACvC,UAAQ,IAAI,YAAY,cAAc;AACtC,UAAQ,IAAI,YAAY,QAAQ,QAAQ,EAAE,CAAC,KAAK;AAChD,UAAQ,IAAI,GAAG;AACf,UACC,0EACA;AACD,UAAQ,IACP,oEACA;AACD,UAAQ,IAAI,kDAAkD;AAC9D,UAAQ,IAAI,kDAAkD;AAC9D,UAAQ,IAAI,GAAG;AACf,SACC,6DACA;UACO,KAAK;AACb,QAAM,4BAA4B,OAAO,IAAI,GAAG;AAChD,UAAQ,KAAK,EAAE;;;;;AC7HjB,IAAA,sBAAe,cAAc;CAC5B,MAAM;EAAE,MAAM;EAAW,aAAa;EAA0B;CAChE,MAAM;EACL,YAAY;GACX,MAAM;GACN,aAAa;GACb,UAAU;GACV;EACD,QAAQ;GACP,MAAM;GACN,aAAa;GACb;EACD,mBAAmB;GAClB,MAAM;GACN,aAAa;GACb;EACD;CACD,MAAM,IAAI,EAAE,QAAQ;AACnB,QAAM,gBAAgB;GACrB,YAAY,KAAK;GACjB,QAAQ,KAAK;GACb,iBAAiB,KAAK;GACtB,CAAC;;CAEH,CAAC"}
@@ -1,7 +1,7 @@
1
- import { l as LIMITS, n as error } from "./output-Dz8fk6Gu.js";
2
- import { t as SkillValidator } from "./validator-DV5zeeel.js";
1
+ import { l as LIMITS, n as error } from "./output-DiffPD2u.js";
2
+ import { t as SkillValidator } from "./validator-Dp5x-OjP.js";
3
3
  import { defineCommand } from "citty";
4
- import { existsSync, readdirSync, statSync } from "node:fs";
4
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  //#region src/commands/stats.ts
7
7
  function stats_command(options) {
@@ -30,6 +30,11 @@ function stats_command(options) {
30
30
  console.log("📊 Skills Overview");
31
31
  console.log("============================================================");
32
32
  console.log(`${skills.length} skill${skills.length === 1 ? "" : "s"} found:\n`);
33
+ const tier_counts = {
34
+ simple: 0,
35
+ standard: 0,
36
+ advanced: 0
37
+ };
33
38
  for (const skill_path of skills) {
34
39
  const skill_name = skill_path.split("/").pop() || "";
35
40
  const result = new SkillValidator(skill_path).validate_all();
@@ -43,6 +48,14 @@ function stats_command(options) {
43
48
  status_text = "warnings";
44
49
  }
45
50
  console.log(`${skill_name} (${status_icon} ${status_text})`);
51
+ const skill_md_path = join(skill_path, "SKILL.md");
52
+ if (existsSync(skill_md_path)) {
53
+ const version_match = readFileSync(skill_md_path, "utf-8").match(/^version:\s*(.+)/m);
54
+ if (version_match) console.log(` Version: ${version_match[1].trim()}`);
55
+ }
56
+ const tier = classify_complexity(skill_path);
57
+ tier_counts[tier]++;
58
+ console.log(` Complexity: ${tier}`);
46
59
  if (result.stats) {
47
60
  const desc_length = result.stats.description_length;
48
61
  let desc_status = "";
@@ -98,6 +111,26 @@ function stats_command(options) {
98
111
  if (skills_with_warnings > 0) console.log(` With warnings: ${skills_with_warnings}`);
99
112
  if (invalid_skills > 0) console.log(` Invalid: ${invalid_skills}`);
100
113
  }
114
+ if (skills.length > 1) console.log(` Complexity: ${tier_counts.simple} simple, ${tier_counts.standard} standard, ${tier_counts.advanced} advanced`);
115
+ }
116
+ /**
117
+ * Classify skill complexity based on filesystem structure
118
+ */
119
+ function classify_complexity(skill_path) {
120
+ const has_scripts = existsSync(join(skill_path, "scripts")) && dir_has_files(join(skill_path, "scripts"));
121
+ const has_references = existsSync(join(skill_path, "references")) && dir_has_files(join(skill_path, "references"));
122
+ const has_assets = existsSync(join(skill_path, "assets")) && dir_has_files(join(skill_path, "assets"));
123
+ const script_count = has_scripts ? readdirSync(join(skill_path, "scripts")).length : 0;
124
+ if (has_assets || script_count > 2 || has_scripts && has_references) return "advanced";
125
+ if (has_references || has_scripts) return "standard";
126
+ return "simple";
127
+ }
128
+ function dir_has_files(dir_path) {
129
+ try {
130
+ return readdirSync(dir_path).length > 0;
131
+ } catch {
132
+ return false;
133
+ }
101
134
  }
102
135
  //#endregion
103
136
  //#region src/commands/stats.cmd.ts
@@ -118,4 +151,4 @@ var stats_cmd_default = defineCommand({
118
151
  //#endregion
119
152
  export { stats_cmd_default as default };
120
153
 
121
- //# sourceMappingURL=stats.cmd-D1ujNiDO.js.map
154
+ //# sourceMappingURL=stats.cmd-Dd46qjoV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.cmd-Dd46qjoV.js","names":[],"sources":["../src/commands/stats.ts","../src/commands/stats.cmd.ts"],"sourcesContent":["import {\n\texistsSync,\n\treaddirSync,\n\treadFileSync,\n\tstatSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport { DESCRIPTION_MAX_LENGTH, LIMITS } from '../constants.js';\nimport { SkillValidator } from '../core/validator.js';\nimport type { SkillComplexityTier, StatsOptions } from '../types.js';\nimport { error } from '../utils/output.js';\n\nexport function stats_command(options: StatsOptions): void {\n\tconst directory = options.directory || '.claude/skills';\n\n\t// Verify directory exists\n\tif (!existsSync(directory)) {\n\t\terror(`Directory not found: ${directory}`);\n\t\tprocess.exit(1);\n\t}\n\n\tconst dir_stats = statSync(directory);\n\tif (!dir_stats.isDirectory()) {\n\t\terror(`Path is not a directory: ${directory}`);\n\t\tprocess.exit(1);\n\t}\n\n\t// Find all skill directories (containing SKILL.md)\n\tconst entries = readdirSync(directory);\n\tconst skills: string[] = [];\n\n\tfor (const entry of entries) {\n\t\tconst skill_path = join(directory, entry);\n\t\tconst stat = statSync(skill_path);\n\n\t\tif (stat.isDirectory()) {\n\t\t\tconst skill_md_path = join(skill_path, 'SKILL.md');\n\t\t\tif (existsSync(skill_md_path)) {\n\t\t\t\tskills.push(skill_path);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (skills.length === 0) {\n\t\tconsole.log(`No skills found in ${directory}`);\n\t\tconsole.log(\n\t\t\t'\\nCreate a skill with: claude-skills-cli init --name my-skill --description \"...\"\\n',\n\t\t);\n\t\treturn;\n\t}\n\n\t// Display overview header\n\tconsole.log('📊 Skills Overview');\n\tconsole.log(\n\t\t'============================================================',\n\t);\n\tconsole.log(\n\t\t`${skills.length} skill${skills.length === 1 ? '' : 's'} found:\\n`,\n\t);\n\n\t// Track complexity tiers for summary\n\tconst tier_counts = { simple: 0, standard: 0, advanced: 0 };\n\n\t// Validate and display each skill\n\tfor (const skill_path of skills) {\n\t\tconst skill_name = skill_path.split('/').pop() || '';\n\t\tconst validator = new SkillValidator(skill_path);\n\t\tconst result = validator.validate_all();\n\n\t\t// Determine status icon\n\t\tlet status_icon = '✅';\n\t\tlet status_text = 'valid';\n\t\tif (!result.is_valid) {\n\t\t\tstatus_icon = '❌';\n\t\t\tstatus_text = 'errors';\n\t\t} else if (result.warnings.length > 0) {\n\t\t\tstatus_icon = '⚠️ ';\n\t\t\tstatus_text = 'warnings';\n\t\t}\n\n\t\tconsole.log(`${skill_name} (${status_icon} ${status_text})`);\n\n\t\t// Show version if present\n\t\tconst skill_md_path = join(skill_path, 'SKILL.md');\n\t\tif (existsSync(skill_md_path)) {\n\t\t\tconst content = readFileSync(skill_md_path, 'utf-8');\n\t\t\tconst version_match = content.match(/^version:\\s*(.+)/m);\n\t\t\tif (version_match) {\n\t\t\t\tconsole.log(` Version: ${version_match[1].trim()}`);\n\t\t\t}\n\t\t}\n\n\t\t// Show complexity tier\n\t\tconst tier = classify_complexity(skill_path);\n\t\ttier_counts[tier]++;\n\t\tconsole.log(` Complexity: ${tier}`);\n\n\t\tif (result.stats) {\n\t\t\t// Description length\n\t\t\tconst desc_length = result.stats.description_length;\n\t\t\tlet desc_status = '';\n\t\t\tif (desc_length > 0) {\n\t\t\t\tif (desc_length <= DESCRIPTION_MAX_LENGTH) {\n\t\t\t\t\tdesc_status = 'optimal';\n\t\t\t\t} else {\n\t\t\t\t\tdesc_status = 'too long';\n\t\t\t\t}\n\t\t\t\tconsole.log(\n\t\t\t\t\t` Description: ${desc_length} chars (${desc_status})`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Body stats\n\t\t\tconst lines = result.stats.line_count;\n\t\t\tconst words = result.stats.word_count;\n\n\t\t\tconst S = LIMITS.strict;\n\t\t\tlet line_status = '';\n\t\t\tif (lines <= S.lines.excellent) {\n\t\t\t\tline_status = 'excellent';\n\t\t\t} else if (lines <= S.lines.good) {\n\t\t\t\tline_status = 'good';\n\t\t\t} else if (lines <= S.lines.max) {\n\t\t\t\tline_status = 'consider splitting';\n\t\t\t} else {\n\t\t\t\tline_status = 'too long';\n\t\t\t}\n\n\t\t\tlet word_status = '';\n\t\t\tif (words < S.words.excellent) {\n\t\t\t\tword_status = 'excellent';\n\t\t\t} else if (words < S.words.good) {\n\t\t\t\tword_status = 'good';\n\t\t\t} else if (words < S.words.max) {\n\t\t\t\tword_status = 'acceptable';\n\t\t\t} else {\n\t\t\t\tword_status = 'too long';\n\t\t\t}\n\n\t\t\tconsole.log(\n\t\t\t\t` Body: ${lines} lines (${line_status}), ${words} words (${word_status})`,\n\t\t\t);\n\n\t\t\t// Count reference files\n\t\t\tconst references_dir = join(skill_path, 'references');\n\t\t\tif (existsSync(references_dir)) {\n\t\t\t\tconst ref_files = readdirSync(references_dir).filter((f) =>\n\t\t\t\t\tf.endsWith('.md'),\n\t\t\t\t);\n\t\t\t\tif (ref_files.length > 0) {\n\t\t\t\t\t// Calculate total size\n\t\t\t\t\tlet total_size = 0;\n\t\t\t\t\tfor (const ref_file of ref_files) {\n\t\t\t\t\t\tconst ref_path = join(references_dir, ref_file);\n\t\t\t\t\t\tconst ref_stat = statSync(ref_path);\n\t\t\t\t\t\ttotal_size += ref_stat.size;\n\t\t\t\t\t}\n\t\t\t\t\tconst size_kb = (total_size / 1024).toFixed(1);\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t` References: ${ref_files.length} file${ref_files.length === 1 ? '' : 's'} (${size_kb} KB)`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Show error/warning count\n\t\tif (!result.is_valid) {\n\t\t\tconsole.log(\n\t\t\t\t` ${result.errors.length} error${result.errors.length === 1 ? '' : 's'}`,\n\t\t\t);\n\t\t}\n\t\tif (result.warnings.length > 0) {\n\t\t\tconsole.log(\n\t\t\t\t` ${result.warnings.length} warning${result.warnings.length === 1 ? '' : 's'}`,\n\t\t\t);\n\t\t}\n\n\t\tconsole.log(''); // Blank line between skills\n\t}\n\n\t// Summary\n\tconst valid_skills = skills.filter((path) => {\n\t\tconst validator = new SkillValidator(path);\n\t\tconst result = validator.validate_all();\n\t\treturn result.is_valid;\n\t}).length;\n\n\tconst skills_with_warnings = skills.filter((path) => {\n\t\tconst validator = new SkillValidator(path);\n\t\tconst result = validator.validate_all();\n\t\treturn result.is_valid && result.warnings.length > 0;\n\t}).length;\n\n\tconst invalid_skills = skills.length - valid_skills;\n\n\tconsole.log('Summary:');\n\tif (invalid_skills === 0 && skills_with_warnings === 0) {\n\t\tconsole.log(\n\t\t\t`✅ All ${skills.length} skills are valid with no warnings`,\n\t\t);\n\t} else {\n\t\tconsole.log(` Valid: ${valid_skills}`);\n\t\tif (skills_with_warnings > 0) {\n\t\t\tconsole.log(` With warnings: ${skills_with_warnings}`);\n\t\t}\n\t\tif (invalid_skills > 0) {\n\t\t\tconsole.log(` Invalid: ${invalid_skills}`);\n\t\t}\n\t}\n\n\t// Complexity tier distribution\n\tif (skills.length > 1) {\n\t\tconsole.log(\n\t\t\t` Complexity: ${tier_counts.simple} simple, ${tier_counts.standard} standard, ${tier_counts.advanced} advanced`,\n\t\t);\n\t}\n}\n\n/**\n * Classify skill complexity based on filesystem structure\n */\nexport function classify_complexity(\n\tskill_path: string,\n): SkillComplexityTier {\n\tconst has_scripts =\n\t\texistsSync(join(skill_path, 'scripts')) &&\n\t\tdir_has_files(join(skill_path, 'scripts'));\n\tconst has_references =\n\t\texistsSync(join(skill_path, 'references')) &&\n\t\tdir_has_files(join(skill_path, 'references'));\n\tconst has_assets =\n\t\texistsSync(join(skill_path, 'assets')) &&\n\t\tdir_has_files(join(skill_path, 'assets'));\n\n\tconst script_count = has_scripts\n\t\t? readdirSync(join(skill_path, 'scripts')).length\n\t\t: 0;\n\n\t// Advanced: assets, many scripts, or both scripts + references\n\tif (\n\t\thas_assets ||\n\t\tscript_count > 2 ||\n\t\t(has_scripts && has_references)\n\t) {\n\t\treturn 'advanced';\n\t}\n\n\t// Standard: has references or scripts\n\tif (has_references || has_scripts) {\n\t\treturn 'standard';\n\t}\n\n\treturn 'simple';\n}\n\nfunction dir_has_files(dir_path: string): boolean {\n\ttry {\n\t\treturn readdirSync(dir_path).length > 0;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import { defineCommand } from 'citty';\nimport { stats_command } from './stats.js';\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'stats',\n\t\tdescription: 'Show overview of all skills in a directory',\n\t},\n\targs: {\n\t\tdirectory: {\n\t\t\ttype: 'positional',\n\t\t\tdescription:\n\t\t\t\t'Directory containing skills (default: .claude/skills)',\n\t\t\trequired: false,\n\t\t},\n\t},\n\trun({ args }) {\n\t\tstats_command({ directory: args.directory });\n\t},\n});\n"],"mappings":"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"}
@@ -5,6 +5,10 @@ name: ${name}
5
5
  # Keep on ONE line, third-person voice, include "Use when/for/to..." trigger
6
6
  # prettier-ignore
7
7
  description: ${description}
8
+ # version: 0.1.0
9
+ # depends-on-skills: []
10
+ # depends-on-mcp: []
11
+ # depends-on-packages: []
8
12
  ---
9
13
 
10
14
  # ${title}
@@ -389,4 +393,4 @@ fi
389
393
  //#endregion
390
394
  export { SIMPLE_HOOK_TEMPLATE as a, SCRIPT_TEMPLATE as i, LLM_EVAL_HOOK_TEMPLATE as n, SKILL_MD_TEMPLATE as o, REFERENCE_TEMPLATE as r, FORCED_EVAL_HOOK_TEMPLATE as t };
391
395
 
392
- //# sourceMappingURL=templates-BQTgkXfH.js.map
396
+ //# sourceMappingURL=templates-fyteNbD0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates-fyteNbD0.js","names":[],"sources":["../src/core/templates.ts"],"sourcesContent":["export const SKILL_MD_TEMPLATE = (\n\tname: string,\n\tdescription: string,\n\ttitle: string,\n\tinclude_examples: boolean = false,\n) => {\n\tconst minimal_template = `---\nname: ${name}\n# Keep on ONE line, third-person voice, include \"Use when/for/to...\" trigger\n# prettier-ignore\ndescription: ${description}\n# version: 0.1.0\n# depends-on-skills: []\n# depends-on-mcp: []\n# depends-on-packages: []\n---\n\n# ${title}\n\n## Quick Start\n\n[Provide ONE minimal working example - the most common use case]\n\n\\`\\`\\`typescript\n// Keep this concise - show essential code only\n// Move detailed examples to references/ for Level 3 loading\n\\`\\`\\`\n\n## Core Principles\n\n- Principle 1: [Key concept]\n- Principle 2: [Key concept]\n- Principle 3: [Key concept]\n\n## Common Patterns\n\n### [Most Frequent Pattern]\n\n[Brief description - keep under 100 words]\n\n## Reference Files\n\nFor detailed documentation, see:\n- [references/](references/) - Add detailed guides here\n\n## Notes\n\n- Important note 1\n- Important note 2\n\n<!--\nPROGRESSIVE DISCLOSURE GUIDELINES:\n- Keep this file ~50 lines total (max ~150 lines)\n- Use 1-2 code blocks only (recommend 1)\n- Keep description <250 chars (Claude truncates at this limit)\n- Move detailed docs to references/ for Level 3 loading\n- This is Level 2 - quick reference ONLY, not a manual\n\nLLM WORKFLOW (when editing this file):\n1. Write/edit SKILL.md\n2. Format (if formatter available)\n3. Run: claude-skills-cli validate <path>\n4. If multi-line description warning: run claude-skills-cli doctor <path>\n5. Validate again to confirm\n-->\n`;\n\n\tconst full_template = `---\nname: ${name}\n# Keep on ONE line, third-person voice, include \"Use when/for/to...\" trigger\n# prettier-ignore\ndescription: ${description}\n---\n\n# ${title}\n\n## Quick Start\n\n[Provide ONE minimal working example - the most common use case]\n\n\\`\\`\\`typescript\n// Keep this concise - show essential code only\n// Detailed examples go in references/examples.md\n\\`\\`\\`\n\n## Core Principles\n\n- Principle 1: [Key concept]\n- Principle 2: [Key concept]\n- Principle 3: [Key concept]\n\n## Common Patterns\n\n### [Most Frequent Pattern]\n\n[Brief description - keep under 100 words]\n\n## Reference Files\n\nFor detailed documentation, see:\n- [references/detailed-guide.md](references/detailed-guide.md) - Complete guide\n- [references/examples.md](references/examples.md) - Additional examples\n\n## Scripts\n\n- \\`scripts/example.js\\` - [What this script does]\n\n## Notes\n\n- Important note 1\n- Important note 2\n\n<!--\nPROGRESSIVE DISCLOSURE GUIDELINES:\n- Keep this file ~50 lines total (max ~150 lines)\n- Use 1-2 code blocks only (recommend 1)\n- Keep description <250 chars (Claude truncates at this limit)\n- Move detailed docs to references/ for Level 3 loading\n- This is Level 2 - quick reference ONLY, not a manual\n\nLLM WORKFLOW (when editing this file):\n1. Write/edit SKILL.md\n2. Format (if formatter available)\n3. Run: claude-skills-cli validate <path>\n4. If multi-line description warning: run claude-skills-cli doctor <path>\n5. Validate again to confirm\n-->\n`;\n\n\treturn include_examples ? full_template : minimal_template;\n};\n\nexport const REFERENCE_TEMPLATE = (\n\ttitle: string,\n) => `# ${title} Reference\n\n<!-- This is a Level 3 resource file -->\n<!-- It's loaded on-demand when Claude needs detailed information -->\n<!-- No size limits - include comprehensive documentation here -->\n\n## Overview\n\n[Detailed explanation of this topic]\n\n## Complete Examples\n\n### Example 1: [Scenario]\n\n[Comprehensive example with full context]\n\n\\`\\`\\`typescript\n// Detailed code with comments\n// Include edge cases and variations\n\\`\\`\\`\n\n### Example 2: [Scenario]\n\n[Another detailed example]\n\n\\`\\`\\`typescript\n// More extensive code\n\\`\\`\\`\n\n## Advanced Topics\n\n[Deep dive into complex aspects]\n\n## API Reference\n\n[Complete API documentation if applicable]\n\n## Best Practices\n\n- [Detailed best practice 1]\n- [Detailed best practice 2]\n- [Detailed best practice 3]\n\n## Troubleshooting\n\n### Issue 1\n\n**Problem:** [Description]\n**Solution:** [Detailed solution]\n\n### Issue 2\n\n**Problem:** [Description]\n**Solution:** [Detailed solution]\n`;\n\nexport const SCRIPT_TEMPLATE = (\n\tfilename: string,\n) => `#!/usr/bin/env node\n\n/**\n * Description of what this script does.\n *\n * Usage:\n * node ${filename}\n */\n\nfunction main() {\n console.log('Script executed successfully');\n}\n\nmain();\n`;\n\n// Hook templates for skill activation\nexport const SIMPLE_HOOK_TEMPLATE = () => `#!/bin/bash\necho 'INSTRUCTION: If the prompt matches any available skill keywords, use Skill(skill-name) to activate it.'\n`;\n\nexport const FORCED_EVAL_HOOK_TEMPLATE = () => `#!/bin/bash\n# UserPromptSubmit hook that forces explicit skill evaluation\n#\n# This hook requires Claude to explicitly evaluate each available skill\n# before proceeding with implementation.\n#\n# Installation: Copy to .claude/hooks/UserPromptSubmit\n\ncat <<'EOF'\nINSTRUCTION: MANDATORY SKILL ACTIVATION SEQUENCE\n\nStep 1 - EVALUATE (do this in your response):\nFor each skill in <available_skills>, state: [skill-name] - YES/NO - [reason]\n\nStep 2 - ACTIVATE (do this immediately after Step 1):\nIF any skills are YES → Use Skill(skill-name) tool for EACH relevant skill NOW\nIF no skills are YES → State \"No skills needed\" and proceed\n\nStep 3 - IMPLEMENT:\nOnly after Step 2 is complete, proceed with implementation.\n\nCRITICAL: You MUST call Skill() tool in Step 2. Do NOT skip to implementation.\nThe evaluation (Step 1) is WORTHLESS unless you ACTIVATE (Step 2) the skills.\n\nExample of correct sequence:\n- research: NO - not a research task\n- svelte5-runes: YES - need reactive state\n- sveltekit-structure: YES - creating routes\n\n[Then IMMEDIATELY use Skill() tool:]\n> Skill(svelte5-runes)\n> Skill(sveltekit-structure)\n\n[THEN and ONLY THEN start implementation]\nEOF\n`;\n\nexport const LLM_EVAL_HOOK_TEMPLATE = () => `#!/bin/bash\n# UserPromptSubmit hook that uses Claude API for intelligent skill evaluation\n#\n# This hook analyses each user prompt and uses the Claude API to determine which\n# skills (if any) are relevant. It then instructs Claude to activate those skills\n# before proceeding with implementation.\n#\n# COST ANALYSIS (per prompt):\n# Current model: Claude Haiku 3.5 ($0.80/$4 per MTok input/output)\n# - Estimated: ~400 input tokens + ~20 output tokens\n# - Cost: ~$0.0004 per evaluation (0.04 cents)\n# - Volume: $0.40 per 1,000 prompts\n#\n# MODEL CONFIGURATION:\n# Uncomment the model you want to use below\n#\n# Alternative: Claude Haiku 4.5 ($1/$5 per MTok) - 25% more expensive\n# - Better accuracy and alignment\n# - Cost: ~$0.0005 per evaluation (0.05 cents)\n# - Trade-off: Extra 0.01¢ per call for improved skill matching\n#\n# REQUIREMENTS:\n# 1. Set ANTHROPIC_API_KEY environment variable\n# export ANTHROPIC_API_KEY=your-key-here\n# 2. Ensure jq is installed for JSON parsing\n\n# ============================================================================\n# MODEL CONFIGURATION - Uncomment the model you want to use\n# ============================================================================\n\n# Haiku 3.5 - Faster, cheaper, good for simple classification\nMODEL=\"claude-3-5-haiku-20241022\"\n\n# Haiku 4.5 - Better accuracy, 25% more expensive (verify model ID from Anthropic docs)\n# MODEL=\"claude-haiku-4-5-20251015\"\n\n# ============================================================================\n\n# Read JSON input from stdin with timeout\nINPUT_JSON=$(timeout 2 cat || echo '{}')\n\n# Extract user prompt and cwd from JSON\nUSER_PROMPT=$(echo \"$INPUT_JSON\" | jq -r '.prompt // \"\"' 2>/dev/null)\nCWD=$(echo \"$INPUT_JSON\" | jq -r '.cwd // \"\"' 2>/dev/null)\n\n# Use CLAUDE_PROJECT_DIR if CWD is empty\nif [ -z \"$CWD\" ] || [ \"$CWD\" = \"null\" ]; then\n\tCWD=\"\\${CLAUDE_PROJECT_DIR:-.}\"\nfi\n\n# Get available skills with descriptions from both global and project skills\nAVAILABLE_SKILLS=\"\"\n\n# Function to scan a skills directory\nscan_skills_dir() {\n\tlocal dir=\"$1\"\n\n\tif [ -d \"$dir\" ]; then\n\t\tfor skill_dir in \"$dir\"/*/; do\n\t\t\tif [ -d \"$skill_dir\" ]; then\n\t\t\t\tskill_file=\"$skill_dir/SKILL.md\"\n\t\t\t\tif [ -f \"$skill_file\" ]; then\n\t\t\t\t\tskill_name=$(basename \"$skill_dir\")\n\t\t\t\t\t# Extract description from YAML frontmatter (between --- markers)\n\t\t\t\t\tskill_desc=$(sed -n '/^---$/,/^---$/p' \"$skill_file\" | grep '^description:' | sed 's/^description: *//' | head -n 1)\n\n\t\t\t\t\tif [ -n \"$skill_desc\" ]; then\n\t\t\t\t\t\tAVAILABLE_SKILLS=\"\\${AVAILABLE_SKILLS}- \\${skill_name}: \\${skill_desc}\\\\n\"\n\t\t\t\t\telse\n\t\t\t\t\t\tAVAILABLE_SKILLS=\"\\${AVAILABLE_SKILLS}- \\${skill_name}\\\\n\"\n\t\t\t\t\tfi\n\t\t\t\tfi\n\t\t\tfi\n\t\tdone\n\tfi\n}\n\n# Scan global skills\nscan_skills_dir \"$HOME/.claude/skills\"\n\n# Scan project skills\nscan_skills_dir \"$CWD/.claude/skills\"\n\nif [ -z \"$AVAILABLE_SKILLS\" ]; then\n\tAVAILABLE_SKILLS=\"No skills found\"\nfi\n\n\n# Fallback instruction message\nFALLBACK_INSTRUCTION=\"INSTRUCTION: If the prompt matches any available skill keywords, use Skill(skill-name) to activate it.\"\n\n# If no API key in environment, fall back\nif [ -z \"$ANTHROPIC_API_KEY\" ]; then\n\techo \"$FALLBACK_INSTRUCTION\"\n\texit 0\nfi\n\n# Prepare the optimized evaluation prompt\nEVAL_PROMPT=$(cat <<EOF\nReturn ONLY a JSON array of skill names that match this request.\n\nRequest: \\${USER_PROMPT}\n\nSkills:\n\\${AVAILABLE_SKILLS}\nFormat: [\"skill-name\"] or []\nEOF\n)\n\n# Call Claude API with optimized parameters\nRESPONSE=$(curl -s https://api.anthropic.com/v1/messages \\\\\n\t-H \"content-type: application/json\" \\\\\n\t-H \"x-api-key: $ANTHROPIC_API_KEY\" \\\\\n\t-H \"anthropic-version: 2023-06-01\" \\\\\n\t-d \"{\n\t\t\\\\\"model\\\\\": \\\\\"$MODEL\\\\\",\n\t\t\\\\\"max_tokens\\\\\": 200,\n\t\t\\\\\"temperature\\\\\": 0,\n\t\t\\\\\"system\\\\\": \\\\\"You are a skill matcher. Return only valid JSON arrays.\\\\\",\n\t\t\\\\\"messages\\\\\": [{\n\t\t\t\\\\\"role\\\\\": \\\\\"user\\\\\",\n\t\t\t\\\\\"content\\\\\": $(echo \"$EVAL_PROMPT\" | jq -Rs .)\n\t\t}]\n\t}\")\n\n# Extract the skill list from response\nRAW_TEXT=$(echo \"$RESPONSE\" | jq -r '.content[0].text' 2>/dev/null)\n\n# Check if we got a valid response\nif [ $? -ne 0 ] || [ -z \"$RAW_TEXT\" ]; then\n\techo \"$FALLBACK_INSTRUCTION\"\n\texit 0\nfi\n\n# Strip markdown code fences if present and extract JSON\nSKILLS=$(echo \"$RAW_TEXT\" | sed -n '/^\\\\[/,/^\\\\]/p' | head -n 1)\n\n# If that didn't work, try the whole text\nif [ -z \"$SKILLS\" ]; then\n\tSKILLS=\"$RAW_TEXT\"\nfi\n\n# Parse the skills array\nSKILL_COUNT=$(echo \"$SKILLS\" | jq 'length' 2>/dev/null)\n\nif [ \"$SKILL_COUNT\" = \"0\" ]; then\n\techo \"INSTRUCTION: LLM evaluation determined no skills are needed for this task.\"\nelif [ -n \"$SKILL_COUNT\" ] && [ \"$SKILL_COUNT\" != \"null\" ]; then\n\tSKILL_NAMES=$(echo \"$SKILLS\" | jq -r '.[]' | paste -sd ',' -)\n\techo \"INSTRUCTION: LLM evaluation determined these skills are relevant: $SKILL_NAMES\"\n\techo \"\"\n\techo \"You MUST activate these skills using the Skill() tool BEFORE implementation:\"\n\techo \"$SKILLS\" | jq -r '.[] | \"- Skill(\\\\(.))\"'\nelse\n\t# Fallback if parsing failed\n\techo \"$FALLBACK_INSTRUCTION\"\nfi\n`;\n"],"mappings":";AAAA,MAAa,qBACZ,MACA,aACA,OACA,mBAA4B,UACxB;CACJ,MAAM,mBAAmB;QAClB,KAAK;;;eAGE,YAAY;;;;;;;IAOvB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkDT,MAAM,gBAAgB;QACf,KAAK;;;eAGE,YAAY;;;IAGvB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDT,QAAO,mBAAmB,gBAAgB;;AAG3C,MAAa,sBACZ,UACI,KAAK,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDhB,MAAa,mBACZ,aACI;;;;;;YAMO,SAAS;;;;;;;;;AAWrB,MAAa,6BAA6B;;;AAI1C,MAAa,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqC/C,MAAa,+BAA+B"}
@@ -1,5 +1,5 @@
1
- import { n as error, o as success, r as info, t as display_validation_stats } from "./output-Dz8fk6Gu.js";
2
- import { t as SkillValidator } from "./validator-DV5zeeel.js";
1
+ import { n as error, o as success, r as info, t as display_validation_stats } from "./output-DiffPD2u.js";
2
+ import { t as SkillValidator } from "./validator-Dp5x-OjP.js";
3
3
  import { defineCommand } from "citty";
4
4
  import { basename } from "node:path";
5
5
  //#region src/commands/validate.ts
@@ -93,4 +93,4 @@ var validate_cmd_default = defineCommand({
93
93
  //#endregion
94
94
  export { validate_cmd_default as default };
95
95
 
96
- //# sourceMappingURL=validate.cmd-CDUJDKGs.js.map
96
+ //# sourceMappingURL=validate.cmd-BxF4HNsu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.cmd-CDUJDKGs.js","names":[],"sources":["../src/commands/validate.ts","../src/commands/validate.cmd.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { SkillValidator } from '../core/validator.js';\nimport type { ValidateOptions } from '../types.js';\nimport {\n\tdisplay_validation_stats,\n\terror,\n\tinfo,\n\tsuccess,\n} from '../utils/output.js';\n\nexport function validate_command(options: ValidateOptions): void {\n\tconst {\n\t\tskill_path,\n\t\tstrict,\n\t\tformat = 'text',\n\t\tlenient = false,\n\t\tloose = false,\n\t} = options;\n\t// Normalize path by removing trailing slashes before extracting basename\n\tconst normalized_path = skill_path.replace(/\\/+$/, '');\n\tconst skill_name = basename(normalized_path);\n\n\t// Determine validation mode: loose > lenient > strict (default)\n\tconst mode = loose ? 'loose' : lenient ? 'lenient' : 'strict';\n\tconst validator = new SkillValidator(skill_path, { mode });\n\tconst result = validator.validate_all();\n\n\t// JSON output\n\tif (format === 'json') {\n\t\tconst json_output = {\n\t\t\tskill_name,\n\t\t\tvalid: result.is_valid,\n\t\t\terrors: result.errors,\n\t\t\twarnings: result.warnings,\n\t\t\tvalidation: result.validation,\n\t\t\tstats: result.stats,\n\t\t};\n\t\tconsole.log(JSON.stringify(json_output, null, 2));\n\n\t\t// Exit codes for JSON mode\n\t\tif (!result.is_valid) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (strict && result.warnings.length > 0) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tprocess.exit(0);\n\t}\n\n\t// Text output (existing)\n\tinfo(`Validating skill: ${skill_name}`);\n\tconsole.log('='.repeat(60));\n\n\t// Display progressive disclosure stats\n\tif (result.stats) {\n\t\tdisplay_validation_stats(result.stats);\n\t}\n\n\t// Print errors\n\tif (result.errors.length > 0) {\n\t\tconsole.log('\\n❌ Errors:');\n\t\tfor (const err of result.errors) {\n\t\t\tconsole.log(` ${err}`);\n\t\t}\n\t}\n\n\t// Print warnings\n\tif (result.warnings.length > 0) {\n\t\tconsole.log('\\n⚠️ Warnings:');\n\t\tfor (const warn of result.warnings) {\n\t\t\tconsole.log(` ${warn}`);\n\t\t}\n\t}\n\n\t// Print final status\n\tif (!result.errors.length && !result.warnings.length) {\n\t\tconsole.log('');\n\t\tsuccess('Skill is valid!');\n\t} else if (!result.errors.length) {\n\t\tconsole.log('');\n\t\tsuccess('Skill is valid (with warnings)');\n\t} else {\n\t\tconsole.log('');\n\t\terror(\n\t\t\t`Skill validation failed with ${result.errors.length} error(s)`,\n\t\t);\n\t}\n\n\t// Handle exit codes\n\tif (!result.is_valid) {\n\t\tprocess.exit(1);\n\t}\n\n\tif (strict && result.warnings.length > 0) {\n\t\tconsole.log('\\n❌ Failed in strict mode due to warnings');\n\t\tprocess.exit(1);\n\t}\n\n\tprocess.exit(0);\n}\n","import { defineCommand } from 'citty';\nimport { validate_command } from './validate.js';\n\nexport default defineCommand({\n\tmeta: { name: 'validate', description: 'Validate a skill' },\n\targs: {\n\t\tskill_path: {\n\t\t\ttype: 'positional',\n\t\t\tdescription: 'Path to skill directory',\n\t\t\trequired: true,\n\t\t},\n\t\tformat: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Output format (json or text)',\n\t\t\tdefault: 'text',\n\t\t},\n\t\tstrict: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Fail validation if warnings present',\n\t\t},\n\t\tlenient: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Use relaxed limits (150 lines max)',\n\t\t},\n\t\tloose: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Use Anthropic official limits (500 lines max)',\n\t\t},\n\t},\n\trun({ args }) {\n\t\tvalidate_command({\n\t\t\tskill_path: args.skill_path,\n\t\t\tstrict: args.strict,\n\t\t\tformat: args.format === 'json' ? 'json' : 'text',\n\t\t\tlenient: args.lenient,\n\t\t\tloose: args.loose,\n\t\t});\n\t},\n});\n"],"mappings":";;;;;AAUA,SAAgB,iBAAiB,SAAgC;CAChE,MAAM,EACL,YACA,QACA,SAAS,QACT,UAAU,OACV,QAAQ,UACL;CAGJ,MAAM,aAAa,SADK,WAAW,QAAQ,QAAQ,GAAG,CACV;CAK5C,MAAM,SADY,IAAI,eAAe,YAAY,EAAE,MADtC,QAAQ,UAAU,UAAU,YAAY,UACI,CAAC,CACjC,cAAc;AAGvC,KAAI,WAAW,QAAQ;EACtB,MAAM,cAAc;GACnB;GACA,OAAO,OAAO;GACd,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,YAAY,OAAO;GACnB,OAAO,OAAO;GACd;AACD,UAAQ,IAAI,KAAK,UAAU,aAAa,MAAM,EAAE,CAAC;AAGjD,MAAI,CAAC,OAAO,SACX,SAAQ,KAAK,EAAE;AAEhB,MAAI,UAAU,OAAO,SAAS,SAAS,EACtC,SAAQ,KAAK,EAAE;AAEhB,UAAQ,KAAK,EAAE;;AAIhB,MAAK,qBAAqB,aAAa;AACvC,SAAQ,IAAI,IAAI,OAAO,GAAG,CAAC;AAG3B,KAAI,OAAO,MACV,0BAAyB,OAAO,MAAM;AAIvC,KAAI,OAAO,OAAO,SAAS,GAAG;AAC7B,UAAQ,IAAI,cAAc;AAC1B,OAAK,MAAM,OAAO,OAAO,OACxB,SAAQ,IAAI,KAAK,MAAM;;AAKzB,KAAI,OAAO,SAAS,SAAS,GAAG;AAC/B,UAAQ,IAAI,kBAAkB;AAC9B,OAAK,MAAM,QAAQ,OAAO,SACzB,SAAQ,IAAI,KAAK,OAAO;;AAK1B,KAAI,CAAC,OAAO,OAAO,UAAU,CAAC,OAAO,SAAS,QAAQ;AACrD,UAAQ,IAAI,GAAG;AACf,UAAQ,kBAAkB;YAChB,CAAC,OAAO,OAAO,QAAQ;AACjC,UAAQ,IAAI,GAAG;AACf,UAAQ,iCAAiC;QACnC;AACN,UAAQ,IAAI,GAAG;AACf,QACC,gCAAgC,OAAO,OAAO,OAAO,WACrD;;AAIF,KAAI,CAAC,OAAO,SACX,SAAQ,KAAK,EAAE;AAGhB,KAAI,UAAU,OAAO,SAAS,SAAS,GAAG;AACzC,UAAQ,IAAI,4CAA4C;AACxD,UAAQ,KAAK,EAAE;;AAGhB,SAAQ,KAAK,EAAE;;;;AC/FhB,IAAA,uBAAe,cAAc;CAC5B,MAAM;EAAE,MAAM;EAAY,aAAa;EAAoB;CAC3D,MAAM;EACL,YAAY;GACX,MAAM;GACN,aAAa;GACb,UAAU;GACV;EACD,QAAQ;GACP,MAAM;GACN,aAAa;GACb,SAAS;GACT;EACD,QAAQ;GACP,MAAM;GACN,aAAa;GACb;EACD,SAAS;GACR,MAAM;GACN,aAAa;GACb;EACD,OAAO;GACN,MAAM;GACN,aAAa;GACb;EACD;CACD,IAAI,EAAE,QAAQ;AACb,mBAAiB;GAChB,YAAY,KAAK;GACjB,QAAQ,KAAK;GACb,QAAQ,KAAK,WAAW,SAAS,SAAS;GAC1C,SAAS,KAAK;GACd,OAAO,KAAK;GACZ,CAAC;;CAEH,CAAC"}
1
+ {"version":3,"file":"validate.cmd-BxF4HNsu.js","names":[],"sources":["../src/commands/validate.ts","../src/commands/validate.cmd.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { SkillValidator } from '../core/validator.js';\nimport type { ValidateOptions } from '../types.js';\nimport {\n\tdisplay_validation_stats,\n\terror,\n\tinfo,\n\tsuccess,\n} from '../utils/output.js';\n\nexport function validate_command(options: ValidateOptions): void {\n\tconst {\n\t\tskill_path,\n\t\tstrict,\n\t\tformat = 'text',\n\t\tlenient = false,\n\t\tloose = false,\n\t} = options;\n\t// Normalize path by removing trailing slashes before extracting basename\n\tconst normalized_path = skill_path.replace(/\\/+$/, '');\n\tconst skill_name = basename(normalized_path);\n\n\t// Determine validation mode: loose > lenient > strict (default)\n\tconst mode = loose ? 'loose' : lenient ? 'lenient' : 'strict';\n\tconst validator = new SkillValidator(skill_path, { mode });\n\tconst result = validator.validate_all();\n\n\t// JSON output\n\tif (format === 'json') {\n\t\tconst json_output = {\n\t\t\tskill_name,\n\t\t\tvalid: result.is_valid,\n\t\t\terrors: result.errors,\n\t\t\twarnings: result.warnings,\n\t\t\tvalidation: result.validation,\n\t\t\tstats: result.stats,\n\t\t};\n\t\tconsole.log(JSON.stringify(json_output, null, 2));\n\n\t\t// Exit codes for JSON mode\n\t\tif (!result.is_valid) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (strict && result.warnings.length > 0) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tprocess.exit(0);\n\t}\n\n\t// Text output (existing)\n\tinfo(`Validating skill: ${skill_name}`);\n\tconsole.log('='.repeat(60));\n\n\t// Display progressive disclosure stats\n\tif (result.stats) {\n\t\tdisplay_validation_stats(result.stats);\n\t}\n\n\t// Print errors\n\tif (result.errors.length > 0) {\n\t\tconsole.log('\\n❌ Errors:');\n\t\tfor (const err of result.errors) {\n\t\t\tconsole.log(` ${err}`);\n\t\t}\n\t}\n\n\t// Print warnings\n\tif (result.warnings.length > 0) {\n\t\tconsole.log('\\n⚠️ Warnings:');\n\t\tfor (const warn of result.warnings) {\n\t\t\tconsole.log(` ${warn}`);\n\t\t}\n\t}\n\n\t// Print final status\n\tif (!result.errors.length && !result.warnings.length) {\n\t\tconsole.log('');\n\t\tsuccess('Skill is valid!');\n\t} else if (!result.errors.length) {\n\t\tconsole.log('');\n\t\tsuccess('Skill is valid (with warnings)');\n\t} else {\n\t\tconsole.log('');\n\t\terror(\n\t\t\t`Skill validation failed with ${result.errors.length} error(s)`,\n\t\t);\n\t}\n\n\t// Handle exit codes\n\tif (!result.is_valid) {\n\t\tprocess.exit(1);\n\t}\n\n\tif (strict && result.warnings.length > 0) {\n\t\tconsole.log('\\n❌ Failed in strict mode due to warnings');\n\t\tprocess.exit(1);\n\t}\n\n\tprocess.exit(0);\n}\n","import { defineCommand } from 'citty';\nimport { validate_command } from './validate.js';\n\nexport default defineCommand({\n\tmeta: { name: 'validate', description: 'Validate a skill' },\n\targs: {\n\t\tskill_path: {\n\t\t\ttype: 'positional',\n\t\t\tdescription: 'Path to skill directory',\n\t\t\trequired: true,\n\t\t},\n\t\tformat: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Output format (json or text)',\n\t\t\tdefault: 'text',\n\t\t},\n\t\tstrict: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Fail validation if warnings present',\n\t\t},\n\t\tlenient: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Use relaxed limits (150 lines max)',\n\t\t},\n\t\tloose: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Use Anthropic official limits (500 lines max)',\n\t\t},\n\t},\n\trun({ args }) {\n\t\tvalidate_command({\n\t\t\tskill_path: args.skill_path,\n\t\t\tstrict: args.strict,\n\t\t\tformat: args.format === 'json' ? 'json' : 'text',\n\t\t\tlenient: args.lenient,\n\t\t\tloose: args.loose,\n\t\t});\n\t},\n});\n"],"mappings":";;;;;AAUA,SAAgB,iBAAiB,SAAgC;CAChE,MAAM,EACL,YACA,QACA,SAAS,QACT,UAAU,OACV,QAAQ,UACL;CAGJ,MAAM,aAAa,SADK,WAAW,QAAQ,QAAQ,GAAG,CACV;CAK5C,MAAM,SADY,IAAI,eAAe,YAAY,EAAE,MADtC,QAAQ,UAAU,UAAU,YAAY,UACI,CAAC,CACjC,cAAc;AAGvC,KAAI,WAAW,QAAQ;EACtB,MAAM,cAAc;GACnB;GACA,OAAO,OAAO;GACd,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,YAAY,OAAO;GACnB,OAAO,OAAO;GACd;AACD,UAAQ,IAAI,KAAK,UAAU,aAAa,MAAM,EAAE,CAAC;AAGjD,MAAI,CAAC,OAAO,SACX,SAAQ,KAAK,EAAE;AAEhB,MAAI,UAAU,OAAO,SAAS,SAAS,EACtC,SAAQ,KAAK,EAAE;AAEhB,UAAQ,KAAK,EAAE;;AAIhB,MAAK,qBAAqB,aAAa;AACvC,SAAQ,IAAI,IAAI,OAAO,GAAG,CAAC;AAG3B,KAAI,OAAO,MACV,0BAAyB,OAAO,MAAM;AAIvC,KAAI,OAAO,OAAO,SAAS,GAAG;AAC7B,UAAQ,IAAI,cAAc;AAC1B,OAAK,MAAM,OAAO,OAAO,OACxB,SAAQ,IAAI,KAAK,MAAM;;AAKzB,KAAI,OAAO,SAAS,SAAS,GAAG;AAC/B,UAAQ,IAAI,kBAAkB;AAC9B,OAAK,MAAM,QAAQ,OAAO,SACzB,SAAQ,IAAI,KAAK,OAAO;;AAK1B,KAAI,CAAC,OAAO,OAAO,UAAU,CAAC,OAAO,SAAS,QAAQ;AACrD,UAAQ,IAAI,GAAG;AACf,UAAQ,kBAAkB;YAChB,CAAC,OAAO,OAAO,QAAQ;AACjC,UAAQ,IAAI,GAAG;AACf,UAAQ,iCAAiC;QACnC;AACN,UAAQ,IAAI,GAAG;AACf,QACC,gCAAgC,OAAO,OAAO,OAAO,WACrD;;AAIF,KAAI,CAAC,OAAO,SACX,SAAQ,KAAK,EAAE;AAGhB,KAAI,UAAU,OAAO,SAAS,SAAS,GAAG;AACzC,UAAQ,IAAI,4CAA4C;AACxD,UAAQ,KAAK,EAAE;;AAGhB,SAAQ,KAAK,EAAE;;;;AC/FhB,IAAA,uBAAe,cAAc;CAC5B,MAAM;EAAE,MAAM;EAAY,aAAa;EAAoB;CAC3D,MAAM;EACL,YAAY;GACX,MAAM;GACN,aAAa;GACb,UAAU;GACV;EACD,QAAQ;GACP,MAAM;GACN,aAAa;GACb,SAAS;GACT;EACD,QAAQ;GACP,MAAM;GACN,aAAa;GACb;EACD,SAAS;GACR,MAAM;GACN,aAAa;GACb;EACD,OAAO;GACN,MAAM;GACN,aAAa;GACb;EACD;CACD,IAAI,EAAE,QAAQ;AACb,mBAAiB;GAChB,YAAY,KAAK;GACjB,QAAQ,KAAK;GACb,QAAQ,KAAK,WAAW,SAAS,SAAS;GAC1C,SAAS,KAAK;GACd,OAAO,KAAK;GACZ,CAAC;;CAEH,CAAC"}
@@ -1,5 +1,5 @@
1
- import { l as LIMITS } from "./output-Dz8fk6Gu.js";
2
- import { a as validate_name_format, i as validate_hard_limits, r as validate_frontmatter_structure, t as extract_frontmatter } from "./frontmatter-validator-DO686mla.js";
1
+ import { l as LIMITS } from "./output-DiffPD2u.js";
2
+ import { c as validate_hard_limits, i as extract_frontmatter, l as validate_name_format, n as validate_dependencies, s as validate_frontmatter_structure } from "./dependency-validator-CQJ1hCoU.js";
3
3
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  //#region src/validators/text-analysis.ts
@@ -667,6 +667,14 @@ var SkillValidator = class {
667
667
  this.stats.long_paragraphs = content_validation.stats.long_paragraphs;
668
668
  content_validation.errors.forEach((err) => this.error(err.message));
669
669
  content_validation.warnings.forEach((warn) => this.warning(warn.message));
670
+ const parts = content.split("---\n");
671
+ if (parts.length >= 3) {
672
+ const frontmatter_raw = parts[1];
673
+ const dep_result = validate_dependencies(frontmatter_raw);
674
+ dep_result.errors.forEach((err) => this.error(err));
675
+ dep_result.warnings.forEach((warn) => this.warning(warn));
676
+ this.structured_validation.dependency_validation = dep_result.validation;
677
+ }
670
678
  return true;
671
679
  }
672
680
  validate_all() {
@@ -718,4 +726,4 @@ var SkillValidator = class {
718
726
  //#endregion
719
727
  export { SkillValidator as t };
720
728
 
721
- //# sourceMappingURL=validator-DV5zeeel.js.map
729
+ //# sourceMappingURL=validator-Dp5x-OjP.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validator-DV5zeeel.js","names":[],"sources":["../src/validators/text-analysis.ts","../src/validators/alignment-validator.ts","../src/validators/content-validator.ts","../src/validators/description-validator.ts","../src/validators/file-structure-validator.ts","../src/validators/references-validator.ts","../src/core/validator.ts"],"sourcesContent":["/**\n * Text analysis utilities for skill validation\n */\n\n/**\n * Extract body content from SKILL.md (excluding YAML frontmatter)\n */\nexport function extract_body(content: string): string {\n\tconst parts = content.split('---\\n');\n\treturn parts.length >= 3\n\t\t? parts.slice(2).join('---\\n').trim()\n\t\t: content;\n}\n\n/**\n * Count words in text\n */\nexport function count_words(text: string): number {\n\treturn text\n\t\t.trim()\n\t\t.split(/\\s+/)\n\t\t.filter((w) => w.length > 0).length;\n}\n\n/**\n * Estimate tokens (rough approximation: 1 word ≈ 1.3 tokens for English)\n */\nexport function estimate_tokens(word_count: number): number {\n\treturn Math.round(word_count * 1.3);\n}\n\n/**\n * Estimate tokens for a string by counting words and applying ratio\n */\nexport function estimate_string_tokens(text: string): number {\n\tconst word_count = count_words(text);\n\treturn estimate_tokens(word_count);\n}\n\n/**\n * Remove HTML comments from content (for line counting)\n */\nexport function strip_html_comments(text: string): string {\n\treturn text.replace(/<!--[\\s\\S]*?-->/g, '');\n}\n\n/**\n * Extract keywords from text (simplified extraction)\n */\nexport function extract_keywords(text: string): string[] {\n\tconst words = text\n\t\t.toLowerCase()\n\t\t.replace(/[^\\w\\s-]/g, ' ')\n\t\t.split(/\\s+/)\n\t\t.filter((w) => w.length > 3);\n\n\tconst unique = [...new Set(words)];\n\treturn unique.filter(\n\t\t(w) =>\n\t\t\t![\n\t\t\t\t'this',\n\t\t\t\t'that',\n\t\t\t\t'with',\n\t\t\t\t'from',\n\t\t\t\t'have',\n\t\t\t\t'will',\n\t\t\t\t'when',\n\t\t\t\t'what',\n\t\t\t\t'where',\n\t\t\t\t'which',\n\t\t\t\t'their',\n\t\t\t\t'them',\n\t\t\t\t'then',\n\t\t\t\t'than',\n\t\t\t\t'these',\n\t\t\t\t'those',\n\t\t\t\t'there',\n\t\t\t].includes(w),\n\t);\n}\n","/**\n * Alignment validation - checks description and content alignment\n */\n\nimport { extract_keywords } from './text-analysis.js';\nimport type { KeywordAnalysis, AlignmentAnalysis } from '../types.js';\n\nexport interface AlignmentWarning {\n\ttype: 'low_overlap';\n\tmessage: string;\n}\n\nexport interface AlignmentValidation {\n\tkeywords: KeywordAnalysis;\n\talignment: AlignmentAnalysis;\n\twarnings: AlignmentWarning[];\n}\n\n/**\n * Analyze description and content alignment\n */\nexport function analyze_alignment(\n\tdescription: string,\n\tbody: string,\n): AlignmentValidation {\n\tconst desc_keywords = extract_keywords(description);\n\tconst content_keywords = extract_keywords(body);\n\n\tconst overlap = desc_keywords.filter((k) =>\n\t\tcontent_keywords.includes(k),\n\t);\n\tconst desc_only = desc_keywords.filter(\n\t\t(k) => !content_keywords.includes(k),\n\t);\n\tconst content_only = content_keywords\n\t\t.filter((k) => !desc_keywords.includes(k))\n\t\t.slice(0, 20);\n\n\tconst overlap_ratio =\n\t\tdesc_keywords.length > 0\n\t\t\t? overlap.length / desc_keywords.length\n\t\t\t: 0;\n\n\tlet severity: 'good' | 'moderate' | 'critical' = 'good';\n\tlet explanation = 'Description aligns well with content';\n\n\tif (overlap_ratio < 0.2 && desc_keywords.length > 5) {\n\t\tseverity = 'critical';\n\t\texplanation = `Very low keyword overlap (${Math.round(overlap_ratio * 100)}%). Description may not match skill content.`;\n\t} else if (overlap_ratio < 0.3 && desc_keywords.length > 5) {\n\t\tseverity = 'moderate';\n\t\texplanation = `Low keyword overlap (${Math.round(overlap_ratio * 100)}%). Description may not accurately reflect skill content.`;\n\t}\n\n\tconst keywords: KeywordAnalysis = {\n\t\tdescription_keywords: desc_keywords,\n\t\tcontent_keywords: content_keywords.slice(0, 30),\n\t\toverlap,\n\t\tdescription_only: desc_only,\n\t\tcontent_only,\n\t};\n\n\tconst alignment: AlignmentAnalysis = {\n\t\tseverity,\n\t\tdescription_focus: desc_keywords.slice(0, 10),\n\t\tcontent_focus: content_keywords.slice(0, 10),\n\t\tmatches: overlap,\n\t\tmismatches: desc_only,\n\t\texplanation,\n\t};\n\n\tconst warnings: AlignmentWarning[] = [];\n\n\tif (overlap_ratio < 0.3 && desc_keywords.length > 5) {\n\t\twarnings.push({\n\t\t\ttype: 'low_overlap',\n\t\t\tmessage:\n\t\t\t\t`Low keyword overlap between description and content (${Math.round(overlap_ratio * 100)}%)\\n` +\n\t\t\t\t` → Description may not accurately reflect skill content`,\n\t\t});\n\t}\n\n\treturn { keywords, alignment, warnings };\n}\n","/**\n * Content validation (Level 2 progressive disclosure)\n */\n\nimport {\n\tLIMITS,\n\tLONG_PARAGRAPH_WORDS,\n\tMIN_BODY_LENGTH,\n} from '../constants.js';\nimport {\n\tcount_words,\n\testimate_tokens,\n\tstrip_html_comments,\n} from './text-analysis.js';\n\nexport interface ContentStats {\n\tword_count: number;\n\testimated_tokens: number;\n\tline_count: number;\n\tcode_blocks: number;\n\tsections: number;\n\tlong_paragraphs: number;\n}\n\nexport interface ContentWarning {\n\ttype:\n\t\t| 'word_count'\n\t\t| 'line_count'\n\t\t| 'code_blocks'\n\t\t| 'long_paragraphs'\n\t\t| 'sections'\n\t\t| 'missing_quick_start'\n\t\t| 'no_references'\n\t\t| 'short_body'\n\t\t| 'todo_placeholders';\n\tmessage: string;\n}\n\nexport interface ContentError {\n\ttype: 'word_count' | 'line_count';\n\tmessage: string;\n}\n\nexport interface ContentValidation {\n\tstats: ContentStats;\n\twarnings: ContentWarning[];\n\terrors: ContentError[];\n}\n\nimport type { ValidationMode } from '../types.js';\n\nexport interface ContentValidationOptions {\n\tmode?: ValidationMode;\n}\n\n/**\n * Analyze content structure and patterns\n */\nexport function analyze_content_structure(\n\tbody: string,\n): Pick<\n\tContentStats,\n\t'code_blocks' | 'sections' | 'long_paragraphs'\n> {\n\t// Count code blocks\n\tconst code_block_matches = body.match(/```[\\s\\S]*?```/g);\n\tconst code_blocks = code_block_matches\n\t\t? code_block_matches.length\n\t\t: 0;\n\n\t// Count markdown sections (headings)\n\tconst heading_matches = body.match(/^#{1,6}\\s/gm);\n\tconst sections = heading_matches ? heading_matches.length : 0;\n\n\t// Count long paragraphs\n\tconst paragraphs = body.split(/\\n\\n+/);\n\tconst long_paragraphs = paragraphs.filter((p) => {\n\t\tconst words = count_words(p);\n\t\treturn words > LONG_PARAGRAPH_WORDS;\n\t}).length;\n\n\treturn { code_blocks, sections, long_paragraphs };\n}\n\n/**\n * Validate progressive disclosure (word count, token budget, and line count)\n */\nexport function validate_content(\n\tbody: string,\n\toptions: ContentValidationOptions = {},\n): ContentValidation {\n\tconst { mode = 'strict' } = options;\n\tconst limits = LIMITS[mode];\n\n\tconst word_count = count_words(body);\n\tconst estimated_tokens = estimate_tokens(word_count);\n\n\t// Strip HTML comments before counting lines (progressive disclosure guidance shouldn't inflate count)\n\tconst body_without_comments = strip_html_comments(body);\n\tconst line_count = body_without_comments.trim().split('\\n').length;\n\n\t// Analyze content structure\n\tconst structure = analyze_content_structure(body);\n\n\tconst validation: ContentValidation = {\n\t\tstats: {\n\t\t\tword_count,\n\t\t\testimated_tokens,\n\t\t\tline_count,\n\t\t\t...structure,\n\t\t},\n\t\twarnings: [],\n\t\terrors: [],\n\t};\n\n\t// Word count validation\n\tif (word_count > limits.words.max) {\n\t\tvalidation.errors.push({\n\t\t\ttype: 'word_count',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md body has ${word_count} words (MAX: ${limits.words.max})\\n` +\n\t\t\t\t` → Move detailed content to references/ directory for Level 3 loading\\n` +\n\t\t\t\t` → This is a hard limit - skills must be concise`,\n\t\t});\n\t} else if (word_count > limits.words.good) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'word_count',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md body has ${word_count} words (recommended: <${limits.words.good}, max: ${limits.words.max})\\n` +\n\t\t\t\t` → Consider moving examples/docs to references/ for better token efficiency`,\n\t\t});\n\t}\n\n\t// Line count validation (Level 2 progressive disclosure)\n\tif (line_count > limits.lines.max) {\n\t\tvalidation.errors.push({\n\t\t\ttype: 'line_count',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md body is ${line_count} lines (MAX: ${limits.lines.max})\\n` +\n\t\t\t\t` → Move detailed content to references/ directory\\n` +\n\t\t\t\t` → This is a hard limit - skills must be concise`,\n\t\t});\n\t} else if (line_count > limits.lines.good) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'line_count',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md body is ${line_count} lines (recommended: <${limits.lines.good}, max: ${limits.lines.max})\\n` +\n\t\t\t\t` → Consider moving examples to references/ for Level 3 loading`,\n\t\t});\n\t}\n\n\t// Content analysis warnings\n\t// Code blocks: Recommend 1-2, warn at >3\n\tif (structure.code_blocks > 3) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'code_blocks',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md contains ${structure.code_blocks} code examples (recommended: 1-2)\\n` +\n\t\t\t\t` → Move additional examples to references/examples.md for Level 3 loading`,\n\t\t});\n\t}\n\n\t// Long paragraphs\n\tif (structure.long_paragraphs > 3) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'long_paragraphs',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md contains ${structure.long_paragraphs} lengthy paragraphs (>${LONG_PARAGRAPH_WORDS} words)\\n` +\n\t\t\t\t` → Consider moving detailed explanations to references/`,\n\t\t});\n\t}\n\n\t// Sections: Recommend 3-5, warn at >8\n\tif (structure.sections > 8) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'sections',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md contains ${structure.sections} sections (recommended: 3-5)\\n` +\n\t\t\t\t` → Consider splitting into focused reference files`,\n\t\t});\n\t}\n\n\t// Check for \"Quick Start\" section\n\tif (\n\t\t!body.includes('## Quick Start') &&\n\t\t!body.includes('## Quick start')\n\t) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'missing_quick_start',\n\t\t\tmessage:\n\t\t\t\t`Missing \"## Quick Start\" section\\n` +\n\t\t\t\t` → Add one minimal working example to help Claude get started quickly`,\n\t\t});\n\t}\n\n\t// Check for references/ links when body is long (warn when exceeding good threshold)\n\tconst has_references = body.includes('references/');\n\tif (!has_references && line_count > limits.lines.good) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'no_references',\n\t\t\tmessage:\n\t\t\t\t`No references/ links found but SKILL.md is ${line_count} lines\\n` +\n\t\t\t\t` → Consider splitting detailed content into reference files`,\n\t\t});\n\t}\n\n\t// Check body content\n\tif (body.trim().length < MIN_BODY_LENGTH) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'short_body',\n\t\t\tmessage: 'SKILL.md body is very short',\n\t\t});\n\t}\n\n\t// Check for TODO placeholders\n\tif (\n\t\tbody.includes('TODO') ||\n\t\tbody.includes('[Add your') ||\n\t\tbody.includes('[Provide')\n\t) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'todo_placeholders',\n\t\t\tmessage: 'SKILL.md contains TODO placeholders',\n\t\t});\n\t}\n\n\treturn validation;\n}\n","/**\n * Description validation (Level 1 progressive disclosure)\n */\n\nimport {\n\tDESCRIPTION_MAX_LENGTH,\n\tDESCRIPTION_MIN_LENGTH,\n} from '../constants.js';\nimport type {\n\tTriggerPhraseAnalysis,\n\tUserPhrasingAnalysis,\n} from '../types.js';\nimport { estimate_string_tokens } from './text-analysis.js';\n\nexport interface DescriptionStats {\n\tdescription_length: number;\n\tdescription_tokens: number;\n}\n\nexport interface DescriptionWarning {\n\ttype:\n\t\t| 'length'\n\t\t| 'trigger'\n\t\t| 'list_bloat'\n\t\t| 'short'\n\t\t| 'first_person'\n\t\t| 'second_person'\n\t\t| 'vague'\n\t\t| 'passive';\n\tmessage: string;\n}\n\nexport interface DescriptionError {\n\ttype: 'length';\n\tmessage: string;\n}\n\nexport interface DescriptionValidation {\n\tstats: DescriptionStats;\n\twarnings: DescriptionWarning[];\n\terrors: DescriptionError[];\n}\n\n/**\n * Validate description length and quality\n */\nexport function validate_description_content(\n\tdescription: string,\n): DescriptionValidation {\n\tconst desc_length = description.length;\n\tconst desc_tokens = estimate_string_tokens(description);\n\n\tconst validation: DescriptionValidation = {\n\t\tstats: {\n\t\t\tdescription_length: desc_length,\n\t\t\tdescription_tokens: desc_tokens,\n\t\t},\n\t\twarnings: [],\n\t\terrors: [],\n\t};\n\n\t// Enforced limit: Claude truncates descriptions at this limit in skill listing\n\tif (desc_length > DESCRIPTION_MAX_LENGTH) {\n\t\tvalidation.errors.push({\n\t\t\ttype: 'length',\n\t\t\tmessage:\n\t\t\t\t`Description is ${desc_length} characters (MAX: ${DESCRIPTION_MAX_LENGTH} — Claude truncates at this limit)\\n` +\n\t\t\t\t` → Keep descriptions concise - anything past ${DESCRIPTION_MAX_LENGTH} chars is never seen`,\n\t\t});\n\t}\n\n\t// Check for trigger keywords\n\tconst lower_desc = description.toLowerCase();\n\tconst has_trigger =\n\t\tlower_desc.includes('use when') ||\n\t\tlower_desc.includes('use for') ||\n\t\tlower_desc.includes('use to');\n\n\tif (!has_trigger) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'trigger',\n\t\t\tmessage:\n\t\t\t\t`Description missing trigger keywords ('Use when...', 'Use for...', 'Use to...')\\n` +\n\t\t\t\t` → Help Claude know when to activate this skill`,\n\t\t});\n\t}\n\n\t// Check for list bloat (multiple commas indicating detailed lists)\n\t// Only warn if BOTH long description AND many commas (allows concise technical lists)\n\tconst comma_count = (description.match(/,/g) || []).length;\n\tif (desc_length > 150 && comma_count >= 5) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'list_bloat',\n\t\t\tmessage:\n\t\t\t\t`Description contains long lists (${comma_count} commas, ${desc_length} chars)\\n` +\n\t\t\t\t` → Move detailed lists to Level 2 (SKILL.md body) or Level 3 (references/)`,\n\t\t});\n\t}\n\n\t// Short description check\n\tif (desc_length < DESCRIPTION_MIN_LENGTH) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'short',\n\t\t\tmessage:\n\t\t\t\t`Description is very short (${desc_length} chars, minimum recommended: ${DESCRIPTION_MIN_LENGTH})\\n` +\n\t\t\t\t` → Must answer both \"what does it do\" AND \"when to use it\"`,\n\t\t});\n\t}\n\n\treturn validation;\n}\n\n/**\n * Analyze trigger phrase in description\n */\nexport function analyze_trigger_phrase(\n\tdescription: string,\n): TriggerPhraseAnalysis {\n\tconst lower = description.toLowerCase();\n\tconst has_trigger =\n\t\tlower.includes('use when') ||\n\t\tlower.includes('use for') ||\n\t\tlower.includes('use to');\n\n\tlet trigger_phrase: string | null = null;\n\tlet trigger_type: 'specific' | 'generic' | 'missing' = 'missing';\n\n\tif (has_trigger) {\n\t\tconst match = description.match(\n\t\t\t/(use when|use for|use to)[^.!?]*/i,\n\t\t);\n\t\tif (match) {\n\t\t\ttrigger_phrase = match[0].trim();\n\t\t\ttrigger_type =\n\t\t\t\ttrigger_phrase.length > 50 ? 'specific' : 'generic';\n\t\t}\n\t}\n\n\treturn {\n\t\thas_explicit_trigger: has_trigger,\n\t\ttrigger_phrase,\n\t\ttrigger_type,\n\t};\n}\n\n/**\n * Analyze user phrasing style\n */\nexport function analyze_user_phrasing(description: string): {\n\tanalysis: UserPhrasingAnalysis;\n\twarnings: DescriptionWarning[];\n} {\n\tconst issues: Array<{\n\t\ttype: 'first_person' | 'passive_voice' | 'vague';\n\t\ttext: string;\n\t\tsuggestion: string;\n\t}> = [];\n\tconst warnings: DescriptionWarning[] = [];\n\n\t// Check for first person\n\tconst is_third_person = !/\\b(I can|I will|I help|my|me)\\b/i.test(\n\t\tdescription,\n\t);\n\tconst first_person_patterns = /\\b(I can|I will|I help|my|me)\\b/i;\n\tif (first_person_patterns.test(description)) {\n\t\tconst match = description.match(first_person_patterns);\n\t\tif (match) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'first_person',\n\t\t\t\tmessage:\n\t\t\t\t\t`Description uses first person: \"${match[0]}\"\\n` +\n\t\t\t\t\t` → Anthropic requires third-person voice (e.g., \"Generates...\" not \"I can generate...\")`,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Check for second person\n\tconst second_person_patterns =\n\t\t/\\b(You can|You should|You could|You'll|You will|You need|your)\\b/i;\n\tif (second_person_patterns.test(description)) {\n\t\tconst match = description.match(second_person_patterns);\n\t\tif (match) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'second_person',\n\t\t\t\tmessage:\n\t\t\t\t\t`Description uses second person: \"${match[0]}\"\\n` +\n\t\t\t\t\t` → Anthropic requires third-person voice (e.g., \"Processes...\" not \"You can process...\")`,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Check for vague terms\n\tconst vague_patterns =\n\t\t/\\b(helper|utility|tool|various|several|some)\\b/i;\n\tif (vague_patterns.test(description)) {\n\t\tconst match = description.match(vague_patterns);\n\t\tif (match) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'vague',\n\t\t\t\tmessage:\n\t\t\t\t\t`Description contains vague term: \"${match[0]}\"\\n` +\n\t\t\t\t\t` → Be specific about what the skill does`,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Check for gerund form (verbs ending in -ing)\n\tconst uses_gerund = /\\b\\w+ing\\b/i.test(description);\n\n\t// Check for action-oriented (starts with action verbs)\n\tconst action_verbs =\n\t\t/^(create|build|design|analyze|test|validate|generate|process|manage|execute|handle|provide)/i;\n\tconst is_action_oriented = action_verbs.test(description.trim());\n\n\t// Suggest action-oriented language if neither gerund nor action verb\n\tif (!uses_gerund && !is_action_oriented) {\n\t\twarnings.push({\n\t\t\ttype: 'passive',\n\t\t\tmessage:\n\t\t\t\t`Description lacks action-oriented language\\n` +\n\t\t\t\t` → Start with a verb or gerund (e.g., \"Generates...\", \"Managing...\", \"Extract...\")`,\n\t\t});\n\t}\n\n\tconst analysis: UserPhrasingAnalysis = {\n\t\tstyle_checks: {\n\t\t\tis_third_person,\n\t\t\tuses_gerund_form: uses_gerund,\n\t\t\tis_action_oriented,\n\t\t},\n\t\tissues,\n\t};\n\n\treturn { analysis, warnings };\n}\n","/**\n * File structure validation - paths, scripts, assets\n */\n\nimport {\n\texistsSync,\n\treaddirSync,\n\treadFileSync,\n\tstatSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport type {\n\tPathFormatValidation,\n\tPathFormatIssue,\n} from '../types.js';\n\nexport interface PathFormatError {\n\ttype: 'windows_path';\n\tmessage: string;\n}\n\nexport interface ScriptsWarning {\n\ttype: 'empty_directory' | 'not_executable' | 'missing_shebang';\n\tmessage: string;\n}\n\nexport interface AssetsWarning {\n\ttype: 'empty_directory';\n\tmessage: string;\n}\n\nexport interface DirectoryError {\n\ttype: 'not_found' | 'not_directory';\n\tmessage: string;\n}\n\n/**\n * Validate that skill directory exists and is valid\n */\nexport function validate_directory(skill_path: string): {\n\tvalid: boolean;\n\terrors: DirectoryError[];\n} {\n\tconst errors: DirectoryError[] = [];\n\n\tif (!existsSync(skill_path)) {\n\t\terrors.push({\n\t\t\ttype: 'not_found',\n\t\t\tmessage: `Skill directory does not exist: ${skill_path}`,\n\t\t});\n\t\treturn { valid: false, errors };\n\t}\n\n\tconst stats = statSync(skill_path);\n\tif (!stats.isDirectory()) {\n\t\terrors.push({\n\t\t\ttype: 'not_directory',\n\t\t\tmessage: `Path is not a directory: ${skill_path}`,\n\t\t});\n\t\treturn { valid: false, errors };\n\t}\n\n\treturn { valid: true, errors: [] };\n}\n\n/**\n * Validate path formats (no Windows backslashes)\n */\nexport function validate_path_formats(\n\tcontent: string,\n\tfile_name: string = 'SKILL.md',\n): {\n\tvalidation: PathFormatValidation;\n\terrors: PathFormatError[];\n} {\n\tconst invalid_paths: PathFormatIssue[] = [];\n\tconst errors: PathFormatError[] = [];\n\tconst lines = content.split('\\n');\n\n\tlines.forEach((line, index) => {\n\t\t// Skip code blocks (they might legitimately show Windows paths as examples)\n\t\tif (line.trim().startsWith('```')) return;\n\n\t\t// Detect backslashes in file paths\n\t\t// Match patterns like: scripts\\file.py, references\\doc.md, etc.\n\t\tconst backslash_pattern =\n\t\t\t/(?:scripts|references|assets|examples)\\\\[\\w\\\\.-]+/g;\n\t\tconst matches = line.match(backslash_pattern);\n\n\t\tif (matches) {\n\t\t\tmatches.forEach((match) => {\n\t\t\t\tconst fixed = match.replace(/\\\\/g, '/');\n\n\t\t\t\t// Store in validation\n\t\t\t\tinvalid_paths.push({\n\t\t\t\t\tline_number: index + 1,\n\t\t\t\t\tpath: match,\n\t\t\t\t\terror: 'Windows-style backslash detected',\n\t\t\t\t\tsuggested_fix: fixed,\n\t\t\t\t});\n\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: 'windows_path',\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Windows-style path in ${file_name}:${index + 1}\\n` +\n\t\t\t\t\t\t` → Found: ${match}\\n` +\n\t\t\t\t\t\t` → Use: ${fixed}`,\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t});\n\n\treturn {\n\t\tvalidation: { invalid_paths },\n\t\terrors,\n\t};\n}\n\n/**\n * Validate scripts directory\n */\nexport function validate_scripts(skill_path: string): {\n\twarnings: ScriptsWarning[];\n} {\n\tconst scripts_dir = join(skill_path, 'scripts');\n\tconst warnings: ScriptsWarning[] = [];\n\n\tif (existsSync(scripts_dir)) {\n\t\tconst files = readdirSync(scripts_dir);\n\t\tconst script_files = files.filter(\n\t\t\t(f) =>\n\t\t\t\tf.endsWith('.js') ||\n\t\t\t\tf.endsWith('.ts') ||\n\t\t\t\tf.endsWith('.mjs') ||\n\t\t\t\tf.endsWith('.sh'),\n\t\t);\n\n\t\tif (script_files.length === 0) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'empty_directory',\n\t\t\t\tmessage: 'scripts/ directory exists but is empty',\n\t\t\t});\n\t\t}\n\n\t\tfor (const script_file of script_files) {\n\t\t\tconst script_path = join(scripts_dir, script_file);\n\t\t\tconst stats = statSync(script_path);\n\n\t\t\t// Check if executable (0o111 = --x--x--x)\n\t\t\tif ((stats.mode & 0o111) === 0) {\n\t\t\t\twarnings.push({\n\t\t\t\t\ttype: 'not_executable',\n\t\t\t\t\tmessage: `Script is not executable: ${script_file}`,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Check for shebang\n\t\t\tconst content = readFileSync(script_path, 'utf-8');\n\t\t\tconst first_line = content.split('\\n')[0];\n\t\t\tif (!first_line.startsWith('#!')) {\n\t\t\t\twarnings.push({\n\t\t\t\t\ttype: 'missing_shebang',\n\t\t\t\t\tmessage: `Script missing shebang: ${script_file}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { warnings };\n}\n\n/**\n * Validate assets directory\n */\nexport function validate_assets(skill_path: string): {\n\twarnings: AssetsWarning[];\n} {\n\tconst assets_dir = join(skill_path, 'assets');\n\tconst warnings: AssetsWarning[] = [];\n\n\tif (existsSync(assets_dir)) {\n\t\tconst files = readdirSync(assets_dir);\n\n\t\tif (files.length === 0) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'empty_directory',\n\t\t\t\tmessage: 'assets/ directory exists but is empty',\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { warnings };\n}\n","/**\n * References validation (Level 3 progressive disclosure)\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ReferenceNesting } from '../types.js';\n\nexport interface ReferencesValidation {\n\tfiles_found: string[];\n\tfiles_referenced: string[];\n\tmissing_files: string[];\n\torphaned_files: string[];\n\tnesting: ReferenceNesting[];\n\tmax_nesting_depth: number;\n}\n\nexport interface ReferencesWarning {\n\ttype: 'empty_directory' | 'orphaned_file' | 'nesting_depth';\n\tmessage: string;\n}\n\nexport interface ReferencesError {\n\ttype: 'missing_file';\n\tmessage: string;\n}\n\nexport interface ReferencesResult {\n\tvalidation: ReferencesValidation;\n\twarnings: ReferencesWarning[];\n\terrors: ReferencesError[];\n}\n\n/**\n * Strip fenced code blocks from content to avoid parsing example links\n */\nfunction strip_code_blocks(content: string): string {\n\t// Remove fenced code blocks (``` or ~~~)\n\treturn content.replace(/```[\\s\\S]*?```|~~~[\\s\\S]*?~~~/g, '');\n}\n\n/**\n * Check nesting depth of reference files\n */\nfunction check_reference_nesting(\n\tskill_path: string,\n\tfile_path: string,\n\tvisited: Set<string> = new Set(),\n): { depth: number; references: string[] } {\n\tif (visited.has(file_path)) {\n\t\treturn { depth: 0, references: [] };\n\t}\n\tvisited.add(file_path);\n\n\tconst full_path = join(skill_path, file_path);\n\tif (!existsSync(full_path)) {\n\t\treturn { depth: 0, references: [] };\n\t}\n\n\tconst content = readFileSync(full_path, 'utf-8');\n\t// Strip code blocks to avoid parsing example links inside them\n\tconst content_without_code = strip_code_blocks(content);\n\tconst reference_pattern = /\\[([^\\]]+)\\]\\(([^)]+\\.md)\\)/g;\n\tconst matches = [\n\t\t...content_without_code.matchAll(reference_pattern),\n\t];\n\tconst references = matches.map((m) => m[2]);\n\n\tif (references.length === 0) {\n\t\treturn { depth: 1, references: [] };\n\t}\n\n\t// Recursively check nested references\n\tlet max_depth = 1;\n\tfor (const ref of references) {\n\t\tconst nested = check_reference_nesting(\n\t\t\tskill_path,\n\t\t\tref,\n\t\t\tnew Set(visited),\n\t\t);\n\t\tmax_depth = Math.max(max_depth, 1 + nested.depth);\n\t}\n\n\treturn { depth: max_depth, references };\n}\n\n/**\n * Validate references directory and links\n */\nexport function validate_references(\n\tskill_path: string,\n): ReferencesResult {\n\tconst references_dir = join(skill_path, 'references');\n\tconst skill_md_path = join(skill_path, 'SKILL.md');\n\n\tconst files_found: string[] = [];\n\tconst files_referenced: string[] = [];\n\tconst missing_files: string[] = [];\n\tconst nesting_data: ReferenceNesting[] = [];\n\tconst warnings: ReferencesWarning[] = [];\n\tconst errors: ReferencesError[] = [];\n\n\t// Check references directory if it exists\n\tif (existsSync(references_dir)) {\n\t\tconst files = readdirSync(references_dir);\n\t\tconst md_files = files.filter((f) => f.endsWith('.md'));\n\t\tfiles_found.push(...md_files.map((f) => `references/${f}`));\n\n\t\tif (md_files.length === 0) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'empty_directory',\n\t\t\t\tmessage: 'references/ directory exists but is empty',\n\t\t\t});\n\t\t}\n\n\t\t// Check for references in SKILL.md\n\t\tif (existsSync(skill_md_path)) {\n\t\t\tconst skill_content = readFileSync(skill_md_path, 'utf-8');\n\n\t\t\tfor (const md_file of md_files) {\n\t\t\t\tif (!skill_content.includes(md_file)) {\n\t\t\t\t\twarnings.push({\n\t\t\t\t\t\ttype: 'orphaned_file',\n\t\t\t\t\t\tmessage: `Reference file 'references/${md_file}' not mentioned in SKILL.md`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check root-level .md files (excluding SKILL.md)\n\tif (existsSync(skill_path)) {\n\t\tconst root_files = readdirSync(skill_path);\n\t\tconst root_md_files = root_files.filter(\n\t\t\t(f) =>\n\t\t\t\tf.endsWith('.md') && f !== 'SKILL.md' && f !== 'README.md',\n\t\t);\n\t\tfiles_found.push(...root_md_files);\n\n\t\tif (existsSync(skill_md_path)) {\n\t\t\tconst skill_content = readFileSync(skill_md_path, 'utf-8');\n\n\t\t\tfor (const md_file of root_md_files) {\n\t\t\t\tif (!skill_content.includes(md_file)) {\n\t\t\t\t\twarnings.push({\n\t\t\t\t\t\ttype: 'orphaned_file',\n\t\t\t\t\t\tmessage: `Root file '${md_file}' not mentioned in SKILL.md`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Level 3 validation: Check that all referenced files exist\n\tif (existsSync(skill_md_path)) {\n\t\tconst skill_content = readFileSync(skill_md_path, 'utf-8');\n\t\t// Strip code blocks to avoid parsing example links inside them\n\t\tconst content_without_code = strip_code_blocks(skill_content);\n\n\t\t// Extract markdown links to .md files (references/ or root-level)\n\t\t// Matches: [text](file.md), [text](references/file.md), etc.\n\t\tconst reference_link_pattern = /\\[([^\\]]+)\\]\\(([^)]+\\.md)\\)/g;\n\t\tconst matches = content_without_code.matchAll(\n\t\t\treference_link_pattern,\n\t\t);\n\n\t\tfor (const match of matches) {\n\t\t\tconst link_text = match[1];\n\t\t\tconst file_path = match[2]; // e.g., \"references/examples.md\"\n\t\t\tconst full_path = join(skill_path, file_path);\n\n\t\t\tfiles_referenced.push(file_path);\n\n\t\t\tif (!existsSync(full_path)) {\n\t\t\t\tmissing_files.push(file_path);\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: 'missing_file',\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Referenced file not found: ${file_path}\\n` +\n\t\t\t\t\t\t` → Linked from: [${link_text}]\\n` +\n\t\t\t\t\t\t` → Create the file or remove the broken link`,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Check nesting depth\n\t\t\t\tconst nesting = check_reference_nesting(\n\t\t\t\t\tskill_path,\n\t\t\t\t\tfile_path,\n\t\t\t\t);\n\t\t\t\tlet warning: string | null = null;\n\n\t\t\t\tif (nesting.depth > 1) {\n\t\t\t\t\twarning = `File has depth ${nesting.depth} (recommended: 1). Keep references one level deep from SKILL.md.`;\n\t\t\t\t\twarnings.push({\n\t\t\t\t\t\ttype: 'nesting_depth',\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`${file_path} has nesting depth ${nesting.depth} (recommended: 1)\\n` +\n\t\t\t\t\t\t\t` → Keep references one level deep from SKILL.md for clarity`,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tnesting_data.push({\n\t\t\t\t\tfile: file_path,\n\t\t\t\t\treferences: nesting.references,\n\t\t\t\t\tdepth: nesting.depth,\n\t\t\t\t\twarning,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Calculate orphaned files\n\tconst orphaned = files_found.filter(\n\t\t(f) => !files_referenced.some((ref) => ref.includes(f)),\n\t);\n\n\tconst validation: ReferencesValidation = {\n\t\tfiles_found,\n\t\tfiles_referenced,\n\t\tmissing_files,\n\t\torphaned_files: orphaned,\n\t\tnesting: nesting_data,\n\t\tmax_nesting_depth:\n\t\t\tnesting_data.length > 0\n\t\t\t\t? Math.max(...nesting_data.map((n) => n.depth))\n\t\t\t\t: 0,\n\t};\n\n\treturn { validation, warnings, errors };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n\tDESCRIPTION_MAX_LENGTH,\n\tLIMITS,\n\tNAME_MAX_LENGTH,\n} from '../constants.js';\nimport type {\n\tStructuredValidation,\n\tValidationResult,\n\tValidationStats,\n} from '../types.js';\n\n// Import validators\nimport { analyze_alignment } from '../validators/alignment-validator.js';\nimport { validate_content } from '../validators/content-validator.js';\nimport {\n\tanalyze_trigger_phrase,\n\tanalyze_user_phrasing,\n\tvalidate_description_content,\n} from '../validators/description-validator.js';\nimport {\n\tvalidate_assets,\n\tvalidate_directory,\n\tvalidate_path_formats,\n\tvalidate_scripts,\n} from '../validators/file-structure-validator.js';\nimport {\n\textract_frontmatter,\n\tvalidate_frontmatter_structure,\n\tvalidate_hard_limits,\n\tvalidate_name_format,\n} from '../validators/frontmatter-validator.js';\nimport { validate_references } from '../validators/references-validator.js';\n\nimport type { ValidationMode } from '../types.js';\n\nexport interface ValidatorOptions {\n\tmode?: ValidationMode;\n}\n\nexport class SkillValidator {\n\tprivate skill_path: string;\n\tprivate options: ValidatorOptions;\n\tprivate errors: string[] = [];\n\tprivate warnings: string[] = [];\n\tprivate stats: ValidationStats = {\n\t\tword_count: 0,\n\t\testimated_tokens: 0,\n\t\tline_count: 0,\n\t\tdescription_length: 0,\n\t\tdescription_tokens: 0,\n\t\tcode_blocks: 0,\n\t\tsections: 0,\n\t\tlong_paragraphs: 0,\n\t};\n\n\t// Structured validation data\n\tprivate structured_validation: StructuredValidation = {\n\t\thard_limits: {\n\t\t\tname: {\n\t\t\t\tlength: 0,\n\t\t\t\tlimit: NAME_MAX_LENGTH,\n\t\t\t\tvalid: true,\n\t\t\t\terror: null,\n\t\t\t},\n\t\t\tdescription: {\n\t\t\t\tlength: 0,\n\t\t\t\tlimit: DESCRIPTION_MAX_LENGTH,\n\t\t\t\tvalid: true,\n\t\t\t\terror: null,\n\t\t\t},\n\t\t},\n\t\tname_format: {\n\t\t\tname: '',\n\t\t\tformat_valid: true,\n\t\t\tdirectory_name: '',\n\t\t\tmatches_directory: true,\n\t\t\terrors: [],\n\t\t},\n\t\tyaml_validation: {\n\t\t\tvalid: true,\n\t\t\thas_frontmatter: false,\n\t\t\tparse_error: null,\n\t\t\tmissing_fields: [],\n\t\t},\n\t\tpath_format: {\n\t\t\tinvalid_paths: [],\n\t\t},\n\t\ttriggering: {\n\t\t\ttrigger_phrase: {\n\t\t\t\thas_explicit_trigger: false,\n\t\t\t\ttrigger_phrase: null,\n\t\t\t\ttrigger_type: 'missing',\n\t\t\t},\n\t\t\tuser_phrasing: {\n\t\t\t\tstyle_checks: {\n\t\t\t\t\tis_third_person: true,\n\t\t\t\t\tuses_gerund_form: true,\n\t\t\t\t\tis_action_oriented: true,\n\t\t\t\t},\n\t\t\t\tissues: [],\n\t\t\t},\n\t\t\tkeywords: {\n\t\t\t\tdescription_keywords: [],\n\t\t\t\tcontent_keywords: [],\n\t\t\t\toverlap: [],\n\t\t\t\tdescription_only: [],\n\t\t\t\tcontent_only: [],\n\t\t\t},\n\t\t\talignment: {\n\t\t\t\tseverity: 'good',\n\t\t\t\tdescription_focus: [],\n\t\t\t\tcontent_focus: [],\n\t\t\t\tmatches: [],\n\t\t\t\tmismatches: [],\n\t\t\t\texplanation: '',\n\t\t\t},\n\t\t},\n\t};\n\n\tconstructor(skill_path: string, options: ValidatorOptions = {}) {\n\t\tthis.skill_path = skill_path;\n\t\tthis.options = options;\n\t}\n\n\tprivate error(msg: string): void {\n\t\tthis.errors.push(`❌ ${msg}`);\n\t}\n\n\tprivate warning(msg: string): void {\n\t\tthis.warnings.push(`⚠️ ${msg}`);\n\t}\n\n\tprivate validate_skill_md(): boolean {\n\t\tconst skill_md_path = join(this.skill_path, 'SKILL.md');\n\n\t\tif (!existsSync(skill_md_path)) {\n\t\t\tthis.error('SKILL.md file not found');\n\t\t\treturn false;\n\t\t}\n\n\t\tconst content = readFileSync(skill_md_path, 'utf-8');\n\n\t\t// Validate path formats (no Windows backslashes)\n\t\tconst path_format_result = validate_path_formats(content);\n\t\tthis.structured_validation.path_format =\n\t\t\tpath_format_result.validation;\n\t\tpath_format_result.errors.forEach((err) =>\n\t\t\tthis.error(err.message),\n\t\t);\n\n\t\t// Validate frontmatter structure\n\t\tconst frontmatter_validation =\n\t\t\tvalidate_frontmatter_structure(content);\n\t\tthis.structured_validation.yaml_validation =\n\t\t\tfrontmatter_validation;\n\n\t\tif (!frontmatter_validation.valid) {\n\t\t\tif (frontmatter_validation.parse_error) {\n\t\t\t\tthis.error(frontmatter_validation.parse_error);\n\t\t\t}\n\t\t\tfrontmatter_validation.missing_fields.forEach((field) => {\n\t\t\t\tthis.error(`SKILL.md frontmatter missing '${field}' field`);\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\t// Warn about unknown frontmatter fields\n\t\tif (frontmatter_validation.unknown_fields?.length) {\n\t\t\tfor (const field of frontmatter_validation.unknown_fields) {\n\t\t\t\tthis.warning(\n\t\t\t\t\t`Unknown frontmatter field '${field}'\\n` +\n\t\t\t\t\t\t` → See https://code.claude.com/docs/en/skills#frontmatter-reference`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Warn about invalid field values\n\t\tif (frontmatter_validation.field_value_warnings?.length) {\n\t\t\tfor (const warn of frontmatter_validation.field_value_warnings) {\n\t\t\t\tthis.warning(warn);\n\t\t\t}\n\t\t}\n\n\t\t// Extract frontmatter data\n\t\tconst { name, description, body, description_is_multiline } =\n\t\t\textract_frontmatter(content);\n\n\t\tif (!name || !description) {\n\t\t\tthis.error(\n\t\t\t\t'Failed to extract name or description from frontmatter',\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\t// Warn if description spans multiple lines\n\t\tif (description_is_multiline) {\n\t\t\tthis.warning(\n\t\t\t\t`Multi-line description detected. Claude Code cannot recognize skills with multi-line descriptions.\\n` +\n\t\t\t\t\t` → Run 'claude-skills-cli doctor ${this.skill_path}' to fix automatically`,\n\t\t\t);\n\t\t}\n\n\t\t// Get directory name (normalize path to handle trailing slashes)\n\t\tconst normalized_path = this.skill_path.replace(/\\/+$/, '');\n\t\tconst dir_name = normalized_path.split('/').pop() || '';\n\n\t\t// Validate name format\n\t\tconst name_validation = validate_name_format(name, dir_name);\n\t\tthis.structured_validation.name_format = name_validation;\n\t\tname_validation.errors.forEach((err) => this.error(err));\n\n\t\t// Validate hard limits\n\t\tconst hard_limits = validate_hard_limits(name, description);\n\t\tthis.structured_validation.hard_limits = hard_limits;\n\n\t\tif (!hard_limits.name.valid && hard_limits.name.error) {\n\t\t\tthis.error(hard_limits.name.error);\n\t\t}\n\n\t\tif (\n\t\t\t!hard_limits.description.valid &&\n\t\t\thard_limits.description.error\n\t\t) {\n\t\t\tthis.error(hard_limits.description.error);\n\t\t}\n\n\t\t// Validate description content\n\t\tconst desc_validation = validate_description_content(description);\n\t\tthis.stats.description_length =\n\t\t\tdesc_validation.stats.description_length;\n\t\tthis.stats.description_tokens =\n\t\t\tdesc_validation.stats.description_tokens;\n\t\tdesc_validation.errors.forEach((err) => this.error(err.message));\n\t\tdesc_validation.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\t// Analyze trigger phrase\n\t\tconst trigger_analysis = analyze_trigger_phrase(description);\n\t\tthis.structured_validation.triggering!.trigger_phrase =\n\t\t\ttrigger_analysis;\n\n\t\tif (!trigger_analysis.has_explicit_trigger) {\n\t\t\tthis.warning(\n\t\t\t\t`Description missing explicit trigger phrase ('Use when...', 'Use for...', 'Use to...')\\n` +\n\t\t\t\t\t` → Help Claude know when to activate this skill`,\n\t\t\t);\n\t\t}\n\n\t\t// Analyze user phrasing\n\t\tconst {\n\t\t\tanalysis: phrasing_analysis,\n\t\t\twarnings: phrasing_warnings,\n\t\t} = analyze_user_phrasing(description);\n\t\tthis.structured_validation.triggering!.user_phrasing =\n\t\t\tphrasing_analysis;\n\t\tphrasing_warnings.forEach((warn) => this.warning(warn.message));\n\n\t\t// Analyze alignment\n\t\tconst alignment_result = analyze_alignment(description, body);\n\t\tthis.structured_validation.triggering!.keywords =\n\t\t\talignment_result.keywords;\n\t\tthis.structured_validation.triggering!.alignment =\n\t\t\talignment_result.alignment;\n\t\talignment_result.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\t// Validate content (progressive disclosure)\n\t\tconst content_validation = validate_content(body, {\n\t\t\tmode: this.options.mode,\n\t\t});\n\t\tthis.stats.word_count = content_validation.stats.word_count;\n\t\tthis.stats.estimated_tokens =\n\t\t\tcontent_validation.stats.estimated_tokens;\n\t\tthis.stats.line_count = content_validation.stats.line_count;\n\t\tthis.stats.code_blocks = content_validation.stats.code_blocks;\n\t\tthis.stats.sections = content_validation.stats.sections;\n\t\tthis.stats.long_paragraphs =\n\t\t\tcontent_validation.stats.long_paragraphs;\n\t\tcontent_validation.errors.forEach((err) =>\n\t\t\tthis.error(err.message),\n\t\t);\n\t\tcontent_validation.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\treturn true;\n\t}\n\n\tpublic validate_all(): ValidationResult {\n\t\t// Validate directory\n\t\tconst dir_result = validate_directory(this.skill_path);\n\t\tif (!dir_result.valid) {\n\t\t\tdir_result.errors.forEach((err) => this.error(err.message));\n\t\t\treturn {\n\t\t\t\terrors: this.errors,\n\t\t\t\twarnings: this.warnings,\n\t\t\t\tis_valid: false,\n\t\t\t\tstats: this.stats,\n\t\t\t\tvalidation: this.structured_validation,\n\t\t\t};\n\t\t}\n\n\t\t// Validate SKILL.md\n\t\tthis.validate_skill_md();\n\n\t\t// Validate references\n\t\tconst refs_result = validate_references(this.skill_path);\n\t\trefs_result.errors.forEach((err) => this.error(err.message));\n\t\trefs_result.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\t// Validate scripts\n\t\tconst scripts_result = validate_scripts(this.skill_path);\n\t\tscripts_result.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\t// Validate assets\n\t\tconst assets_result = validate_assets(this.skill_path);\n\t\tassets_result.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\t// Populate progressive disclosure structured validation\n\t\tconst mode_limits = LIMITS[this.options.mode || 'strict'];\n\t\tconst line_limit = mode_limits.lines.max;\n\t\tconst word_limit = mode_limits.words.max;\n\t\tthis.structured_validation.progressive_disclosure = {\n\t\t\tskill_md_size: {\n\t\t\t\tlines: this.stats.line_count,\n\t\t\t\twords: this.stats.word_count,\n\t\t\t\ttokens: this.stats.estimated_tokens,\n\t\t\t\texceeds_line_limit: this.stats.line_count > line_limit,\n\t\t\t\texceeds_word_limit: this.stats.word_count > word_limit,\n\t\t\t},\n\t\t\treferences: refs_result.validation,\n\t\t};\n\n\t\treturn {\n\t\t\terrors: this.errors,\n\t\t\twarnings: this.warnings,\n\t\t\tis_valid: this.errors.length === 0,\n\t\t\tstats: this.stats,\n\t\t\tvalidation: this.structured_validation,\n\t\t};\n\t}\n\n\tpublic get_errors(): string[] {\n\t\treturn this.errors;\n\t}\n\n\tpublic get_warnings(): string[] {\n\t\treturn this.warnings;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"validator-Dp5x-OjP.js","names":[],"sources":["../src/validators/text-analysis.ts","../src/validators/alignment-validator.ts","../src/validators/content-validator.ts","../src/validators/description-validator.ts","../src/validators/file-structure-validator.ts","../src/validators/references-validator.ts","../src/core/validator.ts"],"sourcesContent":["/**\n * Text analysis utilities for skill validation\n */\n\n/**\n * Extract body content from SKILL.md (excluding YAML frontmatter)\n */\nexport function extract_body(content: string): string {\n\tconst parts = content.split('---\\n');\n\treturn parts.length >= 3\n\t\t? parts.slice(2).join('---\\n').trim()\n\t\t: content;\n}\n\n/**\n * Count words in text\n */\nexport function count_words(text: string): number {\n\treturn text\n\t\t.trim()\n\t\t.split(/\\s+/)\n\t\t.filter((w) => w.length > 0).length;\n}\n\n/**\n * Estimate tokens (rough approximation: 1 word ≈ 1.3 tokens for English)\n */\nexport function estimate_tokens(word_count: number): number {\n\treturn Math.round(word_count * 1.3);\n}\n\n/**\n * Estimate tokens for a string by counting words and applying ratio\n */\nexport function estimate_string_tokens(text: string): number {\n\tconst word_count = count_words(text);\n\treturn estimate_tokens(word_count);\n}\n\n/**\n * Remove HTML comments from content (for line counting)\n */\nexport function strip_html_comments(text: string): string {\n\treturn text.replace(/<!--[\\s\\S]*?-->/g, '');\n}\n\n/**\n * Extract keywords from text (simplified extraction)\n */\nexport function extract_keywords(text: string): string[] {\n\tconst words = text\n\t\t.toLowerCase()\n\t\t.replace(/[^\\w\\s-]/g, ' ')\n\t\t.split(/\\s+/)\n\t\t.filter((w) => w.length > 3);\n\n\tconst unique = [...new Set(words)];\n\treturn unique.filter(\n\t\t(w) =>\n\t\t\t![\n\t\t\t\t'this',\n\t\t\t\t'that',\n\t\t\t\t'with',\n\t\t\t\t'from',\n\t\t\t\t'have',\n\t\t\t\t'will',\n\t\t\t\t'when',\n\t\t\t\t'what',\n\t\t\t\t'where',\n\t\t\t\t'which',\n\t\t\t\t'their',\n\t\t\t\t'them',\n\t\t\t\t'then',\n\t\t\t\t'than',\n\t\t\t\t'these',\n\t\t\t\t'those',\n\t\t\t\t'there',\n\t\t\t].includes(w),\n\t);\n}\n","/**\n * Alignment validation - checks description and content alignment\n */\n\nimport { extract_keywords } from './text-analysis.js';\nimport type { KeywordAnalysis, AlignmentAnalysis } from '../types.js';\n\nexport interface AlignmentWarning {\n\ttype: 'low_overlap';\n\tmessage: string;\n}\n\nexport interface AlignmentValidation {\n\tkeywords: KeywordAnalysis;\n\talignment: AlignmentAnalysis;\n\twarnings: AlignmentWarning[];\n}\n\n/**\n * Analyze description and content alignment\n */\nexport function analyze_alignment(\n\tdescription: string,\n\tbody: string,\n): AlignmentValidation {\n\tconst desc_keywords = extract_keywords(description);\n\tconst content_keywords = extract_keywords(body);\n\n\tconst overlap = desc_keywords.filter((k) =>\n\t\tcontent_keywords.includes(k),\n\t);\n\tconst desc_only = desc_keywords.filter(\n\t\t(k) => !content_keywords.includes(k),\n\t);\n\tconst content_only = content_keywords\n\t\t.filter((k) => !desc_keywords.includes(k))\n\t\t.slice(0, 20);\n\n\tconst overlap_ratio =\n\t\tdesc_keywords.length > 0\n\t\t\t? overlap.length / desc_keywords.length\n\t\t\t: 0;\n\n\tlet severity: 'good' | 'moderate' | 'critical' = 'good';\n\tlet explanation = 'Description aligns well with content';\n\n\tif (overlap_ratio < 0.2 && desc_keywords.length > 5) {\n\t\tseverity = 'critical';\n\t\texplanation = `Very low keyword overlap (${Math.round(overlap_ratio * 100)}%). Description may not match skill content.`;\n\t} else if (overlap_ratio < 0.3 && desc_keywords.length > 5) {\n\t\tseverity = 'moderate';\n\t\texplanation = `Low keyword overlap (${Math.round(overlap_ratio * 100)}%). Description may not accurately reflect skill content.`;\n\t}\n\n\tconst keywords: KeywordAnalysis = {\n\t\tdescription_keywords: desc_keywords,\n\t\tcontent_keywords: content_keywords.slice(0, 30),\n\t\toverlap,\n\t\tdescription_only: desc_only,\n\t\tcontent_only,\n\t};\n\n\tconst alignment: AlignmentAnalysis = {\n\t\tseverity,\n\t\tdescription_focus: desc_keywords.slice(0, 10),\n\t\tcontent_focus: content_keywords.slice(0, 10),\n\t\tmatches: overlap,\n\t\tmismatches: desc_only,\n\t\texplanation,\n\t};\n\n\tconst warnings: AlignmentWarning[] = [];\n\n\tif (overlap_ratio < 0.3 && desc_keywords.length > 5) {\n\t\twarnings.push({\n\t\t\ttype: 'low_overlap',\n\t\t\tmessage:\n\t\t\t\t`Low keyword overlap between description and content (${Math.round(overlap_ratio * 100)}%)\\n` +\n\t\t\t\t` → Description may not accurately reflect skill content`,\n\t\t});\n\t}\n\n\treturn { keywords, alignment, warnings };\n}\n","/**\n * Content validation (Level 2 progressive disclosure)\n */\n\nimport {\n\tLIMITS,\n\tLONG_PARAGRAPH_WORDS,\n\tMIN_BODY_LENGTH,\n} from '../constants.js';\nimport {\n\tcount_words,\n\testimate_tokens,\n\tstrip_html_comments,\n} from './text-analysis.js';\n\nexport interface ContentStats {\n\tword_count: number;\n\testimated_tokens: number;\n\tline_count: number;\n\tcode_blocks: number;\n\tsections: number;\n\tlong_paragraphs: number;\n}\n\nexport interface ContentWarning {\n\ttype:\n\t\t| 'word_count'\n\t\t| 'line_count'\n\t\t| 'code_blocks'\n\t\t| 'long_paragraphs'\n\t\t| 'sections'\n\t\t| 'missing_quick_start'\n\t\t| 'no_references'\n\t\t| 'short_body'\n\t\t| 'todo_placeholders';\n\tmessage: string;\n}\n\nexport interface ContentError {\n\ttype: 'word_count' | 'line_count';\n\tmessage: string;\n}\n\nexport interface ContentValidation {\n\tstats: ContentStats;\n\twarnings: ContentWarning[];\n\terrors: ContentError[];\n}\n\nimport type { ValidationMode } from '../types.js';\n\nexport interface ContentValidationOptions {\n\tmode?: ValidationMode;\n}\n\n/**\n * Analyze content structure and patterns\n */\nexport function analyze_content_structure(\n\tbody: string,\n): Pick<\n\tContentStats,\n\t'code_blocks' | 'sections' | 'long_paragraphs'\n> {\n\t// Count code blocks\n\tconst code_block_matches = body.match(/```[\\s\\S]*?```/g);\n\tconst code_blocks = code_block_matches\n\t\t? code_block_matches.length\n\t\t: 0;\n\n\t// Count markdown sections (headings)\n\tconst heading_matches = body.match(/^#{1,6}\\s/gm);\n\tconst sections = heading_matches ? heading_matches.length : 0;\n\n\t// Count long paragraphs\n\tconst paragraphs = body.split(/\\n\\n+/);\n\tconst long_paragraphs = paragraphs.filter((p) => {\n\t\tconst words = count_words(p);\n\t\treturn words > LONG_PARAGRAPH_WORDS;\n\t}).length;\n\n\treturn { code_blocks, sections, long_paragraphs };\n}\n\n/**\n * Validate progressive disclosure (word count, token budget, and line count)\n */\nexport function validate_content(\n\tbody: string,\n\toptions: ContentValidationOptions = {},\n): ContentValidation {\n\tconst { mode = 'strict' } = options;\n\tconst limits = LIMITS[mode];\n\n\tconst word_count = count_words(body);\n\tconst estimated_tokens = estimate_tokens(word_count);\n\n\t// Strip HTML comments before counting lines (progressive disclosure guidance shouldn't inflate count)\n\tconst body_without_comments = strip_html_comments(body);\n\tconst line_count = body_without_comments.trim().split('\\n').length;\n\n\t// Analyze content structure\n\tconst structure = analyze_content_structure(body);\n\n\tconst validation: ContentValidation = {\n\t\tstats: {\n\t\t\tword_count,\n\t\t\testimated_tokens,\n\t\t\tline_count,\n\t\t\t...structure,\n\t\t},\n\t\twarnings: [],\n\t\terrors: [],\n\t};\n\n\t// Word count validation\n\tif (word_count > limits.words.max) {\n\t\tvalidation.errors.push({\n\t\t\ttype: 'word_count',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md body has ${word_count} words (MAX: ${limits.words.max})\\n` +\n\t\t\t\t` → Move detailed content to references/ directory for Level 3 loading\\n` +\n\t\t\t\t` → This is a hard limit - skills must be concise`,\n\t\t});\n\t} else if (word_count > limits.words.good) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'word_count',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md body has ${word_count} words (recommended: <${limits.words.good}, max: ${limits.words.max})\\n` +\n\t\t\t\t` → Consider moving examples/docs to references/ for better token efficiency`,\n\t\t});\n\t}\n\n\t// Line count validation (Level 2 progressive disclosure)\n\tif (line_count > limits.lines.max) {\n\t\tvalidation.errors.push({\n\t\t\ttype: 'line_count',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md body is ${line_count} lines (MAX: ${limits.lines.max})\\n` +\n\t\t\t\t` → Move detailed content to references/ directory\\n` +\n\t\t\t\t` → This is a hard limit - skills must be concise`,\n\t\t});\n\t} else if (line_count > limits.lines.good) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'line_count',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md body is ${line_count} lines (recommended: <${limits.lines.good}, max: ${limits.lines.max})\\n` +\n\t\t\t\t` → Consider moving examples to references/ for Level 3 loading`,\n\t\t});\n\t}\n\n\t// Content analysis warnings\n\t// Code blocks: Recommend 1-2, warn at >3\n\tif (structure.code_blocks > 3) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'code_blocks',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md contains ${structure.code_blocks} code examples (recommended: 1-2)\\n` +\n\t\t\t\t` → Move additional examples to references/examples.md for Level 3 loading`,\n\t\t});\n\t}\n\n\t// Long paragraphs\n\tif (structure.long_paragraphs > 3) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'long_paragraphs',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md contains ${structure.long_paragraphs} lengthy paragraphs (>${LONG_PARAGRAPH_WORDS} words)\\n` +\n\t\t\t\t` → Consider moving detailed explanations to references/`,\n\t\t});\n\t}\n\n\t// Sections: Recommend 3-5, warn at >8\n\tif (structure.sections > 8) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'sections',\n\t\t\tmessage:\n\t\t\t\t`SKILL.md contains ${structure.sections} sections (recommended: 3-5)\\n` +\n\t\t\t\t` → Consider splitting into focused reference files`,\n\t\t});\n\t}\n\n\t// Check for \"Quick Start\" section\n\tif (\n\t\t!body.includes('## Quick Start') &&\n\t\t!body.includes('## Quick start')\n\t) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'missing_quick_start',\n\t\t\tmessage:\n\t\t\t\t`Missing \"## Quick Start\" section\\n` +\n\t\t\t\t` → Add one minimal working example to help Claude get started quickly`,\n\t\t});\n\t}\n\n\t// Check for references/ links when body is long (warn when exceeding good threshold)\n\tconst has_references = body.includes('references/');\n\tif (!has_references && line_count > limits.lines.good) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'no_references',\n\t\t\tmessage:\n\t\t\t\t`No references/ links found but SKILL.md is ${line_count} lines\\n` +\n\t\t\t\t` → Consider splitting detailed content into reference files`,\n\t\t});\n\t}\n\n\t// Check body content\n\tif (body.trim().length < MIN_BODY_LENGTH) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'short_body',\n\t\t\tmessage: 'SKILL.md body is very short',\n\t\t});\n\t}\n\n\t// Check for TODO placeholders\n\tif (\n\t\tbody.includes('TODO') ||\n\t\tbody.includes('[Add your') ||\n\t\tbody.includes('[Provide')\n\t) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'todo_placeholders',\n\t\t\tmessage: 'SKILL.md contains TODO placeholders',\n\t\t});\n\t}\n\n\treturn validation;\n}\n","/**\n * Description validation (Level 1 progressive disclosure)\n */\n\nimport {\n\tDESCRIPTION_MAX_LENGTH,\n\tDESCRIPTION_MIN_LENGTH,\n} from '../constants.js';\nimport type {\n\tTriggerPhraseAnalysis,\n\tUserPhrasingAnalysis,\n} from '../types.js';\nimport { estimate_string_tokens } from './text-analysis.js';\n\nexport interface DescriptionStats {\n\tdescription_length: number;\n\tdescription_tokens: number;\n}\n\nexport interface DescriptionWarning {\n\ttype:\n\t\t| 'length'\n\t\t| 'trigger'\n\t\t| 'list_bloat'\n\t\t| 'short'\n\t\t| 'first_person'\n\t\t| 'second_person'\n\t\t| 'vague'\n\t\t| 'passive';\n\tmessage: string;\n}\n\nexport interface DescriptionError {\n\ttype: 'length';\n\tmessage: string;\n}\n\nexport interface DescriptionValidation {\n\tstats: DescriptionStats;\n\twarnings: DescriptionWarning[];\n\terrors: DescriptionError[];\n}\n\n/**\n * Validate description length and quality\n */\nexport function validate_description_content(\n\tdescription: string,\n): DescriptionValidation {\n\tconst desc_length = description.length;\n\tconst desc_tokens = estimate_string_tokens(description);\n\n\tconst validation: DescriptionValidation = {\n\t\tstats: {\n\t\t\tdescription_length: desc_length,\n\t\t\tdescription_tokens: desc_tokens,\n\t\t},\n\t\twarnings: [],\n\t\terrors: [],\n\t};\n\n\t// Enforced limit: Claude truncates descriptions at this limit in skill listing\n\tif (desc_length > DESCRIPTION_MAX_LENGTH) {\n\t\tvalidation.errors.push({\n\t\t\ttype: 'length',\n\t\t\tmessage:\n\t\t\t\t`Description is ${desc_length} characters (MAX: ${DESCRIPTION_MAX_LENGTH} — Claude truncates at this limit)\\n` +\n\t\t\t\t` → Keep descriptions concise - anything past ${DESCRIPTION_MAX_LENGTH} chars is never seen`,\n\t\t});\n\t}\n\n\t// Check for trigger keywords\n\tconst lower_desc = description.toLowerCase();\n\tconst has_trigger =\n\t\tlower_desc.includes('use when') ||\n\t\tlower_desc.includes('use for') ||\n\t\tlower_desc.includes('use to');\n\n\tif (!has_trigger) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'trigger',\n\t\t\tmessage:\n\t\t\t\t`Description missing trigger keywords ('Use when...', 'Use for...', 'Use to...')\\n` +\n\t\t\t\t` → Help Claude know when to activate this skill`,\n\t\t});\n\t}\n\n\t// Check for list bloat (multiple commas indicating detailed lists)\n\t// Only warn if BOTH long description AND many commas (allows concise technical lists)\n\tconst comma_count = (description.match(/,/g) || []).length;\n\tif (desc_length > 150 && comma_count >= 5) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'list_bloat',\n\t\t\tmessage:\n\t\t\t\t`Description contains long lists (${comma_count} commas, ${desc_length} chars)\\n` +\n\t\t\t\t` → Move detailed lists to Level 2 (SKILL.md body) or Level 3 (references/)`,\n\t\t});\n\t}\n\n\t// Short description check\n\tif (desc_length < DESCRIPTION_MIN_LENGTH) {\n\t\tvalidation.warnings.push({\n\t\t\ttype: 'short',\n\t\t\tmessage:\n\t\t\t\t`Description is very short (${desc_length} chars, minimum recommended: ${DESCRIPTION_MIN_LENGTH})\\n` +\n\t\t\t\t` → Must answer both \"what does it do\" AND \"when to use it\"`,\n\t\t});\n\t}\n\n\treturn validation;\n}\n\n/**\n * Analyze trigger phrase in description\n */\nexport function analyze_trigger_phrase(\n\tdescription: string,\n): TriggerPhraseAnalysis {\n\tconst lower = description.toLowerCase();\n\tconst has_trigger =\n\t\tlower.includes('use when') ||\n\t\tlower.includes('use for') ||\n\t\tlower.includes('use to');\n\n\tlet trigger_phrase: string | null = null;\n\tlet trigger_type: 'specific' | 'generic' | 'missing' = 'missing';\n\n\tif (has_trigger) {\n\t\tconst match = description.match(\n\t\t\t/(use when|use for|use to)[^.!?]*/i,\n\t\t);\n\t\tif (match) {\n\t\t\ttrigger_phrase = match[0].trim();\n\t\t\ttrigger_type =\n\t\t\t\ttrigger_phrase.length > 50 ? 'specific' : 'generic';\n\t\t}\n\t}\n\n\treturn {\n\t\thas_explicit_trigger: has_trigger,\n\t\ttrigger_phrase,\n\t\ttrigger_type,\n\t};\n}\n\n/**\n * Analyze user phrasing style\n */\nexport function analyze_user_phrasing(description: string): {\n\tanalysis: UserPhrasingAnalysis;\n\twarnings: DescriptionWarning[];\n} {\n\tconst issues: Array<{\n\t\ttype: 'first_person' | 'passive_voice' | 'vague';\n\t\ttext: string;\n\t\tsuggestion: string;\n\t}> = [];\n\tconst warnings: DescriptionWarning[] = [];\n\n\t// Check for first person\n\tconst is_third_person = !/\\b(I can|I will|I help|my|me)\\b/i.test(\n\t\tdescription,\n\t);\n\tconst first_person_patterns = /\\b(I can|I will|I help|my|me)\\b/i;\n\tif (first_person_patterns.test(description)) {\n\t\tconst match = description.match(first_person_patterns);\n\t\tif (match) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'first_person',\n\t\t\t\tmessage:\n\t\t\t\t\t`Description uses first person: \"${match[0]}\"\\n` +\n\t\t\t\t\t` → Anthropic requires third-person voice (e.g., \"Generates...\" not \"I can generate...\")`,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Check for second person\n\tconst second_person_patterns =\n\t\t/\\b(You can|You should|You could|You'll|You will|You need|your)\\b/i;\n\tif (second_person_patterns.test(description)) {\n\t\tconst match = description.match(second_person_patterns);\n\t\tif (match) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'second_person',\n\t\t\t\tmessage:\n\t\t\t\t\t`Description uses second person: \"${match[0]}\"\\n` +\n\t\t\t\t\t` → Anthropic requires third-person voice (e.g., \"Processes...\" not \"You can process...\")`,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Check for vague terms\n\tconst vague_patterns =\n\t\t/\\b(helper|utility|tool|various|several|some)\\b/i;\n\tif (vague_patterns.test(description)) {\n\t\tconst match = description.match(vague_patterns);\n\t\tif (match) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'vague',\n\t\t\t\tmessage:\n\t\t\t\t\t`Description contains vague term: \"${match[0]}\"\\n` +\n\t\t\t\t\t` → Be specific about what the skill does`,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Check for gerund form (verbs ending in -ing)\n\tconst uses_gerund = /\\b\\w+ing\\b/i.test(description);\n\n\t// Check for action-oriented (starts with action verbs)\n\tconst action_verbs =\n\t\t/^(create|build|design|analyze|test|validate|generate|process|manage|execute|handle|provide)/i;\n\tconst is_action_oriented = action_verbs.test(description.trim());\n\n\t// Suggest action-oriented language if neither gerund nor action verb\n\tif (!uses_gerund && !is_action_oriented) {\n\t\twarnings.push({\n\t\t\ttype: 'passive',\n\t\t\tmessage:\n\t\t\t\t`Description lacks action-oriented language\\n` +\n\t\t\t\t` → Start with a verb or gerund (e.g., \"Generates...\", \"Managing...\", \"Extract...\")`,\n\t\t});\n\t}\n\n\tconst analysis: UserPhrasingAnalysis = {\n\t\tstyle_checks: {\n\t\t\tis_third_person,\n\t\t\tuses_gerund_form: uses_gerund,\n\t\t\tis_action_oriented,\n\t\t},\n\t\tissues,\n\t};\n\n\treturn { analysis, warnings };\n}\n","/**\n * File structure validation - paths, scripts, assets\n */\n\nimport {\n\texistsSync,\n\treaddirSync,\n\treadFileSync,\n\tstatSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport type {\n\tPathFormatValidation,\n\tPathFormatIssue,\n} from '../types.js';\n\nexport interface PathFormatError {\n\ttype: 'windows_path';\n\tmessage: string;\n}\n\nexport interface ScriptsWarning {\n\ttype: 'empty_directory' | 'not_executable' | 'missing_shebang';\n\tmessage: string;\n}\n\nexport interface AssetsWarning {\n\ttype: 'empty_directory';\n\tmessage: string;\n}\n\nexport interface DirectoryError {\n\ttype: 'not_found' | 'not_directory';\n\tmessage: string;\n}\n\n/**\n * Validate that skill directory exists and is valid\n */\nexport function validate_directory(skill_path: string): {\n\tvalid: boolean;\n\terrors: DirectoryError[];\n} {\n\tconst errors: DirectoryError[] = [];\n\n\tif (!existsSync(skill_path)) {\n\t\terrors.push({\n\t\t\ttype: 'not_found',\n\t\t\tmessage: `Skill directory does not exist: ${skill_path}`,\n\t\t});\n\t\treturn { valid: false, errors };\n\t}\n\n\tconst stats = statSync(skill_path);\n\tif (!stats.isDirectory()) {\n\t\terrors.push({\n\t\t\ttype: 'not_directory',\n\t\t\tmessage: `Path is not a directory: ${skill_path}`,\n\t\t});\n\t\treturn { valid: false, errors };\n\t}\n\n\treturn { valid: true, errors: [] };\n}\n\n/**\n * Validate path formats (no Windows backslashes)\n */\nexport function validate_path_formats(\n\tcontent: string,\n\tfile_name: string = 'SKILL.md',\n): {\n\tvalidation: PathFormatValidation;\n\terrors: PathFormatError[];\n} {\n\tconst invalid_paths: PathFormatIssue[] = [];\n\tconst errors: PathFormatError[] = [];\n\tconst lines = content.split('\\n');\n\n\tlines.forEach((line, index) => {\n\t\t// Skip code blocks (they might legitimately show Windows paths as examples)\n\t\tif (line.trim().startsWith('```')) return;\n\n\t\t// Detect backslashes in file paths\n\t\t// Match patterns like: scripts\\file.py, references\\doc.md, etc.\n\t\tconst backslash_pattern =\n\t\t\t/(?:scripts|references|assets|examples)\\\\[\\w\\\\.-]+/g;\n\t\tconst matches = line.match(backslash_pattern);\n\n\t\tif (matches) {\n\t\t\tmatches.forEach((match) => {\n\t\t\t\tconst fixed = match.replace(/\\\\/g, '/');\n\n\t\t\t\t// Store in validation\n\t\t\t\tinvalid_paths.push({\n\t\t\t\t\tline_number: index + 1,\n\t\t\t\t\tpath: match,\n\t\t\t\t\terror: 'Windows-style backslash detected',\n\t\t\t\t\tsuggested_fix: fixed,\n\t\t\t\t});\n\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: 'windows_path',\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Windows-style path in ${file_name}:${index + 1}\\n` +\n\t\t\t\t\t\t` → Found: ${match}\\n` +\n\t\t\t\t\t\t` → Use: ${fixed}`,\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t});\n\n\treturn {\n\t\tvalidation: { invalid_paths },\n\t\terrors,\n\t};\n}\n\n/**\n * Validate scripts directory\n */\nexport function validate_scripts(skill_path: string): {\n\twarnings: ScriptsWarning[];\n} {\n\tconst scripts_dir = join(skill_path, 'scripts');\n\tconst warnings: ScriptsWarning[] = [];\n\n\tif (existsSync(scripts_dir)) {\n\t\tconst files = readdirSync(scripts_dir);\n\t\tconst script_files = files.filter(\n\t\t\t(f) =>\n\t\t\t\tf.endsWith('.js') ||\n\t\t\t\tf.endsWith('.ts') ||\n\t\t\t\tf.endsWith('.mjs') ||\n\t\t\t\tf.endsWith('.sh'),\n\t\t);\n\n\t\tif (script_files.length === 0) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'empty_directory',\n\t\t\t\tmessage: 'scripts/ directory exists but is empty',\n\t\t\t});\n\t\t}\n\n\t\tfor (const script_file of script_files) {\n\t\t\tconst script_path = join(scripts_dir, script_file);\n\t\t\tconst stats = statSync(script_path);\n\n\t\t\t// Check if executable (0o111 = --x--x--x)\n\t\t\tif ((stats.mode & 0o111) === 0) {\n\t\t\t\twarnings.push({\n\t\t\t\t\ttype: 'not_executable',\n\t\t\t\t\tmessage: `Script is not executable: ${script_file}`,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Check for shebang\n\t\t\tconst content = readFileSync(script_path, 'utf-8');\n\t\t\tconst first_line = content.split('\\n')[0];\n\t\t\tif (!first_line.startsWith('#!')) {\n\t\t\t\twarnings.push({\n\t\t\t\t\ttype: 'missing_shebang',\n\t\t\t\t\tmessage: `Script missing shebang: ${script_file}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { warnings };\n}\n\n/**\n * Validate assets directory\n */\nexport function validate_assets(skill_path: string): {\n\twarnings: AssetsWarning[];\n} {\n\tconst assets_dir = join(skill_path, 'assets');\n\tconst warnings: AssetsWarning[] = [];\n\n\tif (existsSync(assets_dir)) {\n\t\tconst files = readdirSync(assets_dir);\n\n\t\tif (files.length === 0) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'empty_directory',\n\t\t\t\tmessage: 'assets/ directory exists but is empty',\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { warnings };\n}\n","/**\n * References validation (Level 3 progressive disclosure)\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ReferenceNesting } from '../types.js';\n\nexport interface ReferencesValidation {\n\tfiles_found: string[];\n\tfiles_referenced: string[];\n\tmissing_files: string[];\n\torphaned_files: string[];\n\tnesting: ReferenceNesting[];\n\tmax_nesting_depth: number;\n}\n\nexport interface ReferencesWarning {\n\ttype: 'empty_directory' | 'orphaned_file' | 'nesting_depth';\n\tmessage: string;\n}\n\nexport interface ReferencesError {\n\ttype: 'missing_file';\n\tmessage: string;\n}\n\nexport interface ReferencesResult {\n\tvalidation: ReferencesValidation;\n\twarnings: ReferencesWarning[];\n\terrors: ReferencesError[];\n}\n\n/**\n * Strip fenced code blocks from content to avoid parsing example links\n */\nfunction strip_code_blocks(content: string): string {\n\t// Remove fenced code blocks (``` or ~~~)\n\treturn content.replace(/```[\\s\\S]*?```|~~~[\\s\\S]*?~~~/g, '');\n}\n\n/**\n * Check nesting depth of reference files\n */\nfunction check_reference_nesting(\n\tskill_path: string,\n\tfile_path: string,\n\tvisited: Set<string> = new Set(),\n): { depth: number; references: string[] } {\n\tif (visited.has(file_path)) {\n\t\treturn { depth: 0, references: [] };\n\t}\n\tvisited.add(file_path);\n\n\tconst full_path = join(skill_path, file_path);\n\tif (!existsSync(full_path)) {\n\t\treturn { depth: 0, references: [] };\n\t}\n\n\tconst content = readFileSync(full_path, 'utf-8');\n\t// Strip code blocks to avoid parsing example links inside them\n\tconst content_without_code = strip_code_blocks(content);\n\tconst reference_pattern = /\\[([^\\]]+)\\]\\(([^)]+\\.md)\\)/g;\n\tconst matches = [\n\t\t...content_without_code.matchAll(reference_pattern),\n\t];\n\tconst references = matches.map((m) => m[2]);\n\n\tif (references.length === 0) {\n\t\treturn { depth: 1, references: [] };\n\t}\n\n\t// Recursively check nested references\n\tlet max_depth = 1;\n\tfor (const ref of references) {\n\t\tconst nested = check_reference_nesting(\n\t\t\tskill_path,\n\t\t\tref,\n\t\t\tnew Set(visited),\n\t\t);\n\t\tmax_depth = Math.max(max_depth, 1 + nested.depth);\n\t}\n\n\treturn { depth: max_depth, references };\n}\n\n/**\n * Validate references directory and links\n */\nexport function validate_references(\n\tskill_path: string,\n): ReferencesResult {\n\tconst references_dir = join(skill_path, 'references');\n\tconst skill_md_path = join(skill_path, 'SKILL.md');\n\n\tconst files_found: string[] = [];\n\tconst files_referenced: string[] = [];\n\tconst missing_files: string[] = [];\n\tconst nesting_data: ReferenceNesting[] = [];\n\tconst warnings: ReferencesWarning[] = [];\n\tconst errors: ReferencesError[] = [];\n\n\t// Check references directory if it exists\n\tif (existsSync(references_dir)) {\n\t\tconst files = readdirSync(references_dir);\n\t\tconst md_files = files.filter((f) => f.endsWith('.md'));\n\t\tfiles_found.push(...md_files.map((f) => `references/${f}`));\n\n\t\tif (md_files.length === 0) {\n\t\t\twarnings.push({\n\t\t\t\ttype: 'empty_directory',\n\t\t\t\tmessage: 'references/ directory exists but is empty',\n\t\t\t});\n\t\t}\n\n\t\t// Check for references in SKILL.md\n\t\tif (existsSync(skill_md_path)) {\n\t\t\tconst skill_content = readFileSync(skill_md_path, 'utf-8');\n\n\t\t\tfor (const md_file of md_files) {\n\t\t\t\tif (!skill_content.includes(md_file)) {\n\t\t\t\t\twarnings.push({\n\t\t\t\t\t\ttype: 'orphaned_file',\n\t\t\t\t\t\tmessage: `Reference file 'references/${md_file}' not mentioned in SKILL.md`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check root-level .md files (excluding SKILL.md)\n\tif (existsSync(skill_path)) {\n\t\tconst root_files = readdirSync(skill_path);\n\t\tconst root_md_files = root_files.filter(\n\t\t\t(f) =>\n\t\t\t\tf.endsWith('.md') && f !== 'SKILL.md' && f !== 'README.md',\n\t\t);\n\t\tfiles_found.push(...root_md_files);\n\n\t\tif (existsSync(skill_md_path)) {\n\t\t\tconst skill_content = readFileSync(skill_md_path, 'utf-8');\n\n\t\t\tfor (const md_file of root_md_files) {\n\t\t\t\tif (!skill_content.includes(md_file)) {\n\t\t\t\t\twarnings.push({\n\t\t\t\t\t\ttype: 'orphaned_file',\n\t\t\t\t\t\tmessage: `Root file '${md_file}' not mentioned in SKILL.md`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Level 3 validation: Check that all referenced files exist\n\tif (existsSync(skill_md_path)) {\n\t\tconst skill_content = readFileSync(skill_md_path, 'utf-8');\n\t\t// Strip code blocks to avoid parsing example links inside them\n\t\tconst content_without_code = strip_code_blocks(skill_content);\n\n\t\t// Extract markdown links to .md files (references/ or root-level)\n\t\t// Matches: [text](file.md), [text](references/file.md), etc.\n\t\tconst reference_link_pattern = /\\[([^\\]]+)\\]\\(([^)]+\\.md)\\)/g;\n\t\tconst matches = content_without_code.matchAll(\n\t\t\treference_link_pattern,\n\t\t);\n\n\t\tfor (const match of matches) {\n\t\t\tconst link_text = match[1];\n\t\t\tconst file_path = match[2]; // e.g., \"references/examples.md\"\n\t\t\tconst full_path = join(skill_path, file_path);\n\n\t\t\tfiles_referenced.push(file_path);\n\n\t\t\tif (!existsSync(full_path)) {\n\t\t\t\tmissing_files.push(file_path);\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: 'missing_file',\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Referenced file not found: ${file_path}\\n` +\n\t\t\t\t\t\t` → Linked from: [${link_text}]\\n` +\n\t\t\t\t\t\t` → Create the file or remove the broken link`,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Check nesting depth\n\t\t\t\tconst nesting = check_reference_nesting(\n\t\t\t\t\tskill_path,\n\t\t\t\t\tfile_path,\n\t\t\t\t);\n\t\t\t\tlet warning: string | null = null;\n\n\t\t\t\tif (nesting.depth > 1) {\n\t\t\t\t\twarning = `File has depth ${nesting.depth} (recommended: 1). Keep references one level deep from SKILL.md.`;\n\t\t\t\t\twarnings.push({\n\t\t\t\t\t\ttype: 'nesting_depth',\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`${file_path} has nesting depth ${nesting.depth} (recommended: 1)\\n` +\n\t\t\t\t\t\t\t` → Keep references one level deep from SKILL.md for clarity`,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tnesting_data.push({\n\t\t\t\t\tfile: file_path,\n\t\t\t\t\treferences: nesting.references,\n\t\t\t\t\tdepth: nesting.depth,\n\t\t\t\t\twarning,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Calculate orphaned files\n\tconst orphaned = files_found.filter(\n\t\t(f) => !files_referenced.some((ref) => ref.includes(f)),\n\t);\n\n\tconst validation: ReferencesValidation = {\n\t\tfiles_found,\n\t\tfiles_referenced,\n\t\tmissing_files,\n\t\torphaned_files: orphaned,\n\t\tnesting: nesting_data,\n\t\tmax_nesting_depth:\n\t\t\tnesting_data.length > 0\n\t\t\t\t? Math.max(...nesting_data.map((n) => n.depth))\n\t\t\t\t: 0,\n\t};\n\n\treturn { validation, warnings, errors };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n\tDESCRIPTION_MAX_LENGTH,\n\tLIMITS,\n\tNAME_MAX_LENGTH,\n} from '../constants.js';\nimport type {\n\tStructuredValidation,\n\tValidationResult,\n\tValidationStats,\n} from '../types.js';\n\n// Import validators\nimport { analyze_alignment } from '../validators/alignment-validator.js';\nimport { validate_content } from '../validators/content-validator.js';\nimport {\n\tanalyze_trigger_phrase,\n\tanalyze_user_phrasing,\n\tvalidate_description_content,\n} from '../validators/description-validator.js';\nimport {\n\tvalidate_assets,\n\tvalidate_directory,\n\tvalidate_path_formats,\n\tvalidate_scripts,\n} from '../validators/file-structure-validator.js';\nimport {\n\textract_frontmatter,\n\tvalidate_frontmatter_structure,\n\tvalidate_hard_limits,\n\tvalidate_name_format,\n} from '../validators/frontmatter-validator.js';\nimport { validate_dependencies } from '../validators/dependency-validator.js';\nimport { validate_references } from '../validators/references-validator.js';\n\nimport type { ValidationMode } from '../types.js';\n\nexport interface ValidatorOptions {\n\tmode?: ValidationMode;\n}\n\nexport class SkillValidator {\n\tprivate skill_path: string;\n\tprivate options: ValidatorOptions;\n\tprivate errors: string[] = [];\n\tprivate warnings: string[] = [];\n\tprivate stats: ValidationStats = {\n\t\tword_count: 0,\n\t\testimated_tokens: 0,\n\t\tline_count: 0,\n\t\tdescription_length: 0,\n\t\tdescription_tokens: 0,\n\t\tcode_blocks: 0,\n\t\tsections: 0,\n\t\tlong_paragraphs: 0,\n\t};\n\n\t// Structured validation data\n\tprivate structured_validation: StructuredValidation = {\n\t\thard_limits: {\n\t\t\tname: {\n\t\t\t\tlength: 0,\n\t\t\t\tlimit: NAME_MAX_LENGTH,\n\t\t\t\tvalid: true,\n\t\t\t\terror: null,\n\t\t\t},\n\t\t\tdescription: {\n\t\t\t\tlength: 0,\n\t\t\t\tlimit: DESCRIPTION_MAX_LENGTH,\n\t\t\t\tvalid: true,\n\t\t\t\terror: null,\n\t\t\t},\n\t\t},\n\t\tname_format: {\n\t\t\tname: '',\n\t\t\tformat_valid: true,\n\t\t\tdirectory_name: '',\n\t\t\tmatches_directory: true,\n\t\t\terrors: [],\n\t\t},\n\t\tyaml_validation: {\n\t\t\tvalid: true,\n\t\t\thas_frontmatter: false,\n\t\t\tparse_error: null,\n\t\t\tmissing_fields: [],\n\t\t},\n\t\tpath_format: {\n\t\t\tinvalid_paths: [],\n\t\t},\n\t\ttriggering: {\n\t\t\ttrigger_phrase: {\n\t\t\t\thas_explicit_trigger: false,\n\t\t\t\ttrigger_phrase: null,\n\t\t\t\ttrigger_type: 'missing',\n\t\t\t},\n\t\t\tuser_phrasing: {\n\t\t\t\tstyle_checks: {\n\t\t\t\t\tis_third_person: true,\n\t\t\t\t\tuses_gerund_form: true,\n\t\t\t\t\tis_action_oriented: true,\n\t\t\t\t},\n\t\t\t\tissues: [],\n\t\t\t},\n\t\t\tkeywords: {\n\t\t\t\tdescription_keywords: [],\n\t\t\t\tcontent_keywords: [],\n\t\t\t\toverlap: [],\n\t\t\t\tdescription_only: [],\n\t\t\t\tcontent_only: [],\n\t\t\t},\n\t\t\talignment: {\n\t\t\t\tseverity: 'good',\n\t\t\t\tdescription_focus: [],\n\t\t\t\tcontent_focus: [],\n\t\t\t\tmatches: [],\n\t\t\t\tmismatches: [],\n\t\t\t\texplanation: '',\n\t\t\t},\n\t\t},\n\t};\n\n\tconstructor(skill_path: string, options: ValidatorOptions = {}) {\n\t\tthis.skill_path = skill_path;\n\t\tthis.options = options;\n\t}\n\n\tprivate error(msg: string): void {\n\t\tthis.errors.push(`❌ ${msg}`);\n\t}\n\n\tprivate warning(msg: string): void {\n\t\tthis.warnings.push(`⚠️ ${msg}`);\n\t}\n\n\tprivate validate_skill_md(): boolean {\n\t\tconst skill_md_path = join(this.skill_path, 'SKILL.md');\n\n\t\tif (!existsSync(skill_md_path)) {\n\t\t\tthis.error('SKILL.md file not found');\n\t\t\treturn false;\n\t\t}\n\n\t\tconst content = readFileSync(skill_md_path, 'utf-8');\n\n\t\t// Validate path formats (no Windows backslashes)\n\t\tconst path_format_result = validate_path_formats(content);\n\t\tthis.structured_validation.path_format =\n\t\t\tpath_format_result.validation;\n\t\tpath_format_result.errors.forEach((err) =>\n\t\t\tthis.error(err.message),\n\t\t);\n\n\t\t// Validate frontmatter structure\n\t\tconst frontmatter_validation =\n\t\t\tvalidate_frontmatter_structure(content);\n\t\tthis.structured_validation.yaml_validation =\n\t\t\tfrontmatter_validation;\n\n\t\tif (!frontmatter_validation.valid) {\n\t\t\tif (frontmatter_validation.parse_error) {\n\t\t\t\tthis.error(frontmatter_validation.parse_error);\n\t\t\t}\n\t\t\tfrontmatter_validation.missing_fields.forEach((field) => {\n\t\t\t\tthis.error(`SKILL.md frontmatter missing '${field}' field`);\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\t// Warn about unknown frontmatter fields\n\t\tif (frontmatter_validation.unknown_fields?.length) {\n\t\t\tfor (const field of frontmatter_validation.unknown_fields) {\n\t\t\t\tthis.warning(\n\t\t\t\t\t`Unknown frontmatter field '${field}'\\n` +\n\t\t\t\t\t\t` → See https://code.claude.com/docs/en/skills#frontmatter-reference`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Warn about invalid field values\n\t\tif (frontmatter_validation.field_value_warnings?.length) {\n\t\t\tfor (const warn of frontmatter_validation.field_value_warnings) {\n\t\t\t\tthis.warning(warn);\n\t\t\t}\n\t\t}\n\n\t\t// Extract frontmatter data\n\t\tconst { name, description, body, description_is_multiline } =\n\t\t\textract_frontmatter(content);\n\n\t\tif (!name || !description) {\n\t\t\tthis.error(\n\t\t\t\t'Failed to extract name or description from frontmatter',\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\t// Warn if description spans multiple lines\n\t\tif (description_is_multiline) {\n\t\t\tthis.warning(\n\t\t\t\t`Multi-line description detected. Claude Code cannot recognize skills with multi-line descriptions.\\n` +\n\t\t\t\t\t` → Run 'claude-skills-cli doctor ${this.skill_path}' to fix automatically`,\n\t\t\t);\n\t\t}\n\n\t\t// Get directory name (normalize path to handle trailing slashes)\n\t\tconst normalized_path = this.skill_path.replace(/\\/+$/, '');\n\t\tconst dir_name = normalized_path.split('/').pop() || '';\n\n\t\t// Validate name format\n\t\tconst name_validation = validate_name_format(name, dir_name);\n\t\tthis.structured_validation.name_format = name_validation;\n\t\tname_validation.errors.forEach((err) => this.error(err));\n\n\t\t// Validate hard limits\n\t\tconst hard_limits = validate_hard_limits(name, description);\n\t\tthis.structured_validation.hard_limits = hard_limits;\n\n\t\tif (!hard_limits.name.valid && hard_limits.name.error) {\n\t\t\tthis.error(hard_limits.name.error);\n\t\t}\n\n\t\tif (\n\t\t\t!hard_limits.description.valid &&\n\t\t\thard_limits.description.error\n\t\t) {\n\t\t\tthis.error(hard_limits.description.error);\n\t\t}\n\n\t\t// Validate description content\n\t\tconst desc_validation = validate_description_content(description);\n\t\tthis.stats.description_length =\n\t\t\tdesc_validation.stats.description_length;\n\t\tthis.stats.description_tokens =\n\t\t\tdesc_validation.stats.description_tokens;\n\t\tdesc_validation.errors.forEach((err) => this.error(err.message));\n\t\tdesc_validation.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\t// Analyze trigger phrase\n\t\tconst trigger_analysis = analyze_trigger_phrase(description);\n\t\tthis.structured_validation.triggering!.trigger_phrase =\n\t\t\ttrigger_analysis;\n\n\t\tif (!trigger_analysis.has_explicit_trigger) {\n\t\t\tthis.warning(\n\t\t\t\t`Description missing explicit trigger phrase ('Use when...', 'Use for...', 'Use to...')\\n` +\n\t\t\t\t\t` → Help Claude know when to activate this skill`,\n\t\t\t);\n\t\t}\n\n\t\t// Analyze user phrasing\n\t\tconst {\n\t\t\tanalysis: phrasing_analysis,\n\t\t\twarnings: phrasing_warnings,\n\t\t} = analyze_user_phrasing(description);\n\t\tthis.structured_validation.triggering!.user_phrasing =\n\t\t\tphrasing_analysis;\n\t\tphrasing_warnings.forEach((warn) => this.warning(warn.message));\n\n\t\t// Analyze alignment\n\t\tconst alignment_result = analyze_alignment(description, body);\n\t\tthis.structured_validation.triggering!.keywords =\n\t\t\talignment_result.keywords;\n\t\tthis.structured_validation.triggering!.alignment =\n\t\t\talignment_result.alignment;\n\t\talignment_result.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\t// Validate content (progressive disclosure)\n\t\tconst content_validation = validate_content(body, {\n\t\t\tmode: this.options.mode,\n\t\t});\n\t\tthis.stats.word_count = content_validation.stats.word_count;\n\t\tthis.stats.estimated_tokens =\n\t\t\tcontent_validation.stats.estimated_tokens;\n\t\tthis.stats.line_count = content_validation.stats.line_count;\n\t\tthis.stats.code_blocks = content_validation.stats.code_blocks;\n\t\tthis.stats.sections = content_validation.stats.sections;\n\t\tthis.stats.long_paragraphs =\n\t\t\tcontent_validation.stats.long_paragraphs;\n\t\tcontent_validation.errors.forEach((err) =>\n\t\t\tthis.error(err.message),\n\t\t);\n\t\tcontent_validation.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\t// Validate dependencies (depends-on-* fields)\n\t\tconst parts = content.split('---\\n');\n\t\tif (parts.length >= 3) {\n\t\t\tconst frontmatter_raw = parts[1];\n\t\t\tconst dep_result = validate_dependencies(frontmatter_raw);\n\t\t\tdep_result.errors.forEach((err) => this.error(err));\n\t\t\tdep_result.warnings.forEach((warn) => this.warning(warn));\n\t\t\tthis.structured_validation.dependency_validation =\n\t\t\t\tdep_result.validation;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic validate_all(): ValidationResult {\n\t\t// Validate directory\n\t\tconst dir_result = validate_directory(this.skill_path);\n\t\tif (!dir_result.valid) {\n\t\t\tdir_result.errors.forEach((err) => this.error(err.message));\n\t\t\treturn {\n\t\t\t\terrors: this.errors,\n\t\t\t\twarnings: this.warnings,\n\t\t\t\tis_valid: false,\n\t\t\t\tstats: this.stats,\n\t\t\t\tvalidation: this.structured_validation,\n\t\t\t};\n\t\t}\n\n\t\t// Validate SKILL.md\n\t\tthis.validate_skill_md();\n\n\t\t// Validate references\n\t\tconst refs_result = validate_references(this.skill_path);\n\t\trefs_result.errors.forEach((err) => this.error(err.message));\n\t\trefs_result.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\t// Validate scripts\n\t\tconst scripts_result = validate_scripts(this.skill_path);\n\t\tscripts_result.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\t// Validate assets\n\t\tconst assets_result = validate_assets(this.skill_path);\n\t\tassets_result.warnings.forEach((warn) =>\n\t\t\tthis.warning(warn.message),\n\t\t);\n\n\t\t// Populate progressive disclosure structured validation\n\t\tconst mode_limits = LIMITS[this.options.mode || 'strict'];\n\t\tconst line_limit = mode_limits.lines.max;\n\t\tconst word_limit = mode_limits.words.max;\n\t\tthis.structured_validation.progressive_disclosure = {\n\t\t\tskill_md_size: {\n\t\t\t\tlines: this.stats.line_count,\n\t\t\t\twords: this.stats.word_count,\n\t\t\t\ttokens: this.stats.estimated_tokens,\n\t\t\t\texceeds_line_limit: this.stats.line_count > line_limit,\n\t\t\t\texceeds_word_limit: this.stats.word_count > word_limit,\n\t\t\t},\n\t\t\treferences: refs_result.validation,\n\t\t};\n\n\t\treturn {\n\t\t\terrors: this.errors,\n\t\t\twarnings: this.warnings,\n\t\t\tis_valid: this.errors.length === 0,\n\t\t\tstats: this.stats,\n\t\t\tvalidation: this.structured_validation,\n\t\t};\n\t}\n\n\tpublic get_errors(): string[] {\n\t\treturn this.errors;\n\t}\n\n\tpublic get_warnings(): string[] {\n\t\treturn this.warnings;\n\t}\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-skills-cli",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "description": "CLI toolkit for creating and managing Claude Agent Skills",
5
5
  "keywords": [
6
6
  "agent",
@@ -1 +0,0 @@
1
- {"version":3,"file":"doctor.cmd-CkNw6ine.js","names":[],"sources":["../src/commands/doctor.ts","../src/commands/doctor.cmd.ts"],"sourcesContent":["import { readFileSync, writeFileSync } from 'node:fs';\nimport { basename, join } from 'node:path';\nimport type { DoctorOptions } from '../types.js';\nimport { error, info, success } from '../utils/output.js';\nimport {\n\textract_frontmatter,\n\tis_description_multiline,\n} from '../validators/frontmatter-validator.js';\n\nexport function doctor_command(options: DoctorOptions): void {\n\tconst { skill_path } = options;\n\tconst skill_name = basename(skill_path);\n\tconst skill_md_path = join(skill_path, 'SKILL.md');\n\n\tinfo(`Running doctor on: ${skill_name}`);\n\tconsole.log('='.repeat(60));\n\n\t// Read SKILL.md\n\tlet content: string;\n\ttry {\n\t\tcontent = readFileSync(skill_md_path, 'utf-8');\n\t} catch (err) {\n\t\terror(`Failed to read SKILL.md: ${String(err)}`);\n\t\tprocess.exit(1);\n\t}\n\n\t// Extract frontmatter\n\tconst frontmatter_data = extract_frontmatter(content);\n\n\tif (!frontmatter_data.description_is_multiline) {\n\t\tsuccess(\n\t\t\t'No issues found. Description is already on a single line.',\n\t\t);\n\t\tprocess.exit(0);\n\t}\n\n\tinfo('Found multi-line description. Fixing...');\n\n\t// Fix the multi-line description\n\tconst fixed_content = fix_multiline_description(content);\n\n\t// Write fixed content back\n\ttry {\n\t\twriteFileSync(skill_md_path, fixed_content, 'utf-8');\n\t\tsuccess('Fixed multi-line description!');\n\t\tconsole.log('\\nChanges made:');\n\t\tconsole.log(\n\t\t\t' • Added # prettier-ignore comment before description',\n\t\t);\n\t\tconsole.log(' • Reflowed description to single line');\n\t\tconsole.log('\\n✓ Run validate command to confirm the fix');\n\t\tprocess.exit(0);\n\t} catch (err) {\n\t\terror(`Failed to write SKILL.md: ${String(err)}`);\n\t\tprocess.exit(1);\n\t}\n}\n\n/**\n * Fix multi-line description by adding prettier-ignore and reflowing to single line\n */\nfunction fix_multiline_description(content: string): string {\n\tconst lines = content.split('\\n');\n\tconst fixed_lines: string[] = [];\n\tlet in_frontmatter = false;\n\tlet frontmatter_count = 0;\n\tlet in_description = false;\n\tlet description_parts: string[] = [];\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i];\n\n\t\t// Track frontmatter boundaries\n\t\tif (line.trim() === '---') {\n\t\t\tfrontmatter_count++;\n\n\t\t\t// If we're closing frontmatter and still collecting description\n\t\t\tif (frontmatter_count === 2 && in_description) {\n\t\t\t\t// Write out the collected description\n\t\t\t\tconst full_description = description_parts.join(' ');\n\t\t\t\tfixed_lines.push(`description: ${full_description}`);\n\t\t\t\tdescription_parts = [];\n\t\t\t\tin_description = false;\n\t\t\t}\n\n\t\t\tin_frontmatter = frontmatter_count === 1;\n\t\t\tfixed_lines.push(line);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Not in frontmatter, just pass through\n\t\tif (!in_frontmatter) {\n\t\t\tfixed_lines.push(line);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check if this is the description line\n\t\tif (line.match(/^description:/)) {\n\t\t\t// Check if it's already multi-line\n\t\t\tif (!is_description_multiline(lines.slice(i).join('\\n'))) {\n\t\t\t\t// Single line, just pass through\n\t\t\t\tfixed_lines.push(line);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tin_description = true;\n\n\t\t\t// Extract value on same line (if any)\n\t\t\tconst match = line.match(/^description:\\s*(.*)$/);\n\t\t\tconst value_on_line = match ? match[1].trim() : '';\n\t\t\tif (value_on_line) {\n\t\t\t\tdescription_parts.push(value_on_line);\n\t\t\t}\n\n\t\t\t// Add prettier-ignore comment\n\t\t\tfixed_lines.push('# prettier-ignore');\n\t\t\t// We'll add the description line later\n\t\t\tcontinue;\n\t\t}\n\n\t\t// If we're in description, collect continuation lines\n\t\tif (in_description) {\n\t\t\t// Stop if we hit another YAML field\n\t\t\tif (line.match(/^[a-z_-]+:/)) {\n\t\t\t\t// Done collecting description, write it out\n\t\t\t\tconst full_description = description_parts.join(' ');\n\t\t\t\tfixed_lines.push(`description: ${full_description}`);\n\t\t\t\tdescription_parts = [];\n\t\t\t\tin_description = false;\n\n\t\t\t\t// Add the current line (next field)\n\t\t\t\tfixed_lines.push(line);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Continuation line - collect it\n\t\t\tconst trimmed = line.trim();\n\t\t\tif (trimmed && !trimmed.startsWith('#')) {\n\t\t\t\tdescription_parts.push(trimmed);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Regular frontmatter line\n\t\tfixed_lines.push(line);\n\t}\n\n\t// If we ended while still in description (at end of frontmatter)\n\tif (in_description && description_parts.length > 0) {\n\t\tconst full_description = description_parts.join(' ');\n\t\tfixed_lines.push(`description: ${full_description}`);\n\t}\n\n\treturn fixed_lines.join('\\n');\n}\n","import { defineCommand } from 'citty';\nimport { doctor_command } from './doctor.js';\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'doctor',\n\t\tdescription: 'Fix common skill issues automatically',\n\t},\n\targs: {\n\t\tskill_path: {\n\t\t\ttype: 'positional',\n\t\t\tdescription: 'Path to skill directory',\n\t\t\trequired: true,\n\t\t},\n\t},\n\trun({ args }) {\n\t\tdoctor_command({ skill_path: args.skill_path });\n\t},\n});\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"frontmatter-validator-DO686mla.js","names":[],"sources":["../src/validators/frontmatter-validator.ts"],"sourcesContent":["/**\n * YAML frontmatter validation for SKILL.md\n */\n\nimport {\n\tDESCRIPTION_MAX_LENGTH,\n\tNAME_MAX_LENGTH,\n} from '../constants.js';\nimport type {\n\tHardLimitValidation,\n\tNameFormatValidation,\n\tYAMLValidation,\n} from '../types.js';\n\nexport interface FrontmatterData {\n\tname: string | null;\n\tdescription: string | null;\n\tbody: string;\n\tdescription_is_multiline: boolean;\n}\n\n/**\n * Check if content has valid YAML frontmatter\n */\nexport function has_yaml_frontmatter(content: string): boolean {\n\treturn content.startsWith('---\\n') || content.startsWith('---\\r\\n');\n}\n\n/**\n * Check if description field spans multiple lines in raw YAML\n */\nexport function is_description_multiline(\n\tfrontmatter: string,\n): boolean {\n\t// Find the description line\n\tconst desc_line_match = frontmatter.match(/^description:\\s*(.*)$/m);\n\tif (!desc_line_match) {\n\t\treturn false;\n\t}\n\n\tconst value_on_same_line = desc_line_match[1].trim();\n\n\t// If there's no value on the same line as \"description:\", it's multi-line\n\tif (!value_on_same_line) {\n\t\treturn true;\n\t}\n\n\t// Check if there are continuation lines (indented lines after description:)\n\t// that are not other YAML fields\n\tconst lines = frontmatter.split('\\n');\n\tlet found_desc = false;\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i];\n\t\tif (line.match(/^description:/)) {\n\t\t\tfound_desc = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (found_desc) {\n\t\t\t// If next line starts with spaces/tabs and is not a comment and is not another field\n\t\t\tif (\n\t\t\t\tline.match(/^\\s+\\S/) &&\n\t\t\t\t!line.trim().startsWith('#') &&\n\t\t\t\t!line.match(/^[a-z_-]+:/)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t// Stop checking after we hit another field or end\n\t\t\tif (line.match(/^[a-z_-]+:/)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Extract frontmatter and body from SKILL.md content\n */\nexport function extract_frontmatter(\n\tcontent: string,\n): FrontmatterData {\n\tif (!has_yaml_frontmatter(content)) {\n\t\treturn {\n\t\t\tname: null,\n\t\t\tdescription: null,\n\t\t\tbody: content,\n\t\t\tdescription_is_multiline: false,\n\t\t};\n\t}\n\n\tconst parts = content.split('---\\n');\n\tif (parts.length < 3) {\n\t\treturn {\n\t\t\tname: null,\n\t\t\tdescription: null,\n\t\t\tbody: content,\n\t\t\tdescription_is_multiline: false,\n\t\t};\n\t}\n\n\tconst frontmatter = parts[1];\n\tconst body = parts.slice(2).join('---\\n');\n\n\t// Extract name\n\tconst name_match = frontmatter.match(/name:\\s*(.+)/);\n\tconst name = name_match ? name_match[1].trim() : null;\n\n\t// Extract description\n\tconst desc_match = frontmatter.match(\n\t\t/description:\\s*(.+?)(?=\\n[a-z]+:|$)/s,\n\t);\n\tconst description = desc_match ? desc_match[1].trim() : null;\n\n\t// Check if description spans multiple lines in the raw YAML\n\tconst description_is_multiline =\n\t\tis_description_multiline(frontmatter);\n\n\treturn { name, description, body, description_is_multiline };\n}\n\n/**\n * Known frontmatter fields per Anthropic spec\n * https://code.claude.com/docs/en/skills#frontmatter-reference\n */\nconst KNOWN_FRONTMATTER_FIELDS = new Set([\n\t'name',\n\t'description',\n\t'argument-hint',\n\t'disable-model-invocation',\n\t'user-invocable',\n\t'allowed-tools',\n\t'model',\n\t'effort',\n\t'context',\n\t'agent',\n\t'hooks',\n\t'paths',\n\t'shell',\n]);\n\n/**\n * Fields with constrained values\n */\nconst FIELD_VALUES: Record<string, readonly string[]> = {\n\teffort: ['low', 'medium', 'high', 'max'],\n\tcontext: ['fork'],\n\tshell: ['bash', 'powershell'],\n\t'disable-model-invocation': ['true', 'false'],\n\t'user-invocable': ['true', 'false'],\n};\n\n/**\n * Extract top-level field names from raw YAML frontmatter\n */\nfunction extract_field_names(\n\tfrontmatter: string,\n): Map<string, string> {\n\tconst fields = new Map<string, string>();\n\tfor (const line of frontmatter.split('\\n')) {\n\t\tconst match = line.match(/^([a-z][a-z0-9_-]*):\\s*(.*)/);\n\t\tif (match) {\n\t\t\tfields.set(match[1], match[2].trim());\n\t\t}\n\t}\n\treturn fields;\n}\n\n/**\n * Validate YAML frontmatter structure\n */\nexport function validate_frontmatter_structure(\n\tcontent: string,\n): YAMLValidation {\n\tconst validation: YAMLValidation = {\n\t\tvalid: true,\n\t\thas_frontmatter: false,\n\t\tparse_error: null,\n\t\tmissing_fields: [],\n\t\tunknown_fields: [],\n\t\tfield_value_warnings: [],\n\t};\n\n\tif (!has_yaml_frontmatter(content)) {\n\t\tvalidation.valid = false;\n\t\tvalidation.parse_error = 'Missing YAML frontmatter';\n\t\treturn validation;\n\t}\n\n\tvalidation.has_frontmatter = true;\n\n\tconst parts = content.split('---\\n');\n\tif (parts.length < 3) {\n\t\tvalidation.valid = false;\n\t\tvalidation.parse_error = 'Malformed YAML frontmatter';\n\t\treturn validation;\n\t}\n\n\tconst frontmatter = parts[1];\n\n\t// Check required fields\n\tif (!frontmatter.includes('name:')) {\n\t\tvalidation.missing_fields.push('name');\n\t\tvalidation.valid = false;\n\t}\n\n\tif (!frontmatter.includes('description:')) {\n\t\tvalidation.missing_fields.push('description');\n\t\tvalidation.valid = false;\n\t}\n\n\t// Check for unknown fields\n\tconst fields = extract_field_names(frontmatter);\n\tfor (const [field, value] of fields) {\n\t\tif (!KNOWN_FRONTMATTER_FIELDS.has(field)) {\n\t\t\tvalidation.unknown_fields!.push(field);\n\t\t}\n\n\t\t// Validate constrained field values\n\t\tconst allowed = FIELD_VALUES[field];\n\t\tif (allowed && value && !allowed.includes(value)) {\n\t\t\tvalidation.field_value_warnings!.push(\n\t\t\t\t`'${field}' has value '${value}' (expected: ${allowed.join(', ')})`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn validation;\n}\n\n/**\n * Validate skill name format\n */\nexport function validate_name_format(\n\tname: string,\n\tdirectory_name: string,\n): NameFormatValidation {\n\tconst validation: NameFormatValidation = {\n\t\tname,\n\t\tformat_valid: true,\n\t\tdirectory_name,\n\t\tmatches_directory: true,\n\t\terrors: [],\n\t};\n\n\t// Validate kebab-case format\n\tif (!/^[a-z0-9-]+$/.test(name)) {\n\t\tvalidation.format_valid = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name must be lowercase kebab-case: '${name}'`,\n\t\t);\n\t}\n\n\t// Reject leading/trailing hyphens\n\tif (name.startsWith('-') || name.endsWith('-')) {\n\t\tvalidation.format_valid = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name must not start or end with a hyphen: '${name}'`,\n\t\t);\n\t}\n\n\t// Reject consecutive hyphens\n\tif (name.includes('--')) {\n\t\tvalidation.format_valid = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name must not contain consecutive hyphens: '${name}'`,\n\t\t);\n\t}\n\n\t// Reject reserved prefixes\n\tif (name.startsWith('claude') || name.startsWith('anthropic')) {\n\t\tvalidation.format_valid = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name must not use reserved prefix 'claude' or 'anthropic': '${name}'`,\n\t\t);\n\t}\n\n\t// Reject XML angle brackets in name (security)\n\tif (name.includes('<') || name.includes('>')) {\n\t\tvalidation.format_valid = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name must not contain XML angle brackets: '${name}'`,\n\t\t);\n\t}\n\n\t// Check name matches directory\n\tif (name !== directory_name) {\n\t\tvalidation.matches_directory = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name '${name}' must match directory name '${directory_name}'`,\n\t\t);\n\t}\n\n\treturn validation;\n}\n\n/**\n * Validate hard limits for name and description\n */\nexport function validate_hard_limits(\n\tname: string | null,\n\tdescription: string | null,\n): HardLimitValidation {\n\tconst limits: HardLimitValidation = {\n\t\tname: { length: 0, limit: 64, valid: true, error: null },\n\t\tdescription: {\n\t\t\tlength: 0,\n\t\t\tlimit: DESCRIPTION_MAX_LENGTH,\n\t\t\tvalid: true,\n\t\t\terror: null,\n\t\t},\n\t};\n\n\t// Validate name length\n\tif (name) {\n\t\tlimits.name.length = name.length;\n\t\tif (name.length > NAME_MAX_LENGTH) {\n\t\t\tlimits.name.valid = false;\n\t\t\tlimits.name.error = `Skill name too long (max ${NAME_MAX_LENGTH} chars): ${name.length}`;\n\t\t}\n\t}\n\n\t// Validate description length (truncated at limit in skill listing)\n\tif (description) {\n\t\tlimits.description.length = description.length;\n\t\tif (description.length > DESCRIPTION_MAX_LENGTH) {\n\t\t\tlimits.description.valid = false;\n\t\t\tlimits.description.error = `Description too long (max ${DESCRIPTION_MAX_LENGTH} chars — Claude truncates at this limit): ${description.length}`;\n\t\t}\n\t\t// Reject XML angle brackets in description (security)\n\t\tif (description.includes('<') || description.includes('>')) {\n\t\t\tlimits.description.valid = false;\n\t\t\tlimits.description.error = `Description must not contain XML angle brackets`;\n\t\t}\n\t}\n\n\treturn limits;\n}\n"],"mappings":"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"}