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
This commit is contained in:
Jiang Bohan 2026-01-30 17:39:03 +08:00
parent b2d281c29d
commit f19e51da31
2 changed files with 349 additions and 178 deletions

View file

@ -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 <skill-name>
# 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 <skill-name>
```
### Step 3: Edit the Skill
After initialization, edit `~/.super-multica/skills/<skill-name>/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-name>
```
## 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/<skill-name>
```
2. Write the SKILL.md file at `~/.super-multica/skills/<skill-name>/SKILL.md`
3. If including scripts, create them at `~/.super-multica/skills/<skill-name>/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 <skill-name>
To remove a skill:
```bash
# Remove a skill
pnpm skills:cli remove <skill-name>
# or
rm -rf ~/.super-multica/skills/<skill-name>
```
Or use the CLI:
```bash
pnpm skills:cli remove <skill-name>
```
## 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/<id>/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.

View file

@ -0,0 +1,248 @@
#!/usr/bin/env python3
"""
Skill Initializer - Creates a new skill in the managed skills directory
Usage:
init_skill.py <skill-name> [--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/<skill-name>/
"""
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()