claude-skills-cli 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -9
- package/dist/commands/doctor.js +128 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.js +5 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.js +1 -1
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/stats.js +1 -1
- package/dist/commands/stats.js.map +1 -1
- package/dist/commands/validate.js +24 -3
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/watch.js +82 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/core/templates.js +18 -0
- package/dist/core/templates.js.map +1 -1
- package/dist/core/validator.js +174 -321
- package/dist/core/validator.js.map +1 -1
- package/dist/index.js +38 -12
- package/dist/index.js.map +1 -1
- package/dist/skills/skill-creator/SKILL.md +28 -112
- package/dist/skills/skill-creator/references/cli-reference.md +152 -38
- package/dist/skills/skill-creator/references/development-process.md +208 -0
- package/dist/skills/skill-creator/references/skill-examples.md +1 -1
- package/dist/validators/alignment-validator.js +54 -0
- package/dist/validators/alignment-validator.js.map +1 -0
- package/dist/validators/content-validator.js +144 -0
- package/dist/validators/content-validator.js.map +1 -0
- package/dist/validators/description-validator.js +136 -0
- package/dist/validators/description-validator.js.map +1 -0
- package/dist/validators/file-structure-validator.js +125 -0
- package/dist/validators/file-structure-validator.js.map +1 -0
- package/dist/validators/frontmatter-validator.js +165 -0
- package/dist/validators/frontmatter-validator.js.map +1 -0
- package/dist/validators/references-validator.js +125 -0
- package/dist/validators/references-validator.js.map +1 -0
- package/dist/validators/text-analysis.js +71 -0
- package/dist/validators/text-analysis.js.map +1 -0
- package/docs/prompt-that-started-it-all.md +19 -0
- package/package.json +3 -3
- package/docs/SKILL-DEVELOPMENT.md +0 -460
- package/docs/SKILL-EXAMPLES.md +0 -528
- package/docs/SKILLS-ARCHITECTURE.md +0 -381
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File structure validation - paths, scripts, assets
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, readdirSync, readFileSync, statSync, } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
/**
|
|
7
|
+
* Validate that skill directory exists and is valid
|
|
8
|
+
*/
|
|
9
|
+
export function validate_directory(skill_path) {
|
|
10
|
+
const errors = [];
|
|
11
|
+
if (!existsSync(skill_path)) {
|
|
12
|
+
errors.push({
|
|
13
|
+
type: 'not_found',
|
|
14
|
+
message: `Skill directory does not exist: ${skill_path}`,
|
|
15
|
+
});
|
|
16
|
+
return { valid: false, errors };
|
|
17
|
+
}
|
|
18
|
+
const stats = statSync(skill_path);
|
|
19
|
+
if (!stats.isDirectory()) {
|
|
20
|
+
errors.push({
|
|
21
|
+
type: 'not_directory',
|
|
22
|
+
message: `Path is not a directory: ${skill_path}`,
|
|
23
|
+
});
|
|
24
|
+
return { valid: false, errors };
|
|
25
|
+
}
|
|
26
|
+
return { valid: true, errors: [] };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validate path formats (no Windows backslashes)
|
|
30
|
+
*/
|
|
31
|
+
export function validate_path_formats(content, file_name = 'SKILL.md') {
|
|
32
|
+
const invalid_paths = [];
|
|
33
|
+
const errors = [];
|
|
34
|
+
const lines = content.split('\n');
|
|
35
|
+
lines.forEach((line, index) => {
|
|
36
|
+
// Skip code blocks (they might legitimately show Windows paths as examples)
|
|
37
|
+
if (line.trim().startsWith('```'))
|
|
38
|
+
return;
|
|
39
|
+
// Detect backslashes in file paths
|
|
40
|
+
// Match patterns like: scripts\file.py, references\doc.md, etc.
|
|
41
|
+
const backslash_pattern = /(?:scripts|references|assets|examples)\\[\w\\.-]+/g;
|
|
42
|
+
const matches = line.match(backslash_pattern);
|
|
43
|
+
if (matches) {
|
|
44
|
+
matches.forEach((match) => {
|
|
45
|
+
const fixed = match.replace(/\\/g, '/');
|
|
46
|
+
// Store in validation
|
|
47
|
+
invalid_paths.push({
|
|
48
|
+
line_number: index + 1,
|
|
49
|
+
path: match,
|
|
50
|
+
error: 'Windows-style backslash detected',
|
|
51
|
+
suggested_fix: fixed,
|
|
52
|
+
});
|
|
53
|
+
errors.push({
|
|
54
|
+
type: 'windows_path',
|
|
55
|
+
message: `Windows-style path in ${file_name}:${index + 1}\n` +
|
|
56
|
+
` → Found: ${match}\n` +
|
|
57
|
+
` → Use: ${fixed}`,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
validation: { invalid_paths },
|
|
64
|
+
errors,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validate scripts directory
|
|
69
|
+
*/
|
|
70
|
+
export function validate_scripts(skill_path) {
|
|
71
|
+
const scripts_dir = join(skill_path, 'scripts');
|
|
72
|
+
const warnings = [];
|
|
73
|
+
if (existsSync(scripts_dir)) {
|
|
74
|
+
const files = readdirSync(scripts_dir);
|
|
75
|
+
const script_files = files.filter((f) => f.endsWith('.js') ||
|
|
76
|
+
f.endsWith('.ts') ||
|
|
77
|
+
f.endsWith('.mjs') ||
|
|
78
|
+
f.endsWith('.sh'));
|
|
79
|
+
if (script_files.length === 0) {
|
|
80
|
+
warnings.push({
|
|
81
|
+
type: 'empty_directory',
|
|
82
|
+
message: 'scripts/ directory exists but is empty',
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
for (const script_file of script_files) {
|
|
86
|
+
const script_path = join(scripts_dir, script_file);
|
|
87
|
+
const stats = statSync(script_path);
|
|
88
|
+
// Check if executable (0o111 = --x--x--x)
|
|
89
|
+
if ((stats.mode & 0o111) === 0) {
|
|
90
|
+
warnings.push({
|
|
91
|
+
type: 'not_executable',
|
|
92
|
+
message: `Script is not executable: ${script_file}`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// Check for shebang
|
|
96
|
+
const content = readFileSync(script_path, 'utf-8');
|
|
97
|
+
const first_line = content.split('\n')[0];
|
|
98
|
+
if (!first_line.startsWith('#!')) {
|
|
99
|
+
warnings.push({
|
|
100
|
+
type: 'missing_shebang',
|
|
101
|
+
message: `Script missing shebang: ${script_file}`,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { warnings };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Validate assets directory
|
|
110
|
+
*/
|
|
111
|
+
export function validate_assets(skill_path) {
|
|
112
|
+
const assets_dir = join(skill_path, 'assets');
|
|
113
|
+
const warnings = [];
|
|
114
|
+
if (existsSync(assets_dir)) {
|
|
115
|
+
const files = readdirSync(assets_dir);
|
|
116
|
+
if (files.length === 0) {
|
|
117
|
+
warnings.push({
|
|
118
|
+
type: 'empty_directory',
|
|
119
|
+
message: 'assets/ directory exists but is empty',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { warnings };
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=file-structure-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-structure-validator.js","sourceRoot":"","sources":["../../src/validators/file-structure-validator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,UAAU,EACV,WAAW,EACX,YAAY,EACZ,QAAQ,GACR,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA0BjC;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IAIpD,MAAM,MAAM,GAAqB,EAAE,CAAC;IAEpC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,mCAAmC,UAAU,EAAE;SACxD,CAAC,CAAC;QACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACjC,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,4BAA4B,UAAU,EAAE;SACjD,CAAC,CAAC;QACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACjC,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACpC,OAAe,EACf,YAAoB,UAAU;IAK9B,MAAM,aAAa,GAAsB,EAAE,CAAC;IAC5C,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC7B,4EAA4E;QAC5E,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO;QAE1C,mCAAmC;QACnC,gEAAgE;QAChE,MAAM,iBAAiB,GACtB,oDAAoD,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAE9C,IAAI,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;gBACzB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;gBAExC,sBAAsB;gBACtB,aAAa,CAAC,IAAI,CAAC;oBAClB,WAAW,EAAE,KAAK,GAAG,CAAC;oBACtB,IAAI,EAAE,KAAK;oBACX,KAAK,EAAE,kCAAkC;oBACzC,aAAa,EAAE,KAAK;iBACpB,CAAC,CAAC;gBAEH,MAAM,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,cAAc;oBACpB,OAAO,EACN,yBAAyB,SAAS,IAAI,KAAK,GAAG,CAAC,IAAI;wBACnD,cAAc,KAAK,IAAI;wBACvB,YAAY,KAAK,EAAE;iBACpB,CAAC,CAAC;YACJ,CAAC,CAAC,CAAC;QACJ,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,OAAO;QACN,UAAU,EAAE,EAAE,aAAa,EAAE;QAC7B,MAAM;KACN,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IAGlD,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAqB,EAAE,CAAC;IAEtC,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;QACvC,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAChC,CAAC,CAAC,EAAE,EAAE,CACL,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;YACjB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;YACjB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YAClB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAClB,CAAC;QAEF,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC;gBACb,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,wCAAwC;aACjD,CAAC,CAAC;QACJ,CAAC;QAED,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;YACxC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YACnD,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;YAEpC,0CAA0C;YAC1C,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBAChC,QAAQ,CAAC,IAAI,CAAC;oBACb,IAAI,EAAE,gBAAgB;oBACtB,OAAO,EAAE,6BAA6B,WAAW,EAAE;iBACnD,CAAC,CAAC;YACJ,CAAC;YAED,oBAAoB;YACpB,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,QAAQ,CAAC,IAAI,CAAC;oBACb,IAAI,EAAE,iBAAiB;oBACvB,OAAO,EAAE,2BAA2B,WAAW,EAAE;iBACjD,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB;IAGjD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;QAEtC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,QAAQ,CAAC,IAAI,CAAC;gBACb,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,uCAAuC;aAChD,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML frontmatter validation for SKILL.md
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Check if content has valid YAML frontmatter
|
|
6
|
+
*/
|
|
7
|
+
export function has_yaml_frontmatter(content) {
|
|
8
|
+
return content.startsWith('---\n') || content.startsWith('---\r\n');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Check if description field spans multiple lines in raw YAML
|
|
12
|
+
*/
|
|
13
|
+
export function is_description_multiline(frontmatter) {
|
|
14
|
+
// Find the description line
|
|
15
|
+
const desc_line_match = frontmatter.match(/^description:\s*(.*)$/m);
|
|
16
|
+
if (!desc_line_match) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const value_on_same_line = desc_line_match[1].trim();
|
|
20
|
+
// If there's no value on the same line as "description:", it's multi-line
|
|
21
|
+
if (!value_on_same_line) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
// Check if there are continuation lines (indented lines after description:)
|
|
25
|
+
// that are not other YAML fields
|
|
26
|
+
const lines = frontmatter.split('\n');
|
|
27
|
+
let found_desc = false;
|
|
28
|
+
for (let i = 0; i < lines.length; i++) {
|
|
29
|
+
const line = lines[i];
|
|
30
|
+
if (line.match(/^description:/)) {
|
|
31
|
+
found_desc = true;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (found_desc) {
|
|
35
|
+
// If next line starts with spaces/tabs and is not a comment and is not another field
|
|
36
|
+
if (line.match(/^\s+\S/) &&
|
|
37
|
+
!line.trim().startsWith('#') &&
|
|
38
|
+
!line.match(/^[a-z_-]+:/)) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
// Stop checking after we hit another field or end
|
|
42
|
+
if (line.match(/^[a-z_-]+:/)) {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Extract frontmatter and body from SKILL.md content
|
|
51
|
+
*/
|
|
52
|
+
export function extract_frontmatter(content) {
|
|
53
|
+
if (!has_yaml_frontmatter(content)) {
|
|
54
|
+
return {
|
|
55
|
+
name: null,
|
|
56
|
+
description: null,
|
|
57
|
+
body: content,
|
|
58
|
+
description_is_multiline: false,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const parts = content.split('---\n');
|
|
62
|
+
if (parts.length < 3) {
|
|
63
|
+
return {
|
|
64
|
+
name: null,
|
|
65
|
+
description: null,
|
|
66
|
+
body: content,
|
|
67
|
+
description_is_multiline: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const frontmatter = parts[1];
|
|
71
|
+
const body = parts.slice(2).join('---\n');
|
|
72
|
+
// Extract name
|
|
73
|
+
const name_match = frontmatter.match(/name:\s*(.+)/);
|
|
74
|
+
const name = name_match ? name_match[1].trim() : null;
|
|
75
|
+
// Extract description
|
|
76
|
+
const desc_match = frontmatter.match(/description:\s*(.+?)(?=\n[a-z]+:|$)/s);
|
|
77
|
+
const description = desc_match ? desc_match[1].trim() : null;
|
|
78
|
+
// Check if description spans multiple lines in the raw YAML
|
|
79
|
+
const description_is_multiline = is_description_multiline(frontmatter);
|
|
80
|
+
return { name, description, body, description_is_multiline };
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Validate YAML frontmatter structure
|
|
84
|
+
*/
|
|
85
|
+
export function validate_frontmatter_structure(content) {
|
|
86
|
+
const validation = {
|
|
87
|
+
valid: true,
|
|
88
|
+
has_frontmatter: false,
|
|
89
|
+
parse_error: null,
|
|
90
|
+
missing_fields: [],
|
|
91
|
+
};
|
|
92
|
+
if (!has_yaml_frontmatter(content)) {
|
|
93
|
+
validation.valid = false;
|
|
94
|
+
validation.parse_error = 'Missing YAML frontmatter';
|
|
95
|
+
return validation;
|
|
96
|
+
}
|
|
97
|
+
validation.has_frontmatter = true;
|
|
98
|
+
const parts = content.split('---\n');
|
|
99
|
+
if (parts.length < 3) {
|
|
100
|
+
validation.valid = false;
|
|
101
|
+
validation.parse_error = 'Malformed YAML frontmatter';
|
|
102
|
+
return validation;
|
|
103
|
+
}
|
|
104
|
+
const frontmatter = parts[1];
|
|
105
|
+
// Check required fields
|
|
106
|
+
if (!frontmatter.includes('name:')) {
|
|
107
|
+
validation.missing_fields.push('name');
|
|
108
|
+
validation.valid = false;
|
|
109
|
+
}
|
|
110
|
+
if (!frontmatter.includes('description:')) {
|
|
111
|
+
validation.missing_fields.push('description');
|
|
112
|
+
validation.valid = false;
|
|
113
|
+
}
|
|
114
|
+
return validation;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Validate skill name format
|
|
118
|
+
*/
|
|
119
|
+
export function validate_name_format(name, directory_name) {
|
|
120
|
+
const validation = {
|
|
121
|
+
name,
|
|
122
|
+
format_valid: true,
|
|
123
|
+
directory_name,
|
|
124
|
+
matches_directory: true,
|
|
125
|
+
errors: [],
|
|
126
|
+
};
|
|
127
|
+
// Validate kebab-case format
|
|
128
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
129
|
+
validation.format_valid = false;
|
|
130
|
+
validation.errors.push(`Skill name must be lowercase kebab-case: '${name}'`);
|
|
131
|
+
}
|
|
132
|
+
// Check name matches directory
|
|
133
|
+
if (name !== directory_name) {
|
|
134
|
+
validation.matches_directory = false;
|
|
135
|
+
validation.errors.push(`Skill name '${name}' must match directory name '${directory_name}'`);
|
|
136
|
+
}
|
|
137
|
+
return validation;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Validate hard limits for name and description
|
|
141
|
+
*/
|
|
142
|
+
export function validate_hard_limits(name, description) {
|
|
143
|
+
const limits = {
|
|
144
|
+
name: { length: 0, limit: 64, valid: true, error: null },
|
|
145
|
+
description: { length: 0, limit: 1024, valid: true, error: null },
|
|
146
|
+
};
|
|
147
|
+
// Validate name length
|
|
148
|
+
if (name) {
|
|
149
|
+
limits.name.length = name.length;
|
|
150
|
+
if (name.length > 64) {
|
|
151
|
+
limits.name.valid = false;
|
|
152
|
+
limits.name.error = `Skill name too long (max 64 chars): ${name.length}`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Validate description length (Anthropic hard limit)
|
|
156
|
+
if (description) {
|
|
157
|
+
limits.description.length = description.length;
|
|
158
|
+
if (description.length > 1024) {
|
|
159
|
+
limits.description.valid = false;
|
|
160
|
+
limits.description.error = `Description too long (max 1024 chars per Anthropic): ${description.length}`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return limits;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=frontmatter-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter-validator.js","sourceRoot":"","sources":["../../src/validators/frontmatter-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* References validation (Level 3 progressive disclosure)
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
/**
|
|
7
|
+
* Check nesting depth of reference files
|
|
8
|
+
*/
|
|
9
|
+
function check_reference_nesting(skill_path, file_path, visited = new Set()) {
|
|
10
|
+
if (visited.has(file_path)) {
|
|
11
|
+
return { depth: 0, references: [] };
|
|
12
|
+
}
|
|
13
|
+
visited.add(file_path);
|
|
14
|
+
const full_path = join(skill_path, file_path);
|
|
15
|
+
if (!existsSync(full_path)) {
|
|
16
|
+
return { depth: 0, references: [] };
|
|
17
|
+
}
|
|
18
|
+
const content = readFileSync(full_path, 'utf-8');
|
|
19
|
+
const reference_pattern = /\[([^\]]+)\]\((references\/[^)]+\.md)\)/g;
|
|
20
|
+
const matches = [...content.matchAll(reference_pattern)];
|
|
21
|
+
const references = matches.map((m) => m[2]);
|
|
22
|
+
if (references.length === 0) {
|
|
23
|
+
return { depth: 1, references: [] };
|
|
24
|
+
}
|
|
25
|
+
// Recursively check nested references
|
|
26
|
+
let max_depth = 1;
|
|
27
|
+
for (const ref of references) {
|
|
28
|
+
const nested = check_reference_nesting(skill_path, ref, new Set(visited));
|
|
29
|
+
max_depth = Math.max(max_depth, 1 + nested.depth);
|
|
30
|
+
}
|
|
31
|
+
return { depth: max_depth, references };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Validate references directory and links
|
|
35
|
+
*/
|
|
36
|
+
export function validate_references(skill_path) {
|
|
37
|
+
const references_dir = join(skill_path, 'references');
|
|
38
|
+
const skill_md_path = join(skill_path, 'SKILL.md');
|
|
39
|
+
const files_found = [];
|
|
40
|
+
const files_referenced = [];
|
|
41
|
+
const missing_files = [];
|
|
42
|
+
const nesting_data = [];
|
|
43
|
+
const warnings = [];
|
|
44
|
+
const errors = [];
|
|
45
|
+
// Check references directory if it exists
|
|
46
|
+
if (existsSync(references_dir)) {
|
|
47
|
+
const files = readdirSync(references_dir);
|
|
48
|
+
const md_files = files.filter((f) => f.endsWith('.md'));
|
|
49
|
+
files_found.push(...md_files);
|
|
50
|
+
if (md_files.length === 0) {
|
|
51
|
+
warnings.push({
|
|
52
|
+
type: 'empty_directory',
|
|
53
|
+
message: 'references/ directory exists but is empty',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Check for references in SKILL.md
|
|
57
|
+
if (existsSync(skill_md_path)) {
|
|
58
|
+
const skill_content = readFileSync(skill_md_path, 'utf-8');
|
|
59
|
+
for (const md_file of md_files) {
|
|
60
|
+
if (!skill_content.includes(md_file)) {
|
|
61
|
+
warnings.push({
|
|
62
|
+
type: 'orphaned_file',
|
|
63
|
+
message: `Reference file '${md_file}' not mentioned in SKILL.md`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Level 3 validation: Check that all referenced files exist
|
|
70
|
+
if (existsSync(skill_md_path)) {
|
|
71
|
+
const skill_content = readFileSync(skill_md_path, 'utf-8');
|
|
72
|
+
// Extract markdown links to references/ directory
|
|
73
|
+
// Matches: [text](references/file.md) or [text](references/subdir/file.md)
|
|
74
|
+
const reference_link_pattern = /\[([^\]]+)\]\((references\/[^)]+\.md)\)/g;
|
|
75
|
+
const matches = skill_content.matchAll(reference_link_pattern);
|
|
76
|
+
for (const match of matches) {
|
|
77
|
+
const link_text = match[1];
|
|
78
|
+
const file_path = match[2]; // e.g., "references/examples.md"
|
|
79
|
+
const full_path = join(skill_path, file_path);
|
|
80
|
+
files_referenced.push(file_path);
|
|
81
|
+
if (!existsSync(full_path)) {
|
|
82
|
+
missing_files.push(file_path);
|
|
83
|
+
errors.push({
|
|
84
|
+
type: 'missing_file',
|
|
85
|
+
message: `Referenced file not found: ${file_path}\n` +
|
|
86
|
+
` → Linked from: [${link_text}]\n` +
|
|
87
|
+
` → Create the file or remove the broken link`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// Check nesting depth
|
|
92
|
+
const nesting = check_reference_nesting(skill_path, file_path);
|
|
93
|
+
let warning = null;
|
|
94
|
+
if (nesting.depth > 1) {
|
|
95
|
+
warning = `File has depth ${nesting.depth} (recommended: 1). Keep references one level deep from SKILL.md.`;
|
|
96
|
+
warnings.push({
|
|
97
|
+
type: 'nesting_depth',
|
|
98
|
+
message: `${file_path} has nesting depth ${nesting.depth} (recommended: 1)\n` +
|
|
99
|
+
` → Keep references one level deep from SKILL.md for clarity`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
nesting_data.push({
|
|
103
|
+
file: file_path,
|
|
104
|
+
references: nesting.references,
|
|
105
|
+
depth: nesting.depth,
|
|
106
|
+
warning,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Calculate orphaned files
|
|
112
|
+
const orphaned = files_found.filter((f) => !files_referenced.some((ref) => ref.includes(f)));
|
|
113
|
+
const validation = {
|
|
114
|
+
files_found,
|
|
115
|
+
files_referenced,
|
|
116
|
+
missing_files,
|
|
117
|
+
orphaned_files: orphaned,
|
|
118
|
+
nesting: nesting_data,
|
|
119
|
+
max_nesting_depth: nesting_data.length > 0
|
|
120
|
+
? Math.max(...nesting_data.map((n) => n.depth))
|
|
121
|
+
: 0,
|
|
122
|
+
};
|
|
123
|
+
return { validation, warnings, errors };
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=references-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"references-validator.js","sourceRoot":"","sources":["../../src/validators/references-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text analysis utilities for skill validation
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Extract body content from SKILL.md (excluding YAML frontmatter)
|
|
6
|
+
*/
|
|
7
|
+
export function extract_body(content) {
|
|
8
|
+
const parts = content.split('---\n');
|
|
9
|
+
return parts.length >= 3
|
|
10
|
+
? parts.slice(2).join('---\n').trim()
|
|
11
|
+
: content;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Count words in text
|
|
15
|
+
*/
|
|
16
|
+
export function count_words(text) {
|
|
17
|
+
return text
|
|
18
|
+
.trim()
|
|
19
|
+
.split(/\s+/)
|
|
20
|
+
.filter((w) => w.length > 0).length;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Estimate tokens (rough approximation: 1 word ≈ 1.3 tokens for English)
|
|
24
|
+
*/
|
|
25
|
+
export function estimate_tokens(word_count) {
|
|
26
|
+
return Math.round(word_count * 1.3);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Estimate tokens for a string by counting words and applying ratio
|
|
30
|
+
*/
|
|
31
|
+
export function estimate_string_tokens(text) {
|
|
32
|
+
const word_count = count_words(text);
|
|
33
|
+
return estimate_tokens(word_count);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Remove HTML comments from content (for line counting)
|
|
37
|
+
*/
|
|
38
|
+
export function strip_html_comments(text) {
|
|
39
|
+
return text.replace(/<!--[\s\S]*?-->/g, '');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Extract keywords from text (simplified extraction)
|
|
43
|
+
*/
|
|
44
|
+
export function extract_keywords(text) {
|
|
45
|
+
const words = text
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.replace(/[^\w\s-]/g, ' ')
|
|
48
|
+
.split(/\s+/)
|
|
49
|
+
.filter((w) => w.length > 3);
|
|
50
|
+
const unique = [...new Set(words)];
|
|
51
|
+
return unique.filter((w) => ![
|
|
52
|
+
'this',
|
|
53
|
+
'that',
|
|
54
|
+
'with',
|
|
55
|
+
'from',
|
|
56
|
+
'have',
|
|
57
|
+
'will',
|
|
58
|
+
'when',
|
|
59
|
+
'what',
|
|
60
|
+
'where',
|
|
61
|
+
'which',
|
|
62
|
+
'their',
|
|
63
|
+
'them',
|
|
64
|
+
'then',
|
|
65
|
+
'than',
|
|
66
|
+
'these',
|
|
67
|
+
'those',
|
|
68
|
+
'there',
|
|
69
|
+
].includes(w));
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=text-analysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-analysis.js","sourceRoot":"","sources":["../../src/validators/text-analysis.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC,MAAM,IAAI,CAAC;QACvB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE;QACrC,CAAC,CAAC,OAAO,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACvC,OAAO,IAAI;SACT,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AACtC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IAClD,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACrC,OAAO,eAAe,CAAC,UAAU,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC5C,MAAM,KAAK,GAAG,IAAI;SAChB,WAAW,EAAE;SACb,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC;SACzB,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE9B,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,MAAM,CACnB,CAAC,CAAC,EAAE,EAAE,CACL,CAAC;QACA,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,OAAO;QACP,OAAO;QACP,OAAO;QACP,MAAM;QACN,MAAM;QACN,MAAM;QACN,OAAO;QACP,OAAO;QACP,OAAO;KACP,CAAC,QAAQ,CAAC,CAAC,CAAC,CACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
ok, you're going to become a super duper Claude skills builder, ok?
|
|
2
|
+
|
|
3
|
+
I'm going to give you some resources I want you to study
|
|
4
|
+
|
|
5
|
+
- https://www.anthropic.com/news/skills
|
|
6
|
+
- https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills
|
|
7
|
+
- https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview#custom-skills-examples
|
|
8
|
+
|
|
9
|
+
feel free to continue you're resource gathering outside of these
|
|
10
|
+
links, there are many linked items in these links that could be of
|
|
11
|
+
use!
|
|
12
|
+
|
|
13
|
+
this is from an image in one of the links which is quite important!
|
|
14
|
+
|
|
15
|
+
| Level | File | Context Window | # Tokens |
|
|
16
|
+
| ----- | ----------------------------------------- | -------------------------- | ----------- |
|
|
17
|
+
| 1 | SKILL.md Metadata (YAML) | Always loaded | ~100 |
|
|
18
|
+
| 2 | SKILL.md Body (Markdown) | Loaded when Skill triggers | <5k |
|
|
19
|
+
| 3+ | Bundled files (text files, scripts, data) | Loaded as-needed by Claude | unlimited\* |
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-skills-cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "CLI toolkit for creating and managing Claude Agent Skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"claude-skills": "./dist/index.js"
|
|
8
|
+
"claude-skills-cli": "./dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@changesets/cli": "^2.29.7",
|
|
42
|
-
"@types/node": "^24.
|
|
42
|
+
"@types/node": "^24.9.0",
|
|
43
43
|
"@types/archiver": "^6.0.3",
|
|
44
44
|
"prettier": "^3.6.2",
|
|
45
45
|
"typescript": "^5.9.3"
|