[ CREATIVE ]
/reference-ads
Generate ad creatives using the client's OWN existing designs from Google Drive as style references for Krea.ai.
ACME Agency, <id> and you@example.com mark values that are per-agency — your install fills them with YOUR clients and accounts. If a section references a helper script you don't have yet, it ships with that workflow's install./reference-ads
Generate production-ready ad creatives by cloning the visual style of the client's existing designs. Instead of building from templates (like /static-ad-generator), this skill browses the client's Google Drive folder, uses vision to pick the best 2-3 existing designs as references, and passes them to Krea.ai so the output matches the actual brand identity — colors, typography, composition, mood.
vs /static-ad-generator: static-ad-generator uses 40 pre-built prompt templates and writes everything from scratch. Good for clients with no design history. reference-ads uses the client's OWN designs as the creative template — good for clients where the design team has already established a visual language.
vs /krea-create-images: krea-create-images requires the user to manually provide a reference URL. reference-ads automates the entire reference discovery flow — browse Drive, evaluate candidates, pick the best, upload as public URLs, build the prompts.
With /copywrite: Pass --with-copywriter to delegate copy generation to the copywriter agent (self-test loop, HR/BS/DE/EN). Without the flag, the skill writes copy inline.
Triggers
/reference-ads "ACME Agency"— full pipeline with 3 concepts (default)/reference-ads "ACME Agency" --concepts 5— 5 different ad concepts/reference-ads "ACME Agency" --refresh— force fresh reference selection (ignore cache)/reference-ads "ACME Agency" --ratio 1:1 --batch 2— square format, 2 variants per concept/reference-ads "ACME Agency" --with-copywriter— delegate copy to the copywriter agent
Preflight
Run ALL checks before any expensive operations. If ANY check fails, abort with the specific failure.
- Client exists in
ACME Agency/clients/clients.json. Resolve the canonical key (case-insensitive). drive_folder_idis present and reachable. Test with alistFiles()call. If missing or unreachable → abort: "This skill requires a client with designs in Drive. Use/static-ad-generatorfor template-based generation instead."CLIENT.mdexists atACME Agency/clients/<Client>/CLIENT.md. If missing → warn but continue (reference images provide visual context, and the skill can still work without it).- Krea API key set in
.env(KREA_API_KEY). If missing → abort. - concepts x batch stays sane:
concepts * batch <= 16. If higher → require explicit confirmation. - Slack channel resolves via
getClientChannel(). Skip Slack cleanly if bot not invited. - Disk space: at least 200 MB free in the local client folder.
If all checks pass, log: "preflight: OK (<client>, <concepts> concepts x <batch> batch = <total> images, ratio <ratio>)"
Phase 1 — Reference Discovery & Selection
Goal: Find the client's best 2-3 existing designs in Drive and prepare them as Krea reference images.
Step 1.1 — Check cache
Look for ACME Agency/clients/<Client>/reference_selection.json. If it exists:
- Parse it. Check
selected_attimestamp. - If same calendar month AND
--refreshwas NOT passed → skip to Step 1.6 (reuse cached references). - If different month OR
--refreshwas passed → continue to Step 1.2 (fresh selection).
Step 1.2 — Browse Drive folder
Use google_drive.mjs to recursively discover design images:
node --input-type=module <<'EOF'
import { listFiles, downloadFile } from './ACME Agency/scripts/lib/google_drive.mjs';
import { mkdir } from 'fs/promises';
const FOLDER_ID = '<drive_folder_id>';
const CLIENT = '<ClientName>';
// List top-level contents
const topLevel = await listFiles(FOLDER_ID);
console.log(`Top-level: ${topLevel.length} items`);
// Find design-related subfolders (max depth 2)
const DESIGN_NAMES = /social.?media|dizajn|design|kreativ|galerij|gallery|oglas|ads|vizual/i;
const designFolders = topLevel.filter(f =>
f.mimeType.includes('folder') && DESIGN_NAMES.test(f.name)
);
// Collect image files from design folders + root
let allImages = [];
// Root-level images
const rootImages = (await listFiles(FOLDER_ID, { mimeType: 'image' }))
.filter(f => !f.name.startsWith('.'));
allImages.push(...rootImages);
// Subfolder images (up to depth 2)
for (const folder of designFolders) {
const subItems = await listFiles(folder.id);
// Direct images in this folder
const images = subItems.filter(f => f.mimeType.startsWith('image/'));
allImages.push(...images);
// Go one level deeper (e.g. Social media design/Croatia/2026/)
const subFolders = subItems.filter(f => f.mimeType.includes('folder'));
for (const sf of subFolders.slice(0, 5)) {
const deepImages = await listFiles(sf.id, { mimeType: 'image' });
allImages.push(...deepImages.slice(0, 10));
}
}
// Deduplicate by ID, sort by modifiedTime desc
const seen = new Set();
allImages = allImages.filter(f => {
if (seen.has(f.id)) return false;
seen.add(f.id);
return true;
}).sort((a, b) => new Date(b.modifiedTime) - new Date(a.modifiedTime));
// Filter to last 60 days
const cutoff = Date.now() - 60 * 24 * 60 * 60 * 1000;
const recent = allImages.filter(f => new Date(f.modifiedTime).getTime() > cutoff);
console.log(`Found ${recent.length} images from the last 60 days`);
// Take top 10 candidates
const candidates = recent.slice(0, 10);
// Download to ref-candidates/
await mkdir(`ACME Agency/clients/${CLIENT}/ref-candidates`, { recursive: true });
for (const img of candidates) {
const ext = img.name.split('.').pop() || 'png';
const dest = `ACME Agency/clients/${CLIENT}/ref-candidates/${img.id}.${ext}`;
await downloadFile(img.id, dest);
console.log(`Downloaded: ${img.name} (${img.modifiedTime})`);
}
EOF
If zero images found: Abort with: "No recent designs found in <Client>'s Drive folder (last 60 days). Use /static-ad-generator for template-based generation instead."
If fewer than 3 images found: Continue with what's available — even 1 reference is better than none.
Step 1.3 — Vision evaluation
Read each downloaded candidate image with Claude vision. For each, evaluate on a 1-10 scale:
| Criterion | What to look for |
|---|---|
| Production quality | Professional finish, not a rough draft, screenshot, or meme. Clean edges, proper resolution. |
| Brand consistency | Uses consistent colors, typography, logo placement. Looks like it belongs to the same brand. |
| Composition | Well-structured for an ad format. Clear visual hierarchy, not cluttered. |
| Style clarity | Has a clear, replicable visual style that Krea can clone. Not a generic stock photo. |
| Brief relevance | (Only if campaign brief provided) Relates to the product/service/concept being advertised. |
Scoring:
- Score each criterion 1-10
- Compute average
- Rank all candidates by average score
- Penalize heavily: images with lots of overlaid text (Krea will try to clone the text layout, which breaks), screenshots of other platforms, low-resolution images, images that are clearly logos/icons (not full ad designs)
Step 1.4 — Select top references
Pick the top 2-3 images. Log the selection with reasoning:
Reference selection for <Client>:
1. <filename> (modified <date>) — score 9.2 — clean layout, strong brand colors, professional product shot
2. <filename> (modified <date>) — score 8.8 — good typography, relevant to campaign brief
3. <filename> (modified <date>) — score 8.5 — consistent brand style, good composition
Step 1.5 — Upload as public URLs
For each selected reference, upload to Drive with public access:
node --input-type=module <<'EOF'
import { uploadPublic } from './ACME Agency/scripts/lib/google_drive.mjs';
import { readFile, writeFile } from 'fs/promises';
const CLIENT = '<ClientName>';
const FOLDER_ID = '<drive_folder_id>';
const refs = [
{ local: 'ACME Agency/clients/<Client>/ref-candidates/<id1>.png', tag: 'ref-style-1' },
{ local: 'ACME Agency/clients/<Client>/ref-candidates/<id2>.png', tag: 'ref-style-2' },
{ local: 'ACME Agency/clients/<Client>/ref-candidates/<id3>.png', tag: 'ref-style-3' },
];
const urls = {};
for (const r of refs) {
const url = await uploadPublic(r.local, FOLDER_ID, `${r.tag}.png`);
urls[r.tag] = url;
console.log(`${r.tag}: ${url}`);
}
// Update clients.json reference_assets
const registryPath = 'ACME Agency/clients/clients.json';
const registry = JSON.parse(await readFile(registryPath, 'utf8'));
const clientKey = Object.keys(registry.clients).find(k => k.toLowerCase() === CLIENT.toLowerCase());
if (!registry.clients[clientKey].reference_assets) registry.clients[clientKey].reference_assets = {};
Object.assign(registry.clients[clientKey].reference_assets, urls);
await writeFile(registryPath, JSON.stringify(registry, null, 2));
console.log('Updated clients.json reference_assets');
EOF
Step 1.6 — Cache selection
Save ACME Agency/clients/<Client>/reference_selection.json:
{
"selected_at": "2026-04-13T10:30:00Z",
"client": "ClientName",
"method": "vision-scored",
"references": [
{
"drive_file_id": "...",
"name": "original-filename.png",
"score": 9.2,
"score_breakdown": { "quality": 9, "brand": 10, "composition": 9, "style": 9, "relevance": 9 },
"public_url": "https://drive.google.com/uc?id=...",
"local_path": "ACME Agency/clients/<Client>/ref-candidates/<id>.png",
"tag": "ref-style-1"
}
]
}
Phase 2 — Prompt + Copy Construction
Goal: Build Krea prompts that describe each ad concept while instructing Krea to clone the reference style. Write the ad copy for each concept.
Step 2.1 — Deep reference analysis
Read the selected reference images again with vision and extract a detailed style profile:
- Color palette: Dominant colors, accent colors, background treatment (gradient/solid/textured)
- Typography style: Weight (bold/light), case (uppercase/mixed), placement (top/center/bottom), approximate size relative to image
- Layout pattern: Where's the product/hero shot? Where's the text? How much whitespace?
- Mood: Professional/warm/energetic/minimal/luxurious/clinical
- Brand elements: Logo placement and size, any recurring visual motifs
Compile this into a style brief — a 3-5 sentence summary that can be prepended to every Krea prompt. Example:
"Deep purple gradient background with gold accent text. Bold white uppercase headlines at top, product mockup centered. Clean professional composition with ample breathing room. Logo bottom-right, small. Mood: confident, premium but approachable."
Step 2.2 — Build concepts
For each concept (default 3, configurable via --concepts):
- Define the concept angle based on the campaign brief:
- What product/service/offer is being advertised?
- What's the unique angle for this specific concept? (testimonial, direct pitch, problem-solution, stat/authority, lifestyle, event/deadline)
- What should the image SHOW? (product, person, environment, abstract)
- Write the ad copy for this concept:
- Headline for the image overlay (max 8 words, punchy, benefit-driven)
- Accent word (1-2 words in a contrasting color for emphasis, usually the key benefit)
- CTA text (button or bottom text: "Saznajte vise", "Kontaktirajte nas", etc.)
- Primary text (the text that goes below the image in Meta — 2-3 paragraphs, conversational, follows Croatian/Bosnian copywriting rules from CLAUDE.md)
- Description (one-liner for Meta link description field)
If --with-copywriter was passed, delegate to the copywriter agent (Task tool, subagent_type: "copywriter") with the concept details and CLIENT.md context.
- Build the Krea prompt by combining:
- The style brief from Step 2.1 (prepended to every prompt)
- The concept description (what should be in the image)
- Image overlay text placement instructions (from the copy)
- CLIENT.md brand details if available (exact hex colors, logo description)
- Client market cue ("Croatian market", "Bosnian market", "German market")
Prompt structure: ``` [STYLE BRIEF from Step 2.1]
Modern [aspect_ratio] advertisement for [client business description]. [Concept description: what's in the image, the scene, the subject, the mood].
Text overlay: bold [uppercase/mixed] headline at [position] reading "[HEADLINE TEXT]" with the word "[ACCENT WORD]" in [accent color]. [CTA placement]: "[CTA TEXT]" in [style]. Logo [position], [size].
Style: match the visual style, color palette, typography weight, and composition of the reference images. Clean, professional, high-quality advertising creative. ```
Step 2.3 — Save prompts JSON
Save to ACME Agency/clients/<ClientName>/static_ads_prompts.json in the standard format that static_ads_generate.mjs reads:
{
"client": "ClientName",
"generated_at": "2026-04-13T10:45:00Z",
"brand_dna_version": "reference-ads",
"input_mode": "reference",
"campaign": "Campaign description from brief",
"prompts": [
{
"template_number": 1,
"template_name": "direct-pitch",
"prompt": "Deep purple gradient background matching reference style...",
"aspect_ratio": "4:5",
"reference_tags": ["ref-style-1", "ref-style-2", "ref-style-3"],
"notes": "Copy: Headline='BRENDIRANJE ZA VAS PORTAL', CTA='POSALJITE UPIT'"
},
{
"template_number": 2,
"template_name": "testimonial",
"prompt": "...",
"aspect_ratio": "4:5",
"reference_tags": ["ref-style-1", "ref-style-2", "ref-style-3"],
"notes": "..."
}
]
}
Key: Every prompt uses the SAME reference_tags — all point to the selected reference designs. This is what makes every generated image match the client's existing style.
Show the user a summary table before Phase 3:
Concepts for <Client>:
| # | Name | Headline | Ratio | Refs |
|---|----------------|-------------------------------|-------|------|
| 1 | direct-pitch | BRENDIRANJE ZA VAS PORTAL | 4:5 | 3 |
| 2 | testimonial | ZADOVOLJNI KLIJENTI | 4:5 | 3 |
| 3 | problem-solve | ZASTO NAM VJERUJU | 4:5 | 3 |
Total: 3 images (3 concepts x 1 batch). Ready to generate?
Phase 3 — Generation
Goal: Run batch generation via the existing pipeline.
Step 3.1 — Verify reference_assets
Before generating, confirm clients.json > reference_assets has all the ref-style-* keys that the prompts reference:
node --input-type=module <<'EOF'
import { readFile } from 'fs/promises';
const reg = JSON.parse(await readFile('ACME Agency/clients/clients.json', 'utf8'));
const client = reg.clients['<ClientName>'];
const assets = client.reference_assets || {};
for (const tag of ['ref-style-1', 'ref-style-2', 'ref-style-3']) {
console.log(`${tag}: ${assets[tag] || 'MISSING'}`);
}
EOF
If any are missing → abort. Something went wrong in Phase 1.
Step 3.2 — Run static_ads_generate.mjs
node ACME Agency/scripts/static_ads_generate.mjs "<ClientName>" --batch <batch>
The script:
- Reads
static_ads_prompts.json - For each prompt, maps
reference_tagstoreference_assetsURLs - Auto-prepends the client logo (via
ensureClientLogo()) as the first reference and augments the prompt with a "logo is canonical" instruction. The logo is added on top of your selectedref-style-*references, not in place of them. - Calls
generateAndDownload()with the reference URLs asimageUrls - Uploads all generated images to Drive (nested folder structure)
- Posts ONE consolidated Slack report
- Saves manifest to
static_ads_manifest.json
Do NOT call krea_create_images.mjs in a loop. The whole point of this skill is to use the batch pipeline for consolidated output.
Phase 4 — Delivery & Verification
Step 4.1 — Verify output
Check:
- [ ] Total images generated = concepts x batch (no silent skips)
- [ ] Every local file exists and is > 10KB
- [ ] Drive URLs match local count
- [ ]
static_ads_manifest.jsonhas a new entry with today's timestamp - [ ] Reference images were actually passed to Krea (check manifest for
imageUrlsor look at script logs) - [ ] Slack report was posted successfully (if
--no-slackwas not passed)
Step 4.2 — Post copy to Slack thread
If ad copy was generated in Phase 2, post it as a threaded reply to the Slack report:
*Ad Copy — <Client> — <Campaign>*
*Concept 1: direct-pitch*
Headline: BRENDIRANJE ZA VAS PORTAL
Primary text: [full primary text]
CTA: POSALJITE UPIT
*Concept 2: testimonial*
...
Phase 5 — Iteration
If the user wants changes:
- Different references: Run with
--refreshto re-browse Drive and pick new references - More/fewer concepts: Run with
--concepts N - Different copy: Edit
static_ads_prompts.jsondirectly, then re-run Phase 3 only withnode ACME Agency/scripts/static_ads_generate.mjs "<Client>" --batch 1 - Different ratio: Run with
--ratio 1:1(or9:16,16:9) - Higher quality: Run with
--resolution 4Kand--batch 2for more variants
Key Files
| File | Purpose |
|---|---|
ACME Agency/clients/clients.json | Client registry — drive_folder_id, reference_assets, slack_channel |
ACME Agency/clients/<Client>/CLIENT.md | Brand context (optional but improves quality) |
ACME Agency/clients/<Client>/reference_selection.json | Cached reference selection (vision scores, URLs) |
ACME Agency/clients/<Client>/static_ads_prompts.json | Prompts JSON consumed by static_ads_generate.mjs |
ACME Agency/clients/<Client>/static_ads_manifest.json | Generation manifest (written by static_ads_generate.mjs) |
ACME Agency/clients/<Client>/ref-candidates/ | Downloaded reference candidate images |
ACME Agency/scripts/static_ads_generate.mjs | Batch generation engine (Phase 3) |
ACME Agency/scripts/lib/google_drive.mjs | listFiles, downloadFile, uploadPublic, findOrCreateFolder |
ACME Agency/scripts/lib/krea.mjs | generateAndDownload (via static_ads_generate.mjs) |
ACME Agency/scripts/lib/slack.mjs | getClientChannel, postMessage |
Tips for Better Output
- Fewer text overlays = better results. Krea is bad at rendering non-Latin text (Croatian diacritics). Keep overlay text minimal (1 headline + 1 CTA) and plan to add text in post-production if needed.
- Reference quality matters more than quantity. 2 excellent references beat 5 mediocre ones. Prefer clean, professional designs over busy social media posts.
- Same ratio as references. If the reference designs are 1:1 squares, generate 1:1. Krea clones composition better when the aspect ratio matches.
- Run with
--batch 2on first attempts — gives you 2 variants per concept so you can pick the better one. - The style brief is critical. Spend time on Step 2.1 — a precise style description (exact colors, exact typography weight, exact layout) produces dramatically better Krea output than a vague "match the style."