Initial commit
This commit is contained in:
301
skills/claude-code-knowledge/scripts/skill-creator/init_skill.ts
Executable file
301
skills/claude-code-knowledge/scripts/skill-creator/init_skill.ts
Executable file
@@ -0,0 +1,301 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Skill Initializer - Creates a new skill from template
|
||||
*
|
||||
* Usage:
|
||||
* init_skill.ts <skill-name> --path <path>
|
||||
*
|
||||
* Examples:
|
||||
* init_skill.ts my-new-skill --path skills/public
|
||||
* init_skill.ts my-api-helper --path skills/private
|
||||
* init_skill.ts custom-skill --path /custom/location
|
||||
*/
|
||||
|
||||
import { existsSync } from 'node:fs';
|
||||
import { chmod, mkdir, writeFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
|
||||
const SKILL_TEMPLATE = (skillName: string, skillTitle: string) => `---
|
||||
name: ${skillName}
|
||||
description: [TODO: Complete and informative explanation of what the skill does and when to use it. Include WHEN to use this skill - specific scenarios, file types, or tasks that trigger it.]
|
||||
---
|
||||
|
||||
# ${skillTitle}
|
||||
|
||||
## Overview
|
||||
|
||||
[TODO: 1-2 sentences explaining what this skill enables]
|
||||
|
||||
## Structuring This Skill
|
||||
|
||||
[TODO: Choose the structure that best fits this skill's purpose. Common patterns:
|
||||
|
||||
**1. Workflow-Based** (best for sequential processes)
|
||||
- Works well when there are clear step-by-step procedures
|
||||
- Example: DOCX skill with "Workflow Decision Tree" → "Reading" → "Creating" → "Editing"
|
||||
- Structure: ## Overview → ## Workflow Decision Tree → ## Step 1 → ## Step 2...
|
||||
|
||||
**2. Task-Based** (best for tool collections)
|
||||
- Works well when the skill offers different operations/capabilities
|
||||
- Example: PDF skill with "Quick Start" → "Merge PDFs" → "Split PDFs" → "Extract Text"
|
||||
- Structure: ## Overview → ## Quick Start → ## Task Category 1 → ## Task Category 2...
|
||||
|
||||
**3. Reference/Guidelines** (best for standards or specifications)
|
||||
- Works well for brand guidelines, coding standards, or requirements
|
||||
- Example: Brand styling with "Brand Guidelines" → "Colors" → "Typography" → "Features"
|
||||
- Structure: ## Overview → ## Guidelines → ## Specifications → ## Usage...
|
||||
|
||||
**4. Capabilities-Based** (best for integrated systems)
|
||||
- Works well when the skill provides multiple interrelated features
|
||||
- Example: Product Management with "Core Capabilities" → numbered capability list
|
||||
- Structure: ## Overview → ## Core Capabilities → ### 1. Feature → ### 2. Feature...
|
||||
|
||||
Patterns can be mixed and matched as needed. Most skills combine patterns (e.g., start with task-based, add workflow for complex operations).
|
||||
|
||||
Delete this entire "Structuring This Skill" section when done - it's just guidance.]
|
||||
|
||||
## [TODO: Replace with the first main section based on chosen structure]
|
||||
|
||||
[TODO: Add content here. See examples in existing skills:
|
||||
- Code samples for technical skills
|
||||
- Decision trees for complex workflows
|
||||
- Concrete examples with realistic user requests
|
||||
- References to scripts/templates/references as needed]
|
||||
|
||||
## Resources
|
||||
|
||||
This skill includes example resource directories that demonstrate how to organize different types of bundled resources:
|
||||
|
||||
### scripts/
|
||||
Executable code (Python/Bash/etc.) that can be run directly to perform specific operations.
|
||||
|
||||
**Examples from other skills:**
|
||||
- PDF skill: \`fill_fillable_fields.py\`, \`extract_form_field_info.py\` - utilities for PDF manipulation
|
||||
- DOCX skill: \`document.py\`, \`utilities.py\` - Python modules for document processing
|
||||
|
||||
**Appropriate for:** Python scripts, shell scripts, or any executable code that performs automation, data processing, or specific operations.
|
||||
|
||||
**Note:** Scripts may be executed without loading into context, but can still be read by Claude for patching or environment adjustments.
|
||||
|
||||
### references/
|
||||
Documentation and reference material intended to be loaded into context to inform Claude's process and thinking.
|
||||
|
||||
**Examples from other skills:**
|
||||
- Product management: \`communication.md\`, \`context_building.md\` - detailed workflow guides
|
||||
- BigQuery: API reference documentation and query examples
|
||||
- Finance: Schema documentation, company policies
|
||||
|
||||
**Appropriate for:** In-depth documentation, API references, database schemas, comprehensive guides, or any detailed information that Claude should reference while working.
|
||||
|
||||
### assets/
|
||||
Files not intended to be loaded into context, but rather used within the output Claude produces.
|
||||
|
||||
**Examples from other skills:**
|
||||
- Brand styling: PowerPoint template files (.pptx), logo files
|
||||
- Frontend builder: HTML/React boilerplate project directories
|
||||
- Typography: Font files (.ttf, .woff2)
|
||||
|
||||
**Appropriate for:** Templates, boilerplate code, document templates, images, icons, fonts, or any files meant to be copied or used in the final output.
|
||||
|
||||
---
|
||||
|
||||
**Any unneeded directories can be deleted.** Not every skill requires all three types of resources.
|
||||
`;
|
||||
|
||||
const EXAMPLE_SCRIPT = (skillName: string) => `#!/usr/bin/env python3
|
||||
"""
|
||||
Example helper script for ${skillName}
|
||||
|
||||
This is a placeholder script that can be executed directly.
|
||||
Replace with actual implementation or delete if not needed.
|
||||
|
||||
Example real scripts from other skills:
|
||||
- pdf/scripts/fill_fillable_fields.py - Fills PDF form fields
|
||||
- pdf/scripts/convert_pdf_to_images.py - Converts PDF pages to images
|
||||
"""
|
||||
|
||||
def main():
|
||||
print("This is an example script for ${skillName}")
|
||||
# TODO: Add actual script logic here
|
||||
# This could be data processing, file conversion, API calls, etc.
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
`;
|
||||
|
||||
const EXAMPLE_REFERENCE = (skillTitle: string) => `# Reference Documentation for ${skillTitle}
|
||||
|
||||
This is a placeholder for detailed reference documentation.
|
||||
Replace with actual reference content or delete if not needed.
|
||||
|
||||
Example real reference docs from other skills:
|
||||
- product-management/references/communication.md - Comprehensive guide for status updates
|
||||
- product-management/references/context_building.md - Deep-dive on gathering context
|
||||
- bigquery/references/ - API references and query examples
|
||||
|
||||
## When Reference Docs Are Useful
|
||||
|
||||
Reference docs are ideal for:
|
||||
- Comprehensive API documentation
|
||||
- Detailed workflow guides
|
||||
- Complex multi-step processes
|
||||
- Information too lengthy for main SKILL.md
|
||||
- Content that's only needed for specific use cases
|
||||
|
||||
## Structure Suggestions
|
||||
|
||||
### API Reference Example
|
||||
- Overview
|
||||
- Authentication
|
||||
- Endpoints with examples
|
||||
- Error codes
|
||||
- Rate limits
|
||||
|
||||
### Workflow Guide Example
|
||||
- Prerequisites
|
||||
- Step-by-step instructions
|
||||
- Common patterns
|
||||
- Troubleshooting
|
||||
- Best practices
|
||||
`;
|
||||
|
||||
const EXAMPLE_ASSET = `# Example Asset File
|
||||
|
||||
This placeholder represents where asset files would be stored.
|
||||
Replace with actual asset files (templates, images, fonts, etc.) or delete if not needed.
|
||||
|
||||
Asset files are NOT intended to be loaded into context, but rather used within
|
||||
the output Claude produces.
|
||||
|
||||
Example asset files from other skills:
|
||||
- Brand guidelines: logo.png, slides_template.pptx
|
||||
- Frontend builder: hello-world/ directory with HTML/React boilerplate
|
||||
- Typography: custom-font.ttf, font-family.woff2
|
||||
- Data: sample_data.csv, test_dataset.json
|
||||
|
||||
## Common Asset Types
|
||||
|
||||
- Templates: .pptx, .docx, boilerplate directories
|
||||
- Images: .png, .jpg, .svg, .gif
|
||||
- Fonts: .ttf, .otf, .woff, .woff2
|
||||
- Boilerplate code: Project directories, starter files
|
||||
- Icons: .ico, .svg
|
||||
- Data files: .csv, .json, .xml, .yaml
|
||||
|
||||
Note: This is a text placeholder. Actual assets can be any file type.
|
||||
`;
|
||||
|
||||
function titleCaseSkillName(skillName: string): string {
|
||||
return skillName
|
||||
.split('-')
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
async function initSkill(skillName: string, path: string): Promise<string | null> {
|
||||
// Determine skill directory path
|
||||
const skillDir = join(path, skillName);
|
||||
|
||||
// Check if directory already exists
|
||||
if (existsSync(skillDir)) {
|
||||
console.log(`❌ Error: Skill directory already exists: ${skillDir}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create skill directory
|
||||
try {
|
||||
await mkdir(skillDir, { recursive: true });
|
||||
console.log(`✅ Created skill directory: ${skillDir}`);
|
||||
} catch (e) {
|
||||
console.log(`❌ Error creating directory: ${e}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create SKILL.md from template
|
||||
const skillTitle = titleCaseSkillName(skillName);
|
||||
const skillContent = SKILL_TEMPLATE(skillName, skillTitle);
|
||||
|
||||
const skillMdPath = join(skillDir, 'SKILL.md');
|
||||
try {
|
||||
await writeFile(skillMdPath, skillContent);
|
||||
console.log('✅ Created SKILL.md');
|
||||
} catch (e) {
|
||||
console.log(`❌ Error creating SKILL.md: ${e}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create resource directories with example files
|
||||
try {
|
||||
// Create scripts/ directory with example script
|
||||
const scriptsDir = join(skillDir, 'scripts');
|
||||
await mkdir(scriptsDir, { recursive: true });
|
||||
|
||||
const exampleScript = join(scriptsDir, 'example.py');
|
||||
await writeFile(exampleScript, EXAMPLE_SCRIPT(skillName));
|
||||
await chmod(exampleScript, 0o755);
|
||||
console.log('✅ Created scripts/example.py');
|
||||
|
||||
// Create references/ directory with example reference doc
|
||||
const referencesDir = join(skillDir, 'references');
|
||||
await mkdir(referencesDir, { recursive: true });
|
||||
|
||||
const exampleReference = join(referencesDir, 'api_reference.md');
|
||||
await writeFile(exampleReference, EXAMPLE_REFERENCE(skillTitle));
|
||||
console.log('✅ Created references/api_reference.md');
|
||||
|
||||
// Create assets/ directory with example asset placeholder
|
||||
const assetsDir = join(skillDir, 'assets');
|
||||
await mkdir(assetsDir, { recursive: true });
|
||||
|
||||
const exampleAsset = join(assetsDir, 'example_asset.txt');
|
||||
await writeFile(exampleAsset, EXAMPLE_ASSET);
|
||||
console.log('✅ Created assets/example_asset.txt');
|
||||
} catch (e) {
|
||||
console.log(`❌ Error creating resource directories: ${e}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Print next steps
|
||||
console.log(`\n✅ Skill '${skillName}' initialized successfully at ${skillDir}`);
|
||||
console.log('\nNext steps:');
|
||||
console.log('1. Edit SKILL.md to complete the TODO items and update the description');
|
||||
console.log('2. Customize or delete the example files in scripts/, references/, and assets/');
|
||||
console.log('3. Run the validator when ready to check the skill structure');
|
||||
|
||||
return skillDir;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 3 || args[1] !== '--path') {
|
||||
console.log('Usage: init_skill.ts <skill-name> --path <path>');
|
||||
console.log('\nSkill name requirements:');
|
||||
console.log(" - Hyphen-case identifier (e.g., 'data-analyzer')");
|
||||
console.log(' - Lowercase letters, digits, and hyphens only');
|
||||
console.log(' - Max 40 characters');
|
||||
console.log(' - Must match directory name exactly');
|
||||
console.log('\nExamples:');
|
||||
console.log(' init_skill.ts my-new-skill --path skills/public');
|
||||
console.log(' init_skill.ts my-api-helper --path skills/private');
|
||||
console.log(' init_skill.ts custom-skill --path /custom/location');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const skillName = args[0];
|
||||
const path = args[2];
|
||||
|
||||
console.log(`🚀 Initializing skill: ${skillName}`);
|
||||
console.log(` Location: ${path}`);
|
||||
console.log();
|
||||
|
||||
const result = await initSkill(skillName, path);
|
||||
|
||||
if (result) {
|
||||
process.exit(0);
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
148
skills/claude-code-knowledge/scripts/skill-creator/package_skill.ts
Executable file
148
skills/claude-code-knowledge/scripts/skill-creator/package_skill.ts
Executable file
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Skill Packager - Creates a distributable zip file of a skill folder
|
||||
*
|
||||
* Usage:
|
||||
* bun package_skill.ts <path/to/skill-folder> [output-directory]
|
||||
*
|
||||
* Example:
|
||||
* bun package_skill.ts skills/public/my-skill
|
||||
* bun package_skill.ts skills/public/my-skill ./dist
|
||||
*/
|
||||
|
||||
import { existsSync, readdirSync, statSync } from 'node:fs';
|
||||
import { mkdir } from 'node:fs/promises';
|
||||
import { basename, join, relative, resolve } from 'node:path';
|
||||
import { validateSkill } from './quick_validate';
|
||||
|
||||
async function packageSkill(skillPath: string, outputDir?: string): Promise<string | null> {
|
||||
const skillPathResolved = resolve(skillPath);
|
||||
|
||||
// Validate skill folder exists
|
||||
if (!existsSync(skillPathResolved)) {
|
||||
console.log(`❌ Error: Skill folder not found: ${skillPathResolved}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!statSync(skillPathResolved).isDirectory()) {
|
||||
console.log(`❌ Error: Path is not a directory: ${skillPathResolved}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Validate SKILL.md exists
|
||||
const skillMd = join(skillPathResolved, 'SKILL.md');
|
||||
|
||||
if (!existsSync(skillMd)) {
|
||||
console.log(`❌ Error: SKILL.md not found in ${skillPathResolved}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Run validation before packaging
|
||||
console.log('🔍 Validating skill...');
|
||||
const { valid, message } = await validateSkill(skillPathResolved);
|
||||
|
||||
if (!valid) {
|
||||
console.log(`❌ Validation failed: ${message}`);
|
||||
console.log(' Please fix the validation errors before packaging.');
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log(`✅ ${message}\n`);
|
||||
|
||||
// Determine output location
|
||||
const skillName = basename(skillPathResolved);
|
||||
let outputPath: string;
|
||||
|
||||
if (outputDir) {
|
||||
outputPath = resolve(outputDir);
|
||||
if (!existsSync(outputPath)) {
|
||||
await mkdir(outputPath, { recursive: true });
|
||||
}
|
||||
} else {
|
||||
outputPath = process.cwd();
|
||||
}
|
||||
|
||||
const zipFilename = join(outputPath, `${skillName}.zip`);
|
||||
|
||||
// Create the zip file using system zip command
|
||||
try {
|
||||
// Remove existing zip file if it exists
|
||||
if (existsSync(zipFilename)) {
|
||||
const { unlinkSync } = require('node:fs');
|
||||
unlinkSync(zipFilename);
|
||||
}
|
||||
|
||||
// Collect all files for display
|
||||
const files: { path: string; arcname: string }[] = [];
|
||||
|
||||
function walkDirectory(dir: string, baseDir: string) {
|
||||
const entries = readdirSync(dir);
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = join(dir, entry);
|
||||
const stat = statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
walkDirectory(fullPath, baseDir);
|
||||
} else if (stat.isFile()) {
|
||||
const arcname = relative(baseDir, fullPath);
|
||||
files.push({ path: fullPath, arcname });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const parentDir = resolve(skillPathResolved, '..');
|
||||
walkDirectory(skillPathResolved, parentDir);
|
||||
|
||||
// Create zip using system command
|
||||
const { $ } = await import('bun');
|
||||
const cwd = parentDir;
|
||||
const skillDirName = basename(skillPathResolved);
|
||||
|
||||
await $`cd ${cwd} && zip -r ${zipFilename} ${skillDirName}`.quiet();
|
||||
|
||||
// List files that were added
|
||||
for (const { arcname } of files) {
|
||||
console.log(` Added: ${arcname}`);
|
||||
}
|
||||
|
||||
console.log(`\n✅ Successfully packaged skill to: ${zipFilename}`);
|
||||
return zipFilename;
|
||||
} catch (e) {
|
||||
console.log(`❌ Error creating zip file: ${e}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 1) {
|
||||
console.log('Usage: bun package_skill.ts <path/to/skill-folder> [output-directory]');
|
||||
console.log('\nExample:');
|
||||
console.log(' bun package_skill.ts skills/public/my-skill');
|
||||
console.log(' bun package_skill.ts skills/public/my-skill ./dist');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const skillPath = args[0];
|
||||
const outputDir = args[1];
|
||||
|
||||
console.log(`📦 Packaging skill: ${skillPath}`);
|
||||
|
||||
if (outputDir) {
|
||||
console.log(` Output directory: ${outputDir}`);
|
||||
}
|
||||
|
||||
console.log();
|
||||
|
||||
const result = await packageSkill(skillPath, outputDir);
|
||||
|
||||
if (result) {
|
||||
process.exit(0);
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
104
skills/claude-code-knowledge/scripts/skill-creator/quick_validate.ts
Executable file
104
skills/claude-code-knowledge/scripts/skill-creator/quick_validate.ts
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Quick validation script for skills - minimal version
|
||||
*/
|
||||
|
||||
import { existsSync } from 'node:fs';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
|
||||
interface ValidationResult {
|
||||
valid: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
async function validateSkill(skillPath: string): Promise<ValidationResult> {
|
||||
// Check SKILL.md exists
|
||||
const skillMd = join(skillPath, 'SKILL.md');
|
||||
|
||||
if (!existsSync(skillMd)) {
|
||||
return { valid: false, message: 'SKILL.md not found' };
|
||||
}
|
||||
|
||||
// Read and validate frontmatter
|
||||
const content = await readFile(skillMd, 'utf-8');
|
||||
|
||||
if (!content.startsWith('---')) {
|
||||
return { valid: false, message: 'No YAML frontmatter found' };
|
||||
}
|
||||
|
||||
// Extract frontmatter
|
||||
const frontmatterMatch = content.match(/^---\n(.*?)\n---/s);
|
||||
|
||||
if (!frontmatterMatch) {
|
||||
return { valid: false, message: 'Invalid frontmatter format' };
|
||||
}
|
||||
|
||||
const frontmatter = frontmatterMatch[1];
|
||||
|
||||
// Check required fields
|
||||
if (!frontmatter.includes('name:')) {
|
||||
return { valid: false, message: "Missing 'name' in frontmatter" };
|
||||
}
|
||||
|
||||
if (!frontmatter.includes('description:')) {
|
||||
return { valid: false, message: "Missing 'description' in frontmatter" };
|
||||
}
|
||||
|
||||
// Extract name for validation
|
||||
const nameMatch = frontmatter.match(/name:\s*(.+)/);
|
||||
|
||||
if (nameMatch) {
|
||||
const name = nameMatch[1].trim();
|
||||
|
||||
// Check naming convention (hyphen-case: lowercase with hyphens)
|
||||
if (!/^[a-z0-9-]+$/.test(name)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: `Name '${name}' should be hyphen-case (lowercase letters, digits, and hyphens only)`,
|
||||
};
|
||||
}
|
||||
|
||||
if (name.startsWith('-') || name.endsWith('-') || name.includes('--')) {
|
||||
return {
|
||||
valid: false,
|
||||
message: `Name '${name}' cannot start/end with hyphen or contain consecutive hyphens`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Extract and validate description
|
||||
const descMatch = frontmatter.match(/description:\s*(.+)/);
|
||||
|
||||
if (descMatch) {
|
||||
const description = descMatch[1].trim();
|
||||
|
||||
// Check for angle brackets
|
||||
if (description.includes('<') || description.includes('>')) {
|
||||
return { valid: false, message: 'Description cannot contain angle brackets (< or >)' };
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: true, message: 'Skill is valid!' };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length !== 1) {
|
||||
console.log('Usage: bun quick_validate.ts <skill_directory>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { valid, message } = await validateSkill(args[0]);
|
||||
console.log(message);
|
||||
process.exit(valid ? 0 : 1);
|
||||
}
|
||||
|
||||
// Export for use in other scripts
|
||||
export { validateSkill };
|
||||
|
||||
// Only run main if this is the main module
|
||||
if (import.meta.main) {
|
||||
main();
|
||||
}
|
||||
Reference in New Issue
Block a user