figura-cli 0.16.0 → 0.17.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.16.0",
3
+ "version": "0.17.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_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_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 *)'
6
6
  ---
7
7
 
8
8
  # /fig — figure it out before you build it
@@ -47,6 +47,44 @@ point. Only build a standalone `single` fig when the screen is genuinely net-new
47
47
  Unsure whether a version already exists? **Ask the user** rather than assume
48
48
  greenfield.
49
49
 
50
+ ### Reconstruct the baseline from SOURCE — required, no screenshot
51
+
52
+ The existing/current/broken frame of a `compare` fig must be a **faithful
53
+ recreation of what actually ships**, not a hand-approximation — an approximated
54
+ "before" quietly rebuilds a strawman and the diptych argues against a screen that
55
+ doesn't exist. So when you build the baseline state and **no screenshot is
56
+ provided**, studying the source and rebuilding its layout is a **REQUIRED step**,
57
+ not optional reading:
58
+
59
+ 1. **Locate the screen** — Grep/Glob for the route dir, the `page`/`route`
60
+ file, and the component(s) it renders (e.g. `apps/*/src/app/<route>/page.tsx`
61
+ plus imported views).
62
+ 2. **Read the component file(s)** and extract the real structure:
63
+ - the **JSX/TSX element hierarchy and order** (containers → sections →
64
+ controls, top to bottom);
65
+ - the **class names / style modules / design tokens** on each element
66
+ (`flex`/`grid`, spacing, radii, colors) — these are the layout;
67
+ - the **imported UI primitives** (`Button`, `Input`, `Card`, …) and *their*
68
+ rendered shape (a `variant="cta"` white pill vs an `outline` button is a
69
+ visible difference — open the primitive if unsure);
70
+ - **conditional branches** — auth-provider variants, stages, empty/error/
71
+ loading states — and decide which branch the baseline depicts (state it);
72
+ - the **real copy and labels** (headings, placeholders, button text, error
73
+ banners) verbatim, not paraphrased.
74
+ 3. **Rebuild that layout as the baseline** — real element hierarchy, real
75
+ controls, real copy, real spacing — so the "before" frame matches the shipped
76
+ screen line-for-line, then drift only the *proposed* frame.
77
+ 4. **Fallback (only if the source genuinely can't be located/read):**
78
+ approximate, and **caption the baseline as an approximation** (a chip / note on
79
+ the frame: "approximated — source not found") so no one mistakes a guess for
80
+ the shipped UI.
81
+
82
+ This is what "grounded in the real current state" above means: the baseline is
83
+ **source-faithful by reconstruction**, or it is explicitly labelled a guess —
84
+ never a silent approximation. It applies to **both** the hand-build path and the
85
+ server render below — on a server render you can't read the repo *for* the engine,
86
+ so you hand it the studied structure in the brief (see step 3, "Server render").
87
+
50
88
  ## How to fig
51
89
 
52
90
  1. **Verify the destination team** — call **`figura_whoami`** (or
@@ -64,16 +102,26 @@ greenfield.
64
102
  re-verify. If the response has no `binding` field (older CLI/MCP), confirm
65
103
  the team slug matches this project with the user before proceeding.
66
104
 
67
- 2. **Default — build it yourself, with YOUR Claude.** Fetch the team's design
68
- system `bunx figura-cli brand --json` and hand-build the fig's HTML against
69
- it, then land it with **`figura_create_fig`**, setting
70
- `area`, `orientation` (`portrait` / `landscape` / `both`), `mode` (`single` /
71
- `compare` / `variations` — default to `compare` when Existing-first turned up a
72
- baseline), `title`, and a real description. This uses the
73
- caller's own Claude (the model running this skill) no extra cost, full control
74
- for precise broken-vs-proposed diptychs and applies the team's brand from the
75
- fetched design system. **Follow that design system verbatim; never invent brand
76
- values.**
105
+ 2. **Default — build it yourself, with YOUR Claude.** Fetch the **canonical
106
+ authoring scaffold****`figura_fig_scaffold`** (CLI:
107
+ `bunx figura-cli fig scaffold --platform <mobile|web> [--device <id>] [--area <str>]`)
108
+ and hand-build the fig's HTML against it, then land it with
109
+ **`figura_create_fig`**, setting `area`, `orientation` (`portrait` /
110
+ `landscape` / `both`), `mode` (`single` / `compare` / `variations` default
111
+ to `compare` when Existing-first turned up a baseline), `title`, and a real
112
+ description. This uses the caller's own Claude (the model running this skill)
113
+ no extra cost, full control for precise broken-vs-proposed diptychs.
114
+
115
+ The scaffold IS the source of truth: it's the **exact system prompt Figura
116
+ injects when it renders a fig server-side** — the design rules, exemplars,
117
+ accent hints, your team's composed brand, and the component/device kit
118
+ context, all in one. Follow it verbatim; never invent brand values or restate
119
+ design rules from memory. Match `--platform` to the target (a web fig's
120
+ scaffold composes the team's *web* brand surface, a mobile fig's the *mobile*
121
+ one). **Fallback if the scaffold fetch fails** (older engine/CLI, or the
122
+ `figura_fig_scaffold` tool is unavailable): fetch just the brand with
123
+ `bunx figura-cli brand --json` and author against that — the scaffold is the
124
+ preferred path, the raw brand is the degraded one.
77
125
 
78
126
  **Landing IS hosting — leave `publish` and `commit` unset.** Creating a fig
79
127
  always persists it to Figura (there is no separate "save it for real" step);
@@ -110,6 +158,19 @@ greenfield.
110
158
  on brand). Never pick this path on your own to save effort: it costs credits
111
159
  and hand-built figs track the brand better.
112
160
 
161
+ **The engine CANNOT read your repo — carry the studied baseline in the brief.**
162
+ `figura_generate_fig` runs server-side against the team brand; it has no access
163
+ to the caller's filesystem, so for a `compare`/existing state it would otherwise
164
+ *guess* the "before" too. Close the seam yourself: do the source study from
165
+ "Reconstruct the baseline from SOURCE" above **first**, then pass the
166
+ reconstructed structure into the `prompt` brief as an explicit "before" spec —
167
+ the file you grounded it in, the element order top-to-bottom, the real controls
168
+ and primitive shapes, the branch depicted, and the verbatim copy/labels. The
169
+ `prompt` is free NL text, so this needs no extra parameter — the studied layout
170
+ just becomes the baseline half of the brief (the proposed half describes the
171
+ change). If you truly couldn't locate the source, say so in the brief and label
172
+ the baseline an approximation, same as the hand-build fallback.
173
+
113
174
  4. **Open the HOSTED fig for approval — never a local file.** Run
114
175
  `figura fig open <id>` (the id `figura_create_fig`/`figura_generate_fig` just
115
176
  returned). It opens **`viewUrl`** in the reviewer's browser — the actual
@@ -173,7 +234,9 @@ truncation, and doesn't need a library):
173
234
  as annotation chips: what's broken today, what each variation improves,
174
235
  what it trades away.
175
236
  - **In both views:** render the current state honestly from the shipped
176
- implementation; color-code chips by function (broken / improved /
237
+ implementation **reconstructed from source** per "Reconstruct the baseline
238
+ from SOURCE" (no screenshot ⇒ study the component file(s) and rebuild the real
239
+ layout, don't approximate); color-code chips by function (broken / improved /
177
240
  trade-off / detail) using the team's status colors from the fetched design
178
241
  system; mark the recommended variation and close with a short "my read"
179
242
  arguing the pick against the others. Keep the chrome around the frames
@@ -255,11 +318,20 @@ with two things you do **not** hand-write:
255
318
 
256
319
  So when you **hand-build** a fig (the default path), do your part of the contract:
257
320
 
258
- - **Mobile:** size the body to the exact logical screen and emit
259
- `<meta name="figura:artboard" content="WxH">` matching it (iPhone portrait
260
- `393x852` / landscape `852x393`). For OS-chrome insets prefer
261
- `var(--fig-safe-top, 59px)` / `.fig-safe-top` over hardcoded pixels — the stamp
262
- fills the value, the fallback keeps you correct if the kit is absent.
321
+ - **Bare mobile screen and ONLY then:** when the fig's HTML *is* one raw app
322
+ screen (no masthead, no state pills, no hand-drawn `.fig-device` frames — the
323
+ Figura viewer draws the iPhone shell around it), size the body to the exact
324
+ logical screen and emit `<meta name="figura:artboard" content="WxH">` matching
325
+ it (iPhone portrait `393x852` / landscape `852x393`). For OS-chrome insets
326
+ prefer `var(--fig-safe-top, 59px)` / `.fig-safe-top` over hardcoded pixels —
327
+ the stamp fills the value, the fallback keeps you correct if the kit is absent.
328
+ - **A document-style fig NEVER gets an artboard.** The artboard meta is the
329
+ viewer's opt-in signal for "size me to exactly WxH and wrap me in a device
330
+ shell" — stamped on an overview ⇄ focus redesign page (or any compare /
331
+ variations document with its own masthead, pills, or hand-drawn device
332
+ frames), it crams the whole document into a phone frame. A document fig
333
+ renders full-page precisely because it carries NO artboard meta, even when
334
+ every screen it depicts is a mobile app.
263
335
  - **Always:** emit `<meta name="figura:context" content="...">` (design intent).
264
336
  - **Optional but valued:** if the fig grounds a specific feature or flag, emit
265
337
  `<meta name="figura:feature" content="<flag-or-slug>">` so it links back to the
@@ -270,8 +342,9 @@ So when you **hand-build** a fig (the default path), do your part of the contrac
270
342
  - **MCP unavailable** (headless / cron, or no interactive auth): fall back to the
271
343
  CLI — `bunx figura-cli fig create` / `fig publish`, with `fig revise` /
272
344
  `fig revisions` for versions, `fig commit` / `fig uncommit` (or `--commit` on
273
- the final create/revise) for the team library, and
274
- `bunx figura-cli brand --json` for the brand.
345
+ the final create/revise) for the team library, `bunx figura-cli fig scaffold`
346
+ for the authoring scaffold, and `bunx figura-cli brand --json` for the raw
347
+ brand (the scaffold's fallback).
275
348
  - **`figura_create_fig`/`figura_revise_fig` itself fails** (no network, API down,
276
349
  auth expired): the fig never entered Figura, so there is no `id`/`viewUrl` to
277
350
  open. Only THEN write the HTML to a local scratch file (project-scoped —