/ skills / skill-creator / scripts / init_skill.py
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()