From pai-pro
Generates or edits images on a filmmaking canvas via a local CLI, with canvas-aware conventions for character portraits, location stills, edits, variations, and storyboard mosaics.
How this skill is triggered — by the user, by Claude, or both
Slash command
/pai-pro:image-composeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
All patterns below shell out to:
All patterns below shell out to:
node "$PAI_REPO_ROOT/server/scripts/generate_image.js" --prompt "..." [--aspect-ratio 16:9] [--image-size 2K] [--ref-image-url URL ...] [--label "..."] [--subtype <character|location|edit|reference|split>] [--name "..."] [--role "..."] [--description "..."] [--source-node-id <id>] [--ref-source-id <id> ...]
$PAI_REPO_ROOT is exported by the viewer — see CLAUDE.md § "Media CLIs / Invocation path".
Calls go via --stage — see CLAUDE.md § "Draft gate". Pass run_in_background: true on the Bash call and poll with BashOutput — the PreToolUse hook blocks foreground attempts (each parallel-staged variant needs the flag too).
--label defaults to the truncated prompt (≤30 chars) if omitted; pass an explicit one when you have a better caption.
When references are passed, refer to them in the prompt positionally as @Image1, @Image2, … in URL-passing order — same convention as the video skill. The corresponding --ref-source-id <node-id> must accompany each --ref-image-url (same positional order); that's how the CLI knows which source nodes to draw provenance edges from.
If a canvas note authored this image (a shot note rendered as a still, a script note designing a character / location), pass --source-node-id <note_id> — see CLAUDE.md § "Authorship edges".
Do not attempt to invent images via ASCII art or markdown embedding — call the CLI.
Pick the one that fits. When unsure, read ./workflow.json first to see what's already on the canvas (reads are unrestricted; only writes go through the mutator).
Triggers: "design / create / introduce / cast a character / protagonist / antagonist / hero / villain / lead / portrait / headshot".
node "$PAI_REPO_ROOT/server/scripts/generate_image.js" --prompt "..." --aspect-ratio 9:16 --image-size 2K --subtype character --name "Detective Morris" --role "..." --description "..." — no --ref-image-url. A character is an identity anchor, not a derivative.
[style] character portrait of [NAME], [role]. [age, build, wardrobe, distinguishing features]. Front-facing medium close-up, eye-level, looking directly at camera, neutral expression. Plain neutral background, soft even lighting. No dramatic shadows, no stylized lighting, no side profile, no multiple views.
--ref-source-id.Triggers: "establish / design / picture [LOCATION]", or "yes" to a script-compose parse offer listing locations.
node "$PAI_REPO_ROOT/server/scripts/generate_image.js" --prompt "..." --aspect-ratio 16:9 --image-size 2K --subtype location --name "Causeway" --description "..." — no --ref-image-url. A location is a setting anchor, not a derivative.
[style] establishing still of [LOCATION NAME]. [visual brief — architecture, lighting, atmosphere]. Wide shot, eye-level, no characters present.
script-compose parse offer (i.e. the last location in the run), offer the user one short line: "Locations are up — want me to lay out a storyboard per location?" Bridges into Pattern 6. Skip if locations were designed ad-hoc, not from a script offer.Triggers: "change / edit / swap / replace / add / remove / tweak / what-if", OR "make a turnaround / 3D version / alternate style / variation" — applied to an image already on the canvas.
Identify the source node (usually the most recent image_result, or one the user named). Grab source.id, source.image_url, source.metadata.aspect_ratio.
node "$PAI_REPO_ROOT/server/scripts/generate_image.js" --prompt "..." --aspect-ratio <source ratio> --image-size <source size or 2K> --ref-image-url <source.image_url> --subtype edit --source-node-id <source.id> --ref-source-id <source.id>.
Prompt as a transformation, not a full re-description:
<concrete change>. Preserve everything else.
✅ "Change the rain to falling snow. Keep the detective, wardrobe, and camera framing unchanged." ✅ "Render as a full 3D turnaround sheet of the same character. Preserve face, wardrobe, and proportions." ❌ "A detective in a snowy alley at night wearing a trench coat…" — over-specifies, identity drifts.
The CLI emits the derived edge from <source.id> based on --ref-source-id.
Multi-step chains (A → B → C) use one edge per step. Do not flatten to a single A → C — make N separate CLI calls, each with the previous result as the new source.
Triggers: "put [character] in [setting]", "a shot of [X] and [Y]", "[character] does [action] in [location]" — when at least one canvas character is involved.
image_result of that person (up to 16). Collect each one's image_url and id.node "$PAI_REPO_ROOT/server/scripts/generate_image.js" --prompt "..." --aspect-ratio <fit the shot> --image-size 2K --ref-image-url <char1.image_url> --ref-image-url <char2.image_url> ... --ref-source-id <char1.id> --ref-source-id <char2.id> ... (same positional order).--subtype — a scene is neither a character nor an edit. CLI emits one derived edge per --ref-source-id.Triggers: a fresh image unrelated to existing canvas content ("generate a mountain at dusk", "a noir alley — just the setting").
node "$PAI_REPO_ROOT/server/scripts/generate_image.js" --prompt "..." with sensible defaults (16:9, 2K unless the user asks otherwise). No subtype, no refs.Triggers: user asks for a storyboard, mosaic, NxN / N×M grid, shot list, coverage, keyframe sheet, shot planning, or image previs. The intent is ONE composite image with N×M panels per location, NOT one image per panel and NOT a video.
generate_image.js (standard tier). Layout fidelity is best at ≤4 cells; past that, cells drift in framing and identity. Warn the user one short line before firing for any grid larger than 2×2: "Heads up — the standard image tier loses layout fidelity past ~4 cells; cells may drift in framing."node "$PAI_REPO_ROOT/server/scripts/generate_image.js" --prompt "..." --aspect-ratio 16:9 --image-size 2K --label "Storyboard — <location>" per mosaic. The pattern's point is one composite image per location, not N×M small generations.16:9 — this is filmmaking. Each panel inside the mosaic is a 16:9 cinematic frame, and the overall sheet should also feel cinematic landscape. The grid shape (3×3, 4×2, etc.) describes cell layout, NOT canvas shape. Only override 16:9 if the user explicitly says "portrait", "square", "vertical", or names a different ratio:
16:9 (default)1:19:169:16--ref-image-url + matching --ref-source-id (≤16 each) so identity stays locked across cells and the provenance edges land. The script-analyzed case (shot notes + locations on the canvas) triggers one-mosaic-per-location iteration with required ref ordering — see references/storyboard-mosaic.md.3×3 or larger, warn first: "grids past 2×2 lose layout fidelity on the standard tier — consider a smaller grid or per-panel generation."For the canvas pre-flight, per-location iteration logic, no-location nudge, verbatim prompt template, and default 9-panel coverage when no script slice exists: see references/storyboard-mosaic.md.
image_result node PER mosaic.--label ("Storyboard — <location_name>" if per-location; "Storyboard" if location-less), and stamps metadata.task_type: "storyboard_mosaic", metadata.grid: "NxM", metadata.location_id / metadata.shot_ids if you pass them via additional metadata flags. (Storyboard-mosaic-specific extras: pass --ref-source-id <location_id> and one per shot reference; the CLI emits derived edges from each.)One sentence with the price — see CLAUDE.md § "Draft gate".
See CLAUDE.md § "Failure handling". limits.max_image_refs is 16. For content_filtered, propose softer wording.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub utopai-research/pai-pro --plugin pai-pro