figura-cli 0.23.0 → 0.23.2
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 +150 -148
- package/dist/mcp.js +31 -31
- package/package.json +1 -1
- package/skills/fig/SKILL.md +96 -28
- package/skills/skills.json +8 -2
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_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 *)'
|
|
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
|
|
@@ -123,9 +123,12 @@ so you hand it the studied structure in the brief (see step 3, "Server render").
|
|
|
123
123
|
|
|
124
124
|
- **`layout`** — `single` (one frame), `compare` (as-is → proposed diptych;
|
|
125
125
|
default this when Existing-first turned up a baseline — pass a `label` per
|
|
126
|
-
screen), `
|
|
127
|
-
|
|
128
|
-
`
|
|
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).
|
|
129
132
|
- **`screens`** — an ARRAY. Each screen's **`html` is CONTENT ONLY**: the
|
|
130
133
|
markup that goes *inside* the phone. Do **NOT** draw a frame, bezel, status
|
|
131
134
|
bar, or safe padding; do **NOT** set `--fig-safe-*`; do **NOT** wrap it in
|
|
@@ -140,6 +143,19 @@ so you hand it the studied structure in the brief (see step 3, "Server render").
|
|
|
140
143
|
in that screen's `css`.
|
|
141
144
|
- Plus `area`, `title`, `orientation`, and a real `description`.
|
|
142
145
|
|
|
146
|
+
**Small screens are the SERVER's job — with one exception that is yours.**
|
|
147
|
+
Below a catalog-derived breakpoint every multi-frame layout (`compare`,
|
|
148
|
+
`variations`, `overview-focus`) restructures itself: ONE frame at a time, as
|
|
149
|
+
large as fits (never under 0.72 scale), behind a sticky bottom switcher, with
|
|
150
|
+
the Overview grid dropped. You do not write a media query, a toggle, or a
|
|
151
|
+
scale for this, and there is nothing to remember — it is emitted for you.
|
|
152
|
+
What the server CANNOT do is name the frames: **each screen's `label` IS the
|
|
153
|
+
switcher button**, so `label: 'Empty state'` / `'After save'` works and a
|
|
154
|
+
missing label degrades to `1`, `2`, `3`. Give every screen a short, distinct,
|
|
155
|
+
thumb-sized label. Design each screen's CONTENT to survive a 0.72 scale too —
|
|
156
|
+
~11px is the floor for body copy at that scale, so 10px annotations are yours
|
|
157
|
+
to avoid.
|
|
158
|
+
|
|
143
159
|
This uses the caller's own Claude — no extra cost. Follow the scaffold's brand
|
|
144
160
|
values verbatim; never invent brand values or restate design rules from memory.
|
|
145
161
|
**Fallback if the scaffold fetch fails** (older engine/CLI): fetch just the
|
|
@@ -247,33 +263,52 @@ so you hand it the studied structure in the brief (see step 3, "Server render").
|
|
|
247
263
|
`figura fig uncommit <id>`) returns it to a draft — the share link is
|
|
248
264
|
untouched either way.
|
|
249
265
|
|
|
250
|
-
|
|
266
|
+
8. **Assign a reviewer.** Committing notifies the team in a digest — that is
|
|
267
|
+
ambient, not an assignment. If the fig argues a change or wants a decision,
|
|
268
|
+
finish by putting it in front of a named person with
|
|
269
|
+
**`figura_request_feedback`** (CLI: `figura fig request-feedback <id> --from
|
|
270
|
+
<name|email> --note <text>`). See "Assign a reviewer" below — this step is
|
|
271
|
+
what stops a fig from being landed, committed, and never actually read.
|
|
272
|
+
|
|
273
|
+
## Presenting a redesign — FOCUS first, overview second
|
|
251
274
|
|
|
252
275
|
When a fig argues a change against a current state (a shipped baseline, or
|
|
253
276
|
several candidate directions), use **`figura_compose_fig` with
|
|
254
277
|
`layout: 'overview-focus'`** — Figura emits the two switchable views (the
|
|
255
|
-
CSS-only radio-tab
|
|
278
|
+
CSS-only radio-tab Focus full-frame toggle ⇄ Overview grid) for you from your
|
|
256
279
|
per-screen content. You supply each state's CONTENT + a `label`; the composition,
|
|
257
|
-
the
|
|
280
|
+
the focus pills, the scaled overview grid, and the no-horizontal-scroll guarantee
|
|
258
281
|
are server-owned. The design intent below still guides WHAT each state's content
|
|
259
282
|
says and which is recommended — you just no longer hand-write the toggle markup
|
|
260
283
|
or the frame scaling. (If you're on the `create` escape hatch instead, the same
|
|
261
284
|
pattern must be hand-authored — plain CSS-only radio tabs, binary, no library:)
|
|
262
285
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
286
|
+
**★ FOCUS IS THE DEFAULT VIEW.** The fig opens on ONE full-size frame with state
|
|
287
|
+
pills — on desktop AND mobile. Overview is the *alternative*, behind the second
|
|
288
|
+
tab, and on a phone it is dropped entirely: there the pills ARE the interface. Never open a redesign fig on the scaled grid: `layout: 'variations'` (a
|
|
289
|
+
scan-only grid at the kit's `--fig-scale:0.34`) is NOT a redesign presentation —
|
|
290
|
+
using it for one shipped a 4-state fig as 134px phones with ~3px annotation
|
|
291
|
+
chips. A Focus-only fig is **complete on its own**; add Overview only when there
|
|
292
|
+
are enough states that scanning them at once genuinely helps.
|
|
293
|
+
|
|
294
|
+
- **Focus — full frame (default).** One state at a time behind state pills
|
|
295
|
+
(Today / A / B / …), each state framed identically so flipping states in place
|
|
296
|
+
reads as spot-the-difference. This is where the context lives, pinned on the
|
|
297
|
+
frame as annotation chips: what's broken today, what each variation improves,
|
|
298
|
+
what it trades away. Open on the first pill = the honest current state.
|
|
299
|
+
- **Never put annotation chips inside a clipping box.** `position:absolute`
|
|
300
|
+
cannot escape an ancestor's `overflow:hidden`, so a chip that straddles a
|
|
301
|
+
rounded-corner frame ships sliced. Hang chips off a positioned wrapper as
|
|
302
|
+
siblings of the frame, anchored to grow away from its edge.
|
|
303
|
+
- **Overview — side by side (alternative).** Every state at once: the honest
|
|
304
|
+
current state first, then each variation, as scaled-down frames in a grid.
|
|
305
|
+
Under each: a status chip and ONE condensed context line (`+ / –`). This view
|
|
306
|
+
is for scanning and picking a direction, not for reading detail. The grid must
|
|
267
307
|
wrap to one column before it can overflow — **a fig never scrolls
|
|
268
308
|
horizontally**, at any viewport. To shrink the framed phones, wrap each in the
|
|
269
309
|
kit's **`.fig-mini`** wrapper (see "Scaling a framed device" below), never a
|
|
270
310
|
rule on `.fig-device` and never CSS `zoom` (zoom leaks an unzoomed
|
|
271
311
|
`scrollWidth` into horizontal overflow — the exact thing this view forbids).
|
|
272
|
-
- **Focus — full frame.** One state at a time behind state pills (Today / A /
|
|
273
|
-
B / …), each state framed identically so flipping states in place reads as
|
|
274
|
-
spot-the-difference. This is where the context lives, pinned on the frame
|
|
275
|
-
as annotation chips: what's broken today, what each variation improves,
|
|
276
|
-
what it trades away.
|
|
277
312
|
- **In both views:** render the current state honestly from the shipped
|
|
278
313
|
implementation — **reconstructed from source** per "Reconstruct the baseline
|
|
279
314
|
from SOURCE" (no screenshot ⇒ study the component file(s) and rebuild the real
|
|
@@ -297,21 +332,29 @@ pattern must be hand-authored — plain CSS-only radio tabs, binary, no library:
|
|
|
297
332
|
giving it dimensions. It checks every injected component (device frame,
|
|
298
333
|
status bar, control bar, browser chrome), cross-checks the manifest's
|
|
299
334
|
`components` array, and reports the fig's own `frameKit` / `complete` /
|
|
300
|
-
`doubleFramed` checks; `--json` for machine output.
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
335
|
+
`doubleFramed` checks; `--json` for machine output. This is the check that
|
|
336
|
+
*proves* a frame renders. These are static checks over the served bytes —
|
|
337
|
+
`verify` reads markup and CSS, it lays nothing out, so it cannot see
|
|
338
|
+
overflow or a clipped chip.
|
|
339
|
+
3. **Screenshot THAT yourself** — layout is nothing `verify` reports. Take the
|
|
340
|
+
printed served HTML, saved to a deterministic project-scoped path (the
|
|
341
|
+
repo's `.figura/figs/` (gitignored), or `~/.figura/figs/` outside a bound
|
|
342
|
+
repo, never `/tmp`/`mktemp`), and render it at a few viewport widths.
|
|
343
|
+
`document.scrollWidth` must equal the viewport in every view and state
|
|
344
|
+
(sideways scroll = broken), no annotation chip may cover the content it
|
|
345
|
+
annotates, and no chip may be cut by a clipping ancestor: walk each chip's
|
|
346
|
+
ancestors up to `<html>` and, wherever computed `overflow` is
|
|
347
|
+
`hidden`/`clip`, assert the chip's rect stays inside that ancestor's box —
|
|
348
|
+
the viewport box for `<body>`/`<html>`, whose overflow propagates there.
|
|
349
|
+
Comparing a chip to the frame's rect alone misses the vertical escape, and
|
|
350
|
+
no rect test sees a rounded clip, which is why the authoring rule above is
|
|
351
|
+
the real guarantee. `figura fig open <id> --local` writes the same served
|
|
352
|
+
bytes to `<repo>/.figura/figs/figura-fig-<id>.html`.
|
|
310
353
|
|
|
311
354
|
A **wipe-slider** stays the narrow tool for exactly two states of the same
|
|
312
355
|
layout with pixel-level tweaks. Never ship a squeezed side-by-side as the
|
|
313
|
-
*only* view
|
|
314
|
-
|
|
356
|
+
*only* view — a scan-only grid is never the whole argument. The reverse is
|
|
357
|
+
NOT true: a Focus-only toggle is a complete fig, however many states it has.
|
|
315
358
|
|
|
316
359
|
## JavaScript is ALLOWED — use it for high-fidelity animation
|
|
317
360
|
|
|
@@ -352,6 +395,31 @@ Example — a GSAP-driven entrance for a mobile screen, pinned + allowlisted:
|
|
|
352
395
|
</script>
|
|
353
396
|
```
|
|
354
397
|
|
|
398
|
+
## Assign a reviewer — a proposal fig isn't done until someone's on it
|
|
399
|
+
|
|
400
|
+
**A link nobody was pointed at is a fig nobody saw.** Landing, publishing, and
|
|
401
|
+
committing all produce a URL; none of them puts the fig in front of a specific
|
|
402
|
+
person. Committing notifies the team in a *digest*, which is ambient — it is not
|
|
403
|
+
an assignment, and it is not a decision request.
|
|
404
|
+
|
|
405
|
+
So after landing any fig that **argues a change or wants a decision**, send a
|
|
406
|
+
feedback request as a required final step (step 8 above):
|
|
407
|
+
|
|
408
|
+
- **MCP:** `figura_request_feedback` — the fig id, the teammate, and a one-line
|
|
409
|
+
note saying what to look at or decide.
|
|
410
|
+
- **CLI:** `figura fig request-feedback <id> --from <name|email> [--note <text>]`
|
|
411
|
+
(use this when the MCP is bound to a different team than the fig — the CLI
|
|
412
|
+
reads the repo's `.figura/config.json`, the MCP may not).
|
|
413
|
+
|
|
414
|
+
They get an in-app notification, a push, and an email, and the discussion lands
|
|
415
|
+
in the fig's comment thread. **Name what you want from them** ("does 02 close
|
|
416
|
+
#2375?") rather than "thoughts?" — a request with a question attached gets
|
|
417
|
+
answered; a bare link gets deferred.
|
|
418
|
+
|
|
419
|
+
Ask the user who should review only when it isn't obvious from context. Skip the
|
|
420
|
+
request only for throwaway/exploratory figs, or when the user explicitly wants it
|
|
421
|
+
private.
|
|
422
|
+
|
|
355
423
|
## Metadata & geometry — stamped for you
|
|
356
424
|
|
|
357
425
|
Every landed fig is **auto-stamped server-side** (on `create` and `generate`)
|
package/skills/skills.json
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{
|
|
2
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
3
|
"skills": {
|
|
4
|
-
"fig": {
|
|
5
|
-
|
|
4
|
+
"fig": {
|
|
5
|
+
"version": "2026.08.04",
|
|
6
|
+
"releasedAt": "2026-08-04T17:44:19.938Z"
|
|
7
|
+
},
|
|
8
|
+
"figura-brand": {
|
|
9
|
+
"version": "2026.07.26",
|
|
10
|
+
"releasedAt": "2026-07-26T00:00:00.000Z"
|
|
11
|
+
}
|
|
6
12
|
}
|
|
7
13
|
}
|