# /krea-create-images

> Generate ad images for a ACME Agency client using Krea.ai Nano Banana 2.


# /krea-create-images

Generates ad creatives for a ACME Agency client using **Krea.ai Nano Banana 2** (`nano-banana-flash`). Reads brand context, builds an optimized prompt, generates images, uploads to Drive, and reports to Slack.

## Trigger

- `/krea-create-images [ClientName]`
- `/krea-create-images ACME Agency --ratio 4:5 --brief "dental clinic, happy patient"`

## Preflight (run BEFORE any expensive Krea call)

Validate every assumption that has burned us in production. If ANY check fails, abort with a clear message naming the failure. Do not retry, do not call diagnostician — preflight is the cheap fix path.

1. **Client exists** in `ACME Agency/clients/clients.json` (case-insensitive match). If missing → abort with the list of valid clients.
2. **Ratio is valid** for Krea NB2: `1:1`, `4:5`, `9:16`, `16:9`, `3:2`, `2:3`. Reject anything else.
3. **Batch is 1-4** (Krea API max). If passed >4, clamp and warn.
4. **Resolution is `1K`, `2K`, or `4K`**. Default `2K`.
5. **Brief OR reference URL is present.** If neither → abort with usage hint.
6. **Reference URLs (if passed) are publicly fetchable** — they must be HTTP(S), NOT local file paths. If a local path was passed by mistake, upload it via `uploadPublic()` from `lib/google_drive.mjs` first.
7. **`KREA_API_KEY` is set** in `.env`. If missing → abort with "add KREA_API_KEY to .env".
8. **`drive_folder_id` is present** if `--drive` (default). If missing → either skip drive cleanly OR abort if explicitly required.
9. **Slack channel resolves** if `--slack` (default). If `getClientChannel()` returns null and bot isn't invited → skip with warning, don't crash.

## Workflow

### Step 1 — Identify client

- Parse the client name from the argument
- Look up the client in `ACME Agency/clients/clients.json`
- Confirm the match with the user if ambiguous

### Step 2 — Load brand context

- Read `ACME Agency/clients/<ClientName>/CLIENT.md` if it exists
- Extract: products/services, target audience, brand tone, colors, market
- If CLIENT.md is missing, note it and rely on the user's brief

### Step 3 — Gather brief (ask if not provided)

If no `--brief` was given, ask the user:

1. **What's this image for?** (ad creative, organic post, story, banner, etc.)
2. **Any specific copy or text to include in the image?** (headline, CTA, phone number, etc.) — Nano Banana 2 handles typography well
3. **Preferred visual style?** (photorealistic, clean minimal, lifestyle, product shot, architectural, etc.)
4. **Preferred color palette?** (or derive from CLIENT.md if it has brand colors)
5. **Ratio?** → default `4:5` for feed ads, `1:1` for square, `9:16` for stories/reels

### Step 4 — Build the Krea prompt

Construct a detailed, well-structured prompt. Nano Banana 2 works best with:

**Structure:**
```
[Main subject / scene] — [style descriptor] — [lighting/mood] — [text to overlay if any] — [color palette] — [target feel]
```

**Tips:**
- Lead with the most important visual element
- Be specific: "woman in white lab coat smiling at camera, dental clinic background" beats "dentist"
- Include text overlay instructions explicitly if needed: e.g. `bold Croatian text "Zakažite pregled" bottom center`
- Name colors: "white, navy blue, light grey" not "brand colors"
- Include market/cultural cues: "Croatian market", "Mediterranean setting", "modern European"
- Do NOT use negative prompts — Nano Banana 2 does not support them

**Example prompts:**
- `professional dental clinic reception, female dentist in white coat smiling warmly, clean white interior, soft natural light, modern European aesthetic, Croatian market, photorealistic`
- `luxury apartment building exterior, golden hour lighting, Mediterranean architecture, modern minimalist, warm tones, real estate advertisement style`

### Step 5 — Run the script

```bash
node ACME Agency/scripts/krea_create_images.mjs "ClientName" \
  --ratio 4:5 \
  --brief "your constructed prompt here" \
  --batch 2 \
  --resolution 2K
```

**Default settings:**
- Ratio: `4:5` (best for Meta feed ads)
- Resolution: `2K`
- Batch: `1` (increase to 2–4 for variations)

**Note on `--batch`:** Krea removed the API-side `batchSize` field on 2026-05-26 (one POST → one image). `lib/krea.mjs` now fires N parallel POST requests when `--batch N` is set, so the public behavior is unchanged from a caller's perspective — you still get N images back. Latency is roughly the same as before (parallel) but each image counts as one Krea credit instead of N images per credit (refer to current Krea pricing for the exact cost delta).

**Common ratio choices:**
| Use case | Ratio |
|----------|-------|
| Meta feed ad | `4:5` |
| Square post / story | `1:1` |
| Story / Reel | `9:16` |
| Facebook cover | `16:9` |

**Automatic logo reference (always-on):**

When `<ClientName>` is passed, the script auto-includes the client's official logo as the first reference image (via `ensureClientLogo()` from `lib/logo_prepare.mjs`). It resolves from `clients.json > reference_assets.logo`, then a local `logo.png` in the client folder, then a Playwright screenshot of the website. The prompt is also augmented with a "logo is canonical, reproduce pixel-exact" instruction. No flag — this is on by default to fix Nano Banana's tendency to redraw logos.

If the log says `[Logo] No logo available`, the client has no `landing_page` and no local logo file — drop a `logo.png` into `ACME Agency/clients/<ClientName>/` and re-run, or run `node ACME Agency/scripts/lib/logo_prepare.mjs "<ClientName>"` manually.

**Reference-image flags (for "make something like this" requests):**

Krea NB2 can clone style and composition from a reference image. Use these flags when the user supplies an image as inspiration:

```bash
# Single reference (most common)
node ACME Agency/scripts/krea_create_images.mjs "ClientName" \
  --brief "in the style of the reference, but for a dental clinic" \
  --reference-url "https://drive.google.com/uc?id=ABC123"

# Multiple references (up to 14)
node ACME Agency/scripts/krea_create_images.mjs "ClientName" \
  --brief "fuse these visual themes" \
  --reference-urls "url1,url2,url3"

# Reference-only (no brief — useful for quick clones)
node ACME Agency/scripts/krea_create_images.mjs "ClientName" \
  --reference-url "https://drive.google.com/uc?id=ABC123"

# Style transfer with strength control
node ACME Agency/scripts/krea_create_images.mjs "ClientName" \
  --brief "modern dental clinic" \
  --style-image-url "https://drive.google.com/uc?id=XYZ" \
  --style-strength 1.5
```

**Important:** reference URLs must be **publicly fetchable by Krea**. The proven path is:
1. Upload the local image via `uploadPublic(localPath, parentFolderId)` from `ACME Agency/scripts/lib/google_drive.mjs`
2. The function returns a `https://drive.google.com/uc?id=<fileId>` URL
3. Pass that URL to `--reference-url`

This is the same pattern `static_ads_generate.mjs` already uses in production for product-image references.

**When the `creative-director` agent invokes this skill:** the agent will handle the upload-to-public step automatically. Skill scripts should never assume the user has a public URL handy — if the user gives a local file path, the upload step is part of the workflow.

### Step 6 — ACME Agencyw & iterate

After generation:
1. Show the user the Drive links and local paths
2. Ask: "Want a variation, different style, different ratio, or adjusted copy?"
3. If iterating, tweak the prompt and re-run — append `/v2`, `/v3` to track versions mentally
4. Offer to generate a batch of 2–4 with slight prompt variations for A/B testing

### Step 7 — Output

The script automatically:
- Downloads images to `tmp/krea_<timestamp>_<n>.jpg`
- Uploads to client's Google Drive folder (via `drive_folder_id` in clients.json)
- Posts Drive links to the client's Slack channel
- Saves a manifest to `ACME Agency/clients/<ClientName>/krea_generations.json`

## Verification (run AFTER Step 7 — prove it landed, don't assume)

Rubric: [verify.md](verify.md). Two layers — one enforced, one you run.

**Deterministic (automatic, in `krea_create_images.mjs`).** The script now runs
`validateFiles()` from `shared/verify/assert_output.mjs` right after generation: 0-byte /
sub-10 KB stubs are dropped before upload, and if NONE are valid it exits non-zero instead
of posting an empty result. The manifest records `dropped`. Still confirm from the output:
valid image count = batch (a short count is logged, not swallowed), `driveUrls` map 1:1 to
the valid images, Slack returned a non-null `ts`.

**Vision quality (you run, before reporting to the user).** Invoke the `verifier` agent on
the generated image(s) against [verify.md](verify.md) (threshold 8): is the logo/text real
and legible, does it match the brief, is it on-brand and usable as an ad? Re-run with a
tweaked prompt for anything < 8 (name the defect); offer a 2–4 variation batch when style
is the issue.

Never claim success while either layer shows a gap — give the specific evidence.

## Key Files

| File | Purpose |
|------|---------|
| `ACME Agency/scripts/krea_create_images.mjs` | Main script |
| `ACME Agency/scripts/lib/krea.mjs` | Krea API wrapper (generateImage, pollJob, downloadImage) |
| `ACME Agency/scripts/lib/slack.mjs` | Slack posting |
| `ACME Agency/clients/clients.json` | Client registry (drive_folder_id, etc.) |
| `ACME Agency/clients/<Name>/CLIENT.md` | Brand context |
| `ACME Agency/clients/<Name>/krea_generations.json` | Generation history |

## API Details

- **Endpoint:** `POST https://api.krea.ai/generate/image/google/nano-banana-flash`
- **Auth:** `KREA_API_KEY` in `.env`
- **Polling:** `GET https://api.krea.ai/jobs/{id}` until `status: completed`
- **Result:** `result.urls[]` — direct CDN download URLs
- **Cost:** ~48 compute units per image, ~15s generation time
- **No negative_prompt support** on this model

## Troubleshooting

| Problem | Fix |
|---------|-----|
| `KREA_API_KEY not set` | Check `.env` has `KREA_API_KEY=...` |
| `Insufficient credits (402)` | Top up credits at krea.ai/settings |
| `Drive upload failed` | Check `gws` auth — run `gws auth login` if expired |
| `drive_folder_id missing` | Add `drive_folder_id` to the client entry in `clients.json` |
| Image not in Slack | Bot may not be invited — run `/invite @agency-osslack` in channel |
| Job times out | Normal for high load — re-run; `timeoutMs` is 120s |
