aiblueprint-cli 1.4.22 → 1.4.24
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/claude-code-config/skills/git-commit/SKILL.md +42 -0
- package/claude-code-config/{commands/git/create-pr.md → skills/git-create-pr/SKILL.md} +12 -18
- package/claude-code-config/skills/git-fix-pr-comments/SKILL.md +51 -0
- package/claude-code-config/skills/git-merge/SKILL.md +68 -0
- package/claude-code-config/skills/{claude-memory → meta-claude-memory}/SKILL.md +7 -2
- package/claude-code-config/skills/meta-claude-memory/references/rules-directory-guide.md +298 -0
- package/claude-code-config/skills/meta-prompt-creator/SKILL.md +285 -0
- package/claude-code-config/skills/meta-prompt-creator/references/anthropic-best-practices.md +126 -0
- package/claude-code-config/skills/meta-prompt-creator/references/anti-patterns.md +57 -0
- package/claude-code-config/skills/meta-prompt-creator/references/clarity-principles.md +54 -0
- package/claude-code-config/skills/meta-prompt-creator/references/context-management.md +389 -0
- package/claude-code-config/skills/meta-prompt-creator/references/few-shot-patterns.md +47 -0
- package/claude-code-config/skills/meta-prompt-creator/references/openai-best-practices.md +50 -0
- package/claude-code-config/skills/meta-prompt-creator/references/prompt-templates.md +110 -0
- package/claude-code-config/skills/meta-prompt-creator/references/reasoning-techniques.md +52 -0
- package/claude-code-config/skills/meta-prompt-creator/references/system-prompt-patterns.md +48 -0
- package/claude-code-config/skills/meta-prompt-creator/references/xml-structure.md +36 -0
- package/claude-code-config/skills/meta-skill-creator/LICENSE.txt +202 -0
- package/claude-code-config/skills/meta-skill-creator/SKILL.md +421 -0
- package/claude-code-config/skills/meta-skill-creator/package.json +5 -0
- package/claude-code-config/skills/meta-skill-creator/references/output-patterns.md +82 -0
- package/claude-code-config/skills/meta-skill-creator/references/progressive-disclosure-patterns.md +374 -0
- package/claude-code-config/skills/meta-skill-creator/references/prompting-integration.md +363 -0
- package/claude-code-config/skills/meta-skill-creator/references/real-world-examples.md +513 -0
- package/claude-code-config/skills/meta-skill-creator/references/script-patterns.md +385 -0
- package/claude-code-config/skills/meta-skill-creator/references/workflows.md +28 -0
- package/claude-code-config/skills/meta-skill-creator/references/xml-tag-guide.md +606 -0
- package/claude-code-config/skills/meta-skill-creator/scripts/init-skill.ts +214 -0
- package/claude-code-config/skills/meta-skill-creator/scripts/package-skill.ts +146 -0
- package/claude-code-config/skills/meta-skill-creator/scripts/validate.ts +138 -0
- package/claude-code-config/skills/{create-subagents → meta-subagent-creator}/SKILL.md +41 -15
- package/claude-code-config/skills/{setup-ralph → ralph-loop}/steps/step-00-init.md +2 -3
- package/claude-code-config/skills/utils-fix-errors/SKILL.md +61 -0
- package/claude-code-config/skills/utils-fix-grammar/SKILL.md +59 -0
- package/claude-code-config/skills/utils-oneshot/SKILL.md +56 -0
- package/claude-code-config/skills/workflow-apex/SKILL.md +303 -0
- package/claude-code-config/skills/workflow-apex/scripts/setup-templates.sh +134 -0
- package/claude-code-config/skills/workflow-apex/scripts/update-progress.sh +80 -0
- package/claude-code-config/skills/workflow-apex/steps/step-00-init.md +288 -0
- package/claude-code-config/skills/workflow-apex/steps/step-00b-branch.md +126 -0
- package/claude-code-config/skills/workflow-apex/steps/step-00b-economy.md +244 -0
- package/claude-code-config/skills/workflow-apex/steps/step-00b-interactive.md +153 -0
- package/claude-code-config/skills/workflow-apex/steps/step-01-analyze.md +361 -0
- package/claude-code-config/skills/workflow-apex/steps/step-02-plan.md +264 -0
- package/claude-code-config/skills/workflow-apex/steps/step-03-execute.md +239 -0
- package/claude-code-config/skills/workflow-apex/steps/step-04-validate.md +264 -0
- package/claude-code-config/skills/workflow-apex/steps/step-05-examine.md +294 -0
- package/claude-code-config/skills/workflow-apex/steps/step-06-resolve.md +237 -0
- package/claude-code-config/skills/workflow-apex/steps/step-07-tests.md +250 -0
- package/claude-code-config/skills/workflow-apex/steps/step-08-run-tests.md +308 -0
- package/claude-code-config/skills/workflow-apex/steps/step-09-finish.md +193 -0
- package/claude-code-config/skills/workflow-apex/templates/00-context.md +51 -0
- package/claude-code-config/skills/workflow-apex/templates/01-analyze.md +10 -0
- package/claude-code-config/skills/workflow-apex/templates/02-plan.md +10 -0
- package/claude-code-config/skills/workflow-apex/templates/03-execute.md +10 -0
- package/claude-code-config/skills/workflow-apex/templates/04-validate.md +10 -0
- package/claude-code-config/skills/workflow-apex/templates/05-examine.md +10 -0
- package/claude-code-config/skills/workflow-apex/templates/06-resolve.md +10 -0
- package/claude-code-config/skills/workflow-apex/templates/07-tests.md +10 -0
- package/claude-code-config/skills/workflow-apex/templates/08-run-tests.md +10 -0
- package/claude-code-config/skills/workflow-apex/templates/09-finish.md +10 -0
- package/claude-code-config/skills/workflow-apex/templates/README.md +195 -0
- package/claude-code-config/skills/workflow-apex/templates/step-complete.md +7 -0
- package/dist/cli.js +46 -3
- package/package.json +1 -1
- package/claude-code-config/commands/explore.md +0 -90
- package/claude-code-config/commands/git/commit.md +0 -60
- package/claude-code-config/commands/git/fix-pr-comments.md +0 -59
- package/claude-code-config/commands/oneshot.md +0 -57
- package/claude-code-config/skills/create-slash-commands/SKILL.md +0 -1110
- package/claude-code-config/skills/create-slash-commands/references/arguments.md +0 -273
- package/claude-code-config/skills/create-slash-commands/references/patterns.md +0 -947
- package/claude-code-config/skills/create-slash-commands/references/prompt-examples.md +0 -656
- package/claude-code-config/skills/create-slash-commands/references/tool-restrictions.md +0 -389
- /package/claude-code-config/skills/{claude-memory → meta-claude-memory}/references/comprehensive-example.md +0 -0
- /package/claude-code-config/skills/{claude-memory → meta-claude-memory}/references/project-patterns.md +0 -0
- /package/claude-code-config/skills/{claude-memory → meta-claude-memory}/references/prompting-techniques.md +0 -0
- /package/claude-code-config/skills/{claude-memory → meta-claude-memory}/references/section-templates.md +0 -0
- /package/claude-code-config/skills/{create-subagents → meta-subagent-creator}/references/context-management.md +0 -0
- /package/claude-code-config/skills/{create-subagents → meta-subagent-creator}/references/debugging-agents.md +0 -0
- /package/claude-code-config/skills/{create-subagents → meta-subagent-creator}/references/error-handling-and-recovery.md +0 -0
- /package/claude-code-config/skills/{create-subagents → meta-subagent-creator}/references/evaluation-and-testing.md +0 -0
- /package/claude-code-config/skills/{create-subagents → meta-subagent-creator}/references/orchestration-patterns.md +0 -0
- /package/claude-code-config/skills/{create-subagents → meta-subagent-creator}/references/subagents.md +0 -0
- /package/claude-code-config/skills/{create-subagents → meta-subagent-creator}/references/writing-subagent-prompts.md +0 -0
- /package/claude-code-config/skills/{setup-ralph → ralph-loop}/SKILL.md +0 -0
- /package/claude-code-config/skills/{setup-ralph → ralph-loop}/scripts/setup.sh +0 -0
- /package/claude-code-config/skills/{setup-ralph → ralph-loop}/steps/step-01-interactive-prd.md +0 -0
- /package/claude-code-config/skills/{setup-ralph → ralph-loop}/steps/step-02-create-stories.md +0 -0
- /package/claude-code-config/skills/{setup-ralph → ralph-loop}/steps/step-03-finish.md +0 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Skill Initializer - Creates a new skill from template
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bun scripts/init-skill.ts <skill-name> --path <path>
|
|
7
|
+
*
|
|
8
|
+
* Examples:
|
|
9
|
+
* bun scripts/init-skill.ts my-new-skill --path ~/.claude/skills
|
|
10
|
+
* bun scripts/init-skill.ts my-api-helper --path .claude/skills
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync, mkdirSync, writeFileSync, chmodSync } from "fs";
|
|
14
|
+
import { join, resolve } from "path";
|
|
15
|
+
|
|
16
|
+
const SKILL_TEMPLATE = `---
|
|
17
|
+
name: {skill_name}
|
|
18
|
+
description: [TODO: What the skill does and when to use it. Include trigger phrases like "use when..." or specific scenarios.]
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# {skill_title}
|
|
22
|
+
|
|
23
|
+
## Overview
|
|
24
|
+
|
|
25
|
+
[TODO: 1-2 sentences explaining what this skill enables]
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
[TODO: Immediate, actionable guidance - minimal working example]
|
|
30
|
+
|
|
31
|
+
## Workflow
|
|
32
|
+
|
|
33
|
+
[TODO: Step-by-step procedures if applicable]
|
|
34
|
+
|
|
35
|
+
## Resources
|
|
36
|
+
|
|
37
|
+
This skill includes example resource directories:
|
|
38
|
+
|
|
39
|
+
### scripts/
|
|
40
|
+
Executable code (TypeScript/Python/Bash) for automation tasks.
|
|
41
|
+
|
|
42
|
+
### references/
|
|
43
|
+
Documentation loaded into context as needed. Keep SKILL.md lean (<500 lines) and move detailed content here.
|
|
44
|
+
|
|
45
|
+
### assets/
|
|
46
|
+
Files used in output (templates, images, fonts) - not loaded into context.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
**Delete unused directories.** Not every skill needs all three resource types.
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const EXAMPLE_SCRIPT = `#!/usr/bin/env bun
|
|
54
|
+
/**
|
|
55
|
+
* Example helper script for {skill_name}
|
|
56
|
+
*
|
|
57
|
+
* Replace with actual implementation or delete if not needed.
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
function main() {
|
|
61
|
+
console.log("This is an example script for {skill_name}");
|
|
62
|
+
// TODO: Add actual script logic here
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
main();
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
const EXAMPLE_REFERENCE = `# Reference Documentation for {skill_title}
|
|
69
|
+
|
|
70
|
+
This is a placeholder for detailed reference documentation.
|
|
71
|
+
Replace with actual content or delete if not needed.
|
|
72
|
+
|
|
73
|
+
## When Reference Docs Are Useful
|
|
74
|
+
|
|
75
|
+
- Comprehensive API documentation
|
|
76
|
+
- Detailed workflow guides
|
|
77
|
+
- Complex multi-step processes
|
|
78
|
+
- Information too lengthy for main SKILL.md
|
|
79
|
+
- Content only needed for specific use cases
|
|
80
|
+
|
|
81
|
+
## Structure Suggestions
|
|
82
|
+
|
|
83
|
+
### API Reference Example
|
|
84
|
+
- Overview
|
|
85
|
+
- Authentication
|
|
86
|
+
- Endpoints with examples
|
|
87
|
+
- Error codes
|
|
88
|
+
|
|
89
|
+
### Workflow Guide Example
|
|
90
|
+
- Prerequisites
|
|
91
|
+
- Step-by-step instructions
|
|
92
|
+
- Common patterns
|
|
93
|
+
- Troubleshooting
|
|
94
|
+
`;
|
|
95
|
+
|
|
96
|
+
const EXAMPLE_ASSET = `# Example Asset File
|
|
97
|
+
|
|
98
|
+
This placeholder represents where asset files would be stored.
|
|
99
|
+
Replace with actual assets (templates, images, fonts) or delete if not needed.
|
|
100
|
+
|
|
101
|
+
Asset files are NOT loaded into context - they're used in Claude's output.
|
|
102
|
+
|
|
103
|
+
## Common Asset Types
|
|
104
|
+
|
|
105
|
+
- Templates: .pptx, .docx, boilerplate directories
|
|
106
|
+
- Images: .png, .jpg, .svg
|
|
107
|
+
- Fonts: .ttf, .woff2
|
|
108
|
+
- Boilerplate: Project directories, starter files
|
|
109
|
+
- Data: .csv, .json sample files
|
|
110
|
+
`;
|
|
111
|
+
|
|
112
|
+
function titleCase(skillName: string): string {
|
|
113
|
+
return skillName
|
|
114
|
+
.split("-")
|
|
115
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
116
|
+
.join(" ");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function initSkill(skillName: string, basePath: string): string | null {
|
|
120
|
+
const skillDir = resolve(basePath, skillName);
|
|
121
|
+
|
|
122
|
+
if (existsSync(skillDir)) {
|
|
123
|
+
console.log(`❌ Error: Skill directory already exists: ${skillDir}`);
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
mkdirSync(skillDir, { recursive: true });
|
|
129
|
+
console.log(`✅ Created skill directory: ${skillDir}`);
|
|
130
|
+
} catch (e) {
|
|
131
|
+
console.log(`❌ Error creating directory: ${e}`);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const skillTitle = titleCase(skillName);
|
|
136
|
+
|
|
137
|
+
// Create SKILL.md
|
|
138
|
+
const skillContent = SKILL_TEMPLATE.replace(/{skill_name}/g, skillName).replace(
|
|
139
|
+
/{skill_title}/g,
|
|
140
|
+
skillTitle
|
|
141
|
+
);
|
|
142
|
+
const skillMdPath = join(skillDir, "SKILL.md");
|
|
143
|
+
try {
|
|
144
|
+
writeFileSync(skillMdPath, skillContent);
|
|
145
|
+
console.log("✅ Created SKILL.md");
|
|
146
|
+
} catch (e) {
|
|
147
|
+
console.log(`❌ Error creating SKILL.md: ${e}`);
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// Create scripts/
|
|
153
|
+
const scriptsDir = join(skillDir, "scripts");
|
|
154
|
+
mkdirSync(scriptsDir);
|
|
155
|
+
const exampleScript = join(scriptsDir, "example.ts");
|
|
156
|
+
writeFileSync(
|
|
157
|
+
exampleScript,
|
|
158
|
+
EXAMPLE_SCRIPT.replace(/{skill_name}/g, skillName)
|
|
159
|
+
);
|
|
160
|
+
chmodSync(exampleScript, 0o755);
|
|
161
|
+
console.log("✅ Created scripts/example.ts");
|
|
162
|
+
|
|
163
|
+
// Create references/
|
|
164
|
+
const referencesDir = join(skillDir, "references");
|
|
165
|
+
mkdirSync(referencesDir);
|
|
166
|
+
writeFileSync(
|
|
167
|
+
join(referencesDir, "api-reference.md"),
|
|
168
|
+
EXAMPLE_REFERENCE.replace(/{skill_title}/g, skillTitle)
|
|
169
|
+
);
|
|
170
|
+
console.log("✅ Created references/api-reference.md");
|
|
171
|
+
|
|
172
|
+
// Create assets/
|
|
173
|
+
const assetsDir = join(skillDir, "assets");
|
|
174
|
+
mkdirSync(assetsDir);
|
|
175
|
+
writeFileSync(join(assetsDir, "example-asset.txt"), EXAMPLE_ASSET);
|
|
176
|
+
console.log("✅ Created assets/example-asset.txt");
|
|
177
|
+
} catch (e) {
|
|
178
|
+
console.log(`❌ Error creating resource directories: ${e}`);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
console.log(`\n✅ Skill '${skillName}' initialized at ${skillDir}`);
|
|
183
|
+
console.log("\nNext steps:");
|
|
184
|
+
console.log("1. Edit SKILL.md to complete the TODO items");
|
|
185
|
+
console.log("2. Customize or delete example files in scripts/, references/, assets/");
|
|
186
|
+
console.log("3. Run validator: bun scripts/validate.ts " + skillDir);
|
|
187
|
+
|
|
188
|
+
return skillDir;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (import.meta.main) {
|
|
192
|
+
const args = process.argv.slice(2);
|
|
193
|
+
|
|
194
|
+
if (args.length < 3 || args[1] !== "--path") {
|
|
195
|
+
console.log("Usage: bun scripts/init-skill.ts <skill-name> --path <path>");
|
|
196
|
+
console.log("\nSkill name requirements:");
|
|
197
|
+
console.log(" - Hyphen-case (e.g., 'data-analyzer')");
|
|
198
|
+
console.log(" - Lowercase letters, digits, and hyphens only");
|
|
199
|
+
console.log(" - Max 64 characters");
|
|
200
|
+
console.log("\nExamples:");
|
|
201
|
+
console.log(" bun scripts/init-skill.ts my-skill --path ~/.claude/skills");
|
|
202
|
+
console.log(" bun scripts/init-skill.ts api-helper --path .claude/skills");
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const skillName = args[0];
|
|
207
|
+
const basePath = args[2];
|
|
208
|
+
|
|
209
|
+
console.log(`🚀 Initializing skill: ${skillName}`);
|
|
210
|
+
console.log(` Location: ${basePath}\n`);
|
|
211
|
+
|
|
212
|
+
const result = initSkill(skillName, basePath);
|
|
213
|
+
process.exit(result ? 0 : 1);
|
|
214
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Skill Packager - Creates a distributable .skill file
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bun scripts/package-skill.ts <path/to/skill-folder> [output-directory]
|
|
7
|
+
*
|
|
8
|
+
* Examples:
|
|
9
|
+
* bun scripts/package-skill.ts ~/.claude/skills/my-skill
|
|
10
|
+
* bun scripts/package-skill.ts ~/.claude/skills/my-skill ./dist
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync, mkdirSync, readdirSync, statSync, readFileSync } from "fs";
|
|
14
|
+
import { join, resolve, relative, basename } from "path";
|
|
15
|
+
import { validateSkill } from "./validate";
|
|
16
|
+
|
|
17
|
+
async function getAllFiles(dir: string): Promise<string[]> {
|
|
18
|
+
const files: string[] = [];
|
|
19
|
+
|
|
20
|
+
function walk(currentDir: string) {
|
|
21
|
+
const entries = readdirSync(currentDir);
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
const fullPath = join(currentDir, entry);
|
|
24
|
+
const stat = statSync(fullPath);
|
|
25
|
+
if (stat.isDirectory()) {
|
|
26
|
+
walk(fullPath);
|
|
27
|
+
} else {
|
|
28
|
+
files.push(fullPath);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
walk(dir);
|
|
34
|
+
return files;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function packageSkill(
|
|
38
|
+
skillPath: string,
|
|
39
|
+
outputDir?: string
|
|
40
|
+
): Promise<string | null> {
|
|
41
|
+
const resolvedPath = resolve(skillPath);
|
|
42
|
+
|
|
43
|
+
if (!existsSync(resolvedPath)) {
|
|
44
|
+
console.log(`❌ Error: Skill folder not found: ${resolvedPath}`);
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const stat = statSync(resolvedPath);
|
|
49
|
+
if (!stat.isDirectory()) {
|
|
50
|
+
console.log(`❌ Error: Path is not a directory: ${resolvedPath}`);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const skillMdPath = join(resolvedPath, "SKILL.md");
|
|
55
|
+
if (!existsSync(skillMdPath)) {
|
|
56
|
+
console.log(`❌ Error: SKILL.md not found in ${resolvedPath}`);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Validate skill
|
|
61
|
+
console.log("🔍 Validating skill...");
|
|
62
|
+
const { valid, message } = validateSkill(resolvedPath);
|
|
63
|
+
if (!valid) {
|
|
64
|
+
console.log(`❌ Validation failed: ${message}`);
|
|
65
|
+
console.log(" Please fix the validation errors before packaging.");
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
console.log(`✅ ${message}\n`);
|
|
69
|
+
|
|
70
|
+
// Determine output location
|
|
71
|
+
const skillName = basename(resolvedPath);
|
|
72
|
+
const outputPath = outputDir ? resolve(outputDir) : process.cwd();
|
|
73
|
+
|
|
74
|
+
if (outputDir && !existsSync(outputPath)) {
|
|
75
|
+
mkdirSync(outputPath, { recursive: true });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const skillFilename = join(outputPath, `${skillName}.skill`);
|
|
79
|
+
|
|
80
|
+
// Create zip file using Bun's built-in zip
|
|
81
|
+
try {
|
|
82
|
+
const files = await getAllFiles(resolvedPath);
|
|
83
|
+
const zipEntries: { path: string; data: Uint8Array }[] = [];
|
|
84
|
+
|
|
85
|
+
for (const filePath of files) {
|
|
86
|
+
const relativePath = join(skillName, relative(resolvedPath, filePath));
|
|
87
|
+
const data = readFileSync(filePath);
|
|
88
|
+
zipEntries.push({ path: relativePath, data });
|
|
89
|
+
console.log(` Added: ${relativePath}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Use Bun's native zip writer
|
|
93
|
+
const zipFile = Bun.file(skillFilename);
|
|
94
|
+
const writer = zipFile.writer();
|
|
95
|
+
|
|
96
|
+
// Create a simple zip manually or use a library
|
|
97
|
+
// For now, use the 'archiver' pattern with Bun.spawn
|
|
98
|
+
const { spawn } = await import("child_process");
|
|
99
|
+
|
|
100
|
+
// Create zip using system zip command (cross-platform approach)
|
|
101
|
+
const parentDir = resolve(resolvedPath, "..");
|
|
102
|
+
const result = Bun.spawnSync(["zip", "-r", skillFilename, skillName], {
|
|
103
|
+
cwd: parentDir,
|
|
104
|
+
stdout: "pipe",
|
|
105
|
+
stderr: "pipe",
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (result.exitCode !== 0) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`zip command failed: ${Buffer.from(result.stderr).toString()}`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log(`\n✅ Successfully packaged skill to: ${skillFilename}`);
|
|
115
|
+
return skillFilename;
|
|
116
|
+
} catch (e) {
|
|
117
|
+
console.log(`❌ Error creating .skill file: ${e}`);
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (import.meta.main) {
|
|
123
|
+
const args = process.argv.slice(2);
|
|
124
|
+
|
|
125
|
+
if (args.length < 1) {
|
|
126
|
+
console.log(
|
|
127
|
+
"Usage: bun scripts/package-skill.ts <path/to/skill-folder> [output-directory]"
|
|
128
|
+
);
|
|
129
|
+
console.log("\nExamples:");
|
|
130
|
+
console.log(" bun scripts/package-skill.ts ~/.claude/skills/my-skill");
|
|
131
|
+
console.log(" bun scripts/package-skill.ts ~/.claude/skills/my-skill ./dist");
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const skillPath = args[0];
|
|
136
|
+
const outputDir = args[1];
|
|
137
|
+
|
|
138
|
+
console.log(`📦 Packaging skill: ${skillPath}`);
|
|
139
|
+
if (outputDir) {
|
|
140
|
+
console.log(` Output directory: ${outputDir}`);
|
|
141
|
+
}
|
|
142
|
+
console.log();
|
|
143
|
+
|
|
144
|
+
const result = await packageSkill(skillPath, outputDir);
|
|
145
|
+
process.exit(result ? 0 : 1);
|
|
146
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Skill Validator - Validates skill structure and frontmatter
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bun scripts/validate.ts <skill-directory>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, readFileSync } from "fs";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
import { parse as parseYaml } from "yaml";
|
|
12
|
+
|
|
13
|
+
const ALLOWED_PROPERTIES = new Set([
|
|
14
|
+
"name",
|
|
15
|
+
"description",
|
|
16
|
+
"license",
|
|
17
|
+
"metadata",
|
|
18
|
+
"allowed-tools",
|
|
19
|
+
"disable-model-invocation",
|
|
20
|
+
"user-invocable",
|
|
21
|
+
"argument-hint",
|
|
22
|
+
"model",
|
|
23
|
+
"context",
|
|
24
|
+
"agent",
|
|
25
|
+
"hooks",
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
interface ValidationResult {
|
|
29
|
+
valid: boolean;
|
|
30
|
+
message: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function validateSkill(skillPath: string): ValidationResult {
|
|
34
|
+
const skillMdPath = join(skillPath, "SKILL.md");
|
|
35
|
+
|
|
36
|
+
if (!existsSync(skillMdPath)) {
|
|
37
|
+
return { valid: false, message: "SKILL.md not found" };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const content = readFileSync(skillMdPath, "utf-8");
|
|
41
|
+
|
|
42
|
+
if (!content.startsWith("---")) {
|
|
43
|
+
return { valid: false, message: "No YAML frontmatter found" };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
47
|
+
if (!frontmatterMatch) {
|
|
48
|
+
return { valid: false, message: "Invalid frontmatter format" };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let frontmatter: Record<string, unknown>;
|
|
52
|
+
try {
|
|
53
|
+
frontmatter = parseYaml(frontmatterMatch[1]);
|
|
54
|
+
if (typeof frontmatter !== "object" || frontmatter === null) {
|
|
55
|
+
return { valid: false, message: "Frontmatter must be a YAML dictionary" };
|
|
56
|
+
}
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return { valid: false, message: `Invalid YAML in frontmatter: ${e}` };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const unexpectedKeys = Object.keys(frontmatter).filter(
|
|
62
|
+
(key) => !ALLOWED_PROPERTIES.has(key)
|
|
63
|
+
);
|
|
64
|
+
if (unexpectedKeys.length > 0) {
|
|
65
|
+
return {
|
|
66
|
+
valid: false,
|
|
67
|
+
message: `Unexpected key(s) in frontmatter: ${unexpectedKeys.join(", ")}. Allowed: ${[...ALLOWED_PROPERTIES].sort().join(", ")}`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const name = frontmatter.name;
|
|
72
|
+
if (name !== undefined) {
|
|
73
|
+
if (typeof name !== "string") {
|
|
74
|
+
return { valid: false, message: `Name must be a string` };
|
|
75
|
+
}
|
|
76
|
+
const trimmedName = name.trim();
|
|
77
|
+
if (trimmedName) {
|
|
78
|
+
if (!/^[a-z0-9-]+$/.test(trimmedName)) {
|
|
79
|
+
return {
|
|
80
|
+
valid: false,
|
|
81
|
+
message: `Name '${trimmedName}' should be hyphen-case (lowercase letters, digits, and hyphens only)`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (
|
|
85
|
+
trimmedName.startsWith("-") ||
|
|
86
|
+
trimmedName.endsWith("-") ||
|
|
87
|
+
trimmedName.includes("--")
|
|
88
|
+
) {
|
|
89
|
+
return {
|
|
90
|
+
valid: false,
|
|
91
|
+
message: `Name '${trimmedName}' cannot start/end with hyphen or contain consecutive hyphens`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (trimmedName.length > 64) {
|
|
95
|
+
return {
|
|
96
|
+
valid: false,
|
|
97
|
+
message: `Name is too long (${trimmedName.length} characters). Maximum is 64 characters.`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const description = frontmatter.description;
|
|
104
|
+
if (description !== undefined) {
|
|
105
|
+
if (typeof description !== "string") {
|
|
106
|
+
return { valid: false, message: `Description must be a string` };
|
|
107
|
+
}
|
|
108
|
+
const trimmedDesc = description.trim();
|
|
109
|
+
if (trimmedDesc) {
|
|
110
|
+
if (trimmedDesc.includes("<") || trimmedDesc.includes(">")) {
|
|
111
|
+
return {
|
|
112
|
+
valid: false,
|
|
113
|
+
message: "Description cannot contain angle brackets (< or >)",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (trimmedDesc.length > 1024) {
|
|
117
|
+
return {
|
|
118
|
+
valid: false,
|
|
119
|
+
message: `Description is too long (${trimmedDesc.length} characters). Maximum is 1024 characters.`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { valid: true, message: "Skill is valid!" };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (import.meta.main) {
|
|
129
|
+
const args = process.argv.slice(2);
|
|
130
|
+
if (args.length !== 1) {
|
|
131
|
+
console.log("Usage: bun scripts/validate.ts <skill_directory>");
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const { valid, message } = validateSkill(args[0]);
|
|
136
|
+
console.log(message);
|
|
137
|
+
process.exit(valid ? 0 : 1);
|
|
138
|
+
}
|