From pai-pro
Writes and rewrites screenplays, splits scripts into shot notes, and designs characters/locations from script input.
How this skill is triggered — by the user, by Claude, or both
Slash command
/pai-pro:script-composeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Run only on explicit user intent — never on a file drop. If the user just dropped a script, the bridge already wrote a filename-reference note; do nothing more until they ask.
Run only on explicit user intent — never on a file drop. If the user just dropped a script, the bridge already wrote a filename-reference note; do nothing more until they ask.
Director defaults: a 30s beat is ONE moment; trust silence; match the user's input language.
Classify the input, then capture as in §2. Never skip straight to §3.
read ./uploads/<filename> first. Pick a 2–5 word title in the user's language (use the script's own if present).image-compose / video-compose.Torn between screenplay and story? Prefer screenplay — safer than rewriting.
Rewrite rules (story → screenplay):
INT./EXT. LOCATION - TIME slug, present-tense action, ALL-CAPS cue + dialogue. No scene numbering. No camera directions (that's video-compose).ONE note. No split, no further action. All writes go through the
canvas mutator — a PreToolUse hook blocks direct Write / Edit on
workflow.json.
read ./workflow.json (read-only inspection — see if title is already set).subtype: "script" so the renderer applies the script-card chrome and video-compose can recognise it without label parsing:
node "$PAI_REPO_ROOT/server/scripts/canvas_mutate.js" \
--op addNode \
--payload-json '{"node":{"type":"note","data":{"subtype":"script","label":"Script: <title>","body":"<full screenplay verbatim>","metadata":{"author":"agent","timestamp":"<ISO>"}}}}'
Stdout returns assigned.node_id — keep it for §3 (shots derive from this id).node "$PAI_REPO_ROOT/server/scripts/canvas_mutate.js" --op setTitle --payload-json '{"title":"<title>"}'
Captured. Want me to split it into ≤15s shots and pull out the characters / locations?STOP. Do NOT proceed to §3 without an explicit user command.
Triggers (judge intent): "split into shots / clips", "break this up", "pull the characters / locations", "who's in this", "analyze this script", "design the characters from this script". Not triggers: "what's in this", "summarize", "tell me about it" — those are read-and-reply.
When triggered:
-2, -3.ceil(total_seconds / 15) shots) — not rigid; sub-divide smaller when a hard cut or strong beat genuinely demands it, but don't over-fragment just because the script's own time markers say so. Pacing: ~2.5 dialogue words/sec; silent action ~3–5s. Never rewrite when splitting — each shot body is a verbatim slice. Each shot note carries subtype: "shot" so video-compose can locate them structurally and the canvas renders the shot-card chrome. Build ONE addBatch payload with N shot notes + N derived edges from the script note, and apply it in one mutator call:
node "$PAI_REPO_ROOT/server/scripts/canvas_mutate.js" \
--op addBatch \
--payload-json '{
"nodes": [
{"type":"note","data":{"subtype":"shot","label":"Shot 1 (0–15s)","body":"<slice>","metadata":{"author":"agent","timestamp":"<ISO>"}}},
{"type":"note","data":{"subtype":"shot","label":"Shot 2 (15–30s)","body":"<slice>","metadata":{"author":"agent","timestamp":"<ISO>"}}}
],
"edges": [
{"from":"<script_note_id>","to":"$0","kind":"derived"},
{"from":"<script_note_id>","to":"$1","kind":"derived"}
]
}'
$N placeholders are 0-indexed positions in nodes; the mutator resolves them to the assigned ids after running. Reply's assigned.node_ids is the array of shot ids in the same order.
I see <N> character(s) (<names>) and <M> location(s) (<names>). Want me to design them?Skip if N=0 AND M=0. On "yes", route toimage-compose(one character portrait + one location still per name) with--source-node-id <script_note_id>so the new node wires back to the script. Don't generate here.
If the user's command was narrower ("just the shots", "only characters"), do only that sub-step and skip the offer.
Surgical (title still fits): update script-note body + affected shot bodies in place. Use the mutator's updateNode op (one call per node, or batched via updateBatch).
Structural (title no longer fits): new script note (addNode); old→new edge addEdge with kind:"derived"; new shot family via addBatch against the new script note. Leave old shots; delete only if asked (deleteNode cascades the edges + group memberships for you).
npx claudepluginhub utopai-research/pai-pro --plugin pai-proCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.