197 lines
4.8 KiB
Markdown
197 lines
4.8 KiB
Markdown
---
|
|
name: skill-creator
|
|
description: Create new OpenCode skills with proper structure, SKILL.md format, and script templates. Use this skill when you need to create a new OpenCode skill.
|
|
---
|
|
|
|
# Skill Creator
|
|
|
|
Guide and tools for creating new OpenCode skills.
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
python3 scripts/create_skill.py <skill-name> "<description>"
|
|
```
|
|
|
|
## SKILL.md Format (Required)
|
|
|
|
Every skill must have a `SKILL.md` file with YAML frontmatter:
|
|
|
|
```yaml
|
|
---
|
|
name: skill-name
|
|
description: Brief description. Use when user wants to [specific action].
|
|
---
|
|
|
|
# Skill Name
|
|
|
|
Brief explanation of what this skill does.
|
|
|
|
## Commands
|
|
|
|
| Command | Args | Description |
|
|
|---------|------|-------------|
|
|
| `command1` | `<arg>` | What it does |
|
|
|
|
## Options
|
|
|
|
| Option | Default | Range | Description |
|
|
|--------|---------|-------|-------------|
|
|
| `--option` | 100 | 1-1000 | What it does |
|
|
|
|
## Examples
|
|
|
|
```bash
|
|
python3 scripts/script.py command "arg" --option 50
|
|
```
|
|
|
|
## Output Format
|
|
|
|
- Success: `Result: filename [id]`
|
|
- Error: `Error: message` (to stderr)
|
|
|
|
## Notes
|
|
|
|
- Required environment variables
|
|
- Important constraints
|
|
```
|
|
|
|
## Frontmatter Rules
|
|
|
|
| Field | Required | Rules |
|
|
|-------|----------|-------|
|
|
| `name` | Yes | 1-64 chars, lowercase alphanumeric + hyphens, no leading/trailing/consecutive hyphens |
|
|
| `description` | Yes | 1-1024 chars, specific enough for agent to choose correctly |
|
|
| `license` | No | e.g., MIT |
|
|
| `compatibility` | No | e.g., opencode |
|
|
| `metadata` | No | String-to-string map |
|
|
|
|
## Directory Structure
|
|
|
|
```
|
|
skills/
|
|
└── skill-name/
|
|
├── SKILL.md # Required: skill definition
|
|
└── scripts/
|
|
├── main_script.py # Executable script
|
|
├── .env.example # Required: env var template
|
|
└── requirements.txt # Optional: Python deps
|
|
```
|
|
|
|
## Script Best Practices
|
|
|
|
### 1. Load Environment Variables
|
|
|
|
```python
|
|
def load_env():
|
|
env_path = Path(__file__).parent / ".env"
|
|
if env_path.exists():
|
|
for line in env_path.read_text().splitlines():
|
|
line = line.strip()
|
|
if line and not line.startswith("#") and "=" in line:
|
|
k, v = line.split("=", 1)
|
|
os.environ.setdefault(k.strip(), v.strip().strip("\"'"))
|
|
|
|
load_env()
|
|
API_TOKEN = os.environ.get("API_TOKEN")
|
|
```
|
|
|
|
### 2. Handle API Responses (Binary + JSON)
|
|
|
|
APIs may return raw binary or JSON with base64. Handle both:
|
|
|
|
```python
|
|
response = requests.post(url, headers=headers, json=payload, timeout=300)
|
|
response.raise_for_status()
|
|
|
|
content_type = response.headers.get("Content-Type", "")
|
|
|
|
if "image/" in content_type or "application/octet-stream" in content_type:
|
|
# Raw binary response
|
|
data = response.content
|
|
else:
|
|
# JSON with base64
|
|
result = response.json()
|
|
if isinstance(result, list) and len(result) > 0:
|
|
image_data = result[0].get("data", "")
|
|
if image_data.startswith("data:"):
|
|
data = base64.b64decode(image_data.split(",", 1)[1])
|
|
else:
|
|
data = base64.b64decode(image_data)
|
|
```
|
|
|
|
### 3. Send Base64 (Plain, Not Data URI)
|
|
|
|
Some APIs expect plain base64, not data URI:
|
|
|
|
```python
|
|
import base64
|
|
|
|
with open(image_path, "rb") as f:
|
|
image_bytes = f.read()
|
|
|
|
# Plain base64 (no data: prefix)
|
|
b64_string = base64.b64encode(image_bytes).decode("utf-8")
|
|
```
|
|
|
|
### 4. Output Format
|
|
|
|
Follow OpenCode conventions:
|
|
|
|
```python
|
|
# Success with ID
|
|
print(f"Result: {filename} [{timestamp}]")
|
|
|
|
# Error to stderr
|
|
print(f"Error: {message}", file=sys.stderr)
|
|
sys.exit(1)
|
|
```
|
|
|
|
### 5. CLI Arguments
|
|
|
|
Use argparse for clean CLI:
|
|
|
|
```python
|
|
parser = argparse.ArgumentParser(description="What this does")
|
|
parser.add_argument("required_arg", help="Description")
|
|
parser.add_argument("--optional", type=int, default=100, help="Description")
|
|
args = parser.parse_args()
|
|
```
|
|
|
|
## .env.example Template
|
|
|
|
```
|
|
# API credentials
|
|
# Get your token from https://service.com/account
|
|
#
|
|
# WARNING: Never commit actual credentials!
|
|
|
|
API_TOKEN=your_api_token_here
|
|
```
|
|
|
|
## Installation Paths
|
|
|
|
| Type | Path |
|
|
|------|------|
|
|
| Global | `~/.config/opencode/skills/<name>/SKILL.md` |
|
|
| Project | `./.opencode/skills/<name>/SKILL.md` |
|
|
|
|
## Common Issues
|
|
|
|
| Issue | Solution |
|
|
|-------|----------|
|
|
| 400 Bad Request | Check payload format - may need flat JSON, not nested |
|
|
| Skill not found | Verify path is `skills/<name>/SKILL.md` (plural "skills") |
|
|
| API token not loaded | Check .env is in same directory as script |
|
|
| Binary response fails | Check Content-Type header, handle raw bytes |
|
|
|
|
## Checklist for New Skills
|
|
|
|
- [ ] `SKILL.md` with required frontmatter (name, description)
|
|
- [ ] `scripts/` directory with main script
|
|
- [ ] `scripts/.env.example` with placeholder credentials
|
|
- [ ] `scripts/requirements.txt` if external deps needed
|
|
- [ ] Script handles both binary and JSON responses
|
|
- [ ] Output follows format: `Result: name [id]`
|
|
- [ ] Errors go to stderr with `sys.exit(1)`
|