scripts/init_skill.py
scripts/init_skill.pyBrowse 9 files
2,238 tokens
10,314 bytes
Token encoding: o200k_base
Snapshot 18d7ace
← Back to SKILL.md
1#!/usr/bin/env python32"""3Skill Initializer - Creates a new skill from template4 5Usage:6 init_skill.py <skill-name> --path <path> [--resources scripts,references,assets] [--examples] [--interface key=value]7 8Examples:9 init_skill.py my-new-skill --path skills/public10 init_skill.py my-new-skill --path skills/public --resources scripts,references11 init_skill.py my-api-helper --path skills/private --resources scripts --examples12 init_skill.py custom-skill --path /custom/location13 init_skill.py my-skill --path skills/public --interface short_description="Short UI label"14"""15 16import argparse17import re18import sys19from pathlib import Path20 21from generate_openai_yaml import write_openai_yaml22 23MAX_SKILL_NAME_LENGTH = 6424ALLOWED_RESOURCES = {"scripts", "references", "assets"}25 26SKILL_TEMPLATE = """---27name: {skill_name}28description: "[TODO: Briefly describe what this skill does and when it applies.]"29---30 31# {skill_title}32 33[TODO: Add the task-specific guidance Codex needs. Reference supporting files only when they are relevant.]34"""35 36EXAMPLE_SCRIPT = '''#!/usr/bin/env python337"""38Example helper script for {skill_name}39 40This is a placeholder script that can be executed directly.41Replace with actual implementation or delete if not needed.42 43Example real scripts from other skills:44- pdf/scripts/fill_fillable_fields.py - Fills PDF form fields45- pdf/scripts/convert_pdf_to_images.py - Converts PDF pages to images46"""47 48def main():49 print("This is an example script for {skill_name}")50 # TODO: Add actual script logic here51 # This could be data processing, file conversion, API calls, etc.52 53if __name__ == "__main__":54 main()55'''56 57EXAMPLE_REFERENCE = """# Reference for {skill_title}58 59Replace this placeholder with maintained, task-specific details that Codex60would not reliably know, such as operational constraints, local schemas, or61fragile integration behavior.62 63Delete this file if no supported workflow needs it.64"""65 66EXAMPLE_ASSET = """# Example Asset File67 68This placeholder represents where asset files would be stored.69Replace with actual asset files (templates, images, fonts, etc.) or delete if not needed.70 71Asset files are NOT intended to be loaded into context, but rather used within72the output Codex produces.73 74Example asset files from other skills:75- Brand guidelines: logo.png, slides_template.pptx76- Frontend builder: hello-world/ directory with HTML/React boilerplate77- Typography: custom-font.ttf, font-family.woff278- Data: sample_data.csv, test_dataset.json79 80## Common Asset Types81 82- Templates: .pptx, .docx, boilerplate directories83- Images: .png, .jpg, .svg, .gif84- Fonts: .ttf, .otf, .woff, .woff285- Boilerplate code: Project directories, starter files86- Icons: .ico, .svg87- Data files: .csv, .json, .xml, .yaml88 89Note: This is a text placeholder. Actual assets can be any file type.90"""91 92 93def normalize_skill_name(skill_name):94 """Normalize a skill name to lowercase hyphen-case."""95 normalized = skill_name.strip().lower()96 normalized = re.sub(r"[^a-z0-9]+", "-", normalized)97 normalized = normalized.strip("-")98 normalized = re.sub(r"-{2,}", "-", normalized)99 return normalized100 101 102def title_case_skill_name(skill_name):103 """Convert hyphenated skill name to Title Case for display."""104 return " ".join(word.capitalize() for word in skill_name.split("-"))105 106 107def parse_resources(raw_resources):108 if not raw_resources:109 return []110 resources = [item.strip() for item in raw_resources.split(",") if item.strip()]111 invalid = sorted({item for item in resources if item not in ALLOWED_RESOURCES})112 if invalid:113 allowed = ", ".join(sorted(ALLOWED_RESOURCES))114 print(f"[ERROR] Unknown resource type(s): {', '.join(invalid)}")115 print(f" Allowed: {allowed}")116 sys.exit(1)117 deduped = []118 seen = set()119 for resource in resources:120 if resource not in seen:121 deduped.append(resource)122 seen.add(resource)123 return deduped124 125 126def create_resource_dirs(127 skill_dir, skill_name, skill_title, resources, include_examples128):129 for resource in resources:130 resource_dir = skill_dir / resource131 resource_dir.mkdir(exist_ok=True)132 if resource == "scripts":133 if include_examples:134 example_script = resource_dir / "example.py"135 example_script.write_text(EXAMPLE_SCRIPT.format(skill_name=skill_name))136 example_script.chmod(0o755)137 print("[OK] Created scripts/example.py")138 else:139 print("[OK] Created scripts/")140 elif resource == "references":141 if include_examples:142 example_reference = resource_dir / "api_reference.md"143 example_reference.write_text(144 EXAMPLE_REFERENCE.format(skill_title=skill_title)145 )146 print("[OK] Created references/api_reference.md")147 else:148 print("[OK] Created references/")149 elif resource == "assets":150 if include_examples:151 example_asset = resource_dir / "example_asset.txt"152 example_asset.write_text(EXAMPLE_ASSET)153 print("[OK] Created assets/example_asset.txt")154 else:155 print("[OK] Created assets/")156 157 158def init_skill(skill_name, path, resources, include_examples, interface_overrides):159 """160 Initialize a new skill directory with template SKILL.md.161 162 Args:163 skill_name: Name of the skill164 path: Path where the skill directory should be created165 resources: Resource directories to create166 include_examples: Whether to create example files in resource directories167 168 Returns:169 Path to created skill directory, or None if error170 """171 # Determine skill directory path172 skill_dir = Path(path).resolve() / skill_name173 174 # Check if directory already exists175 if skill_dir.exists():176 print(f"[ERROR] Skill directory already exists: {skill_dir}")177 return None178 179 # Create skill directory180 try:181 skill_dir.mkdir(parents=True, exist_ok=False)182 print(f"[OK] Created skill directory: {skill_dir}")183 except Exception as e:184 print(f"[ERROR] Error creating directory: {e}")185 return None186 187 # Create SKILL.md from template188 skill_title = title_case_skill_name(skill_name)189 skill_content = SKILL_TEMPLATE.format(190 skill_name=skill_name, skill_title=skill_title191 )192 193 skill_md_path = skill_dir / "SKILL.md"194 try:195 skill_md_path.write_text(skill_content)196 print("[OK] Created SKILL.md")197 except Exception as e:198 print(f"[ERROR] Error creating SKILL.md: {e}")199 return None200 201 # Create agents/openai.yaml202 try:203 result = write_openai_yaml(skill_dir, skill_name, interface_overrides)204 if not result:205 return None206 except Exception as e:207 print(f"[ERROR] Error creating agents/openai.yaml: {e}")208 return None209 210 # Create resource directories if requested211 if resources:212 try:213 create_resource_dirs(214 skill_dir, skill_name, skill_title, resources, include_examples215 )216 except Exception as e:217 print(f"[ERROR] Error creating resource directories: {e}")218 return None219 220 # Print next steps221 print(f"\n[OK] Skill '{skill_name}' initialized successfully at {skill_dir}")222 print("\nNext steps:")223 print("1. Edit SKILL.md to complete the TODO items and update the description")224 if resources:225 if include_examples:226 print(227 "2. Customize or delete the example files in scripts/, references/, and assets/"228 )229 else:230 print("2. Add resources to scripts/, references/, and assets/ as needed")231 else:232 print(233 "2. Create resource directories only if needed (scripts/, references/, assets/)"234 )235 print("3. Update agents/openai.yaml if the UI metadata should differ")236 print("4. Run the validator when ready to check the skill structure")237 print(238 "5. Consider independent forward-testing only when complexity or risk warrants it"239 )240 241 return skill_dir242 243 244def main():245 parser = argparse.ArgumentParser(246 description="Create a new skill directory with a SKILL.md template.",247 )248 parser.add_argument("skill_name", help="Skill name (normalized to hyphen-case)")249 parser.add_argument("--path", required=True, help="Output directory for the skill")250 parser.add_argument(251 "--resources",252 default="",253 help="Comma-separated list: scripts,references,assets",254 )255 parser.add_argument(256 "--examples",257 action="store_true",258 help="Create example files inside the selected resource directories",259 )260 parser.add_argument(261 "--interface",262 action="append",263 default=[],264 help="Interface override in key=value format (repeatable)",265 )266 args = parser.parse_args()267 268 raw_skill_name = args.skill_name269 skill_name = normalize_skill_name(raw_skill_name)270 if not skill_name:271 print("[ERROR] Skill name must include at least one letter or digit.")272 sys.exit(1)273 if len(skill_name) > MAX_SKILL_NAME_LENGTH:274 print(275 f"[ERROR] Skill name '{skill_name}' is too long ({len(skill_name)} characters). "276 f"Maximum is {MAX_SKILL_NAME_LENGTH} characters."277 )278 sys.exit(1)279 if skill_name != raw_skill_name:280 print(f"Note: Normalized skill name from '{raw_skill_name}' to '{skill_name}'.")281 282 resources = parse_resources(args.resources)283 if args.examples and not resources:284 print("[ERROR] --examples requires --resources to be set.")285 sys.exit(1)286 287 path = args.path288 289 print(f"Initializing skill: {skill_name}")290 print(f" Location: {path}")291 if resources:292 print(f" Resources: {', '.join(resources)}")293 if args.examples:294 print(" Examples: enabled")295 else:296 print(" Resources: none (create as needed)")297 print()298 299 result = init_skill(skill_name, path, resources, args.examples, args.interface)300 301 if result:302 sys.exit(0)303 else:304 sys.exit(1)305 306 307if __name__ == "__main__":308 main()309 Referenced from SKILL.md
SKILL.mdView in source ↗
Source excerpt starting at line 174.SKILL.mdView in source ↗174```bash175scripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples]176```
Source excerpt starting at line 180.180```bash181scripts/init_skill.py my-skill --path "${CODEX_HOME:-$HOME/.codex}/skills"182scripts/init_skill.py my-skill --path "${CODEX_HOME:-$HOME/.codex}/skills" --resources references183```
