@jokerized/decksmith 0.9.6 → 0.9.7
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/dist/cli.js +96 -4
- package/dist/index.js +95 -4
- package/dist/mcp.js +95 -4
- package/dist/types/plan/codex.d.ts +2 -0
- package/dist/types/plan/variety.d.ts +12 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -23630,15 +23630,26 @@ function isScene(beat2, design2) {
|
|
|
23630
23630
|
return pictured(p.backdrop);
|
|
23631
23631
|
}
|
|
23632
23632
|
}
|
|
23633
|
+
function shapesFor(storyboard, i, offered, images) {
|
|
23634
|
+
const beats = storyboard.beats;
|
|
23635
|
+
const others = beats.filter((_, k) => k !== i);
|
|
23636
|
+
const kinetic2 = others.filter((b) => b.archetype === "kinetic").length;
|
|
23637
|
+
const panels = others.filter((b) => PANEL_ARCHETYPES.has(b.archetype)).length;
|
|
23638
|
+
const panelCap = panelsAllowed(beats.length, images);
|
|
23639
|
+
return offered.filter(
|
|
23640
|
+
(a) => a !== beats[i - 1]?.archetype && a !== beats[i + 1]?.archetype && (a !== "title" || i === 0) && (a !== "kinetic" || kinetic2 < KINETIC_MAX) && (!PANEL_ARCHETYPES.has(a) || panels < panelCap)
|
|
23641
|
+
);
|
|
23642
|
+
}
|
|
23633
23643
|
function varietyFindings(storyboard, images, design2) {
|
|
23634
23644
|
const beats = storyboard.beats;
|
|
23635
23645
|
const out = [];
|
|
23646
|
+
const avoid = (beat2, other) => [beat2.archetype, other?.archetype].filter((x, k, xs) => x !== void 0 && xs.indexOf(x) === k).map((x) => `\`${x}\``).join(" or ");
|
|
23636
23647
|
for (let i = 1; i < beats.length; i++) {
|
|
23637
23648
|
const a = beats[i - 1];
|
|
23638
23649
|
const b = beats[i];
|
|
23639
23650
|
if (a.archetype === b.archetype) {
|
|
23640
23651
|
out.push(
|
|
23641
|
-
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other.
|
|
23652
|
+
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other. Redraw ${a.id} as anything but ${avoid(a, beats[i - 2])}, or ${b.id} as anything but ${avoid(b, beats[i + 1])}.`
|
|
23642
23653
|
);
|
|
23643
23654
|
}
|
|
23644
23655
|
}
|
|
@@ -25011,7 +25022,8 @@ async function codexPlanner(source, opts = {}) {
|
|
|
25011
25022
|
try {
|
|
25012
25023
|
const schemaPath = join9(dir, "storyboard.schema.json");
|
|
25013
25024
|
const outPath = join9(dir, "storyboard.json");
|
|
25014
|
-
|
|
25025
|
+
const schema = schemaFor(prefs, source);
|
|
25026
|
+
await writeFile7(schemaPath, JSON.stringify(schema));
|
|
25015
25027
|
const ask = async (prompt2) => {
|
|
25016
25028
|
await rm3(outPath, { force: true });
|
|
25017
25029
|
await (opts.run ?? runCodex)({
|
|
@@ -25155,6 +25167,46 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
25155
25167
|
);
|
|
25156
25168
|
return plan;
|
|
25157
25169
|
};
|
|
25170
|
+
const beatSchemaPath = join9(dir, "beat.schema.json");
|
|
25171
|
+
const offered = beatVariants(schema).map((v) => v.properties.archetype.const);
|
|
25172
|
+
const reshape2 = async (deck, i) => {
|
|
25173
|
+
const beat2 = deck.beats[i];
|
|
25174
|
+
const shapes = shapesFor(deck, i, offered, prefs.images);
|
|
25175
|
+
if (!shapes.length) return deck;
|
|
25176
|
+
await writeFile7(beatSchemaPath, JSON.stringify(beatSchemaFor(schema, shapes)));
|
|
25177
|
+
let problems = [];
|
|
25178
|
+
for (let t2 = 1; t2 <= SCENE_REPAIRS; t2++) {
|
|
25179
|
+
opts.onReshape?.(beat2.id, shapes);
|
|
25180
|
+
const out = join9(dir, `beat-${beat2.id}-${t2}.json`);
|
|
25181
|
+
try {
|
|
25182
|
+
await (opts.run ?? runCodex)({
|
|
25183
|
+
prompt: beatPrompt(prompt, deck, beat2, shapes, problems),
|
|
25184
|
+
schemaPath: beatSchemaPath,
|
|
25185
|
+
outPath: out,
|
|
25186
|
+
...opts.model === void 0 ? {} : { model: opts.model },
|
|
25187
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
25188
|
+
});
|
|
25189
|
+
const got = stripNulls(JSON.parse(await readFile8(out, "utf8")))?.beat;
|
|
25190
|
+
const redrawn = {
|
|
25191
|
+
...got,
|
|
25192
|
+
id: beat2.id,
|
|
25193
|
+
...beat2.literal ? { literal: beat2.literal } : {}
|
|
25194
|
+
};
|
|
25195
|
+
const beats = deck.beats.map((b, k) => k === i ? redrawn : b);
|
|
25196
|
+
return withStockLabels(checkPlan({ ...deck, beats }, source, prefs), source);
|
|
25197
|
+
} catch (err) {
|
|
25198
|
+
problems = err instanceof PlanRejected ? err.problems : [`the ask failed: ${err instanceof Error ? err.message : err}`];
|
|
25199
|
+
}
|
|
25200
|
+
}
|
|
25201
|
+
return deck;
|
|
25202
|
+
};
|
|
25203
|
+
const neighbours = async (planned) => {
|
|
25204
|
+
let plan = planned;
|
|
25205
|
+
for (let i = 1; i < plan.beats.length; i++)
|
|
25206
|
+
if (plan.beats[i]?.archetype === plan.beats[i - 1]?.archetype)
|
|
25207
|
+
plan = await reshape2(plan, i);
|
|
25208
|
+
return plan;
|
|
25209
|
+
};
|
|
25158
25210
|
let answer = await attempt2(prompt);
|
|
25159
25211
|
if (answer.plan && listOf(answer.broken).length === 0) return await scenes(answer.plan);
|
|
25160
25212
|
const firstBeats = beatCount(answer.json);
|
|
@@ -25174,7 +25226,13 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
25174
25226
|
advice: []
|
|
25175
25227
|
};
|
|
25176
25228
|
if (next.plan && listOf(still).length === 0) return await scenes(next.plan);
|
|
25177
|
-
if (round8 === REPAIRS)
|
|
25229
|
+
if (round8 === REPAIRS) {
|
|
25230
|
+
if (!next.plan || !listOf(still).every(isNeighbour)) throw unrepaired(next, still, round8);
|
|
25231
|
+
const plan = await neighbours(next.plan);
|
|
25232
|
+
const left = { ...findings(plan), advice: [] };
|
|
25233
|
+
if (listOf(left).length) throw unrepaired(next, left, round8);
|
|
25234
|
+
return await scenes(plan);
|
|
25235
|
+
}
|
|
25178
25236
|
merging ||= still.size.length > 0;
|
|
25179
25237
|
answer = { ...next, broken: still };
|
|
25180
25238
|
}
|
|
@@ -25229,6 +25287,37 @@ ${list2}
|
|
|
25229
25287
|
|
|
25230
25288
|
Return {"literal": ...}: beat ${id2}'s scene, redrawn so none of these hold \u2014 every number, word and figure one the source states, every slot its verb needs filled. Keep its verb unless a finding names another. Return a scene: this ask has no way to leave it out. No other beat is yours to change.
|
|
25231
25289
|
|
|
25290
|
+
THE STORYBOARD:
|
|
25291
|
+
${JSON.stringify(plan, null, 2)}`;
|
|
25292
|
+
}
|
|
25293
|
+
function isNeighbour(finding2) {
|
|
25294
|
+
return /^\S+ and \S+ are both `[^`]+`, next to each other\./.test(finding2);
|
|
25295
|
+
}
|
|
25296
|
+
function beatVariants(schema) {
|
|
25297
|
+
return schema.properties.beats.items.anyOf;
|
|
25298
|
+
}
|
|
25299
|
+
function beatSchemaFor(schema, shapes) {
|
|
25300
|
+
const anyOf = beatVariants(schema).filter((v) => shapes.includes(v.properties.archetype.const)).map((v) => hideFromPlanner(v, /* @__PURE__ */ new Set(["literal"])));
|
|
25301
|
+
return {
|
|
25302
|
+
type: "object",
|
|
25303
|
+
properties: { beat: { anyOf } },
|
|
25304
|
+
required: ["beat"],
|
|
25305
|
+
additionalProperties: false
|
|
25306
|
+
};
|
|
25307
|
+
}
|
|
25308
|
+
function beatPrompt(prompt, plan, beat2, shapes, problems) {
|
|
25309
|
+
const refused = problems.length ? `
|
|
25310
|
+
Your last answer was refused for:
|
|
25311
|
+
${problems.map((m) => ` - ${m}`).join("\n")}
|
|
25312
|
+
` : "";
|
|
25313
|
+
return `${prompt}
|
|
25314
|
+
|
|
25315
|
+
THE DECK IS PLANNED. ONE BEAT'S SHAPE IS LEFT.
|
|
25316
|
+
|
|
25317
|
+
The storyboard below is settled, but beat ${beat2.id} is \`${beat2.archetype}\` beside a beat of the same shape, and no two beats in a row may share an archetype.
|
|
25318
|
+
${refused}
|
|
25319
|
+
Return {"beat": ...}: beat ${beat2.id} redrawn as one of ${shapes.map((s) => `\`${s}\``).join(", ")} \u2014 whichever shows its point best \u2014 with the params that shape needs, every number, word and figure one the source states. Keep its id, its point, its evidence, its narration and its part. No other beat is yours to change.
|
|
25320
|
+
|
|
25232
25321
|
THE STORYBOARD:
|
|
25233
25322
|
${JSON.stringify(plan, null, 2)}`;
|
|
25234
25323
|
}
|
|
@@ -25326,7 +25415,9 @@ async function readPlan(outPath, source, prefs) {
|
|
|
25326
25415
|
${raw2.slice(0, 200)}`
|
|
25327
25416
|
);
|
|
25328
25417
|
}
|
|
25329
|
-
|
|
25418
|
+
return checkPlan(stripNulls(parsed), source, prefs);
|
|
25419
|
+
}
|
|
25420
|
+
function checkPlan(plan, source, prefs) {
|
|
25330
25421
|
const result = storyboardSchema.safeParse(plan);
|
|
25331
25422
|
if (!result.success) {
|
|
25332
25423
|
const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
|
|
@@ -38201,6 +38292,7 @@ imageFlags(
|
|
|
38201
38292
|
step(`plan: redrawing ${beat2}'s scene on its own \u2014`);
|
|
38202
38293
|
for (const m of broken) step(`plan: ${m}`);
|
|
38203
38294
|
},
|
|
38295
|
+
onReshape: (beat2, shapes) => step(`plan: redrawing ${beat2} on its own, as one of: ${shapes.join(", ")}`),
|
|
38204
38296
|
onDropScenes: (beats, why3) => {
|
|
38205
38297
|
step(
|
|
38206
38298
|
`plan: WARNING: ${beats.join(", ")} drawn by their archetype instead; their scenes stay broken \u2014`
|
package/dist/index.js
CHANGED
|
@@ -25977,15 +25977,26 @@ function isScene(beat2, design2) {
|
|
|
25977
25977
|
return pictured(p.backdrop);
|
|
25978
25978
|
}
|
|
25979
25979
|
}
|
|
25980
|
+
function shapesFor(storyboard, i, offered, images) {
|
|
25981
|
+
const beats = storyboard.beats;
|
|
25982
|
+
const others = beats.filter((_, k) => k !== i);
|
|
25983
|
+
const kinetic2 = others.filter((b) => b.archetype === "kinetic").length;
|
|
25984
|
+
const panels = others.filter((b) => PANEL_ARCHETYPES.has(b.archetype)).length;
|
|
25985
|
+
const panelCap = panelsAllowed(beats.length, images);
|
|
25986
|
+
return offered.filter(
|
|
25987
|
+
(a) => a !== beats[i - 1]?.archetype && a !== beats[i + 1]?.archetype && (a !== "title" || i === 0) && (a !== "kinetic" || kinetic2 < KINETIC_MAX) && (!PANEL_ARCHETYPES.has(a) || panels < panelCap)
|
|
25988
|
+
);
|
|
25989
|
+
}
|
|
25980
25990
|
function varietyFindings(storyboard, images, design2) {
|
|
25981
25991
|
const beats = storyboard.beats;
|
|
25982
25992
|
const out = [];
|
|
25993
|
+
const avoid = (beat2, other) => [beat2.archetype, other?.archetype].filter((x, k, xs) => x !== void 0 && xs.indexOf(x) === k).map((x) => `\`${x}\``).join(" or ");
|
|
25983
25994
|
for (let i = 1; i < beats.length; i++) {
|
|
25984
25995
|
const a = beats[i - 1];
|
|
25985
25996
|
const b = beats[i];
|
|
25986
25997
|
if (a.archetype === b.archetype) {
|
|
25987
25998
|
out.push(
|
|
25988
|
-
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other.
|
|
25999
|
+
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other. Redraw ${a.id} as anything but ${avoid(a, beats[i - 2])}, or ${b.id} as anything but ${avoid(b, beats[i + 1])}.`
|
|
25989
26000
|
);
|
|
25990
26001
|
}
|
|
25991
26002
|
}
|
|
@@ -27358,7 +27369,8 @@ async function codexPlanner(source, opts = {}) {
|
|
|
27358
27369
|
try {
|
|
27359
27370
|
const schemaPath = join12(dir, "storyboard.schema.json");
|
|
27360
27371
|
const outPath = join12(dir, "storyboard.json");
|
|
27361
|
-
|
|
27372
|
+
const schema = schemaFor(prefs, source);
|
|
27373
|
+
await writeFile10(schemaPath, JSON.stringify(schema));
|
|
27362
27374
|
const ask = async (prompt2) => {
|
|
27363
27375
|
await rm5(outPath, { force: true });
|
|
27364
27376
|
await (opts.run ?? runCodex)({
|
|
@@ -27502,6 +27514,46 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
27502
27514
|
);
|
|
27503
27515
|
return plan;
|
|
27504
27516
|
};
|
|
27517
|
+
const beatSchemaPath = join12(dir, "beat.schema.json");
|
|
27518
|
+
const offered = beatVariants(schema).map((v) => v.properties.archetype.const);
|
|
27519
|
+
const reshape2 = async (deck, i) => {
|
|
27520
|
+
const beat2 = deck.beats[i];
|
|
27521
|
+
const shapes = shapesFor(deck, i, offered, prefs.images);
|
|
27522
|
+
if (!shapes.length) return deck;
|
|
27523
|
+
await writeFile10(beatSchemaPath, JSON.stringify(beatSchemaFor(schema, shapes)));
|
|
27524
|
+
let problems = [];
|
|
27525
|
+
for (let t2 = 1; t2 <= SCENE_REPAIRS; t2++) {
|
|
27526
|
+
opts.onReshape?.(beat2.id, shapes);
|
|
27527
|
+
const out = join12(dir, `beat-${beat2.id}-${t2}.json`);
|
|
27528
|
+
try {
|
|
27529
|
+
await (opts.run ?? runCodex)({
|
|
27530
|
+
prompt: beatPrompt(prompt, deck, beat2, shapes, problems),
|
|
27531
|
+
schemaPath: beatSchemaPath,
|
|
27532
|
+
outPath: out,
|
|
27533
|
+
...opts.model === void 0 ? {} : { model: opts.model },
|
|
27534
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2
|
|
27535
|
+
});
|
|
27536
|
+
const got = stripNulls(JSON.parse(await readFile12(out, "utf8")))?.beat;
|
|
27537
|
+
const redrawn = {
|
|
27538
|
+
...got,
|
|
27539
|
+
id: beat2.id,
|
|
27540
|
+
...beat2.literal ? { literal: beat2.literal } : {}
|
|
27541
|
+
};
|
|
27542
|
+
const beats = deck.beats.map((b, k) => k === i ? redrawn : b);
|
|
27543
|
+
return withStockLabels(checkPlan({ ...deck, beats }, source, prefs), source);
|
|
27544
|
+
} catch (err) {
|
|
27545
|
+
problems = err instanceof PlanRejected ? err.problems : [`the ask failed: ${err instanceof Error ? err.message : err}`];
|
|
27546
|
+
}
|
|
27547
|
+
}
|
|
27548
|
+
return deck;
|
|
27549
|
+
};
|
|
27550
|
+
const neighbours = async (planned) => {
|
|
27551
|
+
let plan = planned;
|
|
27552
|
+
for (let i = 1; i < plan.beats.length; i++)
|
|
27553
|
+
if (plan.beats[i]?.archetype === plan.beats[i - 1]?.archetype)
|
|
27554
|
+
plan = await reshape2(plan, i);
|
|
27555
|
+
return plan;
|
|
27556
|
+
};
|
|
27505
27557
|
let answer = await attempt2(prompt);
|
|
27506
27558
|
if (answer.plan && listOf(answer.broken).length === 0) return await scenes(answer.plan);
|
|
27507
27559
|
const firstBeats = beatCount(answer.json);
|
|
@@ -27521,7 +27573,13 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
27521
27573
|
advice: []
|
|
27522
27574
|
};
|
|
27523
27575
|
if (next.plan && listOf(still).length === 0) return await scenes(next.plan);
|
|
27524
|
-
if (round8 === REPAIRS)
|
|
27576
|
+
if (round8 === REPAIRS) {
|
|
27577
|
+
if (!next.plan || !listOf(still).every(isNeighbour)) throw unrepaired(next, still, round8);
|
|
27578
|
+
const plan = await neighbours(next.plan);
|
|
27579
|
+
const left = { ...findings(plan), advice: [] };
|
|
27580
|
+
if (listOf(left).length) throw unrepaired(next, left, round8);
|
|
27581
|
+
return await scenes(plan);
|
|
27582
|
+
}
|
|
27525
27583
|
merging ||= still.size.length > 0;
|
|
27526
27584
|
answer = { ...next, broken: still };
|
|
27527
27585
|
}
|
|
@@ -27576,6 +27634,37 @@ ${list2}
|
|
|
27576
27634
|
|
|
27577
27635
|
Return {"literal": ...}: beat ${id2}'s scene, redrawn so none of these hold \u2014 every number, word and figure one the source states, every slot its verb needs filled. Keep its verb unless a finding names another. Return a scene: this ask has no way to leave it out. No other beat is yours to change.
|
|
27578
27636
|
|
|
27637
|
+
THE STORYBOARD:
|
|
27638
|
+
${JSON.stringify(plan, null, 2)}`;
|
|
27639
|
+
}
|
|
27640
|
+
function isNeighbour(finding2) {
|
|
27641
|
+
return /^\S+ and \S+ are both `[^`]+`, next to each other\./.test(finding2);
|
|
27642
|
+
}
|
|
27643
|
+
function beatVariants(schema) {
|
|
27644
|
+
return schema.properties.beats.items.anyOf;
|
|
27645
|
+
}
|
|
27646
|
+
function beatSchemaFor(schema, shapes) {
|
|
27647
|
+
const anyOf = beatVariants(schema).filter((v) => shapes.includes(v.properties.archetype.const)).map((v) => hideFromPlanner(v, /* @__PURE__ */ new Set(["literal"])));
|
|
27648
|
+
return {
|
|
27649
|
+
type: "object",
|
|
27650
|
+
properties: { beat: { anyOf } },
|
|
27651
|
+
required: ["beat"],
|
|
27652
|
+
additionalProperties: false
|
|
27653
|
+
};
|
|
27654
|
+
}
|
|
27655
|
+
function beatPrompt(prompt, plan, beat2, shapes, problems) {
|
|
27656
|
+
const refused = problems.length ? `
|
|
27657
|
+
Your last answer was refused for:
|
|
27658
|
+
${problems.map((m) => ` - ${m}`).join("\n")}
|
|
27659
|
+
` : "";
|
|
27660
|
+
return `${prompt}
|
|
27661
|
+
|
|
27662
|
+
THE DECK IS PLANNED. ONE BEAT'S SHAPE IS LEFT.
|
|
27663
|
+
|
|
27664
|
+
The storyboard below is settled, but beat ${beat2.id} is \`${beat2.archetype}\` beside a beat of the same shape, and no two beats in a row may share an archetype.
|
|
27665
|
+
${refused}
|
|
27666
|
+
Return {"beat": ...}: beat ${beat2.id} redrawn as one of ${shapes.map((s) => `\`${s}\``).join(", ")} \u2014 whichever shows its point best \u2014 with the params that shape needs, every number, word and figure one the source states. Keep its id, its point, its evidence, its narration and its part. No other beat is yours to change.
|
|
27667
|
+
|
|
27579
27668
|
THE STORYBOARD:
|
|
27580
27669
|
${JSON.stringify(plan, null, 2)}`;
|
|
27581
27670
|
}
|
|
@@ -27673,7 +27762,9 @@ async function readPlan(outPath, source, prefs) {
|
|
|
27673
27762
|
${raw2.slice(0, 200)}`
|
|
27674
27763
|
);
|
|
27675
27764
|
}
|
|
27676
|
-
|
|
27765
|
+
return checkPlan(stripNulls(parsed), source, prefs);
|
|
27766
|
+
}
|
|
27767
|
+
function checkPlan(plan, source, prefs) {
|
|
27677
27768
|
const result = storyboardSchema.safeParse(plan);
|
|
27678
27769
|
if (!result.success) {
|
|
27679
27770
|
const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
|
package/dist/mcp.js
CHANGED
|
@@ -24489,15 +24489,26 @@ function isScene(beat, design2) {
|
|
|
24489
24489
|
return pictured(p.backdrop);
|
|
24490
24490
|
}
|
|
24491
24491
|
}
|
|
24492
|
+
function shapesFor(storyboard, i, offered, images) {
|
|
24493
|
+
const beats = storyboard.beats;
|
|
24494
|
+
const others = beats.filter((_, k) => k !== i);
|
|
24495
|
+
const kinetic2 = others.filter((b) => b.archetype === "kinetic").length;
|
|
24496
|
+
const panels = others.filter((b) => PANEL_ARCHETYPES.has(b.archetype)).length;
|
|
24497
|
+
const panelCap = panelsAllowed(beats.length, images);
|
|
24498
|
+
return offered.filter(
|
|
24499
|
+
(a) => a !== beats[i - 1]?.archetype && a !== beats[i + 1]?.archetype && (a !== "title" || i === 0) && (a !== "kinetic" || kinetic2 < KINETIC_MAX) && (!PANEL_ARCHETYPES.has(a) || panels < panelCap)
|
|
24500
|
+
);
|
|
24501
|
+
}
|
|
24492
24502
|
function varietyFindings(storyboard, images, design2) {
|
|
24493
24503
|
const beats = storyboard.beats;
|
|
24494
24504
|
const out = [];
|
|
24505
|
+
const avoid = (beat, other) => [beat.archetype, other?.archetype].filter((x, k, xs) => x !== void 0 && xs.indexOf(x) === k).map((x) => `\`${x}\``).join(" or ");
|
|
24495
24506
|
for (let i = 1; i < beats.length; i++) {
|
|
24496
24507
|
const a = beats[i - 1];
|
|
24497
24508
|
const b = beats[i];
|
|
24498
24509
|
if (a.archetype === b.archetype) {
|
|
24499
24510
|
out.push(
|
|
24500
|
-
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other.
|
|
24511
|
+
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other. Redraw ${a.id} as anything but ${avoid(a, beats[i - 2])}, or ${b.id} as anything but ${avoid(b, beats[i + 1])}.`
|
|
24501
24512
|
);
|
|
24502
24513
|
}
|
|
24503
24514
|
}
|
|
@@ -25828,7 +25839,8 @@ async function codexPlanner(source, opts = {}) {
|
|
|
25828
25839
|
try {
|
|
25829
25840
|
const schemaPath = join11(dir, "storyboard.schema.json");
|
|
25830
25841
|
const outPath = join11(dir, "storyboard.json");
|
|
25831
|
-
|
|
25842
|
+
const schema2 = schemaFor(prefs, source);
|
|
25843
|
+
await writeFile8(schemaPath, JSON.stringify(schema2));
|
|
25832
25844
|
const ask = async (prompt2) => {
|
|
25833
25845
|
await rm5(outPath, { force: true });
|
|
25834
25846
|
await (opts.run ?? runCodex)({
|
|
@@ -25972,6 +25984,46 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
25972
25984
|
);
|
|
25973
25985
|
return plan;
|
|
25974
25986
|
};
|
|
25987
|
+
const beatSchemaPath = join11(dir, "beat.schema.json");
|
|
25988
|
+
const offered = beatVariants(schema2).map((v) => v.properties.archetype.const);
|
|
25989
|
+
const reshape2 = async (deck, i) => {
|
|
25990
|
+
const beat = deck.beats[i];
|
|
25991
|
+
const shapes = shapesFor(deck, i, offered, prefs.images);
|
|
25992
|
+
if (!shapes.length) return deck;
|
|
25993
|
+
await writeFile8(beatSchemaPath, JSON.stringify(beatSchemaFor(schema2, shapes)));
|
|
25994
|
+
let problems = [];
|
|
25995
|
+
for (let t2 = 1; t2 <= SCENE_REPAIRS; t2++) {
|
|
25996
|
+
opts.onReshape?.(beat.id, shapes);
|
|
25997
|
+
const out = join11(dir, `beat-${beat.id}-${t2}.json`);
|
|
25998
|
+
try {
|
|
25999
|
+
await (opts.run ?? runCodex)({
|
|
26000
|
+
prompt: beatPrompt(prompt, deck, beat, shapes, problems),
|
|
26001
|
+
schemaPath: beatSchemaPath,
|
|
26002
|
+
outPath: out,
|
|
26003
|
+
...opts.model === void 0 ? {} : { model: opts.model },
|
|
26004
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2
|
|
26005
|
+
});
|
|
26006
|
+
const got = stripNulls(JSON.parse(await readFile9(out, "utf8")))?.beat;
|
|
26007
|
+
const redrawn = {
|
|
26008
|
+
...got,
|
|
26009
|
+
id: beat.id,
|
|
26010
|
+
...beat.literal ? { literal: beat.literal } : {}
|
|
26011
|
+
};
|
|
26012
|
+
const beats = deck.beats.map((b, k) => k === i ? redrawn : b);
|
|
26013
|
+
return withStockLabels(checkPlan({ ...deck, beats }, source, prefs), source);
|
|
26014
|
+
} catch (err) {
|
|
26015
|
+
problems = err instanceof PlanRejected ? err.problems : [`the ask failed: ${err instanceof Error ? err.message : err}`];
|
|
26016
|
+
}
|
|
26017
|
+
}
|
|
26018
|
+
return deck;
|
|
26019
|
+
};
|
|
26020
|
+
const neighbours = async (planned) => {
|
|
26021
|
+
let plan = planned;
|
|
26022
|
+
for (let i = 1; i < plan.beats.length; i++)
|
|
26023
|
+
if (plan.beats[i]?.archetype === plan.beats[i - 1]?.archetype)
|
|
26024
|
+
plan = await reshape2(plan, i);
|
|
26025
|
+
return plan;
|
|
26026
|
+
};
|
|
25975
26027
|
let answer = await attempt2(prompt);
|
|
25976
26028
|
if (answer.plan && listOf(answer.broken).length === 0) return await scenes(answer.plan);
|
|
25977
26029
|
const firstBeats = beatCount(answer.json);
|
|
@@ -25991,7 +26043,13 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
25991
26043
|
advice: []
|
|
25992
26044
|
};
|
|
25993
26045
|
if (next.plan && listOf(still).length === 0) return await scenes(next.plan);
|
|
25994
|
-
if (round6 === REPAIRS)
|
|
26046
|
+
if (round6 === REPAIRS) {
|
|
26047
|
+
if (!next.plan || !listOf(still).every(isNeighbour)) throw unrepaired(next, still, round6);
|
|
26048
|
+
const plan = await neighbours(next.plan);
|
|
26049
|
+
const left = { ...findings(plan), advice: [] };
|
|
26050
|
+
if (listOf(left).length) throw unrepaired(next, left, round6);
|
|
26051
|
+
return await scenes(plan);
|
|
26052
|
+
}
|
|
25995
26053
|
merging ||= still.size.length > 0;
|
|
25996
26054
|
answer = { ...next, broken: still };
|
|
25997
26055
|
}
|
|
@@ -26046,6 +26104,37 @@ ${list2}
|
|
|
26046
26104
|
|
|
26047
26105
|
Return {"literal": ...}: beat ${id2}'s scene, redrawn so none of these hold \u2014 every number, word and figure one the source states, every slot its verb needs filled. Keep its verb unless a finding names another. Return a scene: this ask has no way to leave it out. No other beat is yours to change.
|
|
26048
26106
|
|
|
26107
|
+
THE STORYBOARD:
|
|
26108
|
+
${JSON.stringify(plan, null, 2)}`;
|
|
26109
|
+
}
|
|
26110
|
+
function isNeighbour(finding) {
|
|
26111
|
+
return /^\S+ and \S+ are both `[^`]+`, next to each other\./.test(finding);
|
|
26112
|
+
}
|
|
26113
|
+
function beatVariants(schema2) {
|
|
26114
|
+
return schema2.properties.beats.items.anyOf;
|
|
26115
|
+
}
|
|
26116
|
+
function beatSchemaFor(schema2, shapes) {
|
|
26117
|
+
const anyOf = beatVariants(schema2).filter((v) => shapes.includes(v.properties.archetype.const)).map((v) => hideFromPlanner(v, /* @__PURE__ */ new Set(["literal"])));
|
|
26118
|
+
return {
|
|
26119
|
+
type: "object",
|
|
26120
|
+
properties: { beat: { anyOf } },
|
|
26121
|
+
required: ["beat"],
|
|
26122
|
+
additionalProperties: false
|
|
26123
|
+
};
|
|
26124
|
+
}
|
|
26125
|
+
function beatPrompt(prompt, plan, beat, shapes, problems) {
|
|
26126
|
+
const refused = problems.length ? `
|
|
26127
|
+
Your last answer was refused for:
|
|
26128
|
+
${problems.map((m) => ` - ${m}`).join("\n")}
|
|
26129
|
+
` : "";
|
|
26130
|
+
return `${prompt}
|
|
26131
|
+
|
|
26132
|
+
THE DECK IS PLANNED. ONE BEAT'S SHAPE IS LEFT.
|
|
26133
|
+
|
|
26134
|
+
The storyboard below is settled, but beat ${beat.id} is \`${beat.archetype}\` beside a beat of the same shape, and no two beats in a row may share an archetype.
|
|
26135
|
+
${refused}
|
|
26136
|
+
Return {"beat": ...}: beat ${beat.id} redrawn as one of ${shapes.map((s) => `\`${s}\``).join(", ")} \u2014 whichever shows its point best \u2014 with the params that shape needs, every number, word and figure one the source states. Keep its id, its point, its evidence, its narration and its part. No other beat is yours to change.
|
|
26137
|
+
|
|
26049
26138
|
THE STORYBOARD:
|
|
26050
26139
|
${JSON.stringify(plan, null, 2)}`;
|
|
26051
26140
|
}
|
|
@@ -26143,7 +26232,9 @@ async function readPlan(outPath, source, prefs) {
|
|
|
26143
26232
|
${raw2.slice(0, 200)}`
|
|
26144
26233
|
);
|
|
26145
26234
|
}
|
|
26146
|
-
|
|
26235
|
+
return checkPlan(stripNulls(parsed), source, prefs);
|
|
26236
|
+
}
|
|
26237
|
+
function checkPlan(plan, source, prefs) {
|
|
26147
26238
|
const result = storyboardSchema.safeParse(plan);
|
|
26148
26239
|
if (!result.success) {
|
|
26149
26240
|
const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
|
|
@@ -31,6 +31,8 @@ export interface CodexOptions {
|
|
|
31
31
|
onScene?: (beat: string, broken: readonly string[]) => void;
|
|
32
32
|
/** Told the beats whose scene was dropped (still broken after its own repairs, or broken only by a symbolic quantity), and why. */
|
|
33
33
|
onDropScenes?: (beats: readonly string[], why: readonly string[]) => void;
|
|
34
|
+
/** Told a beat still beside its own shape after the last repair is redrawn on its own, as one of `shapes`. */
|
|
35
|
+
onReshape?: (beat: string, shapes: readonly string[]) => void;
|
|
34
36
|
/** Each source figure's panel count as the build will cut it, when the caller read them. */
|
|
35
37
|
panels?: Readonly<Record<string, number>>;
|
|
36
38
|
}
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* So the rule is a minimum, not an invitation, and it is CHECKED: the prompt
|
|
13
13
|
* (src/plan/prompt.ts) states these numbers by importing them from here, and
|
|
14
14
|
* `codexPlanner` (src/plan/codex.ts) runs `varietyFindings` on what comes back,
|
|
15
|
-
* asks
|
|
15
|
+
* asks for up to three repairs, redraws a beat still beside its own shape on
|
|
16
|
+
* its own (`shapesFor`), and refuses a plan that still breaks it. One table,
|
|
16
17
|
* read by both, so the sentence the model is given and the check it is held to
|
|
17
18
|
* cannot drift apart.
|
|
18
19
|
*
|
|
@@ -90,6 +91,16 @@ export declare function scenesRequired(beats: number, images: Prefs["images"]):
|
|
|
90
91
|
* the cards-on-paper the share exists to refuse.
|
|
91
92
|
*/
|
|
92
93
|
export declare function isScene(beat: Beat, design?: Design): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* The shapes, of those `offered`, that beat `i` may be redrawn as: not either
|
|
96
|
+
* neighbour's, not a title past the first beat, and not one that tips the
|
|
97
|
+
* kinetic or panel cap. What the last-resort redraw of one beat
|
|
98
|
+
* (src/plan/codex.ts) offers in its schema, so the shape cannot be wrong.
|
|
99
|
+
*
|
|
100
|
+
* ponytail: the A/B/A/B and scene-share rules are not foreseen here; the plan
|
|
101
|
+
* is checked whole after the redraw, and refused if one of them broke.
|
|
102
|
+
*/
|
|
103
|
+
export declare function shapesFor(storyboard: Storyboard, i: number, offered: readonly Archetype[], images: Prefs["images"]): Archetype[];
|
|
93
104
|
/**
|
|
94
105
|
* Every way `storyboard` breaks the variety rule, as sentences the planner can
|
|
95
106
|
* act on. Empty is a pass.
|