From ads-creative
Generates images from filled prompt templates (1-40) using viostudio.id API (nano-banana-2). Uploads brand reference images (skips UGC templates), submits generation, polls completion, saves result log. Needs VIOSTUDIO_API_KEY and *-prompts.md.
npx claudepluginhub aidityasadhakim/ads-creative-skill --plugin ads-creativeThis skill uses the workspace's default tool permissions.
Generate an image from a filled prompt template via viostudio.id API.
Fills ad creative prompt templates with brand DNA for products, generating ready-to-use image prompts. Handles single template or all 40, saves to Markdown output.
Generates and edits AI images using Google Gemini Nano Banana models. Orchestrates text-to-image, image editing, batch workflows, presets, and creative sessions via /banana or auto-triggers on image requests.
Guides creation of production-quality image prompts for Nano Banana AI generator through brand selection, concept exploration, crafting, and iterative revision. Activates on 'create image prompt' or blog image requests.
Share bugs, ideas, or general feedback.
Generate an image from a filled prompt template via viostudio.id API.
$ARGUMENTS — template number, 1 through 40.
If $ARGUMENTS is empty or not a valid number, ask:
Template nomor berapa yang ingin di-generate? (1-40) Contoh:
/ads-creative:template-generate 5
Check for VIOSTUDIO_API_KEY environment variable.
If not set, ask the user:
⚠️ API key viostudio.id belum dikonfigurasi.
Set environment variable sebelum menjalankan Claude Code:
export VIOSTUDIO_API_KEY=vio_sk_xxxxxAtau berikan API key-nya sekarang dan saya akan gunakan untuk sesi ini.
If user provides a key inline, use it for this session. Then STOP until key is available.
Look for *-prompts.md files in the output/ directory of the current working directory.
If none found:
⚠️ Belum ada filled prompts. Jalankan dulu:
/ads-creative:template-builder [nama produk]
Then STOP.
If multiple files found, ask user which one to use.
Read the prompts file and extract:
**Brand Directory:** brands/{brand-name}/## Template $ARGUMENTS (or ## Template $ARGUMENTS —), everything under ### Prompt until the next ---Rasio aspek 4:5, rasio 4:5, aspect ratio 4:5, or 4:5. Default to 1:1 if none found.UGC templates — skip reference images entirely:
The following templates are UGC/native style where the product is not shown and reference images cause a "Gambar referensi tidak valid" rejection from the model. For these templates, skip all image upload and asset steps and go directly to Step 2 (Submit Generation) with no reference_asset_ids:
| Template | Name |
|---|---|
| 8 | Before & After UGC |
| 20 | Advertorial / Editorial Content Card |
| 29 | UGC + Viral Post Overlay |
| 32 | UGC Story Callout |
| 36 | Whiteboard Before/After |
| 39 | Curiosity Gap / Scroll-Stopper |
For all other templates, proceed with the reference image flow below.
Scan the Brand Directory parsed above for image files with extensions: .jpg, .jpeg, .png (case-insensitive). Exclude assets.json and brand-dna.md from the scan.
If the Brand Directory is missing or no images are found:
⚠️ Tidak ada foto referensi ditemukan di
brands/{brand-name}/.Tambahkan foto produk, logo, atau packaging ke folder tersebut sebelum generate. Format: JPEG atau PNG, maks. 20 MB per file.
Contoh:
brands/{brand-name}/ ├── brand-dna.md ├── logo.png ├── product-front.jpg └── packaging.jpg
Then STOP.
Asset cache check:
Check if brands/{brand-name}/assets.json exists and read it if so. The file has this structure:
{
"uploaded_at": "2026-03-24T15:00:00Z",
"assets": [
{"filename": "logo.png", "asset_id": 123, "url": "https://s3.viostudio.id/..."},
{"filename": "product-front.jpg", "asset_id": 124, "url": "https://s3.viostudio.id/..."}
]
}
Apply the following logic:
Cache is stale (older than 25 days): Ignore the cache entirely — re-upload all images. The viostudio.id API expires assets after 30 days; the 25-day threshold gives a 5-day safety buffer.
Cache exists and is fresh:
filename → asset_id from the cacheasset_id, skip uploadasset_idassets.json with the new entries (keep existing ones, append new ones, update uploaded_at to now)Cache does not exist: Upload all images, create assets.json.
To force a full re-upload (e.g. user replaced a file with the same name), the user can delete assets.json and re-run.
After resolving asset IDs, report to user:
📸 [N] foto referensi siap (X dari cache, Y di-upload baru):
- logo.png → asset ID 123 (cached)
- product-front.jpg → asset ID 124 (cached)
- new-photo.jpg → asset ID 125 (uploaded)
curl -s -X GET "https://api.viostudio.id/v1/account/credits" \
-H "Authorization: Bearer $VIOSTUDIO_API_KEY"
If the response indicates insufficient credits (total_credits < 3):
⚠️ Credits tidak cukup untuk generate (butuh minimal 3 credits untuk nano-banana-2). Sisa credits: [total_credits]
Then STOP.
If credits are sufficient, continue.
Only upload image files that were not resolved from the cache in Pre-check 4. Skip this step entirely if all images were served from cache.
For each image that needs uploading:
curl -s -X POST "https://api.viostudio.id/v1/assets" \
-H "Authorization: Bearer $VIOSTUDIO_API_KEY" \
-F "file=@brands/{brand-name}/{filename}"
Response 201 Created:
{
"asset_id": 123,
"content_type": "image/jpeg",
"url": "https://s3.viostudio.id/assets/...",
"created_at": "..."
}
asset_id values and merge with cached IDs into a single arraybrands/{brand-name}/assets.json with all current entries and set uploaded_at to the current ISO timestampcurl -s -X POST "https://api.viostudio.id/v1/images/generate" \
-H "Authorization: Bearer $VIOSTUDIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "nano-banana-2",
"prompt": "[FULL PROMPT TEXT]",
"aspect_ratio": "[ASPECT RATIO]",
"reference_asset_ids": [123, 124, 125]
}'
Response 202 Accepted:
{
"generation_ids": [1001],
"status": "queued",
"credits_charged": 3,
"estimated_seconds": 30
}
generation_ids (array) — use the first ID for pollingcredits_charged from this responsePoll GET /v1/generations/{generation_id} using generation_ids[0]:
| Phase | Interval | Max attempts |
|---|---|---|
| Phase 1 | every 1s | 10 times (0–10s) |
| Phase 2 | every 5s | 10 times (10–60s) |
| Phase 3 | every 10s | until done or 10min total timeout |
curl -s -X GET "https://api.viostudio.id/v1/generations/{generation_id}" \
-H "Authorization: Bearer $VIOSTUDIO_API_KEY"
Response:
{
"id": 1001,
"status": "completed",
"asset_url": "https://s3.viostudio.id/generations/...",
"credits_charged": 3,
"error_message": null
}
Update the user between phases:
⏳ Status: [status] — [elapsed]s
Possible statuses: queued, processing, completed, failed
If status is failed, report error_message and STOP.
If 10 minutes pass without completion, report a timeout and STOP.
On completed:
asset_url from the poll responsecredits_charged from the initial 202 response (Step 2)asset_url (e.g. .jpg, .png). Default to .jpg if unclear.output/generated/template-{id}-{timestamp}{ext}:curl -s -L "{asset_url}" -o "output/generated/template-{id}-{timestamp}{ext}"
If the download fails (non-zero exit or empty file), warn the user but do not STOP — the image is still accessible via the URL.
Create directories if needed, then save to output/generated/template-{id}-{timestamp}.md:
# Generated: Template {id}
- **Template:** {template name}
- **Model:** nano-banana-2
- **Aspect Ratio:** {ratio}
- **Status:** completed
- **Credits Used:** {credits_charged}
- **Generated At:** {ISO timestamp}
- **Image URL:** {asset_url}
- **Local File:** output/generated/template-{id}-{timestamp}{ext}
- **Reference Images:** {comma-separated filenames} (asset IDs: {comma-separated IDs})
## Prompt Used
{full prompt text}
✅ Template {id} berhasil di-generate!
🖼️ Image URL: {asset_url}
💾 Saved to: output/generated/template-{id}-{timestamp}{ext}
💳 Credits used: {credits_charged}
📸 Reference images: {N} foto (asset IDs: {ids})
📄 Log: output/generated/template-{id}-{timestamp}.md
These are confirmed causes of generation rejection by nano-banana-2. If a generation fails, check the prompt against this list before retrying.
| Pattern | Why it fails | Fix |
|---|---|---|
| Mentioning real platform brands (Twitter, X, Reddit, Facebook, Instagram, TikTok) | Trademark filter | Use generic terms: "platform media sosial", "forum online", "postingan komunitas" |
| Fake social media UI specifics (username @handle, upvote counts, retweet numbers, timestamps like "2j") | Detected as fake screenshot generation | Use generic UI shapes: "kotak putih bersudut membulat", "nama pengguna anonim", "stempel waktu" |
| Reference image sent with UGC/selfie-style prompt | Reference image (product photo) conflicts with selfie/UGC style prompt | Skip reference_asset_ids for UGC templates (see list above) |
| Mentioning real publication brands (Daily Mail, Vogue, TODAY) in editorial templates | Trademark filter | Use generic: "publikasi media terkemuka", "majalah gaya hidup ternama" |
If a non-UGC template fails with an error that suggests image conflict or content rejection, automatically retry once without reference_asset_ids and notify the user:
⚠️ Generate gagal dengan referensi gambar. Mencoba ulang tanpa referensi...
If the retry succeeds, note it in the result log: **Reference Images:** none (retry without references).
| Endpoint | Method | Purpose |
|---|---|---|
/v1/account/credits | GET | Check remaining credits |
/v1/assets | POST | Upload reference image (multipart/form-data) |
/v1/images/generate | POST | Submit image generation |
/v1/generations/{id} | GET | Poll generation status |
Auth header: Authorization: Bearer $VIOSTUDIO_API_KEY
Default model: nano-banana-2 — 3 credits/image
Base URL: https://api.viostudio.id/v1
Max reference images: 10 (nano-banana-2)
Supported upload formats: JPEG, PNG (max 20 MB each)