@wji.ai/cli 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/dist/commands/canvas.js +91 -12
- package/dist/commands/fsnode.js +30 -1
- package/dist/commands/gen.js +4 -19
- package/dist/commands/workspace.js +6 -2
- package/dist/index.js +4 -1
- package/package.json +1 -1
- package/skill/short-drama/SKILL.md +305 -0
- package/skill/short-drama/examples/night-shift/episodes/E01.json +1883 -0
- package/skill/short-drama/examples/night-shift/project.json +774 -0
- package/skill/short-drama/reference/canvas-recipes.md +199 -0
- package/skill/short-drama/reference/craft-motion.md +116 -0
- package/skill/short-drama/reference/craft-shots.md +177 -0
- package/skill/short-drama/reference/craft-story.md +188 -0
- package/skill/short-drama/reference/dialogue-craft.md +120 -0
- package/skill/short-drama/reference/schema.md +435 -0
- package/skill/short-drama/reference/stage-art.md +56 -0
- package/skill/short-drama/reference/stage-characters.md +206 -0
- package/skill/short-drama/reference/stage-outline.md +103 -0
- package/skill/short-drama/reference/stage-script.md +113 -0
- package/skill/short-drama/reference/stage-storyboard.md +465 -0
- package/skill/short-drama/reference/workspace-layout.md +118 -0
- package/skill/short-drama/scripts/selftest.mjs +1411 -0
- package/skill/short-drama/scripts/short-drama.mjs +3371 -0
- package/skill/wji/SKILL.md +97 -22
- package/skill/wji/reference/canvas-recipes.md +16 -2
- package/skill/wji/reference/commands.md +15 -6
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Stage 2 — Characters
|
|
2
|
+
|
|
3
|
+
Turn every `characters[]` entry into a reusable asset: a profile people read, an English portrait
|
|
4
|
+
prompt, a 16:9 character-sheet prompt, and a voice prompt — then the three assets WJI needs for a
|
|
5
|
+
character to hold across shots: **headshot** (icon), **sheet**, **voice** (`primaryAudio`).
|
|
6
|
+
|
|
7
|
+
## Profile (drama language)
|
|
8
|
+
|
|
9
|
+
`oneLiner`, `profile.gender/ageRange/identity/appearance/personality(3–5 words)/temperament/
|
|
10
|
+
motivation/relationships/evidence`. Inferred facts are fine when marked (`(推断)`); `evidence`
|
|
11
|
+
holds verbatim quotes only, or stays empty. Keep the tier from the outline — if it looks wrong,
|
|
12
|
+
change the outline, not this stage.
|
|
13
|
+
|
|
14
|
+
### `identityEn` — the name-free handle
|
|
15
|
+
|
|
16
|
+
One short English phrase, ≤ 80 characters, distinct per character: `the woman in her mid thirties in
|
|
17
|
+
headphones`, `the stocky man in his sixties with a grey crew cut`. Prompts may never carry a name,
|
|
18
|
+
but they constantly have to say *who* — who speaks this line, who turns — and this is the words
|
|
19
|
+
they use. Pick something visible and permanent (age, build, the thing they always wear), not a
|
|
20
|
+
plot role: the model can see a grey crew cut, it cannot see "the retired security guard". Gated:
|
|
21
|
+
present, English, name-free, no two characters alike.
|
|
22
|
+
|
|
23
|
+
## The bible — `bible` and `profile.relationships` (gates `bible`, `relationships`)
|
|
24
|
+
|
|
25
|
+
A profile says what they look like; the bible says how they got here, how they act now and what
|
|
26
|
+
the story will make them pay. Method in `craft-story.md` §7. By tier:
|
|
27
|
+
|
|
28
|
+
- every character: `bible.function` — 主角 / 共同主角 / 反派 / 爱情线 / 导师 / 盟友 / 镜像 / 压力测试者 /
|
|
29
|
+
喜剧调节 (the dramatic job, independent of the tier);
|
|
30
|
+
- `support` and `lead`: `hookLine` (身份 + 核心矛盾 + 外在欲望 + 风险), `want` and `need` (not the same
|
|
31
|
+
sentence), `misbelief`, `contradiction`, `pressureResponse`, `speech.rhythm` and `speech.silence`
|
|
32
|
+
(plus `catchphrase`, `forbiddenWords`, an optional `register` override);
|
|
33
|
+
- `lead`: also `wound` (150–250 字, the choice and what they believed after), ≥ 2 `pastChoices`,
|
|
34
|
+
`firstImpression` → `finalUnderstanding`, exactly three `forbidden` and three `mustFace`, and an
|
|
35
|
+
`arc` of ≥ 3 stages with the 高潮选择 among them, each bound to an episode.
|
|
36
|
+
|
|
37
|
+
`profile.relationships[]` becomes the 关系网: `{id, name, surface, real, power, secret, turnEpisode}`.
|
|
38
|
+
Every lead has at least one with `surface` + `real` + `power`, and somewhere in the cast a
|
|
39
|
+
relationship carries a `secret` — that is where the reversals come from.
|
|
40
|
+
|
|
41
|
+
**`bible.speech` is the source of the voice.** `voice.pace` / `videoDirection` and every line's
|
|
42
|
+
`delivery` are written from `rhythm`, `silence` and `forbiddenWords`; `seed script` prints them in
|
|
43
|
+
the `seedNote` and `render dialogue` puts them beside each line for the polish pass.
|
|
44
|
+
|
|
45
|
+
## Image prompts (English, always)
|
|
46
|
+
|
|
47
|
+
- **No names, aliases, author or title anywhere** — image models paint what they remember of a
|
|
48
|
+
name. Describe the person.
|
|
49
|
+
- **Ethnicity + era + region explicit** (`East Asian, Han Chinese features`, `contemporary 2020s
|
|
50
|
+
Chinese city`, `Republican-era river town`). Left out, the model defaults to a contemporary
|
|
51
|
+
Western face. Both are gated.
|
|
52
|
+
- `image.prompt`: an expressive three-quarter bust on a neutral background, soft directional key
|
|
53
|
+
light, shallow depth of field. Realism comes from imperfection: uneven skin tone, visible pores,
|
|
54
|
+
asymmetry in eyelids and brows, stray hairs, fabric with weave and wear. Put the individual
|
|
55
|
+
description first and make it long — two similar-age characters written as "template + age +
|
|
56
|
+
jacket colour" come out as the same person (`distinct` gate, 75 % word overlap).
|
|
57
|
+
- `image.negativePrompt`: with `style: photoreal` (live action — the right choice for a
|
|
58
|
+
drama shot like a TV series) ban *drawing*: `illustration, anime, cartoon, painting`, and
|
|
59
|
+
**never** `photorealistic`, `film grain` or `skin texture`. With `style: realistic` ban *fakeness* (plastic skin, doll face, dead
|
|
60
|
+
eyes, helmet hair, flat fabric) and **never** `photorealistic` / `3d render`; with `ghibli` you
|
|
61
|
+
**must** ban `photorealistic`. Both gated. Always add `logo, brand name, text` — a "store
|
|
62
|
+
uniform" comes back wearing a real chain's badge otherwise (seen in testing), and a real brand
|
|
63
|
+
on a character is a problem in every shot after it.
|
|
64
|
+
- `image.sheet`: the model-sheet layout, written out in full every time:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
┌──────────┬────────────────────────────┐
|
|
68
|
+
│ bust │ front side back │ ONE 16:9 canvas, pure white background
|
|
69
|
+
│ portrait ├────────────────────────────┤ LEFT ZONE ≈ 34 % width: head-and-shoulders bust,
|
|
70
|
+
│ (face │ detail · detail · detail │ both shoulders complete, flat bottom edge
|
|
71
|
+
│ ref) │ │ RIGHT-TOP: three full-body views, same height,
|
|
72
|
+
└──────────┴────────────────────────────┘ same head-to-body ratio, PROPORTIONS ARE CRITICAL
|
|
73
|
+
RIGHT-BOTTOM: 4–5 small detail studies;
|
|
74
|
+
the detail studies give way, not the figures
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Plus: `all views share the SAME face as the bust portrait`, `LIGHTING IN THE LEFT ZONE ONLY:
|
|
78
|
+
soft directional key…`, `LIGHTING IN THE RIGHT ZONES: flat even orthographic lighting`, the
|
|
79
|
+
style's render phrase (`STYLE_PRESETS[style].render` — `node scripts/short-drama.mjs` exports
|
|
80
|
+
it; copy it verbatim), `No text, no watermark`. Gated: `16:9`, render phrase, no names.
|
|
81
|
+
|
|
82
|
+
## Voice
|
|
83
|
+
|
|
84
|
+
Six local-language fields (`timbre` `pitch` `pace` `accent` `emotion` + `referenceHint` optional)
|
|
85
|
+
for people, one English `voice.prompt` for a voice-design engine, and a **`voice.sample`** — the
|
|
86
|
+
short acted line that becomes the character's voice asset.
|
|
87
|
+
|
|
88
|
+
### `voice.sample` — the 3–5 s reference
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
"sample": { "line": "這裡是海風電台。", "direction": "貼著麥克風,像只講給一個人聽" }
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- **`line`**: one line in the drama's language, **3–5 s at `params.speechCharsPerSecond`**
|
|
95
|
+
(default 2.4 — the voice model speaks far slower than the 4.5 chars/s the script uses for
|
|
96
|
+
reading, so that is **8–12 Chinese characters**, not twenty). Gated. No quote marks: the
|
|
97
|
+
engine reads them.
|
|
98
|
+
- **`direction`**: how *this line* is acted, in the drama's language.
|
|
99
|
+
- Pick a line that shows the character's default register, not a plot moment: it is a timbre
|
|
100
|
+
reference, and every later clip inherits it.
|
|
101
|
+
|
|
102
|
+
### The prompt the model actually receives
|
|
103
|
+
|
|
104
|
+
`prompt <project> voice C01` composes it from the fields you already wrote — you do not write it
|
|
105
|
+
by hand, and it is a **parenthetical instruction followed by the line**:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
(三十五歲上下女;基隆地方電台深夜節目主持人;性格冷靜、自持、嘴硬、念舊;偏低的胸腔共鳴,帶一點沙;
|
|
109
|
+
中低音域,起伏小;慢,句尾拖半拍;台灣口音國語,捲舌音輕;克制的溫柔,預設是平靜;
|
|
110
|
+
此刻貼著麥克風,像只講給一個人聽)這裡是海風電台。
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
In order: age + gender, identity, `profile.personality`, then `voice.timbre` / `pitch` / `pace` /
|
|
114
|
+
`accent` / `emotion`, then `此刻` + the sample's `direction`. `params.speechAccent` is appended
|
|
115
|
+
only when `voice.accent` is empty — the drama-language wording wins. Quote marks are stripped,
|
|
116
|
+
because inside the parenthetical they would be read aloud.
|
|
117
|
+
|
|
118
|
+
**The parenthetical is obeyed, not spoken.** Measured on Seed Audio 1.0: the same 8-character line
|
|
119
|
+
ran 3.16 s bare and 3.50 s behind a ~100-character description — speaking that description would
|
|
120
|
+
have added some forty seconds. It came back slower and lower, as the description asked. So write
|
|
121
|
+
the voice fields richly: personality, rhythm, where the voice sits, what it does at the end of a
|
|
122
|
+
phrase. That detail is free and it is the only thing separating two characters of the same age.
|
|
123
|
+
|
|
124
|
+
The English `voice.prompt` stays as the static voice identity (≤ 400 characters, a parameter
|
|
125
|
+
string, **no quoted lines** — gated) for engines that take a description instead of a sample.
|
|
126
|
+
|
|
127
|
+
### `voice.videoDirection` — the clause every clip carries
|
|
128
|
+
|
|
129
|
+
One English clause, ≤ 160 characters, that goes **beside every line this character speaks in every
|
|
130
|
+
video prompt**:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
slow and close-miked, low and chest-resonant with a light rasp, falling at the end of every phrase,
|
|
134
|
+
Taiwanese-accented Mandarin, restrained
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
It is `voice.prompt` compressed to what a video model can act on in one breath: pace, where the
|
|
138
|
+
voice sits, what it does at the end of a phrase, accent, default attitude. Without it a video model
|
|
139
|
+
reads every character the same way — evenly, brightly, like a newsreader — and the clip stops
|
|
140
|
+
matching the voice asset the same bible produced. Gated in stage 2 (present, English, ≤ 160 chars,
|
|
141
|
+
no names, no quoted lines) and again in stage 5: a segment prompt must carry the direction of every
|
|
142
|
+
character who speaks in it. For `wan`, the drama-language `timbre` / `pace` / `accent` / `emotion`
|
|
143
|
+
fields are used instead, since that prompt is Chinese.
|
|
144
|
+
|
|
145
|
+
## On WJI
|
|
146
|
+
|
|
147
|
+
Dataset per character in `character/`:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
wji dataset new <character folder> "<name>" -t character -d "<oneLiner>" # bind characters.C01.wji.datasetId
|
|
151
|
+
node {baseDir}/scripts/short-drama.mjs render <project> character C01 > /tmp/C01.md
|
|
152
|
+
node {baseDir}/scripts/short-drama.mjs prompt <project> voice C01 > /tmp/C01-voice.txt
|
|
153
|
+
wji fs set <id> --merge content=@/tmp/C01.md --merge image_prompt=@/tmp/C01-sheet.txt \
|
|
154
|
+
--merge audio_prompt=@/tmp/C01-voice.txt --merge metadata.shortDrama=@/tmp/C01.json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`characters` canvas, one row per character (see `canvas-recipes.md` §1):
|
|
158
|
+
|
|
159
|
+
| Node | Model | Ratio | Prompt |
|
|
160
|
+
| --- | --- | --- | --- |
|
|
161
|
+
| headshot (image, generate) | GPT Image 2.5 Sunburst (or the best image model listed) | 1:1 | `image.prompt` + `clean front-facing portrait, plain background` |
|
|
162
|
+
| sheet (image, generate, `--from` headshot) | same | 16:9 | `@image1 is the face to match exactly. ` + `image.sheet` |
|
|
163
|
+
| **voice (audio, generate, `--from` headshot)** | **`seed-audio-1.0`** | — | `prompt voice C01` — `(direction)line` |
|
|
164
|
+
|
|
165
|
+
**The voice node references the headshot.** Seed Audio 1.0 does 参考图片配音: given the face it
|
|
166
|
+
invents a voice that fits it, which is why the headshot must be approved first. It takes exactly
|
|
167
|
+
one reference, so pass only the headshot node. About 3.6 credits a sample.
|
|
168
|
+
|
|
169
|
+
Generate headshots first (a set of headshots is a batch — quote the total), **look at them**
|
|
170
|
+
(download and open), then sheets and voices from the approved headshots. Check a sheet for: one
|
|
171
|
+
face across all views, three full bodies of equal height, no squashed figures, white background.
|
|
172
|
+
Regenerate rather than accept a sheet with two faces — every shot inherits it. Listen to the voice
|
|
173
|
+
before you attach it: the age and the weight of the voice should match the face.
|
|
174
|
+
|
|
175
|
+
Attach and bind:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
wji fs set <datasetId> --add-images <headshotAsset> <sheetAsset> <voiceAsset> --icon <headshotAsset> --set primaryAudio=<voiceAsset>
|
|
179
|
+
node {baseDir}/scripts/short-drama.mjs bind <project> characters.C01.wji.assets.headshot <headshotAsset> # and sheet / voice, nodes.*
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The voice lives in **three** places on the dataset, and all three matter: `images[]` (the asset
|
|
183
|
+
list takes audio too — the clip is then playable from the dataset card next to the headshot and
|
|
184
|
+
the sheet), `primaryAudio` (which one is *the* voice), and `content.audio_prompt` (the brief that
|
|
185
|
+
produced it, so it can be regenerated identically). `render character` puts the same brief, the
|
|
186
|
+
sample line and its acting direction into the readable 声线 section of `content.content`.
|
|
187
|
+
|
|
188
|
+
**The sample is a dry voice.** Seed Audio is an audio-scene generator — asked for a line it will
|
|
189
|
+
happily add a room, a score and a coffee machine — so the composed prompt ends its parenthetical
|
|
190
|
+
with `干声,无配乐无音效`. The same model makes the segments' soundtrack beds in stage 5
|
|
191
|
+
(`prompt audio`), where the room, the score and the coffee machine belong.
|
|
192
|
+
|
|
193
|
+
### What the voice asset is for
|
|
194
|
+
|
|
195
|
+
It is a **reference for later**, not a deliverable. Every ref-capable video model takes audio
|
|
196
|
+
references (`wan-3.0-ref` up to 5, ≤ 15 s total; `seedance-2.5-ref`; `minimax-h3-ref` up to 3), so
|
|
197
|
+
on an episode canvas the character's voice display node connects to the video node alongside the
|
|
198
|
+
sheets and becomes `@audio1…`, holding the timbre across clips the way the sheet holds the face.
|
|
199
|
+
Set `params.voiceSamples: false` only when no TTS model is enabled on the account — `manifest`
|
|
200
|
+
then stops asking for the node.
|
|
201
|
+
|
|
202
|
+
## Gates
|
|
203
|
+
|
|
204
|
+
`profile` · `bible` · `relationships` · `char-prompts` · `voice` · `distinct`. Missing assets are
|
|
205
|
+
reported by `manifest`, not by `validate` — a character without a voice model available is still
|
|
206
|
+
a valid character.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Stage 1 — Outline
|
|
2
|
+
|
|
3
|
+
Turn the source (novel, synopsis, or an original idea) into the structure everything else hangs
|
|
4
|
+
on: what stays, who is who, where the beats land, what happens each episode. **Skeleton before
|
|
5
|
+
episodes, sign-off between them.**
|
|
6
|
+
|
|
7
|
+
## Inputs
|
|
8
|
+
|
|
9
|
+
1. The source text the user pointed at — read it; never invent content from a title. If it is
|
|
10
|
+
long, summarise per volume in a scratch file first and keep quotes for `keep[].evidence`.
|
|
11
|
+
2. The four parameters (episodes × minutes, genre, adapt mode, ratio) — already in `params`.
|
|
12
|
+
3. Preferences the user stated (keep this character, keep that scene) — honour them and record
|
|
13
|
+
them in `outline.keep`.
|
|
14
|
+
|
|
15
|
+
## Order of work (the order is the method)
|
|
16
|
+
|
|
17
|
+
0. **Brief.** `outline.brief` — the six judgements of `craft-story.md` §1 (logline, audience,
|
|
18
|
+
the 爽点 engine, the main hook, the pressures on both sides, the emotional line, the promise).
|
|
19
|
+
A one-line idea is not enough to fill these; ask the user the focusing questions rather than
|
|
20
|
+
inventing a genre. Then `params.register` (语体) if not given at `init`.
|
|
21
|
+
1. **Cut.** A short drama carries one main line and at most one sub-line. Everything else goes to
|
|
22
|
+
`outline.cut` with a `why`. Any adapt mode other than `原创` / `忠实` must cut something.
|
|
23
|
+
Write `cutNote`: what the story's endpoint becomes after the cuts.
|
|
24
|
+
2. **Merge people.** Characters with the same function become one face. Then tier them:
|
|
25
|
+
`lead` (1–5, the protagonists and main antagonist, `arc` required), `support` (≤10, named
|
|
26
|
+
secondary characters, `arc` required), `functional` (≤10, a role label as the name, no arc).
|
|
27
|
+
Unnamed background people are not listed. Write `mergeNote`: why these are the leads.
|
|
28
|
+
3. **Collect scenes.** Primary scenes are capped at `4 + ⌈episodes/10⌉` (5–15) — generated scenes
|
|
29
|
+
are cheap, but every extra one is another consistency asset to maintain. A scene used in only
|
|
30
|
+
one episode needs a `reusePlan` (which existing environment it derives from, what changes) or
|
|
31
|
+
becomes a `variantOf` in Stage 3.
|
|
32
|
+
4. **Place the beats (爽点).** Majors first — where the big reveals land — then minors to fill.
|
|
33
|
+
Rules: adjacent beats ≤ `maxBeatGap` (3) episodes apart, a beat in episode 1, a beat in the
|
|
34
|
+
finale, at least one major, and the first major never on the last episode. `type` follows the
|
|
35
|
+
genre (打脸 / 揭破 / 反转 / 逆袭 / 认亲 …).
|
|
36
|
+
5. **Pick the narrative props.** After the beats, not before — a prop is a prop because it carries a
|
|
37
|
+
beat. Test: *if it broke, got lost or was swapped, would the plot collapse?* Yes → `props[]`
|
|
38
|
+
with `function` and `beatIds`. No → set dressing; it becomes a scene anchor in Stage 3. At most
|
|
39
|
+
8.
|
|
40
|
+
6. **Structure and guideline.** `outline.structure` — golden / escalation / climax with episode
|
|
41
|
+
ranges (first ~20 % / ~50 % / ~30 %; `craft-story.md` §2) and a task, 爽点 plan and hook plan
|
|
42
|
+
each. `outline.guideline` — the 创作指引: tone, emotional intensity, dialogue style, visual
|
|
43
|
+
style, production limits, ≥ 3 forbidden rules, 3–5 priorities. It is the document every later
|
|
44
|
+
stage re-reads (`render guideline`), so write it as rules, not as a summary.
|
|
45
|
+
|
|
46
|
+
Run `validate --stage outline` now. The `episodes` gate will fail (no episodes yet) — every other
|
|
47
|
+
gate should pass before you show the skeleton.
|
|
48
|
+
|
|
49
|
+
## Sign-off ⛔
|
|
50
|
+
|
|
51
|
+
Show the user, in plain language: the lines cut, the people merged, the episode of each major beat.
|
|
52
|
+
Wait for a yes. Change the skeleton on their feedback and validate again.
|
|
53
|
+
|
|
54
|
+
## Episodes (batches of ≤10)
|
|
55
|
+
|
|
56
|
+
Design first, prose second. Per episode the 分集目录 fields: `function` (开局钩子 / 冲突升级 /
|
|
57
|
+
身份揭露 / 情感推进 / 大反转 / 清算收束), `task` (`goal` / `obstacle` / `stake`), `change` (这一集改变了
|
|
58
|
+
什么 — one sentence; "they talked" means the episode is not strong enough), `satisfaction`
|
|
59
|
+
(`type` from the 爽点 library + the visible `action` that does it), `hookType` (身份 / 危机 / 结果 /
|
|
60
|
+
情感 / 反转 / 道具). The first three episodes are the retention window: 1 grabs (开局钩子), 2 proves
|
|
61
|
+
the 爽点 (its type is the brief's engine), 3 widens the line (冲突升级 / 身份揭露 / 大反转); the finale
|
|
62
|
+
includes 清算收束 or 大反转. Then `synopsis` (narrative prose — a quotation mark means you are writing dialogue, which
|
|
63
|
+
is Stage 4's job), `hook` (the question that makes them tap the next episode), `suspense` (what is
|
|
64
|
+
still buried), `sceneIds`, `characterIds`, `propIds`, `crowdPlan` when three or more characters
|
|
65
|
+
appear (how the group is staged: who is foreground, who is over-the-shoulder, or "never in one
|
|
66
|
+
frame — separate scenes"), and `warnings` from the fixed list (雨戏 / 肢体接触 / 人群 / 手部特写 —
|
|
67
|
+
the things video models get wrong; over-report).
|
|
68
|
+
|
|
69
|
+
A beat placed on an episode must actually happen in that episode's synopsis. Every character,
|
|
70
|
+
scene and prop must be used somewhere — `refs` will name the idle ones.
|
|
71
|
+
|
|
72
|
+
## Sync
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
node {baseDir}/scripts/short-drama.mjs render <project> outline > /tmp/outline.md
|
|
76
|
+
wji dataset new <story folder> "<title> 大纲" -t outline -d "<core sentence>" # once; bind wji.outlineDatasetId
|
|
77
|
+
wji fs set <id> --merge content=@/tmp/outline.md --merge viewMode=content \
|
|
78
|
+
--merge metadata.shortDrama=@/tmp/outline.json # outline block + params
|
|
79
|
+
node {baseDir}/scripts/short-drama.mjs render <project> guideline > /tmp/guideline.md
|
|
80
|
+
wji dataset new <story folder> "<title> 创作指引" -t outline -d "<logline>" # once; bind wji.guidelineDatasetId
|
|
81
|
+
wji fs set <id> --merge content=@/tmp/guideline.md --merge viewMode=content \
|
|
82
|
+
--merge metadata.shortDrama=@/tmp/guideline.json # {brief, structure, guideline, register}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The 创作指引 is its own dataset because it is the thing to open first, every time — before a
|
|
86
|
+
character, an episode or a storyboard, print `render guideline` and read it.
|
|
87
|
+
|
|
88
|
+
Re-run the same `fs set` after every outline change; the dataset is a view of `project.json`.
|
|
89
|
+
|
|
90
|
+
## Gates (what `validate --stage outline` checks)
|
|
91
|
+
|
|
92
|
+
`params` · `brief` · `structure` · `guideline` · `adaptation` · `tiers` · `scenes` · `props-cap` ·
|
|
93
|
+
`beats` · `episodes` · `episode-design` · `refs` — see the gate labels in the output; each failure
|
|
94
|
+
names the entity and the fix.
|
|
95
|
+
|
|
96
|
+
## Rhythm for a short drama
|
|
97
|
+
|
|
98
|
+
A 短劇 episode is paced by its 爽点, not its minutes: plan **one beat per 30–45 s** plus the
|
|
99
|
+
cliff, so a 1-minute episode carries 1–2 beats and a 90-second one 2–3 (the gates only demand one
|
|
100
|
+
per episode and a gap ≤ 3 — the rhythm is on you). Put the hook's image inside the first three
|
|
101
|
+
script beats, a beat near the midpoint, and let the cliff be the last thing on screen. Adding beats
|
|
102
|
+
to an existing episode is fine — append new ids (`B11`, `B12`), never renumber, and list them in
|
|
103
|
+
that episode's `beatsClaimed`.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Stage 4 — Script
|
|
2
|
+
|
|
3
|
+
One episode per task, batches of ≤3 episodes before moving on — the script is the layer that gets
|
|
4
|
+
rewritten most; small batches fail cheaply. **The script handles drama; the storyboard handles
|
|
5
|
+
shooting** — no shot numbers, no camera, no prompts here.
|
|
6
|
+
|
|
7
|
+
## Start from the seed
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
node {baseDir}/scripts/short-drama.mjs seed <project> script --ep 1
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`episodes/E01.json` now has `targetSeconds`, `hook`, `cliff`, `beatsClaimed`, `hookBeat: [1, 1]`,
|
|
14
|
+
the episode design copied from the outline (`function`, `task`, `change`, `hookType`),
|
|
15
|
+
`satisfactionBeat: null` (to be set) and a `seedNote` with the synopsis, the 爽点 to realise, the
|
|
16
|
+
register, the guideline's priorities and forbidden rules, every speaker's `bible.speech`, the ids
|
|
17
|
+
allowed in this episode, the crowd plan, the warnings, the previous episode's cliff and the beat
|
|
18
|
+
budget. **Before writing: `render guideline`, then the scene bibles (anchors, lighting states) and
|
|
19
|
+
the speakers' `render character` — the bible's `speech` is where the lines come from** (see
|
|
20
|
+
`dialogue-craft.md`). Delete `seedNote` when the scenes are written.
|
|
21
|
+
|
|
22
|
+
## Hard rules (all gated)
|
|
23
|
+
|
|
24
|
+
1. **The duration budget is the wall.** Lines cost `chars ÷ 4.5` s, action beats 2.5 s; the
|
|
25
|
+
episode must land within ±15 % of `targetSeconds`. 60 s ≈ 18–22 beats, 120 s ≈ 35, 180 s ≈ 50.
|
|
26
|
+
Over: cut action beats first. Under: add conflict, never small talk.
|
|
27
|
+
2. **One line, one breath**: ≤ 35 non-whitespace characters. Split long lines with a reaction
|
|
28
|
+
beat in between.
|
|
29
|
+
3. **Speakers are in the scene** (`characters`), or `VO` (inner voice — whose, in `delivery`).
|
|
30
|
+
VO is emphasis, not a crutch: two or three per episode at most.
|
|
31
|
+
4. **Action beats: one thing each, narrative, common actions only, no quotation marks.** The video
|
|
32
|
+
model performs actions it has seen a million times — walk in, sit down, hand over, nod, turn,
|
|
33
|
+
grab, hold tight. Precise physical interactions (block a pole with an oar), micro-expressions
|
|
34
|
+
(lashes tremble), inch-level moves, metaphors — rewrite or delete. Drama comes from the
|
|
35
|
+
*combination and timing* of ordinary actions.
|
|
36
|
+
5. **Every scene has an action beat.** Three lines with nothing happening is radio.
|
|
37
|
+
6. **Cold open.** The hook's concrete image is on screen within the first 3 beats (`hookBeat`
|
|
38
|
+
points at it; gated) and it is *in motion* — running with the suitcase, not a still close-up of
|
|
39
|
+
it. Static detail close-ups belong to the second appearance.
|
|
40
|
+
7. **The last beat is the cliff** — an unfinished sentence, an unexplained action, a sound with no
|
|
41
|
+
source. `cliff` describes the effect; the flow delivers it.
|
|
42
|
+
8. **Claimed beats are performed.** Each id in `beatsClaimed` (seeded from the outline) needs a run
|
|
43
|
+
of consecutive beats that actually realises setup → payoff.
|
|
44
|
+
9. **The 短剧 rhythm** (`rhythm`): the hook image by `hookSeconds` (15 s) of cumulative time; a
|
|
45
|
+
turn — new information, an obstacle, a counter, a choice — at least every `turnSeconds` (30 s),
|
|
46
|
+
marked `turn: true` on the beat; the 爽点 (`satisfactionBeat`) after the hook, before the last
|
|
47
|
+
beat, on an action beat or a line with an action beside it; never more than
|
|
48
|
+
`maxConsecutiveLines` (3) lines in a row; action beats at least `minActionRatio` (35 %) of the
|
|
49
|
+
lines. The seconds come from the same duration math as the budget.
|
|
50
|
+
10. **The design is realised** (`script-design`): `function`, `task`, `change`, `hookType` equal
|
|
51
|
+
the outline episode's; the 爽点 type the outline promised is what `satisfactionBeat` delivers.
|
|
52
|
+
11. **Filmable** (`filmable`): no 心想 / 内心 / 回忆起 / 脑海, no 像是 / 似乎 / seems to, no more than
|
|
53
|
+
three hand verbs in a beat (`craft-shots.md` §generability).
|
|
54
|
+
12. **The register** (`dialogue-register`, advisory): lines match `params.register` — Cantonese
|
|
55
|
+
markers only in 粤语, no modern words in 古装, no net slang on the middle-aged, English short and
|
|
56
|
+
contracted with names used sparingly. It reports and never blocks; a character who breaks the
|
|
57
|
+
register on purpose gets `bible.speech.register`.
|
|
58
|
+
|
|
59
|
+
Scene fields: `sceneId` and `lighting` must exist upstream (a new lighting state is added to the
|
|
60
|
+
scene in `project.json` first, never bypassed), `characters` / `props` ⊆ the outline's ids for
|
|
61
|
+
this episode.
|
|
62
|
+
|
|
63
|
+
## Editing discipline
|
|
64
|
+
|
|
65
|
+
- **Change one beat, re-read three.** Position, who holds the prop, who is present — spatial state
|
|
66
|
+
lives in your head while writing and vanishes while editing. Re-reading the neighbours is the
|
|
67
|
+
only way to get it back.
|
|
68
|
+
- **A picture change is a sound change** downstream — remember it when the storyboard exists.
|
|
69
|
+
|
|
70
|
+
## Feel (not gated, decides whether it plays)
|
|
71
|
+
|
|
72
|
+
Short lines, dense action: an action or reaction every two or three lines, never four lines in a
|
|
73
|
+
row. Subtext goes in `delivery` ("says sit, doesn't move"), not spoken. Stage groups per the
|
|
74
|
+
outline's `crowdPlan`. Scene changes are free for the generator but cost the viewer — tighten one
|
|
75
|
+
scene rather than hopping four.
|
|
76
|
+
|
|
77
|
+
| Fault | Symptom | Fix |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| radio play | lines only | insert a visual action every 2–3 lines |
|
|
80
|
+
| literary | actions as prose (oar blocks the pole, lashes tremble) | common actions |
|
|
81
|
+
| dead opening | first beat is a still close-up | give the same image in motion |
|
|
82
|
+
| space jump | on the path, next beat on the pier | add the crossing beat; re-read three |
|
|
83
|
+
| padding | greetings, tea, directions | delete; every beat advances or flips |
|
|
84
|
+
| speech | one person, four lines | split to the partner or break with action |
|
|
85
|
+
| omniscience | lines explain everything | move information into action and delivery |
|
|
86
|
+
| flat ending | ends calm | last beat becomes the cliff, even just a whistle |
|
|
87
|
+
|
|
88
|
+
## Polish (`render dialogue --ep N`)
|
|
89
|
+
|
|
90
|
+
When the user asks for a polish, when the dialogue is the visible weakness, or when the register
|
|
91
|
+
gate keeps noting the same scene: `render dialogue --ep 1` prints the 逐句校对表 (原句 / 问题 / 修改句 /
|
|
92
|
+
修改理由 / 导演/演员提示) with each speaker's speech rules in the last column. Fill it per
|
|
93
|
+
`dialogue-craft.md` §3, apply the changes to the flow, re-validate, and append the filled table to
|
|
94
|
+
the screenplay dataset under `## 对白校对`.
|
|
95
|
+
|
|
96
|
+
## Sync
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
node {baseDir}/scripts/short-drama.mjs validate <project> --stage script --ep 1
|
|
100
|
+
node {baseDir}/scripts/short-drama.mjs render <project> script --ep 1 > /tmp/E01-script.md # 短剧 episode format + 创作自检
|
|
101
|
+
wji dataset new <story folder> "E01 剧本" -t screenplay -d "<hook>" # bind --ep 1 wji.screenplayDatasetId
|
|
102
|
+
wji fs set <id> --merge content=@/tmp/E01-script.md --merge viewMode=content --merge metadata.shortDrama=@/tmp/E01.json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Gates
|
|
106
|
+
|
|
107
|
+
`script-shape` · `beat-flow` · `duration` · `hook-cliff` · `beats-claimed` · `script-design` ·
|
|
108
|
+
`rhythm` · `filmable` · `dialogue-register` (advisory).
|
|
109
|
+
|
|
110
|
+
`render script` writes the reference's episode format — `△(from-to秒)` action lines with the
|
|
111
|
+
seconds derived, `角色(delivery):line`, `【爽点标记】` after the 爽点, `【钩子标记】` at the end — and
|
|
112
|
+
closes with the 创作自检 table, eight questions answered from the same helpers the gates use. A
|
|
113
|
+
green episode is 16/16; a row that says 否 names what to fix.
|