- name
- illo
- description
- Creates original editorial illustrations where a recurring mascot character performs the idea — one caught scene by default, a hand-built explainer diagram (labeled stages, a fan-out, timeline, loop, or stack) when the structure itself is the point, or a transparent character cutout (pose-only compositing asset, no scene or text) — in one of seventeen bundled looks (sixteen print, plus a photoreal toy-brick set). Also handles "surprise me" / "random" (optionally scoped to a focus or character): rolls provenance, builds three saying candidates, picks via interactive choice or auto-pick-best (`--autopick`), and renders one image. Triggers only when the skill is directly invoked or "illo" is requested; never on generic illustrate / draw / make-an-image requests.
- version
- 0.37.0
- argument-hint
- [idea or article URL] | build a character | install <character> | surprise me [focus] [--autopick] [using character]
- author
- Trevin Chow
- license
- MIT
- metadata
- {"hermes":{"tags":["illustration","riso","image-generation","editorial","mascot","codex","grok","openrouter","muse"],"category":"creative","requires_toolsets":["terminal"]},"openclaw":{"emoji":"🎨","homepage":"https://illo-skill.com","os":["macos","linux"],"requires":{"bins":"[Truncated]"}}}
# Illo
Make original, distinctive editorial illustrations for written content. One
image explains one idea: a key judgment, a flow, a before/after, a trap, a
loop. A **recurring mascot** is the one performing the idea in every scene —
the subject, never decoration. When one idea advances through stages, it can
be a **mini-comic**: 2–4 panels inside a single image. And when the idea is
itself a traceable structure — a pipeline, labeled stages, a fan-out, a
timeline, a loop — it can be an **explainer**: the same mascot and look
drawing the structure as a hand-built sketch-diagram with arrows and
callouts (`references/composition.md`, "Two registers" and "Pick the
diagram type"; editorial scene is always the default). A named pipeline
or recipe is **labeled stages** inside that register — named phases in
order, one connected system, pack-solved for this body, never a new look.
Or a **character cutout**: the mascot alone on a transparent PNG for downstream overlay
— pose and contact continuity only, no idea, no text, no environment
(`references/cutout.md`).
This is a configurable house style, not a generic image generator. The
**methodology is the constant**; the **character pack and palette are the
parameters** — and a character pack carries its **style** with it: one look
per pack, chosen from the bundled look library (riso — grainy halftone,
ink-layer offset, paper grain, one bold softly-rounded outline — plus
blueprint, woodcut, pixel, clay, manila, chalk, phosphor, enamel,
gouache, felt, diorama, sketchbook, bricks, fizz, bloom, and snes) or a custom style file. The default mascot is
**Blot**, a deadpan ink-drop in riso. Palettes come
from presets, the user's own palette file, or one derived color. Whatever the
parameters, it is intentionally not a photo — with one deliberate exception, the
`bricks` look, a toy-brick photography style — not a logo, not a corporate
infographic, not a formal boxes-and-diamonds flowchart look, not a UI
mockup. Asking for a flowchart still means labeled stages in the pack's
look — the formality ban is a look constraint, not a refusal of the word.
## Use cases — route the request
| The user wants | The path |
|---|---|
| **Illustrate an article / post / newsletter / URL** | Steps 0–7: route the source first (thesis → coverage: hero / hero+set / set / mini-comic — `references/composition.md`, "Source routing"), then shot list (hero row + anchors), one image per anchor, interleave by placement. |
| **One image for a single concept** | Step 1 concept branch (up to ~3 quick questions if the idea is thin), then a single image. |
| **Surprise / random** — "surprise me", "random", "surprise me with art quote using bray", "surprise me --autopick" | Read `references/surprise.md` in full: Step 0 first, then character + provenance (ignore `defaultCharacter`; `* quote` forces a cited quote; else ~1/3 roll), build **three** safe candidates, interactive picker or auto-pick-best (`--autopick` preferred for schedulers), then register from the locked saying, then Steps 3–7 as one image. Deliver saying + image. Poster titles default off; mini-comics still get per-panel labels. |
| **A sequence — story beat, before→after, fail→fix** | One **mini-comic** when the progression sits in one place (shape routing in `references/composition.md` — the idea picks the shape, the destination never does). A specified process diagram / flowchart / labeled workflow is labeled stages, not this row. |
| **A traceable structure** — "show the flow", "as labeled stages", "label the steps", "walk the stages", "diagram the pipeline", "like that factory diagram", "map the steps", "as an explainer", or specified flowchart / labeled-workflow / process-diagram intention | The **explainer register** (`references/composition.md`, "Pick the diagram type" and "The explainer register"): a hand-built labeled-stages / flow / fan-out / timeline / loop / stack / system slice in the active look, the mascot a working part of it. Specified flowchart / labeled-workflow / process-diagram intention locks **labeled stages** in the pack's look — the formal-flowchart ban is a look constraint (no Visio, no title/legend/grid), not a refusal of the word. Labeled stages is a structure type inside explainer, not a new register or look — pack-solve it for the active character before the prompt. BEST when a unit's thesis IS a named pipeline, recipe, or staged process; never the automatic choice for every explainer. |
| **Social-ready art for X posts / article body images** | 16:9 (or 1:1 when square is explicitly useful), bold `ink-punch`, watermark with the `x` handle if configured or asked. |
| **X Article banner / hero image** | Use the unique banner format: **1536 × 640 px** when the user asks for an X Article hero/banner. Prompt and render through the normal `illo.py generate` image pipeline, with normal, undistorted character/object proportions and crop-safe breathing room. Do not satisfy this by manually compositing or rebuilding crops from another image unless the user explicitly asks for post-processing. |
| **Blog / brand / site-matched art** | A named or custom palette, or derive the palette from one dominant color (`references/palettes.md`). |
| **Their own mascot** — "make me a character", "use our mascot", "replace Blot" | The character builder: read `references/character-builder.md` in full and follow it end to end. |
| **Community characters** — "what characters are available", "install blip", "install all characters", "update mole", "publish my character" | `references/pack-sharing.md` — engine `packs list/show/install/update`, including `packs install --all`; publish via a GitHub PR. |
| **A different look** — "in blueprint", "woodcut style", "pixel version of blip" | Styles travel with character packs: build a **style variant pack** via `references/character-builder.md`, "Style variants". |
| **Options to pick from, or "which model is best"** | Step 5b: `--count` variations or a model loop → `gallery` with a recommendation. |
| **Fix an existing image** (stray title, recolor, mascot too decorative) | Edit prompts in `references/prompt-recipe.md`, passing the image back as `--ref`. |
| **Character cutout / transparent PNG / overlay sticker** — "just the mascot", "no background", "paste on something else" | The **cutout register** (`references/cutout.md`): read in full, prompt from `references/prompt-recipe.md` "Cutout variant", generate with `--cutout` and `--aspect 1:1`. OpenRouter cutouts default to GPT Image 2 (not Grok). Not for explaining an idea — reroute to editorial if the ask needs a scene. |
| **Animated idle / bot avatar / looping GIF of the mascot** | The **cutout register** plus `references/cutout.md`, "Idle loop / bot avatar": one transparent 1:1 cutout with `--cutout` and the character sheet as `--ref`, then programmatic motion on that PNG. |
## Prerequisites
The engine (`scripts/illo.py`, stdlib Python, no installs) renders through one
of **three engine backends** plus **two agent-side transports**; `python3` and
network access are the only hard requirements. **Grok Bot** (Cursor's Grok
Bot / the Grok desktop assistant) is an agent-side transport: use its built-in
Grok image tool directly, not `illo.py generate`, when no user config
explicitly selects an engine backend. **Muse** (Meta's personal assistant,
Blip) is the other agent-side transport: when *you* are Blip, build the illo
prompt per this skill and call your native image-generation tool with the
active character sheet attached as a reference. Other agents that happen to
expose some image API must not take either native path — the agent must be
able to call its own built-in image tool *and* be named above.
**Running the engine — set `$SKILL_DIR` inline in each block.** Every engine
command below is `python3 "$SKILL_DIR/scripts/illo.py" …`. Set `SKILL_DIR` to the
absolute path of the directory this `SKILL.md` was loaded from (it contains
`scripts/illo.py` and `assets/`) **in the same command block that uses it** — shell
state does not persist between separate command runs, so a value set in an earlier
block is gone by the next. If the harness does not expose that path, find the
installed `scripts/illo.py` and use its parent; if neither resolves, stop rather
than guessing the working directory. The engine self-locates its own bundled
assets, so `$SKILL_DIR` only has to be right enough to launch `illo.py` and to
point `--ref` at the bundled character sheet.
Write the block **flatten-safe** — some hosts (Codex observed) collapse a fenced
block to one line, turning a newline into a space. Terminate the assignment with
`;` (`SKILL_DIR="…";` — without it, a flattened `SKILL_DIR="…" python3 "$SKILL_DIR/…"`
becomes an env-prefix whose `$SKILL_DIR` expands to empty **before** the assignment
applies, so the path collapses to `/scripts/illo.py`). Put **no comment on an
assignment or command line** (a flattened `#` comments out the rest of the line and
the command silently vanishes), and keep each invocation on **one line** (a
flattened `\` continuation injects stray arguments). A wrong or unset value makes
`doctor` (Workflow step 0) fail loudly (`can't open file …/scripts/illo.py`) — the
signal to fix the path, not a skill fault.
- **Codex backend (free for Codex subscribers).** When the host has a usable
**Codex CLI** — installed, `codex login`-ed, with the `image_generation`
feature — illo can generate through the user's Codex subscription at no
per-image charge (it draws on their Codex quota). No API key, no token: illo
only shells out to the user's own CLI. Detected, not assumed; gpt-image-2 is
automatic; unsupported on Windows/WSL.
- **Grok CLI backend (free for Grok/xAI subscribers).** When the host has a usable
**Grok CLI** — installed and `grok login`-ed — illo can generate through the
user's Grok subscription via `grok -p` (headless), drawing on their Grok
quota. Same env-free, token-free subprocess design as Codex. **Grok returns
JPEG with no alpha, so it cannot make transparent cutouts** — those auto-fall
back to a cutout-capable backend. The image tool exposes no model selector.
- **Grok Bot native transport (agent-side, free for Grok Bot users).** When
**you are Grok Bot** — specifically Cursor's Grok Bot / the Grok desktop
assistant with the built-in Grok image tool — build the illo prompt and call
that tool with the active character's model sheet as a reference image. Do
not require the Grok CLI, Codex CLI, or an OpenRouter key; do not treat a
missing engine backend as a reason to run `init`. This is not a generic
"host image API" rule and not an `illo.py --backend` value.
- **OpenRouter backend (paid, direct or explicit fallback).** Needs an
**OpenRouter API key** in the user's config file — the **single credential
channel** — written once by the user-run `init` (mode 600). The engine never
reads secrets from the environment and never accepts them as command-line
arguments. A host without a subscription CLI can select this engine path directly.
A failed Codex/Grok CLI render does **not** spend money automatically: paid
fallback requires `--allow-paid-fallback`. It is **model-selectable**
(`--model`).
Capsule of the backend/transport model (resolution and precedence, the CLI
requirements, the Grok Bot native path, the built-in image tool being
automatic, quota vs. charge, cutout limits, Windows/WSL, fallback): **read
`references/backends.md` in full before choosing or explaining a backend** —
the mechanics live there, once.
### Setup is the user's job (never enter the key yourself)
Entering an API key is something the **user** does. Do not type, paste, print,
or store the user's key — direct them to bootstrap it:
- **Bootstrap (user runs it):** `python3 "$SKILL_DIR/scripts/illo.py" init` —
prompts for the key at a hidden prompt (never echoed) and writes the
YAML config `${XDG_CONFIG_HOME:-~/.config}/illo/config.yaml` (mode 600). It
can also store non-secret defaults: `--model`, `--palette`, `--aspect`,
`--character`, `--watermark`. Use `--no-key` to update preferences without
touching the stored key. (The config is read via PyYAML when installed;
without it a minimal built-in parser still reads the flat keys — `apiKey`,
`model`, … — so generation needs no installs. Only nested settings like
`watermark` need PyYAML: `python -m pip install 'PyYAML==6.0.2'`.)
- **Non-secret prefs may be seeded** for the user with the same command and
`--no-key`, but the key itself is theirs to enter.
### Hermes Agent only: binary asset repair preflight
Some Hermes versions corrupt binary files (the bundled character sheets) when
installing multi-file skills from GitHub — text files survive, binaries don't,
and a corrupted sheet silently breaks the character lock. **Under Hermes
Agent**, run this once before first use (and whenever `doctor` reports
`assets: CORRUPTED`):
```bash
bash ${HERMES_SKILL_DIR}/scripts/repair-hermes-assets.sh
```
It verifies every bundled binary against known-good SHA256 hashes
(`assets/checksums.txt`) and re-downloads only mismatched files from pinned,
immutable URLs — a no-op when everything checks out. Under Claude Code,
Codex, OpenClaw, or any runtime that installs faithfully: skip this; `doctor`
checks asset integrity everywhere and will say if repair is ever needed.
## Read these references as needed
Do not load everything at once. Pull the file that matches the step:
- `references/visual-style.md` — riso, the house default look: the risograph technique, line language, paper/ink, hard do/don'ts.
- `references/styles/<name>.md` — the rest of the look library (`blueprint`, `woodcut`, `pixel`, `clay`, `manila`, `chalk`, `phosphor`, `enamel`, `gouache`, `felt`, `diorama`, `sketchbook`, `bricks`, `fizz`, `bloom`, `snes`), consumed by character packs. Read the active character's style file in full before generating.
- `references/character.md` — the character rules (the load-bearing test, anti-complexity guardrails, value-follows-palette, the **interaction model** — declared per pack or derived conservatively from the locked design and reference sheet), the default character **Blot**, and the custom-pack format. Read before any character work.
- `references/character-builder.md` — the guided flow for designing and installing a user's own mascot. Read in full before building or replacing a character.
- `references/pack-sharing.md` — installing characters from the community repo and publishing a pack via PR. Read before any install/publish request.
- `references/palettes.md` — named presets, default resolution, custom palettes, **and the derive-a-palette-from-one-color algorithm**. Read in full before choosing or deriving any palette.
- `references/composition.md` — the two registers (editorial scene / explainer diagram), the diagram-type picker, the explainer's structure types and budget (including labeled stages, arrow notes, and its pack-solve), stagings, turning an idea into a move, the **anatomy-action feasibility gate** (validate the contact map against the character's interaction model before rendering), the no-recycled-composition rule, and the shot-list format.
- `references/cutout.md` — the cutout register: transparent compositing assets, contact continuity, pose vocabulary, and generate flags. Read in full before any cutout request.
- `references/surprise.md` — surprise / random mode: preflight-first, scope parse, random character, provenance variety + three saying candidates (optional parallel verify for sourced modes), interactive picker or `--autopick` / auto-pick-best, full re-roll on refresh, register after the locked saying, saying bar + sense bar, multi-source quote verification, safety-before-offer, headless contract. Read in full before any surprise/random request.
- `references/backends.md` — the three-backend image engine plus the Grok Bot native transport: how the engine backend resolves (precedence Codex > Grok > OpenRouter, and the self-identify rule), when Grok Bot bypasses `illo.py generate`, the Codex/Grok CLI requirements, artifact-first success, the built-in image tool being automatic (no model selection), quota-vs-charge, Grok's no-cutout limit, Windows/WSL, and opt-in paid fallback. Read before choosing or explaining a backend.
- `references/models.md` — the model lineup (**OpenRouter backend only**): friendly-name → OpenRouter id map, traits, aspect caveats, 404/fallback handling. Read before passing any `--model`.
- `references/prompt-recipe.md` — the generation prompt template and the edit/recolor prompts.
- `references/quality-bar.md` — the post-generation checklist and iteration rules. Read before delivering.
`assets/character-reference.webp` is the default character's canonical model
sheet — the consistency anchor (used by the engine, below); a custom pack
brings its own. Style-calibration examples are **not bundled** — each style
file links its own by URL (fetch when needed): study line density, negative
space, and accent restraint. **Never copy their compositions** — invent a
fresh metaphor for the current piece.
## Workflow
### 0. Preflight
Before generating, confirm the engine is ready:
```bash
SKILL_DIR="<path to this skill>";
python3 "$SKILL_DIR/scripts/illo.py" doctor
```
Run the `illo.py` call standalone — never chained with `&&` — so the displayed exit code is
the readiness signal itself (0 = ready): a chained neighbor's failure paints
a healthy check as an error.
It reports python, the config path, the resolved model/palette defaults,
whether a **custom character pack** or **custom palettes file** exists,
**Codex/Grok CLI detection and the resolved backend/transport**, and whether an
OpenRouter key is found (without revealing it); exit 0 = the resolved backend
is ready. An OpenRouter-only install (no subscription CLI) stays exit 0 —
readiness follows the resolved backend, not a hardwired key check
(`references/backends.md`).
**Grok Bot native path (agent-side).** If you are **Grok Bot** (Cursor's Grok
Bot / the Grok desktop assistant with the built-in Grok image tool) and the
user has not explicitly chosen `backend: openrouter`, `backend: codex`, or
`backend: grok`, initialize the agent-side transport before relying on
`doctor`:
```bash
SKILL_DIR="<path to this skill>";
python3 "$SKILL_DIR/scripts/illo.py" init --backend grok-bot --no-key
```
Then run `doctor` normally: exit 0 is meaningful readiness for this path.
Missing Codex CLI, Grok CLI, or OpenRouter key are not failures when
`backend: grok-bot`; corrupted assets, unreadable custom packs, malformed
palettes, or the wrong `SKILL_DIR` still fail and must be fixed before
rendering. Generate in Step 5 by calling Grok Bot's built-in Grok image tool
with the active model sheet reference. This route is only for Grok Bot's own
Grok image tool; other agents with unrelated image tools must use the engine
backends below.
Short path for Grok Bot: run `init --backend grok-bot --no-key` once when
backend is unset/auto, run `doctor` for assets/config/packs, use `packs`
commands normally (including `packs install --all` after install so community
characters are local), read the same references, build the same prompt, then
call Grok Bot's built-in Grok image tool with the active character reference.
Skip `illo.py init` for OpenRouter unless the user explicitly wants OpenRouter
or another engine backend default, and skip `illo.py generate` unless the user
explicitly selected an engine backend.
**Muse native path (agent-side).** If you are **Blip** (Meta's personal
assistant, Muse) and the user has not explicitly chosen an engine backend
View on GitHub