@jokerized/decksmith 0.9.0 → 0.9.1

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 (35) hide show
  1. package/README.md +212 -0
  2. package/dist/cli.js +20183 -14325
  3. package/dist/deck-player-element.js +1 -1
  4. package/dist/deck-player.js +1 -1
  5. package/dist/index.js +10605 -4957
  6. package/dist/mcp.js +1558 -319
  7. package/dist/types/bespoke/art.d.ts +173 -0
  8. package/dist/types/bespoke/cache.d.ts +69 -0
  9. package/dist/types/bespoke/callouts.d.ts +76 -0
  10. package/dist/types/bespoke/contract.d.ts +57 -0
  11. package/dist/types/bespoke/inspect.d.ts +69 -0
  12. package/dist/types/bespoke/pipeline.d.ts +207 -0
  13. package/dist/types/bespoke/probe.d.ts +34 -0
  14. package/dist/types/bespoke/prompt.d.ts +182 -0
  15. package/dist/types/bespoke/references.d.ts +73 -0
  16. package/dist/types/bespoke/repair.d.ts +121 -0
  17. package/dist/types/bespoke/scene.d.ts +97 -0
  18. package/dist/types/bespoke/select.d.ts +52 -0
  19. package/dist/types/bespoke/sheet.d.ts +26 -0
  20. package/dist/types/bespoke/shots.d.ts +128 -0
  21. package/dist/types/bespoke/untangle.d.ts +5 -0
  22. package/dist/types/emit/archetypes/index.d.ts +0 -7
  23. package/dist/types/emit/composition.d.ts +37 -2
  24. package/dist/types/emit/kit.d.ts +17 -0
  25. package/dist/types/emit/themes/pick.d.ts +6 -3
  26. package/dist/types/index.d.ts +17 -0
  27. package/dist/types/plan/codex.d.ts +36 -0
  28. package/dist/types/prefs.d.ts +10 -1
  29. package/dist/types/render/capture.d.ts +6 -0
  30. package/dist/types/render/timing.d.ts +3 -0
  31. package/dist/types/types.d.ts +71 -0
  32. package/dist/types/verify/check.d.ts +1 -1
  33. package/dist/types/verify/index.d.ts +14 -0
  34. package/dist/types/verify/scenes.d.ts +325 -0
  35. package/package.json +2 -1
package/README.md CHANGED
@@ -924,6 +924,218 @@ already built) and, while a stop's audio plays, seeks the scene through that sto
924
924
  stretch on the audio clock so the emphasis lands on the same word as in the video. Both
925
925
  are off under `prefers-reduced-motion`.
926
926
 
927
+ ## v2 bespoke scenes (`--bespoke`)
928
+
929
+ The archetypes finish building in about five seconds and then hold still while the voice
930
+ keeps talking. `build --design v2 --bespoke` hands four to six beats per deck to Codex
931
+ instead: for each, a GSAP + SVG scene written for that beat, keyed to its narration cues,
932
+ that keeps explaining for as long as the sentence runs. Everything else stays v2. It is off
933
+ unless asked for; v2 without it, and classic, emit the bytes they did.
934
+
935
+ ```bash
936
+ decksmith build storyboard.json --source source.json -o deck --design v2 --bespoke
937
+ # bespoke: b03-action-loop.draft in 121s …
938
+ # bespoke: b05-backbone — every gate passed and the rubric probe is clean; no critique call
939
+ # bespoke: 4 of 5 beats drawn bespoke, 7 Codex call(s), 190985 tokens, 343s
940
+ ```
941
+
942
+ **Which beats.** A deterministic rule (`src/bespoke/select.ts`): mechanism archetypes
943
+ (pipeline, equation-walk/-morph, line-chart, stack, grid, split-/bar-compare, and figures
944
+ last), mechanism words in en/ko/ja/zh, a cited equation, and the beat's weight; ties
945
+ broken by a hash. Never a title, callout or table, a beat with no narration, or a beat a
946
+ camera dives through. `plan --bespoke` lets the planner mark beats `bespoke: true|false`;
947
+ `false` is obeyed, `true` is a strong hint.
948
+
949
+ **Per beat:** illustration → draft → static contract → probe deck (a real build with every
950
+ `verify` gate plus the motion gates, photographed at every cue) → the rubric probe → at most
951
+ one critique-and-fix call with the contact sheet and the measures attached (`codex exec -i`)
952
+ → static contract → probe again → repair rounds. A draft that passed every gate and is clean
953
+ by the rubric probe is kept without the critique call; one that fails only on what the
954
+ repair fixes, and is otherwise clean, is repaired instead of critiqued. A beat that does not
955
+ pass keeps its archetype — the deck is never failed. If the fix round breaks a draft that
956
+ passed, the draft is kept.
957
+
958
+ **The illustration** (`src/bespoke/art.ts`, `src/bespoke/inspect.ts`). Every bespoke beat
959
+ but a data beat gets ONE picture from the image tool of the same Codex account
960
+ (`codex exec` with every tool off but `image_generation`; no API key — the account's own
961
+ login, as for the scenes), all of a deck's pictures asked for at once before the drafts
962
+ queue for their lanes. The illustrator is given the beat (fenced, untrusted), the pack's
963
+ colours and one fixed style paragraph — flat 2D vector, solid fills with hard edges; no
964
+ gradients, shading, shadows, gloss, 3D, clay or toy renders, by name — and asked for three
965
+ or four subjects doing the method, big, left to right, with empty ground between them and
966
+ NO text. Each picture is then inspected locally, for free: its subjects are the connected
967
+ regions that are not its flat ground (boxed, left to right); its flatness is how much of
968
+ it sits in its 16 commonest colours minus how much of it is soft shading (`FLAT_MIN`,
969
+ calibrated on round 3's 21 renders against 12 flat pictures); and its writing is read by
970
+ macOS Vision, once per script (en, ko, ja, zh), through a small Swift helper compiled once
971
+ into `~/.cache/decksmith/tools`. A picture with writing, a shaded render or one whose
972
+ subjects touch is redrawn ONCE with the reason; of two refused pictures the one without
973
+ writing and the flatter is kept, and one with writing never is. Off macOS the text check
974
+ reports `unchecked` in `bespoke.json` rather than passing. The deck gets a WebP with its
975
+ edges feathered into transparency for its box (~30-75 KB against 1.3 MB of PNG; a CSS mask
976
+ on the picture cost ~0.7s a probe frame); the draft call is shown the PNG and a copy with
977
+ the subjects boxed and numbered (`-i`). The shell places it: `<image data-art="1">` with no
978
+ href gets the href and a fixed placement covering the body box (`slice`), so the subjects'
979
+ boxes are known in box px before the scene is written. It is read only from
980
+ `$CODEX_HOME/generated_images/` (after symlinks), only as a PNG by its magic bytes, under
981
+ 12 MB. `--bespoke-art <n>` (default 6; `bespoke.art`) caps pictures per deck, counted apart
982
+ from `--bespoke-calls`; a beat whose picture cannot be had is drawn without one.
983
+
984
+ **Data beats** (line-chart, bar-compare, data-table) get no picture: their scene is asked
985
+ for the chart that builds with the voice — axes draw, bars grow with their counters, the
986
+ line traces, the named value lights — from the beat's own numbers, never a table painted
987
+ over an illustration (`data_over_picture`).
988
+
989
+ **The camera** of an illustrated scene is the shell's, staged in one grammar
990
+ (`src/bespoke/shots.ts`). The scene names its `shots` — on which cue, how far into it, which
991
+ subject — and the shell compiles them: an ESTABLISHING shot of the whole picture for at
992
+ least 1.6s, a PUSH IN (1.1s, power3.inOut) that frames the named subject and its label at
993
+ 1.6-2.2x, a move straight to the next subject, a 3% creep while a shot is held, and a
994
+ REVEAL back to the whole picture at the last cue; shots closer than 1.6s are dropped. Every
995
+ camera tween is a `fromTo` with explicit from-values, and no two touch. The scene's own
996
+ script may not move `#sN-cam` (`script_camera`). A scene without a picture still moves the
997
+ wrapper itself (`scale`/`x`/`y`, arithmetic in the prompt). A scene with a moving camera is
998
+ clipped to its box and marked `data-ds-clip`, so `verify` does not count what a push-in
999
+ carries past the canvas edge as off-canvas.
1000
+
1001
+ **The labels** of an illustrated scene are the shell's too (`src/bespoke/callouts.ts`). The
1002
+ scene says what each subject is called (`labels`); the shell gives each subject a zone just
1003
+ above it (or across its top when there is no room), as wide as half the gap to each
1004
+ neighbour, and sets the name there on a plate — 56px, shrinking to 44, then two lines —
1005
+ with a leader line to a dot on the subject, entering as the camera first arrives on it (or
1006
+ at the reveal). The zones are in the draft prompt, which tells the scene to keep its own
1007
+ drawing out of them; a name that does not fit even on two lines at 44px is refused
1008
+ statically (`labels`). Round 4's first run, with the scene placing its own labels on the
1009
+ subjects, put them there and then failed four scenes in five on their own plates and
1010
+ leader lines crossing their text.
1011
+
1012
+ **Repair** (`src/bespoke/repair.ts`), no model call. On `text_overlap`,
1013
+ `graphic_crosses_text` or `svg_text_overprint`, each colliding label's unit (the text, or
1014
+ the smallest `<g>` holding it and its plate) gets one constant offset — the smallest, in
1015
+ 8px rings over sixteen directions in a fixed order — that clears every other label by 16px,
1016
+ every visible stroke sample by 10px and every shape painted over it, inside the box, at
1017
+ every frame the probe measured, camera scale included; it is written as a wrapping
1018
+ `<g transform="translate()">`. A label never moves farther than 0.8 of its own height
1019
+ (at least 32px): a collision that needs more is left to the critique round. On
1020
+ `end_dimmed`, the elements dimming parts the scene had lit are tweened back to full
1021
+ strength just before the end; on `camera_end`, the camera is tweened home. On `seek_order`,
1022
+ two tweens on one target animating one property over overlapping time — which a timeline
1023
+ resolves one way seeking forward and the other seeking back — are untangled in favour of
1024
+ the later one (the earlier is shortened to end where it starts, or loses that target or
1025
+ property when both start together; `src/bespoke/untangle.ts`). A repaired scene goes
1026
+ through the static contract and is gated again (at most two repair rounds).
1027
+
1028
+ **What the model is shown** (`src/bespoke/prompt.ts`): the beat, its cues, the paper's
1029
+ excerpts (fenced, untrusted), the contract, motion-design rules with numbers (one focal
1030
+ element per cue at 88-120px, the rest dimmed but kept; fill the box; paint it with filled
1031
+ shapes; at least three kinds of motion, one of flow, camera, counter or morph; the end
1032
+ frame a summary), text widths measured in the pack's own font, and two of four hand-made
1033
+ reference scenes (`src/bespoke/references.ts`: a routing mechanism with particle flow and
1034
+ a counter, a camera zoom into one block, a traced curve whose gap morphs into the headline
1035
+ number, a scatter whose dots travel into a bell, and — first, for a beat with an
1036
+ illustration — a scene built on its picture), picked for the beat's archetype and painted
1037
+ in the pack's colours. `test/bespoke-references.test.ts` runs each reference
1038
+ through every gate and the rubric probe in a real deck, so a reference that stops passing
1039
+ stops being one. Parts a cue introduces are `<g data-cue="N">` groups (semantic grouping,
1040
+ after Vector Prism, arXiv 2512.14336). MorphSVG is registered for a deck only when one of
1041
+ its scenes morphs.
1042
+
1043
+ **The rubric probe** (`rubricProbe`, free: it reads what the probe measured) sends a
1044
+ passing draft to the critique round when any of these is off: the drawing paints under 12%
1045
+ of its box at the end (round 1's scenes painted 4-16%), more than half its parts are still
1046
+ dimmed at the end, no label reaches 88px (not asked of an illustrated scene, whose picture
1047
+ is its focus and whose names are the shell's), fewer than three kinds of motion or none of
1048
+ flow/camera/counter/morph, an illustrated scene that holds fewer than two push-ins, does not
1049
+ open wide, or names fewer than two subjects on them, a cue whose picture changes by under
1050
+ 0.5% of the frame, or a warning about the scene. The critique round scores the frames against a six-line rubric
1051
+ (fills the stage, one focus per cue, motion explains, legible hierarchy, the pack, sync),
1052
+ is told the measures and every finding with the colliding labels' coordinates, and returns
1053
+ the fixed scene.
1054
+
1055
+ **Caps**, checked before every call: `--bespoke-calls` (default 12; two per beat) and
1056
+ `--bespoke-seconds` (default 1800). A quota or rate-limit answer stops every further call.
1057
+ Codex runs only through the account's own CLI (`--bespoke-cli`, `--bespoke-model`), never
1058
+ an API key, five calls in flight (`bespoke.concurrency` in the config file), at
1059
+ `bespoke.effort` reasoning (default `medium`; `default` keeps the account's), from the
1060
+ scratch directory, and with its tools off: shell, apps, plugins, browser, image
1061
+ generation (the illustration call alone keeps it), web search and every MCP server the
1062
+ account configured. A bespoke call is a
1063
+ text transform; with tools on, the agent spent its first turns reading the account's own
1064
+ instruction files and skills (measured: 56,679 input tokens over several turns and
1065
+ 135-488s for a draft that tool-less took one turn of 18,247 and 166s). A paced deck (`--speed`/`--duration` other than 1×) and non-16:9 formats are
1066
+ skipped, with a line saying so.
1067
+
1068
+ **Cache.** Keyed by everything a scene is drawn from — the beat's words and params, the
1069
+ quoted excerpts, the cue windows, the stops, the box, the pack, the prompt and contract
1070
+ versions, the model, the illustration's key — under `~/.cache/decksmith/bespoke`
1071
+ (`--bespoke-cache`); pictures under its `art/`, keyed by the beat's words, the pack's
1072
+ colours, the art prompt's version and the model. A rebuild costs no calls. A beat the gates rejected after its critique round is cached as rejected;
1073
+ a fallback the beat did not earn (cap, quota, timeout) is not.
1074
+
1075
+ **What a generated scene may run.** Three layers. (1) A static walk before anything is
1076
+ built or opened (`src/bespoke/contract.ts`, acorn): GSAP timeline calls with literal vars
1077
+ at explicit seconds, `gsap.set`, scoped `root.querySelector`, `Math` minus `random`, local
1078
+ code; `window`, `document`, `fetch`, `eval`, timers, storage, navigation, callbacks and
1079
+ function-valued tween vars are refused by name; CSS must be scoped to the scene with no
1080
+ at-rules, `url()` or animation; markup is SVG and inline HTML with no handlers, SMIL or
1081
+ external references, and `<image>` only as the beat's illustration (`data-art="1"`, no
1082
+ href — the shell writes it); path data must be numbers and commands (a word in a `d`
1083
+ attribute is a console error that used to fail every scene probed with it). (2) A CSP `<meta>` in every composition that carries one —
1084
+ `connect-src 'none'`, no `unsafe-eval`, local files only — and a probe that fails a scene
1085
+ on any page error, request or navigation (an error that names no scene fails every scene
1086
+ probed with it, unless the value it quotes is in exactly one candidate, which is then the
1087
+ one failed). (3) The player's frame is sandboxed
1088
+ (`allow-scripts allow-same-origin allow-downloads`). The paper's text reaches the model
1089
+ fenced as untrusted data. The static walk is not a proof against obfuscation — a
1090
+ property name assembled from parts at run time gets past it — which is what (2) and (3)
1091
+ are for; and (3) isolates only when decks are served from another origin than the host.
1092
+
1093
+ **The motion gates** (`src/verify/scenes.ts`) run on every scene `bespoke.json` lists, as
1094
+ errors: `static_hold` (the picture must change during every cue), `graphic_crosses_text`
1095
+ (a visible stroke through a label — a curve whose bounding box contains a label is not
1096
+ its plate — or a shape painted over one), `seek_order` (the frame must not depend on what
1097
+ was seeked before it, beyond 1500px of rasterising noise — counted on changed AREAS: a pixel
1098
+ counts when it and its 8 neighbours changed, so a picture re-rasterised a hair differently
1099
+ on its first paint, an outline round every subject, 12,698px raw, counts 309), `stage_fill` (the settled
1100
+ drawing's bounding box covers 80% of its box), `type_hierarchy` (one label of 64px or
1101
+ more at the end; not asked of an illustrated scene), `stray_marker` (an SVG marker painted where its line is not drawn),
1102
+ `early_reveal` (a `data-cue="N"` group showing more than 0.5s before cue N),
1103
+ `shot_variety` (an illustrated scene opens on the whole picture and the camera — sampled
1104
+ every 0.5s, no screenshot — HOLDS push-ins at 1.5x or closer on at least two different
1105
+ subjects; round 3's 1.1-1.4x pans are not push-ins), `label_anchor` (a label that names a
1106
+ subject sits within 96px of its box and covers no other subject by more than a quarter of
1107
+ itself, and at the end at least two subjects are named that way), `data_over_picture`
1108
+ (five or more numbers on the illustration),
1109
+ `cue_groups` (no such groups, or one naming a cue the scene does not have), `end_dimmed`
1110
+ (at the settled frame, more than 10% of the parts the scene had shown lit are left under
1111
+ 0.6 opacity — a part drawn translucent from the start is not counted) and `camera_end` (the
1112
+ camera, or a viewBox, not home at the settled frame). Content
1113
+ outside a clipping `<svg>` — a camera zoomed in — is neither off the frame nor crossing
1114
+ anything. Overlap and crossing findings carry the labels' boxes in the body box's px.
1115
+ `seek_order` also runs on every other scene of a v2 deck, as a warning; it is what found
1116
+ the equation-walk bug fixed alongside this (22,037px before, 0 after). Below the tolerance
1117
+ and not traced: v2's equation-walk on the narrated demo differs by 529px on one KaTeX
1118
+ glyph when reached from past the scene's end.
1119
+
1120
+ `build` writes `bespoke.json` beside the deck: what was drawn and from where (cache,
1121
+ draft, critique), why each fallback, calls, tokens, seconds, the stages' wall clock, each
1122
+ picture's inspection (subjects, flatness, writing, draws) and each scene's staging
1123
+ measures. The full motion probe that `verify` would run again on a generated scene is
1124
+ skipped by `build` when the pass already ran it on the same bytes at the same scene id
1125
+ (`gatedAt`); `build` still seeks it with the deck's other scenes. `decksmith verify <dir>`
1126
+ always runs it. Set
1127
+ `DECKSMITH_BESPOKE_WORK=<dir>` to keep the prompts, replies and contact sheets.
1128
+
1129
+ **Costs** (measured on four HypePaper decks, twice each, see
1130
+ [`.planning/2026-10-09-v2-bespoke-round4.md`](.planning/2026-10-09-v2-bespoke-round4.md)):
1131
+ an illustration takes 60-120s, a draft 90-220s and a critique 70-120s; a deck's pass took
1132
+ 380-730s for 5-8 scene calls plus 3-7 pictures (redraws included) and 153-227k tokens, and
1133
+ the whole `build` 466-812s on a Mac with 9-23% memory free — three builds of eight under ten
1134
+ minutes. The pictures add 128-348 KB of WebP to a deck (round 3: 6-7 MB of PNG). Round 3
1135
+ took 593-742s and 214-325k tokens; round 1 13-31 minutes and 540-705k. A rebuild from the
1136
+ cache makes 0 calls. None of that fits a 300-second build timeout: a host running this has
1137
+ to give `build` that long, or run it off the request path.
1138
+
927
1139
  ## Themes
928
1140
 
929
1141
  Three, each a position rather than a hue.