bundles/ai-agents/skills/codex-image-gen/SKILL.md
Generate raster images (icons, illustrations, textures, app icons) from a text prompt by driving the Codex CLI's image tool, then extracting the finished PNG from the Codex session rollout. Use when an agent needs a real generated image and has no native image-generation tool. Requires the `codex` CLI, logged in.
npx skillsauth add shipshitdev/library codex-image-genInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Generate a real raster image from a text prompt when the running agent has no
native image generator. The mechanism is non-obvious: the headless codex exec
path genuinely calls the image tool, but it never writes the PNG to disk — only
the Codex desktop app has the plumbing that polls the async job and saves it.
The finished image is still recoverable, because its full base64 PNG is recorded
in the session rollout JSONL. Run the prompt, read the session id Codex prints,
open the matching rollout, and decode the largest result string to a PNG.
Inputs:
Outputs:
Creates/Modifies:
External Side Effects:
Confirmation Required:
Delegates To:
codex CLI is installed and logged in, and shelling out to it is allowed.If a native image tool exists, prefer it. If the deliverable is a logo or crisp vector, prefer a vector workflow.
Running codex exec "draw a ..." and then watching ~/.codex/generated_images/
produces nothing: that folder is written by the desktop app's async-job poller,
not by codex exec. People conclude CLI image generation is impossible. It is
not — the completed image lives in the session rollout as the result field of
the image-generation response item. This skill reads it from there.
Avoid shell-escaping pain by putting the prompt in a file. Be explicit — the model has no other context:
NO text, NO letters, NO words unless you specifically want type.REPO_TMP="$(git rev-parse --show-toplevel)/.tmp"
mkdir -p "$REPO_TMP"
cat > "$REPO_TMP/img-prompt.txt" <<'PROMPT'
A single app icon: a glossy translucent envelope on a soft blue-to-violet
gradient, Liquid Glass style, centered with even padding, no text, no letters,
1:1 square, high detail.
PROMPT
codex exec and capture stdoutcodex exec -s read-only "$(cat "$REPO_TMP/img-prompt.txt")" 2>&1 | tee "$REPO_TMP/codex-run.log"
Run it in the foreground. exec returns once the turn completes; in practice
the result is already written to the rollout by then.
Codex prints a session id: <uuid> line. Pull it from the captured log:
SESSION_ID=$(grep -oE 'session id: [0-9a-f-]{36}' "$REPO_TMP/codex-run.log" | awk '{print $3}')
echo "session: $SESSION_ID"
The rollout lives at ~/.codex/sessions/YYYY/MM/DD/rollout-*<session-id>*.jsonl.
Walk it, find the largest result string (the base64 image), and decode it. Use
the bundled helper:
python3 scripts/extract-codex-image.py "$SESSION_ID" "$REPO_TMP/out.png"
Confirm the file exists and is a real PNG before using it:
test -s "$REPO_TMP/out.png" && file "$REPO_TMP/out.png" # expect: PNG image data, 1254 x 1254
If extraction finds no base64 result, the turn did not actually generate an
image (e.g. the model answered in text). Re-run step 2 with a more explicit
"generate an image" instruction.
Default output is roughly 1254×1254 PNG, RGB, no alpha. Resize / strip alpha
with sips on macOS:
sips -z 1024 1024 "$REPO_TMP/out.png" --out "$REPO_TMP/icon-1024.png" # downscale
sips -s format png "$REPO_TMP/out.png" --out "$REPO_TMP/flat.png" # normalize
scripts/extract-codex-image.py (also reproduced here so the procedure is
self-contained):
import json, sys, base64, glob, os
session_id, out = sys.argv[1], sys.argv[2]
sess = max(
glob.glob(os.path.expanduser(f"~/.codex/sessions/**/*{session_id}*.jsonl"), recursive=True),
key=os.path.getmtime,
)
best = None
def walk(o):
global best
if isinstance(o, dict):
for k, v in o.items():
if k == "result" and isinstance(v, str) and len(v) > 100000:
if best is None or len(v) > len(best):
best = v
else:
walk(v)
elif isinstance(o, list):
for v in o:
walk(v)
for line in open(sess):
try:
walk(json.loads(line))
except Exception:
pass
if best is None:
sys.exit("no base64 image result found in rollout — the turn may not have generated an image")
open(out, "wb").write(base64.b64decode(best))
print("WROTE", out)
REPO_TMP="$(git rev-parse --show-toplevel)/.tmp"
mkdir -p "$REPO_TMP"
# 1. Generate a 1:1 icon master.
cat > "$REPO_TMP/icon-prompt.txt" <<'PROMPT'
App icon: a glossy translucent envelope, Liquid Glass style, soft blue-to-violet
gradient background, centered, even padding, no text, no letters, 1:1 square.
PROMPT
codex exec -s read-only "$(cat "$REPO_TMP/icon-prompt.txt")" 2>&1 | tee "$REPO_TMP/codex-run.log"
SESSION_ID=$(grep -oE 'session id: [0-9a-f-]{36}' "$REPO_TMP/codex-run.log" | awk '{print $3}')
python3 scripts/extract-codex-image.py "$SESSION_ID" "$REPO_TMP/icon-master.png"
# 2. Make a 1024 master with no alpha (iOS rejects alpha on the marketing icon).
sips -z 1024 1024 "$REPO_TMP/icon-master.png" --out "$REPO_TMP/AppIcon-1024.png"
sips -s format png --setProperty hasAlpha false "$REPO_TMP/AppIcon-1024.png" --out "$REPO_TMP/AppIcon-1024.png"
# 3. Slice into an AppIcon.appiconset (iOS single-size 1024 + the macOS ladder).
mkdir -p AppIcon.appiconset
cp "$REPO_TMP/AppIcon-1024.png" AppIcon.appiconset/icon_1024.png
for sz in 16 32 64 128 256 512 1024; do
sips -z "$sz" "$sz" "$REPO_TMP/AppIcon-1024.png" --out "AppIcon.appiconset/icon_${sz}.png"
done
# Author Contents.json mapping each size/scale to its file, then verify:
# actool --compile "$REPO_TMP/actool-out" --app-icon AppIcon --platform iphoneos \
# --minimum-deployment-target 17.0 AppIcon.appiconset # expect a clean compile
codex alias may inject --dangerously-bypass-approvals-and-sandbox,
which overrides any -s read-only you pass (the session then reports
danger-full-access). Harmless for pure image generation, but worth knowing.
Prefer running codex exec in the foreground — a backgrounded full-access run
can trip auto-approval classifiers.exec ends when the turn completes; the result is already
in the rollout by then in practice. Still assert the file exists and decodes —
do not assume.codex-cli 0.141.0. This depends on a Codex CLI
internal: the rollout result field. A future Codex may change the rollout
schema or add a first-class --save-image / output-path flag. If such a flag
exists, prefer it and keep this rollout-extraction path as the fallback. If the
extractor finds no base64 result, first check whether the rollout layout
changed under ~/.codex/sessions/.development
Coordinates a weekly engineering review of board accuracy, recent code changes, operational health, and scoped cleanup. Use for a recurring repository health review or a review of the last several days.
testing
Audits project board configuration and prepares explicitly requested setup, copy, or normalization changes while preserving the existing workflow and provider boundaries. Use when inspecting a board's fields, columns, scope, or configuration.
testing
Reconciles a project board with current work and delivery evidence, reports incomplete coverage and metadata gaps, and applies only approved provider-supported field changes. Use when auditing board drift, reviewing blocked work, or assessing upcoming delivery.
development
Walk through how a subsystem works. Use for "how does X work", code walkthroughs before changing something, and placement or ownership questions. Explains architecture, runtime flow, and onboarding mental models. Can critique architecture. Use why for motivation.