figma-local 1.0.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/README.md +342 -0
- package/bin/fig-start +289 -0
- package/bin/setup-alias.sh +48 -0
- package/package.json +47 -0
- package/src/blocks/dashboard-01.js +379 -0
- package/src/blocks/index.js +27 -0
- package/src/daemon.js +664 -0
- package/src/figjam-client.js +313 -0
- package/src/figma-client.js +4198 -0
- package/src/figma-patch.js +185 -0
- package/src/index.js +8543 -0
- package/src/platform.js +206 -0
- package/src/prompt-templates.js +289 -0
- package/src/read.js +243 -0
- package/src/shadcn.js +237 -0
package/README.md
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# figma-cli
|
|
2
|
+
|
|
3
|
+
> Control Figma Desktop with Claude Code. Design tokens, shadcn/ui components, AI prompt export, lint, and more — no API key required.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/figma-local)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
[](#)
|
|
9
|
+
[](https://claude.ai/claude-code)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What is this?
|
|
14
|
+
|
|
15
|
+
**figma-cli** connects directly to Figma Desktop and lets you — or Claude Code — control it with natural language and `fig` commands.
|
|
16
|
+
|
|
17
|
+
- **Write** — Create frames, components, design tokens, icons, and full UI kits
|
|
18
|
+
- **Read** — Extract design context in a lean staged format (91–97% fewer tokens than raw data dumps)
|
|
19
|
+
- **Export** — Generate AI-ready prompts for Figma Make, Lovable, Pencil.dev, Paper.design, Google Stitch
|
|
20
|
+
- **Analyse** — Lint designs, check accessibility, audit colors and typography
|
|
21
|
+
- **Zero API key** — Connects via a local plugin bridge, no Figma API credentials needed
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Credits
|
|
26
|
+
|
|
27
|
+
This project is built on the shoulders of two excellent open-source works:
|
|
28
|
+
|
|
29
|
+
| Project | Author | What we built on |
|
|
30
|
+
|---------|--------|------------------|
|
|
31
|
+
| [**silships/figma-cli**](https://github.com/silships/figma-cli) | Sil Bormüller · [Into Design Systems](https://intodesignsystems.com) | Core daemon, plugin bridge, CDP connection, shadcn/ui component library, render engine, design token presets, FigJam support |
|
|
32
|
+
| [**royvillasana/figma-to-ai-prompter**](https://github.com/royvillasana/figma-to-ai-prompter) | Roy Villasana | Staged lean extraction approach, per-tool prompt templates, token-efficiency methodology |
|
|
33
|
+
|
|
34
|
+
**Our additions on top:**
|
|
35
|
+
- `fig read` — 3-phase staged design extraction (metadata → frame structure → used tokens only)
|
|
36
|
+
- `fig prompt` — lean tool-specific prompt generation (figma-make, lovable, pencil, paper, stitch)
|
|
37
|
+
- `fig verify --compare` — prototype vs design comparison loop with correction prompts
|
|
38
|
+
- Security hardening — WebSocket nonce handshake, input validation, rate limiting, body size caps
|
|
39
|
+
- `fig` short command — works globally from any directory
|
|
40
|
+
- Plugin UX — shows actionable "how to connect" instructions instead of infinite "Scanning..."
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
### npm (recommended)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install -g figma-local
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Gives you `fig` and `fig-start` globally on your PATH.
|
|
53
|
+
|
|
54
|
+
### curl (one-line)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
curl -fsSL https://raw.githubusercontent.com/thepreakerebi/figma-cli/main/install.sh | bash
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Homebrew
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
brew tap thepreakerebi/figma-cli
|
|
64
|
+
brew install figma-cli
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### npx (no install)
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx figma-local read
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### From source
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/thepreakerebi/figma-cli.git
|
|
77
|
+
cd figma-cli
|
|
78
|
+
npm install && npm install -g .
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Requirements
|
|
84
|
+
|
|
85
|
+
| | |
|
|
86
|
+
|-|-|
|
|
87
|
+
| Node.js | 18+ |
|
|
88
|
+
| Figma Desktop | Any version (free account works) |
|
|
89
|
+
| OS | macOS or Windows |
|
|
90
|
+
| Claude Code | Optional but recommended |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Setup (one time only)
|
|
95
|
+
|
|
96
|
+
### 1. Import the Figma plugin
|
|
97
|
+
|
|
98
|
+
1. Open **Figma Desktop**
|
|
99
|
+
2. Hamburger menu → **Plugins → Development → Import plugin from manifest...**
|
|
100
|
+
3. Navigate to the `plugin/` folder in this repo (or `$(npm root -g)/figma-local/plugin/`)
|
|
101
|
+
4. Select `manifest.json` → click **Open**
|
|
102
|
+
5. Right-click **FigCli** in the plugin list → **Add to toolbar**
|
|
103
|
+
|
|
104
|
+
### 2. Connect
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
fig-start --safe
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This starts the daemon, waits for you to click FigCli in Figma, then launches Claude Code.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Every session after that
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
1. Open Figma → click FigCli in the toolbar
|
|
118
|
+
2. In terminal: fig-start --safe
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Claude Code reads `CLAUDE.md` and knows every command automatically.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Commands
|
|
126
|
+
|
|
127
|
+
### Read & understand your canvas
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
fig read # List all frames (fast)
|
|
131
|
+
fig read "Login Screen" # Layout + components + used tokens only
|
|
132
|
+
fig read "Login Screen" --tokens # Just the design tokens that frame uses
|
|
133
|
+
fig find "Button" # Find nodes by name
|
|
134
|
+
fig node tree # Layer hierarchy
|
|
135
|
+
fig canvas info # Raw canvas info
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Design tokens
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
fig tokens preset shadcn # 276 variables: primitives + semantic (Light/Dark)
|
|
142
|
+
fig tokens tailwind # Tailwind 242-color palette
|
|
143
|
+
fig var list # All variables
|
|
144
|
+
fig var visualize # Swatches on canvas
|
|
145
|
+
fig var export css # → CSS custom properties
|
|
146
|
+
fig var export tailwind # → Tailwind config
|
|
147
|
+
fig bind fill "primary/500" # Bind variable to selection
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### shadcn/ui components
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
fig shadcn list # 30 available components
|
|
154
|
+
fig shadcn add button card input # Add specific ones
|
|
155
|
+
fig shadcn add --all # All 30 components, 58 variants
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Includes Button, Card, Input, Dialog, Tabs, Select, Switch, Badge, Alert, Checkbox, Radio Group, Accordion, Table, Pagination, Breadcrumb, Sheet, Tooltip, Dropdown, Avatar, Spinner, and more. All wired to Light/Dark variables.
|
|
159
|
+
|
|
160
|
+
### Create anything
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Quick primitives
|
|
164
|
+
fig create rect "Card" -w 320 -h 200 --fill "var:card" --radius 12
|
|
165
|
+
fig create text "Hello" -s 24 -c "var:foreground" -w bold
|
|
166
|
+
fig create icon lucide:home -s 24
|
|
167
|
+
|
|
168
|
+
# JSX render
|
|
169
|
+
fig render '<Frame name="Card" w={320} bg="var:card" rounded={16} flex="col" gap={8} p={24}>
|
|
170
|
+
<Text size={20} weight="bold" color="var:foreground">Title</Text>
|
|
171
|
+
<Text size={14} color="var:muted-foreground">Description</Text>
|
|
172
|
+
</Frame>'
|
|
173
|
+
|
|
174
|
+
# Pre-built blocks
|
|
175
|
+
fig blocks create dashboard-01 # Full analytics dashboard
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Icons (150,000+)
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
fig create icon lucide:home -s 24
|
|
182
|
+
fig create icon material:search -s 24
|
|
183
|
+
fig create icon heroicons:bell -s 20 -c "var:primary"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Lucide, Material Design, Heroicons, Feather, and 50+ more via Iconify.
|
|
187
|
+
|
|
188
|
+
### Export AI prompts
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
fig prompt "Screen" --target figma-make
|
|
192
|
+
fig prompt "Screen" --target lovable --stack "React + shadcn/ui"
|
|
193
|
+
fig prompt "Screen" --target pencil
|
|
194
|
+
fig prompt "Screen" --target stitch --platform responsive
|
|
195
|
+
|
|
196
|
+
# With context for better output
|
|
197
|
+
fig prompt "Login" \
|
|
198
|
+
--target lovable \
|
|
199
|
+
--goal "user signs in with email and password" \
|
|
200
|
+
--interactions "submit validates form, forgot password opens modal" \
|
|
201
|
+
--guardrails "no social login"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Generates ~45 tokens of structured text instead of attaching a Figma frame (300–500+ hidden tokens). **91–97% smaller input, more consistent AI output.**
|
|
205
|
+
|
|
206
|
+
### Verify & compare
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
fig verify # Screenshot of selection for AI review
|
|
210
|
+
fig verify --compare "https://..." # Diff prototype vs Figma design → correction prompts
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Export
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
fig export css # Variables → CSS
|
|
217
|
+
fig export tailwind # Variables → Tailwind config
|
|
218
|
+
fig export screenshot -o out.png # Screenshot (add -s 2 for 2x)
|
|
219
|
+
fig export-jsx "1:234" # → React JSX
|
|
220
|
+
fig export-storybook "1:234" # → Storybook stories
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Lint & accessibility
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
fig lint # All rules
|
|
227
|
+
fig lint --fix # Auto-fix
|
|
228
|
+
fig lint --preset accessibility # WCAG rules only
|
|
229
|
+
fig analyze colors # Color usage audit
|
|
230
|
+
fig analyze typography
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Rules: WCAG AA/AAA contrast ratio, touch targets (44×44px min), hardcoded colors, empty frames, deeply nested layers, missing auto-layout, minimum text size.
|
|
234
|
+
|
|
235
|
+
### FigJam
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
fig fj sticky "Idea" -x 100 -y 100 --color "#FEF08A"
|
|
239
|
+
fig fj shape "Label" -x 200 -y 100 -w 200 -h 100
|
|
240
|
+
fig fj connect "ID1" "ID2"
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Connection modes
|
|
246
|
+
|
|
247
|
+
| | Safe Mode | Yolo Mode |
|
|
248
|
+
|-|-----------|-----------|
|
|
249
|
+
| How | Plugin bridge | Direct CDP |
|
|
250
|
+
| Setup per session | Start FigCli plugin | Nothing (after one-time patch) |
|
|
251
|
+
| Speed | Standard | ~10× faster |
|
|
252
|
+
| Extra permissions | None | macOS: Full Disk Access / Windows: Admin |
|
|
253
|
+
| Command | `fig connect --safe` | `fig connect` |
|
|
254
|
+
|
|
255
|
+
**Recommendation:** Safe Mode. No system permissions needed, works on all machines including managed corporate Macs.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Security
|
|
260
|
+
|
|
261
|
+
The daemon runs only on `127.0.0.1` (never exposed to the network) and is protected by multiple layers:
|
|
262
|
+
|
|
263
|
+
| Layer | Protects against |
|
|
264
|
+
|-------|-----------------|
|
|
265
|
+
| Binds to `127.0.0.1` | Not reachable from outside your machine |
|
|
266
|
+
| Session token (`X-Daemon-Token`) | Unauthorized local processes calling the HTTP API |
|
|
267
|
+
| Host header validation | DNS rebinding attacks |
|
|
268
|
+
| No CORS headers | Cross-origin browser requests |
|
|
269
|
+
| WebSocket nonce handshake | Rogue local processes impersonating the plugin |
|
|
270
|
+
| WebSocket origin validation | Browser tabs connecting to the daemon |
|
|
271
|
+
| 1 MB request/message body cap | Memory exhaustion from oversized payloads |
|
|
272
|
+
| Plugin input validation | Code size cap (512 KB), batch size cap (50), strict field types |
|
|
273
|
+
| Rate limiting | Max 30 evals per 10 s per connection |
|
|
274
|
+
| Idle auto-shutdown | Daemon stops after 10 minutes of inactivity |
|
|
275
|
+
|
|
276
|
+
The session token is generated fresh on every `fig connect`, stored at `~/.figma-ds-cli/.daemon-token` with `chmod 600` (owner-read only).
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Work from anywhere
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
fig read # works from any directory
|
|
284
|
+
fig-start --safe --here # launch from your project dir; Claude sees both your project and Figma
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## For teams
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Install once per machine
|
|
293
|
+
npm install -g figma-local
|
|
294
|
+
|
|
295
|
+
# Import the Figma plugin once per Figma account (5 minutes)
|
|
296
|
+
# Then every designer on the team has identical fig commands
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Pin a version for consistency:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
npm install -g @jetro/figma-local@1.0.0
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Uninstall
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
npm uninstall -g figma-local
|
|
311
|
+
rm -rf ~/.figma-cli ~/.figma-ds-cli
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Remove the plugin in Figma: Plugins → Development → right-click FigCli → Remove.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Contributing
|
|
319
|
+
|
|
320
|
+
Issues and PRs welcome. For major changes, open an issue first to discuss.
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
git clone https://github.com/thepreakerebi/figma-cli.git
|
|
324
|
+
cd figma-cli
|
|
325
|
+
npm install
|
|
326
|
+
node src/index.js --help
|
|
327
|
+
npm test
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
MIT — see [LICENSE](LICENSE).
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Acknowledgements
|
|
339
|
+
|
|
340
|
+
- **[silships/figma-cli](https://github.com/silships/figma-cli)** by Sil Bormüller ([Into Design Systems](https://intodesignsystems.com)) — the original Figma Desktop CLI. The core engine, plugin bridge, CDP connection, shadcn/ui components, render pipeline, and design token presets all originate here. Please star their repo.
|
|
341
|
+
|
|
342
|
+
- **[royvillasana/figma-to-ai-prompter](https://github.com/royvillasana/figma-to-ai-prompter)** by Roy Villasana — the staged extraction and lean prompt approach. The insight that a text context block saves 91–97% of tokens vs attaching a Figma frame comes from their work. Please star their repo too.
|
package/bin/fig-start
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# fig-start — Launch Figma CLI + Claude Code
|
|
4
|
+
# Connects to Figma, lets you pick an open file, then launches Claude Code.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# fig-start → Yolo Mode (default) with file selector
|
|
8
|
+
# fig-start --safe → Safe Mode (plugin-based, no patching)
|
|
9
|
+
# fig-start --here → Stay in current directory (don't cd to repo)
|
|
10
|
+
# fig-start --setup → set/change the figma-cli repo path
|
|
11
|
+
|
|
12
|
+
CONFIG_DIR="$HOME/.figma-cli"
|
|
13
|
+
CONFIG_FILE="$CONFIG_DIR/config.json"
|
|
14
|
+
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
YELLOW='\033[1;33m'
|
|
17
|
+
RED='\033[0;31m'
|
|
18
|
+
BOLD='\033[1m'
|
|
19
|
+
DIM='\033[2m'
|
|
20
|
+
NC='\033[0m'
|
|
21
|
+
|
|
22
|
+
# ── Config helpers ──
|
|
23
|
+
|
|
24
|
+
get_daemon_token() {
|
|
25
|
+
local token_file="$HOME/.figma-ds-cli/.daemon-token"
|
|
26
|
+
if [ -f "$token_file" ]; then
|
|
27
|
+
cat "$token_file" 2>/dev/null
|
|
28
|
+
fi
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get_repo_path() {
|
|
32
|
+
if [ -f "$CONFIG_FILE" ]; then
|
|
33
|
+
python3 -c "import json; print(json.load(open('$CONFIG_FILE')).get('repoPath',''))" 2>/dev/null
|
|
34
|
+
fi
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
save_repo_path() {
|
|
38
|
+
mkdir -p "$CONFIG_DIR"
|
|
39
|
+
python3 -c "
|
|
40
|
+
import json, os
|
|
41
|
+
path = '$CONFIG_FILE'
|
|
42
|
+
cfg = {}
|
|
43
|
+
if os.path.exists(path):
|
|
44
|
+
with open(path) as f: cfg = json.load(f)
|
|
45
|
+
cfg['repoPath'] = '$1'
|
|
46
|
+
with open(path, 'w') as f: json.dump(cfg, f, indent=2)
|
|
47
|
+
"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# ── Arrow-key selector (same UX as fancoolo-start) ──
|
|
51
|
+
|
|
52
|
+
select_file() {
|
|
53
|
+
local options=("$@")
|
|
54
|
+
local count=${#options[@]}
|
|
55
|
+
local selected=0
|
|
56
|
+
|
|
57
|
+
tput civis > /dev/tty 2>/dev/null
|
|
58
|
+
trap 'tput cnorm > /dev/tty 2>/dev/null' EXIT
|
|
59
|
+
|
|
60
|
+
draw_menu() {
|
|
61
|
+
if [ "$1" = "redraw" ]; then
|
|
62
|
+
printf "\033[%dA" "$((count + 2))" > /dev/tty
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
printf "${BOLD}Select a Figma file (↑↓ arrows, Enter to confirm):${NC}\n" > /dev/tty
|
|
66
|
+
printf "\n" > /dev/tty
|
|
67
|
+
for i in "${!options[@]}"; do
|
|
68
|
+
if [ "$i" -eq "$selected" ]; then
|
|
69
|
+
printf " ${GREEN}▸ %s${NC}\n" "${options[$i]}" > /dev/tty
|
|
70
|
+
else
|
|
71
|
+
printf " %s\n" "${options[$i]}" > /dev/tty
|
|
72
|
+
fi
|
|
73
|
+
done
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
draw_menu "first"
|
|
77
|
+
|
|
78
|
+
while true; do
|
|
79
|
+
IFS= read -rsn1 key < /dev/tty
|
|
80
|
+
|
|
81
|
+
case "$key" in
|
|
82
|
+
$'\x1b')
|
|
83
|
+
read -rsn2 seq < /dev/tty
|
|
84
|
+
case "$seq" in
|
|
85
|
+
'[A')
|
|
86
|
+
((selected--))
|
|
87
|
+
[ "$selected" -lt 0 ] && selected=$((count - 1))
|
|
88
|
+
;;
|
|
89
|
+
'[B')
|
|
90
|
+
((selected++))
|
|
91
|
+
[ "$selected" -ge "$count" ] && selected=0
|
|
92
|
+
;;
|
|
93
|
+
esac
|
|
94
|
+
;;
|
|
95
|
+
''|$'\n'|$'\r')
|
|
96
|
+
tput cnorm > /dev/tty 2>/dev/null
|
|
97
|
+
printf "\n" > /dev/tty
|
|
98
|
+
printf '%s' "${options[$selected]}"
|
|
99
|
+
return
|
|
100
|
+
;;
|
|
101
|
+
'q')
|
|
102
|
+
tput cnorm > /dev/tty 2>/dev/null
|
|
103
|
+
printf "\n" > /dev/tty
|
|
104
|
+
return
|
|
105
|
+
;;
|
|
106
|
+
esac
|
|
107
|
+
|
|
108
|
+
draw_menu "redraw"
|
|
109
|
+
done
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# ── Main ──
|
|
113
|
+
|
|
114
|
+
# Handle flags
|
|
115
|
+
SAFE_MODE=false
|
|
116
|
+
STAY_HERE=false
|
|
117
|
+
|
|
118
|
+
for arg in "$@"; do
|
|
119
|
+
case "$arg" in
|
|
120
|
+
--safe) SAFE_MODE=true ;;
|
|
121
|
+
--here) STAY_HERE=true ;;
|
|
122
|
+
esac
|
|
123
|
+
done
|
|
124
|
+
|
|
125
|
+
if [ "$1" = "--setup" ]; then
|
|
126
|
+
printf "${BOLD}Enter the path to your figma-cli repo:${NC} "
|
|
127
|
+
read -r repo_path < /dev/tty
|
|
128
|
+
repo_path="${repo_path/#\~/$HOME}"
|
|
129
|
+
if [ ! -f "$repo_path/src/index.js" ]; then
|
|
130
|
+
echo -e "${RED}Not a valid figma-cli repo:${NC} $repo_path"
|
|
131
|
+
exit 1
|
|
132
|
+
fi
|
|
133
|
+
save_repo_path "$repo_path"
|
|
134
|
+
echo -e "${GREEN}Saved.${NC} Run ${BOLD}fig-start${NC} to begin."
|
|
135
|
+
exit 0
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# Resolve repo path
|
|
139
|
+
REPO_PATH=$(get_repo_path)
|
|
140
|
+
|
|
141
|
+
if [ -z "$REPO_PATH" ] || [ ! -d "$REPO_PATH" ]; then
|
|
142
|
+
# Try to find it relative to this script
|
|
143
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
144
|
+
if [ -f "$SCRIPT_DIR/src/index.js" ]; then
|
|
145
|
+
REPO_PATH="$SCRIPT_DIR"
|
|
146
|
+
save_repo_path "$REPO_PATH"
|
|
147
|
+
else
|
|
148
|
+
echo -e "${YELLOW}figma-cli repo not configured.${NC}"
|
|
149
|
+
printf "${BOLD}Enter the path to your figma-cli repo:${NC} "
|
|
150
|
+
read -r REPO_PATH < /dev/tty
|
|
151
|
+
REPO_PATH="${REPO_PATH/#\~/$HOME}"
|
|
152
|
+
if [ ! -f "$REPO_PATH/src/index.js" ]; then
|
|
153
|
+
echo -e "${RED}Not a valid figma-cli repo:${NC} $REPO_PATH"
|
|
154
|
+
exit 1
|
|
155
|
+
fi
|
|
156
|
+
save_repo_path "$REPO_PATH"
|
|
157
|
+
fi
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
CLI="$REPO_PATH/src/index.js"
|
|
161
|
+
|
|
162
|
+
echo ""
|
|
163
|
+
if [ "$SAFE_MODE" = true ]; then
|
|
164
|
+
echo -e "${BOLD} fig-start${NC} ${DIM}— Figma CLI launcher (Safe Mode)${NC}"
|
|
165
|
+
else
|
|
166
|
+
echo -e "${BOLD} fig-start${NC} ${DIM}— Figma CLI launcher${NC}"
|
|
167
|
+
fi
|
|
168
|
+
echo ""
|
|
169
|
+
|
|
170
|
+
# Step 1: Check if Figma is running, start if not
|
|
171
|
+
if ! pgrep -x "Figma" > /dev/null 2>&1; then
|
|
172
|
+
echo -e " ${YELLOW}Starting Figma...${NC}"
|
|
173
|
+
open -a Figma
|
|
174
|
+
sleep 3
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# Step 2: Connect to Figma
|
|
178
|
+
if [ "$SAFE_MODE" = true ]; then
|
|
179
|
+
# ── Safe Mode: plugin-based connection ──
|
|
180
|
+
# Run connect --safe interactively so user sees plugin setup instructions
|
|
181
|
+
echo -e " ${BOLD}🔒 Safe Mode${NC} — plugin-based, no patching required"
|
|
182
|
+
echo ""
|
|
183
|
+
node "$CLI" connect --safe
|
|
184
|
+
if [ $? -ne 0 ]; then
|
|
185
|
+
echo -e " ${RED}Connection failed.${NC}"
|
|
186
|
+
exit 1
|
|
187
|
+
fi
|
|
188
|
+
echo ""
|
|
189
|
+
|
|
190
|
+
# Skip file selector (Safe Mode only sees the active file via plugin)
|
|
191
|
+
echo -e " ${GREEN}Launching Claude Code...${NC}"
|
|
192
|
+
echo ""
|
|
193
|
+
if [ "$STAY_HERE" = false ]; then
|
|
194
|
+
cd "$REPO_PATH" || exit 1
|
|
195
|
+
fi
|
|
196
|
+
exec claude
|
|
197
|
+
else
|
|
198
|
+
# ── Yolo Mode: CDP-based connection ──
|
|
199
|
+
# Check if CDP is already accessible (avoid killing/restarting Figma)
|
|
200
|
+
CDP_OK=false
|
|
201
|
+
if curl -s -o /dev/null -w "%{http_code}" http://localhost:9222/json 2>/dev/null | grep -q "200"; then
|
|
202
|
+
CDP_OK=true
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
if [ "$CDP_OK" = true ]; then
|
|
206
|
+
# Figma is already accessible — just ensure daemon is running
|
|
207
|
+
DAEMON_OK=false
|
|
208
|
+
DAEMON_TOKEN=$(get_daemon_token)
|
|
209
|
+
if [ -n "$DAEMON_TOKEN" ]; then
|
|
210
|
+
if curl -s -o /dev/null -w "%{http_code}" -H "X-Daemon-Token: $DAEMON_TOKEN" http://localhost:3456/health 2>/dev/null | grep -q "200"; then
|
|
211
|
+
DAEMON_OK=true
|
|
212
|
+
fi
|
|
213
|
+
fi
|
|
214
|
+
if [ "$DAEMON_OK" = false ]; then
|
|
215
|
+
echo -e " Starting daemon..."
|
|
216
|
+
node "$CLI" daemon restart > /dev/null 2>&1
|
|
217
|
+
sleep 1
|
|
218
|
+
fi
|
|
219
|
+
echo -e " ${GREEN}✓ Connected${NC}"
|
|
220
|
+
else
|
|
221
|
+
# Not accessible — run full connect (will kill + restart Figma)
|
|
222
|
+
echo -e " Connecting to Figma..."
|
|
223
|
+
CONNECT_OUT=$(node "$CLI" connect 2>&1)
|
|
224
|
+
if [ $? -ne 0 ]; then
|
|
225
|
+
echo -e " ${RED}Connection failed:${NC}"
|
|
226
|
+
echo "$CONNECT_OUT"
|
|
227
|
+
echo ""
|
|
228
|
+
echo -e " ${YELLOW}Tip:${NC} Try ${BOLD}fig-start --safe${NC} if patching fails."
|
|
229
|
+
exit 1
|
|
230
|
+
fi
|
|
231
|
+
echo -e " ${GREEN}✓ Connected${NC}"
|
|
232
|
+
# Wait for Figma to fully restore all tabs after restart
|
|
233
|
+
sleep 3
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
# Step 3: List open files and let user pick
|
|
237
|
+
echo -e " Loading open files..."
|
|
238
|
+
FILES_JSON=$(node "$CLI" files 2>/dev/null)
|
|
239
|
+
|
|
240
|
+
if [ -z "$FILES_JSON" ] || echo "$FILES_JSON" | grep -q '"error"'; then
|
|
241
|
+
echo -e " ${RED}Could not list files.${NC} Is Figma open with a file?"
|
|
242
|
+
exit 1
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
# Parse file titles
|
|
246
|
+
FILE_TITLES=()
|
|
247
|
+
while IFS= read -r title; do
|
|
248
|
+
FILE_TITLES+=("$title")
|
|
249
|
+
done < <(echo "$FILES_JSON" | python3 -c "
|
|
250
|
+
import json, sys
|
|
251
|
+
files = json.load(sys.stdin)
|
|
252
|
+
for f in files:
|
|
253
|
+
t = f.get('title', 'Untitled')
|
|
254
|
+
# Clean up Figma title format (remove ' – Figma' suffix)
|
|
255
|
+
t = t.replace(' – Figma', '').replace(' - Figma', '')
|
|
256
|
+
print(t)
|
|
257
|
+
" 2>/dev/null)
|
|
258
|
+
|
|
259
|
+
if [ ${#FILE_TITLES[@]} -eq 0 ]; then
|
|
260
|
+
echo -e " ${YELLOW}No Figma files detected.${NC} Open a file in Figma first."
|
|
261
|
+
echo ""
|
|
262
|
+
echo -e " Launching Claude Code anyway..."
|
|
263
|
+
echo ""
|
|
264
|
+
if [ "$STAY_HERE" = false ]; then
|
|
265
|
+
cd "$REPO_PATH" || exit 1
|
|
266
|
+
fi
|
|
267
|
+
exec claude
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
SELECTED_TITLE=""
|
|
271
|
+
|
|
272
|
+
echo ""
|
|
273
|
+
SELECTED_TITLE=$(select_file "${FILE_TITLES[@]}")
|
|
274
|
+
if [ -z "$SELECTED_TITLE" ]; then
|
|
275
|
+
echo " Cancelled."
|
|
276
|
+
exit 0
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
echo -e " ${GREEN}✓ File: ${BOLD}${SELECTED_TITLE}${NC}"
|
|
280
|
+
echo ""
|
|
281
|
+
|
|
282
|
+
# Step 4: Launch Claude Code
|
|
283
|
+
echo -e " ${GREEN}Launching Claude Code...${NC}"
|
|
284
|
+
echo ""
|
|
285
|
+
if [ "$STAY_HERE" = false ]; then
|
|
286
|
+
cd "$REPO_PATH" || exit 1
|
|
287
|
+
fi
|
|
288
|
+
exec claude
|
|
289
|
+
fi
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Adds fig-start alias to ~/.zshrc (or ~/.bashrc)
|
|
4
|
+
|
|
5
|
+
REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
6
|
+
ALIAS_LINE="alias fig-start='$REPO_DIR/bin/fig-start'"
|
|
7
|
+
|
|
8
|
+
GREEN='\033[0;32m'
|
|
9
|
+
YELLOW='\033[1;33m'
|
|
10
|
+
BOLD='\033[1m'
|
|
11
|
+
NC='\033[0m'
|
|
12
|
+
|
|
13
|
+
# Detect shell config file
|
|
14
|
+
if [ -n "$ZSH_VERSION" ] || [ "$SHELL" = "/bin/zsh" ]; then
|
|
15
|
+
RC_FILE="$HOME/.zshrc"
|
|
16
|
+
else
|
|
17
|
+
RC_FILE="$HOME/.bashrc"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Check if alias already exists
|
|
21
|
+
if grep -q "alias fig-start=" "$RC_FILE" 2>/dev/null; then
|
|
22
|
+
# Update existing alias (path may have changed)
|
|
23
|
+
sed -i '' "/alias fig-start=/d" "$RC_FILE"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Add alias
|
|
27
|
+
echo "" >> "$RC_FILE"
|
|
28
|
+
echo "# Figma CLI" >> "$RC_FILE"
|
|
29
|
+
echo "$ALIAS_LINE" >> "$RC_FILE"
|
|
30
|
+
|
|
31
|
+
# Save repo path to config
|
|
32
|
+
mkdir -p "$HOME/.figma-cli"
|
|
33
|
+
python3 -c "
|
|
34
|
+
import json, os
|
|
35
|
+
path = os.path.expanduser('~/.figma-cli/config.json')
|
|
36
|
+
cfg = {}
|
|
37
|
+
if os.path.exists(path):
|
|
38
|
+
with open(path) as f: cfg = json.load(f)
|
|
39
|
+
cfg['repoPath'] = '$REPO_DIR'
|
|
40
|
+
with open(path, 'w') as f: json.dump(cfg, f, indent=2)
|
|
41
|
+
"
|
|
42
|
+
|
|
43
|
+
echo ""
|
|
44
|
+
echo -e " ${GREEN}Done!${NC} Added ${BOLD}fig-start${NC} alias to ${BOLD}$RC_FILE${NC}"
|
|
45
|
+
echo ""
|
|
46
|
+
echo -e " Now run: ${BOLD}source $RC_FILE${NC}"
|
|
47
|
+
echo -e " Then type: ${BOLD}fig-start${NC}"
|
|
48
|
+
echo ""
|