figura-cli 0.22.0 → 0.23.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figura-cli",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Command-line client for the Figura visualization SaaS",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -2,7 +2,7 @@
2
2
  name: fig
3
3
  description: "Mock up a screen as an on-brand, shareable HTML fig via your Figura team — render a UI state in seconds before writing the real code. Use when asked to visualize a screen, mock a UI, or 'make a fig'. iPhone-framed by default; browser frame for web/desktop."
4
4
  argument-hint: '<what to visualize>'
5
- allowed-tools: 'mcp__figura__figura_whoami, mcp__figura__figura_fig_scaffold, mcp__figura__figura_fig_devices, mcp__figura__figura_generate_fig, mcp__figura__figura_create_fig, mcp__figura__figura_revise_fig, mcp__figura__figura_publish_fig, mcp__figura__figura_commit_fig, Bash(figura *), Bash(bunx figura-cli *)'
5
+ allowed-tools: 'mcp__figura__figura_whoami, mcp__figura__figura_fig_scaffold, mcp__figura__figura_fig_devices, mcp__figura__figura_generate_fig, mcp__figura__figura_compose_fig, mcp__figura__figura_create_fig, mcp__figura__figura_revise_fig, mcp__figura__figura_publish_fig, mcp__figura__figura_commit_fig, Bash(figura *), Bash(bunx figura-cli *)'
6
6
  ---
7
7
 
8
8
  # /fig — figure it out before you build it
@@ -105,28 +105,54 @@ so you hand it the studied structure in the brief (see step 3, "Server render").
105
105
  to the user verbatim (a one-line "figura-mcp is behind / below min — run …"),
106
106
  then proceed. It's a passive nudge, shown once per session — never a blocker.
107
107
 
108
- 2. **Default — build it yourself, with YOUR Claude.** Fetch the **canonical
109
- authoring scaffold** **`figura_fig_scaffold`** (CLI:
110
- `bunx figura-cli fig scaffold --platform <mobile|web> [--device <id>] [--area <str>]`)
111
- and hand-build the fig's HTML against it, then land it with
112
- **`figura_create_fig`**, setting `area`, `orientation` (`portrait` /
113
- `landscape` / `both`), `mode` (`single` / `compare` / `variations` default
114
- to `compare` when Existing-first turned up a baseline), `title`, and a real
115
- description. This uses the caller's own Claude (the model running this skill)
116
- no extra cost, full control for precise broken-vs-proposed diptychs.
117
-
118
- The scaffold IS the source of truth: it's the **exact system prompt Figura
119
- injects when it renders a fig server-side** the design rules, exemplars,
120
- accent hints, your team's composed brand, and the component/device kit
121
- context, all in one. Follow it verbatim; never invent brand values or restate
122
- design rules from memory. Match `--platform` to the target (a web fig's
123
- scaffold composes the team's *web* brand surface, a mobile fig's the *mobile*
124
- one). **Fallback if the scaffold fetch fails** (older engine/CLI, or the
125
- `figura_fig_scaffold` tool is unavailable): fetch just the brand with
126
- `bunx figura-cli brand --json` and author against that the scaffold is the
127
- preferred path, the raw brand is the degraded one.
128
-
129
- **Landing IS hosting leave `publish` and `commit` unset.** Creating a fig
108
+ 2. **Default — write the CONTENT, let Figura own the STRUCTURE.** Build device
109
+ figs with **`figura_compose_fig`**: you author only each **screen's CONTENT**
110
+ (the markup *inside* the phone); Figura owns the parts that used to drift — the
111
+ iPhone **frame** (bezel + Dynamic Island/notch + home bar), the **safe zone**,
112
+ the exact catalog **artboard geometry**, and the **composition** (grid /
113
+ diptych / overview). The frame/safe/device/composition can no longer be wrong:
114
+ they're sourced from the device catalog server-side, not hand-written, so the
115
+ chronic failures (a rolled-your-own artboard, a guessed `--fig-safe-top:59px`,
116
+ a forgotten `data-device`) are impossible by construction.
117
+
118
+ First fetch context the **scaffold** (`figura_fig_scaffold`, CLI:
119
+ `bunx figura-cli fig scaffold --platform <mobile|web> [--area <str>]`) for the
120
+ brand tokens + design rules, and the **device catalog**
121
+ (`figura_fig_devices`) for the valid `device` ids. Then call
122
+ **`figura_compose_fig`** with:
123
+
124
+ - **`layout`** `single` (one frame), `compare` (as-is proposed diptych;
125
+ default this when Existing-first turned up a baseline — pass a `label` per
126
+ screen), `variations` (responsive grid of states + status chips),
127
+ `overview-focus` (the overview⇄focus toggle for arguing a redesign), or
128
+ `document` (NO device framing — brand/overview figs).
129
+ - **`screens`**an ARRAY. Each screen's **`html` is CONTENT ONLY**: the
130
+ markup that goes *inside* the phone. Do **NOT** draw a frame, bezel, status
131
+ bar, or safe padding; do **NOT** set `--fig-safe-*`; do **NOT** wrap it in
132
+ `.fig-device`. For every device layout each screen also carries the typed
133
+ intent Figura needs — **`device`** (a real catalog id from
134
+ `figura_fig_devices`, e.g. `iphone-16`), **`orientation`**
135
+ (`portrait`/`landscape`), and **`safeZone`** (`respect` = Figura pads your
136
+ content to the device's real safe insets; `ignore` = edge-to-edge for a
137
+ splash/hero). (`document` screens need none of these.)
138
+ - **`sharedCss`** — ONE stylesheet every screen shares: put your brand tokens
139
+ (from the scaffold) and shared content classes here. Per-screen one-offs go
140
+ in that screen's `css`.
141
+ - Plus `area`, `title`, `orientation`, and a real `description`.
142
+
143
+ This uses the caller's own Claude — no extra cost. Follow the scaffold's brand
144
+ values verbatim; never invent brand values or restate design rules from memory.
145
+ **Fallback if the scaffold fetch fails** (older engine/CLI): fetch just the
146
+ brand with `bunx figura-cli brand --json` and author your content styles
147
+ against that.
148
+
149
+ **`figura_create_fig` is the escape hatch** — reach for it only for a
150
+ `document`/exotic fig where you must hand-author the entire HTML (or when
151
+ `compose` is unavailable on an older engine). It takes a full `html` document
152
+ and you own the whole contract yourself. For any device fig, prefer
153
+ `compose` — it can't drift.
154
+
155
+ **Landing IS hosting — leave `publish` and `commit` unset.** Composing a fig
130
156
  always persists it to Figura (there is no separate "save it for real" step);
131
157
  the response's **`viewUrl`** is a link you can open right now, whether or not
132
158
  anyone has decided to share it wider. It lands as a **workspace draft** —
@@ -137,15 +163,14 @@ so you hand it the studied structure in the brief (see step 3, "Server render").
137
163
  on an exploration — committing is reserved for the session's final
138
164
  recommendation (step 7).
139
165
 
140
- **Pass the fig markup INLINE as `html` — never a shell reference.** The `html`
141
- argument to `figura_create_fig` / `figura_revise_fig` is the *full HTML
142
- document itself*, passed directly as the value. An MCP tool argument is **not**
143
- a shell: `$(cat "…/fig.html")`, a backtick `` `cat file` ``, any command
166
+ **Pass markup INLINE — never a shell reference.** The `html` (create) and each
167
+ screen's `html`/`css` + `sharedCss` (compose) arguments are the *strings
168
+ themselves*, passed directly as the value. An MCP tool argument is **not** a
169
+ shell: `$(cat "…/fig.html")`, a backtick `` `cat file` ``, any command
144
170
  substitution, or a bare file path will **not** expand — the literal string is
145
- stored as the fig, producing a tiny black/empty canvas (the engine now rejects
146
- this with a 400). If you authored the HTML in a scratch file to render/verify
147
- it (step "Verify before publishing"), **read that file's contents** and pass
148
- the contents as `html` — do not reference the path.
171
+ stored (a tiny black/empty canvas; the engine rejects a shell-ref create with
172
+ a 400). If you authored content in a scratch file, **read that file's
173
+ contents** and pass the contents do not reference the path.
149
174
 
150
175
  3. **Server render — the user's call, never yours.** Figura can also render the
151
176
  fig server-side against the team brand: **`figura_generate_fig`** with a
@@ -225,9 +250,15 @@ so you hand it the studied structure in the brief (see step 3, "Server render").
225
250
  ## Presenting a redesign — overview ⇄ focus
226
251
 
227
252
  When a fig argues a change against a current state (a shipped baseline, or
228
- several candidate directions), build one fig with **two switchable views** —
229
- plain CSS-only radio tabs for THIS specific toggle (it's binary, must survive
230
- truncation, and doesn't need a library):
253
+ several candidate directions), use **`figura_compose_fig` with
254
+ `layout: 'overview-focus'`** Figura emits the two switchable views (the
255
+ CSS-only radio-tab Overview grid Focus full-frame toggle) for you from your
256
+ per-screen content. You supply each state's CONTENT + a `label`; the composition,
257
+ the scaled overview grid, the focus pills, and the no-horizontal-scroll guarantee
258
+ are server-owned. The design intent below still guides WHAT each state's content
259
+ says and which is recommended — you just no longer hand-write the toggle markup
260
+ or the frame scaling. (If you're on the `create` escape hatch instead, the same
261
+ pattern must be hand-authored — plain CSS-only radio tabs, binary, no library:)
231
262
 
232
263
  - **Overview — side by side.** Every state at once: the honest current state
233
264
  first, then each variation, as scaled-down frames in a grid. Under each: a
@@ -336,8 +367,11 @@ with two things you do **not** hand-write:
336
367
  search and the self-improving loop. Artifact-descriptive only — no team/brand
337
368
  ids (the block is served on the public link).
338
369
 
339
- So when you **hand-build** a fig (the default path), you do your part of the
340
- contract — but the exact rules are **fetched, not restated here**: the
370
+ With **`figura_compose_fig`** you do NOT hand-write any of the frame/safe/device
371
+ contract below Figura stamps it from your typed params. The rest of this
372
+ section applies ONLY to the **`figura_create_fig` escape hatch**, where you
373
+ hand-author the whole document: the exact rules are **fetched, not restated
374
+ here** — the
341
375
  presentation model (frameless single vs canvas composition), when to emit or
342
376
  omit the whole-fig `<meta name="figura:artboard">`, the `.fig-device` /
343
377
  `data-device` frame markup (Figura stamps the bezel, Dynamic Island, and
@@ -0,0 +1,7 @@
1
+ {
2
+ "$comment": "Skill version manifest (#752) — the source of truth for each Figura skill's OWN version line, DECOUPLED from the figura-cli package version. A release bumps the version here when a skill's content changes, then regenerates the engine seed (bun packages/core/scripts/build-skill-seed.mjs) and re-seeds. Version is a date tag (YYYY.MM.DD[.n]); releasedAt is ISO-8601. The CLI/MCP stamp this version into .figura-version at install; the engine seeds the registry from it.",
3
+ "skills": {
4
+ "fig": { "version": "2026.07.26", "releasedAt": "2026-07-26T00:00:00.000Z" },
5
+ "figura-brand": { "version": "2026.07.26", "releasedAt": "2026-07-26T00:00:00.000Z" }
6
+ }
7
+ }