From f19e51da31b671105708cea301f5b145b773e6a3 Mon Sep 17 00:00:00 2001 From: Jiang Bohan Date: Fri, 30 Jan 2026 17:39:03 +0800 Subject: [PATCH] feat(skill-creator): add init_skill.py script for reliable skill creation - Add init_skill.py that always creates skills in ~/.super-multica/skills/ - Restructure SKILL.md with step-by-step creation process - Make script usage mandatory to prevent skills in wrong directories - Support --description, --emoji, --tag, --resources options --- skills/skill-creator/SKILL.md | 279 ++++++++------------- skills/skill-creator/scripts/init_skill.py | 248 ++++++++++++++++++ 2 files changed, 349 insertions(+), 178 deletions(-) create mode 100755 skills/skill-creator/scripts/init_skill.py diff --git a/skills/skill-creator/SKILL.md b/skills/skill-creator/SKILL.md index 29c8d77f..6afbbf70 100644 --- a/skills/skill-creator/SKILL.md +++ b/skills/skill-creator/SKILL.md @@ -1,7 +1,7 @@ --- name: Skill Creator -description: Create, edit, and manage custom skills to extend agent capabilities -version: 1.0.0 +description: Create, edit, and manage custom skills to extend agent capabilities. Use when the user asks to create a new skill, build a custom capability, or extend the agent's functionality. +version: 1.1.0 metadata: emoji: "🛠️" always: true @@ -15,23 +15,78 @@ metadata: You can create, edit, and manage skills to extend your own capabilities or help users build custom skills. -### Skills Directory +## Skill Creation Process -Custom skills are stored in `~/.super-multica/skills/`. Each skill is a directory containing a `SKILL.md` file. +**ALWAYS follow these steps in order when creating a new skill:** -``` -~/.super-multica/skills/ -├── my-skill/ -│ └── SKILL.md -├── another-skill/ -│ ├── SKILL.md -│ └── scripts/ -│ └── helper.py +1. Understand what the skill should do +2. Initialize the skill using `init_skill.py` +3. Edit the generated SKILL.md +4. Test the skill + +### Step 1: Understand the Skill + +Before creating, clarify: +- What functionality should the skill provide? +- When should it be triggered? +- Does it need helper scripts? + +### Step 2: Initialize the Skill + +**CRITICAL: Always use the init_skill.py script to create skills.** This ensures the skill is created in the correct location (`~/.super-multica/skills/`). + +```bash +# Basic usage +python3 ~/.super-multica/skills/skill-creator/scripts/init_skill.py + +# With description and resources +python3 ~/.super-multica/skills/skill-creator/scripts/init_skill.py my-skill \ + --description "What this skill does" \ + --resources scripts ``` -### SKILL.md Format +**Script options:** +- `--description, -d` - Skill description +- `--emoji, -e` - Emoji for display (default: 🔧) +- `--tag, -t` - Primary tag (default: custom) +- `--resources, -r` - Create directories: `scripts`, `references`, or both -Every skill must have a `SKILL.md` file with YAML frontmatter and Markdown instructions: +**Examples:** +```bash +# Simple skill +python3 scripts/init_skill.py translator -d "Translate text between languages" + +# Skill with helper script +python3 scripts/init_skill.py pdf-rotator -d "Rotate PDF pages" -r scripts + +# Skill with references +python3 scripts/init_skill.py api-helper -d "Help with API calls" -r scripts,references +``` + +The script path when running from the skill-creator directory: +```bash +python3 ~/.super-multica/skills/skill-creator/scripts/init_skill.py +``` + +### Step 3: Edit the Skill + +After initialization, edit `~/.super-multica/skills//SKILL.md`: + +1. Update the `description` - This is the primary trigger mechanism +2. Write clear `## Instructions` - What the agent should do +3. Add helper scripts to `scripts/` if needed +4. Add reference docs to `references/` if needed + +### Step 4: Test the Skill + +The skill is automatically loaded (hot-reload). Verify with: +```bash +pnpm skills:cli list | grep +``` + +## SKILL.md Format + +Every skill must have a `SKILL.md` file with YAML frontmatter: ```markdown --- @@ -42,16 +97,14 @@ metadata: emoji: "🔧" tags: - category1 - - category2 requires: bins: [required-binary] env: [REQUIRED_ENV_VAR] - os: [darwin, linux] --- ## Instructions -Detailed instructions that will be injected into your system prompt... +Detailed instructions for using this skill... ``` ### Frontmatter Fields @@ -59,195 +112,65 @@ Detailed instructions that will be injected into your system prompt... | Field | Required | Description | |-------|----------|-------------| | `name` | Yes | Display name for the skill | -| `description` | No | Short description | +| `description` | Yes | Short description (triggers skill selection) | | `version` | No | Semantic version | | `metadata.emoji` | No | Emoji for display | | `metadata.tags` | No | Categorization tags | -| `metadata.always` | No | If true, always include (skip eligibility) | | `metadata.requires.bins` | No | Required binaries (all must exist) | | `metadata.requires.anyBins` | No | Alternative binaries (one must exist) | | `metadata.requires.env` | No | Required environment variables | -| `metadata.os` | No | Supported platforms: darwin, linux, win32 | -### Creating a Skill +## Directory Structure -**IMPORTANT**: Always create skills in `~/.super-multica/skills/`, NOT in the current working directory. +Skills are stored in `~/.super-multica/skills/`: -To create a new skill: - -1. Create the skill directory (use absolute path): - ```bash - mkdir -p ~/.super-multica/skills/ - ``` - -2. Write the SKILL.md file at `~/.super-multica/skills//SKILL.md` - -3. If including scripts, create them at `~/.super-multica/skills//scripts/` - -4. The skill will be automatically loaded (hot-reload is enabled) - -### Example: Creating a Translation Skill - -```markdown ---- -name: Translator -description: Translate text between languages -version: 1.0.0 -metadata: - emoji: "🌐" - tags: - - language - - translation ---- - -## Instructions - -When asked to translate text: - -1. Identify the source and target languages -2. If source language is not specified, detect it from the text -3. Provide accurate, natural-sounding translations -4. For ambiguous terms, offer alternative translations with context -5. Preserve formatting (bullet points, paragraphs, etc.) - -### Supported Languages - -You can translate between any common languages including: -English, Chinese, Japanese, Korean, Spanish, French, German, etc. - -### Example Usage - -User: "Translate 'Hello, how are you?' to Chinese" -Response: "你好,你好吗?" (Nǐ hǎo, nǐ hǎo ma?) +``` +~/.super-multica/skills/ +├── my-skill/ +│ └── SKILL.md +├── another-skill/ +│ ├── SKILL.md +│ ├── scripts/ +│ │ └── helper.py +│ └── references/ +│ └── api-docs.md ``` -### Example: Creating a Code Formatter Skill - -```markdown ---- -name: Code Formatter -description: Format and beautify code in various languages -version: 1.0.0 -metadata: - emoji: "✨" - requires: - anyBins: [prettier, black, gofmt] - tags: - - code - - formatting ---- - -## Instructions - -When asked to format code: - -1. Detect the programming language -2. Apply language-specific formatting rules -3. Use available formatters when possible: - - JavaScript/TypeScript: prettier - - Python: black - - Go: gofmt -4. If no formatter is available, apply standard conventions -``` - -### Editing a Skill +## Editing Existing Skills To modify an existing skill: 1. Read the current SKILL.md file -2. Make your changes to the frontmatter or instructions -3. Save the file - changes take effect immediately +2. Make changes to frontmatter or instructions +3. Save - changes take effect immediately (hot-reload) -### Listing Skills +## Listing and Removing Skills -To see available skills, check the skills directory: -```bash -ls ~/.super-multica/skills/ -``` - -Or use the CLI: ```bash +# List all skills pnpm skills:cli list -``` -### Removing a Skill +# Check skill status +pnpm skills:cli status -To remove a skill: -```bash +# Remove a skill +pnpm skills:cli remove +# or rm -rf ~/.super-multica/skills/ ``` -Or use the CLI: -```bash -pnpm skills:cli remove -``` +## Best Practices -### Progressive Disclosure +1. **Use init_skill.py** - Never create skills manually in random directories +2. **Clear description** - Include "when to use" triggers in the description +3. **Concise instructions** - Keep SKILL.md under 500 lines +4. **Test scripts** - Run helper scripts to verify they work +5. **Single responsibility** - Each skill should do one thing well -Skills use a multi-level loading system to manage context efficiently: +## Skill Precedence -1. **Metadata** (name + description) - Always loaded for skill selection (~50-100 tokens) -2. **SKILL.md body** - Loaded when skill is invoked or relevant -3. **Bundled resources** - Loaded on-demand when referenced - -**Design principle**: Keep SKILL.md concise. The context window is shared with conversation history and other skills. Only include information that the agent doesn't already know. - -**For large skills**, split content into separate files: - -``` -my-skill/ -├── SKILL.md # Core workflow and navigation -├── references/ -│ ├── api-docs.md # Detailed API documentation -│ └── examples.md # Extended examples -└── scripts/ - └── helper.py # Reusable scripts -``` - -In SKILL.md, reference them with clear guidance: -```markdown -## Advanced Features - -- **API Reference**: See `references/api-docs.md` for complete method documentation -- **Examples**: See `references/examples.md` for common patterns -``` - -### Best Practices - -1. **Concise Instructions**: Keep SKILL.md under 500 lines; split larger content into references -2. **Clear Triggers**: Write descriptions that help the agent know when to use the skill -3. **Examples**: Include usage examples in your skill -4. **Requirements**: Specify binaries/env vars if needed -5. **Versioning**: Update version when making changes -6. **Tags**: Use descriptive tags for organization -7. **Single Responsibility**: Each skill should do one thing well - -### Including Scripts - -Skills can include helper scripts in a `scripts/` subdirectory: - -``` -my-skill/ -├── SKILL.md -└── scripts/ - ├── process.py - └── helper.sh -``` - -Reference them in your instructions: -```markdown -To process data, run the helper script: -\`\`\`bash -python ~/.super-multica/skills/my-skill/scripts/process.py -\`\`\` -``` - -### Skill Precedence - -Skills from different sources have different priorities (highest wins): +Skills from different sources (highest priority wins): 1. Profile-specific skills (`~/.super-multica/agent-profiles//skills/`) 2. User-installed skills (`~/.super-multica/skills/`) 3. Plugin skills (from npm packages) 4. Bundled skills (built into the application) - -A user skill with the same ID as a bundled skill will override it. diff --git a/skills/skill-creator/scripts/init_skill.py b/skills/skill-creator/scripts/init_skill.py new file mode 100755 index 00000000..6af1fa9e --- /dev/null +++ b/skills/skill-creator/scripts/init_skill.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python3 +""" +Skill Initializer - Creates a new skill in the managed skills directory + +Usage: + init_skill.py [--resources scripts,references] + +Examples: + init_skill.py my-translator + init_skill.py code-formatter --resources scripts + init_skill.py api-helper --resources scripts,references + +The skill will be created at: ~/.super-multica/skills// +""" + +import argparse +import os +import re +import sys +from pathlib import Path + +# Fixed output directory - always use managed skills directory +MANAGED_SKILLS_DIR = Path.home() / ".super-multica" / "skills" + +MAX_SKILL_NAME_LENGTH = 64 +ALLOWED_RESOURCES = {"scripts", "references"} + +SKILL_TEMPLATE = """--- +name: {skill_name} +description: {description} +version: 1.0.0 +metadata: + emoji: "{emoji}" + tags: + - {tag} +--- + +## Instructions + +{instructions} +""" + +EXAMPLE_SCRIPT = '''#!/usr/bin/env python3 +""" +Helper script for {skill_name} + +Replace this with your actual implementation. +""" + +def main(): + print("Hello from {skill_name}!") + # TODO: Add your script logic here + +if __name__ == "__main__": + main() +''' + +EXAMPLE_REFERENCE = """# Reference Documentation for {skill_title} + +Add detailed reference documentation here. + +## Overview + +[Describe what this reference covers] + +## Details + +[Add detailed information] +""" + + +def normalize_skill_name(skill_name: str) -> str: + """Normalize a skill name to lowercase hyphen-case.""" + normalized = skill_name.strip().lower() + normalized = re.sub(r"[^a-z0-9]+", "-", normalized) + normalized = normalized.strip("-") + normalized = re.sub(r"-{2,}", "-", normalized) + return normalized + + +def title_case_skill_name(skill_name: str) -> str: + """Convert hyphenated skill name to Title Case for display.""" + return " ".join(word.capitalize() for word in skill_name.split("-")) + + +def parse_resources(raw_resources: str) -> list[str]: + if not raw_resources: + return [] + resources = [item.strip() for item in raw_resources.split(",") if item.strip()] + invalid = sorted({item for item in resources if item not in ALLOWED_RESOURCES}) + if invalid: + allowed = ", ".join(sorted(ALLOWED_RESOURCES)) + print(f"[ERROR] Unknown resource type(s): {', '.join(invalid)}") + print(f" Allowed: {allowed}") + sys.exit(1) + return list(dict.fromkeys(resources)) # dedupe while preserving order + + +def create_resource_dirs(skill_dir: Path, skill_name: str, skill_title: str, resources: list[str]): + for resource in resources: + resource_dir = skill_dir / resource + resource_dir.mkdir(exist_ok=True) + + if resource == "scripts": + example_script = resource_dir / "helper.py" + example_script.write_text(EXAMPLE_SCRIPT.format(skill_name=skill_name)) + example_script.chmod(0o755) + print(f" [OK] Created {resource}/helper.py") + elif resource == "references": + example_ref = resource_dir / "reference.md" + example_ref.write_text(EXAMPLE_REFERENCE.format(skill_title=skill_title)) + print(f" [OK] Created {resource}/reference.md") + + +def init_skill(skill_name: str, description: str, emoji: str, tag: str, + instructions: str, resources: list[str]) -> Path | None: + """ + Initialize a new skill directory with SKILL.md. + + Returns: + Path to created skill directory, or None if error + """ + # Ensure managed skills directory exists + MANAGED_SKILLS_DIR.mkdir(parents=True, exist_ok=True) + + # Determine skill directory path + skill_dir = MANAGED_SKILLS_DIR / skill_name + + # Check if directory already exists + if skill_dir.exists(): + print(f"[ERROR] Skill directory already exists: {skill_dir}") + print(f" To edit, modify files directly in {skill_dir}") + print(f" To recreate, first run: rm -rf {skill_dir}") + return None + + # Create skill directory + try: + skill_dir.mkdir(parents=True, exist_ok=False) + print(f"[OK] Created skill directory: {skill_dir}") + except Exception as e: + print(f"[ERROR] Error creating directory: {e}") + return None + + # Create SKILL.md from template + skill_content = SKILL_TEMPLATE.format( + skill_name=skill_name, + description=description, + emoji=emoji, + tag=tag, + instructions=instructions + ) + + skill_md_path = skill_dir / "SKILL.md" + try: + skill_md_path.write_text(skill_content) + print(" [OK] Created SKILL.md") + except Exception as e: + print(f"[ERROR] Error creating SKILL.md: {e}") + return None + + # Create resource directories if requested + if resources: + skill_title = title_case_skill_name(skill_name) + try: + create_resource_dirs(skill_dir, skill_name, skill_title, resources) + except Exception as e: + print(f"[ERROR] Error creating resource directories: {e}") + return None + + # Print summary + print(f"\n[SUCCESS] Skill '{skill_name}' created at {skill_dir}") + print("\nThe skill is now active (hot-reload enabled).") + print("Test it by running: pnpm skills:cli list") + + return skill_dir + + +def main(): + parser = argparse.ArgumentParser( + description="Create a new skill in ~/.super-multica/skills/", + ) + parser.add_argument("skill_name", help="Skill name (will be normalized to hyphen-case)") + parser.add_argument( + "--description", "-d", + default="[TODO: Add description]", + help="Skill description" + ) + parser.add_argument( + "--emoji", "-e", + default="🔧", + help="Emoji for the skill (default: 🔧)" + ) + parser.add_argument( + "--tag", "-t", + default="custom", + help="Primary tag for the skill (default: custom)" + ) + parser.add_argument( + "--instructions", "-i", + default="[TODO: Add instructions for when and how to use this skill]", + help="Instructions content" + ) + parser.add_argument( + "--resources", "-r", + default="", + help="Comma-separated list: scripts,references" + ) + args = parser.parse_args() + + raw_skill_name = args.skill_name + skill_name = normalize_skill_name(raw_skill_name) + + if not skill_name: + print("[ERROR] Skill name must include at least one letter or digit.") + sys.exit(1) + + if len(skill_name) > MAX_SKILL_NAME_LENGTH: + print( + f"[ERROR] Skill name '{skill_name}' is too long ({len(skill_name)} characters). " + f"Maximum is {MAX_SKILL_NAME_LENGTH} characters." + ) + sys.exit(1) + + if skill_name != raw_skill_name: + print(f"Note: Normalized skill name from '{raw_skill_name}' to '{skill_name}'") + + resources = parse_resources(args.resources) + + print(f"Creating skill: {skill_name}") + print(f" Location: {MANAGED_SKILLS_DIR / skill_name}") + if resources: + print(f" Resources: {', '.join(resources)}") + print() + + result = init_skill( + skill_name=skill_name, + description=args.description, + emoji=args.emoji, + tag=args.tag, + instructions=args.instructions, + resources=resources + ) + + sys.exit(0 if result else 1) + + +if __name__ == "__main__": + main()