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