@motion-studio/cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +309 -2
- package/dist/main.js +57589 -0
- package/dist/mcp-studio.mjs +213 -0
- package/dist/runtime/clock.js +1 -0
- package/dist/runtime/index.html +14 -0
- package/dist/runtime/layer.js +1 -0
- package/dist/runtime/preview.js +62 -0
- package/dist/runtime/runtime.js +1 -0
- package/dist/web/apple-touch-icon.png +0 -0
- package/dist/web/assets/index-CNoW5Xwv.js +34 -0
- package/dist/web/assets/index-DRjYoSZ9.css +1 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
- package/dist/web/assets/logo-Dd9VMLzB.svg +3 -0
- package/dist/web/assets/logo-dark-Yi8yb5WJ.svg +3 -0
- package/dist/web/assets/logo-horizontal-Blb7HKgj.svg +3 -0
- package/dist/web/assets/logo-horizontal-dark-B16it8f0.svg +3 -0
- package/dist/web/assets/react-VlNyIE0d.js +9 -0
- package/dist/web/assets/schibsted-grotesk-latin-400-normal-BkiRe4WW.woff +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-400-normal-DPhJBilQ.woff2 +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-500-normal-Ba39e-CX.woff +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-500-normal-rf9C4Thp.woff2 +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-600-normal-BieE9YjJ.woff +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-600-normal-Czv9Obfv.woff2 +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-700-normal-BkH0uJ1o.woff2 +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-700-normal-Dz-okVa0.woff +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-ext-400-normal-DHVTfbSM.woff2 +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-ext-400-normal-DUPvg9bQ.woff +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-ext-500-normal-BjmYCtCC.woff +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-ext-500-normal-Ch1izu81.woff2 +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-ext-600-normal-C5pQPdUJ.woff2 +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-ext-600-normal-DQsnXf5q.woff +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-ext-700-normal-DDW2aNyx.woff +0 -0
- package/dist/web/assets/schibsted-grotesk-latin-ext-700-normal-o210KhU4.woff2 +0 -0
- package/dist/web/assets/shared-DLFCfgVy.js +29 -0
- package/dist/web/assets/vendor-CfFIcVU3.css +1 -0
- package/dist/web/assets/vendor-IQO2I6nB.js +57 -0
- package/dist/web/favicon.ico +0 -0
- package/dist/web/favicon.svg +3 -0
- package/dist/web/icon-192.png +0 -0
- package/dist/web/icon-512-maskable.png +0 -0
- package/dist/web/icon-512.png +0 -0
- package/dist/web/index.html +22 -0
- package/dist/web/site.webmanifest +15 -0
- package/package.json +51 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aleloca
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,310 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/logo-dark.svg">
|
|
4
|
+
<img src="docs/assets/logo.svg" alt="Motion Studio" height="72">
|
|
5
|
+
</picture>
|
|
6
|
+
</p>
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
<h1 align="center">Motion Studio</h1>
|
|
9
|
+
|
|
10
|
+
<p align="center"><strong>Brief in. Motion out.</strong><br>
|
|
11
|
+
A local, open-source app on top of Claude Code that turns a brief into motion-graphics videos for every format.</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">🇮🇹 <a href="README.it.md">Italiano</a></p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="https://github.com/Aleloca/motion-studio/releases/latest"><img src="https://img.shields.io/github/v/release/Aleloca/motion-studio?label=release" alt="Latest release"></a>
|
|
17
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT license"></a>
|
|
18
|
+
<a href="https://www.npmjs.com/package/@motion-studio/cli"><img src="https://img.shields.io/npm/v/@motion-studio/cli?label=npm" alt="npm version"></a>
|
|
19
|
+
<a href="https://github.com/Aleloca/motion-studio/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/Aleloca/motion-studio/ci.yml?branch=main&label=CI" alt="CI status"></a>
|
|
20
|
+
<a href="https://github.com/Aleloca/motion-studio/actions/workflows/pages.yml"><img src="https://img.shields.io/github/actions/workflow/status/Aleloca/motion-studio/pages.yml?branch=main&label=Pages" alt="Pages status"></a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<a href="https://aleloca.github.io/motion-studio/#final-cut"><img src="docs/assets/hero.gif" alt="A brief becomes three variants, then a change in the chat, then every format recomposed" width="720"></a><br>
|
|
25
|
+
<a href="https://aleloca.github.io/motion-studio/#final-cut">Watch the full film</a>
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
1. **Get the app.** Download it for macOS, Windows or Linux from [Releases](https://github.com/Aleloca/motion-studio/releases/latest), or run `npx @motion-studio/cli` (Node 22 or newer).
|
|
31
|
+
2. **Have Claude Code ready.** Install [Claude Code](https://docs.claude.com/claude-code) and sign in (`claude auth login`). Motion Studio drives it, so you need a Claude plan that includes Claude Code (a paid plan); Motion Studio itself is free.
|
|
32
|
+
3. **Describe what you want.** Open **Creatives → + New creative**, write the brief, pick the formats and press **Generate**.
|
|
33
|
+
|
|
34
|
+
## What it does
|
|
35
|
+
|
|
36
|
+
- **Brief to variants:** one brief, up to five creative directions side by side, each with every format.
|
|
37
|
+
- **Change it by chat or by hand:** ask the agent for changes, or edit the composition directly in the editor.
|
|
38
|
+
- **Your brand:** read from your website or your codebase (palette, fonts, logos, tone).
|
|
39
|
+
- **Images, voice-over and music:** generated on your own provider keys, always asking before a paid call.
|
|
40
|
+
- **Every format recomposed:** a dedicated layout for each channel, not a crop.
|
|
41
|
+
- **Languages:** translate into up to 10 languages besides the original; right-to-left languages are not supported yet.
|
|
42
|
+
- **Subtitles:** transcribed from the voice-over on your computer, drawn on the video in sync, and exported as .srt or .vtt.
|
|
43
|
+
- **Export:** an mp4 for videos and an image for stills, named and organised for each format.
|
|
44
|
+
|
|
45
|
+
Everything stays on your computer: projects are folders of JSON/Markdown files versioned with git.
|
|
46
|
+
|
|
47
|
+
> **User guide**: every feature, step by step, with screenshots: https://aleloca.github.io/motion-studio/guide.html
|
|
48
|
+
> Developer guides: [CONTRIBUTING.md](https://github.com/Aleloca/motion-studio/blob/main/CONTRIBUTING.md), [output contract](https://github.com/Aleloca/motion-studio/blob/main/docs/output-contract.md), [providers](https://github.com/Aleloca/motion-studio/blob/main/docs/providers.md), [agent backends](https://github.com/Aleloca/motion-studio/blob/main/docs/agent-backends.md). The full design is in `docs/superpowers/specs/`.
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
**Desktop app (Electron).** Download the installer from the project's GitHub [Releases](https://github.com/Aleloca/motion-studio/releases) page:
|
|
53
|
+
- macOS: the `.dmg` for Apple Silicon (`arm64`) or Intel (`x64`), signed and notarized; drag Motion Studio to Applications. Updates are checked at launch and installed on restart.
|
|
54
|
+
- Windows: the `.exe` installer. It is not signed yet: the first time, SmartScreen shows "Windows protected your PC": click **More info**, then **Run anyway**.
|
|
55
|
+
- Linux: the `.AppImage`: make it executable (`chmod +x Motion*.AppImage`) and run it.
|
|
56
|
+
|
|
57
|
+
**From npm.** `npx @motion-studio/cli` (Node 22 or newer; installed globally the command is called `motion-studio`): it starts Motion Studio and opens it in the browser.
|
|
58
|
+
|
|
59
|
+
**From source.**
|
|
60
|
+
```bash
|
|
61
|
+
pnpm install
|
|
62
|
+
pnpm motion-studio # build + start on http://127.0.0.1:4317 and open the browser
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Launcher options (after the script name, e.g. `pnpm motion-studio --port 5000 --no-open`):
|
|
66
|
+
- `--port <n>` — port of the local server (1–65535, default 4317).
|
|
67
|
+
- `--no-open` — do not open the browser (if opening fails, the launcher prints the address to open by hand).
|
|
68
|
+
- `--print-url` — print the address of the Motion Studio that is already running, without starting another one (useful if you closed the tab).
|
|
69
|
+
|
|
70
|
+
The address printed at startup contains an access code (`#t=…`): open Motion Studio from that link. The browser remembers it; if the interface asks you to reopen it from the terminal link, use `--print-url`. The desktop app opens its own window.
|
|
71
|
+
|
|
72
|
+
Only one Motion Studio runs per configuration folder: if one is already running (from the terminal or as the desktop app), `motion-studio` prints `Motion Studio is already running: <address>`, opens it in the browser (unless `--no-open`) and exits, and the desktop app opens that address instead of starting another (if that Motion Studio quits, the app offers to restart).
|
|
73
|
+
|
|
74
|
+
## Requirements
|
|
75
|
+
- [Claude Code](https://code.claude.com/docs/en/setup) installed and signed in (`claude auth login`), with a paid Claude plan (Pro, Max, Team or Enterprise) or a Claude Console account: the free plan does not include Claude Code, and the Claude desktop app alone is not enough. If Claude Code is missing, the first-run setup offers **Install Claude Code** (Anthropic's official installer from claude.ai, for your user only, no administrator rights, only after you confirm) and then **Sign in to Claude**; the manual command is shown too.
|
|
76
|
+
- Git
|
|
77
|
+
- FFmpeg: needed to export videos; images work without it (older creatives without it are shown as "unverified"). The setup's **Install FFmpeg** downloads a pinned static build into Motion Studio's data folder, checked against its SHA-256, only after you confirm: on Windows the "release essentials" build of gyan.dev (GPLv3), on macOS and Linux the release build of ffmpeg.martin-riedl.de (GPL, it includes x264 and x265). Nothing else on the computer changes.
|
|
78
|
+
- For the CLI (`npx`) only: Google Chrome or Chromium, which renders the compositions. Without it, **Settings → System check** offers to download Chrome for Testing into Motion Studio's data folder, only after showing its size and with your consent. The desktop app renders with its own Chromium.
|
|
79
|
+
- Node.js 22+ for `npx` and the sources (not needed for the desktop app)
|
|
80
|
+
|
|
81
|
+
## Language
|
|
82
|
+
Motion Studio is available in English and Italian. On first launch it uses your system language: the first of your preferred languages that Motion Studio supports, English if none is. Change it in **Settings → Language** (System / English / Italiano): the interface, the desktop menus and the messages of the terminal launcher switch language, and the agent replies, and writes the texts meant for you, in the chosen language. When the desktop app attaches to a Motion Studio that is already running, its native menu keeps the language it started with; the interface follows the change live. To add another language, see [CONTRIBUTING.md](https://github.com/Aleloca/motion-studio/blob/main/CONTRIBUTING.md#adding-a-language).
|
|
83
|
+
|
|
84
|
+
Texts saved in the past (validation problems in a version, conversation messages, brand guidelines) are not translated; only new texts use the current language.
|
|
85
|
+
|
|
86
|
+
## How it works
|
|
87
|
+
1. In a project, open **Creatives → + New creative**, describe what you want, pick channels and formats and press **Generate**.
|
|
88
|
+
2. The agent works in `creatives/<date-title>/work/` and delivers, in `outputs/vN/compositions/<format>/`, one **composition** per format: a `composition.json` (scenes, tracks, text, shapes, images, video, audio, animations, optional HTML layers) and the `assets/` it uses ([full contract](https://github.com/Aleloca/motion-studio/blob/main/docs/output-contract.md)). The agent never renders final media and never installs a renderer.
|
|
89
|
+
3. Motion Studio checks each composition (schema, files, duration against the brief): if something is off it asks the agent to fix it, up to 3 attempts in total; then it saves the version, even if incomplete. It then makes a storyboard (one small frame per scene) for each format.
|
|
90
|
+
4. The boards show the storyboards and play a **live preview** of the composition: no waiting for a render. A version starts as "Not rendered".
|
|
91
|
+
5. **Export** renders what it needs on your computer, outside the agent's sandbox (there is no separate Render button; the agent can also render a version): an mp4 (H.264, BT.709) for videos, an image file for stills (a png when the format allows it, else a jpg or webp it lists). A version's own render is at the format's size and **High** quality (libx264, about 8 Mbps at 1080p30); it records its quality, size and the encoder that ran. A render made before the quality choice counts as Standard and is rendered again by a default export. A render never blocks the agent: renders wait in their own queue, one at a time. A later change makes a new version, and the render of another version is never touched; rendering the same version again replaces its file.
|
|
92
|
+
6. Each version is a git commit of the project. From the creative's page you can open a format, add comments on a spot/moment, ask for changes, compare versions and restart from an earlier version. A creative made before compositions keeps its videos; at its next change the agent converts it to a composition (the chat says that this turn costs more tokens).
|
|
93
|
+
|
|
94
|
+
### Brief with the agent
|
|
95
|
+
|
|
96
|
+
**New creative** opens on **Fill it in yourself**, the form as always. **With the agent** (top bar), or **Let the agent fill it in** in the goal box, opens a chat with a read-only agent at the left of the form; the goal's text, if any, moves into the message box (it is not sent). The **+** under the message links folders of code on your Mac (read-only, with the secrets scan of brand analysis), picks assets from Assets or uploads files from your Mac (they go into the library too); added items show as pins in the message box and, once sent, in the row under it ("2 folders · 3 assets"), where each can be removed. The agent reads them and fills the real form: goal, key message, video length, assets, linked code folders with a note, notes, formats (catalog ids only) and the variant count when you ask for variants. A field it wrote is marked **Agent** and lights up once; a field you change by hand is yours and the agent never overwrites it, unless you ask it in the chat to rewrite it. Nothing is created until you press **Generate** (or **Save draft**), which are off while the agent works; then the chat becomes the start of the new creative's chat, and the creative's first turn resumes the brief agent's memory. One brief session per project: leaving the page keeps it, and New creative then offers **Resume the brief session** or **Start over**.
|
|
97
|
+
|
|
98
|
+
### Video analysis
|
|
99
|
+
|
|
100
|
+
A video that enters a brief (the brief form, the brief agent's chat, an asset added to a creative's brief) is analysed once: Motion Studio extracts frames at each cut (and one every 10 s in long static scenes) and the audio with ffmpeg, transcribes the speech if you allow it, and a read-only agent describes the frames. The result is a text file next to the video, `assets/.analysis/<video>.md`: a summary, one timeline with what is on screen, the on-screen text and what is said with its times, usable moments, the look (colours, typography, framing, audio), the transcript and tags. The creative agent and the brief agent read it before opening the video. The file carries the video's sha256: the same content is never analysed twice, a changed video is analysed again. Videos over 20 minutes ask first (**Analyse everything** or **Analyse at reduced density**, with frames and estimated tokens).
|
|
101
|
+
|
|
102
|
+
- **Assets**: a video's details have an **Analysis** section: its state (Not analysed, In progress, Ready, Out of date, Error), the summary, the scenes and the transcript (a click on a row moves the player to that time), on-screen text and tags, with **Analyse** / **Analyse again** and **Edit** (the text below the header; an edited analysis asks before it is overwritten). An analysed video shows a small mark on its tile; progress is also in the activity centre.
|
|
103
|
+
- **Generate** while an analysis runs: the creative's first turn waits for it, with "Analysing the video (clip.mp4)… · Skip" in the chat; **Skip** starts the turn without it. A failed analysis never stops a creative.
|
|
104
|
+
- **Transcription is optional.** When a video has speech and no method is set, a choice appears (a dialog in the form and Assets, a card in the brief agent's chat): **Install Whisper** (free, on your Mac, with the download size), **Transcribe with OpenAI** or **with ElevenLabs** (paid, estimated cost shown, the audio leaves the Mac; only with a saved key), **Continue without transcription**, and **Remember the choice**. Nothing is downloaded or paid without that click.
|
|
105
|
+
- **Settings → Video transcription**: the method (Ask every time, Whisper on the Mac, OpenAI, ElevenLabs, Never), the Whisper model (Small, about 490 MB, or Large v3 Turbo, about 1.6 GB) with **Install** and **Remove**, and **Use a Whisper already installed**: the Whispers found on the Mac (PATH and Homebrew folders; **Search again**), or a program (and for whisper.cpp a model file) chosen with **Choose…**, test-run on one second of audio before it is saved ("Whisper found: engine, version, model, Metal/GPU"). whisper.cpp, openai-whisper and faster-whisper work; the Python ones download their own models on first use.
|
|
106
|
+
- **Whisper in the app**: the macOS app ships whisper.cpp's `whisper-cli` (with Metal) and only downloads the model. The development app, the npm package, Windows and Linux use an installed Whisper: on a Mac, `brew install whisper-cpp`, then **Install** a model in Settings (or pick an existing `ggml-*.bin`); or `pip install openai-whisper` / `faster-whisper`.
|
|
107
|
+
|
|
108
|
+
### Archiving and deleting a creative
|
|
109
|
+
|
|
110
|
+
From a creative's **…** menu (on its page or on its card in the project's creatives) you can **Archive** it: it leaves the lists and search and is kept in the project, under **Archived** on the creatives page, where **Restore** brings it back. **Delete** asks first and moves the creative's folder to the system Trash (recoverable until you empty it); its removal is committed to the project, so the git history keeps every version. Neither is possible while a request, a queued request or a render of the creative is in progress. From a group's page both act on all its variants; a variant B–E can be archived or deleted alone from its row menu, and it leaves the group. In the command-line version deleting uses `~/.Trash` on macOS (only for projects on the same disk) and the freedesktop Trash on Linux; elsewhere it is refused. The agent never archives or deletes.
|
|
111
|
+
|
|
112
|
+
### Talking to the agent while it works
|
|
113
|
+
The composer stays active while the agent works.
|
|
114
|
+
- **Queue for after.** A message sent during a turn is added to the request queue and runs as the next request. It starts by itself only after a turn that saved a version; after a failure, Stop, cancel or an app restart it waits for **Run next** (after a restart each queued request needs its own). The console has no queue: a message to a running console turn is sent live or not at all. You can edit, remove and reorder queued requests.
|
|
115
|
+
- **Stop** stops the turn at once and cancels any open approval, so nothing is spent after it. The next request resumes the stopped session as a fork, so the agent knows what was left half done.
|
|
116
|
+
- **Stop and redirect** stops the current step and continues the same job with your new text.
|
|
117
|
+
- **Live messages and interrupt** (sent into the running agent without a restart) are **on by default for Claude Code 2.1.301 and newer**, the version checked with a real Claude Code on 2026-10-16 (`docs/superpowers/notes/2026-10-16-stream-input-probe.md`). Older or unknown versions keep the fallbacks: a message waits as the next request, and Stop and redirect stops the agent and resumes the session. A live message is read by the agent as soon as its current step ends; it shows "Sent" once written and "Delivered" only when Claude Code shows it took it. To switch either off, set `agentLiveInput` or `agentInterrupt` to `false` in the workspace's `.studio/settings.json` (there is no switch in the interface). The Doctor and `GET /api/agent/capabilities` say which path is active.
|
|
118
|
+
|
|
119
|
+
### The agent acts on the app
|
|
120
|
+
In a creative turn the agent can do in the app what you would do, **without a confirmation card**; the chat shows each action as a line marked *Agent action* (for example "Brief duration: 6 s → 60 s", "New variant C: …", "Render started: v2 · YouTube 16:9"). Ask in words ("make a 9:16 too", "add a more ironic variant") and it uses its tools instead of telling you where to click:
|
|
121
|
+
- **Brief**: duration, goal, key message, notes and services (`update_brief`). The brief changes when the agent's turn ends (in a variant group: the group's brief, the idle variants at once); a new duration applies to that turn's output check at once.
|
|
122
|
+
- **Formats and languages**: add a format of the catalog (made in a turn right after, like **Save and regenerate**), duplicate a format, add a language (translated after the turn).
|
|
123
|
+
- **Variants**: add one with its direction (it starts its own paid turn, as **+ Variant** does), discard, restore, set the ★.
|
|
124
|
+
- **Render and export**: render a version, or render what the ★ export needs. The export itself (where the files go) stays yours.
|
|
125
|
+
- **Never**: archive, delete, anything outside its own creative, variant group and project, settings, keys or permissions. Paid services (images, voice, music, sound effects) keep their cost confirmations.
|
|
126
|
+
- **Limits**: 30 actions per turn; at most one follow-up turn for new formats (never from a follow-up turn) and one translation per turn; variants stop at the group's 5 (discarded ones count).
|
|
127
|
+
|
|
128
|
+
When a version's video duration is far from the brief's, the repair attempt may update the brief's duration (when the brief's text or your words clearly ask for it) or ask you, instead of trying three times. An incomplete version says in the chat what is wrong and what to do, and changing the brief's duration checks the latest incomplete version again at once, with no new turn. The New creative form warns when the brief's text states another duration than the field ("The text says 60 s, the duration is 6 s · Use 60 s").
|
|
129
|
+
|
|
130
|
+
### Usage and tokens
|
|
131
|
+
Every agent job (creative turn, fix attempt, brand analysis, asset description) adds one line to `<project>/.studio/usage.jsonl` with the tokens Claude Code reported for that run and, when known, the cost at API list prices. The file is append-only and protected from the agent.
|
|
132
|
+
- **Tokens first, cost second.** The figure shown is input + output + cache-write tokens; cache reads are only in the details. On a Claude subscription the cost is shown as "at API prices it would be about …", since the usage counts toward your plan.
|
|
133
|
+
- **Where you see it:** a live counter on the running job, today's total in the top bar (it opens **Settings → Usage**), the activity center (per job, plus "Today · N tokens"), each version card in the conversation, each brand analysis, the creative's brief, and **Settings → Usage** (last 7 days, by project and by kind of work). The new-creative form estimates a range from your own past first generations.
|
|
134
|
+
- **Resumed sessions:** Claude Code reports cumulative cost for a resumed session, so each line stores the difference from the previous run of the same session; when it cannot be worked out the cost is left empty and marked as estimated.
|
|
135
|
+
- **Nothing invented:** jobs from before this feature have no figures ("Tracked since …"), and missing values show "—" or are hidden.
|
|
136
|
+
- **Large files:** a video whose bitrate is more than 1.5× a sensible target for its size (for example about 4 Mbps for a 1080×1920 reel at 30 fps) gets a "large" chip with the size, bitrate and target. It is a warning only: it never fails the output.
|
|
137
|
+
|
|
138
|
+
### Export
|
|
139
|
+
From the creative's page, **Export** copies the starred (★) version of each format into a folder of your choice (the desktop app also has **Choose…**). Each row shows the final file name, "★ vN" (and "vM newer" when a newer version exists), and "follows …" for a format that follows another: it is exported as its own file from its primary's ★ version. File names come from a pattern with the variables `{title} {channel} {format} {ratio} {v} {date}` (default `{title}-{format}-v{v}`, e.g. `launch-instagram-reel-9x16-v2.mp4`); each value is turned into a slug, other characters become `-`, the name is limited to 120 characters before the extension, `{date}` is the day shown in the dialog (`YYYY-MM-DD`), Windows device names such as `CON` or `NUL` get a `_` and unknown variables are left out (the dialog flags them). The dialog previews the names as you type and **Save as default** stores the pattern for the workspace. If two formats would get the same name (compared ignoring case) the export is refused: add `{format}`. Export never overwrites anything (if the name exists it appends `-2`, `-3`…) and refuses destinations inside the Motion Studio workspace. A ★ whose file is missing, a symlink or a hard link cannot be exported: the dialog disables it and the export refuses it before copying anything. A ★ composition that is not rendered yet (or whose render is out of date) is rendered first, and then copied. The export runs in the background: the dialog can be closed at any time, the **Export** button of the bar shows its progress ("Exporting 26%"), the activity center lists it with **Cancel**, and when the files are in the folder a message with **Show in Finder** appears (a notification too when the app is in the background). **Cancel export** stops it, and the renders it started. **Show in Finder** selects the file when there is one, or opens the folder. **Resolution** (the format size, or 480p to 2160p with the format's ratio, the short side) and **Quality** (Standard: the earlier encode, VideoToolbox allowed; High, the default: libx264 CRF 18; Max: libx264 CRF 16 from lossless frames) apply to the videos made from a composition; a choice other than the format size at High is rendered for that export alone and never replaces the version's render. Stills, identity and store formats keep their size; with other languages the videos stay at the format size and High. The last choice is remembered.
|
|
140
|
+
|
|
141
|
+
### Adding formats
|
|
142
|
+
If you add formats to the brief of an already generated creative and press **Generate** again, Motion Studio asks the agent to add only the missing compositions, reusing the sources already in `work/` and the same style as the previous version. A format linked to another one (for example TikTok following the Reel) needs no agent: it shares its primary's composition and render. On a creative made before compositions, adding only linked formats still needs no agent: they are copies of their primary's video, as before. Adding any other format, or regenerating, converts every format to a composition with the agent; the brief editor says so before **Save and regenerate**, since that turn costs more tokens.
|
|
143
|
+
|
|
144
|
+
### Variants: duplicate formats, languages, store pack
|
|
145
|
+
**Duplicate and reorder formats.** A creative can hold the same format more than once, for example two Stories or ten screenshots for one iPhone. In a format's view, **Duplicate format** copies the composition of the open version into the next free instance (`<format>@2`, up to `@20`; the first instance keeps the plain format id, so older creatives do not change) and saves it as a new version with no agent. From there the instance is iterated alone: in the chat with the format chips ("Applies to"), or in the editor; the other instances stay byte-identical. Each instance can have a **label** (up to 60 characters, edited in the brief editor), otherwise it shows the format name with "#2". The brief editor also reorders the instances (drag, or Move up and Move down) and removes them: removing keeps the saved versions. The order is the display and export order. Export names add the instance (`instagram-post-1x1-2`) when two files of the same format would collide.
|
|
146
|
+
|
|
147
|
+
**Languages.** In the brief editor (**Languages**) pick the original language and up to 10 languages to translate into (Latin, Cyrillic and Greek scripts, Chinese, Japanese, Korean, Hindi, Thai and more; right-to-left languages are listed but not supported yet). The **Languages** button of the creative then opens the panel:
|
|
148
|
+
- **Translate** starts a translation turn on the open version for the languages and formats you tick. The agent only answers with the translated texts (and, for long languages, a text scale); it never edits the composition, so positions, timings, colors and images stay those of the original. Motion Studio checks the result, and when a text does not fit its box it asks for a shorter one (up to 3 attempts).
|
|
149
|
+
- **Voice options**, per language: **Translate** (Motion Studio makes the translated voice-over with the voice service set in the brief, after a single confirmation for the whole turn listing the calls, characters and service; "Always for this project" applies), **Keep original** (the source voice stays) or **Mute**. Without a voice service or key, only Keep original and Mute are offered.
|
|
150
|
+
- **Review**: per format and language, a table with the original, the editable translation, its state (OK, outside the box, outdated) and a text-size slider (**fit**, 70% to 100%). Saving a translation writes only the overlay of that version and re-runs the checks; it never makes a new version of the composition. A translation that does not fit shows "Outside the box" until you shorten it or lower its size.
|
|
151
|
+
- **Preview**: the player and the editor have a language switcher that shows the translation on the live preview.
|
|
152
|
+
- **Fonts**: for scripts the original font cannot draw (for example Japanese), Motion Studio downloads the Noto Sans of that script from Google Fonts into `assets/fonts/` and adds it to the text clips of that language.
|
|
153
|
+
- **Newer versions**: a later version carries the translations whose source text is unchanged; the others are marked **Outdated**, and **Translate** only redoes what is missing or outdated. An outdated text is never shown as translated: the preview and the render use the original text for that clip and say so.
|
|
154
|
+
- **Render and export per language**: **Render** and **Export** accept the languages; files are named `<format>.<language>.<ext>` (the export pattern gets `{locale}` when the creative has languages). Rendering a translation with open problems or outdated texts asks for confirmation ("Render anyway") and marks it in the manifest.
|
|
155
|
+
|
|
156
|
+
**App store pack.** The **Store pack** is an entry of the formats list. In **New creative** it has its own row above the channel groups (search finds it too); in the brief editor of an existing creative it sits in the App Store group. It opens the assistant in a window, which asks for the app (name and a line), the stores (App Store, Google Play or both), the devices, the number of screenshots with a caption each, an optional preview video and the real app screens (images from Assets, one per screenshot, or an explicit "no app screen"). It makes one format instance per screenshot and device, labelled "N · caption"; **Add to the formats** adds them to the chosen formats, next to any other format; a creative has at most one pack. Once added, the entry reads "Store pack · N screenshots" with **Edit** (the assistant again, with the current values) and **Remove**: Motion Studio recomputes only the pack's own instances (it adds the new ones and removes those no longer needed; versions already made stay in the history) and never touches formats you picked by hand. Store screenshots picked one by one are still normal formats, outside the pack and its export. The agent is told the pack is a series: same grid and style, caption on top, the app screen alone on the background, with no device frame (no Apple or Google artwork or logos; creatives made earlier with a frame keep rendering). In the creative's format column the pack's instances form one **Store pack** group, with App Store and Play Store as subgroups. **Export pack**, in that group's header, writes `<title>-store-pack/` with `AppStore/<device>/NN-<caption>.png`, `AppStore/preview.mp4`, `GooglePlay/<device>/…` and `feature-graphic.png`, plus a `README.txt` with the limits, the upload order and a note that Google Play wants a YouTube link rather than a video file (a language level is added when the creative has languages). Before writing each file Motion Studio checks the exact size, PNG or JPEG, no alpha channel (screenshots are flattened onto the background), and for the App Store preview 15 to 30 s, 30 fps, H.264 and an audio track (a silent one is added when the composition has none). A file that fails is skipped with its reason; the rest of the pack is exported. Store formats of the brief that are not part of the pack are not exported with it: the export result, the dialog and the pack's `README.txt` list them. Creatives made with the old **App store pack** card keep working, and the API route `POST …/creatives/store-pack` stays. The limits were read from Apple's and Google's public pages on 2026-10-14. Checked live: see `docs/superpowers/notes/2026-10-14-phase12-live-checks.md`.
|
|
157
|
+
|
|
158
|
+
### Creative variants
|
|
159
|
+
A **variant** is a different creative direction for the same brief, with all the formats. A creative can have 1 to 5 variants (A, B, C…); they share the brief and live as one group in the creatives list ("N variants").
|
|
160
|
+
|
|
161
|
+
**Creating them.** In **New creative** the **Variants** field (1 to 5) makes the group. From 2 up, each variant has an optional direction (a line, for example "minimal, lots of white space"). Directions you leave empty are proposed by a short agent turn that creates no files and makes them clearly different from each other; yours always win and can be edited later (the change applies from that variant's next turn). Later you can add one with **+ Variant** in the creative's ⋯ menu: it starts from the brief or from the latest version of a variant you pick. At most 2 variants work at the same time; the others wait in the queue with the reason shown.
|
|
162
|
+
|
|
163
|
+
**Working on them.** The chat has a selector **Variant · A · B · C · All**. **All** sends the same request to every active variant (pinned comments are not sent to All). With paid services on, one cost confirmation covers the whole batch and the variants only start after you answer. In the canvas, **Group by** switches between format and variant.
|
|
164
|
+
|
|
165
|
+
**Choosing.** The ★ works across variants: for each format, star the version you want from any variant. **Final selection** lists what will be exported (and what is missing); with no ★ the default is the first active variant. **Export** of a group writes the starred file of each format, rendering first what needs it, with the usual file names. **Export this variant** (in the variant's row menu) exports only that variant.
|
|
166
|
+
|
|
167
|
+
**Discard and restore.** **Discard** removes a variant from the choice (you cannot discard the last active one, nor one that is working); its ★ picks fall back to the default. **Show discarded** lists them greyed out. A discarded variant is read-only: no turns, edits, translations or renders until you **Restore** it (an old ★ is not restored).
|
|
168
|
+
|
|
169
|
+
**Identity.** A logo from scratch with several concepts makes variants: each one has its own logo, favicon and app icon, consistent with each other. Concepts of creatives made before this version stay valid as they were.
|
|
170
|
+
|
|
171
|
+
**Limits.** A group cannot be deleted or renamed as a whole (the app has no delete or rename for creatives either). The holds that wait for the cost confirmation live in memory only: if the app restarts in the meantime, the waiting requests are not lost but are not started either, and you start them with **Run next** in the queue. Checked live: see `docs/superpowers/notes/2026-10-16-phase15-live-checks.md`.
|
|
172
|
+
|
|
173
|
+
### Identity formats: logo, favicon, app icon, social preview
|
|
174
|
+
The **Identity** group of the formats list has four formats:
|
|
175
|
+
|
|
176
|
+
| Format | What the render writes, in `outputs/vN/<format>/` next to the main image |
|
|
177
|
+
|---|---|
|
|
178
|
+
| **Logo** (`identity-logo`) | `logo.svg` and `logo.png` (1024 px on the long side, transparent) |
|
|
179
|
+
| **Website favicon** (`identity-favicon`) | `favicon.ico` (16, 32 and 48 px), `favicon.svg`, `apple-touch-icon.png` (180, opaque), `icon-192.png`, `icon-512.png`, `icon-512-maskable.png` (opaque, the drawing at 72%), `site.webmanifest` (theme colour from the brand's primary colour) and `head-snippet.html` with the tags to paste in your page's `<head>` |
|
|
180
|
+
| **App icon** (`identity-app-icon`) | iOS: `AppIcon-1024.png` (opaque) and `Contents.json`. Android: `ic_launcher-playstore.png` (512, opaque, for the Play Console), `ic_launcher_foreground.png` (432, transparent, the drawing at 66/108), `ic_launcher_background.png` (432, a solid colour), `ic_launcher.xml` (adaptive icon) and `mipmap-mdpi` … `mipmap-xxxhdpi/ic_launcher.png` (48 to 192 px) |
|
|
181
|
+
| **Social preview (Open Graph)** (`og-image-1200x630`) | a normal image, 1200×630, PNG or JPEG up to 5 MB; no set |
|
|
182
|
+
|
|
183
|
+
- **Vector first.** Logo, favicon and app icon are still compositions made of one full-frame SVG. Each SVG passes a strict check before it becomes an output: no scripts, `foreignObject`, event attributes, external references, embedded images or URLs in styles; at most 512 KB; text should be converted to paths (a `<text>` element is allowed with a warning, since its look depends on the installed fonts). A refused SVG goes back to the agent with the reasons; in the app you see a short summary, the English reasons are behind **Details**. Every PNG is drawn from the SVG at its exact size (never a big PNG scaled down). For the favicon and the app icon the agent may add a simplified `small` drawing, used up to 48 px. The icons that must have no alpha channel are flattened onto the composition's background when it is opaque, else the brand's primary colour, else white (with a warning). A drawing that leaves the safe zone of the maskable icon or of the Android foreground gets a warning; it never blocks.
|
|
184
|
+
- **Start.** Picking an identity format shows **Start**: **From the brand logo** (the default when the brand has a logo: the agent adapts it, with margins and a simplified version for small sizes, without redrawing it; a raster logo is redrawn as a faithful SVG and the conversation says so) or **From scratch** (a name or initials, optional style keywords, brand or free colours). A logo from scratch makes **3 concepts** by default (1 to 5), as separate instances `identity-logo`, `identity-logo@2`, `identity-logo@3`, so you can compare them and keep one (a creative made through the API with only `identity-logo` and a number of concepts gets the same instances); in the brief editor of an existing creative use **Add another** as for any format. The agent draws SVG: no paid image generation is used for logos.
|
|
185
|
+
- **View.** The page of a logo, a favicon or an app icon switches between **Edit** (the editor), **Image** and **Sizes**: every file of the set at 1:1 and zoomed, the set's warnings, and the icon in context (browser tab at 16 and 32 px, iOS rounded corners, Android circle with the safe zone). A logo is shown on light and on dark side by side. Its status reads "Set ready · N files" once rendered.
|
|
186
|
+
- **Use as brand logo** ("Usa come logo del brand" in Italian), on a version of a logo or a favicon: after a confirmation (main logo or icon) Motion Studio checks the SVG again, copies it into `assets/brand/` and adds it to the brand kit at once, with the source `creative:<slug>/vN`; there is no proposal to approve. If the kit already has a logo of the chosen kind, that one is demoted (main logo to secondary, icon to other), so the kit keeps one main logo and one icon. The app icon set has no SVG, so it does not offer it.
|
|
187
|
+
- **Where the files go.** Web: the favicon files go in your site's root (the paths in `head-snippet.html` and `site.webmanifest` start with `/`). iOS: `AppIcon-1024.png` and `Contents.json` make the `AppIcon.appiconset` folder of your asset catalog (single-size format). Android: `ic_launcher.xml` goes in `res/mipmap-anydpi-v26/`, `ic_launcher_foreground.png` and `ic_launcher_background.png` in `res/mipmap-xxxhdpi/`, each `mipmap-<density>/ic_launcher.png` in the `res/` folder of the same name (the icon for Android before 8), and `ic_launcher-playstore.png` is uploaded to the Play Console.
|
|
188
|
+
- **Export.** The export of the creative copies each set as a folder named like the file (with its `mipmap-*` subfolders), next to the main image.
|
|
189
|
+
|
|
190
|
+
### Editing a composition directly
|
|
191
|
+
Opening a format (from the creative's canvas, **Open editor** on a board, a link or the breadcrumb) opens its editor directly: the format's page is the editor, with no separate preview (old `…/edit` links open the same page). A format that follows another opens its primary's composition, and the header says which formats share it. The editor has the scenes (or, for a still, the layers) on the left, the stage with the live preview in the middle, the timeline below a video, and **Chat** and **Inspector** as tabs on the right: the chat is shown first, selecting a clip shows its inspector, and a comment brings the chat back. The bar keeps what the format's page always had: **All formats**, the format's ★ badge with its versions (view, ★, compare), **Versions**, **Languages**, **Duplicate format** and **Export** (a render or an export in progress shows inside it, at the same size). A format version without a composition (made before compositions), or a format with no version yet, shows its preview instead.
|
|
192
|
+
|
|
193
|
+
- **Read-only, with the reason on the stage and in the inspector:** an earlier version (chosen in the ★ badge, with **View vN** on a version in the chat, or a ★ set by hand on an earlier version) opens in the same editor without editing, and **Edit vN** goes back to the latest one, where your unsaved edits are still waiting; so do a discarded variant and a creative while an agent turn or a render runs.
|
|
194
|
+
- **Playhead:** clicking a clip on the timeline moves the playhead to the point clicked (the clip's start if that point is outside it), without starting or stopping playback; choosing a clip in **In this scene** goes to its start when the playhead is elsewhere. Drag the playhead by its handle on the ruler, or drag anywhere on the ruler; it moves frame by frame.
|
|
195
|
+
|
|
196
|
+
- **What you can change:**
|
|
197
|
+
- a text's content, font (one of the composition's, or a brand kit font that has a file: the file is added to the composition), weight (only those declared for the font), size, colour and alignment;
|
|
198
|
+
- position, size, rotation and opacity: with the handles on the stage (with snapping to guides, the safe area and the frame), with the inspector's numbers, or by nudging with Alt+arrows;
|
|
199
|
+
- timing: trim and move clips on the timeline (snapping, Alt disables it), with **Ripple** to push the later scenes when a clip grows; the scene order, when every clip lies in one scene;
|
|
200
|
+
- the entry and exit animation presets;
|
|
201
|
+
- an image, vector image, video or audio file, replaced from the project's Assets or uploaded there (a video shorter than the clip says so and offers "Fit clip to video"); a video's trim start and volume; an audio clip's volume and fades;
|
|
202
|
+
- an HTML layer's parameters;
|
|
203
|
+
- hiding and locking clips and tracks, and muting audio tracks.
|
|
204
|
+
|
|
205
|
+
New clips, layers, custom animations, effects and new scenes stay with the agent: ask for them in the chat.
|
|
206
|
+
- **Undo and redo:** ⌘Z and ⇧⌘Z (Ctrl on Windows and Linux), never while you type in a field. A drag, or a burst of typing, is one step.
|
|
207
|
+
- **Save:** the pill "Draft · N edits" offers **Save as vN** and **Discard**.
|
|
208
|
+
- Saving makes a normal new version (`origin: editor`) whose request is a summary of the edits, e.g. "Direct edits: Title text, font Schibsted Grotesk".
|
|
209
|
+
- No agent runs and no tokens are used: the usage ledger gets no line.
|
|
210
|
+
- The other formats are carried; formats that follow the edited one are checked again (one that can no longer follow, e.g. over its maximum duration, is unlinked and the chat says so).
|
|
211
|
+
- Renders are carried only for unchanged compositions, and the storyboards are made again.
|
|
212
|
+
- **The draft is never lost by surprise:**
|
|
213
|
+
- leaving the editor with unsaved edits (back, another format or version, a link, a chat message) asks "Save · Discard · Keep editing";
|
|
214
|
+
- reloading or closing the page asks the browser's (or the app's) confirmation (in the app too for ⌘R and View → Reload), and a confirmed reload does lose the draft.
|
|
215
|
+
- **While someone else works:**
|
|
216
|
+
- while an agent turn or a render of the creative runs, Save is not available (the core refuses it too);
|
|
217
|
+
- if a turn or another save changed the same format meanwhile, Save is refused and the draft is kept; **Reopen on the latest version** discards it after a confirmation;
|
|
218
|
+
- a turn on another format does not block the save.
|
|
219
|
+
- **Comments for the agent:** a comment can be on a spot of the frame (**Comment a spot**, the pin in the stage's toolbar, or `P`, then click the spot), on a clip (**Comment on clip**, or `C` with a clip selected) or on a time range (**Comment on range**, or `R`, or Shift+drag on the ruler). The chip in the chat names the clip or the range, and the agent's prompt carries the clip's id and name or the range, with a frame (two for a range). A custom animation in the inspector has **Ask the agent**: it adds a chip about that clip's entry or exit animation and puts the cursor in the chat, so you only type what you want changed.
|
|
220
|
+
|
|
221
|
+
#### Editing an SVG
|
|
222
|
+
Select an `svg` clip (a logo, a favicon, an app icon, or a vector image in an image or a video): the inspector's **SVG content** section edits the inside of its file, with no agent turn.
|
|
223
|
+
|
|
224
|
+
- **Colours:** every distinct colour of the file (fills, strokes, gradient stops, `style` and `<style>`), with how many times it is used. Changing one replaces every occurrence, however it is written (`#fff`, `white`, `rgb(255,255,255)`); the brand palette is among the swatches.
|
|
225
|
+
- **Texts:** the content of each `<text>`, as plain text (font, size and position stay).
|
|
226
|
+
- **Elements:** the groups and shapes, named after their `id` or `<title>`, else after their kind ("Path 5"). Pick one in the list or click it on the stage to outline it and change its own fill and stroke; the eye hides or shows it.
|
|
227
|
+
- Each change is checked by Motion Studio with the same strict SVG check as identity formats, then the clip points at the new file: undo and redo, the draft pill and **Save as vN** work as for any other edit. Another clip using the same file keeps the original.
|
|
228
|
+
- An SVG the check refuses (for example one without a `viewBox`, or with a script) says why and cannot be edited here. A logo's small version and its negative for dark backgrounds are layers of their own ("Small version", "Negative for dark backgrounds"): they do not change with the main logo. After saving an identity format, **Export** (or the agent) renders its PNGs and icons again.
|
|
229
|
+
|
|
230
|
+
A duration changed in the editor is your choice: the version stays complete even when it is far from the brief's duration, and the format's bar shows the brief's next to it ("7 s · brief 6 s (direct edit)"). The next agent turn is told to keep the current duration unless you ask for another one; update the brief's duration yourself if you want it to match.
|
|
231
|
+
|
|
232
|
+
Current limits: signed desktop builds depend on the maintainers' certificates (see Release). On Ubuntu 24.04 and later the AppImage may fail to start because the system restricts unprivileged user namespaces (AppArmor, `kernel.apparmor_restrict_unprivileged_userns`), which Chromium's sandbox needs: you need an AppArmor profile for the app (or, at your own risk, disable that restriction); avoid `--no-sandbox`, which removes the window's isolation.
|
|
233
|
+
|
|
234
|
+
## Security
|
|
235
|
+
Motion Studio runs the agent in a sandbox, keeps your API keys away from it and isolates every composition it renders. The full model, and how to report a vulnerability, are in [SECURITY.md](https://github.com/Aleloca/motion-studio/blob/main/SECURITY.md).
|
|
236
|
+
|
|
237
|
+
## Providers
|
|
238
|
+
| Tool | Provider | Key |
|
|
239
|
+
|---|---|---|
|
|
240
|
+
| Images | OpenAI gpt-image-2 | `OPENAI_API_KEY` |
|
|
241
|
+
| Voice-over | OpenAI TTS, ElevenLabs | `OPENAI_API_KEY`, `ELEVENLABS_API_KEY` |
|
|
242
|
+
| Sound effects, music, voice list | ElevenLabs | `ELEVENLABS_API_KEY` |
|
|
243
|
+
| Stock photos and videos | Pexels, Unsplash | `PEXELS_API_KEY`, `UNSPLASH_ACCESS_KEY` |
|
|
244
|
+
| Fonts | Google Fonts | none |
|
|
245
|
+
| Speech transcription (video analysis, optional) | OpenAI `whisper-1`, ElevenLabs Scribe v2, or Whisper on the Mac | `OPENAI_API_KEY`, `ELEVENLABS_API_KEY`, none |
|
|
246
|
+
|
|
247
|
+
Stock assets keep the attribution required by Pexels and Unsplash (`attribution` field in `assets/assets.json`). Details in [docs/providers.md](https://github.com/Aleloca/motion-studio/blob/main/docs/providers.md).
|
|
248
|
+
|
|
249
|
+
- **Key checks**: in **Settings → Paid services**, saving a key (and the **Test** button of each row) tries it with free, read-only requests and shows what it can do, per capability: works, missing (with the permission to enable on the provider's site), not verified (only a paid generation can tell) or error (network, timeout, 5xx, rate limit). A key the provider does not know shows "The service does not accept this key". The results stay in Motion Studio's memory: never on disk, never with the key.
|
|
250
|
+
- **Services per creative**: the brief (in **New creative → More options**, in the brief editor, and behind the **Services** chip in the chat composer) says which external services the agent may use, per kind: images (Auto, OpenAI, Stock only, Off), voice (Auto, OpenAI, ElevenLabs, Off), music and sound effects (Auto, ElevenLabs, Off). Auto is the behavior from before. Motion Studio enforces the choice in every agent tool, before any confirmation or request: a kind turned off is refused, a chosen provider is never swapped for another, and one without its key is refused. The agent is told the choice and tells you to use the Services button when you ask for something that is off. A provider without its key, or whose key the check found lacking the permission (for example ElevenLabs without `text_to_speech` for voice), cannot be chosen, and the reason is shown. A change applies from the next message. **Generate** in Assets is not affected (it is your own action).
|
|
251
|
+
|
|
252
|
+
## Brand, assets and linked codebases
|
|
253
|
+
- **Brand**: palette, fonts, logos, tone, dos and don'ts, photographic style; each entry shows where it comes from (manual, website, image, codebase: "from codebase · `src/theme.css`"). The prose guidelines are in `brand/guidelines.md`.
|
|
254
|
+
- **Fonts**: **Typography → + Add from Google Fonts** searches the Google Fonts catalog, previews each family in its own face (served by Motion Studio, the page never loads Google), and adds the weights you tick (and italic) to `assets/fonts/` with origin `google-fonts`; the brand font is linked to its file. Adding a family that is already in the brand adds the new weights to it. **+ Add manually** still links your own files.
|
|
255
|
+
- **Sound**: the Brand page's **Sound** section holds the genre, the energy, the instruments to use and to avoid, and the brand voice, picked with the provider's key set: the ElevenLabs voices of the account, with previews, or OpenAI's fixed list (no previews: listening would be a paid call). The agent follows it when it chooses music, sound effects and voices.
|
|
256
|
+
- **Brand analysis**: add one or more websites (and reference images with "Use for brand analysis") and press **Analyze brand**. The analysis shows its phases (reading, fonts, colors, logos, tone, writing). Fonts come first: the `inspect_site_fonts` tool reads the site's HTML and stylesheets and tells which fonts it uses and where they come from; Google fonts are then fetched into `assets/fonts/`, self-hosted ones downloaded (with "license unknown" in the summary), Adobe and system fonts only named. The agent visits the sites, downloads useful assets into `assets/` and proposes changes: you apply them entry by entry, and manually entered entries are never removed. The agent works on copies: the brand kit, guidelines, sources and asset and reference metadata cannot be modified by the editing tools and, if the agent changes them anyway, Motion Studio reverts the change and reports it.
|
|
257
|
+
- **Brand from a codebase**: on the Brand page (the empty state, or **Add a source** in the Sources panel) pick **From a codebase** and choose the folder of your app or site (the desktop app has a folder picker; you can also type the path, and the codebases already linked to the project are offered as suggestions), with an optional note. Up to 5 codebases per brand, mixed freely with websites and images. The folder is checked like a linked codebase: an absolute path, never the disk root, your home, the project or the workspace (or a folder around them), never a codebase inside another one, Motion Studio's configuration folder or a private folder of your home (keys and credentials, `.config`, or anything under `~/Library`, iCloud Drive included). The agent then looks, in this order, for tokens and themes (`tailwind.config.*`, CSS variables, design-token files, Android `colors.xml`, iOS colour sets), fonts, logos and icons (`public/`, `assets/`, favicons, `AppIcon.appiconset`, `mipmap-*`, `manifest.json`) and the tone (README, marketing texts, store metadata). Logos and local font files are copied into `assets/brand/` or `assets/fonts/` with the `import_codebase_file` tool (origin `codebase`, at most 40 copies of up to 50 MB per analysis); Google fonts are fetched as for websites.
|
|
258
|
+
- **Read-only.** The codebase is added to the agent read-only: editing tools are denied on it and, with the sandbox, the system blocks writes too. Motion Studio takes a snapshot of the folder before and after the analysis (git status with content hashes, ignored files and the git configuration included); if anything changed, or if the check cannot run after the analysis, the proposal is discarded and the error names the folder (the changed files are in the analysis notes). A folder that is not a git repository cannot be checked: the analysis says so. These checks only read the repository: git runs without optional locks, so it never rewrites your `.git/index`.
|
|
259
|
+
- **No commands, no content search.** An analysis with a codebase has no `Bash` and no `Grep`, with or without the sandbox: the agent lists files with `Glob` and opens them by name with `Read`, only inside the codebase folders. Command and search requests are refused without asking you. `WebFetch` is there only when the analysis also has a website.
|
|
260
|
+
- **Secret files are never opened.** `.env*`, `*.pem`, `*.key`, `*.p12`, `*.keystore`, `*.jks`, `id_rsa*`, `.npmrc`, `.netrc`, `.pypirc`, `credentials*`, `secrets*`, `*.mobileprovision`, `google-services.json`, `GoogleService-Info.plist` and `.git/` are denied to the agent's file tools, in any letter case, and also by the sandbox. The import tool refuses them too. Without the sandbox the analysis still runs, with a note that the exclusion holds only as a rule of the agent's tools, not as a barrier of the operating system. See the limits below for folder names that break these rules.
|
|
261
|
+
- **Assets and references**: upload files by dragging them, filter by type and origin, add descriptions and tags (also with **Describe with the agent**). Audio and video play in the asset's details (audio with its waveform); durations, sizes and dimensions are read from the files. **Rename file** renames an asset and updates the brand kit and the briefs that use it. Sort by date, name, type or duration ("Longest first").
|
|
262
|
+
- **Generate** (Assets): makes an image (OpenAI), a voice-over (OpenAI or ElevenLabs), a sound effect or a music track (ElevenLabs) straight into Assets, after the usual paid confirmation. A tab is available only when its provider's key is set.
|
|
263
|
+
- **Linked codebases** (project or single-creative Settings): folders on your computer that the agent can read; editing tools are blocked on every turn and, if a folder is a git repository and shows changes after a turn, the conversation reports it. You cannot link the project or workspace folder, a folder that contains them, or a folder inside them, nor a folder whose path has glob characters (see **Current agent limits**).
|
|
264
|
+
|
|
265
|
+
With the sandbox on, interpreters cannot write into linked codebases. Without the sandbox, the rules block only the agent's editing tools: interpreters (for example node or python) could still write into the folder. Motion Studio detects and reports changes only in folders that are git repositories; for the others, changes would not be detected. Prefer linking git repositories with their changes already committed.
|
|
266
|
+
|
|
267
|
+
General advice: analyze only websites you trust.
|
|
268
|
+
|
|
269
|
+
## Current agent limits
|
|
270
|
+
- **Codebase folder names with glob characters are refused:** with Claude Code 2.1.300 the agent's file rules do not match when a codebase path has parentheses `(` `)` together with square brackets or braces (for example `app (old) [v2]`; checked live, `docs/superpowers/notes/2026-10-15-phase14-live-checks.md`), so the secret-file and read-only rules would not hold. Motion Studio therefore refuses to link, as a brand source or as a project or creative codebase, any folder whose path or real path contains `(` `)` `[` `]` `{` `}` `*` `?` or (on macOS and Linux) `\`. On Windows `\` is the path separator and is not refused; there the other characters are checked in each folder name, and the escaping of the agent's rules has not been verified on Windows. Rename the folder (or the parent folder that has them); a symbolic link with a plain name does not help, because the folder's real path is checked too. Codebases linked before this check are left out of turns, console runs and brand analyses, with a note in the conversation or the job.
|
|
271
|
+
- **Workspace and project folder names with glob characters are refused too:** the rules that keep the agent away from Motion Studio's own files (`CLAUDE.md`, `.mcp.json`, `.claude`, `.studio`, the logs) are path rules as well, so Motion Studio does not let you choose a workspace whose path or real path contains `(` `)` `[` `]` `{` `}` `*` `?` or (on macOS and Linux) `\`, nor create a project in one. On Windows the same rule-escaping has not been verified. For a workspace or project that already has such a path, every agent run (creative turns, the project console, brand analysis, asset descriptions, translations) is refused before it starts with a message naming the path, and the Doctor shows a warning; browsing, rendering and exporting still work. Rename the folder (or the parent folder that has them) and choose the workspace again. If your home folder or Motion Studio's configuration folder has such characters, nothing is refused, but the Doctor warns that the agent's deny rules on them (tokens, secret files) may not apply.
|
|
272
|
+
- **Identity formats in other languages:** a translated render of a logo, favicon or app icon makes only the main image, never the file set.
|
|
273
|
+
- **HTML layers hidden only by a clip:** in a render, an HTML layer that is invisible is skipped (not waited for) while it is out of frame or while its own `clip-path` leaves no area: an `inset()` whose opposite sides meet, a `circle()` or `ellipse()` with radius 0, or a `polygon()` collapsed onto one axis, in px or %. Other clips are still waited for: the clip of a parent other than the stage, and clip values with `calc()`, `var()`, `path()`, `url()` or `shape()`; if Chrome stalls on such a layer, the render fails after about 5 seconds with an error and a diagnosis instead of hanging.
|
|
274
|
+
- **Project console:** with the sandbox on, commands run isolated; outside the sandbox the agent can modify the project's files, but requests that need an approval appear in the interface (see [Security](https://github.com/Aleloca/motion-studio/blob/main/SECURITY.md)).
|
|
275
|
+
- **Image, voice, sound, music and stock providers:** they are described in [docs/providers.md](https://github.com/Aleloca/motion-studio/blob/main/docs/providers.md) but have not been verified live with real keys (tests use simulated responses). The key checks were tried live only against a simulated endpoint; Google Fonts and the site-font reading of the brand analysis were tried live (see `docs/superpowers/notes/2026-10-13-phase11-live-checks.md`).
|
|
276
|
+
|
|
277
|
+
## Development
|
|
278
|
+
```bash
|
|
279
|
+
pnpm install
|
|
280
|
+
pnpm dev # core (tsx watch, port 4317) + web (Vite, port 5173 with /api proxy); the core prints the link with the access code
|
|
281
|
+
pnpm test # all tests (they use a fake `claude`, no quota used)
|
|
282
|
+
npx vitest run packages/core/test/server.test.ts # a single file (from the repository root)
|
|
283
|
+
pnpm typecheck
|
|
284
|
+
pnpm check:i18n # flags Italian text left in the code outside the language catalogs
|
|
285
|
+
pnpm build # web + npm package bundle (apps/cli/dist)
|
|
286
|
+
pnpm motion-studio # build + start the local package
|
|
287
|
+
|
|
288
|
+
pnpm --filter motion-studio-desktop dev # Electron app in development (first: `pnpm --filter @motion-studio/web build && pnpm --filter motion-studio-desktop build`)
|
|
289
|
+
pnpm --filter motion-studio-desktop smoke # trial start of the app with a temporary configuration
|
|
290
|
+
pnpm --filter motion-studio-desktop dist:dir # unpackaged app in apps/desktop/release (only this computer's architecture)
|
|
291
|
+
pnpm --filter motion-studio-desktop dist:mac # local installer (dist:win, dist:linux; no argument: the current platform)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Useful variables:
|
|
295
|
+
- `MOTION_STUDIO_CONFIG_DIR` — where to store the app configuration (workspace path).
|
|
296
|
+
- `MOTION_STUDIO_CLAUDE_COMMAND` — the agent command as a JSON array, e.g. `["node","/path/fake-claude.mjs"]`.
|
|
297
|
+
|
|
298
|
+
To contribute, see [CONTRIBUTING.md](https://github.com/Aleloca/motion-studio/blob/main/CONTRIBUTING.md).
|
|
299
|
+
|
|
300
|
+
## Release
|
|
301
|
+
For maintainers. Align the version in `apps/desktop/package.json` and `apps/cli/package.json`, then create and push the matching `vX.Y.Z` tag: every job of the `.github/workflows/release.yml` workflow stops immediately if the tag does not match both versions. The workflow builds the desktop app on macOS (`arm64` and `x64`), Windows and Linux and creates a **draft** GitHub release (on macOS it first builds whisper.cpp's `whisper-cli` for both architectures with `scripts/build-whisper.sh`, verified on every release; it can also be run on a Mac into `apps/desktop/vendor/whisper/`, which the development app then uses) with the installers (to be published by hand); in parallel it publishes `@motion-studio/cli` to npm with npm Trusted Publishing (no token: the package's npm settings trust this repository's `release.yml`; provenance attached) (a failure in a desktop build does not block it). CI (`ci.yml`) runs the type check, the i18n check and the tests on every push to `main` and on pull requests.
|
|
302
|
+
|
|
303
|
+
Repository secrets used by the workflow (all optional: without them the build is unsigned or npm is skipped):
|
|
304
|
+
- macOS (signing and notarization): `MAC_CERT_P12_BASE64`, `MAC_CERT_PASSWORD`, `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`. Without `MAC_CERT_P12_BASE64` signing is off; notarization runs only if the certificate (`MAC_CERT_P12_BASE64`) and all three `APPLE_*` secrets are present, otherwise it is explicitly off.
|
|
305
|
+
- Windows: `WIN_CERT_PFX_BASE64`, `WIN_CERT_PASSWORD`.
|
|
306
|
+
- npm: none (Trusted Publishing).
|
|
307
|
+
- `GITHUB_TOKEN` is provided by GitHub Actions.
|
|
308
|
+
|
|
309
|
+
## License
|
|
310
|
+
MIT
|