返回 JoyAI-Echo
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()
379
379 lines PYTHON