figura-cli 0.13.1 → 0.15.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/dist/index.js +128 -115
- package/dist/mcp.js +32 -32
- package/package.json +1 -1
- package/skills/fig/SKILL.md +60 -18
- package/skills/figura-brand/SKILL.md +26 -11
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_generate_fig, mcp__figura__figura_create_fig, Bash(figura *), Bash(bunx figura-cli *)'
|
|
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, Bash(figura *), Bash(bunx figura-cli *)'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# /fig — figure it out before you build it
|
|
@@ -65,30 +65,63 @@ greenfield.
|
|
|
65
65
|
|
|
66
66
|
2. **Default — build it yourself, with YOUR Claude.** Fetch the team's design
|
|
67
67
|
system — `bunx figura-cli brand --json` — and hand-build the fig's HTML against
|
|
68
|
-
it, then land it with **`figura_create_fig
|
|
68
|
+
it, then land it with **`figura_create_fig`**, setting
|
|
69
69
|
`area`, `orientation` (`portrait` / `landscape` / `both`), `mode` (`single` /
|
|
70
70
|
`compare` / `variations` — default to `compare` when Existing-first turned up a
|
|
71
71
|
baseline), `title`, and a real description. This uses the
|
|
72
72
|
caller's own Claude (the model running this skill) — no extra cost, full control
|
|
73
73
|
for precise broken-vs-proposed diptychs — and applies the team's brand from the
|
|
74
74
|
fetched design system. **Follow that design system verbatim; never invent brand
|
|
75
|
-
values.**
|
|
75
|
+
values.**
|
|
76
|
+
|
|
77
|
+
**Landing IS hosting — leave `publish` unset.** Creating a fig always persists
|
|
78
|
+
it to Figura (there is no separate "save it for real" step); the response's
|
|
79
|
+
**`viewUrl`** is a link you can open right now, whether or not anyone has
|
|
80
|
+
decided to share it wider. Don't pass `publish: true` just to get something
|
|
81
|
+
viewable — that makes a first-draft iteration PUBLIC. Publishing is a
|
|
82
|
+
deliberate, later step (see step 5).
|
|
76
83
|
|
|
77
84
|
3. **Server render — the user's call, never yours.** Figura can also render the
|
|
78
85
|
fig server-side against the team brand: **`figura_generate_fig`** with a
|
|
79
|
-
natural-language brief (same `area` / `orientation` / `mode` / `title
|
|
80
|
-
`publish
|
|
81
|
-
render ("let Figura render it", headless/CI
|
|
82
|
-
when you've offered both paths and they
|
|
83
|
-
**async job the tool polls for you** — expect
|
|
84
|
-
returns; don't re-call while one is in flight.
|
|
85
|
-
**spends the team's Figura credits** (a `403`
|
|
86
|
-
Pass **`fidelity: 'high'`** to render on the
|
|
87
|
-
`'fast'` is lighter — fine for rough states, weaker
|
|
88
|
-
this path on your own to save effort: it costs credits
|
|
89
|
-
track the brand better.
|
|
90
|
-
|
|
91
|
-
4. **
|
|
86
|
+
natural-language brief (same `area` / `orientation` / `mode` / `title`, and
|
|
87
|
+
again leave `publish` unset by default). Use it only when the **user
|
|
88
|
+
explicitly asks** for a server render ("let Figura render it", headless/CI
|
|
89
|
+
flows with no local model) — or when you've offered both paths and they
|
|
90
|
+
picked this one. It runs as an **async job the tool polls for you** — expect
|
|
91
|
+
~30–90s before the landed fig returns; don't re-call while one is in flight.
|
|
92
|
+
Unlike the default path, this **spends the team's Figura credits** (a `403`
|
|
93
|
+
means no active subscription). Pass **`fidelity: 'high'`** to render on the
|
|
94
|
+
better model (the default `'fast'` is lighter — fine for rough states, weaker
|
|
95
|
+
on brand). Never pick this path on your own to save effort: it costs credits
|
|
96
|
+
and hand-built figs track the brand better.
|
|
97
|
+
|
|
98
|
+
4. **Open the HOSTED fig for approval — never a local file.** Run
|
|
99
|
+
`figura fig open <id>` (the id `figura_create_fig`/`figura_generate_fig` just
|
|
100
|
+
returned). It opens **`viewUrl`** in the reviewer's browser — the actual
|
|
101
|
+
Figura render (brand chrome, CSP, favicon, responsive frame), team-scoped so
|
|
102
|
+
any signed-in teammate can open it even though nothing was published. That's
|
|
103
|
+
what gets approved, and it's the exact page that ships once published — not a
|
|
104
|
+
`file://` reconstruction that can drift from it. No display reachable (a
|
|
105
|
+
headless/CI session)? Paste the printed `viewUrl` into the conversation
|
|
106
|
+
instead, or fall back to `figura fig open <id> --local` for a local sandboxed
|
|
107
|
+
HTML preview.
|
|
108
|
+
|
|
109
|
+
5. **Iterate against the SAME fig, not a fresh one.** Changes from review go
|
|
110
|
+
through **`figura_revise_fig`** (`id`, the updated `html`) — it joins the
|
|
111
|
+
existing chain as a new version and returns a fresh `viewUrl`. Publishing is
|
|
112
|
+
NOT inherited automatically: a revision of an already-published fig still
|
|
113
|
+
lands unpublished by default (`viewUrl` is a team-scoped view, and the
|
|
114
|
+
existing public link keeps serving the OLD version) — pass `publish: true`
|
|
115
|
+
on the revise call itself if the fig is already live and the public link
|
|
116
|
+
needs to move to this version. Re-open it (`figura fig open <id>`) and
|
|
117
|
+
repeat until approved.
|
|
118
|
+
|
|
119
|
+
6. **Approve, then share.** Once the human signs off AND wants a link people
|
|
120
|
+
outside the team can open, publish it explicitly —
|
|
121
|
+
**`figura_publish_fig`** (or `figura fig publish <id>`) — Figura's
|
|
122
|
+
`figura.so/f/<id>` share link. Team-only review never needed this; treat
|
|
123
|
+
publishing as a deliberate call the human makes, not something create/revise
|
|
124
|
+
does on its own.
|
|
92
125
|
|
|
93
126
|
## Presenting a redesign — overview ⇄ focus
|
|
94
127
|
|
|
@@ -124,8 +157,10 @@ CSS-only radio tabs, since figs carry no `<script>`:
|
|
|
124
157
|
**deterministic**, project-scoped path — the repo's `.figura/figs/` (already
|
|
125
158
|
gitignored), or `~/.figura/figs/` outside a bound repo — never `/tmp` or
|
|
126
159
|
`mktemp`. A stable path means a rerun overwrites the same file instead of
|
|
127
|
-
littering volatile temp locations, and matches where `figura fig open`
|
|
128
|
-
(`<repo>/.figura/figs/figura-fig-<id>.html`)
|
|
160
|
+
littering volatile temp locations, and matches where `figura fig open --local`
|
|
161
|
+
writes (`<repo>/.figura/figs/figura-fig-<id>.html`) — this is YOUR headless
|
|
162
|
+
self-check copy, separate from the hosted `viewUrl` a human reviewer opens
|
|
163
|
+
(step 4 above).
|
|
129
164
|
|
|
130
165
|
A **wipe-slider** stays the narrow tool for exactly two states of the same
|
|
131
166
|
layout with pixel-level tweaks. Never ship a squeezed side-by-side as the
|
|
@@ -164,5 +199,12 @@ So when you **hand-build** a fig (the default path), do your part of the contrac
|
|
|
164
199
|
- **MCP unavailable** (headless / cron, or no interactive auth): fall back to the
|
|
165
200
|
CLI — `bunx figura-cli fig create` / `fig publish`, with `fig revise` /
|
|
166
201
|
`fig revisions` for versions, and `bunx figura-cli brand --json` for the brand.
|
|
202
|
+
- **`figura_create_fig`/`figura_revise_fig` itself fails** (no network, API down,
|
|
203
|
+
auth expired): the fig never entered Figura, so there is no `id`/`viewUrl` to
|
|
204
|
+
open. Only THEN write the HTML to a local scratch file (project-scoped —
|
|
205
|
+
`.figura/figs/`, never `/tmp`) and tell the user it's a local-only draft that
|
|
206
|
+
still needs landing once connectivity is back — this is the degraded fallback,
|
|
207
|
+
not the default path. Don't reach for it just because publishing felt like
|
|
208
|
+
an extra step; `figura_create_fig` hosts on the first call, every time.
|
|
167
209
|
- **One job, one place.** Brand = the Figura brand profile. Generation = the Figura
|
|
168
210
|
engine. This skill only decides *when* to fig and *which MCP call* to make.
|
|
@@ -48,13 +48,18 @@ Hunt across whatever stack this repo uses — do not assume one framework:
|
|
|
48
48
|
- **Identity** — app/brand name + one-line description: `package.json` (name/description), `Info.plist` (`CFBundleName`), app-store copy, the README's first paragraph.
|
|
49
49
|
- **Role/usage notes** — inline comments next to tokens often state the role
|
|
50
50
|
(e.g. `// primary brand accent; record button`). Carry those into the tokens block.
|
|
51
|
+
- **Product areas** — the app's own top-level sections, e.g. a tab bar, a
|
|
52
|
+
sidebar's nav items, or the router's top-level route folders (`app/(tabs)/*`,
|
|
53
|
+
`src/routes/*`, `src/screens/*`). These become `surfaces` — THIS product's
|
|
54
|
+
real areas ("Feed", "Checkout"), never a generic placeholder list and never
|
|
55
|
+
another product's areas.
|
|
51
56
|
|
|
52
57
|
Use `Glob`/`Grep` to locate, `Read` to extract real values. Use the REAL hexes,
|
|
53
58
|
fonts, and sizes from the repo — never invent them.
|
|
54
59
|
|
|
55
60
|
### 2. Synthesize the brand profile
|
|
56
61
|
|
|
57
|
-
Produce
|
|
62
|
+
Produce four fields (see `reference/brand-format.md` for the exact tokensBlock shape):
|
|
58
63
|
|
|
59
64
|
- **name** — the product/brand name.
|
|
60
65
|
- **appDescription** — one clause: what it is + who it's for.
|
|
@@ -66,30 +71,40 @@ Produce three fields (see `reference/brand-format.md` for the exact tokensBlock
|
|
|
66
71
|
- **Icon language** — SF Symbols / inline SVG / icon set the product uses; ban emoji if the brand does.
|
|
67
72
|
- **Voice** — 3–5 short brand voice lines + a one-line tone rule, inferred from the docs/copy.
|
|
68
73
|
- **Conventions** — any hard rules you found ("never use X", aspect ratios, chrome shape).
|
|
74
|
+
- **surfaces** — THIS product's own key areas/sections, short labels (e.g.
|
|
75
|
+
`["Feed", "Profile", "Settings"]`), read from the real navigation/routes you
|
|
76
|
+
found in step 1. Drives the Studio Area picker. 3–10 is typical; skip
|
|
77
|
+
(leave unset) if the repo genuinely has no discoverable navigation — never
|
|
78
|
+
guess or reuse a generic/other-product list. Capped at 24 areas / 40
|
|
79
|
+
characters each.
|
|
69
80
|
|
|
70
81
|
This synthesis IS the value — enrich the raw tokens with the roles, voice, and
|
|
71
82
|
conventions you read from the repo. Keep it grounded in what's actually there.
|
|
72
83
|
|
|
73
84
|
### 3. Confirm, then push
|
|
74
85
|
|
|
75
|
-
Show the user the synthesized `name`, `appDescription`, and the full
|
|
76
|
-
and confirm the target team (`figura whoami`). On approval:
|
|
86
|
+
Show the user the synthesized `name`, `appDescription`, `surfaces`, and the full
|
|
87
|
+
`tokensBlock`, and confirm the target team (`figura whoami`). On approval:
|
|
77
88
|
|
|
78
|
-
- **MCP (preferred):** call `figura_set_brand` with `{ name, appDescription, tokensBlock }`.
|
|
79
|
-
- **CLI fallback:** pipe the block over stdin —
|
|
89
|
+
- **MCP (preferred):** call `figura_set_brand` with `{ name, appDescription, tokensBlock, surfaces }`.
|
|
90
|
+
- **CLI fallback:** pipe the tokens block over stdin, pass surfaces as a flag —
|
|
80
91
|
```bash
|
|
81
|
-
cat <<'TOKENS' | figura brand set --name "<name>" --description "<one clause>" --tokens -
|
|
92
|
+
cat <<'TOKENS' | figura brand set --name "<name>" --description "<one clause>" --tokens - --surfaces "Feed,Profile,Settings"
|
|
82
93
|
… the tokensBlock …
|
|
83
94
|
TOKENS
|
|
84
95
|
```
|
|
85
96
|
(Use `bunx figura-cli` in place of `figura` if it isn't on PATH.)
|
|
86
97
|
|
|
98
|
+
A user can always add/rename/remove/reorder areas later on the brand page — this
|
|
99
|
+
is a starting point, not a one-shot lock-in.
|
|
100
|
+
|
|
87
101
|
### 4. Verify
|
|
88
102
|
|
|
89
103
|
`figura brand --json` (or the `figura_whoami` → `figura brand` read) → confirm
|
|
90
|
-
`isCustom: true` and that the saved tokens match
|
|
91
|
-
`platformProfile` — the engine auto-detects mobile /
|
|
92
|
-
you just wrote (breakpoints, font stacks,
|
|
104
|
+
`isCustom: true` and that the saved tokens AND `surfaces` match what you set.
|
|
105
|
+
The response also carries `platformProfile` — the engine auto-detects mobile /
|
|
106
|
+
web / both from the tokens you just wrote (breakpoints, font stacks,
|
|
107
|
+
safe-area/tab-bar tokens, hover
|
|
93
108
|
states — deterministic, no LLM) every time you save, so nothing extra to do
|
|
94
109
|
here. It only ever changes on its own until the team explicitly overrides it
|
|
95
110
|
on the brand page (Studio then defaults its device frame off that value —
|
|
@@ -99,5 +114,5 @@ suggest trying `/fig <a screen>` to see it render on-brand.
|
|
|
99
114
|
## Output
|
|
100
115
|
|
|
101
116
|
Report the brand you set (name + a one-line summary of the palette/type/voice you
|
|
102
|
-
captured) and confirm it's live for the team. Don't
|
|
103
|
-
again unless asked.
|
|
117
|
+
captured, plus the surfaces list) and confirm it's live for the team. Don't
|
|
118
|
+
paste the entire tokensBlock again unless asked.
|