---
name: "image_prompt_craft"
description: "Write effective image-generation prompts: subject blocking, composition and camera terms, reusable style tokens, negative-prompt patterns, and a disciplined iterate-and-compare loop. Triggers when an agent needs to generate images, fix bad generations, or keep a series visually consistent."
metadata: { "includeInPrompt": true }
---

# Image Prompt Craft

## Purpose

Turn a vague idea into a prompt that reliably produces the image you meant. This skill covers the words that go into the prompt itself: what to describe, in what order, how to lock a style across a series, which negatives earn their place, and how to iterate without burning endless generations. It is tool-neutral. It names no model flags, samplers, or parameters, because those differ by tool and change over time.

## Workflow

### 1. Write the shot in one plain sentence

Before touching the prompt, write a single sentence: who is in the frame, doing what, where. Example: "A tired knight rests in a rainy forest clearing at dawn." If you cannot write that sentence, the idea is not ready to prompt yet.

### 2. Assemble the prompt in weighted order

Many generators give more weight to early text. When that holds for your tool, order matters: put what must survive first.

1. **Subject.** The concrete thing in frame. Name it precisely: "a scarred female knight in dented plate armor", not "a warrior".
2. **Action or pose.** What the subject is doing. Verbs and poses beat adjectives: "sword planted tip-down, standing at ease".
3. **Setting.** Where it happens, with one or two sensory details: "rain-soaked pine forest clearing".
4. **Camera and composition.** Framing terms: "low-angle full-body shot", "close-up", "rule of thirds", "subject centered", "wide establishing shot".
5. **Lighting.** Name the light source and quality: "soft overcast dawn light", "warm window light from the left". "Moody lighting" is too vague to render.
6. **Style tokens.** The medium and look, as a short fixed phrase: "oil painting, muted earthy palette". Reuse this exact string across a series to hold the style steady.
7. **Quality tail.** Finish with rendering quality: "detailed brushwork, sharp focus". Keep it short; a long tail dilutes the subject.

### 3. Trade abstract adjectives for visible detail

Abstract praise gives the generator nothing to draw. Each time you catch a word like "beautiful", "epic", or "stunning", replace it with something visible.

Before: "a beautiful epic warrior woman in a cool fantasy forest"

After: "full-body portrait of a scarred female knight in dented plate armor, sword planted tip-down in the mud, standing at ease in a rain-soaked pine clearing, low-angle shot, soft overcast dawn light, oil painting, muted earthy palette"

Negative: "extra fingers, deformed hands, watermark, text, blurry"
Reason: hands and watermarks are the two most common artifacts in people shots, so these negatives target the actual failure modes instead of generic "bad quality" filler.

### 4. Keep a style sheet for series work

When several images must look like they belong together, freeze the style tokens. Write one style string, store it in a file, and paste it verbatim into every prompt in the series. Example style sheet entry: `series-noir: "black and white film photography, high contrast, deep shadows, grain"`. When your tool supports seeds and you need reproducibility, record the seed with the prompt. Change the subject and setting per image; never paraphrase the style string.

### 5. Add negatives for failure modes you have actually seen

Only when your tool supports negative prompts. Good negatives name specific artifacts and why they appear:

- "extra fingers, deformed hands, extra limbs" : hands are a known weak point; this targets the most common anatomy break.
- "watermark, text, logo, signature" : training data is full of these, so the model may hallucinate them unprompted.
- "blurry, low detail, jpeg artifacts" : use sparingly, since over-negating detail can flatten textures you wanted.
- "crowded, cluttered background" : when the subject keeps getting lost in busy scenery.

Do not stack negatives you have never needed. Each one is a bet about what will go wrong; keep a short list per project and grow it from your prompt log.

### 6. Iterate one variable at a time

1. Generate the first version from the full prompt.
2. Compare it against the acceptance criteria you wrote in step 1 (two to four must-haves, e.g. "full body visible", "armor reads as metal", "no text artifacts").
3. Change exactly one thing: one phrase, one negative, or the camera term. Regenerate.
4. Compare side by side. Keep the winner, note the loser in the log with one line on why.
5. Stop when all must-haves hold, or when two consecutive rounds show no clear improvement. As a rule of thumb, most prompts settle within about five rounds; past that you are usually redecorating, not fixing.

Keep a prompt log. One line per generation: the prompt (or the changed phrase), keep or drop, and why. The log is what turns one good image into a repeatable style.

### 7. Fix common failure modes

- **Mangled hands.** Compose around the weakness: give hands something simple to do (holding a staff, resting on a table), keep them away from the focal area, or frame them out. Add the hand negatives when your tool supports them.
- **Garbled text or logos.** Ask for at most one to three short words in quotes, like a sign reading "OPEN". For anything longer, generate the image without text and add it afterwards in an editor.
- **Style drift across a series.** Reuse the exact style-token string from your style sheet, and the seed when the tool supports one. Drift usually comes from paraphrasing the style, not from the model.
- **Dropped details.** Long prompts lose their tail. Shorten the prompt, move the must-haves to the front, and cut adjectives that describe nothing visible.
- **Muddy or flat lighting.** Name a real light setup instead of a mood. "Soft overcast light" and "warm lamp light from the right" render; "dramatic atmosphere" does not.

## Output Contract

A finished prompt-craft pass delivers:

- The final prompt string, assembled in weighted order.
- The negative string, or a note that the tool does not support negatives.
- The style-sheet entry used, when part of a series.
- The seed, when the tool supports seeds and reproducibility matters.
- Prompt-log entries for each generation round with keep/drop verdicts.
- The acceptance criteria, each marked met or still open.

## Operating Rules

1. Describe what is visible, not how it feels. When a word cannot be drawn, replace it.
2. Front-load the subject. When the tool weights early text more, the first clause decides the image.
3. Freeze style tokens verbatim across a series. Paraphrasing the style is the usual cause of drift.
4. Change one variable per iteration round. Changing three things at once teaches you nothing about which one worked.
5. Add negatives only for failure modes you have seen or have good reason to expect. A long speculative negative list is clutter.
6. Keep any requested text to a few short quoted words, or plan to add text in an editor instead.
7. Name no tool-specific flags, samplers, or parameter names from memory. If a setting cannot be verified in the tool you are using, describe the expected shape and confirm it yourself before relying on it.
8. Write the acceptance criteria before generating, not after. Judging the image against criteria you invent mid-loop is how "good enough" keeps moving.

## Helper script: promptcraft.py

Save the script below as `~/workspace/promptcraft.py` (or anywhere on your PATH). It assembles an ordered prompt from slots, audits it for vague adjectives, text risks, and length, and keeps the prompt log.

```python
#!/usr/bin/env python3
"""promptcraft: assemble and audit image-generation prompts.

Save as ~/workspace/promptcraft.py, then run:

  python3 promptcraft.py build --subject "..." --action "..." --setting "..." \
      --camera "..." --lighting "..." --style "..." [--quality "..."] \
      [--negative "..."]

  python3 promptcraft.py log --prompt "<final prompt>" --verdict keep|drop \
      --note "..."

Log entries append to ./prompt-log.md as a Markdown table.
"""
import argparse
import os
import re

VAGUE = ["beautiful", "amazing", "stunning", "epic", "cool", "nice",
         "awesome", "gorgeous", "incredible", "breathtaking", "fantastic"]
TEXT_HINTS = ["text", "sign saying", "words", "caption", "logo", "title"]
LOG_FILE = "prompt-log.md"

ORDER = [
    ("subject", "who/what is in frame"),
    ("action", "pose or activity"),
    ("setting", "where it happens"),
    ("camera", "framing and camera terms"),
    ("lighting", "light quality and direction"),
    ("style", "medium and style tokens"),
    ("quality", "quality/finish tail"),
]


def build(args):
    parts = [getattr(args, name) for name, _ in ORDER]
    prompt = ", ".join(p.strip() for p in parts if p and p.strip())
    lines = ["PROMPT:", prompt]
    if args.negative:
        lines += ["", "NEGATIVE:", args.negative.strip()]
    else:
        lines += ["", "NEGATIVE: (none given)"]

    warnings = []
    low = prompt.lower()
    vague_found = sorted({w for w in VAGUE if re.search(r"\b" + w + r"\b", low)})
    if vague_found:
        warnings.append(
            "vague adjectives found (%s): pair each with something visible, "
            "e.g. 'beautiful' -> 'clear skin, symmetrical face'"
            % ", ".join(vague_found))
    words = len(prompt.split())
    if words > 75:
        warnings.append(
            "prompt is %d words; generators often drop late details. "
            "Put the must-haves first and cut low-value adjectives." % words)
    if any(h in low for h in TEXT_HINTS):
        warnings.append(
            "prompt asks for text or logos; keep any text to 1-3 short words "
            "in quotes, or drop the text and add it later in an editor.")
    if not args.negative and ("hand" in low or "person" in low
                              or "portrait" in low):
        warnings.append(
            "people in frame but no negative given; consider adding "
            "'extra fingers, deformed hands, extra limbs' if your tool "
            "supports negative prompts.")
    if warnings:
        lines += ["", "AUDIT:"]
        lines += ["- " + w for w in warnings]
    else:
        lines += ["", "AUDIT: clean."]
    print("\n".join(lines))


def log(args):
    exists = os.path.exists(LOG_FILE)
    with open(LOG_FILE, "a", encoding="utf-8") as f:
        if not exists:
            f.write("| prompt | verdict | note |\n|---|---|---|\n")
        p = args.prompt.replace("|", "\\|").replace("\n", " ")
        n = (args.note or "").replace("|", "\\|").replace("\n", " ")
        f.write("| %s | %s | %s |\n" % (p, args.verdict, n))
    print("logged to %s" % LOG_FILE)


def main():
    ap = argparse.ArgumentParser(description="assemble and audit image prompts")
    sub = ap.add_subparsers(dest="cmd", required=True)

    b = sub.add_parser("build", help="assemble an ordered prompt and audit it")
    for name, help_text in ORDER:
        b.add_argument("--" + name, default="", help=help_text)
    b.add_argument("--negative", default="", help="negative prompt (if supported)")

    l = sub.add_parser("log", help="record a prompt and verdict in the prompt log")
    l.add_argument("--prompt", required=True)
    l.add_argument("--verdict", required=True, choices=["keep", "drop"])
    l.add_argument("--note", default="")

    args = ap.parse_args()
    if args.cmd == "build":
        build(args)
    else:
        log(args)


if __name__ == "__main__":
    main()
```
