| name | short-drama-generation |
| description | Use when the user asks to create a short drama from a prompt — e.g. "生成短剧", "制作微短剧", "make a short drama", "turn this script into a short drama", "ショートドラマを生成", "숏드라마를 만들어". Orchestrates script, storyboard, optional character images, per-shot video clips, and ffmpeg composition through the existing media tools. |
| allowed-tools | ["Bash","Read","Write","Canvas","CanvasState"] |
| user-invocable | true |
| disable-model-invocation | false |
| requires-action | true |
You orchestrate a short drama by driving the existing media tools yourself. There is
no single short-drama-generation tool. allowed-tools is guidance; media generation is enforced by
the internal tool permission path.
Trigger only on a request to CREATE/generate a short drama. Requests to analyze,
rewrite, summarize, or brainstorm a script do NOT trigger this skill unless the user
asks to produce the drama. Progress-only or promise-only replies are not completion:
after starting, either drive the pipeline or report the concrete blocker plainly.
Gating mechanism (confirm each authored stage before spending credits)
Each stage that authors content (script, storyboard, character images, per-shot
results, final order) is gated through the existing Canvas/CanvasState tools so
the user reviews and edits a draft before you act on it. The mechanism uses no new
infrastructure — it is render → end-turn → re-entry → read-back:
- In a desktop inline environment, render the draft with
Canvas, using
canvasId = canvas-drama-<slug>-stage<N> (N = the stage number; <slug> is just a
cosmetic label — the backend assigns the real canvasId). Then end the
current turn — do NOT busy-wait or poll inside the skill.
- The user edits the Canvas and submits; submission automatically starts a new turn.
That new turn first calls
CanvasState with the same canvasId to read back the
confirmed values, then writes artifacts and advances to the next stage.
- Non-desktop environment (no inline submit / no turn re-trigger): degrade to
text-based per-stage confirmation, stating plainly that there is no visual gating in
this environment.
- Never call
imagegen/videogen and never advance on draft values before the
confirmation for that stage has been read back.
Pipeline (run in order; skip any stage whose inputs the prompt already supplies)
Form the drama <id> as <slug>-<session8>, where <slug> is a short kebab slug of
the drama title and <session8> is the first 8 characters of the session id (the
sessionId field returned by every CanvasState read-back — first available after the
Stage 0 read-back). The <session8> suffix is what makes each run land in a fresh
directory; without it, re-running a similar prompt collides with an earlier run's files
and the manifest write fails. Project files go under .puffer/media/drama/<id>/.
Mint <id> exactly once — when you first create .puffer/media/drama/<id>/ (the
Stage 0 manifest write, which happens after the Stage 0 read-back) — then reuse that
exact <id> verbatim for every later stage, recovering it from your own earlier
writes in the conversation. Never re-derive a bare title slug, and never write into a
pre-existing drama directory. (Same session asked for a second drama → append -2,
-3, ….) Generated image/video artifacts are written by the tools to
.puffer/media/images|videos/ — you only reference them, never relocate them.
-
Models (gate before any credit-consuming stage). Confirm the image and video
provider/model up front. Both are mandatory.
Render Canvas with canvasId = canvas-drama-<slug>-stage0 whose body is a single
mediaModelSelect node:
{ "type": "Canvas", "canvasId": "canvas-drama-<slug>-stage0",
"spec": { "title": "Models", "body": [ { "type": "mediaModelSelect" } ] } }
The node is self-populating: it fetches the connected image/video capabilities itself,
seeds each dropdown from the currently-saved global media defaults, renders an in-place
"connect a provider in Settings" prompt for any kind with no connected provider, and on
confirmation persists the choice back to the global media settings. Do not fetch
capabilities, build options, or branch on empty lists yourself. Then end the turn.
In the next turn read back with CanvasState (same canvasId): values carries
{imgProvider,imgModel,vidProvider,vidModel}. Validate: if imgModel or
vidModel is empty, stop and report that both image and video models must be selected (direct the
user to Settings if a kind has no provider). Record these values in manifest.json for Stages 3/4 — this
manifest write first creates .puffer/media/drama/<id>/, so this is where you mint
<id> = <slug>-<session8> using the from this read-back.
Manifest (your working ledger — keep it simple)
Maintain .puffer/media/drama/<id>/manifest.json as you go. It is a plain ordered list,
not a schema'd artifact:
{
"id": "<id>",
"shots": [
{ "shotId": "shot-001", "status": "succeeded", "prompt": "...", "imageReferences": ["https://..."], "videoArtifactId": "...", "videoPath": ".puffer/media/videos/<aid>/..." }
],
"final": ".puffer/media/drama/<id>/final.mp4"
}
Failure contracts (never paper over)
- If a kind has no connected provider, the Stage 0 node shows an in-place "connect a provider in
Settings" prompt and that model stays empty; on read-back, stop and tell the user to connect a
provider for that kind in Settings — never fall back to text-to-video or to config defaults.
- If
imgModel or vidModel is empty on read-back, stop and report that both image and video models
must be selected before continuing.
- Never pass
--aspect to imagegen; squareness comes only from the prompt's square
clause. This sidesteps "axis ratio value not allowed" / "ratio not mapped" rejections on
models that only support their own default ratio. If an image still comes back slightly
non-square, that is acceptable for a reference frame — do not retry with a ratio flag.
- A parallel media batch (Stage 3 images or Stage 4 videos) raises the media approval
once for the whole turn. Choose Always allow when first prompted so later chunks
run with no further prompt; "Approve once" only covers the current turn, so each chunk
re-prompts. A trailing chunk of a single call uses the normal single-command path and may
prompt on its own unless Always allow was chosen — this is expected, not an error.
- Do not advance any gated stage on draft values — wait for the stage's confirmation to
be read back first.
- If
CanvasState returns no value for a gated stage (the user did not submit), report
"no confirmation received" and stop; do not fall back to the draft.
- In a non-desktop environment (no Canvas / no inline submit), degrade to text-based
per-stage confirmation — including provider/model — and say so plainly; do not skip the
confirmation.
- If a chosen video provider is Relaydance (prompt-only) and the user wants image
references, report that the configured provider does not support image references.
- If ffmpeg is unavailable or composition fails, report it plainly and keep the
per-shot clips; do not claim a composed drama was produced.
- Report final-video success only when
final.mp4 actually exists; a missing final
video can still leave useful per-shot clips — say so rather than implying success.
- Do not hand-author placeholder media (SVG, stills, stub mp4) and present it as
generated output.