Back to skills
SkillHub ClubAnalyze Data & AIFull StackData / AI

prompt-artist

Optimize and generate text-to-image prompts for AI art platforms. Use when a user wants to: (1) Optimize prompts for Midjourney, Nano Banana, Dreamina, Qwen image generation, (2) Translate simple descriptions into professional prompts, (3) Generate platform-specific prompt variations, (4) Add style/lighting/composition modifiers, (5) Create negative prompts. Supports Chinese/English input. Integrates SkillPay.me at 0.005 USDT/call.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3,087
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
B71.9

Install command

npx @skill-hub/cli install openclaw-skills-prompt-artist

Repository

openclaw/skills

Skill path: skills/elevo11/prompt-artist

Optimize and generate text-to-image prompts for AI art platforms. Use when a user wants to: (1) Optimize prompts for Midjourney, Nano Banana, Dreamina, Qwen image generation, (2) Translate simple descriptions into professional prompts, (3) Generate platform-specific prompt variations, (4) Add style/lighting/composition modifiers, (5) Create negative prompts. Supports Chinese/English input. Integrates SkillPay.me at 0.005 USDT/call.

Open repository

Best for

Primary workflow: Analyze Data & AI.

Technical facets: Full Stack, Data / AI.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: openclaw.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install prompt-artist into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/openclaw/skills before adding prompt-artist to shared team environments
  • Use prompt-artist for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: prompt-artist
description: >
  Optimize and generate text-to-image prompts for AI art platforms. Use when a user wants to:
  (1) Optimize prompts for Midjourney, Nano Banana, Dreamina, Qwen image generation,
  (2) Translate simple descriptions into professional prompts, (3) Generate platform-specific
  prompt variations, (4) Add style/lighting/composition modifiers, (5) Create negative prompts.
  Supports Chinese/English input. Integrates SkillPay.me at 0.005 USDT/call.
---

# Prompt Artist

AI art prompt optimizer for Midjourney, Nano Banana, Dreamina, Qwen. 0.005 USDT/call.

## Commands

| Command | Script | Description |
|:---|:---|:---|
| **optimize** | `scripts/optimize.py` | Optimize a prompt for target platform |
| **multi** | `scripts/multi_platform.py` | Generate prompts for all platforms at once |
| **style** | `scripts/style_library.py` | Browse/apply art styles |
| **history** | `scripts/history.py` | Prompt history + favorites (NEW) |
| **billing** | `scripts/billing.py` | SkillPay charge/balance/payment |

## Workflow

```
1. Billing:   python3 scripts/billing.py --charge --user-id <id>
2. Optimize:  python3 scripts/optimize.py --prompt "一只猫在月光下" --platform midjourney
3. Multi:     python3 scripts/multi_platform.py --prompt "sunset over mountains"
4. Styles:    python3 scripts/style_library.py --list
```

## Examples

```bash
# Optimize for Midjourney
python3 scripts/optimize.py --prompt "一只猫在月光下" --platform midjourney --style cinematic

# Optimize for Dreamina
python3 scripts/optimize.py --prompt "cyberpunk city" --platform dreamina --ratio 16:9

# All platforms at once
python3 scripts/multi_platform.py --prompt "beautiful girl in garden" --style anime

# List styles
python3 scripts/style_library.py --list
python3 scripts/style_library.py --category photography
```

## Config

| Env Var | Required | Description |
|:---|:---:|:---|
| `SKILLPAY_API_KEY` | Yes | SkillPay.me API key |

## References

See `references/platform-specs.md` for platform-specific prompt syntax and limits.


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### scripts/billing.py

```python
#!/usr/bin/env python3
"""SkillPay.me billing for prompt-artist."""

import json, sys, argparse, os
import urllib.request, urllib.error

API = "https://skillpay.me/api/v1"
SKILL_ID = "66a1cf1e-9177-4fd9-a417-8917978118e3"


def _key(o=None):
    return o or os.environ.get("SKILLPAY_API_KEY")

def _post(path, body, key):
    req = urllib.request.Request(f"{API}{path}", data=json.dumps(body).encode(),
        headers={"Content-Type": "application/json", "X-API-Key": key}, method="POST")
    try:
        with urllib.request.urlopen(req, timeout=15) as r: return json.loads(r.read())
    except urllib.error.HTTPError as e: return json.loads(e.read())

def _get(path, key):
    req = urllib.request.Request(f"{API}{path}", headers={"X-API-Key": key})
    try:
        with urllib.request.urlopen(req, timeout=15) as r: return json.loads(r.read())
    except urllib.error.HTTPError as e: return json.loads(e.read())

def charge(uid, amount=0.005, key=None):
    k = _key(key)
    if not k: return {"success": False, "error": "SKILLPAY_API_KEY not set"}
    d = _post("/billing/charge", {"user_id": uid, "skill_id": SKILL_ID, "amount": amount,
        "currency": "USDT", "description": "Prompt Artist"}, k)
    if d.get("success"): return {"success": True, "charged": amount, "data": d}
    return {"success": False, "needs_payment": bool(d.get("payment_url")),
        "payment_url": d.get("payment_url", ""), "balance": d.get("balance", 0),
        "message": d.get("message", "charge failed")}

def balance(uid, key=None):
    k = _key(key)
    if not k: return {"success": False, "error": "SKILLPAY_API_KEY not set"}
    return {"success": True, "data": _get(f"/billing/balance?user_id={uid}", k)}

def payment_link(uid, amount=5.0, key=None):
    k = _key(key)
    if not k: return {"success": False, "error": "SKILLPAY_API_KEY not set"}
    return {"success": True, "data": _post("/billing/payment-link",
        {"user_id": uid, "skill_id": SKILL_ID, "Amount": amount}, k)}

if __name__ == "__main__":
    p = argparse.ArgumentParser()
    p.add_argument("--user-id", required=True)
    p.add_argument("--amount", type=float, default=0.005)
    p.add_argument("--api-key", default=None)
    g = p.add_mutually_exclusive_group()
    g.add_argument("--charge", action="store_true", default=True)
    g.add_argument("--balance", action="store_true")
    g.add_argument("--payment-link", action="store_true")
    a = p.parse_args()
    if a.balance: r = balance(a.user_id, a.api_key)
    elif a.payment_link: r = payment_link(a.user_id, a.amount or 5.0, a.api_key)
    else: r = charge(a.user_id, a.amount, a.api_key)
    print(json.dumps(r, indent=2, ensure_ascii=False))
    sys.exit(0 if r.get("success") else 1)

```

### scripts/optimize.py

```python
#!/usr/bin/env python3
"""Optimize text-to-image prompts for various AI art platforms."""

import argparse, json, sys, re

# Style modifiers library
STYLES = {
    "cinematic": "cinematic lighting, dramatic atmosphere, film grain, anamorphic lens, movie still",
    "anime": "anime style, cel shading, vibrant colors, detailed eyes, studio ghibli inspired",
    "photorealistic": "photorealistic, 8k uhd, DSLR, high detail, sharp focus, professional photography",
    "oil_painting": "oil painting, brush strokes, canvas texture, classical art, rich colors, masterpiece",
    "watercolor": "watercolor painting, soft edges, color bleeding, paper texture, delicate",
    "cyberpunk": "cyberpunk, neon lights, futuristic, rain, holographic, dark atmosphere, blade runner",
    "fantasy": "fantasy art, magical, ethereal glow, mystical atmosphere, detailed, epic composition",
    "minimalist": "minimalist, clean design, simple composition, negative space, modern aesthetic",
    "vintage": "vintage, retro, faded colors, film photography, nostalgic, 1970s aesthetic",
    "3d_render": "3D render, octane render, C4D, volumetric lighting, smooth, high quality",
    "pixel_art": "pixel art, 16-bit, retro gaming, sprite art, low resolution charm",
    "sketch": "pencil sketch, hand drawn, detailed linework, graphite, artistic",
    "pop_art": "pop art, Andy Warhol style, bold colors, halftone dots, graphic design",
    "surreal": "surrealism, Salvador Dali inspired, dreamlike, impossible geometry, melting",
    "ukiyo_e": "ukiyo-e, Japanese woodblock print, traditional art, Hokusai style",
    "ink_wash": "Chinese ink wash painting, 水墨画, traditional, elegant, flowing brush strokes",
}

# Quality boosters per platform
QUALITY = {
    "midjourney": "masterpiece, best quality, highly detailed, sharp focus, 8k resolution",
    "dreamina": "高清, 精细, 大师级, 高质量, 细节丰富",
    "nano_banana": "best quality, masterpiece, ultra detailed, high resolution",
    "qwen": "高质量, 精美, 细节丰富, 专业级",
}

# Negative prompts per platform
NEGATIVES = {
    "midjourney": "--no blurry, low quality, distorted, deformed, ugly, bad anatomy",
    "dreamina": "低质量, 模糊, 变形, 丑陋, 解剖错误",
    "nano_banana": "blurry, low quality, distorted, deformed, bad anatomy, watermark, text",
    "qwen": "低质量, 模糊, 变形, 水印, 文字",
}

# Platform-specific formatting
PLATFORM_CONFIG = {
    "midjourney": {
        "name": "Midjourney",
        "icon": "🎨",
        "lang": "en",
        "max_len": 6000,
        "suffix_template": " --ar {ratio} --v 6.1 --q 2",
        "tips": ["Use :: for multi-prompts", "Use --ar for aspect ratio", "Use --s for stylize value"],
    },
    "dreamina": {
        "name": "极梦 Dreamina",
        "icon": "🌈",
        "lang": "zh",
        "max_len": 2000,
        "suffix_template": "",
        "tips": ["支持中英文混合", "可指定画面比例", "使用逗号分隔关键词"],
    },
    "nano_banana": {
        "name": "Nano Banana",
        "icon": "🍌",
        "lang": "en",
        "max_len": 4000,
        "suffix_template": "",
        "tips": ["Use detailed descriptions", "Separate concepts with commas", "Add quality tags"],
    },
    "qwen": {
        "name": "Qwen",
        "icon": "🤖",
        "lang": "zh",
        "max_len": 2000,
        "suffix_template": "",
        "tips": ["支持中文自然语言描述", "可添加风格关键词", "描述越详细效果越好"],
    },
}


def detect_language(text):
    """Detect if text is primarily Chinese or English."""
    chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', text))
    return "zh" if chinese_chars > len(text) * 0.3 else "en"


def enhance_prompt(prompt, platform="midjourney", style=None, ratio="1:1", quality=True):
    """Enhance a basic prompt into an optimized one for the target platform."""
    config = PLATFORM_CONFIG.get(platform, PLATFORM_CONFIG["midjourney"])
    parts = []

    # Main prompt
    parts.append(prompt.strip())

    # Add style
    if style and style in STYLES:
        parts.append(STYLES[style])

    # Add quality boosters
    if quality and platform in QUALITY:
        parts.append(QUALITY[platform])

    # Combine
    enhanced = ", ".join(parts)

    # Platform-specific suffix
    suffix = config["suffix_template"].format(ratio=ratio)
    enhanced += suffix

    # Truncate if needed
    if len(enhanced) > config["max_len"]:
        enhanced = enhanced[:config["max_len"] - 3] + "..."

    # Negative prompt
    negative = NEGATIVES.get(platform, "")

    return {
        "platform": config["name"],
        "icon": config["icon"],
        "original": prompt,
        "optimized": enhanced,
        "negative": negative,
        "style": style,
        "ratio": ratio,
        "length": len(enhanced),
        "max_length": config["max_len"],
        "tips": config["tips"],
    }


def format_output(data):
    lines = [f"{data['icon']} {data['platform']} 优化提示词", ""]
    lines.append(f"📝 原始: {data['original']}")
    lines.append("")
    lines.append(f"✨ 优化后:")
    lines.append(f"```")
    lines.append(data["optimized"])
    lines.append(f"```")
    if data["negative"]:
        lines.append(f"\n🚫 反向提示:")
        lines.append(f"```")
        lines.append(data["negative"])
        lines.append(f"```")
    lines.append(f"\n📐 比例: {data['ratio']} | 字数: {data['length']}/{data['max_length']}")
    if data["style"]:
        lines.append(f"🎨 风格: {data['style']}")
    lines.append(f"\n💡 技巧:")
    for t in data["tips"]:
        lines.append(f"  • {t}")
    return "\n".join(lines)


if __name__ == "__main__":
    p = argparse.ArgumentParser()
    p.add_argument("--prompt", required=True, help="Original prompt text")
    p.add_argument("--platform", choices=list(PLATFORM_CONFIG.keys()), default="midjourney")
    p.add_argument("--style", default=None, help="Art style name")
    p.add_argument("--ratio", default="1:1", help="Aspect ratio (e.g. 16:9)")
    p.add_argument("--no-quality", action="store_true", help="Skip quality boosters")
    p.add_argument("--json", action="store_true")
    a = p.parse_args()
    data = enhance_prompt(a.prompt, a.platform, a.style, a.ratio, not a.no_quality)
    print(json.dumps(data, indent=2, ensure_ascii=False) if a.json else format_output(data))

```

### scripts/multi_platform.py

```python
#!/usr/bin/env python3
"""Generate optimized prompts for all platforms at once."""

import argparse, json, sys
from optimize import enhance_prompt, PLATFORM_CONFIG, STYLES


def multi_optimize(prompt, style=None, ratio="1:1"):
    results = []
    for platform in PLATFORM_CONFIG:
        data = enhance_prompt(prompt, platform, style, ratio)
        results.append(data)
    return {"original": prompt, "style": style, "ratio": ratio, "platforms": results}


def format_output(data):
    lines = [f"🎨 多平台提示词优化", f"📝 原始: {data['original']}", ""]
    if data["style"]:
        lines.append(f"🖌️ 风格: {data['style']}")
    lines.append(f"📐 比例: {data['ratio']}")
    lines.append("")

    for p in data["platforms"]:
        lines.append(f"{'='*50}")
        lines.append(f"{p['icon']} **{p['platform']}**")
        lines.append(f"```")
        lines.append(p["optimized"])
        lines.append(f"```")
        if p["negative"]:
            lines.append(f"🚫 反向: {p['negative']}")
        lines.append("")

    return "\n".join(lines)


if __name__ == "__main__":
    p = argparse.ArgumentParser()
    p.add_argument("--prompt", required=True)
    p.add_argument("--style", default=None)
    p.add_argument("--ratio", default="1:1")
    p.add_argument("--json", action="store_true")
    a = p.parse_args()
    data = multi_optimize(a.prompt, a.style, a.ratio)
    print(json.dumps(data, indent=2, ensure_ascii=False) if a.json else format_output(data))

```

### scripts/style_library.py

```python
#!/usr/bin/env python3
"""Art style library browser."""

import argparse, json, sys

CATEGORIES = {
    "photography": {
        "name": "📷 摄影风格",
        "styles": {
            "cinematic": "电影感 — 戏剧性灯光, 浅景深, 电影质感",
            "photorealistic": "超写实 — 8K, DSLR, 专业摄影",
            "vintage": "复古 — 胶片感, 褪色, 怀旧",
            "portrait": "人像 — 柔光, 散景, 特写",
            "landscape": "风景 — 广角, 黄金时刻, 壮丽",
        }
    },
    "illustration": {
        "name": "🖌️ 插画风格",
        "styles": {
            "anime": "动漫 — 赛璐璐, 鲜艳色彩, 日系",
            "watercolor": "水彩 — 柔和边缘, 纸质感",
            "oil_painting": "油画 — 笔触, 画布质感, 古典",
            "sketch": "素描 — 铅笔, 线条, 石墨",
            "ink_wash": "水墨 — 中国传统, 写意",
        }
    },
    "digital_art": {
        "name": "💻 数字艺术",
        "styles": {
            "3d_render": "3D渲染 — Octane, C4D, 体积光",
            "pixel_art": "像素画 — 16-bit, 复古游戏",
            "cyberpunk": "赛博朋克 — 霓虹, 未来, 暗黑",
            "fantasy": "奇幻 — 魔法, 史诗, 神秘",
            "surreal": "超现实 — 达利风格, 梦幻",
        }
    },
    "traditional": {
        "name": "🏛️ 传统艺术",
        "styles": {
            "ukiyo_e": "浮世绘 — 日本木版画",
            "ink_wash": "水墨画 — 中国传统",
            "pop_art": "波普艺术 — 安迪沃霍尔",
            "minimalist": "极简 — 简洁, 留白",
        }
    },
}


def list_styles(category=None):
    if category and category in CATEGORIES:
        cats = {category: CATEGORIES[category]}
    else:
        cats = CATEGORIES
    return cats


def format_output(cats):
    lines = ["🎨 风格库", ""]
    for key, cat in cats.items():
        lines.append(f"{cat['name']}")
        for style, desc in cat["styles"].items():
            lines.append(f"  • {style}: {desc}")
        lines.append("")
    lines.append(f"共 {sum(len(c['styles']) for c in cats.values())} 种风格")
    lines.append("使用: --style <style_name> 应用风格")
    return "\n".join(lines)


if __name__ == "__main__":
    p = argparse.ArgumentParser()
    p.add_argument("--list", action="store_true", help="List all styles")
    p.add_argument("--category", default=None, choices=list(CATEGORIES.keys()))
    p.add_argument("--json", action="store_true")
    a = p.parse_args()
    data = list_styles(a.category)
    if a.json:
        print(json.dumps(data, indent=2, ensure_ascii=False, default=str))
    else:
        print(format_output(data))

```

### scripts/history.py

```python
#!/usr/bin/env python3
"""Prompt history and favorites management."""

import argparse, json, os, sys
from datetime import datetime

HISTORY_FILE = os.path.expanduser("~/.openclaw/workspace/prompt-artist/data/history.json")


def load():
    os.makedirs(os.path.dirname(HISTORY_FILE), exist_ok=True)
    if os.path.exists(HISTORY_FILE):
        return json.load(open(HISTORY_FILE))
    return {"history": [], "favorites": []}


def save(data):
    os.makedirs(os.path.dirname(HISTORY_FILE), exist_ok=True)
    json.dump(data, open(HISTORY_FILE, "w"), indent=2, ensure_ascii=False)


def add_to_history(original, optimized, platform, style=None, ratio="1:1"):
    data = load()
    entry = {
        "id": len(data["history"]) + 1,
        "original": original,
        "optimized": optimized,
        "platform": platform,
        "style": style,
        "ratio": ratio,
        "created": datetime.now().isoformat(),
        "favorite": False,
    }
    data["history"].append(entry)
    # Keep last 100
    data["history"] = data["history"][-100:]
    save(data)
    return entry


def add_favorite(item_id):
    data = load()
    for item in data["history"]:
        if item["id"] == item_id:
            item["favorite"] = True
            data["favorites"].append(item)
            save(data)
            return item
    return None


def remove_favorite(item_id):
    data = load()
    for item in data["history"]:
        if item["id"] == item_id:
            item["favorite"] = False
    data["favorites"] = [f for f in data["favorites"] if f["id"] != item_id]
    save(data)


def show(limit=20, favorites_only=False):
    data = load()
    if favorites_only:
        return {"favorites": data["favorites"]}
    return {"history": data["history"][-limit:]}


def format_output(data, favorites_only=False):
    items = data.get("favorites", []) if favorites_only else data.get("history", [])
    title = "⭐ 收藏提示词" if favorites_only else "📜 提示词历史"
    
    lines = [title, ""]
    for item in items:
        fav_icon = "⭐" if item.get("favorite") else "  "
        lines.append(f"{fav_icon} [{item['id']}] {item['platform']} - {item['original'][:40]}")
        lines.append(f"    ✨ {item['optimized'][:80]}...")
        if item.get("style"):
            lines.append(f"    🎨 风格:{item['style']}")
        lines.append(f"    📅 {item['created'][:10]}")
        lines.append("")
    
    if not items:
        lines.append("  (空列表)")
    
    return "\n".join(lines)


if __name__ == "__main__":
    p = argparse.ArgumentParser()
    p.add_argument("--action", choices=["show", "add", "favorite", "unfavorite", "favorites"], default="show")
    p.add_argument("--id", type=int, default=None)
    p.add_argument("--original", default=None)
    p.add_argument("--optimized", default=None)
    p.add_argument("--platform", default=None)
    p.add_argument("--style", default=None)
    p.add_argument("--ratio", default="1:1")
    p.add_argument("--limit", type=int, default=20)
    p.add_argument("--json", action="store_true")
    a = p.parse_args()
    
    if a.action == "add":
        r = add_to_history(a.original, a.optimized, a.platform, a.style, a.ratio)
        print(f"✅ 已保存:[{r['id']}] {r['original'][:30]}")
    elif a.action == "favorite":
        r = add_favorite(a.id)
        if r:
            print(f"⭐ 已收藏:[{a.id}]")
        else:
            print(f"❌ 未找到 #{a.id}")
    elif a.action == "unfavorite":
        remove_favorite(a.id)
        print(f"✅ 已取消收藏:#{a.id}")
    elif a.action == "favorites":
        data = show(favorites_only=True)
        print(json.dumps(data, indent=2) if a.json else format_output(data, favorites_only=True))
    else:
        data = show(a.limit)
        print(json.dumps(data, indent=2) if a.json else format_output(data))

```

### references/platform-specs.md

```markdown
# Platform Specifications

## Midjourney 🎨
- **Language**: English preferred
- **Max length**: ~6000 chars
- **Params**: `--ar` (ratio), `--v` (version), `--q` (quality), `--s` (stylize), `--no` (negative)
- **Multi-prompt**: Use `::` to separate concepts with weights
- **Best practices**: Descriptive nouns > verbs, specify camera/lens, add lighting

## Dreamina 极梦 🌈
- **Language**: Chinese/English
- **Max length**: ~2000 chars
- **Features**: Style presets, ratio selection, batch generation
- **Best practices**: 中文关键词效果好, 逗号分隔, 可指定艺术家风格

## Nano Banana 🍌
- **Language**: English preferred
- **Max length**: ~4000 chars
- **Features**: Quality tags, negative prompts, seed control
- **Best practices**: Use quality tags (masterpiece, best quality), comma-separated

## Qwen 🤖
- **Language**: Chinese/English
- **Max length**: ~2000 chars
- **Features**: Natural language input, style keywords
- **Best practices**: 自然语言描述, 越详细越好, 可加风格关键词

```



---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### _meta.json

```json
{
  "owner": "elevo11",
  "slug": "prompt-artist",
  "displayName": "Prompt Artist",
  "latest": {
    "version": "1.1.0",
    "publishedAt": 1772787436252,
    "commit": "https://github.com/openclaw/skills/commit/2736205bf5422c6b107dcb908a4384434db54698"
  },
  "history": [
    {
      "version": "1.0.0",
      "publishedAt": 1772782738200,
      "commit": "https://github.com/openclaw/skills/commit/b49108ed0f65127b894d5abc9d7bd26b27ee8dc6"
    }
  ]
}

```