figura-cli 0.22.0 → 0.23.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/dist/index.js +174 -169
- package/dist/mcp.js +37 -37
- package/package.json +1 -1
- package/skills/fig/SKILL.md +126 -49
- package/skills/skills.json +7 -0
package/package.json
CHANGED
package/skills/fig/SKILL.md
CHANGED
|
@@ -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_list_figs, 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, mcp__figura__figura_request_feedback, Bash(figura *), Bash(bunx figura-cli *)'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# /fig — figure it out before you build it
|
|
@@ -105,28 +105,57 @@ 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 —
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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), `overview-focus` (**the one for arguing a redesign or several
|
|
127
|
+
candidate directions** — opens on the full-frame Focus toggle, Overview
|
|
128
|
+
grid behind the second tab), `variations` (a scan-only grid of states at
|
|
129
|
+
~0.34 scale, NO toggle — only for a pure catalogue nobody has to read
|
|
130
|
+
detail in; never for a redesign argument), or `document` (NO device
|
|
131
|
+
framing — brand/overview figs).
|
|
132
|
+
- **`screens`** — an ARRAY. Each screen's **`html` is CONTENT ONLY**: the
|
|
133
|
+
markup that goes *inside* the phone. Do **NOT** draw a frame, bezel, status
|
|
134
|
+
bar, or safe padding; do **NOT** set `--fig-safe-*`; do **NOT** wrap it in
|
|
135
|
+
`.fig-device`. For every device layout each screen also carries the typed
|
|
136
|
+
intent Figura needs — **`device`** (a real catalog id from
|
|
137
|
+
`figura_fig_devices`, e.g. `iphone-16`), **`orientation`**
|
|
138
|
+
(`portrait`/`landscape`), and **`safeZone`** (`respect` = Figura pads your
|
|
139
|
+
content to the device's real safe insets; `ignore` = edge-to-edge for a
|
|
140
|
+
splash/hero). (`document` screens need none of these.)
|
|
141
|
+
- **`sharedCss`** — ONE stylesheet every screen shares: put your brand tokens
|
|
142
|
+
(from the scaffold) and shared content classes here. Per-screen one-offs go
|
|
143
|
+
in that screen's `css`.
|
|
144
|
+
- Plus `area`, `title`, `orientation`, and a real `description`.
|
|
145
|
+
|
|
146
|
+
This uses the caller's own Claude — no extra cost. Follow the scaffold's brand
|
|
147
|
+
values verbatim; never invent brand values or restate design rules from memory.
|
|
148
|
+
**Fallback if the scaffold fetch fails** (older engine/CLI): fetch just the
|
|
149
|
+
brand with `bunx figura-cli brand --json` and author your content styles
|
|
150
|
+
against that.
|
|
151
|
+
|
|
152
|
+
**`figura_create_fig` is the escape hatch** — reach for it only for a
|
|
153
|
+
`document`/exotic fig where you must hand-author the entire HTML (or when
|
|
154
|
+
`compose` is unavailable on an older engine). It takes a full `html` document
|
|
155
|
+
and you own the whole contract yourself. For any device fig, prefer
|
|
156
|
+
`compose` — it can't drift.
|
|
157
|
+
|
|
158
|
+
**Landing IS hosting — leave `publish` and `commit` unset.** Composing a fig
|
|
130
159
|
always persists it to Figura (there is no separate "save it for real" step);
|
|
131
160
|
the response's **`viewUrl`** is a link you can open right now, whether or not
|
|
132
161
|
anyone has decided to share it wider. It lands as a **workspace draft** —
|
|
@@ -137,15 +166,14 @@ so you hand it the studied structure in the brief (see step 3, "Server render").
|
|
|
137
166
|
on an exploration — committing is reserved for the session's final
|
|
138
167
|
recommendation (step 7).
|
|
139
168
|
|
|
140
|
-
**Pass
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
169
|
+
**Pass markup INLINE — never a shell reference.** The `html` (create) and each
|
|
170
|
+
screen's `html`/`css` + `sharedCss` (compose) arguments are the *strings
|
|
171
|
+
themselves*, passed directly as the value. An MCP tool argument is **not** a
|
|
172
|
+
shell: `$(cat "…/fig.html")`, a backtick `` `cat file` ``, any command
|
|
144
173
|
substitution, or a bare file path will **not** expand — the literal string is
|
|
145
|
-
stored
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
the contents as `html` — do not reference the path.
|
|
174
|
+
stored (a tiny black/empty canvas; the engine rejects a shell-ref create with
|
|
175
|
+
a 400). If you authored content in a scratch file, **read that file's
|
|
176
|
+
contents** and pass the contents — do not reference the path.
|
|
149
177
|
|
|
150
178
|
3. **Server render — the user's call, never yours.** Figura can also render the
|
|
151
179
|
fig server-side against the team brand: **`figura_generate_fig`** with a
|
|
@@ -222,27 +250,48 @@ so you hand it the studied structure in the brief (see step 3, "Server render").
|
|
|
222
250
|
`figura fig uncommit <id>`) returns it to a draft — the share link is
|
|
223
251
|
untouched either way.
|
|
224
252
|
|
|
225
|
-
|
|
253
|
+
8. **Assign a reviewer.** Committing notifies the team in a digest — that is
|
|
254
|
+
ambient, not an assignment. If the fig argues a change or wants a decision,
|
|
255
|
+
finish by putting it in front of a named person with
|
|
256
|
+
**`figura_request_feedback`** (CLI: `figura fig request-feedback <id> --from
|
|
257
|
+
<name|email> --note <text>`). See "Assign a reviewer" below — this step is
|
|
258
|
+
what stops a fig from being landed, committed, and never actually read.
|
|
259
|
+
|
|
260
|
+
## Presenting a redesign — FOCUS first, overview second
|
|
226
261
|
|
|
227
262
|
When a fig argues a change against a current state (a shipped baseline, or
|
|
228
|
-
several candidate directions),
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
263
|
+
several candidate directions), use **`figura_compose_fig` with
|
|
264
|
+
`layout: 'overview-focus'`** — Figura emits the two switchable views (the
|
|
265
|
+
CSS-only radio-tab Focus full-frame toggle ⇄ Overview grid) for you from your
|
|
266
|
+
per-screen content. You supply each state's CONTENT + a `label`; the composition,
|
|
267
|
+
the focus pills, the scaled overview grid, and the no-horizontal-scroll guarantee
|
|
268
|
+
are server-owned. The design intent below still guides WHAT each state's content
|
|
269
|
+
says and which is recommended — you just no longer hand-write the toggle markup
|
|
270
|
+
or the frame scaling. (If you're on the `create` escape hatch instead, the same
|
|
271
|
+
pattern must be hand-authored — plain CSS-only radio tabs, binary, no library:)
|
|
272
|
+
|
|
273
|
+
**★ FOCUS IS THE DEFAULT VIEW.** The fig opens on ONE full-size frame with state
|
|
274
|
+
pills — on desktop AND mobile. Overview is the *alternative*, behind the second
|
|
275
|
+
tab. Never open a redesign fig on the scaled grid: `layout: 'variations'` (a
|
|
276
|
+
scan-only grid at the kit's `--fig-scale:0.34`) is NOT a redesign presentation —
|
|
277
|
+
using it for one shipped a 4-state fig as 134px phones with ~3px annotation
|
|
278
|
+
chips. A Focus-only fig is **complete on its own**; add Overview only when there
|
|
279
|
+
are enough states that scanning them at once genuinely helps.
|
|
280
|
+
|
|
281
|
+
- **Focus — full frame (default).** One state at a time behind state pills
|
|
282
|
+
(Today / A / B / …), each state framed identically so flipping states in place
|
|
283
|
+
reads as spot-the-difference. This is where the context lives, pinned on the
|
|
284
|
+
frame as annotation chips: what's broken today, what each variation improves,
|
|
285
|
+
what it trades away. Open on the first pill = the honest current state.
|
|
286
|
+
- **Overview — side by side (alternative).** Every state at once: the honest
|
|
287
|
+
current state first, then each variation, as scaled-down frames in a grid.
|
|
288
|
+
Under each: a status chip and ONE condensed context line (`+ / –`). This view
|
|
289
|
+
is for scanning and picking a direction, not for reading detail. The grid must
|
|
236
290
|
wrap to one column before it can overflow — **a fig never scrolls
|
|
237
291
|
horizontally**, at any viewport. To shrink the framed phones, wrap each in the
|
|
238
292
|
kit's **`.fig-mini`** wrapper (see "Scaling a framed device" below), never a
|
|
239
293
|
rule on `.fig-device` and never CSS `zoom` (zoom leaks an unzoomed
|
|
240
294
|
`scrollWidth` into horizontal overflow — the exact thing this view forbids).
|
|
241
|
-
- **Focus — full frame.** One state at a time behind state pills (Today / A /
|
|
242
|
-
B / …), each state framed identically so flipping states in place reads as
|
|
243
|
-
spot-the-difference. This is where the context lives, pinned on the frame
|
|
244
|
-
as annotation chips: what's broken today, what each variation improves,
|
|
245
|
-
what it trades away.
|
|
246
295
|
- **In both views:** render the current state honestly from the shipped
|
|
247
296
|
implementation — **reconstructed from source** per "Reconstruct the baseline
|
|
248
297
|
from SOURCE" (no screenshot ⇒ study the component file(s) and rebuild the real
|
|
@@ -279,8 +328,8 @@ truncation, and doesn't need a library):
|
|
|
279
328
|
|
|
280
329
|
A **wipe-slider** stays the narrow tool for exactly two states of the same
|
|
281
330
|
layout with pixel-level tweaks. Never ship a squeezed side-by-side as the
|
|
282
|
-
*only* view
|
|
283
|
-
|
|
331
|
+
*only* view — a scan-only grid is never the whole argument. The reverse is
|
|
332
|
+
NOT true: a Focus-only toggle is a complete fig, however many states it has.
|
|
284
333
|
|
|
285
334
|
## JavaScript is ALLOWED — use it for high-fidelity animation
|
|
286
335
|
|
|
@@ -321,6 +370,31 @@ Example — a GSAP-driven entrance for a mobile screen, pinned + allowlisted:
|
|
|
321
370
|
</script>
|
|
322
371
|
```
|
|
323
372
|
|
|
373
|
+
## Assign a reviewer — a proposal fig isn't done until someone's on it
|
|
374
|
+
|
|
375
|
+
**A link nobody was pointed at is a fig nobody saw.** Landing, publishing, and
|
|
376
|
+
committing all produce a URL; none of them puts the fig in front of a specific
|
|
377
|
+
person. Committing notifies the team in a *digest*, which is ambient — it is not
|
|
378
|
+
an assignment, and it is not a decision request.
|
|
379
|
+
|
|
380
|
+
So after landing any fig that **argues a change or wants a decision**, send a
|
|
381
|
+
feedback request as a required final step (step 8 above):
|
|
382
|
+
|
|
383
|
+
- **MCP:** `figura_request_feedback` — the fig id, the teammate, and a one-line
|
|
384
|
+
note saying what to look at or decide.
|
|
385
|
+
- **CLI:** `figura fig request-feedback <id> --from <name|email> [--note <text>]`
|
|
386
|
+
(use this when the MCP is bound to a different team than the fig — the CLI
|
|
387
|
+
reads the repo's `.figura/config.json`, the MCP may not).
|
|
388
|
+
|
|
389
|
+
They get an in-app notification, a push, and an email, and the discussion lands
|
|
390
|
+
in the fig's comment thread. **Name what you want from them** ("does 02 close
|
|
391
|
+
#2375?") rather than "thoughts?" — a request with a question attached gets
|
|
392
|
+
answered; a bare link gets deferred.
|
|
393
|
+
|
|
394
|
+
Ask the user who should review only when it isn't obvious from context. Skip the
|
|
395
|
+
request only for throwaway/exploratory figs, or when the user explicitly wants it
|
|
396
|
+
private.
|
|
397
|
+
|
|
324
398
|
## Metadata & geometry — stamped for you
|
|
325
399
|
|
|
326
400
|
Every landed fig is **auto-stamped server-side** (on `create` and `generate`)
|
|
@@ -336,8 +410,11 @@ with two things you do **not** hand-write:
|
|
|
336
410
|
search and the self-improving loop. Artifact-descriptive only — no team/brand
|
|
337
411
|
ids (the block is served on the public link).
|
|
338
412
|
|
|
339
|
-
|
|
340
|
-
contract —
|
|
413
|
+
With **`figura_compose_fig`** you do NOT hand-write any of the frame/safe/device
|
|
414
|
+
contract below — Figura stamps it from your typed params. The rest of this
|
|
415
|
+
section applies ONLY to the **`figura_create_fig` escape hatch**, where you
|
|
416
|
+
hand-author the whole document: the exact rules are **fetched, not restated
|
|
417
|
+
here** — the
|
|
341
418
|
presentation model (frameless single vs canvas composition), when to emit or
|
|
342
419
|
omit the whole-fig `<meta name="figura:artboard">`, the `.fig-device` /
|
|
343
420
|
`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.28", "releasedAt": "2026-07-28T00:00:00.000Z" },
|
|
5
|
+
"figura-brand": { "version": "2026.07.26", "releasedAt": "2026-07-26T00:00:00.000Z" }
|
|
6
|
+
}
|
|
7
|
+
}
|