npx claudepluginhub pigfoot/claude-code-hubs --plugin superpowersWant just this skill?
Add to a custom plugin, then install with one command.
Use when users request image generation, AI art creation, image editing with Gemini models, need help crafting prompts, or want brand-styled imagery. Handles both direct generation and interactive prompt design.
This skill is limited to using the following tools:
assets/README.mdassets/logos/README.mdassets/logos/trendlife-2026-logo-light.pngassets/templates/README.mdreferences/batch-generation.mdreferences/brand-styles.mdreferences/guide.mdreferences/slide-deck-styles.mdscripts/generate_images.pyscripts/logo_overlay.pyNano Banana
Unified image generation workflow using a fixed Python script with JSON configuration. Eliminates AI hallucinations by avoiding dynamic code generation.
Supports two modes:
- Unified Generation: All image requests use generate_images.py with JSON config
- Interactive Prompting: Guide user through prompt design with proven techniques
šØ CRITICAL: Read This First
API Selection Rule
BEFORE writing ANY code, determine which API to use
model = os.environ.get("NANO_BANANA_MODEL") or "gemini-3-pro-image-preview"
# Select API based on model name
if "imagen" in model.lower():
use_imagen_api = True # ā generate_images()
else:
use_imagen_api = False # ā generate_content()
Two COMPLETELY DIFFERENT APIs
These are NOT interchangeable. Using the wrong API will cause errors.
| Gemini Image | Imagen | |
|---|---|---|
| API Method | generate_content() | generate_images() |
| Config Type | GenerateContentConfig | GenerateImagesConfig |
| Models | gemini-*-image* | imagen-* |
| Prompt Format | contents=[prompt] (array) | prompt=prompt (string) |
| Response | response.parts | response.generated_images |
| Special Config | response_modalities=['IMAGE'] | Not used |
Example model detection
# These trigger Imagen API:
"imagen-4.0-generate-001" ā generate_images()
"custom-imagen-v2" ā generate_images()
# These trigger Gemini API:
"gemini-3-pro-image-preview" ā generate_content()
"gemini-2.5-flash-image" ā generate_content()
"custom-gemini-image" ā generate_content()
ā DOES NOT EXIST - Never Use These
If you find yourself writing these, STOP - you are using the wrong API:
| ā Wrong (Does NOT exist) | ā Correct |
|---|---|
types.ImageGenerationConfig | Use GenerateContentConfig (Gemini) or GenerateImagesConfig (Imagen) |
generate_images() + Gemini model | Use generate_content() for Gemini models |
generate_content() + Imagen model | Use generate_images() for Imagen models |
response_modalities in Imagen | Only use with Gemini's GenerateContentConfig |
NANO_BANANA_MODEL: NEVER Override
Rule: If NANO_BANANA_MODEL is set, use it EXACTLY as-is.
ā WRONG - Do NOT do this:
model = os.environ.get("NANO_BANANA_MODEL", "gemini-3-pro-image")
if not model.endswith("-preview"):
model = f"{model}-preview" # ā NEVER modify user's model name
ā CORRECT:
model = os.environ.get("NANO_BANANA_MODEL")
if not model:
# Only choose default when NANO_BANANA_MODEL is NOT set
model = "gemini-3-pro-image-preview"
# Use model EXACTLY as-is - do NOT add suffixes or change names
Why this matters
- Custom endpoints have their own model names (e.g.,
gemini-3-pro-imagewithout-preview) - User explicitly set the model they want
- DO NOT apply Google's naming conventions to custom endpoints
When to Use
- Image generation ("draw", "create", "generate"), slides, presentations
- Image editing with AI
- Prompt help ("help me craft", "improve my prompt")
- Brand styles ("use style trend", "notebooklm style")
- Reproducible generation ("use seed 42", "regenerate that image")
Mode selection
- Interactive Prompting: User asks for prompt help OR prompt too vague (<5 words)
- Unified Generation: User provides detailed prompt (uses generate_images.py)
Parsing User Intent for Seed & Temperature
Extract seed from user message
- "seed 42" ā
"seed": 42 - "use seed 392664860" ā
"seed": 392664860 - "regenerate with the same seed" ā Use seed from previous results JSON
- "seed: 123" (inline) ā
"seed": 123
Extract temperature from user message
- "temperature 0.5" ā
"temperature": 0.5 - "temp 1.5" ā
"temperature": 1.5 - "lower temperature" ā
"temperature": 0.5(suggest conservative value) - "higher temperature" ā
"temperature": 1.5(suggest exploratory value) - "more creative" ā
"temperature": 1.5 - "temperature: 0.8" (inline) ā
"temperature": 0.8
Combined examples
User: "Generate a robot image, seed 42, temperature 0.8"
ā Config: {"slides": [{"number": 1, "prompt": "robot", "seed": 42, "temperature": 0.8}]}
User: "Regenerate that image with seed 392664860"
ā Config: {"slides": [{"number": 1, "prompt": "<previous_prompt>", "seed": 392664860}]}
User: "Create 3 slides with same seed 100"
ā Config: {"seed": 100, "slides": [{...}, {...}, {...}]}
If user asks about reproducibility
- Explain that seed enables regenerating the same image
- Mention that results JSON automatically records seeds
- Show example of how to use seed from previous generation
Style Detection
Detect: style: "trendlife", style: "notebooklm", or natural language ("use trendlife style", "notebooklm style")
Styles
- TrendLife: Trend Micro's TrendLife product brand (Trend Red #D71920) with automatic logo overlay
- NotebookLM: Clean presentation aesthetic (ā ļø NEVER use "NotebookLM" brand/logo in prompts)
Priority: Inline spec ā Ask in Interactive Mode ā No style (Direct Mode default)
Logo Overlay (TrendLife Style)
TrendLife style includes automatic logo overlay
When style: "trendlife" is detected:
- Generate slide image with TrendLife colors (no logo in prompt)
- Detect layout type from prompt content
- Apply logo overlay with
logo_overlay.overlay_logo() - Output final image with logo
Layout Detection Rules
Automatic detection based on keywords
- "title slide", "cover slide", "opening" ā title layout (15% logo, bottom-right)
- "end slide", "closing", "thank you", "conclusion" ā end layout (20% logo, center-bottom)
- "divider", "section break" ā divider layout (15% logo, bottom-right)
- Slide number 1 (no keywords) ā title layout (assumed opener)
- Default ā content layout (12% logo, bottom-right)
Manual Logo Overlay Override
Use when you need custom logo positioning:
IMPORTANT: This script must be run with uv run to ensure dependencies are available.
#!/usr/bin/env python3
# /// script
# dependencies = ["pillow"]
# ///
# Run with: uv run --managed-python your_script.py
# After image generation, before final output
from pathlib import Path
import sys
# Import logo overlay module
sys.path.insert(0, str(Path(__file__).parent))
from logo_overlay import overlay_logo, detect_layout_type
# Detect layout type (or specify manually)
layout_type = detect_layout_type(prompt, slide_number=1)
# Or override: layout_type = 'title' # 'title', 'content', 'divider', 'end'
# Logo path
logo_path = Path(__file__).parent / 'assets/logos/trendlife-2026-logo-light.png'
# Apply logo overlay
output_with_logo = output_path.with_stem(output_path.stem + '_with_logo')
overlay_logo(
background_path=output_path,
logo_path=logo_path,
output_path=output_with_logo,
layout_type=layout_type,
opacity=1.0 # Optional: 0.0-1.0
)
# Replace original with logo version
output_with_logo.replace(output_path)
Style Trigger Keywords
Explicit: style: "trendlife"
Natural Language
- "trendlife style"
- "use trendlife"
- "trendlife brand"
- "trendlife presentation"
Quick Reference
API Selection (CRITICAL - Check First)
| Model Name Contains | API to Use | Config Type |
|---|---|---|
imagen | generate_images() | GenerateImagesConfig |
| Anything else | generate_content() | GenerateContentConfig |
See CRITICAL section above for complete details
Unified Generation Workflow
All image generation uses the same fixed Python script with JSON config:
- Create Config: Write JSON to system temp directory (NOT skill directory)
- Use
Writetool with path:{temp_dir}/nano-banana-config-{timestamp}.json - Get temp_dir from user's OS temp location (cross-platform)
- Use
- Execute Script:
uv run --managed-python scripts/generate_images.py --config <temp_config_path>- Script path is relative to skill directory
- CRITICAL: Use relative path to script WITHOUT changing directory (no
cdcommand) - WRONG:
cd scripts && uv run --managed-python generate_images.py ...ā - CORRECT:
uv run --managed-python scripts/generate_images.py ...ā - This ensures execution cwd remains in user's project directory, so relative paths in config work correctly
- Track Progress: Monitor progress/results files (for background tasks)
- Return Paths: Report generated image locations
IMPORTANT
- Always write config to system temp directory, NEVER to skill base directory
- Always use
scripts/generate_images.pyfor the script path (relative to skill directory)
Config Requirements
Minimal Config (recommended)
{
"slides": [{"number": 1, "prompt": "...", "style": "trendlife"}],
"output_dir": "./001-feature-name/" // MUST use NNN-short-name format
}
Full Config (optional fields)
{
"slides": [
{
"number": 1,
"prompt": "...",
"style": "trendlife",
"layout": "featured", // Optional: "featured" or "content" (auto-detect if omitted)
"temperature": 0.8, // Optional: 0.0-2.0 per-slide override
"seed": 42 // Optional: integer for reproducible generation
}
],
"output_dir": "./001-feature-name/", // MUST use NNN-short-name format
"format": "webp", // Optional: webp (default, RECOMMENDED), png, jpg
"quality": 90, // Optional: 1-100 (default: 90)
"temperature": 1.0, // Optional: 0.0-2.0 global default (default: 1.0)
"seed": 12345 // Optional: integer global default (default: auto-generate)
}
Format Selection Guide
- webp (RECOMMENDED): Default format, automatically uses lossless compression for presentation styles (trendlife, professional, data-viz). Same quality as PNG but 25-35% smaller file size.
- png: Only use if webp compatibility is a concern (rare). Larger file size.
- jpg: For photos only, not suitable for slides/diagrams (lossy compression).
Layout Field (TrendLife Only)
Purpose: Control logo integration strategy for TrendLife brand slides
Valid values:
-
"featured"- For title slides, dividers, and closing slides- Logo is used as reference image during AI generation (Gemini only)
- AI naturally integrates logo into the overall design
- Provides creative, professional branding
- Note: Imagen API doesn't support reference images, so logo is overlaid instead
-
"content"- For content/information slides- Logo is overlaid in bottom-right corner (50px, 25px padding)
- Fixed positioning ensures logo doesn't interfere with content
- Consistent across all content slides
-
Omitted or
null- Auto-detection (backwards compatibility)- System attempts to detect layout from prompt keywords
- Less reliable than explicit specification
When to use each:
// Title/cover slides
{"number": 1, "prompt": "Product Launch 2026", "style": "trendlife", "layout": "featured"}
// Section dividers
{"number": 3, "prompt": "Part 2: Technical Details", "style": "trendlife", "layout": "featured"}
// Closing slides
{"number": 6, "prompt": "Thank You", "style": "trendlife", "layout": "featured"}
// Content slides (everything else)
{"number": 2, "prompt": "Key features and benefits", "style": "trendlife", "layout": "content"}
{"number": 4, "prompt": "Performance metrics", "style": "trendlife", "layout": "content"}
IMPORTANT: When generating JSON configs for TrendLife presentations:
- Analyze each slide's purpose (not just keywords)
- Title/cover/divider/closing ā
"layout": "featured" - Information/data/content ā
"layout": "content" - Always specify
layoutexplicitly for TrendLife slides (don't rely on auto-detection)
Temperature & Seed Parameters
ā” NEW: Reproducible Generation
Seed Parameter (Recommended)
Purpose: Enable reproducible image generation
How it works
- Same seed + same prompt + same temperature = visually identical image
- Default: Auto-generated timestamp-based seed (recorded in results JSON)
- Use case: "I like this image, regenerate it exactly"
Seed: User natural language patterns
- "use seed 42"
- "regenerate with seed 392664860"
- "same seed as before"
- "seed: 123" (inline spec)
Config examples
// Global seed (all slides use same seed)
{"seed": 42, "slides": [...]}
// Per-slide seed (each slide has different seed)
{"slides": [
{"number": 1, "prompt": "...", "seed": 42},
{"number": 2, "prompt": "...", "seed": 123}
]}
// No seed specified (auto-generate and record)
{"slides": [...]} // Results JSON will contain: "seed": 1738051234
Results tracking
- All generated images record their actual seed in
{output_dir}/generation-results.json - Example output:
{
"outputs": [
{"slide": 1, "path": "slide-01.png", "seed": 392664860, "temperature": 1.0}
]
}
Temperature Parameter (Experimental)
Purpose: Control randomness in generation (0.0-2.0)
Official guidance: Gemini 3 recommends keeping default value 1.0
Effect
- ā ļø Limited impact observed in testing - changes output but effect is unpredictable
- No clear "low temperature = conservative, high temperature = creative" pattern
- Changes generation result when combined with same seed
Temperature: User natural language patterns
- "temperature 0.5"
- "use lower temperature"
- "more creative (temperature 1.5)"
- "temperature: 0.8" (inline spec)
Recommendation
- ā Use seed for reproducibility (highly effective)
- ā ļø Use temperature conservatively for experimentation only
- š” Keep default 1.0 unless exploring variations
Priority rules
- slide.temperature > config.temperature > 1.0 (default)
- slide.seed > config.seed > auto-generate
Field Rules
- ā
output_dirMUST be relative path with NNN-short-name format:./001-feature-name/- NNN = 3-digit number (001, 002, etc.)
- short-name = brief descriptive name (lowercase, hyphens)
- Examples:
./001-ai-safety/,./002-threat-detection/,./003-user-onboarding/
- ā NO absolute paths (breaks cross-platform)
- ā NO plain names without numbers (e.g.,
./slides/is WRONG) - ā NO
modelfield (use NANO_BANANA_MODEL env var)
Output Directory Behavior
- Relative paths resolve to user's current working directory
- Script is executed with absolute path, but cwd remains in user's directory
- Use sequential numbering for different presentation topics
Example: Config File Creation
# Linux/macOS: Write to /tmp/
Write tool: /tmp/nano-banana-config-1234567890.json
# Windows: Write to %TEMP%
Write tool: C:/Users/<user>/AppData/Local/Temp/nano-banana-config-1234567890.json
Multi-Slide Generation
- Any slide count ā Uses same workflow (generate_images.py)
- 5+ slides ā Automatic background execution with progress tracking
- Context efficient ā <500 tokens for any slide count
Complete workflow details: See references/batch-generation.md
Interactive Prompting Mode
| Step | Action |
|---|---|
| 1. Gather | Check for reference images, style specs |
| 2. Clarify | Ask 2-4 questions about output type, subject, style |
| 3. Select Technique | Choose from 16+ patterns (see references/guide.md) |
| 4. Generate Prompt | Apply technique, brand style, aspect ratio |
| 5. Present | Show prompt with explanation and variations |
| 6. Execute | Generate image with crafted prompt |
Interactive Prompting Mode
When to use: User requests prompt help ("help me craft", "improve my prompt") or prompt is too vague (<5 words).
Workflow
- Gather - Check for reference images, existing prompts, inline style specs (
style: "trend") - Clarify - Use
AskUserQuestion: Output type? Subject? Style preference? - Select Technique - Choose from 16+ patterns in
references/guide.md - Generate Prompt - Apply technique + brand style + aspect ratio
- Present - Show prompt with explanation and variations
- Execute - Generate using unified generation workflow (generate_images.py)
Brand Style Integration
-
NotebookLM style (
style: "notebooklm"):- Apply aesthetic: polished tech infographic, clean slide layout, minimal text
- ā ļø NEVER use "NotebookLM" brand/logo/name in prompts (trademark violation)
- ā Use: "clean professional presentation aesthetic", "modern tech infographic style"
- See
references/slide-deck-styles.mdfor complete specs
-
Trend Micro style (
style: "trend"):- Colors: Trend Red (#d71920), Guardian Red (#6f0000), Grays (#58595b-#e6e7e8)
- See
references/brand-styles.mdfor complete specs
Complete techniques and examples: See references/guide.md
Debugging
Most Common Errors
| Error | Quick Fix |
|---|---|
GEMINI_API_KEY not set | export GEMINI_API_KEY="your-key" |
Model not found | Check exact model name, use -preview suffix if needed |
| Wrong API used | Check CRITICAL section: Gemini vs Imagen |
ModuleNotFoundError | Verify # dependencies = ["google-genai", "pillow"] |
| No image generated | Check response.parts (Gemini) or response.generated_images (Imagen) |
Invalid aspect ratio | Use exact strings: "16:9", "1:1", "9:16" (with quotes) |
Debug Steps
- Verify API key:
echo $GEMINI_API_KEY - Test with simple prompt:
"A red circle" - Check response object:
print(response.parts)orprint(response.generated_images) - Review CRITICAL section if API errors occur
Common Mistakes
| Mistake | Fix |
|---|---|
Using types.ImageGenerationConfig | Does NOT exist - use GenerateContentConfig or GenerateImagesConfig |
Using generate_images() with Gemini | Use generate_content() for Gemini models |
Using generate_content() with Imagen | Use generate_images() for Imagen models |
Overriding NANO_BANANA_MODEL when set | Use model EXACTLY as-is - don't add suffixes |
Using google-generativeai (old library) | Use google-genai (new library) |
| Using text models for image gen | Use image models only (gemini-*-image* or imagen-*) |
| Saving to flat files | Use NNN-short-name/ directories |
| Using PIL to draw/edit | Use Gemini/Imagen API with image in contents |
Example Workflows
Scenario 1: First Generation (Auto Seed)
User: "Generate a modern office interior"
Assistant actions:
1. Create config WITHOUT seed (auto-generate):
{
"slides": [{"number": 1, "prompt": "Modern office interior with natural lighting"}],
"output_dir": "./001-office-design/"
}
2. Execute: uv run --managed-python scripts/generate_images.py --config {temp_config}
3. Read results: ./001-office-design/generation-results.json
ā {"outputs": [{"slide": 1, "path": "slide-01.png", "seed": 392664860}]}
4. Report to user: "Generated at ./001-office-design/slide-01.png (seed: 392664860)"
Scenario 2: Reproduce Previous Image
User: "I love that office image! Regenerate it with seed 392664860"
Assistant actions:
1. Create config WITH seed:
{
"slides": [{"number": 1, "prompt": "Modern office interior with natural lighting", "seed": 392664860}],
"output_dir": "./002-office-design-v2/"
}
2. Execute script
3. Result: Visually identical image
Scenario 3: Explore Variations
User: "Generate 3 variations of a robot holding flowers, use different temperatures"
Assistant actions:
1. Create config with per-slide temperatures:
{
"seed": 42, // Same seed for comparison
"slides": [
{"number": 1, "prompt": "A cute robot holding flowers", "temperature": 0.5},
{"number": 2, "prompt": "A cute robot holding flowers", "temperature": 1.0},
{"number": 3, "prompt": "A cute robot holding flowers", "temperature": 1.5}
],
"output_dir": "./003-robot-variations/"
}
2. Execute script
3. Result: 3 different compositions (temperature effect)
Scenario 4: Batch with Consistent Style
User: "Create 5 presentation slides with TrendLife style, use same seed"
Assistant actions:
1. Create config with global seed and appropriate layout for each slide:
{
"seed": 12345,
"slides": [
{"number": 1, "prompt": "AI Safety: Building Secure Systems", "style": "trendlife", "layout": "featured"}, // Title
{"number": 2, "prompt": "Key features overview", "style": "trendlife", "layout": "content"}, // Content
{"number": 3, "prompt": "Technical architecture", "style": "trendlife", "layout": "content"}, // Content
{"number": 4, "prompt": "Use cases and benefits", "style": "trendlife", "layout": "content"}, // Content
{"number": 5, "prompt": "Thank You - Contact Us", "style": "trendlife", "layout": "featured"} // Closing
],
"output_dir": "./004-ai-safety-deck/"
}
2. Execute in background (5+ slides)
3. Monitor progress file
4. Results:
- Slide 1 (featured): Logo integrated naturally by AI into title design
- Slides 2-4 (content): Logo overlaid in bottom-right corner
- Slide 5 (featured): Logo integrated naturally by AI into closing design
- All slides use seed 12345 for visual consistency
Common Errors and Solutions
If script execution fails, check these common issues:
1. "Logo file is Git LFS pointer" or "cannot identify image file"
Most common issue: User doesn't have Git LFS installed
-
Logo files are managed by Git LFS - without it, you get text pointer files instead of images
-
Solution:
# Install Git LFS (once per machine) git lfs install # Pull actual files (in the plugin directory) git lfs pull -
Git LFS install: https://git-lfs.com/
-
After installing, re-run the image generation command
2. "command not found: uv"
-
User needs to install uv: https://docs.astral.sh/uv/
-
Solution: Ask user to run:
curl -LsSf https://astral.sh/uv/install.sh | sh
3. "Python 3.14+ recommended" or "Python 3.9+ required"
- First run with
--managed-pythondownloads Python 3.14+ automatically - If download fails, user can pre-install:
uv python install 3.14 - Script supports Python 3.9+ as fallback but 3.14+ is recommended
- Check: Internet connection and disk space
4. Permission denied or download errors
- Check write permissions in uv cache directory (usually
~/.local/share/uv/) - Verify: Firewall/proxy settings allow uv to download Python
5. Missing dependencies (google-genai, pillow)
- Dependencies are automatically installed by
uv runvia PEP 723 metadata - If user bypasses uv and runs script directly with python, they'll see standard ImportError
- Solution: Always use
uv run --managed-python scripts/generate_images.py
References
- Advanced workflows:
references/guide.md(thinking, search grounding, 16+ prompting techniques) - Brand styles:
references/brand-styles.md(Trend Micro specs) - Slide decks:
references/slide-deck-styles.md(NotebookLM aesthetic, infographics, data viz) - Experiments:
../EXPERIMENT_RESULTS.md(temperature & seed testing results)