init_skill.py
1 #!/usr/bin/env python3 2 """ 3 Skill Initializer - Creates a new skill from template 4 5 Usage: 6 init_skill.py <skill-name> --path <path> [--resources scripts,references,assets] [--examples] 7 8 Examples: 9 init_skill.py my-new-skill --path skills/public 10 init_skill.py my-new-skill --path skills/public --resources scripts,references 11 init_skill.py my-api-helper --path skills/private --resources scripts --examples 12 init_skill.py custom-skill --path /custom/location 13 """ 14 15 import argparse 16 import re 17 import sys 18 from pathlib import Path 19 20 MAX_SKILL_NAME_LENGTH = 64 21 ALLOWED_RESOURCES = {"scripts", "references", "assets"} 22 23 SKILL_TEMPLATE = """--- 24 name: {skill_name} 25 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.] 26 --- 27 28 # {skill_title} 29 30 ## Overview 31 32 [TODO: 1-2 sentences explaining what this skill enables] 33 34 ## Structuring This Skill 35 36 [TODO: Choose the structure that best fits this skill's purpose. Common patterns: 37 38 **1. Workflow-Based** (best for sequential processes) 39 - Works well when there are clear step-by-step procedures 40 - Example: DOCX skill with "Workflow Decision Tree" -> "Reading" -> "Creating" -> "Editing" 41 - Structure: ## Overview -> ## Workflow Decision Tree -> ## Step 1 -> ## Step 2... 42 43 **2. Task-Based** (best for tool collections) 44 - Works well when the skill offers different operations/capabilities 45 - Example: PDF skill with "Quick Start" -> "Merge PDFs" -> "Split PDFs" -> "Extract Text" 46 - Structure: ## Overview -> ## Quick Start -> ## Task Category 1 -> ## Task Category 2... 47 48 **3. Reference/Guidelines** (best for standards or specifications) 49 - Works well for brand guidelines, coding standards, or requirements 50 - Example: Brand styling with "Brand Guidelines" -> "Colors" -> "Typography" -> "Features" 51 - Structure: ## Overview -> ## Guidelines -> ## Specifications -> ## Usage... 52 53 **4. Capabilities-Based** (best for integrated systems) 54 - Works well when the skill provides multiple interrelated features 55 - Example: Product Management with "Core Capabilities" -> numbered capability list 56 - Structure: ## Overview -> ## Core Capabilities -> ### 1. Feature -> ### 2. Feature... 57 58 Patterns can be mixed and matched as needed. Most skills combine patterns (e.g., start with task-based, add workflow for complex operations). 59 60 Delete this entire "Structuring This Skill" section when done - it's just guidance.] 61 62 ## [TODO: Replace with the first main section based on chosen structure] 63 64 [TODO: Add content here. See examples in existing skills: 65 - Code samples for technical skills 66 - Decision trees for complex workflows 67 - Concrete examples with realistic user requests 68 - References to scripts/templates/references as needed] 69 70 ## Resources (optional) 71 72 Create only the resource directories this skill actually needs. Delete this section if no resources are required. 73 74 ### scripts/ 75 Executable code (Python/Bash/etc.) that can be run directly to perform specific operations. 76 77 **Examples from other skills:** 78 - PDF skill: `fill_fillable_fields.py`, `extract_form_field_info.py` - utilities for PDF manipulation 79 - DOCX skill: `document.py`, `utilities.py` - Python modules for document processing 80 81 **Appropriate for:** Python scripts, shell scripts, or any executable code that performs automation, data processing, or specific operations. 82 83 **Note:** Scripts may be executed without loading into context, but can still be read by Codex for patching or environment adjustments. 84 85 ### references/ 86 Documentation and reference material intended to be loaded into context to inform Codex's process and thinking. 87 88 **Examples from other skills:** 89 - Product management: `communication.md`, `context_building.md` - detailed workflow guides 90 - BigQuery: API reference documentation and query examples 91 - Finance: Schema documentation, company policies 92 93 **Appropriate for:** In-depth documentation, API references, database schemas, comprehensive guides, or any detailed information that Codex should reference while working. 94 95 ### assets/ 96 Files not intended to be loaded into context, but rather used within the output Codex produces. 97 98 **Examples from other skills:** 99 - Brand styling: PowerPoint template files (.pptx), logo files 100 - Frontend builder: HTML/React boilerplate project directories 101 - Typography: Font files (.ttf, .woff2) 102 103 **Appropriate for:** Templates, boilerplate code, document templates, images, icons, fonts, or any files meant to be copied or used in the final output. 104 105 --- 106 107 **Not every skill requires all three types of resources.** 108 """ 109 110 EXAMPLE_SCRIPT = '''#!/usr/bin/env python3 111 """ 112 Example helper script for {skill_name} 113 114 This is a placeholder script that can be executed directly. 115 Replace with actual implementation or delete if not needed. 116 117 Example real scripts from other skills: 118 - pdf/scripts/fill_fillable_fields.py - Fills PDF form fields 119 - pdf/scripts/convert_pdf_to_images.py - Converts PDF pages to images 120 """ 121 122 def main(): 123 print("This is an example script for {skill_name}") 124 # TODO: Add actual script logic here 125 # This could be data processing, file conversion, API calls, etc. 126 127 if __name__ == "__main__": 128 main() 129 ''' 130 131 EXAMPLE_REFERENCE = """# Reference Documentation for {skill_title} 132 133 This is a placeholder for detailed reference documentation. 134 Replace with actual reference content or delete if not needed. 135 136 Example real reference docs from other skills: 137 - product-management/references/communication.md - Comprehensive guide for status updates 138 - product-management/references/context_building.md - Deep-dive on gathering context 139 - bigquery/references/ - API references and query examples 140 141 ## When Reference Docs Are Useful 142 143 Reference docs are ideal for: 144 - Comprehensive API documentation 145 - Detailed workflow guides 146 - Complex multi-step processes 147 - Information too lengthy for main SKILL.md 148 - Content that's only needed for specific use cases 149 150 ## Structure Suggestions 151 152 ### API Reference Example 153 - Overview 154 - Authentication 155 - Endpoints with examples 156 - Error codes 157 - Rate limits 158 159 ### Workflow Guide Example 160 - Prerequisites 161 - Step-by-step instructions 162 - Common patterns 163 - Troubleshooting 164 - Best practices 165 """ 166 167 EXAMPLE_ASSET = """# Example Asset File 168 169 This placeholder represents where asset files would be stored. 170 Replace with actual asset files (templates, images, fonts, etc.) or delete if not needed. 171 172 Asset files are NOT intended to be loaded into context, but rather used within 173 the output Codex produces. 174 175 Example asset files from other skills: 176 - Brand guidelines: logo.png, slides_template.pptx 177 - Frontend builder: hello-world/ directory with HTML/React boilerplate 178 - Typography: custom-font.ttf, font-family.woff2 179 - Data: sample_data.csv, test_dataset.json 180 181 ## Common Asset Types 182 183 - Templates: .pptx, .docx, boilerplate directories 184 - Images: .png, .jpg, .svg, .gif 185 - Fonts: .ttf, .otf, .woff, .woff2 186 - Boilerplate code: Project directories, starter files 187 - Icons: .ico, .svg 188 - Data files: .csv, .json, .xml, .yaml 189 190 Note: This is a text placeholder. Actual assets can be any file type. 191 """ 192 193 194 def normalize_skill_name(skill_name): 195 """Normalize a skill name to lowercase hyphen-case.""" 196 normalized = skill_name.strip().lower() 197 normalized = re.sub(r"[^a-z0-9]+", "-", normalized) 198 normalized = normalized.strip("-") 199 normalized = re.sub(r"-{2,}", "-", normalized) 200 return normalized 201 202 203 def title_case_skill_name(skill_name): 204 """Convert hyphenated skill name to Title Case for display.""" 205 return " ".join(word.capitalize() for word in skill_name.split("-")) 206 207 208 def parse_resources(raw_resources): 209 if not raw_resources: 210 return [] 211 resources = [item.strip() for item in raw_resources.split(",") if item.strip()] 212 invalid = sorted({item for item in resources if item not in ALLOWED_RESOURCES}) 213 if invalid: 214 allowed = ", ".join(sorted(ALLOWED_RESOURCES)) 215 print(f"[ERROR] Unknown resource type(s): {', '.join(invalid)}") 216 print(f" Allowed: {allowed}") 217 sys.exit(1) 218 deduped = [] 219 seen = set() 220 for resource in resources: 221 if resource not in seen: 222 deduped.append(resource) 223 seen.add(resource) 224 return deduped 225 226 227 def create_resource_dirs(skill_dir, skill_name, skill_title, resources, include_examples): 228 for resource in resources: 229 resource_dir = skill_dir / resource 230 resource_dir.mkdir(exist_ok=True) 231 if resource == "scripts": 232 if include_examples: 233 example_script = resource_dir / "example.py" 234 example_script.write_text(EXAMPLE_SCRIPT.format(skill_name=skill_name)) 235 example_script.chmod(0o755) 236 print("[OK] Created scripts/example.py") 237 else: 238 print("[OK] Created scripts/") 239 elif resource == "references": 240 if include_examples: 241 example_reference = resource_dir / "api_reference.md" 242 example_reference.write_text(EXAMPLE_REFERENCE.format(skill_title=skill_title)) 243 print("[OK] Created references/api_reference.md") 244 else: 245 print("[OK] Created references/") 246 elif resource == "assets": 247 if include_examples: 248 example_asset = resource_dir / "example_asset.txt" 249 example_asset.write_text(EXAMPLE_ASSET) 250 print("[OK] Created assets/example_asset.txt") 251 else: 252 print("[OK] Created assets/") 253 254 255 def init_skill(skill_name, path, resources, include_examples): 256 """ 257 Initialize a new skill directory with template SKILL.md. 258 259 Args: 260 skill_name: Name of the skill 261 path: Path where the skill directory should be created 262 resources: Resource directories to create 263 include_examples: Whether to create example files in resource directories 264 265 Returns: 266 Path to created skill directory, or None if error 267 """ 268 # Determine skill directory path 269 skill_dir = Path(path).resolve() / skill_name 270 271 # Check if directory already exists 272 if skill_dir.exists(): 273 print(f"[ERROR] Skill directory already exists: {skill_dir}") 274 return None 275 276 # Create skill directory 277 try: 278 skill_dir.mkdir(parents=True, exist_ok=False) 279 print(f"[OK] Created skill directory: {skill_dir}") 280 except Exception as e: 281 print(f"[ERROR] Error creating directory: {e}") 282 return None 283 284 # Create SKILL.md from template 285 skill_title = title_case_skill_name(skill_name) 286 skill_content = SKILL_TEMPLATE.format(skill_name=skill_name, skill_title=skill_title) 287 288 skill_md_path = skill_dir / "SKILL.md" 289 try: 290 skill_md_path.write_text(skill_content) 291 print("[OK] Created SKILL.md") 292 except Exception as e: 293 print(f"[ERROR] Error creating SKILL.md: {e}") 294 return None 295 296 # Create resource directories if requested 297 if resources: 298 try: 299 create_resource_dirs(skill_dir, skill_name, skill_title, resources, include_examples) 300 except Exception as e: 301 print(f"[ERROR] Error creating resource directories: {e}") 302 return None 303 304 # Print next steps 305 print(f"\n[OK] Skill '{skill_name}' initialized successfully at {skill_dir}") 306 print("\nNext steps:") 307 print("1. Edit SKILL.md to complete the TODO items and update the description") 308 if resources: 309 if include_examples: 310 print("2. Customize or delete the example files in scripts/, references/, and assets/") 311 else: 312 print("2. Add resources to scripts/, references/, and assets/ as needed") 313 else: 314 print("2. Create resource directories only if needed (scripts/, references/, assets/)") 315 print("3. Run the validator when ready to check the skill structure") 316 317 return skill_dir 318 319 320 def main(): 321 parser = argparse.ArgumentParser( 322 description="Create a new skill directory with a SKILL.md template.", 323 ) 324 parser.add_argument("skill_name", help="Skill name (normalized to hyphen-case)") 325 parser.add_argument("--path", required=True, help="Output directory for the skill") 326 parser.add_argument( 327 "--resources", 328 default="", 329 help="Comma-separated list: scripts,references,assets", 330 ) 331 parser.add_argument( 332 "--examples", 333 action="store_true", 334 help="Create example files inside the selected resource directories", 335 ) 336 args = parser.parse_args() 337 338 raw_skill_name = args.skill_name 339 skill_name = normalize_skill_name(raw_skill_name) 340 if not skill_name: 341 print("[ERROR] Skill name must include at least one letter or digit.") 342 sys.exit(1) 343 if len(skill_name) > MAX_SKILL_NAME_LENGTH: 344 print( 345 f"[ERROR] Skill name '{skill_name}' is too long ({len(skill_name)} characters). " 346 f"Maximum is {MAX_SKILL_NAME_LENGTH} characters." 347 ) 348 sys.exit(1) 349 if skill_name != raw_skill_name: 350 print(f"Note: Normalized skill name from '{raw_skill_name}' to '{skill_name}'.") 351 352 resources = parse_resources(args.resources) 353 if args.examples and not resources: 354 print("[ERROR] --examples requires --resources to be set.") 355 sys.exit(1) 356 357 path = args.path 358 359 print(f"Initializing skill: {skill_name}") 360 print(f" Location: {path}") 361 if resources: 362 print(f" Resources: {', '.join(resources)}") 363 if args.examples: 364 print(" Examples: enabled") 365 else: 366 print(" Resources: none (create as needed)") 367 print() 368 369 result = init_skill(skill_name, path, resources, args.examples) 370 371 if result: 372 sys.exit(0) 373 else: 374 sys.exit(1) 375 376 377 if __name__ == "__main__": 378 main()