Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Scaffold new Starchild skills with correct frontmatter, directory structure, and progressive-disclosure design principles.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/init_skill.py
1#!/usr/bin/env python32"""3Skill Initializer - Creates a new skill from template45Usage:6init_skill.py <skill-name> --path <path> [--resources scripts,references,assets] [--examples]78Examples:9python init_skill.py my-new-skill --path ./workspace/skills10python init_skill.py api-helper --path ./workspace/skills --resources scripts,references11python init_skill.py custom-skill --path ./workspace/skills --resources scripts --examples12"""1314import argparse15import re16import sys17from pathlib import Path1819MAX_SKILL_NAME_LENGTH = 6420ALLOWED_RESOURCES = {"scripts", "references", "assets"}2122SKILL_TEMPLATE = """---23name: {skill_name}24description: "[TODO: What this skill does. Use when <specific trigger scenarios>.]"2526metadata:27starchild:28emoji:29skillKey: {skill_name}30requires:31env: []32bins: []3334user-invocable: true35---3637# {skill_title}3839[TODO: 1-2 sentences in direct voice. "You do X" not "This skill does X."40Explain what capability this gives the agent and why it matters.]4142## Structuring This Skill4344Pick the pattern that fits best, then delete this section:4546- **Workflow-based** — Step-by-step process (fetch data -> process -> render -> output)47- **Task-based** — Organized by user request ("analyze X" / "compare Y" / "generate Z")48- **Reference/guidelines** — Rules, decision frameworks, core truths49- **Capabilities-based** — Organized by what the skill can do (tool group A / tool group B)5051## [TODO: Main Section]5253[TODO: Core instructions the agent needs every time this skill activates.54Focus on knowledge the agent doesn't already have:55- Domain-specific interpretation guides56- Decision trees ("when X, do Y; when Z, do W")57- Gotchas and edge cases58- Key parameters and thresholds]5960## Resources6162[TODO: Document scripts/, references/, assets/ if used.63Delete this section if no resource directories exist.]64"""6566EXAMPLE_SCRIPT = '''#!/usr/bin/env python367"""68{skill_title} - Helper Script6970Usage:71python scripts/example.py --input <path> [--output <path>]7273This script handles [TODO: describe what this automates].74Scripts are for low-freedom operations: fragile API calls, exact rendering,75repetitive boilerplate. The agent executes these via bash, not by reading76them into context.77"""7879import argparse80import json81import sys828384def main():85parser = argparse.ArgumentParser(description="{skill_title} helper")86parser.add_argument("--input", required=True, help="Input file path")87parser.add_argument("--output", default=None, help="Output file path (default: stdout)")88args = parser.parse_args()8990# TODO: Replace with actual implementation91print(f"Processing: {{args.input}}")9293result = {{"status": "ok", "input": args.input}}9495if args.output:96with open(args.output, "w") as f:97json.dump(result, f, indent=2)98print(f"Output written to: {{args.output}}")99else:100print(json.dumps(result, indent=2))101102103if __name__ == "__main__":104main()105'''106107EXAMPLE_REFERENCE = """# {skill_title} — Reference Documentation108109This reference is loaded on demand via `read_file` when the agent needs110detailed information beyond what's in the main SKILL.md.111112## When to Load This Reference113114- Agent needs full API endpoint details115- Agent needs to look up error codes or schema definitions116- Complex multi-step process requires detailed walkthrough117118## API Reference119120### Authentication121122[TODO: How to authenticate — headers, tokens, env vars]123124### Endpoints125126| Endpoint | Method | Description |127|----------|--------|-------------|128| `/api/example` | GET | [TODO: Description] |129| `/api/example` | POST | [TODO: Description] |130131### Error Codes132133| Code | Meaning | Recovery |134|------|---------|----------|135| 401 | Invalid API key | Check env var is set |136| 429 | Rate limited | Wait and retry (backoff) |137138## Troubleshooting139140[TODO: Common issues and how to resolve them]141"""142143EXAMPLE_ASSET = """{{144"name": "{skill_name}",145"version": "1.0",146"description": "Template asset for {skill_title}",147"TODO": "Replace this with actual asset content (templates, config, data files). Assets are NOT loaded into context — they're used in output generation."148}}149"""150151152def normalize_skill_name(skill_name: str) -> str:153"""Normalize skill name to lowercase hyphen-case."""154normalized = skill_name.strip().lower()155normalized = re.sub(r"[^a-z0-9]+", "-", normalized)156normalized = normalized.strip("-")157normalized = re.sub(r"-{2,}", "-", normalized)158return normalized159160161def title_case_skill_name(skill_name: str) -> str:162"""Convert hyphenated skill name to Title Case."""163return " ".join(word.capitalize() for word in skill_name.split("-"))164165166def parse_resources(raw_resources: str) -> list:167if not raw_resources:168return []169resources = [item.strip() for item in raw_resources.split(",") if item.strip()]170invalid = [item for item in resources if item not in ALLOWED_RESOURCES]171if invalid:172print(f"[ERROR] Unknown resource type(s): {', '.join(invalid)}")173print(f" Allowed: {', '.join(sorted(ALLOWED_RESOURCES))}")174sys.exit(1)175return list(dict.fromkeys(resources)) # dedupe while preserving order176177178def create_resource_dirs(skill_dir: Path, skill_name: str, skill_title: str,179resources: list, include_examples: bool):180for resource in resources:181resource_dir = skill_dir / resource182resource_dir.mkdir(exist_ok=True)183184if resource == "scripts":185if include_examples:186example_script = resource_dir / "example.py"187example_script.write_text(EXAMPLE_SCRIPT.format(188skill_name=skill_name, skill_title=skill_title))189example_script.chmod(0o755)190print(f"[OK] Created {resource}/example.py")191else:192print(f"[OK] Created {resource}/")193194elif resource == "references":195if include_examples:196example_ref = resource_dir / "reference.md"197example_ref.write_text(EXAMPLE_REFERENCE.format(198skill_name=skill_name, skill_title=skill_title))199print(f"[OK] Created {resource}/reference.md")200else:201print(f"[OK] Created {resource}/")202203elif resource == "assets":204if include_examples:205example_asset = resource_dir / "template.json"206example_asset.write_text(EXAMPLE_ASSET.format(207skill_name=skill_name, skill_title=skill_title))208print(f"[OK] Created {resource}/template.json")209else:210print(f"[OK] Created {resource}/")211212213def init_skill(skill_name: str, path: str, resources: list, include_examples: bool) -> Path:214"""215Initialize a new skill directory with template SKILL.md.216217Returns:218Path to created skill directory, or None if error219"""220skill_dir = Path(path).resolve() / skill_name221222if skill_dir.exists():223print(f"[ERROR] Skill directory already exists: {skill_dir}")224return None225226try:227skill_dir.mkdir(parents=True, exist_ok=False)228print(f"[OK] Created skill directory: {skill_dir}")229except Exception as e:230print(f"[ERROR] Error creating directory: {e}")231return None232233# Create SKILL.md234skill_title = title_case_skill_name(skill_name)235skill_content = SKILL_TEMPLATE.format(skill_name=skill_name, skill_title=skill_title)236237skill_md_path = skill_dir / "SKILL.md"238try:239skill_md_path.write_text(skill_content)240print("[OK] Created SKILL.md")241except Exception as e:242print(f"[ERROR] Error creating SKILL.md: {e}")243return None244245# Create resource directories246if resources:247try:248create_resource_dirs(skill_dir, skill_name, skill_title, resources, include_examples)249except Exception as e:250print(f"[ERROR] Error creating resource directories: {e}")251return None252253print(f"\n[OK] Skill '{skill_name}' initialized at {skill_dir}")254print("\nNext steps:")255print("1. Edit SKILL.md — complete the TODOs, write a strong description")256print("2. Add resources to scripts/, references/, assets/ as needed")257print("3. Run validate_skill.py to check for issues")258print("4. Call skill_refresh() to make the skill available")259260return skill_dir261262263def main():264parser = argparse.ArgumentParser(265description="Create a new skill directory with SKILL.md template."266)267parser.add_argument("skill_name", help="Skill name (normalized to hyphen-case)")268parser.add_argument("--path", required=True, help="Output directory for the skill")269parser.add_argument(270"--resources",271default="",272help="Comma-separated list: scripts,references,assets"273)274parser.add_argument(275"--examples",276action="store_true",277help="Create example files in resource directories"278)279args = parser.parse_args()280281raw_name = args.skill_name282skill_name = normalize_skill_name(raw_name)283284if not skill_name:285print("[ERROR] Skill name must include at least one letter or digit.")286sys.exit(1)287288if len(skill_name) > MAX_SKILL_NAME_LENGTH:289print(f"[ERROR] Skill name too long ({len(skill_name)} > {MAX_SKILL_NAME_LENGTH})")290sys.exit(1)291292if skill_name != raw_name:293print(f"Note: Normalized '{raw_name}' to '{skill_name}'")294295resources = parse_resources(args.resources)296297if args.examples and not resources:298print("[ERROR] --examples requires --resources to be set")299sys.exit(1)300301print(f"Initializing skill: {skill_name}")302print(f" Location: {args.path}")303if resources:304print(f" Resources: {', '.join(resources)}")305print()306307result = init_skill(skill_name, args.path, resources, args.examples)308sys.exit(0 if result else 1)309310311if __name__ == "__main__":312main()313