Skip to content

keryx cover

Generate cover art via the image provider (Gemini/Imagen) — a port of the blog gen-cover.py. Resolves an article theme for the style prefix + aspect and appends the per-post --scene.

keryx cover --scene "a quiet desk at dawn" [--theme <article-kw>] [--model <id>] [--n N] [--out cover]
keryx cover --prompt "a fully-formed prompt …"      # bypass theme style + scene
Flag Meaning Default
--scene the per-post scene (required unless --prompt)
--prompt a fully-formed prompt used verbatim (bypasses theme style + scene)
--theme article theme (style prefix + aspect) configured article default
--model force a specific image model id config providers.gemini.model, else the fallback chain
--n number of candidate samples 1
--out output path prefix → <out>-<i>.png cover
--dry-run estimate what this would cost and generate nothing false

Credential: GEMINI_API_KEY (env). Image generation is non-deterministic — make several samples and pick a clean one. Add --output json for the list of paths.

When you get fewer samples than you asked for

The image providers filter candidates non-deterministically, so a request for --n 3 can come back with two. keryx asks the provider again for the shortfall, up to three extra attempts, and writes the full batch when they arrive.

If even that cannot reach the count, the run warns and still writes what it got, naming both numbers:

WARN fewer cover images than requested; re-run to generate more requested=3 written=2

The exit status stays 0 — a partial batch is still usable, and you were going to look at every sample anyway. The warning exists because the alternative is choosing from a sample of one while believing you asked for two, which quietly removes the fallback the "generate several, pick a clean one" workflow depends on. Scripting a batch? Count the paths in --output json rather than trusting the exit code.

Costing a run before making it

--dry-run prices the run and generates nothing — no provider call, no files, no spend. It needs no API key, so it also works as a way to check what a command would do before you configure credentials.

keryx cover --scene "an over-engineered toaster" --n 4 --dry-run
dry run: would generate 4 cover take(s)
  est. spend:  ~USD 0.12
  best-effort estimate, and an upper bound — an unchanged request is served from the cache without spending.
nothing was generated and nothing was written.

The estimate is an upper bound on the images you asked for: an unchanged request is served from the content cache without spending (R-GLOBAL-9), so a real run may cost nothing. The one case that can exceed it is a topped-up short batch — if the provider returns fewer samples than --n, keryx asks again for the shortfall, and a provider that bills per requested sample rather than per returned image would charge for those extra attempts. You never receive more images than --n. Rates come from providers.image.price_per_image and friends — round, tunable cues rather than a vendor's billed rate. When the estimate crosses spend.confirm_above.*, the dry run says so. --output json gives the same figures structured, which is how an assistant driving keryx over MCP asks what something costs before committing.

keryx cover --scene "an over-engineered toaster, one idea" --theme editorial --n 4
# writes cover-1.png … cover-4.png

Provider-selected via providers.image (default Gemini) — see Providers. Tries the Imagen models, falling back to the Gemini image models (parity with the script). R-GEN-1..3.