scripts/generate_openai_yaml.py
scripts/generate_openai_yaml.pyBrowse 9 files
1,571 tokens
6,669 bytes
Token encoding: o200k_base
Snapshot 18d7ace
← Back to SKILL.md
1#!/usr/bin/env python32"""3OpenAI YAML Generator - Creates agents/openai.yaml for a skill folder.4 5Usage:6 generate_openai_yaml.py <skill_dir> [--name <skill_name>] [--interface key=value]7"""8 9import argparse10import re11import sys12from pathlib import Path13 14ACRONYMS = {15 "GH",16 "MCP",17 "API",18 "CI",19 "CLI",20 "LLM",21 "PDF",22 "PR",23 "UI",24 "URL",25 "SQL",26}27 28BRANDS = {29 "openai": "OpenAI",30 "openapi": "OpenAPI",31 "github": "GitHub",32 "pagerduty": "PagerDuty",33 "datadog": "DataDog",34 "sqlite": "SQLite",35 "fastapi": "FastAPI",36}37 38SMALL_WORDS = {"and", "or", "to", "up", "with"}39 40ALLOWED_INTERFACE_KEYS = {41 "display_name",42 "short_description",43 "icon_small",44 "icon_large",45 "brand_color",46 "default_prompt",47}48 49 50def yaml_quote(value):51 escaped = value.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n")52 return f'"{escaped}"'53 54 55def format_display_name(skill_name):56 words = [word for word in skill_name.split("-") if word]57 formatted = []58 for index, word in enumerate(words):59 lower = word.lower()60 upper = word.upper()61 if upper in ACRONYMS:62 formatted.append(upper)63 continue64 if lower in BRANDS:65 formatted.append(BRANDS[lower])66 continue67 if index > 0 and lower in SMALL_WORDS:68 formatted.append(lower)69 continue70 formatted.append(word.capitalize())71 return " ".join(formatted)72 73 74def generate_short_description(display_name):75 description = f"Help with {display_name} tasks"76 77 if len(description) < 25:78 description = f"Help with {display_name} tasks and workflows"79 if len(description) < 25:80 description = f"Help with {display_name} tasks with guidance"81 82 if len(description) > 64:83 description = f"Help with {display_name}"84 if len(description) > 64:85 description = f"{display_name} helper"86 if len(description) > 64:87 description = f"{display_name} tools"88 if len(description) > 64:89 suffix = " helper"90 max_name_length = 64 - len(suffix)91 trimmed = display_name[:max_name_length].rstrip()92 description = f"{trimmed}{suffix}"93 if len(description) > 64:94 description = description[:64].rstrip()95 96 if len(description) < 25:97 description = f"{description} workflows"98 if len(description) > 64:99 description = description[:64].rstrip()100 101 return description102 103 104def read_frontmatter_name(skill_dir):105 skill_md = Path(skill_dir) / "SKILL.md"106 if not skill_md.exists():107 print(f"[ERROR] SKILL.md not found in {skill_dir}")108 return None109 content = skill_md.read_text()110 match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL)111 if not match:112 print("[ERROR] Invalid SKILL.md frontmatter format.")113 return None114 frontmatter_text = match.group(1)115 116 import yaml117 118 try:119 frontmatter = yaml.safe_load(frontmatter_text)120 except yaml.YAMLError as exc:121 print(f"[ERROR] Invalid YAML frontmatter: {exc}")122 return None123 if not isinstance(frontmatter, dict):124 print("[ERROR] Frontmatter must be a YAML dictionary.")125 return None126 name = frontmatter.get("name", "")127 if not isinstance(name, str) or not name.strip():128 print("[ERROR] Frontmatter 'name' is missing or invalid.")129 return None130 return name.strip()131 132 133def parse_interface_overrides(raw_overrides):134 overrides = {}135 optional_order = []136 for item in raw_overrides:137 if "=" not in item:138 print(f"[ERROR] Invalid interface override '{item}'. Use key=value.")139 return None, None140 key, value = item.split("=", 1)141 key = key.strip()142 value = value.strip()143 if not key:144 print(f"[ERROR] Invalid interface override '{item}'. Key is empty.")145 return None, None146 if key not in ALLOWED_INTERFACE_KEYS:147 allowed = ", ".join(sorted(ALLOWED_INTERFACE_KEYS))148 print(f"[ERROR] Unknown interface field '{key}'. Allowed: {allowed}")149 return None, None150 overrides[key] = value151 if (152 key not in ("display_name", "short_description")153 and key not in optional_order154 ):155 optional_order.append(key)156 return overrides, optional_order157 158 159def write_openai_yaml(skill_dir, skill_name, raw_overrides):160 overrides, optional_order = parse_interface_overrides(raw_overrides)161 if overrides is None:162 return None163 164 display_name = overrides.get("display_name") or format_display_name(skill_name)165 short_description = overrides.get(166 "short_description"167 ) or generate_short_description(display_name)168 169 if not (25 <= len(short_description) <= 64):170 print(171 "[ERROR] short_description must be 25-64 characters "172 f"(got {len(short_description)})."173 )174 return None175 176 interface_lines = [177 "interface:",178 f" display_name: {yaml_quote(display_name)}",179 f" short_description: {yaml_quote(short_description)}",180 ]181 182 for key in optional_order:183 value = overrides.get(key)184 if value is not None:185 interface_lines.append(f" {key}: {yaml_quote(value)}")186 187 agents_dir = Path(skill_dir) / "agents"188 agents_dir.mkdir(parents=True, exist_ok=True)189 output_path = agents_dir / "openai.yaml"190 output_path.write_text("\n".join(interface_lines) + "\n")191 print(f"[OK] Created agents/openai.yaml")192 return output_path193 194 195def main():196 parser = argparse.ArgumentParser(197 description="Create agents/openai.yaml for a skill directory.",198 )199 parser.add_argument("skill_dir", help="Path to the skill directory")200 parser.add_argument(201 "--name",202 help="Skill name override (defaults to SKILL.md frontmatter)",203 )204 parser.add_argument(205 "--interface",206 action="append",207 default=[],208 help="Interface override in key=value format (repeatable)",209 )210 args = parser.parse_args()211 212 skill_dir = Path(args.skill_dir).resolve()213 if not skill_dir.exists():214 print(f"[ERROR] Skill directory not found: {skill_dir}")215 sys.exit(1)216 if not skill_dir.is_dir():217 print(f"[ERROR] Path is not a directory: {skill_dir}")218 sys.exit(1)219 220 skill_name = args.name or read_frontmatter_name(skill_dir)221 if not skill_name:222 sys.exit(1)223 224 result = write_openai_yaml(skill_dir, skill_name, args.interface)225 if result:226 sys.exit(0)227 sys.exit(1)228 229 230if __name__ == "__main__":231 main()232 