allyhub 0.1.0-alpha.5 → 0.1.0-alpha.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +29 -564
  2. package/README.md +1 -1
  3. package/dist/commands/dev.js +1 -1
  4. package/dist/commands/dev.js.map +1 -1
  5. package/dist/commands/index-media.js +2 -2
  6. package/dist/commands/index-media.js.map +1 -1
  7. package/dist/commands/sheets.d.ts.map +1 -1
  8. package/dist/commands/sheets.js +2 -1
  9. package/dist/commands/sheets.js.map +1 -1
  10. package/dist/commands/skill.js +1 -1
  11. package/dist/commands/skill.js.map +1 -1
  12. package/dist/commands/sync.js +1 -1
  13. package/dist/commands/sync.js.map +1 -1
  14. package/dist/commands/transcribe.js +1 -1
  15. package/dist/commands/transcribe.js.map +1 -1
  16. package/dist/commands/transcript.js +2 -2
  17. package/dist/commands/transcript.js.map +1 -1
  18. package/dist/dev/agent.d.ts +23 -4
  19. package/dist/dev/agent.d.ts.map +1 -1
  20. package/dist/dev/agent.js +58 -5
  21. package/dist/dev/agent.js.map +1 -1
  22. package/dist/dev/index.d.ts.map +1 -1
  23. package/dist/dev/index.js +3 -1
  24. package/dist/dev/index.js.map +1 -1
  25. package/dist/dev/server.d.ts +10 -0
  26. package/dist/dev/server.d.ts.map +1 -1
  27. package/dist/dev/server.js +17 -5
  28. package/dist/dev/server.js.map +1 -1
  29. package/dist/dev/sheet-routes.d.ts.map +1 -1
  30. package/dist/dev/sheet-routes.js +2 -1
  31. package/dist/dev/sheet-routes.js.map +1 -1
  32. package/dist/excel.d.ts +5 -0
  33. package/dist/excel.d.ts.map +1 -0
  34. package/dist/excel.js +41 -0
  35. package/dist/excel.js.map +1 -0
  36. package/dist/main.d.ts.map +1 -1
  37. package/dist/main.js +10 -0
  38. package/dist/main.js.map +1 -1
  39. package/dist/sheet-import.d.ts.map +1 -1
  40. package/dist/sheet-import.js +2 -1
  41. package/dist/sheet-import.js.map +1 -1
  42. package/dist/workspace.d.ts +9 -0
  43. package/dist/workspace.d.ts.map +1 -1
  44. package/dist/workspace.js +19 -0
  45. package/dist/workspace.js.map +1 -1
  46. package/editor-web/assets/index-D7HxvIEO.js +424 -0
  47. package/editor-web/index.html +1 -1
  48. package/package.json +13 -12
  49. package/skill/allyhub/SKILL.md +7 -6
  50. package/skill/allyhub/references/web-products.md +1 -1
  51. package/editor-web/assets/index-CaR18p31.js +0 -424
package/CHANGELOG.md CHANGED
@@ -1,582 +1,47 @@
1
1
  # Changelog
2
2
 
3
- The AllyHub packages (`@allyhub/sdk`, `@allyhub/runtime`, `allyhub` and the packages they depend on) share one version number and are released together. They are written for the people and agents who use AllyHub, and say only what you need to know about.
3
+ Feature highlights from each AllyHub release.
4
4
 
5
- ## 0.1.0-alpha.5
6
-
7
- - **Available from npm and the skills CLI.** Run `npx allyhub help` for the
8
- command and `npx skills add allyhub-ai/skills -g` to install its skill.
9
- Requires Node.js 22.19 or newer.
10
- - **Studio and the editors in 15 languages:** English, Simplified and
11
- Traditional Chinese, Japanese, Korean, Spanish, French, German, Portuguese,
12
- Italian, Russian, Hindi, Thai, Vietnamese and Turkish.
13
- - Studio starts without waiting for the macOS keychain; export messages say
14
- what was saved, and a file is marked as newly made only when it is new.
15
-
16
- ## 0.1.0-alpha.4
17
-
18
- ### AllyHub Studio
5
+ ## 0.1.0-alpha.7
19
6
 
20
- - **A new Home.** One question, "What are we working on?", over the composer a project starts from; a pill per kind of project (Research, Writing, Video) that opens to its templates; and the last four projects opened. Picking a template writes its starter brief in the composer, with its blanks as fields (Tab moves between them, ⌘Enter sends). Files dropped on Home go into the new project with the brief.
21
- - **Several projects work at once, each with its own agent.** Start a task in one project, open another and start one there: both agents work at the same time, each with its own permissions and Stop. Project tiles and the sidebar say what each project's agent is doing ("Claude is working", "Waiting for your approval"). A project left in the background keeps going while its agent works; closing the window leaves the agents working. A turn that fails says why in the conversation.
22
- - **Studio has a browser of its own.** The pages the agent's scripts open are real pages in Studio that you can click and type in, and a site you sign in to there is signed in for every project's agent. Open it from the project's Browser button. Settings → Browser lists the sites, with Remove.
23
- - **See what the agent reads on the web.** While the agent researches, the card shows the page its browser is on, live, with its address, or the pages it fetched and the searches it made, and the tag says where it is ("Claude · browsing stripe.com").
24
- - **Studio's card follows what the agent touches.** Each change the agent makes puts what it changed on show at once: the doc, sheet, video or slides it writes. A doc the agent changed while it was closed is marked when it opens, with Undo and Keep.
25
- - **The composer has one button while the agent works: Send or Stop.** Enter sends a message into the agent's turn, marked "Sent while Claude worked"; Tab queues it as a row of its own over the composer (↑ takes the last back, × drops one), and queued messages go one per turn, in order. Esc stops the turn and puts the queued messages back in the composer.
26
- - **A project's composer takes typing at once**, whether the project was just made or opened again; its preview starts after, in the background.
27
- - **Sheets: one bar over the grid.** A cell's whole value is in its own editor, which grows to fit; what another hand changed is a pill at the grid's foot. Number columns show one number of decimals down their length; frozen columns cast a shadow while the grid scrolls.
28
- - **Attachment cells.** A `file` column holds one file or several per cell: pictures are drawn in the cell, other files by name, and a viewer shows them whole. Drop, paste or choose files into a cell; they are kept in the sheet's own `files/` folder. Set `height` in sheet.yaml for larger pictures.
29
- - **Docs as a first-time user expects.** The `/` menu's hints say what makes each block; a new doc opens with the cursor in it; the comment box opens beside what it is about; Delete offers Undo; a doc reached by a link has a way back. PDFs are set in the editor's faces.
30
- - **AllyHub's command and skill on this computer, with no password.** Studio keeps its `allyhub` in `~/.allyhub/bin` and every agent runs it by that full path, inside Studio and out, so a shell that drops Studio's folder from PATH no longer loses it (`allyhub: command not found`). On every start Studio links the skill for Claude Code (`~/.claude/skills`) and Codex (`~/.agents/skills`, with the rule that lets the command run outside Codex's sandbox). `allyhub` typed in a terminal is optional: Settings → General → Terminal command installs it, with your Mac's password where needed.
31
- - **AllyHub's own agent no longer asks before every step.** It starts in Auto, as Claude and Codex do: it acts in the project without asking, and asks before it changes a file outside it.
32
- - A connection that drops and comes back no longer leaves a "Connected to Codex." line each time.
7
+ - **Comments in Codex.** Comment on the preview with Codex's own Annotate; each comment carries the moment in the video, the sheet cell or the doc's words.
8
+ - **Codex setup in one sentence.** Ask Codex to "Read github.com/allyhub-ai/skills to install AllyHub for Codex".
9
+ - **Lighter install.** The first `npx allyhub` downloads 120 MB instead of 262 MB; Excel support installs when first used.
10
+ - **Queue and steer.** In Studio, Enter queues a message while the agent works, and Steer sends a queued message now.
33
11
 
34
- ### Tools
12
+ ## 0.1.0-alpha.6
35
13
 
36
- - **`allyhub handoff "<what to do>"`** asks the person to act in Studio's browser (sign in, enter a code) and returns at once; scripts are kept out of the browser until the person answers, and the answer comes back as the agent's next message.
37
- - **`allyhub run <script> --message "…"`** (and `allyhub capture --message`) puts a line over the live page in Studio; `message()` from `allyhub/browser` changes it as the script goes.
38
- - In Studio, `chromium.launch()` (from `allyhub/browser` or plain Playwright) and `allyhub capture` drive Studio's own browser.
39
- - A sheet's check reports each file a cell names that is not in the project.
40
- - `allyhub skill install --agent codex --global` puts the skill in `~/.agents/skills`, where the skills CLI and Studio put it (it was `~/.codex/skills`). Install the skill with `npx skills add allyhub-ai/skills -g`.
41
- - The skill tells agents to run Studio's command by its full path (`/Users/<you>/.allyhub/bin/allyhub`), else `allyhub` on PATH, else `npx -y allyhub`.
14
+ - **Simple installation.** Run the CLI with `npx allyhub` and install its skill with `npx skills add allyhub-ai/skills -g`.
15
+ - **Short release notes.** Each release lists a few feature highlights.
42
16
 
43
- ## 0.1.0-alpha.3
44
-
45
- ### Tools
17
+ ## 0.1.0-alpha.5
46
18
 
47
- - `allyhub find` reads the catalog and asks AllyHub's search together, on one connection. Signed out, its reason says the task's words were matched and that signing in (where the host signs in: `allyhub login`, or where Studio offers it in the conversation) lets AllyHub's model read the task.
48
- - **A cloud tool called in Studio goes straight through.** It returns its result once the tool is done, with the files it made downloaded into the project, each with its record beside it. Studio no longer saves the calls, recovers them in the background or shows a Cloud tool jobs panel, and its agents have no `models_wait` or `models_jobs`.
49
- - The skill, the MCP servers' descriptions and the templates no longer tell the agent to price a paid tool first and wait for a yes; `*_describe` and `*_price` are there for it to use when it chooses.
50
- - **`allyhub upload <file> [more…]`** puts files from this computer in your account's Files on AllyHub (never public) and prints a signed URL for each, for a cloud tool whose input takes a URL: a presenter photo and a voice-over for a talking-presenter video, a reference image or clip. The URL works for six hours. Several files go at once, all checked before any is sent. The same file again uses the copy the account has and, while it has two hours left, the same URL, so a call made again is the same input. A call's record keeps an uploaded file by its path, sha256 and size, never its URL, and leaves out any signed URL's signature. Over MCP it is the `upload` tool.
51
- - **A tool input the tool refuses says which fields and why** (`bad_input`), where it used to fail without saying.
19
+ - **15 languages.** Studio and the video, doc and sheet editors are available in 15 languages.
20
+ - **Faster startup.** Studio opens without waiting for the macOS keychain.
21
+ - **Clearer exports.** Export messages identify what was saved, and newly created files are marked accurately.
52
22
 
53
- ### Breaking changes
23
+ ## 0.1.0-alpha.4
54
24
 
55
- - **Studio's agents have no `models_wait` or `models_jobs`**: a cloud tool called in Studio returns its result once it is done (above).
25
+ - **Parallel projects.** Work on several projects at once, each with its own agent.
26
+ - **Built-in browser.** Browse, sign in and watch the agent research inside Studio.
27
+ - **New Home and composer.** Start from templates, steer work in progress and queue messages.
28
+ - **Better docs and sheets.** Easier editing, attachment cells and review of agent changes.
56
29
 
57
- ### AllyHub Studio
30
+ ## 0.1.0-alpha.3
58
31
 
59
- - **AllyHub Studio asks for a sign-in only when a message needs one, and in place.** Signed out, a new conversation starts with the person's own agent (Claude, else Codex); AllyHub's own agent, cloud tools, credits and the server's tool search need the AllyHub account. A message sent to an agent that is not signed in (or whose sign-in ended, or whose connection failed) stays in the conversation, not sent, and a control in place of the answer takes the person through the whole sign-in: Sign in (a button per method an agent offers), Waiting for the browser… or Connecting… with Cancel, why it failed or that it was cancelled with Sign in again (or Reconnect), then a "Connected to Claude." line, and the message goes, once. A message left by a sign-in that was cancelled, or by a switch to another agent, keeps a Send of its own. The agent picker's sign-in rows are gone, and no message points to Settings. Notices come in at the top right, inside the sheet, not over the composer.
60
- - Studio brings FFmpeg 9.0.2 (it brought 6.1.1), the version AllyHub's own frame tests render with.
32
+ - **Sign in when needed.** Studio handles agent sign-in within the conversation and keeps unsent messages.
33
+ - **Simpler cloud tools.** Calls return their results and download generated files directly into the project.
34
+ - **File uploads.** Use your own media with cloud generation and research tools.
61
35
 
62
36
  ## 0.1.0-alpha.2
63
37
 
64
- A new model for talking-head videos, mix-and-match ad batches and ads replicated from a reference, and a new stance: AllyHub gives the agent tools and measurements and leaves every editorial choice to it. Almost every API changed; there are no compatibility aliases. "Breaking changes" below lists what to rename; the sections after it describe what is there now.
65
-
66
- ### Fonts
67
-
68
- - **The project's fonts are on every stage.** Every `.css` file under `assets/fonts/` (what `allyhub font add` writes) is loaded by every video and deck, in the preview, the check, stills and the render: `look.font` and any `font-family` name its families with no piece importing it. A piece that imports it still works. The stage loads only the faces its text needs before drawing, and a changed font file renders again. `fonts: { exclude: [...] }` in `allyhub.config.ts` leaves one out. `allyhub font add` no longer prints an import line.
69
-
70
- ### Motion and layout helpers
71
-
72
- - **Text fitted to a number of lines.** `fitLines(el, { lines, min, max, width?, height?, precision? })` finds the largest font size at which a text takes at most n lines of a box's width (and, given one, its height), measured in the page, and returns it with each line's text and whether even `min` fits; `fitLines(text, { …, width, style })` measures a text with a style without an element of yours. `textLines(el)` is the text of each line as the browser broke it. In `@allyhub/sdk/dom` and `@allyhub/sdk`.
73
- - **Simplex noise.** `noise2D(seed, x, y)`, `noise3D` and `noise4D`: smooth values in [-1, 1], the same for the same seed on every frame and run, for drift, a handheld camera or a wobble.
74
- - **SVG paths.** `pathLength(d)`, `pointAtLength(d, length)` and `tangentAtLength(d, length)` measure a path without the browser, as Chrome does; `drawOn(d, progress)` gives the stroke-dash values that draw it on; `trimPath(d, from, to)` is the stretch between two fractions of its length; `interpolatePath(a, b, x)` morphs between two paths with the same commands (`normalizePath` writes a path as the absolute commands it compares).
75
- - `css(el, style)` takes an SVG element too.
76
-
77
- ### Tools
78
-
79
- - The image catalog supports GPT Image 2.5 Sunburst and Flare, and Nano Banana 2.1. Grok Imagine Image 2.0 is unchanged.
80
- - **`allyhub find "<task>"` finds what does a task, in your own words.** It ranks the CLI's own commands (free, on this computer) with the cloud's AI models and research tools (with their price). Signed in, AllyHub reads the task for what it needs ("a voiceover" finds the speech models; free); signed out, the task's words are matched, and the answer says so. `allyhub find` alone says what each area holds; `allyhub find --all` writes everything, a line each, to a file and prints its path. Over MCP it is the `find` tool.
81
- - **`allyhub help` lists the commands by area**: find, content, AI models, research and setup, with where each runs and what it costs. A command with several forms (`frames`, `transcribe`) shows its arguments correctly in the list.
82
- - **`allyhub models`** lists the AI models on this computer (speech to text, faces and the person in a video, reading reference videos), installed or not and their size, and the cloud's with prices; `allyhub models install <pack>` downloads one ahead and `allyhub models remove <pack>` takes it away.
83
-
84
- ### Breaking changes
85
-
86
- - **The command line is grouped by area, and some commands have new names.** There are no aliases.
87
-
88
- | Was | Is now |
89
- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
90
- | `allyhub tools search`, `tools list` | `allyhub find "<task>"` |
91
- | `allyhub tools catalog` | `allyhub find --all` |
92
- | `allyhub tools describe`, `price`, `call` (an AI model) | `allyhub models describe`, `price`, `call` (images, voice, video, music, transcription) |
93
- | `allyhub tools describe`, `price`, `call` (a data tool) | `allyhub research describe`, `price`, `call` |
94
- | `allyhub tools wait` | `allyhub models wait` |
95
- | `allyhub sheet` (the stills of a video on one sheet) | `allyhub stills` (`allyhub sheet import` and `sheet profile` are the spreadsheet commands) |
96
- | `allyhub mix`, `allyhub batch manifest` | `allyhub combos`, `allyhub combos manifest` |
97
- | `allyhub asset capture`, `allyhub asset compare` | `allyhub capture`, `allyhub compare-image` |
98
- | MCP `cloud_tools_search` | `find` |
99
- | MCP `cloud_tools_describe`, `_price`, `_call` | `models_describe`, `_price`, `_call` and `research_describe`, `_price`, `_call` |
100
- | MCP `cloud_tools_wait`, `cloud_tools_jobs` (Studio) | `models_wait`, `models_jobs` |
101
- - A tool used under the other area's name is refused, with the name to use.
102
-
103
- - **A project's folders have new names, by what a file is.** Nothing is read from the old places, and there is no command that moves them: move the folders and fix the relative imports.
104
-
105
- | Was | Is now |
106
- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
107
- | `app/videos/`, `app/decks/` | `content/` (what is made: videos, decks, docs, loose files) |
108
- | `media/` | `assets/media/` |
109
- | `media/fonts/` (`allyhub font add`) | `assets/fonts/` |
110
- | `data/` | `assets/data/` |
111
- | `messages/` | `assets/text/` |
112
- | `.allyhub/renders/` | `.local/out/` |
113
- | `.allyhub/cuts/`, `.allyhub/compare/`, `.allyhub/agent/`, `.allyhub/tools/`, `reference/` | `.local/work/cuts/`, `compare/`, `notes/`, `tools/`, `reference/` |
114
- | the rest of `.allyhub/` (builds, the media index, the part cache, types) | `.local/cache/` |
115
- - `.local/` is this computer's own: add the line `.local/` to the project's `.gitignore`. `~/.allyhub` in the home folder (models, runtimes) keeps its name.
116
- - A workspace is recognised by `allyhub.config.ts` or `content/`.
117
-
118
- - **A piece lives in an item folder.**
119
- - Each video or deck is a folder under `content/` whose entry file is named for its kind: `content/<name>/video.ts` or `content/<name>/slides.ts`. Move each piece into a folder of its own, with what only it uses, and fix its relative imports.
120
- - A piece anywhere else is AK-M002. A `video.ts` with a `paged` output, or a `slides.ts` with a `video` one, is AK-M003: give each kind an item of its own.
121
- - A piece without an `id` is named by its folder under `content/` (`launch/film`), not by its file's path.
122
- - `allyhub add` copies into `components/` (was `app/components/`).
123
- - **An item's words are in its script.** `script.<lang>.yaml` in the item's folder replaces the item's keys in `messages/<lang>.json`; text several items share is in `assets/text/<lang>.json`. A part's spoken line is its row's `say`, read as `<id>.<part>.say`.
124
- - **`eyebrow`, `rise` and `pop` are gone from `@allyhub/sdk/dom`.** They were a look (an uppercase accent label, a fade-and-rise entrance, a scale pop), and a look AllyHub provides becomes every video's look. Write your own with `ease` and `progress` from `@allyhub/sdk`, or copy the old ones into the workspace: the example keeps them in `examples/launch/components/motion.ts`.
125
- - **Formats replace sizes.** `definePiece({ formats })` replaces the `size` dimension, `defaults.formats` replaces `defaults.sizes`, `paged(…, { format })` replaces `size`, and `ctx.format` replaces `ctx.size`.
126
- - **The matrix and combos replace variants.**
127
- - `matrix: { dims }` replaces `variants`, and `ctx.combo` replaces `ctx.variant`.
128
- - `--combo` and `--format` replace `--variant`.
129
- - `render --type` replaces `render --format` for file types.
130
- - Renders are named `<output>.<combo key or id>.<format>.<ext>`.
131
- - **Parts of speech and layers replace voice-overs and full-frame components.**
132
- - Removed: `video(…, { voice, prefix })`, `VoiceTake`, `ctx.voice`/`voice.at`, `silent`, `.vo`/`.title` message keys, `ClipSpec`, `clips`/`ctx.clips`, the `clip` component and `music.followVoice`.
133
- - `defineComponent` returns a graphic layer maker. `at`, `until`, `dur` and `z` are reserved; components have no `dur` or `still` of their own.
134
- - **Nothing sounds, loops, snaps or places itself.**
135
- - Components no longer emit sound effects (`cues`, `video({ sfx })` are gone); a part places them.
136
- - Music plays once unless `music.loop`.
137
- - Beat snapping needs `music.snap`.
138
- - `music.duckDb` is now `mix.duck.db`.
139
- - `video({ captions })`, `part({ captions })`, `emphasis: "auto"` and automatic caption and lower-third placement are gone.
140
- - **Checks report only objective errors.** AK-C001, AK-F002, AK-L005, AK-L007, AK-A004, AK-X003, AK-X007 and AK-L008 are now facts read with `allyhub inspect`. AK-V002, AK-V003 and AK-V005 are gone with voice-overs.
141
- - **Data shapes:**
142
- - `allyhub transcript --json` gives `words` as a list and `count`.
143
- - `footage-scale` facts give `min` and `max`.
144
- - `allyhub compare --json` gives `summary` (was `score`).
145
- - faces.json and matte.json are version 2 and are made again.
146
- - `reference.json` files made earlier must be made again.
147
- - `reference.json` is schema 2: measurements only. Role guesses, picture types and caption or watermark labels are gone; the agent writes `interpretation.json`.
148
- - **Host interface:**
149
- - `Timeline.footage` replaces `clips`, and `audio.speech` replaces `audio.voice`.
150
- - `Part.layers`, `speech` and `planes` replace `component`, `silent` and `sentences`. `Part.planes` are the part's graphics planes, back to front, and `TimelineFootage.over` counts the planes under a footage layer.
151
- - `PartText.spoken`, `speak` and `speechKey` replace `vo`, `voSpeak` and `voKey`.
152
- - `BuildSource.frames`, `FRAMES_PATH` and `/__frames/` are gone.
153
- - **Engine names:**
154
- - `Combo`, `Dims`, `comboKey`, `parseComboKey`, `expandCombos` and `comboMatches` replace the `Variant*` names.
155
- - `Format`, `VideoFormat`, `Preset` and `PRESETS` replace `FrameSize`, `VideoSize`, `Size` and `SIZES`.
156
- - `formatInfo` and `presetInfo` replace `frameInfo` and `sizeInfo`, and `FileType` replaces `RenderFormat`.
157
- - **Components move out of the SDK.**
158
- - `@allyhub/sdk/components` is gone; `allyhub add` copies starter components into the workspace. Importing the old path is AK-B006.
159
- - **The starters are mechanisms, not layouts.** They are now `screenDemo` (with `uiDemo`), `codePanel`, `callout`, `chart` and three caption examples, `captionWord`, `captionLine` and `captionSubtitle`.
160
- - **`screenDemo` and `uiDemo` replace `siteStep`.** A demo played on a screen, as a list of steps: `zoom` takes the camera in on what is acted on, which comes forward, a little larger and with a shadow under it, while the rest of the screen stays as it is; `type` clicks into a field and types, each letter rising into place; `click` presses a thing, which dips, lights from inside and springs back as a line leaves its outline; `highlight` brings places forward without the camera; `show` swaps in another state. `uiDemo` plays it on an interface the piece draws in DOM (sharp at any zoom, typed in the interface's own face, its elements found by selector); `screenDemo` on a screenshot whose named elements are cut out to move. Either draws the window and nothing round it; every value is an option, listed in `references/components/screen-demo.md`.
161
- - **Removed: the rest of the screen blurred and faded** (`depth.blur`, `depth.fade`, `depth.wash`). Against a soft, pale page every place where what comes forward missed the element's own edge and corners showed, and a screenshot's cut-out rarely matches them exactly. A piece that still passes one of them throws, naming it: take it out. With nothing veiling the page, a piece cut out of a screenshot comes forward from its own centre, not its group's, so the image's own copy never shows beside it.
162
- - **`codePanel`**, new: plain text as a panel of code, JSON or a terminal. Tokens coloured (JSON, JS/TS, Python, shell, HTTP), typed letter by letter or revealed by lines, paced to end at a moment; steps that band a range of lines or bring a span forward as a chip (found by its text, a key or a key's value) and scroll a long file; a terminal with commands at their cues. A highlighted span is an element another layer can point at.
163
- - **`callout`**, new: marks places (an outline that travels round, a highlight band, brackets), labels them on a side with room, and draws lines between them, in the direction you give. A place is a box or a selector for another layer's element, which the mark follows; a group goes quiet when the next takes over.
164
- - **`chart`** is rebuilt: bars, horizontal bars, a line with its area, and a ring for a share of a whole. Its scale includes zero for bars and steps by 1, 2, 2.5 or 5; labels are fitted by a stated rule and never rotated; values count up as the marks grow from `growAt`, and one highlighted mark comes forward at `highlightAt` with an optional `annotation`. `kind`, `data`, `title` and `highlight` are as before; a copy already in a workspace is the old chart and stays as it is.
165
- - `allyhub asset capture --scale 2` draws a page as a high-density screen does, so the camera can go in and stay sharp. The element boxes are in the image's pixels.
166
- - Gone: `titleCard`, `statement`, `bulletList`, `bigNumber`, `compareCards`, `priceCard`, `quoteCard`, `timeline`, `endCard` and `lowerThird`. Each was a layout and nothing more, and a layout given by default becomes every video's look. Write them in the piece with `custom()` and the DOM helpers.
167
- - A copy already in a workspace stays: it is the workspace's own file, and nothing replaces it.
168
- - `countText(value, x, from?)` (`@allyhub/sdk`) is what `bigNumber` did that was worth keeping: a number counting to its value as text, with its prefix, suffix, decimals and thousands separators.
169
- - **The cloud commands use AllyHub's production by default** (`https://api.allyhub.com`, `https://allyhub.com`) instead of its CI environment. `ALLYHUB_API_URL` and `ALLYHUB_WEB_URL` still point them at another AllyHub; `allyhub doctor` says which one is in use.
170
-
171
- ### The project folder
172
-
173
- - **What a file is decides its folder.**
174
-
175
- ```
176
- content/ what is made for the person: videos, decks, docs, and files left loose beside them
177
- assets/ files used as they are: assets/media/, fonts/, assets/data/, text/ (words several items share)
178
- components/ code several items share
179
- apps/ web apps, each a project of its own (reserved: nothing there is built or checked)
180
- agent/ the agent's own: memory.md
181
- .local/ this computer only, never shared: work/, out/, cache/, trash/
182
- ```
183
-
184
- Made for the person and needed in the end goes in `content/`; a file reused as it is, in `assets/`; code reused, in `components/`; what was learned and should be known next time, in `agent/memory.md`; anything that can be thrown away, in `.local/work/`.
185
-
186
- - **Items.** Each video, deck, doc or sheet is a folder under `content/` with its entry file (`video.ts`, `slides.ts`, `doc.mdx`, `sheet.csv`) and everything only it uses. Folders above an item only group items; their names and depth are the project's own, and the person sees them as a tree. What several items share stays outside `content/` (`components/`, `assets/`).
187
- - **Loose files.** A file under `content/` that is in no item's folder is something the person asked for that is not a video, a deck or a doc (a CSV, a PDF, a picture). It is shown as a file and opened as it is.
188
- - **Only `content/`, `assets/` and `components/` are built, checked and watched.** `agent/`, `apps/` and `.local/` never are: a piece or a broken file there is not the project's.
189
- - **`.local/`** holds everything that is this computer's own, so one line in `.gitignore` keeps it out of what is shared: `.local/work/` is the agent's working folder (experiments, downloaded references, the review sheets of `allyhub frames --cuts` and `allyhub compare`, feedback screenshots, tool results without a place), `.local/out/` the renders, `.local/cache/` what the tools rebuild, and `.local/trash/` the recycle bin.
190
- - **`agent/memory.md`** is the project's memory: what whoever works on it has learned, a line at a time. A new video project's `AGENTS.md` says how to keep it: read it first and add to it as you learn, one line each at its end (the date and time, then one sentence). It is the project's memory, for any agent that works there, not an agent's own.
191
- - **`AGENTS.md`** holds a project's standing instructions: where things go, its brand, the decisions that stand. The skill tells the agent to read it first and to keep lasting preferences there, once the person agrees.
192
- - **`allyhub dev` shows the folders as they are and changes the content for a person.** `/api/files` lists any folder of the project, `/api/files/raw/…` serves a file as it is (nothing in it can run, and nothing outside the project is reached) and `/api/files/text` gives a text file's words. `/api/content/folder`, `/api/content/move` and `/api/content/remove` make a folder under `content/`, move or rename a folder, an item or a loose file, and take one out. The project's view has the content's folders (empty ones too) and its loose files.
193
- - **The recycle bin.** What a person deletes is moved to `.local/trash/` with a record of where it was: `/api/trash` lists it, `/api/trash/restore` puts a thing back where it was, and `/api/trash/empty` deletes one thing, or everything, for good.
194
- - **AllyHub Studio** shows a project's content as a tree down the left: its folders, its videos, decks and docs (an item is one row, never its files) and its loose files, which open as they are in place of the picture. A row is renamed, deleted or dragged into a folder. + makes a blank video, doc or folder, in the content or in a folder. Under the tree are the project's **Assets** (everything in `assets/`, a folder at a time with breadcrumbs: pictures as thumbnails, videos by a frame near their start, what a tool made marked by its record) and its **Agent** (what the agent keeps, `agent/memory.md` first, and its working files). What a person deletes is kept in the project's `.local/trash/`; Studio does not show a recycle bin for now. Every project is one kind of thing, and the sidebar is Home and Projects (there is no Chat: a conversation belongs to a project). A new project starts from a template, Videos, Articles, Research or Blank, as a code project starts from its boilerplate: `content/`, `assets/media/`, `components/`, `agent/memory.md`, `.local/work/`, a `.gitignore`, `allyhub.project.json` (which project it is and its template, shared with it), an `AGENTS.md` with where things go and what the project is for, a `CLAUDE.md` that brings it in for Claude Code, and the template's own files (an Articles project's style guide, a Research project's brief).
195
- - **The browser editor's home** lists the content's loose files under the docs; each opens as it is.
196
-
197
- ### Docs
198
-
199
- - **A doc is a third kind of item**, beside a video and slides: one file, `content/<name>/doc.mdx`, that a person reads and edits as a page and an agent writes as text. It is for what an agent writes for a person: a plan, a brief, a report. It is not a piece: no `definePiece`, no output, format, language or combo.
200
- - **The file is MDX**: Markdown for prose (headings, lists and tasks, quotes, tables, code, pictures), and a few tags for what Markdown cannot say: `<u>`, `<span color bg>`, `<Block align color bg toggle>` for one block's look and the blocks folded under it, `<br />` for an empty line. `<img>`, `<Table>` and `<Callout>` are below. Any other tag on lines of its own is kept as written and shown as a named card; it is not drawn yet.
201
- - **The editor** (AllyHub Studio's project tree, and a page of its own in the browser editor, `?doc=<its place>`) is a block editor: a menu on `/`, drag handles, a toolbar on selected words, colours, alignment, toggles, nested blocks, tables, pictures dropped in. What is typed is saved to the file half a second later.
202
- - **A person's edit rewrites only the blocks that changed.** The rest of the file keeps its bytes. Where Markdown's own mark would not read back (bold around Chinese punctuation), the editor writes a tag. The spaces a line ends with are not written.
203
- - **The title is a field of its own over the doc**: the first-level heading the file opens with. It cannot be dragged away or turned into a list; Enter and Down go into the doc, Up and Backspace from the first line come back. A doc without that heading has an empty title, written in when typed. The project's lists show a doc by its title, and renaming a doc in AllyHub Studio's tree changes its title and, as far as a folder can say it, its folder.
204
- - **It looks like AllyHub, not like its editor library.** The doc is a sheet on the paper with a bar of its own (its name, whether it is saved, Comments, Export). Text is in the product's type and ink; the title is in the serif names are set in, and no heading is smaller than the text under it. The menu on `/` and the toolbar have one line per row: a block's name and what to type for it in Markdown. A block's handle is level with its first line at every heading size. A long doc lists its headings down its right side. Toolbars, the emoji picker, a table's handles and a link's bar are in the same look; a narrow sheet keeps room for the handle.
205
- - **What a doc keeps that Markdown cannot say**: the width a picture was dragged to and where it sits (`<img … width align />`), a table's column widths and whether its first row, its first column, both or neither are its head (`<Table widths head>`), a numbered list that starts again or at another number, and a callout: a line set apart in a box with a sign before it and the blocks under it inside (`<Callout icon color bg>`; `/callout`, and the sign changes when clicked).
206
- - **The doc under the hand.** A click on empty space puts the cursor in the line beside it, or at the end of the doc; a drag over empty space takes every block it passes. `> ` and a space makes a toggle, and a toggle is found open where it was left. Esc takes the block the cursor is in, ⌘D copies it, ⌘⇧↑ and ⌘⇧↓ move it, ⌘/ opens everything that can be done to it, found by typing. A block's handle turns it into another kind; `/` also turns and colours a block by name (`/turn`, `/red`). The cursor is where it was put, also when a key follows a click at once.
207
- - **A person and an agent can work on a doc at once.** What the agent writes to the file shows in the page without a reload; what the person has typed and not saved is kept, and where both changed the same block both versions stay. Each place the agent touched is marked with its name and what was there before, to keep or undo, one by one or all at once. ⌘Z takes back the person's own typing, never the agent's edit. In AllyHub Studio the editor goes to the doc the agent is writing.
208
- - **What another hand changed is a flag in the margin**, with the agent's name, level with the block and never over its words (a dot on a narrow page). Under the pointer, or with the focus, it opens to what was done and what was there, with Undo and Keep. The review bar steps from one change to the next, as a sheet's does.
209
- - **Comments are threads kept with the doc** (`comments.json` beside it). A comment is about selected words or a whole block; the agent's answer shows under it, the person can reply, and marks it done. The words of an open thread stay marked in the doc. Space on an empty line, or "Ask" in the `/` menu, asks the agent to write there. In AllyHub Studio what was said joins the person's next message.
210
- - **Fixed in the doc's editor**: the lists a toolbar opens (a block's type, its alignment) took no choice, because a click in them was read as a click on the sheet's empty space; the alignment list had no names and every row ticked. A doc made or renamed while the project was being read again went missing from the preview for a moment, so a rename right after making one did nothing.
211
- - **AllyHub Studio can be packaged** as an app of its own name: `pnpm studio:package` makes a dmg and a zip for the system it runs on (macOS on Apple silicon so far), signed ad hoc unless asked to sign with a Developer ID. The app brings its own ffmpeg, ffprobe and headless Chrome, so a check or a render needs nothing installed and downloads nothing; its own code is one archive that Electron refuses to load once changed.
212
- - **In AllyHub Studio a video is on a sheet of its own, as a doc is**: the player, its strip and its timeline in one frame, with the video's name, its versions, Script and Export in a bar at its top instead of the window's header. Nothing rules the tree or the agent's panel off from it; each is dragged wider or narrower by the sheet's edge. The window's header is lower, and the agent's panel is shown and hidden from its right end. The built-in agent is named AllyHub AI, with AllyHub's mark. A doc's Export asks where to save the PDF, on the Downloads folder and the doc's title.
213
- - **AllyHub Studio's project tree can be dragged wider or narrower**, and remembers its width. A block's handle sits a little further from its text; its menu has a sign before each row, and Delete is red under the pointer. The doc's scrollbar is the app's thin one.
214
- - **Links to the project's other docs, videos and slides**: `@` lists them by title, and the link is written as a path to that item's file, which an agent and any Markdown reader follow. A click on one opens it.
215
- - **A person can make, rename and delete a doc** from AllyHub Studio's tree, and make one from the browser editor. A deleted doc is moved to `.local/trash`, never deleted.
216
- - **`allyhub check`** reports what is wrong with a doc's file whatever the taste (AK-M008): not MDX, a tag or `{…}` a doc does not draw, a picture that is not in the project, a doc inside another item's folder.
217
- - **`allyhub render`** prints each doc to `.local/out/<its place>/doc.pdf`, and `--type png` adds a picture of the whole page; `--piece <its place under content/>` renders one doc.
218
- - New packages: `@allyhub/doc` (the file's contract: `parseDoc`, `writeBlocks`, `patchDoc`, `merge3`, `docHtml`) and `@allyhub/doc-editor` (the editor, built on BlockNote).
219
- - Not yet: a tag the doc's code draws is still a named card, and there are no columns.
220
- - The example has one: `content/launch/plan/doc.mdx`.
221
-
222
- ### Sheets
223
-
224
- - **A sheet is a fourth kind of item**, beside a video, slides and a doc: rows a person reads, sorts, filters and edits as a grid, and an agent writes as CSV. Its folder holds `sheet.csv` (the rows, plain CSV under a header row, read as they are by any tool) and `sheet.yaml` (what the columns are: a title, the key column, each column's type, options, format, width and wrapping). It is one grid, as a spreadsheet's sheet is: no saved views, boards, galleries, calendars, groups or footers. It is not a piece, and it has no formulas: the agent works values out and writes them.
225
- - **A spreadsheet's basics by hand; the rest is asked of the agent.** A person does what anyone who has used Excel already knows: types, pastes, fills and clears cells; inserts, deletes, moves and resizes rows and columns; renames a column by a double click on its header; sets its format from Excel's short list (Text, Number, Currency, Percent, Date, Checkbox, Dropdown list); sorts and filters a column from its header by ticking its values, as Excel's AutoFilter does; hides and freezes columns; finds and replaces (⌘F, Ctrl+H, in a box over the grid's corner); reads and writes a long value in the bar over the grid; and sees the selection's count, sum and average under it. Everything else (option colours, a number's details, the key and title columns, values a formula would work out) is asked of the agent, in the conversation or with "Ask Claude about this" (the agent's name) on the cells, and the agent writes it in the files. The editor shows all of it and has no controls for it. There is no per-cell styling: the sheet is data every tool reads.
226
- - **Ten column types**: text, number (plain, percent or currency, decimals, thousands), select and multi-select (options with a doc's colours), date (short, long or ISO, with or without the time), checkbox, link, email, phone and file (a path in the project, shown as a picture when it is one). A value is the text a type reads: `1234.5`, `2026-10-14`, `true`.
227
- - **How a person looks at a sheet is theirs**: a sort, a filter, hidden and frozen columns stay on their computer, said in the status line with Clear, and are in no file. Column widths are saved at once, in `sheet.yaml`. Over the grid there is only the bar and the value bar: no toolbar row.
228
- - **A date picker**: a month to pick from under a field that takes `today`, `Oct 14 2026` or `2026/10/14`, with Today, Clear and a time, kept inside the grid. **A row's page** is titled by the sheet's `primary` column (else the first text column that is not the key), its fields set beside their names, the hidden ones under a heading of their own.
229
- - **What another hand changed lasts until the person reviews it**: kept on this computer, so a reload keeps the marks, and what was written while the sheet was closed is marked on opening as changed since they last looked. A card over each changed cell or added row says who changed it and what was there (as its column shows it) with Keep and Undo, and the review bar steps from one change to the next. A question to the agent names its cells by their row and column ("Acme Robotics · arr $84,000").
230
- - **Columns as wide as what they hold** where nothing sets a width (a double click on a column's edge fits it again). **Rows numbered as shown**; the question's box opens by its cells; shortcuts in the cell menu; the review bar slides open. The browser editor's sheet page names the sheet once, in a switcher of the project's sheets, and its home shows each sheet as a card with a glimpse of its columns.
231
- - **The title column leads** the grid and stays in place as the other columns scroll. The browser editor's sheet page puts the save state and Export in its top bar. Links are underlined only under the pointer. Docs show on the home as cards with their opening words (`DevDoc.glimpse`). Studio's sheets are signed by the project's agent, and an empty sheet there imports a file. Chinese text puts a space between Chinese and Latin words.
232
- - **A sheet without rows** offers its first steps: add a row, paste rows (a wider paste makes the columns it needs), import a file, ask the agent. A filter that leaves every row out says so, with a way to clear it; a condition without a value yet does not count.
233
- - **The grid** (AllyHub Studio's project tree, and a page of its own in the browser editor, `?sheet=<its place>`) draws 100,000 rows smoothly (TanStack Table and Virtual), with the header, row numbers and frozen columns kept in place. It edits like a spreadsheet: typing on a cell (an input method's text too), Enter, Tab and the arrows, ranges taken with Shift and a drag, copy and paste as tab-separated text to and from Excel and Google Sheets (a longer paste adds rows; a value that does not fit its column is left out and counted; a select takes new names as options), ⌘D and a fill handle, Delete, undo and redo of every edit. Rows are inserted, deleted and dragged; columns are inserted, renamed, given a format, moved, resized, hidden and deleted; a row opens as a record with every field down a panel.
234
- - **A person's edit writes only what changed**: the rows they changed (every other row keeps its bytes, wherever it went), the field of `sheet.yaml` they changed (the rest of the file keeps its text, comments and style). Renaming a column renames it in the header, `sheet.yaml` and the person's own sort and filter, as one edit to undo.
235
- - **A person and an agent can work on a sheet at once.** What the agent writes shows in the grid without a reload, each cell it changed in its colour (what was there before on hover and in the cell's menu), each row it added marked, with Keep and Undo one by one or all at once. The person's unsaved edits are merged onto the agent's cell by cell, by the key column when the sheet has one; where both changed one cell the person's value is kept and said so. The selection stays on its row. In AllyHub Studio the editor goes to the sheet the agent is writing.
236
- - **Questions are about cells** (`B4`, `B4:C9`, rows counted under the header): "Ask Claude about this" (the agent's name; AllyHub where it is not known) on a range sends the cells, what they say and the rows' keys. In AllyHub Studio it joins the person's next message.
237
- - **A person can make a sheet** (one column to start from), make sheets of a `.csv`, `.tsv` or `.xlsx` file (a sheet per worksheet, a column typed only where the workbook types all of it), rename and delete one.
238
- - **`allyhub check`** reports what is wrong with a sheet whatever the taste (AK-M009), each with its file and line: rows that are not CSV, a header with an empty or repeated name, a row with more fields than the header, a value its column's type cannot read, a file cell naming a file that is not in the project, a key that is empty or repeated, a spec that names a column the sheet does not have or does not match the format, a sheet inside another item's folder. A column of numbers declared text is not a problem.
239
- - **`allyhub sheet profile <sheet>`** measures every column of a sheet too long to read: its type, empty and distinct counts, values its type cannot read, the smallest and largest (a number's sum and mean, a text's lengths) and its most frequent values (`--top`, `--json`). **`allyhub sheet import <file>`** makes a sheet of a CSV, a TSV or a workbook, at `content/<name>/` or `--to <place>`, never over a place that is taken.
240
- - **`allyhub render`** writes each sheet to `.local/out/<its place>/sheet.xlsx` (values by type, numbers and dates in their formats, a select's options as each cell's list); `--type csv,md,json` writes CSV, a Markdown table and JSON. The grid's Export writes the whole sheet. A piece reads a sheet like any data file, `ctx.data("content/<its place>/sheet.csv")`.
241
- - New packages: `@allyhub/sheet` (the contract: `readSheet`, `patchSheet`, `readSpec`, `checkSheet`, `viewRows`, `profileSheet`, `merge3`, and `@allyhub/sheet/xlsx`) and `@allyhub/sheet-editor` (the grid).
242
- - The example has one: `content/launch/partners/`, the launch's design partners.
243
-
244
- ### The script
245
-
246
- - **A script file per item and language**, `script.<lang>.yaml`: a `title`, the item's own `text`, and a row per part with `id`, `say` and `screen`. What a part says is one line, with its anchors and its `<shown|said>` pieces written in it. It is the item's words as a person reads them, in the order they are told, and it can be written before the video.
247
- - **It becomes messages under the piece's prefix**: `<id>.title`, `<id>.<part>.say`, `<id>.<part>.<name>` for each name under `screen` (lists are numbered), `<id>.<name>` for each name under `text`. Notes are not messages. Code reads them with `s()` as before, and the keys are typed.
248
- - **Checked**: a file that is not YAML or not a script, a key made twice, and a key a messages file or another script already has are AK-M007.
249
- - **Anchors: named places in what a part says.** `@{name}` before a word of a part's `say`, or `@{name}…@{/name}` around a stretch of words, and the code times things by the name: `at: "@name"`, `until: "@/name"`, with a shift as `"@name+0.2"`, wherever a word reference goes. An anchor survives a reworded line and is the same name in every language, where a word reference names words that change with both.
250
- - The message is the line without its anchors, so the voice, the captions and a recording's search never see one. An anchor lands by the line's letters matched to the words of its sound, the way a word reference does; before a `say` has its file it is placed in proportion along the line.
251
- - A name the line does not have, the end of a point and an anchor in a part without speech are AK-T004. A name written twice, an end without a start, a stretch with no words, and an anchor in `display` or in a plural are AK-M007.
252
- - `allyhub timeline` prints where each anchor landed, and `timeline.parts[].anchors` has it in film seconds.
253
- - In the script beside the picture an anchor is a small flag with its name before its word, with a dotted line under the words of a stretch; a click goes to its moment. A person's edits move it with the words it is on, and none removes one: typing over an anchored word keeps the anchor on the new word. A line whose anchors only moved is not a change.
254
- - A text edited on the preview page keeps its line's anchors with the words the old and the new text share.
255
- - **An anchor can be dragged on the timeline.** Each anchor of a part's line is a hairline on the voice row with its name, and a stretch has one at each end. Dragged, it goes to the start of the word that starts nearest where it was dropped (the end of a stretch to the end of the word that ends nearest), and the line is written into the script at once: the same words, the anchor elsewhere, only that line's bytes. The picture follows. An anchor goes before a `<shown|said>` piece, never inside, and a stretch keeps words in it.
256
- - The example's launch film times its second line and its steps by anchors, in place of a word per language kept as on-screen text.
257
- - **`<shown|said>`: a piece of a line that is shown one way and said another**, written in the line: `from <12|twelve> fields`, `<AI|A I>`. The part's message is the line as shown with how it is said beside it, so `s()` and captions have the left sides and the voice and a recording's search the right. Either side may be empty (`<™|>`, `<|[laughs]>`). A whole line written as `{ display, speak }` still works; a line has one or the other (AK-M007).
258
- - In the script beside the picture such a piece reads as it is shown, with how it is said small beside it. The person changes what it shows: typed over, it shows the new words and the change sent to the agent has the line as written (`<14|twelve>`), for the agent to write how it is now said; deleted, it is gone from both sides. What is typed right at its edge joins it only when it goes on the same word (a digit after a digit).
259
- - The example's quote says `<12|twelve>` and `<3|three>` this way, in both languages.
260
- - **Captions keep each word's time in a line that is shown one way and said another.** The words the two lines share are timed by their own sound, and only the stretch that differs is timed in proportion (`shownToSpoken`); before, the whole line was. This is for `<shown|said>` and for `{ display, speak }` alike.
261
- - **The script beside the picture (AllyHub Studio).** "Script" in the title bar opens the item's script as one document left of the picture, and the project's tree folds to a strip. It is what is said: every part that says something has a row, in the script's order, with its line (a line of the script, a recording's words, a generated voice); the row of the part on show is marked, and a row's name goes to its moment. A part that says nothing is not listed, and what a part shows is not listed either: that is marked on the picture. A video where nothing is said has no Script button.
262
- - **Edits are a draft.** Words, rows added, removed, moved, split and merged are kept in the app and shown as changes, with what each text was. Nothing is written to the script file. "Add to your message" puts the list of changes in the composer; the agent changes the script file and the video. The draft ends once the file says the same, or once the agent has written the file after it was sent.
263
- - **What the parts say is one document.** Type where the cursor is, Enter splits a row there, Backspace at a row's start joins it to the row above, and a selection can run from the middle of one row to the middle of another: deleting it keeps the first row's start and the last row's end and removes the rows between that say something. Undo and redo work on the draft. Input methods compose in place.
264
- - **About how long new words take.** A row whose words changed, and a new row with words, says about how many seconds they take to say, at the pace of the words the part says now when it says any (else 0.38 s a word and 0.22 s a Chinese, Japanese or Korean character: `speechLength` and `SPEECH_RATE_DEFAULTS` in the engine), beside how long the part's speech is now. It is an estimate and says so.
265
- - **A changed title is tried in the picture**: it shows in the preview a moment later, without being written; Discard takes it back. What a part says is not tried, since new words need new sound.
266
- - **One thing applies at once**: a word struck from a recording is cut from the take, through the take's edits file, and the preview follows. Struck again, it is said again.
267
- - **`allyhub dev`** reads a script again when it changes and says which piece and part the change landed in. A text edited on the page is written to its script file, and only that text's bytes change: what was written is read back whole, and the file must hold the new text there and the same value everywhere else, or nothing is written. Adding, removing or reordering rows stays the agent's.
268
-
269
- ### Pieces, formats and the matrix
270
-
271
- - **Formats.** `definePiece({ formats: ["tiktok", "16x9"] })` lists what every output renders as:
272
- - a preset (`16x9`, `9x16`, `1x1`, `4x5`), `WIDTHxHEIGHT`, or `a4`/`letter` for pages;
273
- - or a **platform profile**: `tiktok`, `reels`, `shorts`, `meta-feed` (4:5), `amazon` (16:9) or `linkedin` (1:1). A profile carries the area its interface covers, its longest video (AK-F001), its loudness and an upload encode (CRF 20 and a bitrate cap).
274
-
275
- An entry `{ format, safe?, maxDuration?, loudness? }` changes them. A format is not a dimension: every combo renders in each format.
276
-
277
- - **The matrix.** `matrix.dims` crosses dimensions in full (`lang` picks `assets/text/<lang>.json`), and `ctx.combo` reads the point. `piece`, `output`, `token`, `size` and `format` cannot name a dimension.
278
- - **Slots for mix-and-match.**
279
- - `matrix.slots` lists each slot's candidates: names, `{ name, tags }`, or `{ pool: "./folder" }` (a candidate per media file, tags from `<stem>.meta.json`).
280
- - `structures` orders slots.
281
- - The agent names the combos to make in **`matrix.combos`**.
282
- - Otherwise `pick`, `seed`, `differ` and `rules` (`if`/`require`/`exclude` over names, `#tags`, dims and `structure`) sample them deterministically: every candidate before repeating one, then spread, new pairings and distance, with a reason per pick.
283
- - Combos get ids (`c01`…) and `--combo c03` chooses one. A part whose key is a slot's name fills it (or `part({ slot })`), and `ctx.pool(slot)` is a pool slot's picked file.
284
- - Rules: AK-X001 (names nothing), AK-X002 (no combination left), AK-X004 (closer than `differ`), AK-X005 (a part reads another slot's pick), AK-X006 (longer than `maxDuration`).
285
-
286
- ### Parts: speech and layers
287
-
288
- - **Parts.** A video or deck is a list of `part({ key, speech?, dur?, lead?, tail?, overlap?, layers, sounds?, still?, tags?, slot? })`.
289
- - A part with speech lasts lead + speech + tail (0.2 s and 0.3 s by default). One without speech lasts `dur` (4 s).
290
- - Project and output defaults go in `defineConfig({ defaults: { part } })` and `video(…, { defaults: { part } })`.
291
- - `overlap: { before, after }` makes J and L cuts.
292
- - `anchors: { press: 7.1, map: [9.6, 13.4] }` names moments of a part in seconds, speech or none: `"@press"`, `"@map"` and `"@/map"` work wherever a word reference does, `allyhub timeline` prints them, and `frames --at` takes them.
293
- - A part's clock and random seed are its own (piece, output, dimensions, format, part key, frame).
294
- - **Speech.**
295
- - `take(file)` is a recording, audio or video, with its words in `<name>.words.json`:
296
- - `.range(from, to)` takes a stretch;
297
- - `.saying(text)` takes the stretch that says a line, among the words the edits keep, searching after the previous part and then the whole take (AK-T006 if it is said only in removed words);
298
- - `.edits(file)` cuts it by its text;
299
- - `.denoise(db)` denoises it;
300
- - `.roomTone(false | { from, to })` chooses what fills its lead and tail. The default is its own room.
301
- - `say(text, { voice, tool, params })` is text to speech from `assets/media/gen/say/<hash>`, made by `allyhub voice generate`.
302
- - Rules: AK-T001 (no words), AK-T002 (no say file), AK-T003 (phrase or line not found), AK-T004 (word reference not found), AK-T005 (bad edits file), AK-T006, AK-T007 (a part's line is not in the text its generated take was made from: the line changed after the voice was made).
303
- - **Word references** (`"inbox"`, `"inbox@2"`, `"inbox+0.2"`) resolve against the part's own words. They go in a layer's `at`/`until`, a part's `still`, any `time()` prop, and `sounds`.
304
- - **Layers.**
305
- - `footage(file?, opts)` is video, the part's own take when no file is given. Options:
306
- - placement: `box` (with keyframes), `fit`, `crop`;
307
- - playback: `from`, `speed`, `gainDb`, `audio`;
308
- - motion: `reframe` (`"face"`, `"center"`, `{ x, y }` or `{ face, faceY }`), `speaker` (a face id), `punchIn` (`["word", "free@1.15"]` or `{ at, until, scale, in, out, ease, center }`), `pullback`;
309
- - look: `sharpen`, `shape: "circle"`, `radius` (rounded corners, px at 1080p).
310
-
311
- `box` and `crop` can be given per frame shape, `{ landscape, portrait, square, default }`: each output takes the one for its orientation, so one piece places its footage for 16:9 and 9:16.
312
-
313
- A box past the frame is refused.
314
-
315
- - Graphic layers are components or `custom()`, drawn between `at` and `until`. `z: "behind-person"` draws under the speaker, cut out with the person matte.
316
- - **A part's layers are stacked in the order they are listed, footage and graphics alike.** A graphic layer listed before a footage layer is under it (a card behind a video), one between two footage layers is between them; graphics listed after the footage are over it, as before. A piece that listed a graphic layer before its footage and relied on it showing on top must list it after. `allyhub timeline` prints the order of a part that mixes them, and the `graphics-under-footage` fact lists graphic layers with footage over them.
317
- - **Transitions.** `part({ transition })` on the incoming part.
318
- - Types: `"fade"`, `"dip"`, `"slide"`, `"push"`, `"wipe"`, `"zoom"`, or `{ type: "custom", out, in, color }`, which gives the channels (position, scale, opacity, clip) directly.
319
- - Options: `dur` (0.4 s), `ease`, `split`, `direction`. Nothing transitions unless a part asks.
320
- - Parts keep their lengths: the window lies across the join. Outside its own time a part's graphics hold and its footage runs on. The sound is not changed.
321
- - The preview plays transitions from the same numbers as the render.
322
- - **Backgrounds behind the speaker.** `footage({ background: { blur, dim, desaturate, color, image, feather, shrink } })` treats what is behind the person apart from the person, through the file's person matte.
323
- - `color: "transparent"` leaves the person alone.
324
- - Without a matte the footage shows as it is, and a `fallback` fact says so.
325
- - The preview approximates it; stills and Final look are the render's.
326
- - **Component context:**
327
- - timing: `ctx.layer`, `ctx.words()`, `ctx.at(ref)`;
328
- - captions: `ctx.captions({ maxChars, maxWords, keep })`, the part's cues with word times;
329
- - faces: `ctx.face(t, id?)`, `ctx.faces(t)`;
330
- - layout: `ctx.texts(t)` (other layers' text boxes), `ctx.covered`, `ctx.content` (the content box).
331
- - `@allyhub/sdk/dom` has `layout(ctx, { margin, topGap, bottomGap })`, `captionTop` and `freeAreas` (placement tools the agent may call; nothing calls them on its own), `emphasized`, `wordEmoji` and `emojiEl`. Emoji draw in Noto Color Emoji (`look.font.emoji`).
332
-
333
- ### Talking-head footage and measurement tools
334
-
335
- - **`allyhub transcribe <file>`** recognises speech on this computer with Parakeet TDT 0.6B v3 (25 European languages; others are refused for now). The model is downloaded on first use (670 MB) into `~/.allyhub/`.
336
- - Each word's edges are tightened to the sound (`--raw` keeps the recogniser's), and each word gets a `confidence`.
337
- - `--from` and `--to` hear a stretch again, once per `--pad`, and write nothing.
338
- - **`allyhub transcript <take>`** is the planning view: lines by pause, word indices, and the sounds between words inline.
339
- - Options: `--from`/`--to`, `--search`, `--edits`, `--script`, `--confidence`, and sound and script thresholds with `--all`.
340
- - `--json` gives every word with its levels.
341
- - **`allyhub edits <take> [file]`** is a dry run of a cut list: the kept stretches as the render places them, every removal, the new length, and each join's silence and level step. `--pauses` and `--words` list candidates and never apply them.
342
- - **Cut lists** remove exactly what they list: `remove` (word ranges), `sounds` (stretches between words), `maxPause` and `pad`.
343
- - Cuts land in the silence, on the quietest 10 ms and on the frame grid, with 10 ms fades.
344
- - The build measures what is heard between words (level and voicing, kept as `voice.json`) and reports it; nothing is removed unless listed.
345
- - **`allyhub sync <reference> <other> [more…]`** measures the offset between two recordings of the same moment from their sound, to well under a millisecond: a separate microphone and a camera, or a screen recording and a webcam.
346
- - It also gives, as numbers: the peak and the next peak, each window's offset and correlation, the drift between the two clocks, and both levels.
347
- - A silent or too short file has no offset and says why.
348
- - Apply the offset with `footage(file, { from })`, and a drift with `speed`.
349
- - **Words measured from inside.** `allyhub transcript` compares each word's length with what its letters take at the take's own rate, and lists its voiced stretches and level dips.
350
- - Words far over their expected length are marked `(long 0.62s ×2.4)`.
351
- - Thresholds: `--word-ratio`, `--word-excess`, `--word-dip`; `--all` lists every word.
352
- - An edits file's `inWord: [{ from, to }]` cuts a listed stretch inside a kept word.
353
- - **Room tone matches the join.** A fill plays at the level of the take's own room beside the join (at most 6 dB either way) and cross-fades with it over 0.04 s. Options: `take().roomTone({ from, to, match, gainDb, limitDb, fade })`. `allyhub timeline` and `allyhub edits` print the numbers.
354
- - **`allyhub index <video>`** makes the face track (YuNet: every face with an id) and the person matte (U²-Net, sampled where the picture moves). It installs onnxruntime-node and the models on first use. `check`, `render` and `dev` index what they need on their own (`--no-index` falls back, and reports it as a fact).
355
- - **Phone footage** turned by a display matrix is placed as shown.
356
-
357
- ### Sound
358
-
359
- - **Sound effects** play where a part places them: `part({ sounds: [{ at, sound, gainDb?, what? }] })`, using `look.sfx` names, the built-in `whoosh`/`tick`/`pop`, or an audio file.
360
- - **Music:**
361
- - `music: { file, from, loop, snap, bpm }`, one file per language allowed.
362
- - Snapping uses `bpm` or the tempo measured from the file. With neither, nothing is snapped and a `fallback` fact says so.
363
- - **The mix** follows the moment: music and footage sit at their under-speech levels only while speech plays.
364
- - `video({ mix: { speech, music, musicAlone, sfx, footage, footageUnderSpeech, duck: { db, attack, release, hold }, musicFade } })` sets the levels.
365
- - `master` takes a number, `false`, or `{ lufs, truePeak, lra }`; the default true peak is −3 dBTP.
366
- - `allyhub render --audio-bitrate` sets the bitrate.
367
- - The preview mixes at the render's levels.
368
-
369
- ### Web products
370
-
371
- - **Playwright, in place of a recording language.** A script run with `allyhub run` may import `playwright-core` and `allyhub/browser` by name, with AllyHub's headless Chrome, in a project with no `node_modules` (AllyHub Studio included). `allyhub/browser` gives `chromium`, `glide()` (the pointer travels to a target instead of jumping) and `session()`: several people on one clock, a constant-rate video of each person's screen, and a `<who>.timeline.json` of what happened on it (each press with the box of what was pressed, drags, typing, pages, marks, the pointer's path), logged from the page's own input events whatever code made them.
372
-
373
- ### Rendering
374
-
375
- - **A frame waits for the pictures it shows.** A layer that changes what an `<img>` shows, or an inline background image, while it plays (a captured screen swapped for the next state) was sometimes rendered blank for a few frames: the new picture had not decoded when the frame was captured, and Chrome may let a decoded picture go and decode it again when it is next painted. Every frame now waits until each picture on show is decoded, so the export matches what `frames` and `still` draw.
376
- - **Footage and graphics are drawn apart.** ffmpeg draws the footage straight from the files (no frame cache, the file's own colours). The browser draws only the graphic layers, with alpha, and only where one shows; ffmpeg stacks them with the footage in the part's layer order, one picture from the browser per run of graphic layers between footage. Each part is one ffmpeg run, encoded as a closed-GOP segment, and the segments are joined without re-encoding.
377
- - **Each part renders once.** Segments are cached in `.local/cache/parts/` by everything that draws them, so every combo with the same part reuses it.
378
- - Changing a candidate renders only its parts.
379
- - The cache holds up to 4 GB by default (`ALLYHUB_PART_CACHE_MB`); `ALLYHUB_PART_CACHE=off` turns it off.
380
- - The sound is mixed per whole combo. Joins are hard cuts unless a part asks for a transition.
381
- - **Several parts render at once.**
382
- - One part's footage decoding and encoding overlap the next part's capture. The pages drawing the graphics are shared by the parts in flight, and the sound is mixed meanwhile.
383
- - Renders are about 20–40 % faster for one file and 10–25 % faster for a batch, with byte-identical files.
384
- - More frames are captured once: a frame's signature leaves out hidden parts, and pages at the same picture share one capture.
385
- - Loudness is measured without decoding the picture.
386
- - Options:
387
- - `ALLYHUB_RENDER_PARTS` sets how many parts render at once (default: a third of the CPUs).
388
- - `ALLYHUB_RENDER_SERIAL=1` renders one part at a time.
389
- - `--workers` still sets the number of pages.
390
- - `ALLYHUB_RENDER_TIMINGS=1` adds where a render's time went to `allyhub render --json`.
391
- - **A transition's window renders as a segment of its own**, cached per pair of parts. Parts still render once per batch, and changing a transition never re-renders the part before it. `allyhub render --json` reports `junctions` beside `parts`.
392
- - A custom transition that moves a picture while scaling it past the frame renders as the preview draws it: what the scale pushed past an edge comes back in as the picture moves, where the render had shown the background in a strip.
393
- - **Capped renders are deterministic.** A platform profile's mp4 now renders to the same bytes every time.
394
- - Each segment is encoded without the cap, and only one that overruns it is encoded again, capped, on one thread.
395
- - A video under its cap is the same file as with `--max-bitrate 0`.
396
- - WebM renders are byte-identical from run to run too.
397
- - **Stills composite the same way:** `allyhub still`, `sheet` and `frames`, the preview's Final look, covers, PNG pages, PDF and PPTX. `--raw` leaves out blur, `post` and the LUT.
398
- - **Encoding and output options:**
399
- - `fps` (NTSC rates exact), `transparent`, `motionBlur` (graphic layers only), and `post: { filter, lut }`.
400
- - File types: `mov` (ProRes, 4444 with alpha), `webm` (VP9, alpha), `gif` and `png`.
401
- - Encode flags: `--codec h265`, `--crf`, `--chroma 444`, `--bits 10`, `--max-bitrate`, `--gpu`.
402
- - Subtitle cues: `--caption-*` options.
403
- - **Captions** (`srt`, `vtt`, the player's CC) come from each part's words, with the script's spelling.
404
-
405
- ### Checks and facts
406
-
407
- - **`allyhub check`** reports objective errors only. It counts the facts and points to `allyhub inspect`.
408
- - **`allyhub inspect`** prints the facts for the agent to judge, filtered by `--kind`, `--part`, `--from` and `--to`; `--at` and `--texts` measure one output now. The fact kinds:
409
- - `text-speaker`, `text-covered`, `text-overlap`, `free-bands`;
410
- - `graphics-under-footage` (graphic layers drawn under footage, and how much of the frame it covers);
411
- - `text-overlap-own` (text over text of the same layer), `text-outside` (text reaching outside the box it is drawn in), `text-time` (seconds each text is whole on screen, against its words);
412
- - `footage-scale`, `flat-frames`;
413
- - `music-length`, `join-loudness`;
414
- - `matrix-coverage`, `fallback`;
415
- - `generated-media`, `gen-mismatch`.
416
- - **Layouts are measured after their animations too:** every second of each part and its last frame, besides the still and the middle of each layer and cue. Blocks of one layer, and a block against its box, are compared by what shows of their glyphs (`ink`), so a line's tall box is not an overlap and text behind a mask is not on screen. `allyhub inspect --every <s>` measures a grid of moments now.
417
- - **`allyhub inspect --motion`** follows every text, painted box and picture frame by frame and lists each one's largest step, a move or a fade, beside the steps around it: a jump stands out of a glide. **`allyhub inspect --loop`** compares a video's last frame with its first. Both are measurements on demand, not facts.
418
- - **`words-on-screen`**, a new fact: per part, the most words whole on screen at once, the first stretch that has them, and the count from each change on. `allyhub check` prints one line of the text facts' ends: the least seconds per word of any text, how many texts are never whole, and the most words whole at once.
419
- - **`text-off-frame`**, a new fact: a text whose letters the frame's edges cut, at each measured moment, with the edges, pixels past each, and the share of its glyphs outside. Measured by the glyphs, so a transform or a font taller than its line counts, and a layer that clips at the frame does not hide it. **AK-L002 "Text goes off the frame"** is now measured the same way at every measured moment, not at each part's still alone: it is said for a text the frame cuts at every moment measured that shows it (a caption cut by the top-left corner throughout its cue was missed before), and no longer for one that slides in or out past an edge. A text wholly past the frame's edges at a moment (a camera gone in on another place of a screen) is not on screen then, so not cut; each such moment stays a `text-off-frame` fact.
420
- - **`still-picture`**, a new fact: the stretches where the picture holds (3 s or more by default, a frame looked at every second), with their length, the parts they run through, how much changed in them, and the footage showing meanwhile, which is not compared. **`allyhub inspect --still`** looks again with other options (`--all`, `--still-min`, `--still-change`, `--still-noise`, `--still-every`).
421
- - **How large each text is on a phone.** The `text-time` fact gives each text's font size as drawn (the smallest while it is whole, a transform's scale counted) in the frame's pixels and in points on a phone held upright with the frame fitted inside it (390 × 844 points by default, the fact's `options.phone`). `allyhub check` names the smallest in its line about text on screen; `allyhub inspect --kind text-time` lists every one.
422
- - **How large its lowercase letters are.** Beside its size, `text-time` gives the height of the text's lowercase x as drawn, in pixels and in points on the phone (`size.lowercase`, for text with lowercase Latin letters): a handwriting or display face draws its letters much smaller than its size says (Caveat's x is about a third of its size, Google Sans's half). `allyhub check` names the smallest lowercase too.
423
- - **`cut-seam`**, a new fact: at each join (either side of a transition's window), how the things on screen move over the last three frames before and the first three after: each thing's velocity in pixels a second and its direction in degrees, the largest moving ones listed, everything moving together, the turn and the speeds across, and the things named alike on both sides (`data-ak`, or the same text) with how far they jump. Numbers, no verdict. **`allyhub inspect --seams`** measures again with the picture's own shift either side (`--seam-frames`, `--seam-rest`, `--all`).
424
- - **`flash`**, a new fact: a frame beside a join that is unlike both the frame before and the frame after (a one-frame flash, a blank frame, a layer that shows for one frame), with the share of its pixels outside what its neighbours span and how much it differs from each. **`allyhub inspect --flash`** looks at every frame of a stretch (`--flash-noise`, `--flash-change`, `--flash-frames`, `--all`). A frame that touches a cut is also measured against its own shot alone (`own`: its step to the next frame of its shot, and the shot's usual change a frame), so a frame in a fast move beside a cut, whose other neighbour is the next shot, reads apart from a blank or stray frame.
425
- - **`text-contrast`**, a new fact, measured on demand: **`allyhub inspect --contrast`** measures each text against the pixels drawn behind its letters (the text hidden, the picture captured: a box, a gradient, an image or a shadow counts), as WCAG contrast ratios: the lowest, a low percentile and the median, with the colours. `--final` measures over the final picture, footage included; `--at`, `--every`, `--part`, `--contrast-percentile`, `--contrast-scale`, `--all`. The check does not measure it: a capture a moment.
426
- - **A text names its own message.** A problem about a text on screen names the keys whose text is exactly it; a part of a line is named only from three letters or digits up, so a comma or a digit no longer names every message that has one. A line drawn without its marks' delimiters is found too.
427
- - **`data-ak-viewport`**, on the element of a screen drawn inside the frame (a site demo, a code panel), says its text is the page's: left out of "goes off the frame" (AK-L002), AK-L003, `text-covered` and `text-outside`. It was there and is now documented.
428
- - **A batch is checked by its parts:** only the combos that cover every candidate are loaded.
429
- - **Other new rules:**
430
- - AK-R004: an inline style the browser drops.
431
- - AK-L006: a fallback font, or a faked bold or italic.
432
- - AK-A005: a picture a layer draws that does not load (an `<img>` or a CSS background image whose source is `[object Object]`, a path written by hand or a file that is not there), with the layer and the source it tried.
433
- - **A media value turned into a string is its `url`**, so `img.src = logo` draws the imported file; its JSON and its copies are as they were.
434
-
435
- ### Review and inspection tools
436
-
437
- - **Nothing renders while a piece is made, and nothing reads a render back.** A piece is code, so every tool draws, measures and mixes it on demand, as the render would:
438
- - `allyhub frames --cuts` draws its sheet per cut from the piece (each frame as the render composites it, the sound as the render mixes it); it no longer needs a render.
439
- - `allyhub audio` measures the piece's own mix (`--file` measures another file, a reference's sound); `--mix` is gone, it is what it does.
440
- - `allyhub compare` measures the piece from its code, with no render: its cuts are its part joins, the jumps of its cut takes and full-frame footage starting or ending inside a part; its words its takes'; its text on screen every text block its layers draw while whole (no OCR); its onsets its own mix. The JSON's `measured` says so. `--file`, `--render` and `--no-ocr` are gone.
441
- - `--from-render` is gone from `frames`, `frames --cuts` and `audio`, and `.local/out/latest.json` is no longer written. `frames --file` still takes the frames of any other video.
442
- - The skill's review loop no longer renders, and a test renders a piece and checks a frame after each cut and the whole mix against what code draws and mixes.
443
-
444
- - `allyhub asset compare <reference.png> <candidate.png>` measures how two pictures differ, or a picture and a moment of a piece (`--piece`, `--at`): the share of pixels over a colour tolerance (ΔE), mean, 95th percentile and largest differences, per channel, SSIM, and the regions where they are, with a heatmap, the two side by side and, with `--gif`, a blink. Sizes that differ are resized and reported; `--shift` finds the best offset within a few pixels. Every threshold is an option.
445
-
446
- - `allyhub frames --at … --out grid.png` draws several moments in one grid, as the render composites them. `--at` takes anchors by name beside seconds (`--at @press,@/map,@press+0.2`), and so does `allyhub inspect --at`.
447
- - `allyhub frames --cuts` makes a review sheet per cut of a render: frames, waveform, words, and the sound at the cut. `--part` writes into its own folder.
448
- - `allyhub frames --overlay` draws face boxes, the matte outline, covered areas and text boxes over each frame.
449
- - `allyhub frames --from <s> --to <s> [--every <n>]` draws a stretch of consecutive frames in one image, and `--clip move.gif` (or `.mp4`, `.webm`) writes the stretch as a small silent video to watch.
450
- - `allyhub audio` reports loudness, true peak, silences, onsets against cuts and cues, and each part's speech.
451
- - **Voice**: each speaking part's voice from its own sound file (median pitch with its 10–90 % range, pace, energy above 3 kHz) and the step from the part before, in semitones, percent and dB. A voice tool's voice differs from one request to the next; this is where you read by how much. `--pitch <low-high>` and `--bright <Hz>` change what is measured.
452
- - Every part of a long video has its loudness: before, the parts at the start of a video of more than a few parts had none.
453
- - `console.*` from piece code prints after `check`, `inspect`, `render`, `still`, `sheet`, `frames` and `timeline`, with where it came from (`--console`).
454
- - `allyhub help` prints a synopsis per command; `allyhub help <command>` adds an options table. An unknown option lists the valid ones.
455
- - Media commands find their workspace by its markers and say which one they used.
456
-
457
- ### Mix-and-match batches
458
-
459
- - `allyhub mix` prints the slots, rules, chosen combos (with why) and the render cost.
460
- - `allyhub batch manifest` writes a row per ad: id, key, picks, format, length, file, ad name, and whether it uses AI media.
461
- - In `allyhub dev`, the **Combos** sheet shows slots × candidates. A note can be about a candidate in every combo that uses it, and the export takes any number of combos × formats as one job.
462
-
463
- ### Generated media
464
-
465
- - **ElevenLabs Music.** `music-elevenlabs-compose` generates an MP3 from a description, with duration, model, and instrumental controls. Studio lists it under ElevenLabs Music when the cloud catalog provides it; the downloaded file works with `video({ music })`.
466
- - Compose also accepts a structured `compositionPlan` with ordered `chunks`, explicit durations and musical directions. Supports Music v2/v2.5; prompt mode keeps its 30-second instrumental defaults. Plan duration comes from its chunks, with a maximum total of 600 seconds.
467
-
468
- - **Every file a cloud tool call downloads gets `<file>.gen.json` beside it.** It records the tool, vendor and model, the input sent (secrets redacted, inline data by hash and size), what the tool returned, the credits, the time and the file's sha256. Move it with the file.
469
- - `allyhub tools call`, `tools wait`, `voice generate` and the MCP `cloud_tools_call` all write it.
470
- - **`allyhub tools call` writes a voice's word times** beside the sound, `<name>.words.json`, when the tool returns them, so a narration made in one request is a take whose parts each say their line (`take(file).saying(…)`).
471
- - **`allyhub tools call --name <path>`** places the result in the workspace.
472
- - The same call again keeps the file and charges nothing.
473
- - A file made from another input, or without a record, is not replaced without `--force`.
474
- - **`allyhub tools price`** takes several `--input` or a JSON-lines `--input-file`, and prints each estimate and the total.
475
- - **Facts** (`allyhub inspect`):
476
- - `generated-media`: which generated files an output shows or plays, and when.
477
- - `gen-mismatch`: a record that does not match its file.
478
- - `allyhub timeline --json` and `allyhub batch manifest` read the same records.
479
-
480
- ### Reference and compare
481
-
482
- - **`allyhub reference <file or URL>`** measures a reference video into `.local/work/reference/<id>/`:
483
- - the video (a link is downloaded with yt-dlp, installed on first use);
484
- - its words and contact sheets;
485
- - `reference.json`: cuts with scores, sentences and pauses, boundary candidates with what supports each, every line of on-screen text as an observation with no label (Tesseract OCR), the pace, and a neutral split `s1`, `s2`…
486
-
487
- Thresholds are options (`--pause`, `--cut-within`, `--min-segment`, `--said-within`), and `--all` keeps everything.
488
-
489
- - **The agent writes `interpretation.json`** beside it (or passes `--sections <file>`): sections with roles and pictures in its own words, which lines are captions or watermarks, and the caption style.
490
- - `--understand` asks Gemini (`GEMINI_API_KEY`; this uploads the video) and writes its reading as a second opinion marked `source: "gemini"`.
491
- - `allyhub compare` and `--structure` use the interpretation, else the neutral segments.
492
- - **`allyhub compare <reference> --piece …`** measures the render the same way and aligns its parts with the reference's sections. It reports raw differences per section and overall, plus a summary at a tolerance, and writes a side-by-side sheet.
493
- - **A reference is measured without its words when they cannot be heard.** When speech recognition fails (a language it does not cover, an install or the decoder failing), `allyhub reference` still writes the cuts, beats, on-screen text, contact sheets and a split by cuts, and `reference.json` says why the words are missing (`wordsMissing`). `--no-words` skips recognition on purpose. `compare` says when a reference has no words.
494
- - Two commands that need the speech model, or another download, at the same time download it once: the second waits for the first.
495
- - **`allyhub frames --file <video>`** tiles moments (`--at`, or `--from`/`--to`) of any video file, labelled by its own time, with no piece in the project.
496
-
497
- ### allyhub dev and agents
498
-
499
- - Studio's `allyhub` command drops the line Electron prints about code signing when Codex's sandbox hides its process (`codesign_util … task_name_for_pid`); the CLI's own messages and exit status are as they were.
500
- - Studio's agents think at high effort unless the person chooses another level, whatever the model.
501
- - Studio writes a Codex rule into every project it creates or opens (`.codex/rules/allyhub.rules`): the `allyhub` command runs outside Codex's sandbox at once, instead of waiting for Codex's reviewer each time (its sandbox will not start headless Chrome). Everything else stays sandboxed and reviewed.
502
- - When the preview cannot load, a person no longer sees the raw error: while the agent is at work, only that it is working (the last good picture stays when there is one); once its turn is over, "Something went wrong" with a button that asks the agent to fix it; the error itself goes to the agent, never to the person.
503
- - **The preview is a video editor beside the conversation:**
504
- - a home with a card per video and deck, which waits for the first piece;
505
- - a bar that follows the agent's latest change;
506
- - an error strip that keeps the last picture;
507
- - Final look;
508
- - a **Transcript** that cuts a take by its text (it writes only the edits file);
509
- - a **Measurements** toggle that draws the facts.
510
- - **Notes** take numbered marks (box, arrow, freehand line, timeline stretches), each with its own line. The agent gets them as a list, with a screenshot per moment.
511
- - **Export** in the page renders the chosen combos, formats and file types after the same check, shows progress, can be cancelled, and opens the files. Must-fix problems offer **Ask AllyHub to fix**. The agent gets exports and notes through `allyhub feedback` and `allyhub mcp`.
512
- - **Embedding and integrations:**
513
- - `allyhub launch-config` writes the workspace's `.claude/launch.json` so Claude Code's browser pane runs the preview. The preview is for a person watching; an agent working alone skips it.
514
- - In a browser that blocks sandboxed frames, the stage runs on the page's origin.
515
- - `GET /agent/status` gives the project in a few words, facts included.
516
- - **Notes made without delivery:** `POST /api/agent` takes `deliver: false`, which makes the note (its screenshots and words) and answers it without the agent's inbox, for an app that sends notes with its own messages.
517
- - **The agent at work is a layer over what it works on (AllyHub Studio).** While an agent's turn runs, the card that holds what is on show (a video with its strip and timeline, a doc, a sheet, any view) is lit from above: a light round its edge that breathes, a faint light inside it, and a violet tag on its top edge that says "Claude is working" or what it is doing there ("Claude · editing Hook", "editing this doc"). Where it writes, a violet box is drawn round the place as if by hand, a new one each time, with its name on it: the timeline part it edits, the sheet cell it wrote (a new row whole), the doc block it is changing. Nothing in the layer covers the work or takes a click, and it fades in and out with the turn. It replaces the outline round the picture and the line over the card, which sat on a doc's and a sheet's title. When a turn makes several things at once, each comes on show in turn for a few seconds, and the tag lists what was made, a chip each that opens it; picking something yourself stops the tour. A thing new this turn arrives on screen (a doc's paragraphs, a sheet's rows, a video's parts rise in one after another) with the agent's box round it, "Claude made this", and the project's tree marks it with a violet dot until you have looked at it.
518
- - **A video or a deck without a title is named by its folder** (`teaser`), as the project's tree names it, not by its entry file (`video`).
519
- - **A project's page gives its work the width.** The tree and the conversation share one column down the left, the tree over the conversation, and what is on show (a video, a doc, a sheet) has the rest: about 960 px of a 1440 px window, where it had 700. The tree takes the rows it needs and folds to its header; the line between it and the conversation is dragged. Assets, Agent and the Recycle bin are signs in the tree's header. ⌘\\ folds the column away and leaves the work alone on the page.
520
- - **Projects has one button, New project.** An existing folder is opened as a project from the command menu ("Open a folder…").
521
- - **Agents are called by their names.** Claude, Codex, and AllyHub for AllyHub's own agent or one a page cannot know: "Message Claude", "Reply to Claude…", "Claude is waiting for your approval", "Ask AllyHub to fix", in the picker as on the page. Nothing says "your agent". Studio's picker no longer says "Claude Code" or "AllyHub AI".
522
- - **Two servers for one project:** when AllyHub Studio runs its own `allyhub dev` beside one started in a terminal, `allyhub mcp` finds the terminal's server again once Studio's stops.
523
- - **Studio's commands for the agent:** inside AllyHub Studio, the `allyhub mcp` server of a video project also has `studio_command_list`, `studio_command_run` and `studio_command_batch`. They read what the person has on show (`editor.inspect`: the video, the version, the moment, the timeline's parts) and show them a moment or a version (`editor.seek`, `editor.play`, `editor.pause`, `editor.select`, `editor.measure`). Studio decides what an agent may run and refuses the rest with the reason; the picture is the one the person looks at, and stays where it is while they draw a note.
524
- - **The preview follows the agent:** the browser editor's "editing" marks are in the agent's own colour (violet), kept apart from the blue of what a person wrote or chose.
525
- - **A new mark and a paper working area:** the preview page and its favicon use AllyHub's new mark, two pillars leaning together with a slit at the top, drawn sturdier and with a small cut that stays sharp in a browser tab. The page's working area is paper; stone is kept for sidebars and toolbars.
526
- - **AllyHub in ChatGPT Desktop** (`allyhub mcp --app`):
527
- - the sidebar's Create, Projects, Tools and Account;
528
- - a preview panel with frames, playback, circled feedback and exports;
529
- - a Settings page.
530
- - **Cloud tools:**
531
- - `allyhub mcp` (and the ChatGPT Desktop app) can search, describe, price and call the cloud catalog with this computer's login.
532
- - The skill points to ElevenLabs Scribe v2 for transcripts that keep fillers and speaker IDs, as a paid alternative to local recognition.
533
- - Prices for media duration cover audio as well as video.
534
- - `allyhub tools catalog` writes the catalog to a file to search.
535
- - `tools search`, `list`, `describe` and `price` work before signing in when allowed. `tools call` needs a key.
536
- - `login` and `doctor` say which AllyHub they use.
537
- - `allyhub launch-config --print` writes nothing and prints the preview's entry, for a `launch.json` that is committed.
538
-
539
- ### Skill
540
-
541
- - **The skill keeps only facts an agent cannot work out:** how to run AllyHub, the contract, the tools, and what needs the person's yes. There are no playbooks.
542
- - Its values are worked examples.
543
- - The agent decides cuts, retakes, fillers, captions and sound.
544
- - The agent reviews its own render (`frames --cuts`, `inspect`, `audio`) before showing it.
545
- - **The reference is a file per topic**, so an agent reads only what its job needs: `references/core.md` (every piece), `speech.md`, `sound.md`, `footage.md`, `batches.md`, a file per starter component under `components/`, `web-products.md`, `checks.md`, `reference-videos.md`, `docs.md`, `sheets.md` and `defaults.md`; `SKILL.md` names each and which to read for a product demo, a talking head, a batch of ads, a doc or a sheet.
546
- - `references/defaults.md` lists every default and the option that changes it, and `references/core.md` every `ease` curve.
547
- - `allyhub doctor` says whether the copies of the skill the agents read (in the current folder and the home folder) are the one this `allyhub` comes with, and names the files that differ: a skill left from an older version describes commands and layouts that are gone.
548
-
549
- ### Other
550
-
551
- - `allyhub font add "<family>"` adds an open font from Fontsource, with its licence.
552
- - `@allyhub/sdk/dom` exposes the helpers the starters use (`layout`, `contentBox`, `textBlock`, `fitTo`, `rise`, `pop`, `text`, `list`, `eyebrow`, `wordBreaks`…).
553
- - **Marks in a line.** A line can stress a stretch of itself as Markdown does (`**strong**`, `*em*`, `~~struck~~`, `` `code` ``). `s()` returns it as written; `readMarks`, `markRuns` and `withoutMarks` (`@allyhub/sdk`) read it, and `marked(line, styles?)` (`@allyhub/sdk/dom`) draws it, each marked run in a span of its own.
554
- - An `allyhub` run from a source checkout while it is being rebuilt says so and exits 5 (`not_built`), in place of Node's module-not-found stack.
555
- - A render that fails or is stopped leaves no ffmpeg running. BeginFrame capture is Linux only.
556
- - First-use installs keep their own npm cache (`~/.allyhub/npm-cache`).
557
- - Fixed: a part whose graphics switch between covering the whole frame and not could show the background for a frame or two, with its footage late. The images piped to one ffmpeg run are now one pixel format.
558
- - Fixed: the preview could not save an edits file that carried `pad`.
559
- - `NOTICE` credits FilmCraft (MIT) for the transcript, cut-placement and sync logic.
560
- - **Licensing:** AllyHub is proprietary (`LICENSE`).
561
- - Code adapted from HyperFrames stays under Apache-2.0.
562
- - The starter components are MIT.
563
- - The skill is published in `allyhub-ai/skills` (MIT).
38
+ - **Docs and sheets.** Create and edit MDX documents and CSV spreadsheets alongside videos and decks.
39
+ - **Speech-led editing.** Cut recordings by their words and time captions and graphics to speech.
40
+ - **Ad variants.** Combine hooks, demos and calls to action across languages and platform formats.
41
+ - **Rendering and inspection.** More export formats, richer measurements and tools to examine frames and sound.
564
42
 
565
43
  ## 0.1.0-alpha.1
566
44
 
567
- The first AllyHub preview.
568
-
569
- - The packages are named `@allyhub/*` (`allyhub`, `@allyhub/sdk`, `@allyhub/runtime`…) and the command is `allyhub`. Project files are `allyhub.config.ts` and `.allyhub/`; rule ids start with `AK-`; style variables start with `--ak-`.
570
- - Install once with `npx skills add allyhub-ai/skills`, then ask your agent. It writes the video into a plain folder (`allyhub/` in the current folder): not a Node project, no `package.json` and nothing to install, because the CLI brings the SDK. There is no `allyhub create`, `allyhub upgrade` or template, and the skill is the only one (`allyhub`). Headless Chrome is downloaded the first time a command needs it.
571
- - Needs Node.js 22.12 or newer (was 22.19): checked with Node 22.12, 22.14 and 20.19; 22.12 is what Vite needs on Node 22.
572
- - The skill has playbooks for each kind of video and deck (`references/playbooks/`: launch video, vertical short, product demo and tutorial, motion graphics, team update, slides, carousel), with what every piece needs and a checklist before handing over.
573
- - A research-to-video playbook (`references/playbooks/research-video.md`): from a question to a short video whose every number has a source, with charts read from `data/*.csv`.
574
- - Cloud tools: `allyhub login` signs in with one click in the browser (the key is kept in `~/.allyhub/mcp-headers.json`, or `ALLYHUB_API_KEY`), `allyhub whoami` and `allyhub logout`. `allyhub tools search | list | describe | call` reach AllyHub's tool catalog, by group (`media` for images, voice and video) and with prices, and print the exact credits each call used; a file a tool makes is downloaded, and a long tool is asked again until it finishes. `allyhub voice generate` reads a video's voiceover script with a cloud voice and writes the audio and its word times.
575
- - No local voice commands: a voiceover is an audio file with its word times beside it (`<name>.words.json`); voices come from the cloud tools.
576
- - AllyHub makes videos and decks; websites are removed. There is no `web()` output, no `@allyhub/sdk/react`, `/views`, `/docs`, `/markdown` or `/files`, no `allyhub build`, static site, `<allyhub-player>` embed, watch or deck pages, and no `product-site` or `docs` template.
577
- - `allyhub schema`, `allyhub help sdk` and `allyhub help workflows` are removed: agents read the skill's files directly.
578
- - Everything runs on this computer without an account. AllyHub has nothing to do with git, and `allyhub doctor` checks Node.js, headless Chrome and ffmpeg.
579
- - No collections: `collections` and `ignore` are gone from `allyhub.config.ts`, and so are `collection()`, the `feedback`, `todo` and `note` formats and the AK-C001 and AK-C002 rules. Markdown in a project is just files: AllyHub neither reads nor checks them. The templates keep the team's plan, notes, scripts and storyboard as plain Markdown, with front matter conventions in their guides; remove `collections` and `ignore` from your `allyhub.config.ts`.
580
- - `allyhub dev` previews every video and deck; circle part of a frame and send it to your agent, which takes it with `allyhub feedback --wait` or the `feedback` tool of `allyhub mcp`. Feedback goes only to the agent: it is not saved as a file in the project (the screenshot is in `.allyhub/agent/`). With no agent listening, the page copies it to paste into the chat.
581
- - Video clips, any `WIDTHxHEIGHT` size, captions, covers, PDF and PPTX.
582
- - New repositories get the skill for Claude Code and Codex, a Codex rule that lets `allyhub` run outside the sandbox, and Claude's preview launch file. No MCP server is configured.
45
+ - **Videos and decks as code.** Create and render locally with your coding agent.
46
+ - **Live preview and feedback.** Circle changes in the preview and send them to the agent.
47
+ - **Cloud media tools.** Generate voiceover, images and video through AllyHub's tool catalog.