$npx -y skills add aidityasadhakim/ads-creative-skill --skill template-generateGenerate an image from a filled prompt template using viostudio.id API (nano-banana-2). Takes a template ID (1-40). Uploads brand reference images, submits generation, polls for completion, and saves the result log.
| 1 | # /ads-creative:template-generate |
| 2 | |
| 3 | Generate an image from a filled prompt template via viostudio.id API. |
| 4 | |
| 5 | ## Input |
| 6 | |
| 7 | `$ARGUMENTS` — template number, 1 through 40. |
| 8 | |
| 9 | If `$ARGUMENTS` is empty or not a valid number, ask: |
| 10 | > Template nomor berapa yang ingin di-generate? (1-40) |
| 11 | > Contoh: `/ads-creative:template-generate 5` |
| 12 | |
| 13 | --- |
| 14 | |
| 15 | ## Pre-checks |
| 16 | |
| 17 | ### 1. API Key |
| 18 | |
| 19 | Check for `VIOSTUDIO_API_KEY` environment variable. |
| 20 | |
| 21 | If not set, ask the user: |
| 22 | > ⚠️ API key viostudio.id belum dikonfigurasi. |
| 23 | > |
| 24 | > Set environment variable sebelum menjalankan Claude Code: |
| 25 | > ```bash |
| 26 | > export VIOSTUDIO_API_KEY=vio_sk_xxxxx |
| 27 | > ``` |
| 28 | > Atau berikan API key-nya sekarang dan saya akan gunakan untuk sesi ini. |
| 29 | |
| 30 | If user provides a key inline, use it for this session. Then STOP until key is available. |
| 31 | |
| 32 | ### 2. Filled Prompts File |
| 33 | |
| 34 | Look for `*-prompts.md` files in the `output/` directory of the current working directory. |
| 35 | |
| 36 | If none found: |
| 37 | > ⚠️ Belum ada filled prompts. Jalankan dulu: |
| 38 | > `/ads-creative:template-builder [nama produk]` |
| 39 | |
| 40 | Then STOP. |
| 41 | |
| 42 | If multiple files found, ask user which one to use. |
| 43 | |
| 44 | ### 3. Parse the Requested Template |
| 45 | |
| 46 | Read the prompts file and extract: |
| 47 | - **Brand Directory** — from the header line `**Brand Directory:** brands/{brand-name}/` |
| 48 | - **Full prompt text** — section matching `## Template $ARGUMENTS` (or `## Template $ARGUMENTS —`), everything under `### Prompt` until the next `---` |
| 49 | - **Aspect ratio** — scan the prompt text for patterns like `Rasio aspek 4:5`, `rasio 4:5`, `aspect ratio 4:5`, or `4:5`. Default to `1:1` if none found. |
| 50 | |
| 51 | ### 4. Reference Images |
| 52 | |
| 53 | **UGC templates — skip reference images entirely:** |
| 54 | |
| 55 | 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`: |
| 56 | |
| 57 | | Template | Name | |
| 58 | |----------|------| |
| 59 | | 8 | Before & After UGC | |
| 60 | | 20 | Advertorial / Editorial Content Card | |
| 61 | | 29 | UGC + Viral Post Overlay | |
| 62 | | 32 | UGC Story Callout | |
| 63 | | 36 | Whiteboard Before/After | |
| 64 | | 39 | Curiosity Gap / Scroll-Stopper | |
| 65 | |
| 66 | For all other templates, proceed with the reference image flow below. |
| 67 | |
| 68 | --- |
| 69 | |
| 70 | 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. |
| 71 | |
| 72 | If the Brand Directory is missing or no images are found: |
| 73 | > ⚠️ Tidak ada foto referensi ditemukan di `brands/{brand-name}/`. |
| 74 | > |
| 75 | > Tambahkan foto produk, logo, atau packaging ke folder tersebut sebelum generate. |
| 76 | > Format: JPEG atau PNG, maks. 20 MB per file. |
| 77 | > |
| 78 | > Contoh: |
| 79 | > ``` |
| 80 | > brands/{brand-name}/ |
| 81 | > ├── brand-dna.md |
| 82 | > ├── logo.png |
| 83 | > ├── product-front.jpg |
| 84 | > └── packaging.jpg |
| 85 | > ``` |
| 86 | |
| 87 | Then STOP. |
| 88 | |
| 89 | **Asset cache check:** |
| 90 | |
| 91 | Check if `brands/{brand-name}/assets.json` exists and read it if so. The file has this structure: |
| 92 | |
| 93 | ```json |
| 94 | { |
| 95 | "uploaded_at": "2026-03-24T15:00:00Z", |
| 96 | "assets": [ |
| 97 | {"filename": "logo.png", "asset_id": 123, "url": "https://s3.viostudio.id/..."}, |
| 98 | {"filename": "product-front.jpg", "asset_id": 124, "url": "https://s3.viostudio.id/..."} |
| 99 | ] |
| 100 | } |
| 101 | ``` |
| 102 | |
| 103 | Apply the following logic: |
| 104 | |
| 105 | 1. **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. |
| 106 | |
| 107 | 2. **Cache exists and is fresh:** |
| 108 | - Build a map of `filename → asset_id` from the cache |
| 109 | - For each image file found in the Brand Directory: |
| 110 | - If filename exists in the cache → use the cached `asset_id`, skip upload |
| 111 | - If filename is **not** in the cache (new file added) → upload it, collect the new `asset_id` |
| 112 | - Ignore cache entries whose filename no longer exists in the directory (file was deleted) |
| 113 | - If any new files were uploaded, update `assets.json` with the new entries (keep existing ones, append new ones, update `uploaded_at` to now) |
| 114 | |
| 115 | 3. **Cache does not exist:** Upload all images, create `assets.json`. |
| 116 | |
| 117 | **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. |
| 118 | |
| 119 | After resolving asset IDs, report to user: |
| 120 | > 📸 [N] foto referensi siap (X dari cache, Y di-upload baru): |
| 121 | > - logo.png → asset ID 123 (cached) |
| 122 | > - product-front.jpg → asset ID 124 (cached) |
| 123 | > - new-photo.jpg → asset ID 125 (uploaded) |
| 124 | |
| 125 | ### 5. Check Credits |
| 126 | |
| 127 | ```bash |
| 128 | curl -s -X GET "https://api.viostudio.id/v1/account/credits" \ |
| 129 | -H "Authorization: Bearer $VIOSTUDIO_API_KEY" |
| 130 | ``` |
| 131 | |
| 132 | - If the response indicates insufficient credits (`total_credits` < 3): |
| 133 | > ⚠️ Credits tidak cukup untuk generate (butuh minimal 3 cre |