@lalalic/markcut 2.2.7 → 2.2.9
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/package.json +2 -1
- package/skills/markcut/SKILL.md +0 -2
- package/skills/markcut/docs/markdown-descriptive.md +1 -1
- package/src/config.mjs +60 -63
- package/src/descriptive/resolve.ts +3 -2
- package/src/player/browser.tsx +3 -1
- package/src/player/bundle/player.js +7 -2
- package/src/player/components/SceneThumbnails.tsx +3 -1
- package/src/player/pipeline.mjs +64 -21
- package/src/player/server.mjs +11 -2
- package/src/render/cli.mjs +42 -69
- package/src/vision/cli.mjs +21 -45
- package/skills/markcut/docs/template.md +0 -212
- package/skills/markcut/docs/templates/courseware/TEMPLATE.md +0 -526
- package/skills/markcut/docs/templates/courseware/agents/reviewer.md +0 -84
- package/skills/markcut/docs/templates/courseware/prompts/fix.md +0 -30
- package/skills/markcut/docs/templates/courseware/prompts/outline.md +0 -58
- package/skills/markcut/docs/templates/courseware/prompts/scene.md +0 -70
- package/skills/markcut/docs/templates/deep-dive/TEMPLATE.md +0 -354
- package/skills/markcut/docs/templates/deep-dive/agents/researcher.md +0 -69
- package/skills/markcut/docs/templates/deep-dive/agents/reviewer.md +0 -108
- package/skills/markcut/docs/templates/deep-dive/prompts/fix.md +0 -28
- package/skills/markcut/docs/templates/deep-dive/prompts/outline.md +0 -102
- package/skills/markcut/docs/templates/deep-dive/prompts/script.md +0 -64
- package/skills/markcut/docs/templates/illustrated-book/TEMPLATE.md +0 -356
- package/skills/markcut/docs/templates/illustrated-book/agents/reviewer.md +0 -115
- package/skills/markcut/docs/templates/illustrated-book/prompts/fix.md +0 -29
- package/skills/markcut/docs/templates/illustrated-book/prompts/illustration.md +0 -35
- package/skills/markcut/docs/templates/illustrated-book/prompts/story.md +0 -69
- package/skills/markcut/docs/templates/short-film/TEMPLATE.md +0 -385
- package/skills/markcut/docs/templates/short-film/agents/reviewer.md +0 -115
- package/skills/markcut/docs/templates/short-film/prompts/fix.md +0 -29
- package/skills/markcut/docs/templates/short-film/prompts/screenplay.md +0 -101
- package/skills/markcut/docs/templates/short-film/prompts/storyboard.md +0 -54
- package/skills/markcut/docs/templates/short-video/TEMPLATE.md +0 -301
- package/skills/markcut/docs/templates/short-video/agents/reviewer.md +0 -94
- package/skills/markcut/docs/templates/short-video/prompts/fix.md +0 -27
- package/skills/markcut/docs/templates/short-video/prompts/script.md +0 -65
- package/skills/markcut/docs/templates/vlog/TEMPLATE.md +0 -326
- package/skills/markcut/docs/templates/vlog/agents/reviewer.md +0 -89
- package/skills/markcut/docs/templates/vlog/agents/story-writer.md +0 -100
- package/skills/markcut/docs/templates/vlog/prompts/bgm-select.md +0 -38
- package/skills/markcut/docs/templates/vlog/prompts/group-clips.md +0 -93
- package/skills/markcut/docs/templates/vlog/prompts/local-context.md +0 -59
- package/skills/markcut/docs/templates/vlog/prompts/outline.md +0 -56
- package/skills/markcut/docs/templates/vlog/prompts/review-fix.md +0 -27
- package/skills/markcut/docs/templates/vlog/prompts/storyboard.md +0 -41
- package/src/render/cli.test.ts +0 -13
package/src/vision/cli.mjs
CHANGED
|
@@ -20,11 +20,7 @@
|
|
|
20
20
|
* f) Save complete metadata.json with metadata + userHint + perception
|
|
21
21
|
*
|
|
22
22
|
* Options:
|
|
23
|
-
* --label
|
|
24
|
-
* --agent <tmpl> Custom text LLM CLI for detect-scenes ({prompt})
|
|
25
|
-
* --itt <tmpl> Custom ITT CLI template with {input}, {prompt}
|
|
26
|
-
* --vtt <tmpl> Custom VTT CLI template with {input}, {prompt}
|
|
27
|
-
* --stt <tmpl> Custom STT CLI template with {input}, {output}
|
|
23
|
+
* --label Open label preview → then continue with full AI pipeline
|
|
28
24
|
* --prompts-file <path> Path to prompts markdown file (default: vision_prompts.md)
|
|
29
25
|
* --vtt-sample-interval <n> Sample one video frame every N seconds (default: 5)
|
|
30
26
|
* --context "text" Background context about people/places (injected into prompts)
|
|
@@ -32,7 +28,6 @@
|
|
|
32
28
|
* --skip-stt Skip speech-to-text for videos
|
|
33
29
|
* --dry-run Show what would be processed without running AI
|
|
34
30
|
* --show-prompts Print the prompts file and exit
|
|
35
|
-
* --show-clis Print the default ITT/VTT/STT CLI templates
|
|
36
31
|
* --help Show this help
|
|
37
32
|
*
|
|
38
33
|
* Prompt overrides:
|
|
@@ -716,13 +711,13 @@ function mergeLabelsIntoMetadata(folder, metadata) {
|
|
|
716
711
|
|
|
717
712
|
// ── Perception helpers ────────────────────────────────────────────────────
|
|
718
713
|
|
|
719
|
-
function analyzeImage(imagePath, normPath, prompts,
|
|
714
|
+
function analyzeImage(imagePath, normPath, prompts, context = "", userHint = "", userHints = null) {
|
|
720
715
|
let ctxParts = [];
|
|
721
716
|
if (context) ctxParts.push(`Context: ${context}`);
|
|
722
717
|
if (userHint) ctxParts.push(`User hint: ${userHint}`);
|
|
723
718
|
const ctx = ctxParts.length > 0 ? ctxParts.join("\n") + "\n\n" : "";
|
|
724
719
|
const prompt = ctx + getPrompt(prompts, "image-perception");
|
|
725
|
-
const raw = runITT(normPath, prompt,
|
|
720
|
+
const raw = runITT(normPath, prompt, DEFAULT_ITT_CLI);
|
|
726
721
|
const parsed = looseJSONParse(raw);
|
|
727
722
|
const rawText = stripThinkBlocks(extractRawText(raw));
|
|
728
723
|
return normalizePerception({
|
|
@@ -790,7 +785,7 @@ function buildMergedCues(userHint, cues, sceneChangesMs, totalDurationMs) {
|
|
|
790
785
|
return merged;
|
|
791
786
|
}
|
|
792
787
|
|
|
793
|
-
function analyzeVideo(videoPath, normInfo, normDir, prompts,
|
|
788
|
+
function analyzeVideo(videoPath, normInfo, normDir, prompts, context = "", userHint = "", sampleInterval = DEFAULT_VTT_SAMPLE_INTERVAL, userHints = null) {
|
|
794
789
|
let ctxParts = [];
|
|
795
790
|
if (context) ctxParts.push(`Context: ${context}`);
|
|
796
791
|
if (userHint && typeof userHint === "string") ctxParts.push(`User hint: ${userHint}`);
|
|
@@ -800,13 +795,13 @@ function analyzeVideo(videoPath, normInfo, normDir, prompts, vttCli, sttCli, con
|
|
|
800
795
|
// 1. VTT → overall video description
|
|
801
796
|
emitInfo(` Running video understanding...`);
|
|
802
797
|
const descPrompt = ctx + getPrompt(prompts, "video-perception");
|
|
803
|
-
const descRaw = runVTT(normInfo.path, descPrompt,
|
|
798
|
+
const descRaw = runVTT(normInfo.path, descPrompt, DEFAULT_VTT_CLI, DEFAULT_ITT_CLI, sampleInterval);
|
|
804
799
|
const descText = stripThinkBlocks(extractRawText(descRaw));
|
|
805
800
|
perception.desc = descText.slice(0, 500) || looseJSONParse(descRaw)?.desc || descRaw.slice(0, 500);
|
|
806
801
|
|
|
807
802
|
// 2. STT → VTT subtitle
|
|
808
803
|
emitInfo(` Running speech-to-text...`);
|
|
809
|
-
perception.subtitle = runSTT(videoPath, normDir,
|
|
804
|
+
perception.subtitle = runSTT(videoPath, normDir, DEFAULT_STT_CLI);
|
|
810
805
|
|
|
811
806
|
// 3. Build merged cue timeline from VTT + user hints + ffprobe
|
|
812
807
|
emitInfo(` Building merged segment boundaries...`);
|
|
@@ -843,7 +838,7 @@ function analyzeVideo(videoPath, normInfo, normDir, prompts, vttCli, sttCli, con
|
|
|
843
838
|
if (!existsSync(dummyInput)) {
|
|
844
839
|
writeFileSync(dummyInput, Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==", "base64"));
|
|
845
840
|
}
|
|
846
|
-
const segRaw = runAgent(dummyInput, `${segPrompt}\n\nInput:\n${segInput}`,
|
|
841
|
+
const segRaw = runAgent(dummyInput, `${segPrompt}\n\nInput:\n${segInput}`, DEFAULT_AGENT_CLI || DEFAULT_ITT_CLI);
|
|
847
842
|
const segParsed = looseJSONParse(segRaw);
|
|
848
843
|
|
|
849
844
|
if (segParsed && typeof segParsed === "object" && Object.keys(segParsed).length > 0) {
|
|
@@ -903,7 +898,7 @@ function analyzeVideo(videoPath, normInfo, normDir, prompts, vttCli, sttCli, con
|
|
|
903
898
|
} else { clipPath = clipOut; }
|
|
904
899
|
|
|
905
900
|
// Use runVTT for video clips (extracts frames, then runs ITT)
|
|
906
|
-
const segRawVis = runVTT(clipPath, visSegPrompt,
|
|
901
|
+
const segRawVis = runVTT(clipPath, visSegPrompt, DEFAULT_VTT_CLI, DEFAULT_ITT_CLI, sampleInterval);
|
|
907
902
|
const segVisText = extractRawText(segRawVis);
|
|
908
903
|
seg.vision = stripThinkBlocks(looseJSONParse(segRawVis)?.desc || segVisText.slice(0, 300) || segRawVis.slice(0, 300));
|
|
909
904
|
try { if (clipPath !== normInfo.path) rmSync(clipPath, { force: true }); } catch {}
|
|
@@ -917,7 +912,7 @@ function analyzeVideo(videoPath, normInfo, normDir, prompts, vttCli, sttCli, con
|
|
|
917
912
|
|
|
918
913
|
// ── Full pipeline runner (step 2: label → normalize → percept → segments) ──
|
|
919
914
|
|
|
920
|
-
async function runFullPipeline(folder, prompts,
|
|
915
|
+
async function runFullPipeline(folder, prompts, context, pickSet, vttSampleInterval, skipSTT, dryRun) {
|
|
921
916
|
const metadataPath = join(folder, "metadata.json");
|
|
922
917
|
|
|
923
918
|
// ── Ensure metadata exists ──────────────────────────────────────────
|
|
@@ -987,12 +982,12 @@ async function runFullPipeline(folder, prompts, ittCli, vttCli, sttCli, context,
|
|
|
987
982
|
return;
|
|
988
983
|
}
|
|
989
984
|
|
|
990
|
-
await runNormalizeAndPercept(folder, metadataPath, prompts,
|
|
985
|
+
await runNormalizeAndPercept(folder, metadataPath, prompts, context, pickSet, vttSampleInterval, skipSTT);
|
|
991
986
|
}
|
|
992
987
|
|
|
993
988
|
// ── Normalize + Percept (internal step) ───────────────────────────────────
|
|
994
989
|
|
|
995
|
-
async function runNormalizeAndPercept(folder, metadataPath, prompts,
|
|
990
|
+
async function runNormalizeAndPercept(folder, metadataPath, prompts, context, pickSet, vttSampleInterval, skipSTT) {
|
|
996
991
|
const { results, cacheMap } = loadMetadata(folder);
|
|
997
992
|
const cache = { ...cacheMap };
|
|
998
993
|
const normDir = join(folder, ".normalized");
|
|
@@ -1022,14 +1017,14 @@ async function runNormalizeAndPercept(folder, metadataPath, prompts, ittCli, vtt
|
|
|
1022
1017
|
let cacheKey = "";
|
|
1023
1018
|
const ctx = context ? `Context: ${context}\n\n` : "";
|
|
1024
1019
|
const imgPrompt = (userHint ? `User hint: ${userHint}\n\n` : "") + ctx + getPrompt(prompts, "image-perception");
|
|
1025
|
-
const imgInput =
|
|
1026
|
-
const imgCmd = substituteTemplate(
|
|
1020
|
+
const imgInput = `@${shQuote(normPath)}`;
|
|
1021
|
+
const imgCmd = substituteTemplate(DEFAULT_ITT_CLI, { input: imgInput, prompt: imgPrompt });
|
|
1027
1022
|
cacheKey = perceptionCacheKey(imgPath, imgCmd, "image");
|
|
1028
1023
|
if (cache[cacheKey]) {
|
|
1029
1024
|
perception = cache[cacheKey];
|
|
1030
1025
|
emitInfo(` (cached)`);
|
|
1031
1026
|
} else {
|
|
1032
|
-
perception = analyzeImage(imgPath, normPath, prompts,
|
|
1027
|
+
perception = analyzeImage(imgPath, normPath, prompts, context, userHint, userHints);
|
|
1033
1028
|
if (perception.desc) cache[cacheKey] = perception;
|
|
1034
1029
|
emitInfo(` ${perception.desc?.slice(0, 80)}...`);
|
|
1035
1030
|
}
|
|
@@ -1057,27 +1052,25 @@ async function runNormalizeAndPercept(folder, metadataPath, prompts, ittCli, vtt
|
|
|
1057
1052
|
emitInfo(`\n🎬 Video: ${base}${userHint ? ` (hint: "${userHint}")` : ""}`);
|
|
1058
1053
|
const vidPath = join(folder, fileName);
|
|
1059
1054
|
const normInfo = normalizeVideo(vidPath, normDir, meta.duration);
|
|
1060
|
-
const stt = skipSTT ? null : sttCli;
|
|
1061
|
-
|
|
1062
1055
|
let perception = {};
|
|
1063
1056
|
let cacheKey = "";
|
|
1064
1057
|
const ctxV = context ? `Context: ${context}\n\n` : "";
|
|
1065
1058
|
const vidPrompt = (userHint ? `User hint: ${userHint}\n\n` : "") + ctxV + getPrompt(prompts, "video-perception");
|
|
1066
1059
|
let vttActualCmd;
|
|
1067
|
-
if (
|
|
1068
|
-
vttActualCmd = substituteTemplate(
|
|
1060
|
+
if (DEFAULT_VTT_CLI) {
|
|
1061
|
+
vttActualCmd = substituteTemplate(DEFAULT_VTT_CLI, { input: shQuote(normInfo.path), prompt: vidPrompt });
|
|
1069
1062
|
} else {
|
|
1070
1063
|
const dur = meta.duration || 0;
|
|
1071
1064
|
const n = Math.max(5, Math.min(10, Math.ceil(dur / vttSampleInterval)));
|
|
1072
1065
|
const framePaths = Array.from({ length: n }, (_, i) => `${basename(normInfo.path, extname(normInfo.path))}_${String(i + 1).padStart(3, "0")}.jpg`);
|
|
1073
|
-
vttActualCmd = substituteTemplate(
|
|
1066
|
+
vttActualCmd = substituteTemplate(DEFAULT_ITT_CLI, { input: framePaths.map(p => `@${p}`).join(" "), prompt: vidPrompt });
|
|
1074
1067
|
}
|
|
1075
1068
|
cacheKey = perceptionCacheKey(vidPath, vttActualCmd, "video");
|
|
1076
1069
|
if (cache[cacheKey]) {
|
|
1077
1070
|
perception = cache[cacheKey];
|
|
1078
1071
|
emitInfo(` (cached)`);
|
|
1079
1072
|
} else {
|
|
1080
|
-
perception = analyzeVideo(vidPath, normInfo, normDir, prompts,
|
|
1073
|
+
perception = analyzeVideo(vidPath, normInfo, normDir, prompts, context, userHint, vttSampleInterval, userHints);
|
|
1081
1074
|
if (perception.desc) cache[cacheKey] = perception;
|
|
1082
1075
|
}
|
|
1083
1076
|
|
|
@@ -1105,10 +1098,6 @@ Usage:
|
|
|
1105
1098
|
|
|
1106
1099
|
Options:
|
|
1107
1100
|
--label Open label preview → then continue with full AI pipeline
|
|
1108
|
-
--agent <template> Custom text LLM CLI for detect-scenes ({prompt})
|
|
1109
|
-
--itt <template> Custom ITT CLI template with {input}, {prompt}
|
|
1110
|
-
--vtt <template> Custom VTT CLI template with {input}, {prompt}
|
|
1111
|
-
--stt <template> Custom STT CLI template with {input}, {output}
|
|
1112
1101
|
--prompts-file <path> Path to prompts markdown file (default: vision_prompts.md)
|
|
1113
1102
|
--vtt-sample-interval <n> Sample one video frame every N seconds (default: 5)
|
|
1114
1103
|
--context "text" Background context about people/places (injected into prompts)
|
|
@@ -1116,7 +1105,6 @@ Options:
|
|
|
1116
1105
|
--skip-stt Skip speech-to-text for videos
|
|
1117
1106
|
--dry-run Show what would be processed without running AI
|
|
1118
1107
|
--show-prompts Print the prompts file and exit
|
|
1119
|
-
--show-clis Print the default ITT/VTT/STT CLI templates
|
|
1120
1108
|
--help Show this help
|
|
1121
1109
|
|
|
1122
1110
|
Prompt overrides:
|
|
@@ -1126,10 +1114,6 @@ Prompt overrides:
|
|
|
1126
1114
|
|
|
1127
1115
|
export async function main(args) {
|
|
1128
1116
|
let folder = "";
|
|
1129
|
-
let agentCli = null;
|
|
1130
|
-
let ittCli = null;
|
|
1131
|
-
let vttCli = null;
|
|
1132
|
-
let sttCli = null;
|
|
1133
1117
|
let promptsFile = PROMPTS_FILE;
|
|
1134
1118
|
let context = "";
|
|
1135
1119
|
let vttSampleInterval = DEFAULT_VTT_SAMPLE_INTERVAL;
|
|
@@ -1147,20 +1131,12 @@ export async function main(args) {
|
|
|
1147
1131
|
const flag = args[i++];
|
|
1148
1132
|
if (flag === "--help") { printUsage(); return; }
|
|
1149
1133
|
else if (flag === "--label") { doLabel = true; }
|
|
1150
|
-
|
|
1151
|
-
else if (flag === "--itt" && args[i]) { ittCli = args[i++]; }
|
|
1152
|
-
else if (flag === "--vtt" && args[i]) { vttCli = args[i++]; }
|
|
1153
|
-
else if (flag === "--stt" && args[i]) { sttCli = args[i++]; }
|
|
1134
|
+
|
|
1154
1135
|
else if (flag === "--prompts-file" && args[i]) { promptsFile = resolve(args[i++]); }
|
|
1155
1136
|
else if (flag === "--vtt-sample-interval" && args[i]) { vttSampleInterval = parseInt(args[i++], 10) || DEFAULT_VTT_SAMPLE_INTERVAL; }
|
|
1156
1137
|
else if (flag === "--context" && args[i]) { context = args[i++]; }
|
|
1157
1138
|
else if (flag === "--show-prompts") { console.log(readFileSync(promptsFile, "utf-8")); return; }
|
|
1158
|
-
|
|
1159
|
-
console.log(`DEFAULT_ITT_CLI:\n${DEFAULT_ITT_CLI}\n`);
|
|
1160
|
-
console.log(`DEFAULT_VTT_CLI: (empty — uses ITT via frame extraction, sample every ${DEFAULT_VTT_SAMPLE_INTERVAL}s)\n`);
|
|
1161
|
-
console.log(`DEFAULT_STT_CLI:\n${DEFAULT_STT_CLI}`);
|
|
1162
|
-
return;
|
|
1163
|
-
}
|
|
1139
|
+
|
|
1164
1140
|
else if (flag === "--skip-stt") { skipSTT = true; }
|
|
1165
1141
|
else if (flag === "--pick" && args[i]) { for (const f of args[i++].split(",")) pickSet.add(f.trim()); }
|
|
1166
1142
|
else if (flag === "--dry-run") { dryRun = true; }
|
|
@@ -1175,7 +1151,7 @@ export async function main(args) {
|
|
|
1175
1151
|
for (const [name, value] of promptOverrides) prompts.set(name, value);
|
|
1176
1152
|
|
|
1177
1153
|
if (doLabel) {
|
|
1178
|
-
await runFullPipeline(folder, prompts,
|
|
1154
|
+
await runFullPipeline(folder, prompts, context, pickSet, vttSampleInterval, skipSTT, dryRun);
|
|
1179
1155
|
} else {
|
|
1180
1156
|
await runMetadataMode(folder, pickSet, dryRun);
|
|
1181
1157
|
}
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
# Markcut Template System
|
|
2
|
-
|
|
3
|
-
> How to design and build video templates that agents can follow to produce professional, consistent videos.
|
|
4
|
-
|
|
5
|
-
## Why templates?
|
|
6
|
-
|
|
7
|
-
Markcut templates encode **domain expertise + production polish** into a reusable package. An agent reading a template learns not just markcut syntax, but:
|
|
8
|
-
|
|
9
|
-
- **What** makes a good video of this type (scene structure, pacing, rules)
|
|
10
|
-
- **How** to assemble it (workflow: prompts → agents → render → review)
|
|
11
|
-
- **Why** certain patterns work (production polish: transition timing, overlays, effects)
|
|
12
|
-
|
|
13
|
-
## Template package structure
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
templates/<name>/
|
|
17
|
-
TEMPLATE.md ← The single entry point. Everything except prompts & agents.
|
|
18
|
-
prompts/
|
|
19
|
-
<step>.md ← Fill-in prompt templates. Run in the orchestrator's context.
|
|
20
|
-
...
|
|
21
|
-
agents/
|
|
22
|
-
<name>.md ← Subagent definitions. Run in separate, isolated sessions.
|
|
23
|
-
...
|
|
24
|
-
example.md ← (optional) A complete worked example.
|
|
25
|
-
example.mp4 ← (optional) Rendered output for reference.
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### File type conventions
|
|
29
|
-
|
|
30
|
-
| File | Context | Purpose |
|
|
31
|
-
|---|---|---|
|
|
32
|
-
| `TEMPLATE.md` | Orchestrator's context | Complete spec: inputs, structure, rules, workflow, quality gate, components, styles |
|
|
33
|
-
| `prompts/*.md` | Orchestrator's context | Fill-in-the-blank prompt templates; the orchestrator replaces `{placeholder}` values and executes them |
|
|
34
|
-
| `agents/*.md` | **Separate** session | Full agent definitions with system prompt + task template. Each runs in a fresh, isolated subagent session with its own tools and context |
|
|
35
|
-
|
|
36
|
-
### Why separate prompts/ and agents/?
|
|
37
|
-
|
|
38
|
-
- **prompts/** — tasks the orchestrator does itself (outline generation, scene writing, fix application). These run in the orchestrator's own session so they have full context of the entire project.
|
|
39
|
-
- **agents/** — tasks that benefit from isolation (review, specialized generation). These run in separate sessions to avoid context pollution and to use specialized system prompts without interference.
|
|
40
|
-
|
|
41
|
-
## Anatomy of TEMPLATE.md
|
|
42
|
-
|
|
43
|
-
Every `TEMPLATE.md` follows this section order:
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
§0 Prerequisites — tools, skills, environment checks
|
|
47
|
-
§1 Inputs — what the orchestrator must collect from the user
|
|
48
|
-
§2 Scene grammar — the canonical tree structure with hard rules
|
|
49
|
-
§3 Authoring rules — professional-quality bar for content (text, timing, media)
|
|
50
|
-
§4 Components & styles — copy-verbatim code blocks with documented theme knobs
|
|
51
|
-
§5 Workflow — numbered steps: prompts → agents → render → review
|
|
52
|
-
§6 Quality gate — measurable exit criteria (the reviewer checks these)
|
|
53
|
-
§7 Reference — links to golden examples or related docs
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### §0 Prerequisites
|
|
57
|
-
|
|
58
|
-
What tools/skills the agent needs before starting. Always include:
|
|
59
|
-
|
|
60
|
-
- The engine itself: `npx @lalalic/markcut` runnable
|
|
61
|
-
- Required markcut skill (`SKILL.md` + `docs/markdown-descriptive.md`) — the agent must read these
|
|
62
|
-
- Any domain-specific tools
|
|
63
|
-
|
|
64
|
-
### §1 Inputs — the contract with the user
|
|
65
|
-
|
|
66
|
-
A table of what the orchestrator must collect before starting. Each row:
|
|
67
|
-
|
|
68
|
-
| Input | Required | Default | Notes |
|
|
69
|
-
|---|---|---|---|
|
|
70
|
-
|
|
71
|
-
**Rule:** never invent content beyond given material + well-established knowledge. Missing or ambiguous inputs → ask the user, never fabricate.
|
|
72
|
-
|
|
73
|
-
### §2 Scene grammar — the tree shape
|
|
74
|
-
|
|
75
|
-
The backbone. Show both:
|
|
76
|
-
|
|
77
|
-
1. **An ASCII tree diagram** of the required structure with layout/transition annotations
|
|
78
|
-
2. **Concrete code blocks** showing the exact node structure for each scene type
|
|
79
|
-
|
|
80
|
-
Hard rules come here: number of scenes, layouts, transitions, `isBackground` usage, timing rules, etc.
|
|
81
|
-
|
|
82
|
-
**Crucial design principle:** make the tree as explicit as possible. Show node types, `id:` values, `on:` events, positioning styles. Don't leave structural decisions to the agent.
|
|
83
|
-
|
|
84
|
-
### §3 Authoring rules — the professional bar
|
|
85
|
-
|
|
86
|
-
Rules the reviewer checks. Cover:
|
|
87
|
-
|
|
88
|
-
- Slide/content rules (max bullets, text shape, emoji conventions, table usage)
|
|
89
|
-
- Script/narration rules (pacing, per-sentence structure, example placement, tone)
|
|
90
|
-
- Media rules (positioning, sizing, where media can overlay)
|
|
91
|
-
- Multi-language variant rules
|
|
92
|
-
- Domain-specific rules (e.g. "never fabricate facts" for courseware, "ground every line in clip captions" for vlog)
|
|
93
|
-
|
|
94
|
-
### §4 Components & styles — copy verbatim
|
|
95
|
-
|
|
96
|
-
The trickiest section. Two code blocks:
|
|
97
|
-
|
|
98
|
-
1. **`~~~js imports` block** — all JSX component definitions the video needs. The agent copies this verbatim. Mark which parts are theme knobs (values that can change) vs structural (must stay the same).
|
|
99
|
-
2. **`~~~css stylesheet` block** — all CSS. Same rules: theme knobs documented, structure immutable.
|
|
100
|
-
|
|
101
|
-
In addition, a **theme knobs table** showing what the user can change (colors, fonts, sizes) and their defaults.
|
|
102
|
-
|
|
103
|
-
**Best practice:** keep the component block minimal. One reusable `Slide` component is better than ten one-off components. Use standard markcut features (`- image`, `- video`, `- component`, `- effect`) before writing custom JSX.
|
|
104
|
-
|
|
105
|
-
### §5 Workflow — the orchestrator's playbook
|
|
106
|
-
|
|
107
|
-
Numbered steps from start to finish. Each step is one of:
|
|
108
|
-
|
|
109
|
-
- **Fill a prompt** — "Fill `prompts/outline.md` → course outline. Present to user for confirmation."
|
|
110
|
-
- **Run a subagent** — "Run `agents/reviewer.md` in a fresh session. Pass it these absolute paths."
|
|
111
|
-
- **Execute a CLI command** — "`npx @lalalic/markcut render course.md`"
|
|
112
|
-
- **User interaction** — "Get user approval before continuing."
|
|
113
|
-
|
|
114
|
-
Include fix loops:
|
|
115
|
-
|
|
116
|
-
> 6. **Fix loop** — on FAIL: fill `prompts/fix.md` with the findings, apply edits, re-render. Max 3 iterations, then escalate to user.
|
|
117
|
-
|
|
118
|
-
### §6 Quality gate — measurable exit criteria
|
|
119
|
-
|
|
120
|
-
The checklist the reviewer uses. Each item must be machine-verifiable (by reviewing source, extracting frames, running STT, measuring duration). Examples:
|
|
121
|
-
|
|
122
|
-
- Total duration within ±15% of target
|
|
123
|
-
- Structure matches §2 (hook, title, N concepts, summary, thanks)
|
|
124
|
-
- No blank/black frames at scene boundaries
|
|
125
|
-
- STT transcript matches scripts (≥90% content match)
|
|
126
|
-
- Each bullet has a narration beat
|
|
127
|
-
|
|
128
|
-
### §7 Reference — worked examples
|
|
129
|
-
|
|
130
|
-
Link to golden example files in `tests/fixtures/templates/`. The golden example demonstrates every rule in the template applied correctly.
|
|
131
|
-
|
|
132
|
-
## Production polish patterns (reusable)
|
|
133
|
-
|
|
134
|
-
These are design patterns you can include in any template. They're documented in detail in the courseware template (§2.b) but summarized here:
|
|
135
|
-
|
|
136
|
-
| Pattern | Technique | Use case |
|
|
137
|
-
|---|---|---|
|
|
138
|
-
| **Per-beat media** | `#### Beat` sub-scenes with `layout:parallel` (image + script per beat) | Courseware slides, tutorial demos |
|
|
139
|
-
| **Transition-audio timing** | `start:<T>` on script nodes in `transitionSeries` | Any scripted scene with transitions |
|
|
140
|
-
| **Persistent overlay** | `- image isBackground:true` at root level | Logo, watermark |
|
|
141
|
-
| **Narrator PiP** | `- video/image isBackground:true` with border-radius + name label | Talking-head overlays |
|
|
142
|
-
| **Entrance effects** | `effect:zoomIn` / `effect:fadeIn` on images/components | Reveal animations |
|
|
143
|
-
| **Mermaid diagrams** | ` ```mermaid ` in slide markdown | Flowcharts, architecture, timelines |
|
|
144
|
-
| **TTS expression** | `—` pauses, `*emphasis*`, rate adjustment, phonetic spelling | Natural speech |
|
|
145
|
-
|
|
146
|
-
## Prompt design principles
|
|
147
|
-
|
|
148
|
-
### prompts/*.md
|
|
149
|
-
- Use `{placeholder}` values for every input. The orchestrator replaces these before executing.
|
|
150
|
-
- Include constraints from TEMPLATE.md inline so the prompt is self-contained.
|
|
151
|
-
- Output format should be a concrete template the orchestrator can parse and assemble.
|
|
152
|
-
|
|
153
|
-
### agents/*.md
|
|
154
|
-
- Must include a **system prompt** (top) and a **task template** (bottom, filled by orchestrator).
|
|
155
|
-
- System prompt defines the agent's role, rules, and procedure.
|
|
156
|
-
- Task template uses `{placeholder}` values for paths, params, and inputs.
|
|
157
|
-
- Always mark the agent as `mode: read-only` if it shouldn't edit files.
|
|
158
|
-
- Include an explicit output schema (JSON format) so the orchestrator can parse results reliably.
|
|
159
|
-
- List which tools the agent needs (read, bash, ffmpeg, etc.).
|
|
160
|
-
|
|
161
|
-
## Choosing prompts vs agents
|
|
162
|
-
|
|
163
|
-
| Task type | Where | Why |
|
|
164
|
-
|---|---|---|
|
|
165
|
-
| Creative generation (write a scene) | `prompts/` | Needs full context of the project; best done by orchestrator |
|
|
166
|
-
| Factual check (review) | `agents/` | Needs fresh context; strict rubric; no editing |
|
|
167
|
-
| Research (find images, analyze) | `agents/` | Parallelizable; heavy tool use |
|
|
168
|
-
| Outline / planning | `prompts/` | Needs user interaction (confirm outline) |
|
|
169
|
-
| Quality fix | `prompts/` | Orchestrator applies edits, runs tools |
|
|
170
|
-
|
|
171
|
-
## Testing a template
|
|
172
|
-
|
|
173
|
-
1. **Generate** — have a model follow the template to produce a video markdown
|
|
174
|
-
2. **Render** — `npx @lalalic/markcut render <output>.md`
|
|
175
|
-
3. **Review** — run the reviewer agent on the rendered output
|
|
176
|
-
4. **Fix** — apply fixes, re-render, re-review
|
|
177
|
-
5. **Verify** — check frames visually, check STT transcript
|
|
178
|
-
|
|
179
|
-
The courseware template's test loop is the reference:
|
|
180
|
-
|
|
181
|
-
```
|
|
182
|
-
Agent (deepseek-v4-flash + SKILL.md + markdown spec) → course.md
|
|
183
|
-
└→ npx markcut render → course.mp4
|
|
184
|
-
└→ Reviewer agent (separate session) → PASS/FAIL + findings
|
|
185
|
-
└→ Fix → re-render → re-review (max 3x)
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
## Built-in templates
|
|
189
|
-
All builtin templates are in `templates/` and can be used as-is or forked for your own. Each has a `TEMPLATE.md` with full instructions.
|
|
190
|
-
|
|
191
|
-
## Cross-cutting concerns
|
|
192
|
-
|
|
193
|
-
### Multi-language
|
|
194
|
-
- Use variant blocks (`# <lang>` at file end) with per-language TTS voice.
|
|
195
|
-
- Every script node has a `<lang>:"..."` twin; every slide source has a `<lang>-source` twin.
|
|
196
|
-
- The engine resolves variants via `parseMarkdownVariants`.
|
|
197
|
-
|
|
198
|
-
### Reviewer design
|
|
199
|
-
- Always `mode: read-only` — reviewers report findings, never edit.
|
|
200
|
-
- Two-phase: static (source vs rulebook) + dynamic (rendered artifact via ffprobe/ffmpeg/STT/vision).
|
|
201
|
-
- Output: strict JSON with `{verdict, measured, findings[]}`. Each finding has `id`, `severity`, `scene`, `check`, `issue`, `evidence`, `fix_hint`.
|
|
202
|
-
- PASS requires zero `blocker` and zero `major` findings.
|
|
203
|
-
|
|
204
|
-
### The "orchestrator" role
|
|
205
|
-
The orchestrator is the agent reading the template. It:
|
|
206
|
-
1. Collects inputs from the user
|
|
207
|
-
2. Executes prompts in its own context
|
|
208
|
-
3. Spawns subagents in separate sessions for isolated tasks
|
|
209
|
-
4. Assembles, renders, fixes, and iterates
|
|
210
|
-
5. Escalates to the human user when stuck
|
|
211
|
-
|
|
212
|
-
The template is written FOR the orchestrator. Every instruction is an actionable command: "fill this prompt", "run this agent", "render the file", "if FAIL, fix and re-render".
|