storyforge 0.4.1 → 0.4.3

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.
Files changed (2) hide show
  1. package/dist/index.js +215 -77
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -44,19 +44,19 @@ function loadCredentials() {
44
44
 
45
45
  // src/utils/script-prompt.ts
46
46
  var STYLE_GUIDES = {
47
- "3blue1brown": 'Mathematical rigor, progressive intuition building, "but why?" framing. Build from first principles.',
48
- coldfusion: `Story-driven narrative, industry context, "here's what happened" framing. Data points, company names, real-world impact.`,
49
- educative: "System design interview structure. Functional vs non-functional requirements, architecture, deep dives.",
50
- wendover: 'Real-world logistics framing. "The surprising reason" hooks. Ground abstract concepts in physical analogies.',
51
- veritasium: "Counterintuitive hook or common misconception. Hypothesis, evidence, reveal. Challenge assumptions.",
52
- custom: "Documentary-quality educational content. Clear structure, dense evidence, engaging narration."
47
+ "3blue1brown": 'Mathematical intuition, progressive building, "but why?" framing. Each section earns the next.',
48
+ coldfusion: `Investigative story, real-world stakes, "here's what happened and why it matters" framing.`,
49
+ educative: "Engineering clarity, structured deep-dive, teaches one mechanism deeply rather than many shallowly.",
50
+ wendover: 'Systemic reasoning, "the surprising reason" hooks, physical-world analogies for abstract systems.',
51
+ veritasium: "Counter-intuitive setup, audience misconception as target, reveal as payoff.",
52
+ custom: "Retention-first documentary: one strong metaphor, open loops, mid-roll re-hooks, specific examples."
53
53
  };
54
54
  function styleGuideFor(style) {
55
55
  return STYLE_GUIDES[style] ?? STYLE_GUIDES.custom;
56
56
  }
57
57
  function buildScriptPrompt(args) {
58
- const minMin = args.targetMinutesMin ?? 18;
59
- const maxMin = args.targetMinutesMax ?? 25;
58
+ const minMin = args.targetMinutesMin ?? 16;
59
+ const maxMin = args.targetMinutesMax ?? 24;
60
60
  const tagline = (args.channelTagline ?? "Documentary-quality explainers").trim();
61
61
  const handle = (args.channelHandle ?? "@channel").trim();
62
62
  const channel = (args.channelName ?? "this channel").trim();
@@ -81,99 +81,237 @@ function buildScriptPrompt(args) {
81
81
  ]).map((s) => ` - "${s}"`).join("\n");
82
82
  const referenceBlock = args.referenceScript ? `
83
83
 
84
- === REFERENCE SCRIPT (match this density and craft) ===
84
+ === ONE CRAFT SAMPLE (voice calibration ONLY \u2014 do NOT copy its structure) ===
85
85
 
86
- ${args.referenceScript.slice(0, 12e3)}
86
+ ${args.referenceScript.slice(0, 8e3)}
87
87
 
88
- === END REFERENCE ===
88
+ === END SAMPLE ===
89
89
 
90
- The script you write must match the reference's:
91
- - Hook structure (pattern of historical examples \u2192 reversal \u2192 stakes \u2192 bet).
92
- - Evidence density (named companies, specific numbers, dollar amounts, dates).
93
- - Section pacing (varying section lengths from 2s teasers to 200s deep dives).
94
- - Sentence rhythm (mix of long flowing sentences and 4-word punches).` : "";
90
+ Use this ONLY to calibrate the channel's voice \u2014 sentence rhythm, tolerance for technical detail, willingness to name real things. Do NOT copy its structure. If the sample opens with historical examples and this topic works better with a counter-intuitive question or a single unfolding metaphor, choose the format that serves the topic. The best script for THIS topic may look structurally different from the sample, and that's correct.` : "";
95
91
  return `You are a senior documentary scriptwriter for ${channel} (${handle}). The channel's promise: "${tagline}".
96
92
 
97
- You write the kind of script that earns 25 minutes of someone's attention. The kind where every paragraph adds load-bearing information. The kind where one historical example replaces three paragraphs of generic exposition.
93
+ Your job is to write a script that EARNS attention from second 0 to the end. Every single chunk must justify its existence by either building comprehension or holding attention \u2014 ideally both.
98
94
 
99
- YOUR TASK
100
- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
101
- Write a narrated documentary-style script on this topic:
95
+ TOPIC: "${args.topic}"
96
+ STYLE: ${args.style} \u2014 ${args.styleGuide}
97
+ TARGET LENGTH: ${minMin}-${maxMin} minutes (write to the topic's natural length \u2014 pad nothing, rush nothing).
102
98
 
103
- TOPIC: "${args.topic}"
104
- STYLE: ${args.style} \u2014 ${args.styleGuide}
99
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
100
+ HARD WORD-COUNT CONTRACT (this is RULE ZERO \u2014 non-negotiable)
101
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
105
102
 
106
- The video target length is ${minMin}-${maxMin} minutes. Don't pad. If the topic legitimately needs less, write less; if it needs more, write more. Anemic length comes from anemic thinking \u2014 go deeper, not shorter.
103
+ Each chunk's narrationText MUST contain at least (durationEstimate \xD7 2.5)
104
+ words. That's the speech rate of a polished narrator at 150 wpm.
107
105
 
108
- HOW THE HOOK WORKS (this is non-negotiable)
109
- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
110
- The first chunk is the hook. It is NOT "Imagine a world where..." or "In today's fast-paced..." or any LLM-template opener. The hook follows this structure:
106
+ - 120s chunk \u2192 \u2265300 words of narration
107
+ - 90s chunk \u2192 \u2265225 words
108
+ - 60s chunk \u2192 \u2265150 words
109
+ - 180s chunk \u2192 \u2265450 words
110
+ - 30s chunk \u2192 \u226575 words
111
111
 
112
- 1. PATTERN (4-6 sentences): Open with 3-5 SPECIFIC historical examples that share a common pattern. Real companies. Real years. Real outcomes.
113
- 2. REVERSAL: Name the surprising counter-pattern that connects to the topic.
114
- 3. STAKES: One paragraph naming the specific cost / consequence / scale.
115
- 4. BET: Frame the topic as a bet between two competing approaches with concrete numbers.
116
- 5. PROMISE: One sentence promising what the viewer will see in the next ${minMin}+ minutes.
112
+ Previous generations have FAILED this contract \u2014 writing 65 words for
113
+ a 120s chunk (claiming 120s of speech but actually delivering ~26s).
114
+ Do NOT repeat that mistake. If you cannot fill the duration with
115
+ substantive content, REDUCE the durationEstimate. Do not pad with
116
+ filler \u2014 but do not lie about duration either.
117
117
 
118
- The hook is typically 90-150 seconds. Do NOT aim for a uniform short hook.
118
+ Count your words before submitting each chunk. Total script word
119
+ count should be approximately (total duration in seconds \xD7 2.5).
119
120
 
120
- EVIDENCE RULES (every paragraph must satisfy at least one)
121
- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
122
- - Name a specific company / lab / paper / person.
123
- - Include a specific number with units (dollars, percent, milliseconds, GB, parameters, miles, etc.).
124
- - Cite a specific year or product version.
125
- - Reference a specific event or product launch.
121
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
122
+ RETENTION PRINCIPLES (these govern every decision you make)
123
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
126
124
 
127
- If you cannot satisfy any of these for a paragraph, REMOVE the paragraph. Do not write filler.
125
+ 1. OPEN LOOP: In the first 30 seconds, plant a question or claim that
126
+ feels counter-intuitive, stakes-laden, or genuinely confusing. DON'T
127
+ answer it yet. Pay it off 70-85% through the video. The viewer stays
128
+ because you promised them something they haven't gotten yet.
128
129
 
129
- FORBIDDEN PHRASES (zero of them)
130
- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
130
+ 2. MID-ROLL RE-HOOKS: Every 3-4 minutes, re-state the stakes for the
131
+ viewer who just tuned in or drifted. "Remember, what we're really
132
+ asking is..." \u2014 a sentence that pulls the viewer back into the core
133
+ question. You should have 3-5 of these naturally distributed.
134
+
135
+ 3. ONE DOMINANT METAPHOR \u2014 TEACHING, NOT DECORATION:
136
+ Pick ONE concrete analogy before you start writing. Thread it
137
+ through every section. Every time the mechanism gets technical,
138
+ bend back to the metaphor to scaffold understanding.
139
+
140
+ The metaphor MUST DO WORK \u2014 it must PREDICT the mechanism, not
141
+ merely label it.
142
+
143
+ BAD (decoration): "Like our metaphorical chef in the kitchen,
144
+ KV Cache speeds things up."
145
+ GOOD (teaching): "If our chef can grab pre-prepped ingredients
146
+ without looking, what happens when fifty orders
147
+ come in at once? Each new order needs the same
148
+ lookups \u2014 but the prep is already done. That is
149
+ exactly what KV Cache does: it computes each
150
+ key-value pair once, then reuses them across
151
+ every subsequent token."
152
+
153
+ Banned: the word "metaphorical". The word "akin to" used more
154
+ than once. Phrases like "back to our chef" used as filler instead
155
+ of as a vehicle for predicting the next mechanism.
156
+
157
+ Pick your metaphor in a pre-planning step before you write chunk 1.
158
+ Good metaphor tests: (a) can a non-expert picture it in 2 seconds?
159
+ (b) does it survive at least 3 levels of zoom-in? (c) does it map to
160
+ \u22655 of the mechanism's key components? (d) does each section USE
161
+ the metaphor to advance understanding, not just nod at it?
162
+
163
+ 4. SPECIFICITY BEATS SCALE: One vivid example explained deeply is
164
+ worth five examples listed shallowly. If you're tempted to write
165
+ "companies like X, Y, and Z do this", STOP. Pick ONE and explain
166
+ HOW they do it, with a real outcome.
167
+
168
+ 5. EVIDENCE DENSITY: Every paragraph must satisfy ONE of:
169
+ - A specific named company / lab / paper / person
170
+ - A specific number with units (dollars, ms, parameters, tokens, GB)
171
+ - A specific year or product version
172
+ - A specific event (launch, paper publication, failure)
173
+
174
+ AND each chunk's narrationText overall must contain AT LEAST 3
175
+ specific entities (3 named things, numbers, or years collectively).
176
+ Vague references like "Google Research, in 2021" without naming the
177
+ paper or product are NOT specific enough \u2014 name the paper, name
178
+ the product, name the team.
179
+
180
+ If a paragraph can't satisfy one rule, REMOVE IT. If a chunk has
181
+ fewer than 3 specific entities total, REWRITE IT before submitting.
182
+
183
+ 6. VARIABLE PACING: Sections must have DIFFERENT lengths. Rhythm wins
184
+ retention. A 300s deep-dive should be followed by a 45s breath
185
+ (short callback chunk) before another long section. Do NOT write
186
+ uniform 120s body chunks.
187
+
188
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
189
+ FORBIDDEN PHRASES (zero of these \u2014 patterns AND exact strings)
190
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
131
191
  ${banList}
132
192
 
133
- Plus: no rhetorical questions opening sections, no "let's explore", no "in this video we'll look at".
193
+ ALSO BANNED (regardless of channel DNA):
194
+ - "Imagine teaching a..." / "Imagine taking..." / "Imagine a..."
195
+ - "Step into the kitchen of..." / "Step into the world of..."
196
+ - "back to our metaphorical..." / "our metaphorical X"
197
+ - "akin to" used more than once total
198
+ - "Are we prepared for..." / "What if..." as section openers
199
+ - Section titles that are just questions without setup
200
+ - "the magic of...", "the power of...", "the secret to..."
201
+ - "memory's power is underestimated" type abstract claims
134
202
 
135
- LENGTH + PACING
136
- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
137
- - 14 to 22 chunks. Vary durations:
138
- * Hook: 90-150s
139
- * Channel intro / cold-open: 2-5s
140
- * Body sections: 100-220s each
141
- * Conclusion: 90-180s
142
- * CTA: 8-15s
143
- * Next-video teaser: 20-40s
144
- * End-screen: 8-15s
145
- - Total duration: ${minMin}-${maxMin} minutes.
203
+ Plus: no rhetorical questions opening sections, no "let's explore",
204
+ no "in this video we'll look at", no generic "welcome back" openers.
146
205
 
147
- NARRATION VOICE
148
- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
149
- - Conversational but authoritative. Speak in declarative sentences.
150
- - Mix sentence lengths: occasionally drop a 4-word sentence after a long one for rhythm.
151
- - No exclamation marks.
152
- - "And" / "But" / "Because" can start sentences.
153
- - End each section with a sentence that creates forward pull.
206
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
207
+ STRUCTURE (choose the format that serves THIS topic \u2014 do NOT default)
208
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
154
209
 
155
- STYLE: ${args.style.toUpperCase()}
156
- ${args.styleGuide}
157
- ${referenceBlock}
210
+ You have multiple formats available. Pick the one this topic is best
211
+ suited to. Do NOT default to Tesla-style "history \u2192 bet \u2192 stakes".
212
+
213
+ A. COUNTER-INTUITIVE (Veritasium mode)
214
+ Hook with a wrong-sounding claim. Challenge viewer's intuition.
215
+ Reveal the actual mechanism piece by piece. Best for topics where
216
+ common sense gets it wrong.
158
217
 
159
- OUTPUT FORMAT
160
- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
161
- Return ONLY a valid JSON array, no markdown fences, no commentary. Each chunk:
218
+ B. SINGLE METAPHOR UNFOLDING (3Blue1Brown mode)
219
+ Start with the metaphor. Let the mechanism emerge by asking what
220
+ the metaphor predicts. Perfect for algorithmic/mathematical topics.
162
221
 
222
+ C. MYSTERY / INVESTIGATION (Wendover mode)
223
+ Anomaly observed. Why? Progressive theories. Final reveal. Best
224
+ for systems-level topics where the answer isn't obvious.
225
+
226
+ D. CHASE (ColdFusion mode)
227
+ Two competing approaches. Timeline unfolds. Winner not obvious
228
+ until late. Best for technology races and paradigm shifts.
229
+
230
+ E. ZOOM (Johnny Harris / Kurzgesagt mode)
231
+ Start zoomed-all-the-way-out (something universal, human-scale).
232
+ Progressive zoom-in to the specific mechanism. Zoom back out to
233
+ stakes. Best for topics that need emotional hooks.
234
+
235
+ Pick ONE format. Name it in the first chunk's meta.format field.
236
+
237
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
238
+ CHUNK STRUCTURE
239
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
240
+
241
+ 14-22 chunks. Vary durations by purpose:
242
+
243
+ - HOOK (1 chunk, 45-90s): plant the open loop. End with a forward-pull
244
+ sentence. Do NOT introduce yourself here; that's the cold-open.
245
+ - COLD OPEN (1 chunk, 2-5s): 1-2 sentences of channel intro.
246
+ - BODY SECTIONS (4-8 chunks, varied 45-300s each):
247
+ * Long sections (180-300s) build the mechanism
248
+ * Short sections (45-90s) are breaths, callbacks, or reveals
249
+ * Each section either advances the metaphor, advances the
250
+ mechanism, or re-hooks
251
+ - PAYOFF (1 chunk, 90-180s): close the open loop. The viewer should
252
+ feel the "oh" moment here.
253
+ - REFRAME (1 chunk, 60-120s): don't summarize. Reframe what the
254
+ viewer now knows so they walk away with a changed mental model.
255
+ - CTA (1 chunk, 8-15s): specific ask with reasoning.
256
+ - NEXT-VIDEO TEASER (1 chunk, 20-40s): preview the next topic with
257
+ a single provocative claim (re-opens a new loop for next video).
258
+ - END-SCREEN (1 chunk, 8-15s).
259
+
260
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
261
+ PER-CHUNK FIELDS (output JSON \u2014 no extra commentary, no markdown)
262
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
263
+
264
+ [
163
265
  {
164
- "id": "c01-hook" or "c07-section-three-mechanism" (semantic, kebab-case),
165
- "title": "Hook \u2014 <Title>" or "Section Three. <Phrase>." etc.
166
- "narrationText": "Full narration body. Multiple paragraphs separated by blank lines.",
167
- "durationEstimate": <integer seconds>
266
+ "id": "c01-hook" | "c05-mechanism-one" | "c12-payoff" (kebab-case, semantic),
267
+ "title": short Title-Case phrase,
268
+ "narrationText": narration body, multiple paragraphs split by blank lines, MUST be \u2265(durationEstimate \xD7 2.5) words,
269
+ "durationEstimate": integer seconds per the chunk structure above,
270
+ "purpose": one of: "hook" | "cold-open" | "mechanism" | "metaphor-extension" | "mid-roll-rehook" | "example" | "payoff" | "reframe" | "cta" | "teaser" | "end",
271
+ "metaphor": the single video-wide metaphor you chose (string, same value on every chunk),
272
+ "openLoopId": present on the chunk that plants a loop + on the chunk that pays it off. Same id links them.
273
+ "imagePromptHints": [
274
+ "3-5 teaching-oriented image prompts for THIS chunk. Each should illustrate the concept, not decorate it. Example (KV Cache): 'A chef mid-service at a high-end restaurant, every ingredient prepped and labeled in steel containers, hands reaching without looking \u2014 represents instant key-value lookup.' NOT 'glowing neural network nodes over dark background.' Write prompts that a cinematographer could use to teach a 12-year-old the concept."
275
+ ]
168
276
  }
277
+ ]
278
+
279
+ On chunk 1 ONLY, add a meta field:
280
+ "format": "counter-intuitive" | "single-metaphor" | "mystery" | "chase" | "zoom"
281
+ "plannedMetaphor": the dominant metaphor you committed to
282
+ "plannedOpenLoop": the question/claim you planted in the hook
283
+ "plannedPayoffChunkId": id of the chunk where the loop closes
284
+
285
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
286
+ VOICE
287
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
288
+
289
+ Conversational but authoritative. Speak in declarative sentences.
290
+ Use the second person ("you") for stakes and rhetorical pivots.
291
+ Mix sentence lengths \u2014 drop a 4-word sentence after a long one.
292
+ No exclamation marks. "And" / "But" / "Because" can start sentences.
293
+ Every section ends with forward pull.
294
+
295
+ STYLE: ${args.style.toUpperCase()}
296
+ ${args.styleGuide}
297
+ ${referenceBlock}
169
298
 
170
- CRITICAL CHECKLIST:
171
- \u25A1 Hook follows pattern \u2192 reversal \u2192 stakes \u2192 bet \u2192 promise.
172
- \u25A1 Total duration is ${minMin * 60}-${maxMin * 60} seconds.
173
- \u25A1 Every paragraph has a specific number, name, year, or product reference.
174
- \u25A1 ZERO forbidden phrases.
175
- \u25A1 Section durations vary (not uniform 45s blocks).
176
- \u25A1 Conclusion connects back to the hook's pattern.
299
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
300
+ CRITICAL CHECKLIST (review before you return \u2014 be honest)
301
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
302
+ \u25A1 Each chunk's narrationText word count \u2265 (durationEstimate \xD7 2.5).
303
+ Total script word count \u2248 (total seconds \xD7 2.5).
304
+ \u25A1 ONE format chosen (not a mash-up). Named in chunk 1's meta.
305
+ \u25A1 ONE metaphor committed to and threaded through every section.
306
+ The metaphor PREDICTS the mechanism \u2014 does NOT just label it.
307
+ \u25A1 Open loop planted in the hook, paid off at 70-85% mark, reframed at end.
308
+ \u25A1 3-5 mid-roll re-hooks spaced through body sections.
309
+ \u25A1 Section durations VARY (not uniform). At least one 45-90s breath chunk.
310
+ \u25A1 Every chunk has \u22653 teaching-oriented imagePromptHints.
311
+ \u25A1 Every paragraph has specific evidence (name / number / year / event).
312
+ \u25A1 Every chunk has \u22653 specific entities total in its narration.
313
+ \u25A1 ZERO forbidden phrases anywhere.
314
+ \u25A1 The conclusion REFRAMES \u2014 does not summarize, does not "in conclusion".
177
315
 
178
316
  Now write the script.`;
179
317
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyforge",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "StoryForge — local bridge for the Forge video production web app. Zero runtime dependencies.",
5
5
  "type": "module",
6
6
  "bin": {