claude-skills-cli 0.0.20 → 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.
- package/dist/add-hook.cmd-JWw5UqA3.js +193 -0
- package/dist/add-hook.cmd-JWw5UqA3.js.map +1 -0
- package/dist/dependency-validator-CQJ1hCoU.js +382 -0
- package/dist/dependency-validator-CQJ1hCoU.js.map +1 -0
- package/dist/doctor.cmd-DJpHLDCV.js +142 -0
- package/dist/doctor.cmd-DJpHLDCV.js.map +1 -0
- package/dist/fs-CuGv3Ob2.js +23 -0
- package/dist/fs-CuGv3Ob2.js.map +1 -0
- package/dist/index.js +24 -22
- package/dist/index.js.map +1 -1
- package/dist/init.cmd-BdqImX8b.js +108 -0
- package/dist/init.cmd-BdqImX8b.js.map +1 -0
- package/dist/install.cmd-BaP8k9d2.js +79 -0
- package/dist/install.cmd-BaP8k9d2.js.map +1 -0
- package/dist/output-DiffPD2u.js +104 -0
- package/dist/output-DiffPD2u.js.map +1 -0
- package/dist/package.cmd-BYhkheya.js +107 -0
- package/dist/package.cmd-BYhkheya.js.map +1 -0
- package/dist/stats.cmd-Dd46qjoV.js +154 -0
- package/dist/stats.cmd-Dd46qjoV.js.map +1 -0
- package/dist/{core/templates.js → templates-fyteNbD0.js} +20 -13
- package/dist/templates-fyteNbD0.js.map +1 -0
- package/dist/validate.cmd-BxF4HNsu.js +96 -0
- package/dist/validate.cmd-BxF4HNsu.js.map +1 -0
- package/dist/validator-Dp5x-OjP.js +729 -0
- package/dist/validator-Dp5x-OjP.js.map +1 -0
- package/package.json +34 -35
- package/dist/commands/add-hook.cmd.js +0 -35
- package/dist/commands/add-hook.cmd.js.map +0 -1
- package/dist/commands/add-hook.js +0 -216
- package/dist/commands/add-hook.js.map +0 -1
- package/dist/commands/doctor.cmd.js +0 -19
- package/dist/commands/doctor.cmd.js.map +0 -1
- package/dist/commands/doctor.js +0 -128
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/init.cmd.js +0 -37
- package/dist/commands/init.cmd.js.map +0 -1
- package/dist/commands/init.js +0 -86
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/install.cmd.js +0 -23
- package/dist/commands/install.cmd.js.map +0 -1
- package/dist/commands/install.js +0 -64
- package/dist/commands/install.js.map +0 -1
- package/dist/commands/package.cmd.js +0 -28
- package/dist/commands/package.cmd.js.map +0 -1
- package/dist/commands/package.js +0 -134
- package/dist/commands/package.js.map +0 -1
- package/dist/commands/stats.cmd.js +0 -19
- package/dist/commands/stats.cmd.js.map +0 -1
- package/dist/commands/stats.js +0 -154
- package/dist/commands/stats.js.map +0 -1
- package/dist/commands/validate.cmd.js +0 -39
- package/dist/commands/validate.cmd.js.map +0 -1
- package/dist/commands/validate.js +0 -77
- package/dist/commands/validate.js.map +0 -1
- package/dist/core/templates.js.map +0 -1
- package/dist/core/validator.js +0 -252
- package/dist/core/validator.js.map +0 -1
- package/dist/help.js +0 -305
- package/dist/help.js.map +0 -1
- package/dist/skills/.gitkeep +0 -0
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/utils/fs.js +0 -25
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/output.js +0 -102
- package/dist/utils/output.js.map +0 -1
- package/dist/validators/alignment-validator.js +0 -54
- package/dist/validators/alignment-validator.js.map +0 -1
- package/dist/validators/content-validator.js +0 -156
- package/dist/validators/content-validator.js.map +0 -1
- package/dist/validators/description-validator.js +0 -150
- package/dist/validators/description-validator.js.map +0 -1
- package/dist/validators/file-structure-validator.js +0 -125
- package/dist/validators/file-structure-validator.js.map +0 -1
- package/dist/validators/frontmatter-validator.js +0 -190
- package/dist/validators/frontmatter-validator.js.map +0 -1
- package/dist/validators/references-validator.js +0 -155
- package/dist/validators/references-validator.js.map +0 -1
- package/dist/validators/text-analysis.js +0 -71
- package/dist/validators/text-analysis.js.map +0 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { t as ensure_dir } from "./fs-CuGv3Ob2.js";
|
|
2
|
+
import { a as search, c as warning, i as package_, n as error, o as success, s as upload } from "./output-DiffPD2u.js";
|
|
3
|
+
import { t as SkillValidator } from "./validator-Dp5x-OjP.js";
|
|
4
|
+
import { defineCommand } from "citty";
|
|
5
|
+
import { existsSync, statSync } from "node:fs";
|
|
6
|
+
import { basename, join, resolve } from "node:path";
|
|
7
|
+
import { execSync } from "node:child_process";
|
|
8
|
+
//#region src/commands/package.ts
|
|
9
|
+
function validate_skill(skill_path) {
|
|
10
|
+
search("Validating skill...");
|
|
11
|
+
const result = new SkillValidator(skill_path).validate_all();
|
|
12
|
+
if (result.errors.length > 0) {
|
|
13
|
+
console.log("\n❌ Errors:");
|
|
14
|
+
for (const err of result.errors) console.log(` ${String(err)}`);
|
|
15
|
+
}
|
|
16
|
+
if (result.warnings.length > 0) {
|
|
17
|
+
console.log("\n⚠️ Warnings:");
|
|
18
|
+
for (const warn of result.warnings) console.log(` ${warn}`);
|
|
19
|
+
}
|
|
20
|
+
if (result.is_valid) {
|
|
21
|
+
success("Skill is valid!");
|
|
22
|
+
console.log("");
|
|
23
|
+
return true;
|
|
24
|
+
} else {
|
|
25
|
+
error("Validation failed. Fix errors before packaging.");
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function package_skill(skill_path, output_dir) {
|
|
30
|
+
const skill_name = basename(skill_path);
|
|
31
|
+
const output_file = resolve(output_dir, `${skill_name}.zip`);
|
|
32
|
+
package_(`Packaging skill: ${skill_name}`);
|
|
33
|
+
ensure_dir(output_dir);
|
|
34
|
+
if (existsSync(output_file)) execSync(`rm ${output_file}`);
|
|
35
|
+
execSync(`cd "${resolve(skill_path, "..")}" && zip -r "${output_file}" "${skill_name}" -x '${skill_name}/.*' '${skill_name}/*.swp' '${skill_name}/*~' '${skill_name}/.DS_Store'`);
|
|
36
|
+
return output_file;
|
|
37
|
+
}
|
|
38
|
+
async function package_command(options) {
|
|
39
|
+
const { skill_path, output, skip_validation } = options;
|
|
40
|
+
if (!existsSync(skill_path)) {
|
|
41
|
+
error(`Skill directory does not exist: ${skill_path}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
if (!statSync(skill_path).isDirectory()) {
|
|
45
|
+
error(`Path is not a directory: ${skill_path}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
if (!existsSync(join(skill_path, "SKILL.md"))) {
|
|
49
|
+
error(`SKILL.md not found in ${skill_path}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
if (!skip_validation) {
|
|
53
|
+
if (!validate_skill(skill_path)) process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const output_file = package_skill(skill_path, output || "dist");
|
|
57
|
+
const size_kb = statSync(output_file).size / 1024;
|
|
58
|
+
console.log("");
|
|
59
|
+
success("Skill packaged successfully!");
|
|
60
|
+
console.log(` File: ${output_file}`);
|
|
61
|
+
console.log(` Size: ${size_kb.toFixed(1)} KB`);
|
|
62
|
+
console.log("");
|
|
63
|
+
warning("Note: ZIP packaging is not an official Claude Code distribution method.");
|
|
64
|
+
console.log(" Official distribution uses the plugin system (/plugin install)");
|
|
65
|
+
console.log(" or direct file placement in .claude/skills/.");
|
|
66
|
+
console.log(" See: https://code.claude.com/docs/en/plugins");
|
|
67
|
+
console.log("");
|
|
68
|
+
upload("Upload to Claude.ai: Settings > Features > Skills > Upload");
|
|
69
|
+
} catch (err) {
|
|
70
|
+
error(`Failed to package skill: ${String(err)}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/commands/package.cmd.ts
|
|
76
|
+
var package_cmd_default = defineCommand({
|
|
77
|
+
meta: {
|
|
78
|
+
name: "package",
|
|
79
|
+
description: "Package a skill to zip"
|
|
80
|
+
},
|
|
81
|
+
args: {
|
|
82
|
+
skill_path: {
|
|
83
|
+
type: "positional",
|
|
84
|
+
description: "Path to skill directory",
|
|
85
|
+
required: true
|
|
86
|
+
},
|
|
87
|
+
output: {
|
|
88
|
+
type: "string",
|
|
89
|
+
description: "Output path for zip file"
|
|
90
|
+
},
|
|
91
|
+
"skip-validation": {
|
|
92
|
+
type: "boolean",
|
|
93
|
+
description: "Skip validation before packaging"
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
async run({ args }) {
|
|
97
|
+
await package_command({
|
|
98
|
+
skill_path: args.skill_path,
|
|
99
|
+
output: args.output,
|
|
100
|
+
skip_validation: args["skip-validation"]
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
//#endregion
|
|
105
|
+
export { package_cmd_default as default };
|
|
106
|
+
|
|
107
|
+
//# sourceMappingURL=package.cmd-BYhkheya.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { l as LIMITS, n as error } from "./output-DiffPD2u.js";
|
|
2
|
+
import { t as SkillValidator } from "./validator-Dp5x-OjP.js";
|
|
3
|
+
import { defineCommand } from "citty";
|
|
4
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
//#region src/commands/stats.ts
|
|
7
|
+
function stats_command(options) {
|
|
8
|
+
const directory = options.directory || ".claude/skills";
|
|
9
|
+
if (!existsSync(directory)) {
|
|
10
|
+
error(`Directory not found: ${directory}`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
if (!statSync(directory).isDirectory()) {
|
|
14
|
+
error(`Path is not a directory: ${directory}`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const entries = readdirSync(directory);
|
|
18
|
+
const skills = [];
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
const skill_path = join(directory, entry);
|
|
21
|
+
if (statSync(skill_path).isDirectory()) {
|
|
22
|
+
if (existsSync(join(skill_path, "SKILL.md"))) skills.push(skill_path);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (skills.length === 0) {
|
|
26
|
+
console.log(`No skills found in ${directory}`);
|
|
27
|
+
console.log("\nCreate a skill with: claude-skills-cli init --name my-skill --description \"...\"\n");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log("📊 Skills Overview");
|
|
31
|
+
console.log("============================================================");
|
|
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
|
+
};
|
|
38
|
+
for (const skill_path of skills) {
|
|
39
|
+
const skill_name = skill_path.split("/").pop() || "";
|
|
40
|
+
const result = new SkillValidator(skill_path).validate_all();
|
|
41
|
+
let status_icon = "✅";
|
|
42
|
+
let status_text = "valid";
|
|
43
|
+
if (!result.is_valid) {
|
|
44
|
+
status_icon = "❌";
|
|
45
|
+
status_text = "errors";
|
|
46
|
+
} else if (result.warnings.length > 0) {
|
|
47
|
+
status_icon = "⚠️ ";
|
|
48
|
+
status_text = "warnings";
|
|
49
|
+
}
|
|
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}`);
|
|
59
|
+
if (result.stats) {
|
|
60
|
+
const desc_length = result.stats.description_length;
|
|
61
|
+
let desc_status = "";
|
|
62
|
+
if (desc_length > 0) {
|
|
63
|
+
if (desc_length <= 250) desc_status = "optimal";
|
|
64
|
+
else desc_status = "too long";
|
|
65
|
+
console.log(` Description: ${desc_length} chars (${desc_status})`);
|
|
66
|
+
}
|
|
67
|
+
const lines = result.stats.line_count;
|
|
68
|
+
const words = result.stats.word_count;
|
|
69
|
+
const S = LIMITS.strict;
|
|
70
|
+
let line_status = "";
|
|
71
|
+
if (lines <= S.lines.excellent) line_status = "excellent";
|
|
72
|
+
else if (lines <= S.lines.good) line_status = "good";
|
|
73
|
+
else if (lines <= S.lines.max) line_status = "consider splitting";
|
|
74
|
+
else line_status = "too long";
|
|
75
|
+
let word_status = "";
|
|
76
|
+
if (words < S.words.excellent) word_status = "excellent";
|
|
77
|
+
else if (words < S.words.good) word_status = "good";
|
|
78
|
+
else if (words < S.words.max) word_status = "acceptable";
|
|
79
|
+
else word_status = "too long";
|
|
80
|
+
console.log(` Body: ${lines} lines (${line_status}), ${words} words (${word_status})`);
|
|
81
|
+
const references_dir = join(skill_path, "references");
|
|
82
|
+
if (existsSync(references_dir)) {
|
|
83
|
+
const ref_files = readdirSync(references_dir).filter((f) => f.endsWith(".md"));
|
|
84
|
+
if (ref_files.length > 0) {
|
|
85
|
+
let total_size = 0;
|
|
86
|
+
for (const ref_file of ref_files) {
|
|
87
|
+
const ref_stat = statSync(join(references_dir, ref_file));
|
|
88
|
+
total_size += ref_stat.size;
|
|
89
|
+
}
|
|
90
|
+
const size_kb = (total_size / 1024).toFixed(1);
|
|
91
|
+
console.log(` References: ${ref_files.length} file${ref_files.length === 1 ? "" : "s"} (${size_kb} KB)`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!result.is_valid) console.log(` ${result.errors.length} error${result.errors.length === 1 ? "" : "s"}`);
|
|
96
|
+
if (result.warnings.length > 0) console.log(` ${result.warnings.length} warning${result.warnings.length === 1 ? "" : "s"}`);
|
|
97
|
+
console.log("");
|
|
98
|
+
}
|
|
99
|
+
const valid_skills = skills.filter((path) => {
|
|
100
|
+
return new SkillValidator(path).validate_all().is_valid;
|
|
101
|
+
}).length;
|
|
102
|
+
const skills_with_warnings = skills.filter((path) => {
|
|
103
|
+
const result = new SkillValidator(path).validate_all();
|
|
104
|
+
return result.is_valid && result.warnings.length > 0;
|
|
105
|
+
}).length;
|
|
106
|
+
const invalid_skills = skills.length - valid_skills;
|
|
107
|
+
console.log("Summary:");
|
|
108
|
+
if (invalid_skills === 0 && skills_with_warnings === 0) console.log(`✅ All ${skills.length} skills are valid with no warnings`);
|
|
109
|
+
else {
|
|
110
|
+
console.log(` Valid: ${valid_skills}`);
|
|
111
|
+
if (skills_with_warnings > 0) console.log(` With warnings: ${skills_with_warnings}`);
|
|
112
|
+
if (invalid_skills > 0) console.log(` Invalid: ${invalid_skills}`);
|
|
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
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region src/commands/stats.cmd.ts
|
|
137
|
+
var stats_cmd_default = defineCommand({
|
|
138
|
+
meta: {
|
|
139
|
+
name: "stats",
|
|
140
|
+
description: "Show overview of all skills in a directory"
|
|
141
|
+
},
|
|
142
|
+
args: { directory: {
|
|
143
|
+
type: "positional",
|
|
144
|
+
description: "Directory containing skills (default: .claude/skills)",
|
|
145
|
+
required: false
|
|
146
|
+
} },
|
|
147
|
+
run({ args }) {
|
|
148
|
+
stats_command({ directory: args.directory });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
//#endregion
|
|
152
|
+
export { stats_cmd_default as default };
|
|
153
|
+
|
|
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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"}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/core/templates.ts
|
|
2
|
+
const SKILL_MD_TEMPLATE = (name, description, title, include_examples = false) => {
|
|
3
|
+
const minimal_template = `---
|
|
3
4
|
name: ${name}
|
|
4
5
|
# Keep on ONE line, third-person voice, include "Use when/for/to..." trigger
|
|
5
6
|
# prettier-ignore
|
|
6
7
|
description: ${description}
|
|
8
|
+
# version: 0.1.0
|
|
9
|
+
# depends-on-skills: []
|
|
10
|
+
# depends-on-mcp: []
|
|
11
|
+
# depends-on-packages: []
|
|
7
12
|
---
|
|
8
13
|
|
|
9
14
|
# ${title}
|
|
@@ -43,7 +48,7 @@ For detailed documentation, see:
|
|
|
43
48
|
PROGRESSIVE DISCLOSURE GUIDELINES:
|
|
44
49
|
- Keep this file ~50 lines total (max ~150 lines)
|
|
45
50
|
- Use 1-2 code blocks only (recommend 1)
|
|
46
|
-
- Keep description <
|
|
51
|
+
- Keep description <250 chars (Claude truncates at this limit)
|
|
47
52
|
- Move detailed docs to references/ for Level 3 loading
|
|
48
53
|
- This is Level 2 - quick reference ONLY, not a manual
|
|
49
54
|
|
|
@@ -55,7 +60,7 @@ LLM WORKFLOW (when editing this file):
|
|
|
55
60
|
5. Validate again to confirm
|
|
56
61
|
-->
|
|
57
62
|
`;
|
|
58
|
-
|
|
63
|
+
const full_template = `---
|
|
59
64
|
name: ${name}
|
|
60
65
|
# Keep on ONE line, third-person voice, include "Use when/for/to..." trigger
|
|
61
66
|
# prettier-ignore
|
|
@@ -104,7 +109,7 @@ For detailed documentation, see:
|
|
|
104
109
|
PROGRESSIVE DISCLOSURE GUIDELINES:
|
|
105
110
|
- Keep this file ~50 lines total (max ~150 lines)
|
|
106
111
|
- Use 1-2 code blocks only (recommend 1)
|
|
107
|
-
- Keep description <
|
|
112
|
+
- Keep description <250 chars (Claude truncates at this limit)
|
|
108
113
|
- Move detailed docs to references/ for Level 3 loading
|
|
109
114
|
- This is Level 2 - quick reference ONLY, not a manual
|
|
110
115
|
|
|
@@ -116,9 +121,9 @@ LLM WORKFLOW (when editing this file):
|
|
|
116
121
|
5. Validate again to confirm
|
|
117
122
|
-->
|
|
118
123
|
`;
|
|
119
|
-
|
|
124
|
+
return include_examples ? full_template : minimal_template;
|
|
120
125
|
};
|
|
121
|
-
|
|
126
|
+
const REFERENCE_TEMPLATE = (title) => `# ${title} Reference
|
|
122
127
|
|
|
123
128
|
<!-- This is a Level 3 resource file -->
|
|
124
129
|
<!-- It's loaded on-demand when Claude needs detailed information -->
|
|
@@ -173,7 +178,7 @@ export const REFERENCE_TEMPLATE = (title) => `# ${title} Reference
|
|
|
173
178
|
**Problem:** [Description]
|
|
174
179
|
**Solution:** [Detailed solution]
|
|
175
180
|
`;
|
|
176
|
-
|
|
181
|
+
const SCRIPT_TEMPLATE = (filename) => `#!/usr/bin/env node
|
|
177
182
|
|
|
178
183
|
/**
|
|
179
184
|
* Description of what this script does.
|
|
@@ -188,11 +193,10 @@ function main() {
|
|
|
188
193
|
|
|
189
194
|
main();
|
|
190
195
|
`;
|
|
191
|
-
|
|
192
|
-
export const SIMPLE_HOOK_TEMPLATE = () => `#!/bin/bash
|
|
196
|
+
const SIMPLE_HOOK_TEMPLATE = () => `#!/bin/bash
|
|
193
197
|
echo 'INSTRUCTION: If the prompt matches any available skill keywords, use Skill(skill-name) to activate it.'
|
|
194
198
|
`;
|
|
195
|
-
|
|
199
|
+
const FORCED_EVAL_HOOK_TEMPLATE = () => `#!/bin/bash
|
|
196
200
|
# UserPromptSubmit hook that forces explicit skill evaluation
|
|
197
201
|
#
|
|
198
202
|
# This hook requires Claude to explicitly evaluate each available skill
|
|
@@ -228,7 +232,7 @@ Example of correct sequence:
|
|
|
228
232
|
[THEN and ONLY THEN start implementation]
|
|
229
233
|
EOF
|
|
230
234
|
`;
|
|
231
|
-
|
|
235
|
+
const LLM_EVAL_HOOK_TEMPLATE = () => `#!/bin/bash
|
|
232
236
|
# UserPromptSubmit hook that uses Claude API for intelligent skill evaluation
|
|
233
237
|
#
|
|
234
238
|
# This hook analyses each user prompt and uses the Claude API to determine which
|
|
@@ -386,4 +390,7 @@ else
|
|
|
386
390
|
echo "$FALLBACK_INSTRUCTION"
|
|
387
391
|
fi
|
|
388
392
|
`;
|
|
389
|
-
//#
|
|
393
|
+
//#endregion
|
|
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 };
|
|
395
|
+
|
|
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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
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
|
+
import { defineCommand } from "citty";
|
|
4
|
+
import { basename } from "node:path";
|
|
5
|
+
//#region src/commands/validate.ts
|
|
6
|
+
function validate_command(options) {
|
|
7
|
+
const { skill_path, strict, format = "text", lenient = false, loose = false } = options;
|
|
8
|
+
const skill_name = basename(skill_path.replace(/\/+$/, ""));
|
|
9
|
+
const result = new SkillValidator(skill_path, { mode: loose ? "loose" : lenient ? "lenient" : "strict" }).validate_all();
|
|
10
|
+
if (format === "json") {
|
|
11
|
+
const json_output = {
|
|
12
|
+
skill_name,
|
|
13
|
+
valid: result.is_valid,
|
|
14
|
+
errors: result.errors,
|
|
15
|
+
warnings: result.warnings,
|
|
16
|
+
validation: result.validation,
|
|
17
|
+
stats: result.stats
|
|
18
|
+
};
|
|
19
|
+
console.log(JSON.stringify(json_output, null, 2));
|
|
20
|
+
if (!result.is_valid) process.exit(1);
|
|
21
|
+
if (strict && result.warnings.length > 0) process.exit(1);
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
info(`Validating skill: ${skill_name}`);
|
|
25
|
+
console.log("=".repeat(60));
|
|
26
|
+
if (result.stats) display_validation_stats(result.stats);
|
|
27
|
+
if (result.errors.length > 0) {
|
|
28
|
+
console.log("\n❌ Errors:");
|
|
29
|
+
for (const err of result.errors) console.log(` ${err}`);
|
|
30
|
+
}
|
|
31
|
+
if (result.warnings.length > 0) {
|
|
32
|
+
console.log("\n⚠️ Warnings:");
|
|
33
|
+
for (const warn of result.warnings) console.log(` ${warn}`);
|
|
34
|
+
}
|
|
35
|
+
if (!result.errors.length && !result.warnings.length) {
|
|
36
|
+
console.log("");
|
|
37
|
+
success("Skill is valid!");
|
|
38
|
+
} else if (!result.errors.length) {
|
|
39
|
+
console.log("");
|
|
40
|
+
success("Skill is valid (with warnings)");
|
|
41
|
+
} else {
|
|
42
|
+
console.log("");
|
|
43
|
+
error(`Skill validation failed with ${result.errors.length} error(s)`);
|
|
44
|
+
}
|
|
45
|
+
if (!result.is_valid) process.exit(1);
|
|
46
|
+
if (strict && result.warnings.length > 0) {
|
|
47
|
+
console.log("\n❌ Failed in strict mode due to warnings");
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/commands/validate.cmd.ts
|
|
54
|
+
var validate_cmd_default = defineCommand({
|
|
55
|
+
meta: {
|
|
56
|
+
name: "validate",
|
|
57
|
+
description: "Validate a skill"
|
|
58
|
+
},
|
|
59
|
+
args: {
|
|
60
|
+
skill_path: {
|
|
61
|
+
type: "positional",
|
|
62
|
+
description: "Path to skill directory",
|
|
63
|
+
required: true
|
|
64
|
+
},
|
|
65
|
+
format: {
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "Output format (json or text)",
|
|
68
|
+
default: "text"
|
|
69
|
+
},
|
|
70
|
+
strict: {
|
|
71
|
+
type: "boolean",
|
|
72
|
+
description: "Fail validation if warnings present"
|
|
73
|
+
},
|
|
74
|
+
lenient: {
|
|
75
|
+
type: "boolean",
|
|
76
|
+
description: "Use relaxed limits (150 lines max)"
|
|
77
|
+
},
|
|
78
|
+
loose: {
|
|
79
|
+
type: "boolean",
|
|
80
|
+
description: "Use Anthropic official limits (500 lines max)"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
run({ args }) {
|
|
84
|
+
validate_command({
|
|
85
|
+
skill_path: args.skill_path,
|
|
86
|
+
strict: args.strict,
|
|
87
|
+
format: args.format === "json" ? "json" : "text",
|
|
88
|
+
lenient: args.lenient,
|
|
89
|
+
loose: args.loose
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
//#endregion
|
|
94
|
+
export { validate_cmd_default as default };
|
|
95
|
+
|
|
96
|
+
//# sourceMappingURL=validate.cmd-BxF4HNsu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|