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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figura-cli",
3
- "version": "0.13.1",
3
+ "version": "0.15.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, 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`** (`publish: true`), setting
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.** `figura_create_fig` returns a shareable `figura.so/f/<id>` link.
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: true`). Use it only when the **user explicitly asks** for a server
81
- render ("let Figura render it", headless/CI flows with no local model) — or
82
- when you've offered both paths and they picked this one. It runs as an
83
- **async job the tool polls for you** — expect ~30–90s before the landed fig
84
- returns; don't re-call while one is in flight. Unlike the default path, this
85
- **spends the team's Figura credits** (a `403` means no active subscription).
86
- Pass **`fidelity: 'high'`** to render on the better model (the default
87
- `'fast'` is lighter — fine for rough states, weaker on brand). Never pick
88
- this path on your own to save effort: it costs credits and hand-built figs
89
- track the brand better.
90
-
91
- 4. **Review** the returned link, share it, move on.
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` writes
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 three fields (see `reference/brand-format.md` for the exact tokensBlock shape):
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 `tokensBlock`,
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. The response also carries
91
- `platformProfile` — the engine auto-detects mobile / web / both from the tokens
92
- you just wrote (breakpoints, font stacks, safe-area/tab-bar tokens, hover
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 paste the entire tokensBlock
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.