# resaiz

Make and change ad statics and motion in every size from the command line. The `resaiz` command drives [resaiz](https://www.resaiz.com): you design one master, the engine lays out every other size (970x250, 300x250, story, feed, the whole catalogue), and renders come from the resaiz editor itself, pixel for pixel. It is built for Claude Code, and works just as well by hand.

## Install today (before the npm package is out)

macOS, Linux, WSL: `curl -fsSL https://www.resaiz.com/install.sh | sh`

Windows PowerShell, or anywhere with npm: `npm install -g https://www.resaiz.com/cli/resaiz-0.1.1.tgz`

Node.js 20 or newer. The sha256 of the package is in https://www.resaiz.com/cli/latest.json and install.sh checks it. Then `resaiz login`. The rest of this file is the package README.

## Install

```
npm install -g resaiz
resaiz --version
```

Node 20 or newer. The pixel commands (render, export, sheet, the text fit, pixel QA, motion video) use the Google Chrome you already have, through Playwright; without Chrome they say so and exit 4 (`npx playwright install chrome` also works).

## Sign in

```
resaiz login
```

Your browser opens resaiz. Sign in there if you are not already, check the computer name, and click Authorize. The terminal says `signed in as you@company.com`. This machine gets its own session: signing out here leaves the browser signed in, and the other way round. If the browser does not open, the CLI prints the link; open it in a browser on the same machine. `--no-open` only prints the link. The CLI waits 5 minutes, then gives up (exit 3).

On a machine without a browser (a server, a remote shell), sign in by email instead:

```
resaiz login --email you@company.com
```

resaiz sends you its usual sign in email. Paste the code from it, or the whole link (copy it; do not click it), at the prompt. `resaiz login you@company.com` (the email alone) works too. The CLI signs in existing accounts only: sign up at https://www.resaiz.com first.

- The session lives in `~/.config/resaiz/session.json` (mode 600, in a folder made with mode 700; `RESAIZ_CONFIG_DIR` moves it). No command prints a token.
- For scripts and agents (`--json` never prompts): `resaiz login --json` prints `{ "opened": true, "url": "..." }` on one line, waits for Authorize, then prints `{ "signedIn": true, "email": "...", "userId": "...", "workspace": {...}, "workspaces": [...], "can": {...} }` on a second line. By email: `resaiz login --email you@company.com --json` sends the email, then `resaiz login --email you@company.com --code 123456 --json` or `--link '<the link>'` finishes.
- After a successful sign in the CLI makes one anonymous count call (`GET https://www.resaiz.com/api/event?name=cli_login`, no token, nothing about you or your machine; the site keeps only a daily total). The install script does the same once with `cli_install`. Set `RESAIZ_NO_COUNT=1` to turn both off.
- `resaiz whoami` shows who you are and what you may do: the account, then one line per workspace with its kind, your role, what that role may do (create, edit, trash, export, invite, approve), its plan and how many documents it holds, the active one marked `*`. `resaiz login` prints the same lines after "signed in as". With one workspace it is active on its own; with several, `resaiz cloud use <workspaceId>` picks one (it is remembered across sign ins). `resaiz logout` signs this machine out and deletes the file.
- A command your role does not allow says so by name, for example `your role in Team is viewer; trashing needs editor or above`.

Editing and rendering never need an account. Only the `cloud` commands do (exit 3 without a session).

## The Claude Code skill

```
resaiz skill install
```

copies the skill to `~/.claude/skills/resaiz`. Then, in Claude Code, say for example "make a 970x250 banner for our spring sale and render it". The skill teaches Claude the folder, the loop and the QA codes.

## MCP

`resaiz mcp` is a Model Context Protocol server on stdio, for Claude Desktop and any MCP client (Claude Code can use it too, though there the shell CLI and the skill are enough). `--dir` is the folder the tools work on by default; every tool also takes a `dir`. Pixel tools need Google Chrome, as the CLI does.

Claude Desktop, in `claude_desktop_config.json`:

```
{
  "mcpServers": {
    "resaiz": { "command": "resaiz", "args": ["mcp", "--dir", "/path/to/folder"] }
  }
}
```

Claude Code:

```
claude mcp add resaiz -- resaiz mcp --dir /path/to/folder
```

Tools:

- `resaiz_run`: any CLI command, `{ args: ["size","add","970x250"], dir? }`; `--json` and `--dir` are added, the command's JSON comes back, an error comes back as `isError` with the CLI's error object. `mcp` itself is refused.
- `resaiz_info`, `resaiz_new`, `resaiz_text_set`, `resaiz_el_add`, `resaiz_size_add`, `resaiz_copydeck_apply` (rows inline: `{ rows: [{ id, locale, value, short? }] }`), `resaiz_cloud_pull`, `resaiz_cloud_push`, `resaiz_note`, `resaiz_schema`: typed tools over the same commands.
- `resaiz_render` (`{ boards?, locale?, longEdge? }`) and `resaiz_sheet`: the pictures come back inline as PNG images (1280 px on the long edge by default), with the full size file's path in a text part, so the agent sees what it made.

Resources: `resaiz://skill` (the skill) and `resaiz://docs/cli` (this readme). Prompt: `resaiz_operator` (the loop and what to do when the engine gets a size wrong). Commands run inside the server process; nothing they print reaches the protocol stream. Relative paths in `args` resolve against the folder. Sign in stays the person's step: `resaiz login` in a terminal.

## The folder

A document is a folder:

- `resaiz.json`: the document exactly as the editor saves it, plus a `_cli` block (the kind, the name, and after a pull or push the cloud id, the workspace, the stamp and the picture map).
- `assets/`: pictures and font files, `assets/<hash>_<name>`.
- `out/`: renders, exports and sheets.

Every command works on the folder you are in, or on `--dir <folder>`.

## The loop

```
resaiz new --size 970x250 --name "Spring sale"
resaiz el add --role headline --text "Spring sale"
resaiz pic add ./product.png --role product
resaiz size add 300x250 728x90 story
resaiz hug                     # the editor's text box fit
resaiz qa                      # the fast inner loop: copy collisions from the font files, no browser
resaiz check                   # the operator's eyes: overlap, outside and cut from the pixels, a marked sheet
resaiz render                  # every size into out/, then look at the PNGs
resaiz export                  # the Export drawer's zip
resaiz cloud push              # into your files; open it in the editor
```

## check

`resaiz check [boards...|all|on] [--locale L | --locales es,en,pt] [--out DIR] [--no-render]` renders the boards in the browser and reports only what is objectively broken, from the pixels: two elements whose ink meets (`overlap`, with the box and its area), anything leaving its board (`outside`, px past which edge), and copy under a platform band that will cover it (`cut`, which band). No scores, no warnings, no legibility opinions. It writes every board's PNG and a sheet per locale (`check-sheet.png`, or `check-sheet-<locale>.png` when several locales run; the boards in a row, every fact drawn in red or amber) into `out/check/`; `--no-render` only prints the facts. Exit 0 clean, 1 when there is a fact, 4 without a browser. For an operator it replaces `qa --pixels` as the last word before calling a document done. A fact the design means: `resaiz check --accept <key>` (the key is in `--json`) marks it intended and it is not reported again.

Fonts: `resaiz qa`, `hug --no-browser` and `el add` measure copy with the document's own font files (Google Fonts, this computer's fonts, the document's font files, the Font Helper). `resaiz font add <file>` keeps a font file in the document and `resaiz font add --local "<family>"` embeds an installed one, so the copy renders the same on every computer and in the cloud copy (check the font's licence allows embedding); `font ls` and `font rm <family>` list and drop them.

Motion: `resaiz new --motion`, `layer add text|shape|photo`, `layer set` (`--fill` takes a colour or a paint), `duration`, `background` (`paint <paint>` too), `motion render`, `motion export`.

## Cloud

```
resaiz cloud ls [--motion | --all] [--trash] [--workspace id]
resaiz cloud pull <id> [--force]
resaiz cloud push [--new] [--name N] [--force] [--workspace id] [--thumbnail | --no-thumbnail]   # also draws the file card picture (thumbnail: uploaded | skipped | off); --thumbnail draws it even when nothing changed
resaiz cloud open [--browser]
resaiz cloud use <workspaceId>
resaiz cloud rm <id>
resaiz cloud restore <id>
```

- `rm` moves a document to the Trash the way the app does (`deleted_at`, `deleted_by`); `restore` brings it back; `ls --trash` lists the Trash. The CLI calls no `/api` route, so no audit row is written for either.

- `pull` writes the document into the folder and its pictures into `assets/`. It refuses a folder with changes since its last pull or push, or one that holds another document, unless `--force`.
- `push` makes a new document in the active workspace (or `--new`), or updates the folder's own. Pictures added on the command line are uploaded and their keys rewritten, in the cloud and in the folder. When the document changed in the app since your pull, push refuses (`version_conflict`): pull first, or `--force`. A push with nothing new does nothing.
- `open` prints the editor link (`--browser` opens it).

## Engine notes

The derived boards are the engine's job. A hand move on a derived board, an overlap cleared by hand, or a missing command worked around is an engine bug report, not a fix: note it so the engine loop can turn it into a fixture and a test.

```
resaiz note "<what you wanted and what the engine did>" [--kind engine|cli|docs] [--board <id>] [--element <id>] [--no-qa]
resaiz inbox ls [--workspace id] [--status open|fixture|fixed|wont|all] [--limit N]
resaiz inbox pull <id> [--out DIR]
```

- `note` (statics, needs a sign in) stores one row in the folder's workspace (else the active one) with an anonymised snapshot of the document: boxes, roles, sizes, overrides and ids, the copy's length but never the copy, whether there is a picture but never its key, no font names, no document name. It also stores the QA counts as the folder is now and the hand fix counts per derived board. `resaiz info` prints the same count: `hand fixes on derived boards: 33 (r169 16, r11 17)`.
- `resaiz engine show | update`: every document carries the layout rules revision it was laid out with (`engine` in `resaiz info --json`, with `engineCurrent`; the human line is `engine: 1 (current 2)`). A document keeps its rules so derived boards tuned by hand never move; `engine update` moves it to the current rules on purpose (the derived boards may move, hand adjustments stay as deltas). New documents start on the current rules. A note's snapshot carries the revision too.
- `inbox ls` lists the workspace's notes newest first (open ones by default); `inbox pull` writes one whole note to `ops/engine/inbox/<id>.json` (or `--out`). Members of a workspace read and add its notes; nobody changes or deletes them from the CLI.

## Display sets

Banners come from their own document: `resaiz new --display --from <social folder|cloud id>` makes a 300x250 master with the IAB sizes (the social sizes off) from a social document's headline, sub, CTA, logo, product, fills, fonts and locales. Tiers (`resaiz el tier <id> 1..4|auto`) and short copy (`resaiz text set <id> --short "..."`, the copy deck's `short` column, `resaiz copydeck export`) decide what a small banner keeps; `resaiz size keep <board> <ids>` and `resaiz size short <board> <id> on|off|auto` override one size. QA adds `banner-missing-tier1`, `banner-overflow`, `short-copy`, `short-copy-missing` and, after an export, `file-size` over 150 KB. Display exports are named `<campaign>_<w>x<h>_<locale>.<ext>`, one folder per size with `--folder channel`.

## Safe zones

Every board has a safe zone: where the platform draws its own UI. The defaults are Meta Stories on 9:16 (14 percent top, 20 bottom), nothing on banners and a 4 percent margin everywhere else. `resaiz safe ls` shows them; `resaiz safe set r916 --preset meta_reels` or `resaiz safe set r169 --top 5 --bottom 10` changes one board (percent, 0 to 45), `all` every board that is on, and `resaiz safe clear` goes back to the default. QA flags copy inside it (`resaiz qa`); the layout keeps copy above the bottom band.

## Design primitives (statics)

- `resaiz fx add t_head stroke --color "#000000" --width 8 --position outside`: layer effects (`fx ls|add|set|rm|on|off|order|clear`): shadows, glows, stroke, colour and gradient overlay (linear or radial), vignette.
- `resaiz el add headline --text "PUNTA CANA" --font-family "Bebas Neue" --text-align center --ink 57,472,977,206`: copy placed by its glyph bounds, the numbers a PSD gives for a text layer (tracking written out: `letterSpacing: 0` unless `--letter-spacing` is given, so no role default applies; a `cta` is placed by its plate, `--w --h`, never `--ink`); `resaiz text ink <id>` reads them back to compare. `apply` takes `inkBox` on `addElement` the same way, and a batch is a plain array of `{ op, args }` or `{ "commands": [...] }`.
- `resaiz el add --role shape --shape line --x 64 --y 1106 --w 952 --h 2 --stroke "#FFC629"`: shapes (`rect`, `ellipse`, `line`, `pill`) with `--fill`, `--stroke`, `--stroke-width`, `--radius`; `resaiz shape set <id>` changes one.
- `resaiz el lock <ids...>` and `resaiz el unlock <ids...>`: the editor's layer lock (Figma). A locked layer cannot be clicked, dragged, resized or deleted on the canvas; the Layers panel still selects it. Placement, exports and shares are unchanged. `resaiz info` marks it `locked: true` in the JSON and `(locked)` in the layer list.
- Thin shapes are rules and dividers: a gold line under the copy spans the copy column on every size, a thin vertical line in a stats group stays as tall as the group. `--intent rule|divider` on `el add`, `resaiz el intent <id> rule|divider|auto` later. A vertical line is `--w 2 --h 114`; never a rotated line.
- `resaiz background gradient "linear:90:#0b1a2a@0,#2e6bff@1"`: the frame background, solid or gradient; `background paint <paint>` takes the same grammar on statics and motion.
- `resaiz pic flip <id> --x`, `pic adjust <id> --hue 20 --tint "#ff8800:30"`, `pic filter <id> --blur 6`, `pic mask <id> ellipse`, `pic clip <id> --to headline` (the picture inside the headline's letters, on every size; `--none` releases it).
- `resaiz style set <id> --blend multiply --line-height 0.9 --uppercase`. `--fill-opacity 30` dims the layer itself and never its effects (Photoshop's fill).
- `resaiz fx style save t_head "Gold outline"`: keeps the layer's effects, blend, opacity and fill opacity as a document style; `resaiz fx style apply t_sub st_1` copies it onto another layer, `fx style ls` lists the styles, `fx style rm st_1` drops one.

A paint is `#rrggbb`, a CSS colour name, `linear:<angle>:<stops>` (0 left to right, 90 top to bottom) or `radial:<cx>,<cy>,<r>:<stops>`; a stop is `<colour>@<at>` or `<colour>:<alpha>@<at>`. The full table is in `docs/cli.md`.

## Landing pages

The campaign's landing page, the one the app's Landing tab derives from the statics, from the command line (statics folders):

```
resaiz landing derive                      # the tab's first open: a product object opens on the launch template
resaiz landing show                        # template, look, sections and their copy per locale, seo, nav, key, markets
resaiz landing copy set hero headline "PUNTA CANA" --locale es
resaiz landing copydeck export --out deck.csv   # section,slot,locale,value; fill it, then
resaiz landing copydeck apply deck.csv
resaiz landing sections hero proof offer cta   # these on, in this order
resaiz landing render                      # full page PNGs at 1280 and 390 into out/landing/
resaiz landing check                       # facts only, exit 1 on one
resaiz landing publish --dry-run           # what would be uploaded, with sizes
resaiz landing publish                     # live at https://www.resaiz.com/p/<campaign key>/
```

- Also: `landing templates`, `landing looks`, `landing look <id>`, `landing template <id>|none`, `landing section <id> on|off|--variant V|--motion M|--reset`, `landing seo --title T --description D`, `landing nav --link "Label=href"`, `landing key <campaign key>`, `landing confirm`, `landing reset`, `landing unpublish`, `landing url`.
- Changes go through the same document mutations as the Landing tab. Lines you write (copy set, a deck) are yours; lines the in app agent wrote stay drafts until confirmed, and `publish` refuses them without `--confirm-drafts` (the Publish sheet's gate).
- `render` and `publish` use the browser (the pixel commands' Chrome): the render is the tab's preview page at rest (reduced motion), and a publish builds the Publish sheet's own bundle there (pictures, the 1.91:1 share image per route) before it uploads with your session. `publish` needs `resaiz login` and a pushed folder (`cloud push`); it records the live URL in `_cli.landing`.

## Commands

`resaiz --help` lists every command with its flags. In short: `new`, `info`, `schema`, `size add|rm|ls|on|off`, `safe ls|set|clear`, `master set`, `apply`, `text set|ink`, `el add|rm|move|rename|intent|tier|lock|unlock`, `style set`, `fx ls|add|set|rm|on|off|order|clear`, `fx style ls|save|apply|rm`, `shape set`, `locale add|rm|set`, `copydeck apply`, `landing show|derive|sections|section|look|template|seo|nav|key|copy|copydeck|render|check|publish|unpublish|url`, `font add|ls|rm`, `pic add|flip|adjust|filter|mask|clip`, `layer add|set|rm`, `duration`, `background`, `qa`, `check`, `hug`, `render`, `export`, `sheet`, `motion render|export|poster`, `login`, `logout`, `whoami`, `cloud ls|pull|push|open|use|rm|restore`, `note`, `inbox ls|pull`, `skill path|install`.

## Output and exit codes

- `--json` gives one JSON value on stdout (the default when stdout is not a terminal). An error is one object on stderr: `{ "error": { "code", "message", "hint"? } }`.
- Exit codes: 0 ok, 1 command error, 2 usage, 3 not signed in (or the sign in was refused), 4 browser (Chrome missing, the app not reachable, a render the page could not make).
- Global flags: `--dir D`, `--app URL` (the app the pixel commands open, default https://www.resaiz.com), `--quiet`, `--version`, `--help`.

## Licence

Proprietary; see LICENSE. Copyright (c) 2026 Young Hoon An. The open source packages bundled into `dist/resaiz.mjs` and their licences are listed in `dist/THIRD_PARTY_NOTICES.txt`.
