Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate or edit images via two backends: Google Gemini (gemini-3-pro-image) or any OpenAI-compatible endpoint (gpt-image-2, dall-e-3). Use when the user wants to create, draw, illustrate, or edit an image, icon, logo, poster, or concept art.
.claude/skills/fradser-generate-image/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-13 | ✗→✓ | ▲ Improved | 97% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-17 | ✓→✗ | ▼ Worse | 113% | 0% |
| case-22 | ✓→✗ | ▼ Worse | 20% | 0% |
| case-23 | ✓→✗ | ▼ Worse | 81% | 0% |
Turn a text prompt — optionally with reference images — into one or more images via one of two explicit backends. --backend has no default — always pick one (gemini or openai), or set IMAGE_BACKEND. The script does the API call, file saving, and configuration; your job is to pick the backend, craft a strong prompt, and wire up the flags.
gemini — Google's native Gemini API via google-genai. Full featureset: --aspect-ratio, --size tiers (1K/2K/4K), and multi-image edit/compose (-i a.png -i b.png). Use GEMINI_API_KEY / GEMINI_IMAGE_MODEL.
openai — any OpenAI-compatible image endpoint (OpenAI official,DashScope, new-api gateways, ...). Use OPENAI_API_KEY / OPENAI_BASE_URL / OPENAI_IMAGE_MODEL. Supports gpt-image-2, dall-e-3, etc.
uv available (the script is a self-contained uv run script; deps install on first run).export GEMINI_API_KEY=... (gemini backend) or export OPENAI_API_KEY=... (openai backend), or.env file (checked in order: $PWD/.env, then ${CLAUDE_PLUGIN_ROOT}/.env), or--api-key ... on the command line.CRITICAL -- Never paste the API key into chat or commit a .env. If the key is missing, the script prints exactly how to set it — relay that to the user rather than guessing.
A one-line request like "draw a fox in a spacesuit" needs no questions — just generate. Ask (via AskUserQuestion) only when a choice would materially change the result and you cannot reasonably default it, e.g. aspect ratio for a "banner vs. avatar", or whether an attached image should be edited vs. used as style reference.
Read references/prompting.md before writing a non-trivial prompt. In short: describe subject, composition, lighting, style, and mood in concrete terms; put any literal text to render in quotes; and for edits, state what to change AND what to keep. A vivid one-paragraph prompt beats a terse phrase.
Invoke it directly (the shebang runs it through uv):
bash${CLAUDE_PLUGIN_ROOT}/skills/generate-image/scripts/generate_image.py "PROMPT" --backend BACKEND -o OUT.png [flags]
Flags:
| Flag | Purpose | Default | |------|---------|---------| | --backend | gemini (native Google API) or openai (OpenAI-compatible endpoint). No default — also via IMAGE_BACKEND. | required | | -o, --output | Output path (.png/.jpeg) | generated.png | | -i, --input | Reference/input image (repeatable). gemini: edit/compose multi-image. openai: edit single image (first wins). | none | | --aspect-ratio | 1:1 2:3 3:2 3:4 4:3 4:5 5:4 9:16 16:9 21:9. gemini: via image_config. openai: via extra_body (Gemini-compat endpoints only). | model decides | | --size | gemini: 1K/2K/4K. openai: free string (1024x1024, auto, ...). | model decides | | --count | Number of images. gemini: N separate calls. openai: one call, n=N. | 1 | | --model | Model id or alias (else GEMINI_IMAGE_MODEL/OPENAI_IMAGE_MODEL). gemini: pro/flash/raw id. openai: raw id (e.g. gpt-image-2). | backend default | | --quality | low/medium/high/auto — openai backend only. | model decides | | --response-format | b64_json/url/none — openai backend only. url is downloaded to disk; none omits the param. Some gateways require url or reject the param (use none). | b64_json | | --base-url | openai backend only: OpenAI-compatible base URL. Also via IMAGE_BASE_URL/OPENAI_BASE_URL. | required for openai | | --api-key | Override the API key (else GEMINI_API_KEY for gemini, OPENAI_API_KEY for openai). | required |
Models (pass the alias to --model or set the backend's env var):
| Backend | Alias / id | Notes | |---------|-----------|-------| | gemini | pro (default) → gemini-3-pro-image | highest quality | | gemini | flash → gemini-2.5-flash-image | faster / cheaper | | openai | gpt-image-2, dall-e-3, gpt-image-1, ... | any id the endpoint serves |
Examples:
bash# Gemini, 2K wide banner generate_image.py "podcast cover art" --backend gemini --aspect-ratio 16:9 --size 2K -o cover.png # Gemini multi-image compose generate_image.py "put the watch from image 1 on the wrist in image 2" --backend gemini -i watch.png -i wrist.png -o composite.png # OpenAI-compatible gateway, gpt-image-2 (this gateway needs url format) generate_image.py "a red bicycle" --backend openai --base-url https://api.tu-zi.com/v1 \ --model gpt-image-2 --size 1024x1024 --response-format url -o bike.png # OpenAI official generate_image.py "a red bicycle" --backend openai --base-url https://api.openai.com/v1 --model gpt-image-2 -o bike.png
When the user wants choices to pick from, request --count 2 (or more) and show all outputs. With -i, the prompt becomes an edit/compose instruction over the supplied image(s).
Tell the user the saved path(s). If nothing was returned, it is usually a safety-filtered prompt — say so and offer a reworded prompt. Output files are images: reference them by path; do not try to inline their bytes.
Backend, key, base URL, model, and quality are each resolved by lib/progressive_env.py in this order, stopping at the first hit: CLI flag → process env → .env chain → built-in default. This is why the same command works in a project with a local .env, in a shell with exports, or with everything overridden inline — and why a newer model or a different endpoint can be selected with export GEMINI_IMAGE_MODEL=... / export OPENAI_BASE_URL=... without touching code. See references/prompting.md for the parameter reference.
scripts/generate_image.py — the generator (google-genai for gemini, openai for openai).references/prompting.md — prompt-writing guide and full parameter reference.${CLAUDE_PLUGIN_ROOT}/lib/progressive_env.py — shared progressive config resolver.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-04 | fail→fail | 40,064 | 8,165 | -80% | 1 | 1 | 0% | 7,342 | 2,225 | -70% | 0 | 0 | — |
case-05 | fail→fail | 2,175 | 6,978 | +221% | 1 | 1 | 0% | 319 | 2,207 | +592% | 0 | 0 | — |
case-01 | fail→fail | 10,111 | 16,801 | +66% | 1 | 1 | 0% | 785 | 2,379 | +203% | 0 | 0 | — |
case-02 | fail→fail | 25,417 | 8,090 | -68% | 1 | 1 | 0% | 4,566 | 2,553 | -44% | 0 | 0 | — |
case-03 | fail→fail | 10,562 | 11,962 | +13% | 1 | 1 | 0% | 2,058 | 2,717 | +32% | 0 | 0 | — |
case-11 | fail→fail | 6,797 | 8,921 | +31% | 1 | 1 | 0% | 1,259 | 2,138 | +70% | 0 | 0 | — |
case-06 | pass→pass | 8,445 | 4,042 | -52% | 1 | 1 | 0% | 1,701 | 2,555 | +50% | 0 | 0 | — |
case-07 | fail→fail | 10,264 | 9,344 | -9% | 1 | 1 | 0% | 690 | 2,301 | +233% | 0 | 0 | — |
case-08 | fail→fail | 7,144 | 10,364 | +45% | 1 | 1 | 0% | 756 | 2,551 | +237% | 0 | 0 | — |
case-09 | fail→fail | 21,224 | 12,427 | -41% | 1 | 1 | 0% | 3,613 | 2,503 | -31% | 0 | 0 | — |
case-10 | fail→fail | 13,829 | 10,418 | -25% | 1 | 1 | 0% | 789 | 2,461 | +212% | 0 | 0 | — |
case-12 | fail→fail | 8,639 | 12,410 | +44% | 1 | 1 | 0% | 1,453 | 2,512 | +73% | 0 | 0 | — |
case-13 | fail→pass | 8,057 | 5,469 | -32% | 1 | 1 | 0% | 1,412 | 2,780 | +97% | 0 | 0 | — |
case-14 | fail→pass | 13,066 | 6,651 | -49% | 1 | 1 | 0% | 2,046 | 2,959 | +45% | 0 | 0 | — |
case-15 | pass→pass | 3,945 | 2,780 | -30% | 1 | 1 | 0% | 516 | 2,272 | +340% | 0 | 0 | — |
case-16 | pass→pass | 9,726 | 2,687 | -72% | 1 | 1 | 0% | 1,947 | 2,313 | +19% | 0 | 0 | — |
case-17 | pass→fail | 7,931 | 12,093 | +52% | 1 | 1 | 0% | 1,147 | 2,443 | +113% | 0 | 0 | — |
case-18 | fail→fail | 9,796 | 9,132 | -7% | 1 | 1 | 0% | 526 | 2,290 | +335% | 0 | 0 | — |
case-19 | fail→fail | 16,986 | 11,149 | -34% | 1 | 1 | 0% | 1,161 | 2,444 | +111% | 0 | 0 | — |
case-20 | fail→fail | 12,293 | 10,769 | -12% | 1 | 1 | 0% | 2,053 | 2,446 | +19% | 0 | 0 | — |
case-21 | fail→fail | 7,334 | 6,922 | -6% | 1 | 1 | 0% | 1,326 | 2,210 | +67% | 0 | 0 | — |
case-22 | pass→fail | 11,217 | 5,677 | -49% | 1 | 1 | 0% | 1,838 | 2,205 | +20% | 0 | 0 | — |
case-23 | pass→fail | 6,646 | 11,527 | +73% | 1 | 1 | 0% | 1,340 | 2,432 | +81% | 0 | 0 | — |
case-24 | pass→pass | 9,496 | 7,153 | -25% | 1 | 1 | 0% | 1,724 | 3,144 | +82% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted, and 6 counted toward the lift figure. The other 18 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of -4 percentage points is the difference between those two pass rates over the 6 comparable cases. 4 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.