PORTAL / LIBRARY / reference-ads

[ CREATIVE ]

/reference-ads

Generate ad creatives using the client's OWN existing designs from Google Drive as style references for Krea.ai.

Download the skill file (.md)

Placeholders like 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


Preflight

Run ALL checks before any expensive operations. If ANY check fails, abort with the specific failure.

  1. Client exists in ACME Agency/clients/clients.json. Resolve the canonical key (case-insensitive).
  2. drive_folder_id is present and reachable. Test with a listFiles() call. If missing or unreachable → abort: "This skill requires a client with designs in Drive. Use /static-ad-generator for template-based generation instead."
  3. CLIENT.md exists at ACME Agency/clients/<Client>/CLIENT.md. If missing → warn but continue (reference images provide visual context, and the skill can still work without it).
  4. Krea API key set in .env (KREA_API_KEY). If missing → abort.
  5. concepts x batch stays sane: concepts * batch <= 16. If higher → require explicit confirmation.
  6. Slack channel resolves via getClientChannel(). Skip Slack cleanly if bot not invited.
  7. 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:

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:

CriterionWhat to look for
Production qualityProfessional finish, not a rough draft, screenshot, or meme. Clean edges, proper resolution.
Brand consistencyUses consistent colors, typography, logo placement. Looks like it belongs to the same brand.
CompositionWell-structured for an ad format. Clear visual hierarchy, not cluttered.
Style clarityHas 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:

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:

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):

  1. Define the concept angle based on the campaign brief:
  1. Write the ad copy for this concept:

If --with-copywriter was passed, delegate to the copywriter agent (Task tool, subagent_type: "copywriter") with the concept details and CLIENT.md context.

  1. Build the Krea prompt by combining:

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:

  1. Reads static_ads_prompts.json
  2. For each prompt, maps reference_tags to reference_assets URLs
  3. 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 selected ref-style-* references, not in place of them.
  4. Calls generateAndDownload() with the reference URLs as imageUrls
  5. Uploads all generated images to Drive (nested folder structure)
  6. Posts ONE consolidated Slack report
  7. 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:

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:


Key Files

FilePurpose
ACME Agency/clients/clients.jsonClient registry — drive_folder_id, reference_assets, slack_channel
ACME Agency/clients/<Client>/CLIENT.mdBrand context (optional but improves quality)
ACME Agency/clients/<Client>/reference_selection.jsonCached reference selection (vision scores, URLs)
ACME Agency/clients/<Client>/static_ads_prompts.jsonPrompts JSON consumed by static_ads_generate.mjs
ACME Agency/clients/<Client>/static_ads_manifest.jsonGeneration manifest (written by static_ads_generate.mjs)
ACME Agency/clients/<Client>/ref-candidates/Downloaded reference candidate images
ACME Agency/scripts/static_ads_generate.mjsBatch generation engine (Phase 3)
ACME Agency/scripts/lib/google_drive.mjslistFiles, downloadFile, uploadPublic, findOrCreateFolder
ACME Agency/scripts/lib/krea.mjsgenerateAndDownload (via static_ads_generate.mjs)
ACME Agency/scripts/lib/slack.mjsgetClientChannel, postMessage

Tips for Better Output

  1. 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.
  2. Reference quality matters more than quantity. 2 excellent references beat 5 mediocre ones. Prefer clean, professional designs over busy social media posts.
  3. Same ratio as references. If the reference designs are 1:1 squares, generate 1:1. Krea clones composition better when the aspect ratio matches.
  4. Run with --batch 2 on first attempts — gives you 2 variants per concept so you can pick the better one.
  5. 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."